python多进程关闭socket_python实现多进程监听同一个socket的prefork服务端模型

这两天迁移数据,没时间写博客了……   正好这两天跟同事聊了下prefork模式,就拿出来聊聊。。。 如果你跟我一样是python程序员,我很建议你用prefork+gevent协程的方式。

python实现socket服务相当的容易,但是默认是单进程状态,是堵塞的….   我想喜欢prefork这个模式的,虽然没有epoll那种基于事件的高性能,但也是可以解决单进程带来的堵塞的问题。

因为prefork用的是os.fork模式,所以可以绑定在多个核上…   我想大家最先知道prefork模型应该是apache http server上。

下面是普通的单进程socket实现…

#xiaorui.cc

import sys

import socket

import select

import os

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

try:

s.bind(('localhost',9000) )

s.listen(1)

except Exception, e:

raise e

while 1:

client,address = s.accept()

print "%s get a client[%s] from %s" % (os.getpid(),str(client),address)

client.close()

这是prefork的相关代码… …  需要注意的是,不要把apache的prefork想的太简单了,php-fpm就是prefork多进程管理很好的例子,可以动态的创建进程和销毁进程, 可以根据各种情况增加减少进程,以及最必须的监控子进程存活的模式。。。。

import sys

import socket

import select

import os

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

try:

s.bind(('localhost',9000) )

s.listen(1)

except Exception, e:

raise e

for i in range(1,10):

pid = os.fork()

if pid <0:

print 'fork error'

sys.exit(-1)

elif pid >0:

print 'fork process %d' % pid

else:

pass

while 1:

client,address = s.accept()

print "%s get a client[%s] from %s" % (os.getpid(),str(client),address)

client.close()

一次启动10个通过os.fork的子进程,然后又同事监听同一个端口, 流程是 create/bind/listen -> fork ->accept -> respones

一般来说一个进程是只能绑定在一个端口上的,想上面的多个进程accept了同一个socket,有些匪夷所思,但是linux从操作系统层面支持了这种做法。

那么来看看Linux是这样实现accept调用的流程是怎么样的

把当前进程插入这个fd的等待队列然后阻塞

当新连接进来的时候,操作系统会唤醒这个fd的等待队列的第一个进程,只唤醒一个进程,其他的进程还是在就绪的等待状态.

这是Linux kernel 2.4引入的功能. 相关论文:Accept scalability on Linux

Prefork vs multiprocessing 对比 .

都是可以实现多进程,但是multiprocessing实现友好的数据共享,例如manager … prefork实现的方式比较的原生.

说实话关于服务端的开发,我也经过不少,一般来说都会维持一个队列,也就是tcp缓冲队列,把用户的请求放到一个队列里面,然后让先前fork出来的进程来消费这些链接。

他比prefork的方式相比,在并发大的时候,不至于大片的请求失败。

pre-fork是一个重大的改善,极大的简化了网络server的编程,Linux可能会走得更远,Linux Kernel 3.9会引入一个新的socket option,只要设置socket的SO_REUSEPORT属性,那么不同的进程和线程都可以同时bind这个ip和port。有时间找个ubuntu高版本内核的机器测试下….

Q & A

有个朋友提了一个问题,我觉得有些典型就整理了这问题.

如果10个worker进程监听在同一个端口,那么当 netstat 的时候看到 LISTEN 是哪个进程的呢?我机器上看到 nginx 是第一个 worker,而 gunicorn 则是 master。这两者之间有什么实现上的差别?

他们之间用的都是一种模式 ! nginx、 gunicorn、php-fpm等用的都是SO_REUSEADDR模式,同时只能有一个进程来listen端口,剩下的worker用accept来监控fd。 如果你想多个worker都监听一个端口,那么可以使用SO_REUSEPORT模式, 但需要注意的是so_reuseport需要高版本linux内核的支持。

现在nginx 1.9.x是支持so_reuseport socket mode模式. Nginx和Gunicorn在这方面是没有啥区别的,都只能有一个worker去监听端口。

另外,对于多个worker去accept数据不会产生惊群的现象。 现在的内核已经解决这个惊群问题,内核只会对第一个accept fd的进程进行唤醒。 nginx是有惊群的现象,但也只是在epoll_wait获取事件对象时发生,所以nginx会对epoll wait动作进行加锁防止惊群。

大家觉得文章对你有些作用!

如果想赏钱,可以用微信扫描下面的二维码,感谢!

另外再次标注博客原地址  xiaorui.cc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值