gunicorn + flask 处理高并发请求介绍
一,独角兽
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 ............
二, 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) .........
在handle函数的循环内部,handle_one_request函数首先读取HTTP 请求,初始化WSGI环境,然后最终调用run_application函数来处理请求
def run_application(self): self.result = self.application(self.environ, self.start_response) self.process_result()