關於 python gevent 架框 作為 TCP伺服器 的 代碼問題 , 每個 socket 的 消息 接收 是否有使用 事件監聽回調的方法呢?

關於 python gevent TCP伺服器的問題,關於 TCPServer , 每個 客戶端 連接到 TCPServer 時 gevent 會自動分配一個 greenlet 執行 ,可是 如何 監聽 每一個 sokcet 客戶端的消息 接收呢, 看到的 代碼是 在 greenlet 方法里 運用 循環 接收,難道沒有 回調嗎? 如圖


gevent 比起其他框架(比如tornado,twisted)的一個巨大優勢就是:用同步的方法(自然沒有回調函數)寫非同步應用,因為同步的方式更接近開發人員的編程思維。

gevent可以用一句話向pythoner闡述:使用多路IO復用對文件描述符的事件監聽,從而撬動協程的「透明」切換。這句話說起來容易,但是闡述起來就複雜些:

  1. 底層(或者說主協程)自然有一個多路IO復用循環(linux上是epoll,unix是kqueue,以下統一用epoll代替描述)
  2. 當處理一個socket鏈接時,就創建一個協程greenlet去處理。
  3. 當socket遇到阻塞的時候,比如等待數據的返回或者發送,此時gevent做了很關鍵的兩步:
    1. 為這個socket的fd在epoll上添加可讀或者可寫事件回調,而這個回調函數便是 gevent.getcurrent().switch
    2. 通過 get_hub().switch() 切換到主協程。切換回主協程,去干其他事情了。但是當該socket可讀或者可寫,epoll自然會調用上述添加的回調函數,從而切換回socket的處理協程,從上次懸掛點接著往下執行。

之所以做到透明,是因為python socket上打了patch。所謂打patch,就是自己實現了一個socket模塊替換了python的標準socket模塊。

def patch_socket():

from gevent import socket

_socket = __import__("socket")

_socket.socket = socket.socket

...

gevent實現的socket模塊,比起python的標準socket模塊,做了以下修改:

  1. 將所有的socket設置成非阻塞。
  2. 修改關鍵函數,比如send,recv,發生阻塞(捕獲到異常 EWOULDBLOCK)時,在socket的fd添加回調函數,並跳回到主協程。

綜上所述,gevent不是沒有事件監聽回調,而是通過給python socket打patch,使其透明化,最終達到讓編程人員用同步的思維去開發。

ps,給python socket打patch,固然好,但是有個小缺點,就是:python c擴展模塊中的socket並不受gevent的管制和調度,因此用在gevent中的網路庫,都盡量使用純python庫


這裡這個循環是驅動一個客戶端的整個會話的循環。整個 echo 都是當有客戶連接到 StreamServer 監聽埠之後,被當作回調,放在一個 greenlet 里執行的。


推薦閱讀:

如何學習 TCP/IP 協議?
在TCP的四次分手當中,被動關閉方是如何知道數據已經接收完了?
DNS解析的過程是什麼,求詳細的?
《tcp/ip詳解》三卷本該怎麼看?
為什麼整個127.*網段都被拿來當做環回地址了?

TAG:Python | TCPIP | gevent |