6

大家好,我有下一个环境python2.7.5

flask==0.10.1
flask-wtf==0.8.4
jinja2==2.7
werkzeug==0.9.1
flask-mongoengine==0.7.0
mongoengine==0.8.2
pymongo==2.5.2
uwsgi==1.9.13

并有下一个应用程序'app.py':

from flask import Flask
from flask.ext.mongoengine import Document, MongoEngine
from mongoengine import StringField  

class Config(object):
    DEBUG = True
    MONGODB_HOST = ('mongodb://localhost:27017,localhost:27018/'
                    'test?replicaSet=rs0')
    MONGODB_DB = True

app = Flask(__name__)
app.config.from_object(Config)
MongoEngine(app)

class Test(Document):
    test = StringField(default='test')

    meta = {
        'allow_inheritance': False,
    }

    def __unicode__(self):
        return self.test

Test(test='test1').save()

@app.route('/')
def hello_world():
    return unicode(Test.objects.first())

if __name__ == '__main__':
    app.run('0.0.0.0', 8080, True)

我有下一个 nginx 配置:

server {
    listen       80;
    server_name  localhost;
    location / {
        include uwsgi_params;
        uwsgi_pass unix:/tmp/uwsgi.sock;
    }

}

我开始 uwsgi 为:

/path/to/env/bin/uwsgi \
  --module app:app \
  --env /path/to/env/ \
  --pythonpath /path/to/app/ \
  --socket /tmp/uwsgi.sock \
  --pidfile /tmp/uwsgi.pid \
  --daemonize /tmp/uwsgi.log \
  --processes 2 \
  --threads 2 \
  --master

我有两个 mongodb 实例:

mongod --port 27017 --dbpath /path/to/mongo/data/rs0-0 --replSet rs0 \
  --smallfiles --oplogSize 128

mongod --port 27018 --dbpath /path/to/mongo/data/rs0-1 --replSet rs0 \
  --smallfiles --oplogSize 128

并将 mongo 控制台中的副本集配置为:

rsconf = {
    _id: "rs0",
    members: [{_id: 0, host: "127.0.0.1:27017"}]
};
rs.initiate(rsconf);
rs.add("127.0.0.1:27018");

所以它运作良好。但是当我关闭和启动主要或次要 mongo 实例时,我的应用程序无法恢复连接,并且每次之后我都有下一个异常:

...
  File "/path/to/app/replica.py", line 33, in hello_world
    return unicode(Test.objects.first())
  File "/path/to/env/local/lib/python2.7/site-packages/mongoengine/queryset/queryset.py", line 325, in first
    result = queryset[0]
  File "/path/to/env/local/lib/python2.7/site-packages/mongoengine/queryset/queryset.py", line 211, in __getitem__
    return queryset._document._from_son(queryset._cursor[key],
  File "/path/to/env/local/lib/python2.7/site-packages/pymongo/cursor.py", line 470, in __getitem__
    for doc in clone:
  File "/path/to/env/local/lib/python2.7/site-packages/pymongo/cursor.py", line 814, in next
    if len(self.__data) or self._refresh():
  File "/path/to/env/local/lib/python2.7/site-packages/pymongo/cursor.py", line 763, in _refresh
    self.__uuid_subtype))
  File "/path/to/env/local/lib/python2.7/site-packages/pymongo/cursor.py", line 700, in __send_message
    **kwargs)
  File "/path/to/env/local/lib/python2.7/site-packages/pymongo/mongo_replica_set_client.py", line 1546, in _send_message_with_response
    raise AutoReconnect(msg, errors)
pymongo.errors.AutoReconnect: No replica set primary available for query with ReadPreference PRIMARY

当我使用mongoengie==0.7.10which useReplicaSetConnection代替MongoReplicaSetClient时,mongoengine==0.8.2我有下一个例外:

  1. 次要,获取请求,次要,获取请求:

    我第一次有:

    pymongo.errors.AutoReconnect: 127.0.0.1:27017: [Errno 104] Connection reset by peer
    

    后:

    pymongo.errors.AutoReconnect: No replica set primary available for query with ReadPreference PRIMARY
    
  2. Down primary,获取请求,up primary,获取请求:

    我第一次有:

    pymongo.errors.AutoReconnect: 127.0.0.1:27017: [Errno 111] Connection refused
    

    后:

    pymongo.errors.AutoReconnect: No replica set primary available for query with ReadPreference PRIMARY
    
  3. 向下主要或次要,向上主要或次要,获取我一直的请求:

    pymongo.errors.AutoReconnect: not master and slaveOk=false
    

