不得不注意tornado的多进程部署

原创 2018年04月16日 11:01:59

tornado多进程启动时,采用的是fork的方式。

一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间(百度百科上的错误)如果采用copy on write技术,在存储的内容未发生修改前父子进程是共享同一份存储空间
UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。在不同的UNIX (Like)系统下,我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的实现。所以在移植代码的时候我们不应该对此作出任何的假设。

上述内容摘自百度百科对fork函数的说明,在调用fork函数产生的子进程将会获得父进程的数据空间、堆、栈等资源的“副本”,这里的“副本”有可能是存储空间的同一份。父进程打开的文件句柄,socket等资源也会被原封不动的拷备给子进程。

最常出现的问题就是logging模块,python的logging模块是线程安全的,但不是进程安全的。如果在fork子进程之前开始实例化logging模块,各个子进程都会共享了同一个fd,所以会出现日志错乱的问题。为了避免这个问题,各个进程打都输出到独立的文件,延后Logger的实例化。

sockets = tornado.netutil.bind_sockets(int(options.port))
fork_id = tornado.process.fork_processes(int(redis_info.num_processes))
server = tornado.httpserver.HTTPServer(application)
server.add_sockets(sockets)
#实现logger init方式,接收fork_id来控制文件名
logger.init(fork_id)

另一点就是socket的复用导致的问题,实现了一个python版本的dubbo客户端,连接发送方式如下。在使用过程中,fork进程之前实例化了一个client, 并保持了一个socket。

def __call__(self, command):
    json.dumps(args)[1:-1])
    self.__socket.send(command.encode('utf-8') + b'\r\n')
    ans = bytes()
    try:
        while True:
            # ret = self.__socket.recv(1024000)
            # ans += ret
            # if ret.endswith("dubbo>"):
            #     break
            ready = select.select([self.__socket], [], [], 0.5)
            if ready[0]:
                import time
                ret = self.__socket.recv(1024)
                ans = ans.join([ret])
                if ans.endswith(b"dubbo>"):
                    break
            else:
                logger.error("dubbo timeout")
                break
    except Exception as e:
        pass
    try:
        msg = ans.decode('GB2312', 'ignore')
        ret_data, elapsed_time, prompt = msg.split('\r\n')
        print("[receive", msg, "]")
        return ret_data
    except Exception as e1:
        print("[error recive",msg, traceback.format_exc(),"]")
        try:
            msg = ans.decode('GB2312', 'ignore')
            info = msg.split("dubbo>")[1]
            ret_data = info.split('\r\n')[0]
            return ret_data
        except Exception as e2:
            logger.exception(e2)
            #print "e2: ", e2
            return ""

web接口

class MainHandler(tornado.web.RequestHandler):

    def get(self):
        dubbo_client =  DubboClient("127.0.0.1", 10192)
        resp = dubbo_client.dubboserver.getByAccountId(pid)
        print("[ session: send accountId ({}) get msg ({}) ]".format(pid, resp))
        #print("[",pid, resp, "]")
        self.write(resp)

ab测试并发

ab -n 20 -c 10 http://127.0.0.1:10087/

截取了一部分异常输出结果

...
[ session: send accountId (1) get msg ({"accountId":3}) ]
[ session: send accountId (3) get msg ({"accountId":0}) ]
[error recive  Traceback (most recent call last):
  File "/home/gikieng/project/TestTornado/src/commons/DubboClient.py", line 82, in __call__
    ret_data, elapsed_time, prompt = msg.split('\r\n')
ValueError: not enough values to unpack (expected 3, got 1)
 ]
ERROR:root:list index out of range
Traceback (most recent call last):
  File "/home/gikieng/project/TestTornado/src/commons/DubboClient.py", line 82, in __call__
    ret_data, elapsed_time, prompt = msg.split('\r\n')
ValueError: not enough values to unpack (expected 3, got 1)
...
During handling of the above exception, another exception occurred:
[ session: send accountId (2) get msg () ]
...

从输出结果中看出,各个子进程都在竞争同一个socket的读写,导致了信息的收发进程不一致。在网络波动的情况下,还有进程收抢到了报文片段。

结论:
在使用tornado多进程部署方式的过程中,要注意到fork带来的副作用。为了简单使用,最好还是使用单进程多实例的方式部署,然后再加上一个反射代理层对外提供统一接口。单为了提高并发能力,使用多进程的效益并不是很高。

Tornado 多进程 & 异步

基本版: #coding=utf-8 import tornado.web import tornado.httpserver import tornado.options import torn...
  • yongche_shi
  • yongche_shi
  • 2016年12月16日 15:48
  • 3002

Tornado 多进程实现分析

时间 2014-04-11 14:35:00  cold night's Linux Blog 原文  http://www.linuxzen.com/tornado-duo-jin-cheng...
  • oMingZi12345678
  • oMingZi12345678
  • 2016年10月26日 00:36
  • 1768

Tornado多进程方式log切分错误的解决方案

tornado多进程log日志切分错误的解决方式
  • networm3
  • networm3
  • 2013年11月18日 16:34
  • 5299

部署项目Nginx+Tornado+Supervisor

Tornado Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,...
  • a519640026
  • a519640026
  • 2017年07月26日 18:08
  • 522

tornado 多进程模式

https://www.douban.com/note/217901726/ 官方文档的helloworld实例中的启动方法: if __name__ == "__main__": ...
  • oMingZi12345678
  • oMingZi12345678
  • 2016年10月26日 00:31
  • 1230

tornado 使用supervisor管理进程,使用nginx做负载均衡

tornado 使用supervisor管理进程,使用nginx做负载均衡部署方式采用nginx作为load banlancernginx的配置文件如下,放在/etc/nginx/sites-avai...
  • thao6626
  • thao6626
  • 2016年01月26日 21:20
  • 1031

tornado 11、部署生产环境

一、后台运行  一般调试过程中我们使用python app.py运行网站,方便我们在命令行中看运行状况。 但在生产环境下我们需要后台运行网站。我们可以使用linux的nohup命令。nohup py...
  • qq_16234613
  • qq_16234613
  • 2016年08月20日 23:10
  • 1534

微信统一支付详解,坑太多,不得不写

最近开发app支付,支付宝按照开发文档很快搞定,本想微信支付开发也一样的容易,结果我错了,一路踩坑不断,到最后终于完成,耗了不少时间和精力,所以想写一篇关于微信统一支付的开发过程,希望大家能少走弯路 ...
  • java_mars
  • java_mars
  • 2017年06月08日 17:34
  • 669

flask笔记:13:将Flask应用程序部署在nginx,tornado的简单方法

flask代码,main.py: from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): ...
  • u013055678
  • u013055678
  • 2017年04月15日 14:00
  • 1134

Python+Tornado+Nginx服务器部署解决方案

目标:反向代码+Tornado框架实现高性能web服务器原则:生产环境部署,提升服务端性能。 适用岗位:开发人员、运维人员 实施步骤: 1、 选定Python版本2.7.8 32位; (1...
  • sc4599
  • sc4599
  • 2015年11月27日 14:14
  • 7012
收藏助手
不良信息举报
您举报文章:不得不注意tornado的多进程部署
举报原因:
原因补充:

(最多只允许输入30个字)