tarnado源碼解析系列一

来源:http://www.cnblogs.com/huwentao/archive/2017/06/29/7091935.html
-Advertisement-
Play Games

目錄 tarnado tarnado源碼安裝 tarnado測試程式 application類的解析 一. tarnado簡介 最近在學習Python,無意間接觸到的tarnado,感覺tarnado還蠻好的那麼tarnado到底什麼呢?tarnado是由Python開發的一個非阻塞式web伺服器框 ...


目錄

  • tarnado
  • tarnado源碼安裝
  • tarnado測試程式
  • application類的解析

 

一. tarnado簡介

  最近在學習Python,無意間接觸到的tarnado,感覺tarnado還蠻好的那麼tarnado到底什麼呢?tarnado是由Python開發的一個非阻塞式web伺服器框架,他與許多主流的web框架有很大的不同(當然其他的web框架我還真的不知道多少),epoll和非阻塞的方式讓他可以每秒數以千計的連接,非常適合與實時的web服務。以下地址為tarnado官方的解釋http://www.tornadoweb.cn/

二. tarnado源碼安裝

  到上面的地址上去下載tornado-1.2.1.tar.gz

  解壓縮之後在cmd命令框中找到此路徑,進行安裝,具體步驟如下:

 註意:經過本人測試,在python3.5上此代碼不能執行,在2.7上面可以執行,因此建議安裝在python2.7上進行測試和學習。

三. 測試程式

  安裝完成之後,打開pycharm, 新建py文件,把下麵測試代碼寫入,執行後,在瀏覽器中輸入http://127.0.0.1:8888會顯示hello, world字樣,就代表這安裝成功。

 1 import tornado.ioloop
 2 import tornado.web
 3 
 4 class MainHandler(tornado.web.RequestHandler):
 5     def get(self):
 6         self.write("Hello, world")
 7 
 8 application = tornado.web.Application([
 9     (r"/", MainHandler),
10 ])
11 
12 if __name__ == "__main__":
13     application.listen(8888)
14     tornado.ioloop.IOLoop.instance().start()
測試代碼

 

四. application類的解析

  下麵終於要進行application的解析了,但是在解析之前,大概說一下關於測試代碼的執行流程。

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 # zhou
 4 # 2017/6/27
 5 
 6 # 導入兩個模塊
 7 import tornado.ioloop
 8 import tornado.web
 9 
10 # 1. 把類RequestHandler載入到記憶體中
11 # 2. 把類RequestHandler做為參數傳入MainHandler中
12 # 3. 把類MainHandler載入到記憶體中
13 # 以上三個步驟實質上都不會坐任何操作,僅僅只是把類裝載到記憶體中以便後續調用
14 class MainHandler(tornado.web.RequestHandler):
15     def get(self):
16         self.write("Hello, world")
17 
18 
19 # 叢這一步驟開始才開始真正的創建對象
20 # 1. 類Application創建了一個對象,名稱為application
21 # 2. r"/" 這個是正則表達式類型的/,也就是我們在瀏覽器中輸入的url
22 # 3. 把類MainHandler作為參數傳遞到application中
23 # 4. 這裡面傳遞的僅僅只是一個變數[]
24 application = tornado.web.Application([
25     (r"/", MainHandler),
26 ])
27 
28 if __name__ == "__main__":
29     
30     # 調用application對象中的listen方法,把8888作為埠號傳遞進去
31     application.listen(8888)
32     tornado.ioloop.IOLoop.instance().start()
測試程式執行流程簡介

  接下來就首先剖析的是下麵這一行代碼

application = tornado.web.Application([
    (r"/", MainHandler),
])

類application的作用:
A collection of request handlers that make up a web application.把許多請求處理器組合起來以實現web應用


  1. application的初始化過程

 1 def __init__(self, handlers=None, default_host="", transforms=None,
 2              wsgi=False, **settings):
 3     if transforms is None:
 4         self.transforms = []
 5         if settings.get("gzip"):
 6             self.transforms.append(GZipContentEncoding)
 7         self.transforms.append(ChunkedTransferEncoding)
 8     else:
 9         self.transforms = transforms
