Press "Enter" to skip to content

PHP MongoClient “No candidate servers found” 分析

根因分析

原因一: mongodb配置的地址和php里使用的地址不一致

  • mongodb server配置为机器名而php里使用的链接地址是ip
  • mongodb server配置为ip而php里使用的链接地址是机器名

扩展C相关代码

    //connections.c>mongo_discover_topology>mongo_connection_ismaster
    if (bson_find_field_as_string(ptr, "me", &connected_name)) {
        we_think_we_are = mongo_server_hash_to_server(con->hash);
        if (strcmp(connected_name, we_think_we_are) == 0) {
            mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "ismaster: the server name matches what we thought it'd be (%s).", we_think_we_are);
        } else {
            mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "ismaster: the server name (%s) did not match with what we thought it'd be (%s).", connected_name, we_think_we_are);
            /* We reset the name as the server responded with a different name than
             * what we thought it was */
            free(server->host);
            server->host = mcon_strndup(connected_name, strchr(connected_name, ':') - connected_name);
            server->port = atoi(strchr(connected_name, ':') + 1);
            retval = 3;
        }

原因二: 建立链接时发现缓存连接不可用

$mongo = new MongoClient("mongodb://127.0.0.1:27018",
              array(
           'replicaSet' => 'rs0',
                   'connect' => true,
                   'connectTimeoutMS' => 10000
        )); \\这里会报No candidate servers found错误

原因三: 创建链接指定connect为false, 在查询或者修改删除时发现连接不可用

$mongo = new MongoClient("mongodb://127.0.0.1:27018",
              array(
           'replicaSet' => 'rs0',
                   'connect' => false,
                   'connectTimeoutMS' => 10000
        ));

$collection = $mongo->test->test;
$cusor = $collection->find()->limit(5);
foreach($cusor as $doc) { \\这里会报No candidate servers found错误
    echo getmypid() .json_encode($doc) . "\n";
}

问题出现场景

  • mongodb服务器挂掉
  • mongodb主挂掉,选举期间
  • mongodb服务器挂掉恢复正常,第一次访问由于缓存长连接失效导致

第三种情况,如果PHP与mongodb采用的是长链接,没有主动close,那么可能会导致这个报错持续很长时间。因为每一个php-fpm进程链接之前故障服务器的缓存链接都不可用了,当第一次访问时就会发生失败,会把失效的链接释放,第二次访问会重新建立链接就OK了。

问题解决

mongo.so扩展经过测试,都会存在这个问题,有几种方式可以解决:
* php与mongodb之间建立链接,使用完成后主动调用close,即每次使用短链接。
* 如果建立链接时指定connect=true,建议增加重试机制,保证链接成功。

$retry = 2;
do {
    try {
        $mongo = new MongoClient("mongodb://127.0.0.1:27018",
                      array(
                   'replicaSet' => 'rs0',
                           'connect' => true,
                           'connectTimeoutMS' => 10000
                ));
        break;
    } catch(MongoException $e) {
        if (0 == $retry--) {
            //log
            return;
        }
    }
} while($retry);
  • 如果建立链接时指定connect=false,建议在读写mongodb时增加重试机制,参考如上代码。
  • 如果您当前使用的PHP版本大于等于5.4,建议使用mongodb.so扩展,具体里面代码没有看过,但是经过实测发现该扩展已经包含了重试的机制在里面。

Be First to Comment

发表评论

邮箱地址不会被公开。 必填项已用*标注