这篇文章或许已经不再使用,测试版本为2.7.x,有兴趣可以测试最新版本
一、问题说明:最近测试mongo复制集,由于没有机器,所以选择在一台虚拟机上搭建。然后使用mongo-java-driver连接。
①、复制集初始化函数如下:
- > 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'}]}
- > rs.initiate(config)
②、java连接代码如下:
- static Mongo m = null;
- static{
- try {
- List<ServerAddress> list= new ArrayList<ServerAddress>();
- ServerAddress sap0 = new ServerAddress("192.168.132.100",20011);
- ServerAddress sas1 = new ServerAddress("192.168.132.100",20012);
- ServerAddress sas2 = new ServerAddress("192.168.132.100",20013);
- list.add(sap0);
- list.add(sas1);
- list.add(sas2);
- m = new Mongo(list);
- } catch (UnknownHostException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
报错:
- Exception in thread "main" com.mongodb.MongoException: can't find a master
原因分析:
m = new Mongo(list);
使用此方法:
- public Mongo( List<ServerAddress> replicaSetSeeds , MongoOptions options )
- throws MongoException {
- ....
- _addrs = replicaSetSeeds;
- ....
- _connector = new DBTCPConnector( this , _addrs );
- _connector.start();
- ...
- }
- public DBTCPConnector( Mongo m , List<ServerAddress> all )
- throws MongoException {
- _portHolder = new DBPortPool.Holder( m._options );
- _checkAddress( all );
- _allHosts = new ArrayList<ServerAddress>( all );
- _rsStatus = new ReplicaSetStatus( m, _allHosts );
- _createLogger.info( all " -> " getAddress() );
- }
这个类是获取replica set 最新状态的,运行时,后台有一个线程ping服务器,所以这个类的状态都是最新的。他会读取rs的初始化函数,得到host,主从等等状态信息。
初始化函数:
- ReplicaSetStatus( Mongo mongo, List<ServerAddress> initial ){
- _all = Collections.synchronizedList( new ArrayList<Node>() );
- for ( ServerAddress addr : initial ){
- _all.add( new Node( addr ) );
- }
- ...
- _updater = new Updater();
- }
Updater即是后台进程,同样是个内部类,继承Thead类:
- class Updater extends Thread {
- Updater(){
- super( "ReplicaSetStatus:Updater" );
- setDaemon( true );
- }
- public void run(){
- while ( ! _closed ){
- try {
- updateAll();
- long now = System.currentTimeMillis();
- if (inetAddrCacheMS > 0 && _nextResolveTime < now) {
- _nextResolveTime = now inetAddrCacheMS;
- for (Node node : _all) {
- node.updateAddr();
- }
- }
- // force check on master
- // otherwise master change may go unnoticed for a while if no write concern
- _mongo.getConnector().checkMaster(true, false);
- }
- catch ( Exception e ){
- _logger.log( Level.WARNING , "couldn't do update pass" , e );
- }
- try {
- Thread.sleep( updaterIntervalMS );
- }
- catch ( InterruptedException ie ){
- }
- }
- }
- }
- synchronized void updateAll(){
- HashSet<Node> seenNodes = new HashSet<Node>();
- for ( int i=0; i<_all.size(); i++ ){
- Node n = _all.get(i);
- n.update(seenNodes);
- }
- ...
- }
- synchronized void update(Set<Node> seenNodes){
- try {
- long start = System.currentTimeMillis();
- CommandResult res = _port.runCommand( _mongo.getDB("admin") , _isMasterCmd );
- ...
- }
- { "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配置复制集了。解决办法如下:
- > 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'}]}
- > rs.initiate(config)
2012-09-12:今天开发的同事上线遇到问题,新版mongo2.2链接复制集找不到主,其实还是一个问题,因为现在配置复制集都是使用hostname代替IP,所以在程序中连IP的时候他还是找不到,无法解析。新版的driver-2.9.0已经支持,应该是Updater进程已经经过了判断,发现hostname链接不上后又替换了IP,所以大家用新版的driver吧,或者在应用服务器上也配置hosts,让程序能找到机器就好了。