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)
1
2
3
4
5
6
另一点就是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 ""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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)
1
2
3
4
5
6
7
8
ab测试并发

ab -n 20 -c 10 http://127.0.0.1:10087/
1
截取了一部分异常输出结果

...
[ 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 () ]
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
从输出结果中看出,各个子进程都在竞争同一个socket的读写,导致了信息的收发进程不一致。在网络波动的情况下,还有进程收抢到了报文片段。

结论:
在使用tornado多进程部署方式的过程中,要注意到fork带来的副作用。为了简单使用,最好还是使用单进程多实例的方式部署,然后再加上一个反射代理层对外提供统一接口。单为了提高并发能力,使用多进程的效益并不是很高。
---------------------
作者:Gikieng
来源:CSDN
原文:https://blog.csdn.net/gikieng/article/details/79957882
版权声明:本文为博主原创文章,转载请附上博文链接!

不得不注意tornado多进程部署的副作用的更多相关文章

  1. supervisor tornado 多进程多端口配置

    base: nginx tornado 目标: tornado 实现多端口多进程运行 pip install supervisor echo_supervisord_conf > /etc/su ...

  2. python tornado nginx deployment tornado 的部署在linux Nginx服务器

    题外话: 这里分享一个链接是将Flask 和 Apache 用 mod_wsgi 来进行传唤的部署方式,在digital ocean VPS 上的. https://www.digitalocean. ...

  3. Tornado 多进程 & 异步

    另外一篇:http://www.cnblogs.com/xiaoshi657/p/6945208.html 基本版: #coding=utf-8 import tornado.web import t ...

  4. tornado 多进程模式

    https://www.douban.com/note/217901726/ 官方文档的helloworld实例中的启动方法: if __name__ == "__main__": ...

  5. tornado多进程模式不同进程写不同日志

    #coding: utf- ''' Author: Time: Target: ''' import logging import logging.handlers import os import ...

  6. Nginx + tornado + supervisor部署

    参考链接:supervisor + Tornado + Nginx 使用详解, 用tornado ,Supervisord ,nginx架网站, tornado官方文档 项目文档树: . ├── ch ...

  7. Django项目在linux系统中虚拟环境部署

    1.在linux系统下,安装virtualenv 命令:pip install virtualenv 2.项目部署前的准备 1. Django web project deployment 1.1.  ...

  8. centos上发布部署python的tornado网站项目完整流程

    先说下大体上的做法,开发环境上要新弄一个 virtualenv的环境,在这个里面放你的开发调试,当然这个其实也不是必须的,但是这样会方便管理一些. 再在centos上也弄一个 virtualenv虚拟 ...

  9. Centos7部署tornado项目

    今天帮一个学生解决tornado的部署问题,在此记录了这其中的过程,其中的tornado项目更换为demo示例. 开发环境: 本地开发环境:Win10 + Python3.5.4 + PyCharm ...

随机推荐

  1. C# ---sender

    在某个方法中: 第一种写法: private void btn4_Click_1(object sender, RoutedEventArgs e) { btn1_Click(null, null); ...

  2. Linux之Shell 脚本加密工具-shc

    Much effort, much prosperity. 为什么要加密Shell脚本呢?当然是为了安全! 可能脚本里面涉及到密码之类的就需要进行加密了 一.下载安装shc工具 要保护自己编写的she ...

  3. 编写第一个Shell脚本【TLCL】

    怎样编写一个 Shell 脚本 编写一个脚本 使脚本文件可执行 把脚本放到Shell能够找到的地方 脚本文件格式 #!/bin/bash # This is our first script. ech ...

  4. Eclipse 添加JSP模板

    0.环境 Eclipse IDE for Java EE Developers (4.3.2) win8.1系统 1.原因 Eclipse自带新建JSP为: <%@ page language= ...

  5. Linux(CentOS)网络配置

    1. 查看网口连接情况 2.修改网卡 3.修改主机名 4.重新启动网络 5.查看连接ifconfig,ping网络 copyright@2015 liupan liu.pan@datatom.com

  6. tyvj 1056 能量项链 区间dp

    P1056 能量项链 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 NOIP2006 提高组 第一道 描述     在Mars星球上,每个Mars人都随身佩 ...

  7. RedHat 6.4企业版利用iso镜像做本地yum源

    修改文章:http://linux.cn/article-1017-1.html 而RedHat的yum则需要注册付费才能使用,如果不这样则有两种解决方案 1. 利用iso镜像做本地yum源 2. 利 ...

  8. python测试函数的使用时间

    1. 使用装饰器来衡量函数执行时间 有一个简单方法,那就是定义一个装饰器来测量函数的执行时间,并输出结果:(代码通用3.x) import time from functools import wra ...

  9. Java多线程 - 线程同步

    多线程操作同一个对象时,容易引发线程安全问题.为了解决线程安全问题,Java多线程引入了同步监视器. 同步代码块 同步代码块语法格式如下: synchronized(obj){ //此处的代码即为同步 ...

  10. JNI_Z_10_Java的数组

    在Java中数组分为两种: (1).基本类型数组 (2).对象类型(Object[])的数组 (数组中存放的是指向Java对象中的引用) 一个能通用于两种不同类型数组的函数: GetArrayLeng ...