tarnado源码解析系列一
目錄
- tarnado
- tarnado源碼安裝
- tarnado測試程序
- application類的解析
?
一. tarnado簡介
最近在學(xué)習(xí)Python,無意間接觸到的tarnado,感覺tarnado還蠻好的那么tarnado到底什么呢?tarnado是由Python開發(fā)的一個(gè)非阻塞式web服務(wù)器框架,他與許多主流的web框架有很大的不同(當(dāng)然其他的web框架我還真的不知道多少),epoll和非阻塞的方式讓他可以每秒數(shù)以千計(jì)的連接,非常適合與實(shí)時(shí)的web服務(wù)。以下地址為tarnado官方的解釋http://www.tornadoweb.cn/
二. tarnado源碼安裝
到上面的地址上去下載tornado-1.2.1.tar.gz
解壓縮之后在cmd命令框中找到此路徑,進(jìn)行安裝,具體步驟如下:
?注意:經(jīng)過本人測試,在python3.5上此代碼不能執(zhí)行,在2.7上面可以執(zhí)行,因此建議安裝在python2.7上進(jìn)行測試和學(xué)習(xí)。
三. 測試程序
安裝完成之后,打開pycharm, 新建py文件,把下面測試代碼寫入,執(zhí)行后,在瀏覽器中輸入http://127.0.0.1:8888會(huì)顯示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類的解析
下面終于要進(jìn)行application的解析了,但是在解析之前,大概說一下關(guān)于測試代碼的執(zhí)行流程。
1 #!/usr/bin/env python2 # -*- coding:utf-8 -*-3 # zhou4 # 2017/6/275 6 # 導(dǎo)入兩個(gè)模塊7 import tornado.ioloop8 import tornado.web9 10 # 1. 把類RequestHandler載入到內(nèi)存中 11 # 2. 把類RequestHandler做為參數(shù)傳入MainHandler中 12 # 3. 把類MainHandler載入到內(nèi)存中 13 # 以上三個(gè)步驟實(shí)質(zhì)上都不會(huì)坐任何操作,僅僅只是把類裝載到內(nèi)存中以便后續(xù)調(diào)用 14 class MainHandler(tornado.web.RequestHandler): 15 def get(self): 16 self.write("Hello, world") 17 18 19 # 叢這一步驟開始才開始真正的創(chuàng)建對(duì)象 20 # 1. 類Application創(chuàng)建了一個(gè)對(duì)象,名稱為application 21 # 2. r"/" 這個(gè)是正則表達(dá)式類型的/,也就是我們?cè)跒g覽器中輸入的url 22 # 3. 把類MainHandler作為參數(shù)傳遞到application中 23 # 4. 這里面?zhèn)鬟f的僅僅只是一個(gè)變量[] 24 application = tornado.web.Application([ 25 (r"/", MainHandler), 26 ]) 27 28 if __name__ == "__main__": 29 30 # 調(diào)用application對(duì)象中的listen方法,把8888作為端口號(hào)傳遞進(jìn)去 31 application.listen(8888) 32 tornado.ioloop.IOLoop.instance().start()測試程序執(zhí)行流程簡介
接下來就首先剖析的是下面這一行代碼
application = tornado.web.Application([(r"/", MainHandler), ])類application的作用:A collection of request handlers that make up a web application.把許多請(qǐng)求處理器組合起來以實(shí)現(xiàn)web應(yīng)用
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>. 就是為對(duì)象application封裝了tranforms變量,
<2>. 如果用戶沒有規(guī)定變量的時(shí)候,系統(tǒng)默認(rèn)規(guī)定了在服務(wù)器和客戶端之間進(jìn)行傳輸?shù)倪^程中要對(duì)其進(jìn)行一定的壓縮,而且要進(jìn)行一塊一塊的傳輸
################################################### if transforms is None:self.transforms = []if settings.get("gzip"):self.transforms.append(GZipContentEncoding)self.transforms.append(ChunkedTransferEncoding) else:self.transforms = transforms ###################################################這里面主要包含了三個(gè)類:GZipContentEncoding(OutputTransform) # gzip內(nèi)容編碼ChunkedTransferEncoding(OutputTransform) # 分塊傳輸編碼OutputTransform() # 是上面兩個(gè)類的父類解釋:A transform modifies the result of an HTTP request(e.g., GZip encoding)主要是用來對(duì)一個(gè)http請(qǐng)求的結(jié)果進(jìn)行轉(zhuǎn)換的,可以是gzip壓縮?
代碼二
<1>. 就是為對(duì)象application封裝了一系列的變量,ui_modules和ui_methods這兩個(gè)變量暫時(shí)還沒有看懂,之后會(huì)進(jìn)行補(bǔ)充。
self.handlers = [] self.named_handlers = {} self.default_host = default_host self.settings = settings self.ui_modules = {} self.ui_methods = {} self._wsgi = wsgi?
代碼三
<1>. 主要是為對(duì)象application封裝ui的模塊的方法,和上面的ui模塊和方法的區(qū)別在哪里呢,我認(rèn)為應(yīng)該是自己定義的和系統(tǒng)默認(rèn)給出的模塊和方法。
self._load_ui_modules(settings.get("ui_modules", {})) self._load_ui_methods(settings.get("ui_methods", {}))他主要調(diào)用了兩個(gè)方法,在此僅僅對(duì)第一個(gè)方法進(jìn)行簡單的描述(_load_ui_modules)
因?yàn)榈诙€(gè)方法和這個(gè)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源代碼
對(duì)于上面源代碼解析
# 把傳入的模塊modules全部變成字典的形式封裝到ui_modules變量中 def _load_ui_modules(self, modules):# types是一個(gè).py文件,他主要是為了定義一些簡單的函數(shù),類似于內(nèi)置函數(shù)可以直接拿來使用的# types里面關(guān)于ModuleType的描述是:ModuleType = type(sys) 也就是sys的類型<type 'module'># 這里其實(shí)就是為了判斷傳出的modules是不是一個(gè)模塊的類型,如果是就把它變成一個(gè)字典形式遞歸判斷if type(modules) is types.ModuleType:self._load_ui_modules(dict((n, getattr(modules, n))for n in dir(modules)))#判斷modules是不是一個(gè)列表,如果是列表,就把列表里面的元素重新代入方法中進(jìn)行調(diào)用 elif isinstance(modules, list):for m in modules: self._load_ui_modules(m)else:# 此處是一個(gè)斷言機(jī)制,也就是說已經(jīng)肯定了modules一定是一個(gè)字典形式的樣子assert isinstance(modules, dict)# 因?yàn)閙odules是一個(gè)字典,所以就把鍵和值分別賦值給name和cls,然后判斷每一個(gè)鍵的值cls是不是UIModule的一個(gè)子類,如果是# 就把這個(gè)值添加到前面封裝的一個(gè)變量中self.ui_modules[name] = clsfor name, cls in modules.iteritems():try:if issubclass(cls, UIModule):self.ui_modules[name] = clsexcept TypeError:pass代碼四
<1>. 它定義了一系列的變量,最重要的變量是handler, ?其中又引出了一個(gè)類StaticFileHandler而這個(gè)類又是繼承了RequestHandler,因?yàn)榇颂幉]有創(chuàng)建任何關(guān)于這個(gè)類的對(duì)象,所以此處不再深究等真正調(diào)用時(shí)候在來關(guān)注。
但是從條件語句中,我們就可以看出來,當(dāng)setting中不含static的時(shí)候,并不會(huì)去創(chuàng)建這些變量,這一點(diǎn)是要注意的。
# 定義了一系列的變量如handlers,path,static_url_prefix # 當(dāng)settings中包含了static_path這個(gè)鍵的時(shí)候,才會(huì)去定義這些變量 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>. 添加給定的處理器到系統(tǒng)的處理器列表中。(其實(shí)這樣說可能不太準(zhǔn)確, 因?yàn)槲覀儚拇a四就可以看出來,如果我們給定的url包含了static_path,那么給定的處理器無論如何都會(huì)發(fā)生改變)
if handlers: self.add_handlers(".*$", handlers)代碼六
add_handles函數(shù)的解析
def add_handlers(self, host_pattern, host_handlers):# 添加給定的處理器到系統(tǒng)的處理器列表中,注意主機(jī)模式是按順序進(jìn)行處理的,直到第一個(gè)被匹配到的這就意味著所有給定主機(jī)的處理器必須被添加到處理器中"""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."""# 如果給定主機(jī)模式不是以"$"結(jié)尾的,就添加$到結(jié)尾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是一個(gè)特殊情況,他們本來在構(gòu)造方法就已經(jīng)被添加了,但是他們的優(yōu)先級(jí)卻低于一些重要的處理器,因此應(yīng)該在之后被添加# 所以如果帶有通配符的處理器組存在,就應(yīng)該把他們放在一個(gè)列表的最后面,否則就插在他的前面# 下面這段代碼就是這個(gè)意思,如果他的pattern是'.*$'開頭的,代表他是沒有通配符的,所以就把他插入最后一個(gè)的前面,否則有通配符的就直接添加到后面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))# 這個(gè)是對(duì)我們傳入的host_handlers進(jìn)行一個(gè)解析,把第一個(gè)采納數(shù)給pattern,第二個(gè)給handler如果有三個(gè),就賦值給kwargs如果沒有第三個(gè)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 = {}# 賦值完成之后就把這些參數(shù)封裝到類URLSpec中spec = URLSpec(pattern, handler, kwargs)# 類URLSpec創(chuàng)建了對(duì)象spec之后,會(huì)重新給self.named_handlers添加一個(gè)handlers的鍵值對(duì),如果鍵值本身就存在,就會(huì)往日志里面寫入警告信息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的解析
在代碼六中創(chuàng)建了一個(gè)spec對(duì)象,用的類URLSpec創(chuàng)建的
class URLSpec(object):# 這個(gè)類的作用主要是在url和handlers之間做一個(gè)特定的映射,主要的體現(xiàn)應(yīng)該就是前面的變量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:# 傳遞進(jìn)來得主機(jī)模式pattern: Regular expression to be matched. Any groups in the regexwill be passed in to the handler's get/post/etc methods asarguments.# 這個(gè)不是特別懂,但是意思是RequestHandler的子類將被調(diào)用handler_class: RequestHandler subclass to be invoked.kwargs (optional): A dictionary of additional arguments to be passedto the handler's constructor.# 這個(gè)handler的名字,是一個(gè)額外的參數(shù)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()?
這個(gè)方法比較有意思,后面會(huì)帶一個(gè)例子來解釋一下
def _find_groups(self):# 就是給特定的url返回一個(gè)元組,下面的就是例子,括號(hào)里面的內(nèi)容都會(huì)轉(zhuǎn)換成%s,后面的2代表小括號(hào)括號(hào)的個(gè)數(shù)"""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的字符串形式,去掉開頭的^和結(jié)尾的$符號(hào)pattern = self.regex.patternif pattern.startswith('^'):pattern = pattern[1:]if pattern.endswith('$'):pattern = pattern[:-1]# 如果正常情況下regex.groups的值應(yīng)該是等于count的,除非特別復(fù)雜的url,會(huì)返回兩個(gè)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)# 這個(gè)就是把url轉(zhuǎn)換成元組的具體代碼,代碼實(shí)現(xiàn)的是把括號(hào)里面的內(nèi)容全部轉(zhuǎn)換成%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:# 把')'之后的所有內(nèi)容拼接起來pieces.append('%s' + fragment[paren_loc + 1:])else:pieces.append(fragment) print(pieces)結(jié)果: ['/abcd123', '[0-9]{4})/lwjeg', '[a-z-]+)/'] ['/abcd123', '%s/lwjeg', '%s/']事例
? 代碼九?
# 自動(dòng)的去重載改變的模塊,這個(gè)調(diào)用的是autorelaad模塊實(shí)現(xiàn)的 # Automatically reload modified modules if self.settings.get("debug") and not wsgi:import autoreloadautoreload.start()?
至此?
application = tornado.web.Application([(r"/", MainHandler), ])就解析完成了,下一篇待續(xù)。。。。?
轉(zhuǎn)載于:https://www.cnblogs.com/huwentao/p/7091935.html
總結(jié)
以上是生活随笔為你收集整理的tarnado源码解析系列一的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装完MAVEN后输入mvn -v, 提
- 下一篇: 就是笔记