打開你寫 Python 代碼用的編輯器,不要問為什么,把下面的代碼一個字不差地錄入進去,并命名保存為 hello.py(目錄自己任意定)。
#!/usr/bin/env Python
#coding:utf-8
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument('greeting', 'Hello')
self.write(greeting + ', welcome you to read: www.itdiffer.com')
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
進入到保存 hello.py 文件的目錄,執(zhí)行:
$ python hello.py
用 Python 運行這個文件,其實就已經(jīng)發(fā)布了一個網(wǎng)站,只不過這個網(wǎng)站太簡單了。
接下來,打開瀏覽器,在瀏覽器中輸入:http://localhost:8000,得到如下界面:
http://wiki.jikexueyuan.com/project/start-learning-python/images/30201.png" alt="" />
我在 ubuntu 的 shell 中還可以用下面方式運行:
$ curl http://localhost:8000/
Hello, welcome you to read: www.itdiffer.com
$ curl http://localhost:8000/?greeting=Qiwsir
Qiwsir, welcome you to read: www.itdiffer.com
此操作,讀者可以根據(jù)自己系統(tǒng)而定。
恭喜你,邁出了決定性一步,已經(jīng)可以用 Tornado 發(fā)布網(wǎng)站了。在這里似乎沒有做什么部署,只是安裝了 Tornado。是的,不需要多做什么,因為 Tornado 就是一個很好的 server,也是一個開發(fā)框架。
下面以這個非常簡單的網(wǎng)站為例,對用 tornado 做的網(wǎng)站的基本結(jié)構(gòu)進行解釋。
任何一個網(wǎng)站都離不開 Web 服務器,這里所說的不是指那個更計算機一樣的硬件設(shè)備,是指里面安裝的軟件,有時候初次接觸的看官容易搞混。就來偉大的維基百科都這么說:
有時,這兩種定義會引起混淆,如 Web 服務器。它可能是指用于網(wǎng)站的計算機,也可能是指像 Apache 這樣的軟件,運行在這樣的計算機上以管理網(wǎng)頁組件和回應網(wǎng)頁瀏覽器的請求。
在具體的語境中,看官要注意分析,到底指的是什么。
關(guān)于 Web 服務器比較好的解釋,推薦看看百度百科的內(nèi)容,我這里就不復制粘貼了,具體可以點擊連接查閱:WEB 服務器
在 WEB 上,用的最多的就是輸入網(wǎng)址,訪問某個網(wǎng)站。全世界那么多網(wǎng)站網(wǎng)頁,如果去訪問,怎么能夠做到彼此互通互聯(lián)呢。為了協(xié)調(diào)彼此,就制定了很多通用的協(xié)議,其中 http 協(xié)議,就是網(wǎng)絡(luò)協(xié)議中的一種。關(guān)于這個協(xié)議的介紹,網(wǎng)上隨處就能找到,請自己 google.
網(wǎng)上偷來的一張圖(從哪里偷來的,我都告訴你了,多實在呀。哈哈。),顯示在下面,簡要說明web服務器的工作過程
http://wiki.jikexueyuan.com/project/start-learning-python/images/30202.png" alt="" />
偷個徹底,把原文中的說明也貼上:
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
這四個都是 Tornado 的模塊,在本例中都是必須的。它們四個在一般的網(wǎng)站開發(fā)中,都要用到,基本作用分別是:
讀者看到這里可能有點莫名其妙,對一些屬于不理解。沒關(guān)系,你可以先不用管它,如果愿意管,就把不理解屬于放到 google 立面查查看。一定要硬著頭皮一字一句地讀下去,隨著學習和實踐的深入,現(xiàn)在不理解的以后就會逐漸領(lǐng)悟理解的。
還有一個模塊引入,是用 from...import 完成的
from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)
這兩句就顯示了所謂“命令行解析模塊”的用途了。在這里通過 tornado.options.define()
定義了訪問本服務器的端口,就是當在瀏覽器地址欄中輸入 http:localhost:8000
的時候,才能訪問本網(wǎng)站,因為 http 協(xié)議默認的端口是 80,為了區(qū)分,我在這里設(shè)置為 8000,為什么要區(qū)分呢?因為我的計算機或許你的也是,已經(jīng)部署了別(或許是 Nginx、Apache)服務器了,它的端口是 80,所以要區(qū)分開(也可能是故意不用80端口),并且,后面我們還會將 tornado 和 Nginx 聯(lián)合起來工作,這樣兩個服務器在同一臺計算機上,就要分開嘍。
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument('greeting', 'Hello')
self.write(greeting + ', welcome you to read: www.itdiffer.com')
所謂“請求處理”程序類,就是要定義一個類,專門應付客戶端(就是你打開的那個瀏覽器界面)向服務器提出的請求(這個請求也許是要讀取某個網(wǎng)頁,也許是要將某些信息存到服務器上),服務器要有相應的程序來接收并處理這個請求,并且反饋某些信息(或者是針對請求反饋所要的信息,或者返回其它的錯誤信息等)。
于是,就定義了一個類,名字是 IndexHandler,當然,名字可以隨便取了,但是,按照習慣,類的名字中的單詞首字母都是大寫的,并且如果這個類是請求處理程序類,那么就最好用 Handler 結(jié)尾,這樣在名稱上很明確,是干什么的。
類 IndexHandler 繼承 tornado.web.RequestHandler
,其中再定義 get()
和 post()
兩個在 web 中應用最多的方法的內(nèi)容(關(guān)于這兩個方法的詳細解釋,可以參考:HTTP GET POST 的本質(zhì)區(qū)別詳解,作者在這篇文章中,闡述了兩個方法的本質(zhì))。
在本例中,只定義了一個 get()
方法。
用 greeting = self.get_argument('greeting', 'Hello')
的方式可以得到 url 中傳遞的參數(shù),比如
$ curl http://localhost:8000/?greeting=Qiwsir
Qiwsir, welcome you to read: www.itdiffer.com
就得到了在 url 中為 greeting 設(shè)定的值 Qiwsir。如果 url 中沒有提供值,就是 Hello.
官方文檔對這個方法的描述如下:
RequestHandler.get_argument(name, default=, []strip=True)
Returns the value of the argument with the given name.
If default is not provided, the argument is considered to be required, and we raise a MissingArgumentError if it is missing.
If the argument appears in the url more than once, we return the last value.
The returned value is always unicode.
接下來的那句 self.write(greeting + ',weblcome you to read: www.itdiffer.com)'
中,write()
方法主要功能是向客戶端反饋信息。也瀏覽一下官方文檔信息,對以后正確理解使用有幫助:
RequestHandler.write(chunk)[source]
Writes the given chunk to the output buffer.
To write the output to the network, use the flush() method below.
If the given chunk is a dictionary, we write it as JSON and set the Content-Type of the response to be application/json. (if you want to send JSON as a different Content-Type, call set_header after calling write()).
if __name__ == "__main__"
,這個方法跟以往執(zhí)行 Python 程序是一樣的。
tornado.options.parse_command_line()
,這是在執(zhí)行 tornado 的解析命令行。在 tornado 的程序中,只要 import 模塊之后,就會在運行的時候自動加載,不需要了解細節(jié),但是,在 main()方法中如果有命令行解析,必須要提前將模塊引入。
下面這句是重點:
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
將 tornado.web.Application 類實例化。這個實例化,本質(zhì)上是建立了整個網(wǎng)站程序的請求處理集合,然后它可以被 HTTPServer 做為參數(shù)調(diào)用,實現(xiàn) http 協(xié)議服務器訪問。Application 類的__init__
方法參數(shù)形式:
def __init__(self, handlers=None, default_host="", transforms=None,**settings):
pass
在一般情況下,handlers 是不能為空的,因為 Application 類通過這個參數(shù)的值處理所得到的請求。例如在本例中,handlers=[(r"/", IndexHandler)]
,就意味著如果通過瀏覽器的地址欄輸入根路徑(http://localhost:8000
就是根路徑,如果是 http://localhost:8000/qiwsir
,就不屬于根,而是一個子路徑或目錄了),對應著就是讓名字為 IndexHandler 類處理這個請求。
通過 handlers 傳入的數(shù)值格式,一定要注意,在后面做復雜結(jié)構(gòu)的網(wǎng)站是,這里就顯得重要了。它是一個 list,list 里面的元素是 tuple,tuple 的組成包括兩部分,一部分是請求路徑,另外一部分是處理程序的類名稱。注意請求路徑可以用正則表達式書寫(關(guān)于正則表達式,后面會進行簡要介紹)。舉例說明:
handlers = [
(r"/", IndexHandlers), #來自根路徑的請求用 IndesHandlers 處理
(r"/qiwsir/(.*)", QiwsirHandlers), #來自 /qiwsir/ 以及其下任何請求(正則表達式表示任何字符)都由 QiwsirHandlers 處理
]
注意
在這里我使用了 r"/"
的樣式,意味著就不需要使用轉(zhuǎn)義符,r 后面的都表示該符號本來的含義。例如,\n,如果單純這么來使用,就以為著換行,因為符號“\”具有轉(zhuǎn)義功能(關(guān)于轉(zhuǎn)義詳細閱讀《字符串(1)》),當寫成 r"\n"
的形式是,就不再表示換行了,而是兩個字符,\ 和 n,不會轉(zhuǎn)意。一般情況下,由于正則表達式和 \ 會有沖突,因此,當一個字符串使用了正則表達式后,最好在前面加上'r'。
關(guān)于 Application 類的介紹,告一段落,但是并未完全講述了,因為還有別的參數(shù)設(shè)置沒有講,請繼續(xù)關(guān)注后續(xù)內(nèi)容。
實例化之后,Application 對象(用app做為標簽的)就可以被另外一個類 HTTPServer 引用,形式為:
http_server = tornado.httpserver.HTTPServer(app)
HTTPServer 是 tornado.httpserver 里面定義的類。HTTPServer 是一個單線程非阻塞 HTTP 服務器,執(zhí)行 HTTPServer 一般要回調(diào) Application 對象,并提供發(fā)送響應的接口,也就是下面的內(nèi)容是跟隨上面語句的(options.port 的值在 IndexHandler 類前面通過 from...import.. 設(shè)置的)。
http_server.listen(options.port)
這種方法,就建立了單進程的 http 服務。
請看官牢記,如果在以后編碼中,遇到需要多進程,請參考官方文檔說明:http://tornado.readthedocs.org/en/latest/httpserver.html#http-server
剩下最后一句了:
tornado.ioloop.IOLoop.instance().start()
這句話,總是在__main()__
的最后一句。表示可以接收來自 HTTP 的請求了。
以上把一個簡單的 hello.py 剖析。想必讀者對 Tornado 編寫網(wǎng)站的基本概念已經(jīng)有了。
如果一頭霧水,也不要著急,以來將上面的內(nèi)容多看幾遍。對整體結(jié)構(gòu)有一個基本了解,不要拘泥于細節(jié)或者某些詞匯含義。然后即繼續(xù)學習。
總目錄 | 上節(jié):為做網(wǎng)站而準備 | 下節(jié):用 tornado 做網(wǎng)站(1)
如果你認為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。