Chinaunix首页 | 论坛 | 博客
  • 博客访问: 809014
  • 博文数量: 50
  • 博客积分: 757
  • 博客等级: 上士
  • 技术积分: 1913
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-29 14:29
个人简介

DBA

文章分类

全部博文(50)

文章存档

2015年(3)

2014年(2)

2013年(14)

2012年(19)

2011年(12)

分类: 数据库开发技术

2012-02-16 20:16:59

这篇文章或许已经不再使用,测试版本为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. }
数据库连接是的实体类。
  1. public DBTCPConnector( Mongo m , List<ServerAddress> all )
  2.         throws MongoException {
  3.         _portHolder = new DBPortPool.Holder( m._options );
  4.         _checkAddress( all );

  5.         _allHosts = new ArrayList<ServerAddress>( all );
  6.         _rsStatus = new ReplicaSetStatus( m, _allHosts );

  7.         _createLogger.info( all " -> " getAddress() );
  8.     }
错误报错是找不到主,我们关注类,继续往下走:
       这个类是获取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.         _updater = new Updater();
  8.     }
可以看到还有一个Node类,这个类是个内部类,保存address的名称,端口等信息。
Updater即是后台进程,同样是个内部类,继承Thead类:
  1. class Updater extends Thread {
  2.         Updater(){
  3.             super( "ReplicaSetStatus:Updater" );
  4.             setDaemon( true );
  5.         }

  6.         public void run(){
  7.             while ( ! _closed ){
  8.                 try {
  9.                     updateAll();

  10.                     long now = System.currentTimeMillis();
  11.                     if (inetAddrCacheMS > 0 && _nextResolveTime < now) {
  12.                         _nextResolveTime = now inetAddrCacheMS;
  13.                         for (Node node : _all) {
  14.                             node.updateAddr();
  15.                         }
  16.                     }

  17.                     // force check on master
  18.                     // otherwise master change may go unnoticed for a while if no write concern
  19.                     _mongo.getConnector().checkMaster(true, false);
  20.                 }
  21.                 catch ( Exception e ){
  22.                     _logger.log( Level.WARNING , "couldn't do update pass" , e );
  23.                 }

  24.                 try {
  25.                     Thread.sleep( updaterIntervalMS );
  26.                 }
  27.                 catch ( InterruptedException ie ){
  28.                 }

  29.             }
  30.         }
  31.     }
_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,让程序能找到机器就好了。

阅读(7863) | 评论(0) | 转发(0) |
0

上一篇:Mongo复制集搭建

下一篇:java链接mongo

给主人留下些什么吧!~~