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
        ............
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()

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM