天天看點

雲計算(2)- eventlet 管理Wsgi服務

作者:遊蕩人生ing

背景:

程序:是一個具有一定獨立功能的程式在一個資料集上的一次動态執行的過程,是作業系統進行資源配置設定和排程的一個獨立單元,是應用程式運作的載體。

線程:是程式執行中一個單一的順序控制流程,是程式執行流的最小單元,是處理器排程和配置設定的基本機關。一個程序可以有一個或多個線程,各個線程之間共享程式的記憶體空間。

協程:協程是一種輕量級線程,協程的排程完全由使用者控制。協程運作在單線程中,協程是組織好的代碼流程,協程需要線程來承載運作,線程是協程的資源。線程的任務排程是通過作業系統來執行的,需要消耗CPU的資源,容易産生資源浪費,而協程其任務排程由使用者決定,且值同時存在于一個程序之中,減少了切換的成本

并發:在多線程中,大部分作業系統的任務排程是采用時間片輪轉的搶占式排程方式,就是說一個任務執行一小段時間後強制暫停去執行下一個任務,每個任務輪流執行,直到所有線程的任務。

并行:把任務同時配置設定給不同的CPU,沒有任務切換的過程,叫并行。

雲計算(2)- eventlet 管理Wsgi服務

綠色線程池

概述:

eventlet是具有支援WSGI的異步架構,一方面是用來處理和網絡相關,另一方面通過協程實作并發。select庫中的epoll是預設的網絡通信模型,greetlet庫是其并發的基礎,eventlet庫對其進行封裝之後,就構成了GreenThread。實作并發,非阻塞。

Hub構成了Eventlet的事件循環,它分發I/O事件,排程greenthread。

Eventlet有多種hub的實作,epolls,poll,selects,pyevent。

Hub的實作是線上程内的,eventlet.hubs.use_hub()隻在目前線程内起作用。當使用多線程且這些線程都有自己的hub時,需要在每個需要特定hub的線程函數前調用該函數,也可在主線程調用一次。hub有一個主線程,當運作中的協程需要進行I/O操作時,它會在hub中注冊一個listener,然後切換到MAINLOOP。如果有其他準備運作的協程,MAINLOOP會切換到,當他們完成執行或需要執行I/O時,又會将控制權切換到MAINLOOP,MAINLOOP確定了每一個協程在自己有事要完成的時間都能夠得到排程。

協程:

實作協程的幾種方式:

  • yield
  • greenlet 封裝yield實作
  • gevent 封裝greenlet進行自動切換
  • asyncio(python3.4)
  • async,await (python3.5)

安裝:

pip install -U eventlet
pip install -U https://github.com/eventlet/eventlet/archive/master.zip           

接口解釋:

  • eventlet.listen(bind_addr, family, backlog=backlog)

建立socket,監聽端口。

  • eventlet.wsgi.server() 調用Wsgi服務
eventlet.wsgi.server(
socket,   # Server socker 已經綁定一個端口且監聽中
site,		# 實作WSGI應用的函數
log=None,  日志輸出的位置
environ=None,   添加到每一次請求的environ字典中的額外參量
max_size=None,   允許的最大用戶端連接配接數
max_http_version='HTTP/1.1', 
protocol=<class eventlet.wsgi.HttpProtocol at 0x7f4f68192f58>, 
minimum_chunk_size=None, 
log_x_forwarded_for=True, 
custom_pool=None,   # 一個定制的GreenPool執行個體,用來孵化用戶端的greenthreads。eventlet.GreenPool(pool_size)
keepalive=True, 
log_output=True, 
log_format='%(client_ip)s - - [%(date_time)s] "%(request_line)s"
%(status_code)s %(body_length)s %(wall_seconds).6f', 
url_length_limit=8192,   # 請求URL的最大長度,如果超長,傳回414錯誤。
debug=True, 
socket_timeout=None,   # 用戶端聯機的套接字操作逾時限制
capitalize_response_headers=True)           
  • eventlet.wrap_ssl(dup_socket, **ssl_kwargs)

建立安全的server

  • eventlet.greenpool

class eventlet.greenpool.GreenPool(size=1000)

該類的對象是green threads的池子。

  1. free() 傳回目前對象中可用的greenthreads,如果為0或更少,那麼spawn和spawn_n将會阻塞調用greenthread直到有新的可用的greenthread為止。
  2. resize() 改變目前允許同時工作的greenthreads最大數量。
  3. running() 傳回目前池子中正在執行任務的greenthreads。
  4. spawn() 從目前的池子中孵化一個可用的greenthread,執行函數。可以通過greenthread對象擷取function的傳回值。
  5. spawn_n() 建立一個greenthread來運作function,傳回值為None。
  6. waitall() 等待池子中的所有greenthreads完成工作。
  7. waiting() 傳回目前等待孵化的greenthreads數。
  • GreenPile

pile=eventlet.GreenPile(pool)

pile.spawn_n(func, value) #值可以重用

調用範例

% python
>>> import eventlet
>>> from eventlet.green import urllib2
>>> gt = eventlet.spawn(urllib2.urlopen, 'http://eventlet.net')
>>> gt2 = eventlet.spawn(urllib2.urlopen, 'http://secondlife.com')
>>> gt2.wait()
>>> gt.wait()           
import eventlet
eventlet.monkey_patch()  #必須要加這段代碼,引用下path

with eventlet.Timeout(3, False):    # 等待3秒,如果3秒内第8-9行代碼執行沒有執行完,則跳過
    time.sleep(4)                   # 等待4秒
    print('結果1')                   # 因為等待了4秒,大于逾時時間,是以’結果1‘不列印           

參考資料

https://www.cnpython.com/pypi/eventlet

https://docs.python.org/zh-cn/dev/library/asyncio-task.html