读书人

【转】为什么java无法连接搭在一台机器

发布时间: 2013-03-14 10:33:15 作者: rapoo

【转】为什么java无法连接搭在一台机器上的mongo复制集
这篇文章或许已经不再使用,测试版本为2.7.x,有兴趣可以测试最新版本

一、问题说明:
?????? 最近测试mongo复制集,由于没有机器,所以选择在一台虚拟机上搭建。然后使用mongo-java-driver连接。
①、复制集初始化函数如下:
???????

  1. > config = {_id: 'shard1', members: [{_id: 0, host: '127.0.0.1:20011'},{_id: 1, host: '127.0.0.1:20012'},{_id: 2, host:'127.0.0.1:20013'}]}
  2. > rs.initiate(config)

??????? 或者你换成localhost,都没有关系。
②、java连接代码如下:

  1. static?Mongo m?=?null;
  2. ????static{
  3. ????????try?{
  4. ????????????List<ServerAddress>?list=?new?ArrayList<ServerAddress>();
  5. ????????????ServerAddress sap0?=?new?ServerAddress("192.168.132.100",20011);
  6. ????????????ServerAddress sas1?=?new?ServerAddress("192.168.132.100",20012);
  7. ????????????ServerAddress sas2?=?new?ServerAddress("192.168.132.100",20013);
  8. ????????????list.add(sap0);
  9. ????????????list.add(sas1);
  10. ????????????list.add(sas2);
  11. ????????????m?=?new?Mongo(list);
  12. ????????}?catch?(UnknownHostException?e)?{
  13. ????????????// TODO Auto-generated catch block
  14. ????????????e.printStackTrace();
  15. ????????}
  16. ????}

192.168.132.100是虚拟机的IP,并不是使用本地localhost或者127.0.0.1,因为程序不再虚拟机上么。
报错:

  1. Exception in thread "main" com.mongodb.MongoException: can't find a master


原因分析:
m?=?new?Mongo(list);
使用此方法:

  1. public?Mongo(?List<ServerAddress>?replicaSetSeeds?,?MongoOptions options?)
  2. ????????throws?MongoException?{
  3. ??????? ....
  4. ????????_addrs?=?replicaSetSeeds;
  5. ??????? ....
  6. ????????_connector?=?new?DBTCPConnector(?this?,?_addrs?);
  7. ????????_connector.start();
  8. ??????? ...
  9. }

数据库连接是DBTCPConnector的实体类。

  1. public?DBTCPConnector(?Mongo m?,?List<ServerAddress>?all?)
  2. ????????throws?MongoException?{
  3. ????????_portHolder?=?new?DBPortPool.Holder(?m._options?);
  4. ????????_checkAddress(?all?);
  5. ?
  6. ????????_allHosts?=?new?ArrayList<ServerAddress>(?all?);?
  7. ????????_rsStatus?=?new?ReplicaSetStatus(?m,?_allHosts?);
  8. ?
  9. ????????_createLogger.info(?all?" -> "?getAddress()?);
  10. ????}

错误报错是找不到主,我们关注ReplicaSetStatus类,继续往下走:
?????? 这个类是获取replica set 最新状态的,运行时,后台有一个线程ping服务器,所以这个类的状态都是最新的。他会读取rs的初始化函数,得到host,主从等等状态信息。
初始化函数:

  1. ReplicaSetStatus(?Mongo mongo,?List<ServerAddress>?initial?){
  2. ????????_all?=?Collections.synchronizedList(?new?ArrayList<Node>()?);
  3. ????????for?(?ServerAddress addr?:?initial?){
  4. ????????????_all.add(?new?Node(?addr?)?);
  5. ????????}
  6. ??????? ...
  7. ?
  8. ????????_updater?=?new?Updater();
  9. ????}

