Python面試-websocket及web框架


 

一、Websocket

    1. websocket概念

    在講websocket之前,我們先來看看ajax輪詢和long poll的實現機制。

    A.  ajax輪詢

    ajax輪詢的原理非常簡單,讓瀏覽器隔個幾秒就發送一次請求,詢問服務器是否有新信息。

    場景再現:

        客戶端:啦啦啦,有沒有新信息(Request)

        服務端:沒有(Response

        客戶端:啦啦啦,有沒有新信息(Request)

        服務端:沒有。。(Response

        客戶端:啦啦啦,有沒有新信息(Request)

        服務端:你好煩啊,沒有啊。。(Response

        客戶端:啦啦啦,有沒有新消息(Request

        服務端:好啦好啦,有啦給你。(Response

        客戶端:啦啦啦,有沒有新消息(Request

        服務端:。。。。。沒。。。。沒。。。沒有(Response—- loop

 

   B.  long poll

   long poll 其實原理跟 ajax輪詢 差不多,都是采用輪詢的方式,不過采取的是阻塞模型(一直打電話,沒收到就不掛電話),也就是說,客戶端發起連接后,如果沒消息,就一直不返回Response給客戶端。直到有消息才返回,返回完之后,客戶端再次建立連接,周而復始。

    場景再現:

   客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request

         服務端:額。。 等待到有消息的時候。。來 給你(Response

   客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request-loop

   

    從上面可以看出其實這兩種方式,都是在不斷地建立HTTP連接,然后等待服務端處理,可以體現HTTP協議的另外一個特點,被動性。

    何為被動性呢,其實就是,服務端不能主動聯系客戶端,只能有客戶端發起。簡單地說就是,服務器是一個很懶的冰箱(不會、不能主動發起連接),但是上司有命令,如果有客戶來,不管多么累都要好好接待。

    說完這個,我們再來說一說上面的缺陷,從上面很容易看出來,不管怎么樣,上面這兩種都是非常消耗資源的。

   ajax輪詢 需要服務器有很快的處理速度和資源。(速度)long poll 需要有很高的並發,也就是說同時接待客戶的能力。(場地大小)

    所以 ajax輪詢  long poll 都有可能發生這種情況。

  客戶端:啦啦啦啦,有新信息么?

  服務端:正忙,請稍后再試(503 Server Unavailable

  客戶端:。。。。好吧,啦啦啦,有新信息么?

  服務端:正忙,請稍后再試(503 Server Unavailable

   

   c. websocket方式

 通過上面這個例子,我們可以看出,這兩種方式都不是最好的方式,需要很多資源。一種需要更快的速度,一種需要更多的電話。這兩種都會導致電話的需求越來越高。

     哦對了,忘記說了HTTP還是一個無狀態協議。通俗的說就是,服務器因為每天要接待太多客戶了,是個健忘鬼,你一掛電話,他就把你的東西全忘光了,把你的東西全丟掉了。你第二次還得再告訴服務器一遍。

    所以在這種情況下出現了,Websocket出現了。他解決了HTTP的這幾個難題。首先,被動性,當服務器完成協議升級后(HTTP->Websocket),服務端就可以主動推送信息給客戶端啦。所以上面的情景可以做如下修改。

  客戶端:啦啦啦,我要建立Websocket協議,需要的服務:chatWebsocket協議版本:17HTTP Request

  服務端:ok,確認,已升級為Websocket協議(HTTP Protocols Switched

  客戶端:麻煩你有信息的時候推送給我噢。。

  服務端:ok,有的時候會告訴你的。

  服務端:balabalabalabala

  服務端:balabalabalabala

  服務端:哈哈哈哈哈啊哈哈哈哈

  服務端:笑死我了哈哈哈哈哈哈哈

    就變成了這樣,只需要經過一次HTTP請求,就可以做到源源不斷的信息傳送了。(在程序設計中,這種設計叫做回調,即:你有信息了再來通知我,而不是我傻乎乎的每次跑來問你 

    這樣的協議解決了上面同步有延遲,而且還非常消耗資源的這種情況

     

 

   2. websocket 使用

   服務端:

 

   客戶端:

 

二、IO多路復用

    1. 什么是多路復用

    設想一個場景:當我們要編寫一個echo服務器程序的時候,需要對用戶從標准輸入鍵入的交互命令做出響應。在這種情況下,服務器必須響應兩個相互獨立的I/O事件:

    1)網絡客戶端發起網絡連接請求;

    2)用戶在鍵盤上鍵入命令行。我們先等待哪個事件呢?沒有哪個選擇是理想的。如果在acceptor中等待一個連接請求,我們就不能響應輸入的命令。類似地,如果在read中等待一個輸入命令,我們就不能響應任何連接請求。針對這種困境的一個解決辦法就是I/O多路復用技術

   I/O多路復用,I/O就是指的我們網絡I/O,多路指多個TCP連接(或多個Channel),復用指復用一個或少量線程。串起來理解就是很多個網絡I/O復用一個或少量的線程來處理這些連接。

 

   2. select

   I/O多路復用這個概念被提出來以后, select是第一個實現 (1983 左右在BSD里面實現的)

   select 被實現以后,很快就暴露出了很多問題。

     select 會修改傳入的參數數組,這個對於一個需要調用很多次的函數,是非常不友好的。

     select 如果任何一個sock(I/O stream)出現了數據,select 僅僅會返回,但是並不會告訴你是那個sock上有數據,於是你只能自己一個一個的找,10幾個sock可能還好,要是幾萬的sock每次都找一遍,這個無謂的開銷就頗有海天盛筵的豪氣了。

     select 只能監視1024個鏈接, 這個跟草榴沒啥關系哦,linux 定義在頭文件中的,參見FD_SETSIZE

     select 不是線程安全的,如果你把一個sock加入到select, 然后突然另外一個線程發現,尼瑪,這個sock不用,要收回。對不起,這個select 不支持的,如果你喪心病狂的竟然關掉這個sock, select的標准行為是。。呃。。不可預測的, 這個可是寫在文檔中的哦.

    “If a file descriptor being monitored by select() is closed in another thread, the result is unspecified”
    霸不霸氣了,不過然后你就有了多進程的各種問題。

 

    2. poll

    於是14年以后(1997年)一幫人又實現了poll, poll 修復了select的很多問題,比如

    poll 去掉了1024個鏈接的限制,於是要多少鏈接呢, 主人你開心就好。

    poll 從設計上來說,不再修改傳入數組,不過這個要看你的平台了,所以行走江湖,還是小心為妙。

    其實拖14年那么久也不是效率問題, 而是那個時代的硬件實在太弱,一台服務器處理1千多個鏈接簡直就是神一樣的存在了,select很長段時間已經滿足需求。

    但是poll仍然不是線程安全的, 這就意味着,不管服務器有多強悍,你也只能在一個線程里面處理一組I/O流。你當然可以那多進程來配合

 

 3. epoll

 5年以后, 2002, 大神 Davide Libenzi 實現了epoll.

   epoll 可以說是I/O 多路復用最新的一個實現,epoll 修復了poll select絕大部分問題, 比如:

   epoll 現在是線程安全的。

   epoll 現在不僅告訴你sock組里面數據,還會告訴你具體哪個sock有數據,你不用自己去找了。可是epoll 有個致命的缺點。。只有linux支持。比如BSD上面對應的實現是kqueue

    而ngnix 的設計原則里面, 它會使用目標平台上面最高效的I/O多路復用模型咯,所以才會有這個設置。一般情況下,如果可能的話,盡量都用epoll/kqueue吧。

 

三、web服務器框架

    1. 同步異步、阻塞非阻塞

    同步和異步針對應用程序來,關注的是程序中間的協作關系;阻塞與非阻塞更關注的是單個進程的執行狀態。

    同步:執行一個操作之后,等待結果,然后才繼續執行后續的操作。

    異步:執行一個操作后,可以去執行其他的操作,然后等待通知再回來執行剛才沒執行完的操作。

    阻塞:進程給CPU傳達一個任務之后,一直等待CPU處理完成,然后才執行后面的操作。

    非阻塞:進程給CPU傳達任我后,繼續處理后續的操作,隔斷時間再來詢問之前的操作是否完成。這樣的過程其實也叫輪詢。

  老張愛喝茶,廢話不說,煮開水。
  出場人物:老張,水壺兩把(普通水壺,簡稱水壺;會響的水壺,簡稱響水壺)。
  1). 老張把水壺放到火上,立等水開。(同步阻塞)
    老張覺得自己有點傻
  2). 老張把水壺放到火上,去客廳看電視,時不時去廚房看看水開沒有。(同步非阻塞)
    老張還是覺得自己有點傻,於是變高端了,買了把會響笛的那種水壺。水開之后,能大聲發出嘀~~~~的噪音。
  3). 老張把響水壺放到火上,立等水開。(異步阻塞)
    老張覺得這樣傻等意義不大
  4). 老張把響水壺放到火上,去客廳看電視,水壺響之前不再去看它了,響了再去拿壺。(異步非阻塞)
    老張覺得自己聰明了。

 

     所謂同步異步,只是對於水壺而言。
    普通水壺,同步;響水壺,異步。
    雖然都能干活,但響水壺可以在自己完工之后,提示老張水開了。這是普通水壺所不能及的。
    同步只能讓調用者去輪詢自己(情況2中),造成老張效率的低下。

    所謂阻塞非阻塞,僅僅對於老張而言。
    傻等的老張,阻塞;看電視的老張,非阻塞。
   情況1和情況3中老張就是阻塞的,媳婦喊他都不知道。雖然3中響水壺是異步的,可對於立等的老張沒有太大的意義。所以一般異步是配合非阻塞使用的,這樣才能發揮異步的效用。

 2. django, flask, tornado

     在Pythonweb開發框架中,目前使用量最高的有DjangoFlaskTornado, 經常會有人拿這幾個對比,相信大家的初步印象應該是 Django大而全、Flask小而精、Tornado性能高。
    DjangoPython 中最全能的 web 開發框架,走大而全的方向。它最出名的是其全自動化的管理后台:只需要使用起ORM,做簡單的對象定義,它就能自動生成數據庫結構、以及全功能的管理后台。不過Django提供的方便,也意味着Django內置的ORM跟框架內的其他模塊耦合程度高,深度綁定了該框架,應用程序必須使用Django內置的ORM,否則就不能享受到框架內提供的種種基於其ORM的優秀特性。


    Tornado全稱Tornado Web Server,是一個用Python語言寫成的Web服務器兼Web應用框架。Tornado走的是少而精的方向,注重的是性能優越,它最出名的是異步非阻塞的服務器方式。(Tornado框架和服務器一起組成一個WSGI的全棧替代品。單獨在WSGI容器中使用tornado web框架或者tornaod http服務器,有一定的局限性,為了最大化的利用tornado的性能,推薦同時使用tornaodweb框架和HTTP服務器。)


    Flask是一個使用 Python 編寫的輕量級 Web 應用框架,也被稱為 “microframework”,語法簡單,部署很方便,整個框架自帶了路徑映射、模板引擎(Jinja2)、簡單的數據庫訪問等web框架組件,支持WSGI協議(采用 Werkzeug)。Flask使用 BSD 授權。 Flask使用簡單的核心,用 extension 增加其他功能,雖然沒有默認使用的數據庫、窗體驗證工具,然而Flask保留了擴增的彈性,可以用Flask-extension加入ORM、窗體驗證工具、文件上傳、各種開放式身份驗證技術這些功能。


    從性能上看Tornado DjangoFlask等主流 Web 服務器框架相比有着明顯的區別:它是非阻塞式服務器,速度相當快。然而 Tornado 相比 Django Flask屬於較為原始的框架,插件少,許多內容需要自己去處理。而Flask插件多,文檔非常專業,有專門的公司團隊維護,對於快速開發很有效率。由於WSGI協議的存在,可以結合 Tornado 的服務器異步特性、並發處理能力和Flask的文檔和擴展能力為一體。雖然像DjangoFlask框架都有自己實現的簡單的WSGI服務器,但一般用於服務器調試,生產環境下建議用其他WSGI服務器,比如Nginx+uwsgi+Django方式。

 

   3. Sanic

   sanic是基於PythonSanic 是一個和類Flask 的基於Python3.5+web框架,它編寫的代碼速度特別快。

除了像Flask 以外,Sanic 還支持以異步請求的方式處理請求。這意味着你可以使用新的 async/await 語法,編寫非阻塞的快速的代碼。

下圖是一張各類web框架性能測試對比(僅供參考,實際情況不同任務及及硬件配置不同)

 


免責聲明!

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



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