10     self.handlers = []
11     self.named_handlers = {}
12     self.default_host = default_host
13     self.settings = settings
14     self.ui_modules = {}
15     self.ui_methods = {}
16     self._wsgi = wsgi
17     self._load_ui_modules(settings.get("ui_modules", {}))
18     self._load_ui_methods(settings.get("ui_methods", {}))
19     if self.settings.get("static_path"):
20         path = self.settings["static_path"]
21         handlers = list(handlers or [])
22         static_url_prefix = settings.get("static_url_prefix",
23                                          "/static/")
24         handlers = [
25                        (re.escape(static_url_prefix) + r"(.*)", StaticFileHandler,
26                         dict(path=path)),
27                        (r"/(favicon\.ico)", StaticFileHandler, dict(path=path)),
28                        (r"/(robots\.txt)", StaticFileHandler, dict(path=path)),
29                    ] + handlers
30     if handlers: self.add_handlers(".*$", handlers)
31 
32     # Automatically reload modified modules
33     if self.settings.get("debug") and not wsgi:
34         import autoreload
35         autoreload.start()
初始化代碼

  代碼一

    <1>. 就是為對象application封裝了tranforms變數,

    <2>. 如果用戶沒有規定變數的時候,系統預設規定了在伺服器和客戶端之間進行傳輸的過程中要對其進行一定的壓縮,而且要進行一塊一塊的傳輸

###################################################
if transforms is None:
    self.transforms = []
    if settings.get("gzip"):
        self.transforms.append(GZipContentEncoding)
    self.transforms.append(ChunkedTransferEncoding)
else:
    self.transforms = transforms
###################################################

這裡面主要包含了三個類:
    GZipContentEncoding(OutputTransform)        # gzip內容編碼
    ChunkedTransferEncoding(OutputTransform)    # 分塊傳輸編碼
    OutputTransform()                           # 是上面兩個類的父類
        解釋:A transform modifies the result of an HTTP request(e.g., GZip encoding)
        主要是用來對一個http請求的結果進行轉換的,可以是gzip壓縮

 

  代碼二

    <1>. 就是為對象application封裝了一系列的變數,ui_modules和ui_methods這兩個變數暫時還沒有看懂,之後會進行補充。

self.handlers = []
self.named_handlers = {}
self.default_host = default_host
self.settings = settings
self.ui_modules = {}
self.ui_methods = {}
self._wsgi = wsgi

 

  代碼三

    <1>. 主要是為對象application封裝ui的模塊的方法,和上面的ui模塊和方法的區別在哪裡呢,我認為應該是自己定義的和系統預設給出的模塊和方法。

self._load_ui_modules(settings.get("ui_modules", {}))
self._load_ui_methods(settings.get("ui_methods", {}))

  他主要調用了兩個方法,在此僅僅對第一個方法進行簡單的描述(_load_ui_modules)

  因為第二個方法和這個modules是一樣的

 1 def _load_ui_modules(self, modules):
 2     if type(modules) is types.ModuleType:
 3         self._load_ui_modules(dict((n, getattr(modules, n))
 4                                    for n in dir(modules)))
 5     elif isinstance(modules, list):
 6         for m in modules: self._load_ui_modules(m)
 7     else:
 8         assert isinstance(modules, dict)
 9         for name, cls in modules.iteritems():
10             try:
11                 if issubclass(cls, UIModule):
12                     self.ui_modules[name] = cls
13             except TypeError:
14                 pass
_load_ui_modules源代碼

  對於上面源代碼解析

