对于具有多个进程的nginx + uwsgi,Mongodb副本集自动协调不起作用

tbi*_*icr 6 python mongodb pymongo mongoengine uwsgi

大家好,我有下一个环境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
Run Code Online (Sandbox Code Playgroud)

并有下一个应用程序'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)
Run Code Online (Sandbox Code Playgroud)

我有下一个nginx配置:

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

}
Run Code Online (Sandbox Code Playgroud)

我开始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
Run Code Online (Sandbox Code Playgroud)

我有两个mongodb实例:

mongod --port 27017 --dbpath /path/to/mongo/data/rs0-0 --replSet rs0 \
  --smallfiles --oplogSize 128
Run Code Online (Sandbox Code Playgroud)

mongod --port 27018 --dbpath /path/to/mongo/data/rs0-1 --replSet rs0 \
  --smallfiles --oplogSize 128
Run Code Online (Sandbox Code Playgroud)

并在mongo控制台中配置副本设置为:

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

所以它运作良好.但是,当我向下和向上主要或辅助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
Run Code Online (Sandbox Code Playgroud)

当我使用mongoengie==0.7.10其使用ReplicaSetConnection,而不是MongoReplicaSetClientmongoengine==0.8.2那我有下一个例外:

  1. 中学,获取请求,中学,获取请求:

    我第一次有:

    pymongo.errors.AutoReconnect: 127.0.0.1:27017: [Errno 104] Connection reset by peer
    
    Run Code Online (Sandbox Code Playgroud)

    后:

    pymongo.errors.AutoReconnect: No replica set primary available for query with ReadPreference PRIMARY
    
    Run Code Online (Sandbox Code Playgroud)
  2. 向下主要,获取请求,向上主要,获取请求:

    我第一次有:

    pymongo.errors.AutoReconnect: 127.0.0.1:27017: [Errno 111] Connection refused
    
    Run Code Online (Sandbox Code Playgroud)

    后:

    pymongo.errors.AutoReconnect: No replica set primary available for query with ReadPreference PRIMARY
    
    Run Code Online (Sandbox Code Playgroud)
  3. 在主要或次要,主要或次要,请求我总是:

    pymongo.errors.AutoReconnect: not master and slaveOk=false
    
    Run Code Online (Sandbox Code Playgroud)

所以两个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
Run Code Online (Sandbox Code Playgroud)

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

/path/to/env/bin/python app.py
Run Code Online (Sandbox Code Playgroud)

向下和向上mongo实例后应用程序恢复连接没有问题.

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

所以我有两个问题:

  1. 为什么会出现几个uwsgi进程?
  2. 向下和向上mongo实例后如何修复正常的应用程序工作?

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

  1. 对于一个过程:

    1. 在此声明之前:rs_state有两个成员:主动up == True,倒塌up == False.
    2. 在此声明之后:rs_state拥有一个活跃成员up == True.
  2. 对于两个过程:

    1. 在此声明之前:rs_state有两个成员:主动up == True,倒塌up == False.
    2. 在此声明之后:rs_state有两个成员:有效up == True,被击倒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)
Run Code Online (Sandbox Code Playgroud)

rec*_*gic 13

尝试使用uwsgi --lazy-apps选项.MongoReplicaSetClient生成一个replicaset MonitorThread,这个线程不能在uwsgi worker进程fork中生存.--lazy-apps将在每个工作进程中初始化pymongo MonitorThread.

  • 我已经更新了MongoReplicaSetClient来引发异常,如果它在fork之后使用,我已经澄清了文档.这些更改将在PyMongo 2.6中发布.[commit 1](https://github.com/mongodb/mongo-python-driver/commit/9a5ded59e4e8e9f32b62dac266359470f7087b3f) [commit 2](https://github.com/mongodb/mongo-python-driver/commit/0a54175b6cfcfc29bd4d9e990f365338c8a6435a) (2认同)