5

我们的应用程序建立在 mongodb 副本集之上。我想捕获副本集处于自动故障转移过程中的时间范围内引发的所有异常。我将使应用程序重试或等待故障转移完成。这样故障转移不会影响用户。我在这里找到了描述 java 驱动程序行为的文档:https ://jira.mongodb.org/browse/DOCS-581

我编写了一个测试程序来查找所有可能的异常,它们都是 MongoException 但带有不同的消息:

  1. MongoException.Network:“对服务器 /10.11.0.121:27017 的读取操作在数据库测试中失败”
  2. MongoException:“找不到主人”
  3. MongoException:“不与主人交谈并重试用完”
  4. MongoException:“[这里是副本集状态]中没有可用的副本集成员用于{“mode”:“primary”}”
  5. 也许更多...

我很困惑,不确定通过错误消息确定是否安全。我也不想捕获所有的 MongoException。有什么建议吗?

谢谢

4

3 回答 3

2

我现在认为 Java 中的 Mongo 在这方面特别薄弱。我认为您解释错误代码的策略不能很好地扩展或将在驱动程序演变中幸存下来。这当然是意见。

好消息是 Mongo 驱动程序提供了一种获取 ReplicaSet 状态的方法:http: //api.mongodb.org/java/2.11.1/com/mongodb/ReplicaSetStatus.html。您可以直接使用它来确定您的应用程序是否可见 Master。如果您只想知道这些,那么您只需要http://api.mongodb.org/java/2.11.1/com/mongodb/Mongo.html#getReplicaSetStatus()。抓住那个孩子并检查一个非空的主人,你就在路上。

ReplicaSetStatus rss = mongo.getReplicaSetStatus();
boolean driverInFailover = rss.getMaster() == null;

如果您真正需要确定 ReplSet 是死的、只读的还是可读写的,这将变得更加困难。这是对我有用的代码。我讨厌它。

@Override
public ReplSetStatus getReplSetStatus() {
    ReplSetStatus rss = ReplSetStatus.DOWN;
    MongoClient freshClient = null;
    try {
        if ( mongo != null ) {
            ReplicaSetStatus replicaSetStatus = mongo.getReplicaSetStatus();
            if ( replicaSetStatus != null ) {
                if ( replicaSetStatus.getMaster() != null ) {
                    rss = ReplSetStatus.ReadWrite;
                } else {
                    /*
                     * When mongo.getReplicaSetStatus().getMaster() returns null, it takes a a
                     * fresh client to assert whether the ReplSet is read-only or completely
                     * down. I freaking hate this, but take it up with 10gen.
                     */
                    freshClient = new MongoClient( mongo.getAllAddress(), mongo.getMongoClientOptions() );
                    replicaSetStatus = freshClient.getReplicaSetStatus();
                    if ( replicaSetStatus != null ) {
                        rss = replicaSetStatus.getMaster() != null ? ReplSetStatus.ReadWrite : ReplSetStatus.ReadOnly;
                    } else {
                        log.warn( "freshClient.getReplicaSetStatus() is null" );
                    }
                }
            } else {
                log.warn( "mongo.getReplicaSetStatus() returned null" );
            }
        } else {
            throw new IllegalStateException( "mongo is null?!?" );
        }
    } catch ( Throwable t ) {
        log.error( "Ingore unexpected error", t );
    } finally {
        if ( freshClient != null ) {
            freshClient.close();
        }
    }
    log.debug( "getReplSetStatus(): {}", rss );
    return rss;
}

我讨厌它,因为它不遵循 Mongo Java Driver 约定,您的应用程序只需要一个 Mongo,通过这个单例,您可以连接到其余的 Mongo 数据结构(DB、Collection 等)。我只能通过在检查期间新建第二个 Mongo 来观察这项工作,以便我可以依靠 ReplicaSetStatus 空检查来区分“ReplSet-DOWN”和“只读”。

这个驱动程序真正需要的是某种方式来直接询问 Mongo 的问题,看看 ReplSet 是否可以在此时支持每个 WriteConcerns 或 ReadPreferences。就像是...

/**
 * @return true if current state of Client can support readPreference, false otherwise
 */
boolean mongo.canDoRead( ReadPreference readPreference )

/**
 * @return true if current state of Client can support writeConcern; false otherwise
 */
boolean mongo.canDoWrite( WriteConcern writeConcern ) 

这对我来说很有意义,因为它承认 ReplSet 在创建 Mongo 时可能很棒,但现在的条件意味着特定类型的读取或写入操作可能会由于条件的变化而失败。

无论如何,也许http://api.mongodb.org/java/2.11.1/com/mongodb/ReplicaSetStatus.html可以满足您的需求。

于 2013-10-01T23:28:57.363 回答
1

当 Mongo 进行故障转移时,没有处于 PRIMARY 状态的节点。您可以通过replSetGetStatus命令获取副本集状态并查找主节点。如果没有找到,可以假设集群处于故障转移过渡状态,并且可以根据需要重试,检查每个失败连接的副本集状态。

于 2013-08-05T10:12:53.230 回答
0

我不知道 Java 驱动程序实现本身,但我会捕获所有MongoExceptions,然后在getCode()基础上过滤它们。如果错误代码不适用于副本集故障,那么我将重新抛出MongoException.

问题是,据我所知,文档中没有错误代码参考。好吧,这里有一个存根,但这相当不完整。唯一的办法就是阅读Java驱动的代码,就知道它使用的是什么代码……</p>

于 2013-08-05T10:09:30.087 回答