# 把傳入的模塊modules全部變成字典的形式封裝到ui_modules變數中
def _load_ui_modules(self, modules):
    # types是一個.py文件,他主要是為了定義一些簡單的函數,類似於內置函數可以直接拿來使用的
    # types裡面關於ModuleType的描述是:ModuleType = type(sys) 也就是sys的類型<type 'module'>
    # 這裡其實就是為了判斷傳出的modules是不是一個模塊的類型,如果是就把它變成一個字典形式遞歸判斷
    if type(modules) is types.ModuleType:
        self._load_ui_modules(dict((n, getattr(modules, n))
                                   for n in dir(modules)))
    #判斷modules是不是一個列表,如果是列表,就把列表裡面的元素重新代入方法中進行調用                             
    elif isinstance(modules, list):
        for m in modules: self._load_ui_modules(m)
    else:
        # 此處是一個斷言機制,也就是說已經肯定了modules一定是一個字典形式的樣子
        assert isinstance(modules, dict)
        # 因為modules是一個字典,所以就把鍵和值分別賦值給name和cls,然後判斷每一個鍵的值cls是不是UIModule的一個子類,如果是
        # 就把這個值添加到前面封裝的一個變數中self.ui_modules[name] = cls
        for name, cls in modules.iteritems():
            try:
                if issubclass(cls, UIModule):
                    self.ui_modules[name] = cls
            except TypeError:
                pass

 

  代碼四

   <1>. 它定義了一系列的變數,最重要的變數是handler,  其中又引出了一個類StaticFileHandler而這個類又是繼承了RequestHandler,因為此處並沒有創建任何關於這個類的對象,所以此處不再深究等真正調用時候在來關註。

   但是從條件語句中,我們就可以看出來,當setting中不含static的時候,並不會去創建這些變數,這一點是要註意的。

# 定義了一系列的變數如handlers,path,static_url_prefix   
# 當settings中包含了static_path這個鍵的時候,才會去定義這些變數         
if self.settings.get("static_path"):
    path = self.settings["static_path"]
    handlers = list(handlers or [])
    static_url_prefix = settings.get("static_url_prefix",
                                     "/static/")
    handlers = [
        (re.escape(static_url_prefix) + r"(.*)", StaticFileHandler,
         dict(path=path)),
        (r"/(favicon\.ico)", StaticFileHandler, dict(path=path)),
        (r"/(robots\.txt)", StaticFileHandler, dict(path=path)),
    ] + handlers  

 

  代碼五

    <1>. 添加給定的處理器到系統的處理器列表中。(其實這樣說可能不太準確, 因為我們從代碼四就可以看出來,如果我們給定的url包含了static_path,那麼給定的處理器無論如何都會發生改變)

if handlers: self.add_handlers(".*$", handlers)

  代碼六  

    add_handles函數的解析

def add_handlers(self, host_pattern, host_handlers):
    # 添加給定的處理器到系統的處理器列表中,註意主機模式是按順序進行處理的,直到第一個被匹配到的這就意味著所有給定主機的處理器必須被添加到處理器中
    """Appends the given handlers to our handler list.

    Note that host patterns are processed sequentially in the
    order they were added, and only the first matching pattern is
    used.  This means that all handlers for a given host must be
    added in a single add_handlers call.
    """
    # 如果給定主機模式不是以"$"結尾的,就添加$到結尾
    if not host_pattern.endswith("$"):
        host_pattern += "$"
    handlers = []
    # The handlers with the wildcard host_pattern are a special
    # case - they're added in the constructor but should have lower
    # precedence than the more-precise handlers added later.
    # If a wildcard handler group exists, it should always be last
    # in the list, so insert new groups just before it.
    # 帶有通配符的handlers是一個特殊情況,他們本來在構造方法就已經被添加了,但是他們的優先順序卻低於一些重要的處理器,因此應該在之後被添加
    # 所以如果帶有通配符的處理器組存在,就應該把他們放在一個列表的最後面,否則就插在他的前面
    # 下麵這段代碼就是這個意思,如果他的pattern是'.*$'開頭的,代表他是沒有通配符的,所以就把他插入最後一個的前面,否則有通配符的就直接添加到後面
    if self.handlers and self.handlers[-1][0].pattern == '.*$':
        self.handlers.insert(-1, (re.compile(host_pattern), handlers))
    else:
        self.handlers.append((re.compile(host_pattern), handlers))
    # 這個是對我們傳入的host_handlers進行一個解析,把第一個採納數給pattern,第二個給handler如果有三個,就賦值給kwargs如果沒有第三個kwargs=={}
    for spec in host_handlers:
        if type(spec) is type(()):
            assert len(spec) in (2, 3)
            pattern = spec[0]
            handler = spec[1]
            if len(spec) == 3:
                kwargs = spec[2]
            else:
                kwargs = {}
            # 賦值完成之後就把這些參數封裝到類URLSpec中
            spec = URLSpec(pattern, handler, kwargs)
        # 類URLSpec創建了對象spec之後,會重新給self.named_handlers添加一個handlers的鍵值對,如果鍵值本身就存在,就會往日誌裡面寫入警告信息
        handlers.append(spec)
        if spec.name:
            if spec.name in self.named_handlers:
                logging.warning(
                    "Multiple handlers named %s; replacing previous value",
                    spec.name)
            self.named_handlers[spec.name] = spec

 

  

  代碼七

  類URLSpec的解析

  在代碼六中創建了一個spec對象,用的類URLSpec創建的

