目錄 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),
])就解析完成了,下一篇待續。。。。