一,独角兽
Gunicorn 服务器作为wsgi app的容器, 采用 pre-fork 模型中有一个管理进程以及几个的工作进程。master 管理多个 slave 进程
创建slave进程,监听事件:
1, 根据定义的 work数量 创建多个 work 进程
2, 在worker.init_process()函数中,每个woker子进程都会单独去实例化我们的wsgi app对象。针对多进程,一个进程实例化一个 app 对象,多线程,一个线程处理实例化app对象,协程根据server对象
3, 然后,worker和master各自进入自己的消息循环。 master的事件循环就是收收信号,管理管理worker进程,
而worker进程的事件循环就是监听网络事件并处理(如新建连接,断开连接,处理请求发送响应等等),真正的连接最终是连到了worker进程上的
# 创建 固定数量的 worker(由 manager 进行维护)
def manage_workers(self):
if len(self.WORKERS.keys()) < self.num_workers:
self.spawn_workers()
while len(workers) > self.num_workers:
(pid, _) = workers.pop(0)
self.kill_worker(pid, signal.SIGQUIT)
# 创建 work 进程
def spawn_worker(self):
self.worker_age += 1
#创建worker。请注意这里的app 对象并不是真正的wsgi app对象,而是gunicorn的app
#对象。gunicorn的app对象负责import我们自己写的wsgi app对象。
worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS,
self.app, self.timeout / 2.0,
self.cfg, self.log)
# pid = 0 表示子进程
pid = os.fork()
#父进程,返回后继续创建其他worker,没worker后进入到自己的消息循环
if pid != 0:
# 父进程记录子进程
self.WORKERS[pid] = worker
return pid
# 子进程继续运行
# Process Child
worker_pid = os.getpid()
try:
..........
worker.init_process() #子进程,初始化woker,进入worker的消息循环,
sys.exit(0)
except SystemExit:
raise
............
View Code
二, Gunicorn 几种启动方式 sync (默认值) eventlet gevent tornado
IO 受限 -建议使用gevent或者asyncio
CPU受限 -建议增加workers数量
不确定内存占用? -建议使用gthread
# gunicorn 启动4个进程(默认启动方式),每个 work 单线程 并发量 4*1
gunicorn -w 4 -b 0.0.0.0:8000 demo:app
# gunicorn 允许每个worker拥有多个线程 并发量 = 4*2
gunicorn -w 4 --thread=2 --worker-class=gthread main:app
# gunicorn 伪线程 gevent (协程) 并发量 3*1000
gunicorn --worker-class=gevent --worker-connections=1000 -w 3 main:app
三, web 异步任务实现方式:
1, web 的多并发 并不是有 flask 来实现的, flask 只是负责根据Request产生Response(一个python程序),
多并发是由 WSGI server是通过进程(pre-fork)来并发的。这样并发就取决与进程数,如果WSGI server用了gevent,
eventlet等 green thread技术,就可以支持更多并发
2, 针对 work 启动方式 gevent
每个ggevent worker启动的时候会启动多个server对象,worker首先为每个listener创建一个server对象,
每个server对象都有运行在一个单独的gevent pool对象中。真正等待链接和处理链接的操作是在server对象中进行的
3,WSGIServer 实际上是创建一个协程去处理该套接字,也就是说在WSGIServer 中,一个协程单独负责一个HTTP链接。
协程中运行的self._handle函数实际上是调用了WSGIHandler的handle函数来不断处理http 请求
4,gunicorn 会启动一组 worker进程,所有worker进程公用一组listener,在每个worker中为每个listener建立一个wsgi server。
每当有HTTP链接到来时,wsgi server创建一个协程来处理该链接,协程处理该链接的时候,先初始化WSGI环境,
然后调用用户提供的app对象去处理HTTP请求(处理http请求可以理解为一个程序)。
#为每个listener创建server对象。
for s in self.sockets:
pool = Pool(self.worker_connections) #创建gevent pool
if self.server_class is not None:
#创建server对象
server = self.server_class(
s, application=self.wsgi, spawn=pool, log=self.log,
handler_class=self.wsgi_handler, **ssl_args)
.............
server.start() #启动server,开始等待链接,服务链接
servers.append(server)
.........
View Code
在handle函数的循环内部,handle_one_request函数首先读取HTTP 请求,初始化WSGI环境,然后最终调用run_application函数来处理请求
def run_application(self):
self.result = self.application(self.environ, self.start_response)
self.process_result()