tarnado源码解析系列一
目錄
- 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.ioloop2 import tornado.web3 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 python2 # -*- coding:utf-8 -*-3 # zhou4 # 2017/6/275 6 # 導入兩個模塊7 import tornado.ioloop8 import tornado.web9 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] = clsfor name, cls in modules.iteritems():try:if issubclass(cls, UIModule):self.ui_modules[name] = clsexcept 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 theorder they were added, and only the first matching pattern isused. This means that all handlers for a given host must beadded 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 regexwill be passed in to the handler's get/post/etc methods asarguments.# 這個不是特別懂,但是意思是RequestHandler的子類將被調用handler_class: RequestHandler subclass to be invoked.kwargs (optional): A dictionary of additional arguments to be passedto the handler's constructor.# 這個handler的名字,是一個額外的參數name (optional): A name for this handler. Used byApplication.reverse_url."""if not pattern.endswith('$'):pattern += '$'self.regex = re.compile(pattern)self.handler_class = handler_classself.kwargs = kwargsself.name = nameself._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 methodwould return ('/%s/%s/', 2)."""# 得到pattern的字符串形式,去掉開頭的^和結尾的$符號pattern = self.regex.patternif pattern.startswith('^'):pattern = pattern[1:]if pattern.endswith('$'):pattern = pattern[:-1]# 如果正常情況下regex.groups的值應該是等于count的,除非特別復雜的url,會返回兩個noneif 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轉換成元組的具體代碼,代碼實現的是把括號里面的內容全部轉換成%spieces = []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 repattern = "/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 autoreloadautoreload.start()?
至此?
application = tornado.web.Application([(r"/", MainHandler), ])就解析完成了,下一篇待續。。。。?
轉載于:https://www.cnblogs.com/huwentao/p/7091935.html
總結
以上是生活随笔為你收集整理的tarnado源码解析系列一的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装完MAVEN后输入mvn -v, 提
- 下一篇: 就是笔记