所以两个 mongo 实例只是简单的例子。如果我再添加一个实例(总共 3 个),那么:

  1. 如果我关闭任何辅助设备,那么一切正常。如果我向下和向上一个二级然后向下或向下和向上第二个二级然后一切正常。

  2. 如果我上下两个辅助 - 一些问题。

  3. 如果我关闭主节点或仅关闭一个主节点(两个辅助节点可用) - 一些问题,尽管 mongo 选择了新的主节点!!!

如果我启动一个 uwsgi 进程(没有--master两个或三个 mongo 实例):

/path/to/env/bin/uwsgi \
  --module app:app \
  --env /path/to/env/ \
  --pythonpath /path/to/app/ \
  --socket /tmp/uwsgi.sock \
  --pidfile /tmp/uwsgi.pid \
  --daemonize /tmp/uwsgi.log \
  --processes 1 \
  --threads 2

或使用开发服务器运行应用程序:

/path/to/env/bin/python app.py

然后应用程序在关闭和启动 mongo 实例后恢复连接没有问题。

我在生产中进行了一些部署,有时与 mongo 实例的连接可能会中断(几秒钟)。之后,在重新启动 uwsgi 之前,我的应用程序无法正常工作。

所以我有两个问题:

  1. 为什么它发生在几个 uwsgi 进程中?
  2. 如何在关闭和启动 mongo 实例后修复正常的应用程序工作?

UPD1self.__schedule_refresh() :我试图理解问题,现在当我关闭一个 mongo 节点时出现连接异常时我有不同的行为:

  1. 对于一个过程:

    1. 在此声明之前:rs_state有两个成员:active with up == True,downed with up == False
    2. 在这句话之后:rs_state有一个活跃的成员up == True
  2. 对于两个进程:

    1. 在此声明之前:rs_state有两个成员:active with up == True,downed with up == False
    2. 在此声明之后:rs_state有两个成员:active with up == True,downed with up == False(未更改)。

当我启动 mongo 节点时,self.__schedule_refresh(sync=sync)也会有不同的行为:

  1. 对于一个过程:

    1. 在这句话之前:rs_state有一个活跃的成员up == True
    2. 在此声明之后:rs_state有两个成员活跃于up == True,向上up == True
  2. 对于两个进程:

    1. 在此声明之前:rs_state有两个成员使用 激活up == True,使用 增加up == False
    2. 在此声明之后:rs_state让两个成员活动,用up == True,向上up == False(没有改变)。

所以看起来 mongo 无法更新副本集状态(请参阅__schedule_refresh):

def __schedule_refresh(self, sync=False):
    """Awake the monitor to update our view of the replica set's state.

    If `sync` is True, block until the refresh completes.

    If multiple application threads call __schedule_refresh while refresh
    is in progress, the work of refreshing the state is only performed
    once.
    """
    self.__monitor.schedule_refresh()
    if sync:
        self.__monitor.wait_for_refresh(timeout_seconds=5)
4

2 回答 2

14

尝试使用 uwsgi--lazy-apps选项。MongoReplicaSetClient 生成一个副本集 MonitorThread,并且该线程无法在 uwsgi 工作进程分支中存活。--lazy-apps将在每个工作进程中初始化 pymongo MonitorThread。

于 2013-07-10T17:40:35.453 回答
0

在您的副本集发生更改(无主副本、新主副本等)后,下一个操作将引发 AutoReconnect 异常。在该操作失败后,底层 PyMongo MongoReplicaSetClient 将重新连接到副本集,并且未来的操作可能会成功。

如果有新的主节点,MongoReplicaSetClient 会找到它,以后的操作就会成功。

如果没有主节点,除非将 ReadPreference 设置为 PRIMARY_PREFERRED,否则任何操作都不会成功。请参阅此处的文档:

http://mongoengine-odm.readthedocs.org/en/latest/guide/connecting.html#replicasets

每个 uwsgi 进程必须重新连接一次。因此,如果您的副本集发生更改,您可以预期每个 uwsgi 进程出现一个 AutoReconnect 异常。

于 2013-07-09T17:21:26.893 回答