可以看到还有一个Node类,这个类是个内部类,保存address的名称,端口等信息。
Updater即是后台进程,同样是个内部类,继承Thead类:

  1. class?Updater?extends?Thread?{
  2. ????????Updater(){
  3. ????????????super(?"ReplicaSetStatus:Updater"?);
  4. ????????????setDaemon(?true?);
  5. ????????}
  6. ?
  7. ????????public?void?run(){
  8. ????????????while?(?!?_closed?){
  9. ????????????????try?{
  10. ????????????????????updateAll();
  11. ?
  12. ????????????????????long?now?=?System.currentTimeMillis();
  13. ????????????????????if?(inetAddrCacheMS?>?0?&&?_nextResolveTime?<?now)?{
  14. ????????????????????????_nextResolveTime?=?now?inetAddrCacheMS;
  15. ????????????????????????for?(Node?node?:?_all)?{
  16. ????????????????????????????node.updateAddr();
  17. ????????????????????????}
  18. ????????????????????}
  19. ?
  20. ????????????????????// force check on master
  21. ????????????????????// otherwise master change may go unnoticed for a while if no write concern
  22. ????????????????????_mongo.getConnector().checkMaster(true,?false);
  23. ????????????????}
  24. ????????????????catch?(?Exception?e?){
  25. ????????????????????_logger.log(?Level.WARNING?,?"couldn't do update pass"?,?e?);
  26. ????????????????}
  27. ?
  28. ????????????????try?{
  29. ????????????????????Thread.sleep(?updaterIntervalMS?);
  30. ????????????????}
  31. ????????????????catch?(?InterruptedException?ie?){
  32. ????????????????}
  33. ?
  34. ????????????}
  35. ????????}
  36. ????}

当_connector.start();执行时,就会启动这个线程。关注绿色代码部分,updateAll()函数

  1. synchronized?void?updateAll(){
  2. ????????HashSet<Node>?seenNodes?=?new?HashSet<Node>();
  3. ????????for?(?int?i=0;?i<_all.size();?i++?){
  4. ????????????Node?n?=?_all.get(i);
  5. ????????????n.update(seenNodes);
  6. ????????}
  7. ??????? ...
  8. ????}

n.update(seenNodes),继续。。

  1. synchronized?void?update(Set<Node>?seenNodes){
  2. ????????????try?{
  3. ????????????????long?start?=?System.currentTimeMillis();
  4. ????????????????CommandResult res?=?_port.runCommand(?_mongo.getDB("admin")?,?_isMasterCmd?);
  5. ?????????????? ...?
  6. }

可以看到程序会远程执行isMaster命令,得到res

  1. { "serverUsed" : "192.168.72.128:20011" , "setName" : "rstest" , "ismaster" : false , "secondary" : true , "hosts" : [ "localhost:20011" , "localhost:20013" , "localhost:20012"] , "primary" : "localhost:20012" , "me" : "localhost:20011" , "maxBsonObjectSize" : 16777216 , "ok" : 1.0}


这样的信息,看到了吧,hosts里面显示的是localhost:20011,就是我们在config函数里配置的IP
然后后面的程序会更新Node,将host变为localhost:20011,这样,我们的程序就无法连接了,毕竟不是在本地配置的。
其实这是个特例了,如果你的程序和mongo在一起的话,这样配置也不会出错,如果程序和mongo不在一起,那么你就需要用外部IP配置复制集了。解决办法如下

  1. > config = {_id: 'shard1', members: [{_id: 0, host: '192.168.132.100:20011'},{_id: 1, host: '192.168.132.100:20012'},{_id: 2, host:'192.168.132.100:20013'}]}
  2. > rs.initiate(config)

说实在的,写到这里我写不下去了,因为这个是另外启动的线程,我无法调试出来具体的过程。但是原因却是是这个,大家注意就好了,如果有调试出具体过程的,感谢分享。

?2012-09-12:今天开发的同事上线遇到问题,新版mongo2.2链接复制集找不到主,其实还是一个问题,因为现在配置复制集都是使用hostname代替IP,所以在程序中连IP的时候他还是找不到,无法解析。新版的driver-2.9.0已经支持,应该是Updater进程已经经过了判断,发现hostname链接不上后又替换了IP,所以大家用新版的driver吧,或者在应用服务器上也配置hosts,让程序能找到机器就好了。转自?http://blog.chinaunix.net/uid-15795819-id-3075083.html

读书人网 >其他数据库

热点推荐