實踐,用tornado實現自定義協議server

前言

俗話說"光說不練假把式",上一篇文裏都只是光看着別人的源碼說,貌似有點紙上談兵的意思.
因此此次寫一個簡單的,本身定義協議的server.
既能夠熟悉Future和coroutine的用法,又能夠在去除了複雜的http協議後,瞭解tornado的工做原理.
代碼很少,加上空行和import也就200行不到.
在github上的源碼點這裏ios

目標

  • 1.定義一個簡單的協議,達到遠程調用的效果,而且是個長鏈接的協議(相似websock)
  • 2.模仿tornado的框架模式開發這個server框架,讓用戶代碼開發方便,而且支持coroutine
  • 3.爲了省去客戶端的開發,客戶端使用telnet

協議

  • 1.客戶端鏈接成後,以換行符分割每次通訊內容
  • 2.第一次通訊內容是須要執行handler名稱,第二次通訊的內容是該handler的方法名
  • 3.對於客戶端的主動close,需等待此鏈接全部的異步操做完成後才關閉鏈接
    最後運行方式以下圖:

clipboard.png

源碼說明

總覽

由於想讓代碼儘可能少,因此委託模式沒有嚴格按照設計模式的規範寫,直接忽略掉了interface的定義.嚴格來講是須要定義interface和判斷傳入參數的類型的(泛華)
這是類的實例關係圖(也不知道是否是這樣畫...)
clipboard.pnggit

MyServer和MyApplication的實例常駐.一個鏈接進來後就會建立圖中其餘的實例各一個.github

異步說明

  • 1.爲了達到目標中的第一點,須要一個while循環,讀取了客戶端數據後,執行handler,
    當即繼續讀取下一條客戶端數據.直到客戶端關閉操做,引起StreamClosedError才退出循環web

  • 2.爲了達到目標中的第二點,判斷handler的返回值,若是類型是Future則yield處理,由於本方法有@gen.coroutine,因此yield就表明異步操做是在gen.Runner中執行的.編程

  • 3.爲了達到目標中的第三點,須要記錄每個異步操做,而且異步操做完成後移除.當客戶端主動關閉鏈接時,需判斷是否還有future未完成.因此close代碼中給每一個future加上done_callback,用以檢查關閉設計模式

詳情見代碼 MyServerConnection._server_request_loopapp

@gen.coroutine
    def _server_request_loop(self, delegate):
        try:
            #get request adepter
            request_delegate = delegate.on_request(self)
            while True:

                try:
                    message_future = self.stream.read_until_regex(b"\n\r?")
                    message = yield message_future
                    message = self._parse_data(message)

                except (iostream.StreamClosedError, 
                        iostream.UnsatisfiableReadError):
                    app_log.error(' close the connect')
                    self.close()
                    return

                except Exception:

                    gen_log.error("Uncaught exception", exc_info=True)
                    self.close()
                    return

                ret = request_delegate.on_message(message)
                #若是是異步執行的方法,保存future,用於確保close時,全部future都已完成
                if isinstance(ret, Future):
                    ret.add_done_callback(lambda f:self._serving_futures.remove(f))
                    self._serving_futures.append(ret)

        finally:
            delegate.on_close(self)
def close(self):

        def mayby_close(f):
            futures = self._serving_futures+self._pending_writes
            app_log.error(futures)
            if not any(futures):
                self.stream.close()

        pending_futrues = self._serving_futures+self._pending_writes
        if any(pending_futrues):
            map(lambda f:f.add_done_callback(mayby_close),pending_futrues)
        else:
            self.stream.close()

關於@coroutine

其實用@coroutine的時候只須要記住幾點就好了
* 1.被包裝的函數(方法),返回值是Future,
* 2.被包裝的函數走完最後一行代碼後,返回的Futurecallback就會被運行(由於在Runner中引起了StopIteration錯誤,被set_result了)
* 3.被包裝的函數是在gen.Runner中運行的,而Runner是在ioloop(callback那塊)中運行的框架

總結

代碼很是簡單,由於tornado爲咱們提供了異步的庫(tornado真強大,協程好厲害!!),而且是單進程的編程,不須要考慮鎖,寫起來就更輕鬆了.
最後附上程序效果圖
效果異步

廢話

這只是個吃飽撐着的程序,一點實際做用都沒啊(好想被拍死!).吃飽撐着的緣由是我還沒下決心去找工做...工做太難找啦(哭~~)!!!!好想被帶走.................函數

相關文章
相關標籤/搜索