class URLSpec(object):
    # 這個類的作用主要是在url和handlers之間做一個特定的映射,主要的體現應該就是前面的變數name_handlers
    # 前面的賦值語句:self.named_handlers[spec.name] = spec
    
    """Specifies mappings between URLs and handlers."""
    def __init__(self, pattern, handler_class, kwargs={}, name=None):
        """Creates a URLSpec.

        Parameters:
        # 傳遞進來得主機模式
        pattern: Regular expression to be matched.  Any groups in the regex
            will be passed in to the handler's get/post/etc methods as
            arguments.
            
        # 這個不是特別懂,但是意思是RequestHandler的子類將被調用
        handler_class: RequestHandler subclass to be invoked.
        kwargs (optional): A dictionary of additional arguments to be passed
            to the handler's constructor.
            
        # 這個handler的名字,是一個額外的參數
        name (optional): A name for this handler.  Used by
            Application.reverse_url.
        """
        if not pattern.endswith('$'):
            pattern += '$'
        self.regex = re.compile(pattern)
        self.handler_class = handler_class
        self.kwargs = kwargs
        self.name = name
        self._path, self._group_count = self._find_groups()

 

  代碼八

  方法self._find_groups() 

  這個方法比較有意思,後面會帶一個例子來解釋一下

def _find_groups(self):
    # 就是給特定的url返回一個元組,下麵的就是例子,括弧裡面的內容都會轉換成%s,後面的2代表小括弧括弧的個數
    """Returns a tuple (reverse string, group count) for a url.

    For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method
    would return ('/%s/%s/', 2).
    """
    # 得到pattern的字元串形式,去掉開頭的^和結尾的$符號
    pattern = self.regex.pattern
    if pattern.startswith('^'):
        pattern = pattern[1:]
    if pattern.endswith('$'):
        pattern = pattern[:-1]

    # 如果正常情況下regex.groups的值應該是等於count的,除非特別複雜的url,會返回兩個none
    if self.regex.groups != pattern.count('('):
        # The pattern is too complicated for our simplistic matching,
        # so we can't support reversing it.
        return (None, None)
        
    # 這個就是把url轉換成元組的具體代碼,代碼實現的是把括弧裡面的內容全部轉換成%s
    pieces = []
    for fragment in pattern.split('('):
        if ')' in fragment:
            paren_loc = fragment.index(')')
            if paren_loc >= 0:
                pieces.append('%s' + fragment[paren_loc + 1:])
        else:
            pieces.append(fragment)
    
    # 把picese重新拼接成字元,返回回去
    return (''.join(pieces), self.regex.groups)

 

  事例:

