这两天迁移数据,没时间写博客了…… 正好这两天跟同事聊了下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高版本内核的机器测试下….
Hello boys,文章的原文地址是 http://xiaorui.cc/?p=1665
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动作进行加锁防止惊群。
如果10个worker进程监听在同一个端口,那么当 netstat 的时候看到 LISTEN 是哪个进程的呢?我机器上看到 nginx 是第一个 worker,而 gunicorn 则是 master。这两者之间有什么实现上的差别?[CODE][root@w1 conf.d]# netstat -antup | grep LISTtcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 2000/nginxtcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN 2000/nginxtcp 0 0 127.0.0.1:8081 0.0.0.0:* LISTEN 2000/nginxtcp 0 0 0.0.0.0:10002 0.0.0.0:* LISTEN 5071/gvp.apptcp 0 0 127.0.0.1:8082 0.0.0.0:* LISTEN 2000/nginxtcp 0 0 127.0.0.1:8083 0.0.0.0:* LISTEN 2000/nginxtcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 901/sshdtcp 0 0 127.0.0.1:11002 0.0.0.0:* LISTEN 12020/gunicorn: mastcp 0 0 127.0.0.1:15770 0.0.0.0:* LISTEN 1118/aegis_quartztcp 0 0 127.0.0.1:10080 0.0.0.0:* LISTEN 2000/nginxtcp 0 0 127.0.0.1:9001 0.0.0.0:* LISTEN 9183/php-cgiroot 26416 0.0 0.3 200880 3600 ? Ss 2015 0:00 nginx: master process /home/admin/install/nginx/sbin/nginx -c /etc/nginx/nginx.confadmin 2000 0.0 2.4 222092 25252 ? S 03:37 0:02 _ nginx: worker processadmin 2001 0.0 2.4 222336 25336 ? S 03:37 0:01 _ nginx: worker processadmin 2002 0.0 0.3 200880 3312 ? S 03:37 0:00 _ nginx: cache manager processadmin 12020 0.0 1.2 212828 12796 ? S 12:48 0:00 gunicorn: master [gvp_uwsgi:app]admin 12025 0.0 1.9 322880 19948 ? S 12:48 0:00 _ gunicorn: worker [gvp_uwsgi:app][/CODE]
这两天出国了,没有及时的回复 (有装逼的嫌疑)! 他们之间用的都是一种模式 ! 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动作进行加锁防止惊群。
我使用SocketServer
prefork值得学的,但是现在不怎么用吧