import re

pattern = "/abcd123([0-9]{4})/lwjeg([a-z-]+)/"
regex = re.compile(pattern)
pieces = []
print(pattern.split('('))
for fragment in pattern.split('('):
    if ')' in fragment:
        # 找到‘)’的位置
        paren_loc = fragment.index(')')
        if paren_loc >= 0:
            # 把')'之後的所有內容拼接起來
            pieces.append('%s' + fragment[paren_loc + 1:])
    else:
        pieces.append(fragment)
print(pieces)

結果:
['/abcd123', '[0-9]{4})/lwjeg', '[a-z-]+)/']
['/abcd123', '%s/lwjeg', '%s/']
事例

   代碼九 

# 自動的去重載改變的模塊,這個調用的是autorelaad模塊實現的    
# Automatically reload modified modules
if self.settings.get("debug") and not wsgi:
    import autoreload
    autoreload.start()  

 

  至此 

application = tornado.web.Application([
(r"/", MainHandler),
])就解析完成了,下一篇待續。。。。

 



  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Python中的可變對象和不可變對象 什麼是可變/不可變對象 不可變對象,該對象所指向的記憶體中的值不能被改變。 當改變某個變數時候,由於其所指的值不能被改變,相當於把原來的值複製一份後再改變,這會開闢一個新的地址,變數再指向這個新的地址。 可變對象,該對象所指向的記憶體中的值可以被改變。 變數(準確的 ...
  • /* 選擇工廠和更新工廠模式,這個模式的類(UpdateFactory和SelectionFactory類)就是用來創建SQL語句的. 因為涉及到之前學習的內容比較多,這裡就儘量將之前相關模式的示例代碼放在一起來進行學習和回顧了。 以下的代碼都是代碼片段而且涉及到連接資料庫,無法進行整體的調試(某些... ...
  • 動態規劃的演算法題往往都是各大公司筆試題的常客。在不少演算法類的微信公眾號中,關於“動態規劃”的文章屢見不鮮,都在試圖用最淺顯易懂的文字來描述講解動態規劃,甚至有的用漫畫來解釋,認真讀每一篇公眾號推送的文章實際上都能讀得懂,都能對動態規劃有一個大概瞭解。 什麼是動態規劃?通俗地理解來說,一個問題的解決辦 ...
  • 這是我寫的登陸註冊界面,使用tkinter,可以實現簡單的登陸和註冊賬號,使用的主要是Label,Entry和Button組件。 ...
  • 一、JavaCC JavaCC是java的compiler compiler。JavaCC是LL解析器生成器,可處理的語法範圍比較狹窄,但支持無限長的token超前掃描。 安裝過程: 我是從github上down下來的zip壓縮包,然後安裝了下ant, 然後通過ant安裝的javacc 1. 首先下 ...
  • 首先辨析“/”與“\” window中的路徑一般用“\”; java中的路徑一般用“/”;如果用“\”需要對其轉義成“\\” 1、絕對路徑 以根目錄作為參考點的的文件或文件夾所在的路徑,是硬碟上的真實路徑。具有唯一性的特點。 例如:C:\caosiege\python\project\C.py,代表 ...
  • 題目描述 設G為有n個頂點的有向無環圖,G中各頂點的編號為1到n,且當為G中的一條邊時有i < j。設w(i,j)為邊的長度,請設計演算法,計算圖G中<1,n>間的最長路徑。 輸入輸出格式 輸入格式: 輸入文件longest.in的第一行有兩個整數n和m,表示有n個頂點和m條邊,接下來m行中每行輸入3 ...
  • 時間限制: 1 s 空間限制: 128000 KB 題目等級 : 鑽石 Diamond 題解 查看運行結果 時間限制: 1 s 空間限制: 128000 KB 題目等級 : 鑽石 Diamond 時間限制: 1 s 空間限制: 128000 KB 題目等級 : 鑽石 Diamond 時間限制: 1 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...