分析Hello

2018-02-24 15:48 更新

打開(kāi)你寫python代碼用的編輯器,不要問(wèn)為什么,把下面的代碼一個(gè)字不差地錄入進(jìn)去,并命名保存為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()

進(jìn)入到保存hello.py文件的目錄,執(zhí)行:

$ python hello.py

用python運(yùn)行這個(gè)文件,其實(shí)就已經(jīng)發(fā)布了一個(gè)網(wǎng)站,只不過(guò)這個(gè)網(wǎng)站太簡(jiǎn)單了。

接下來(lái),打開(kāi)瀏覽器,在瀏覽器中輸入:http://localhost:8000,得到如下界面

我在ubuntu的shell中還可以用下面方式運(yùn)行:

$ 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)站了。在這里似乎沒(méi)有做什么部署,只是安裝了Tornado。是的,不需要多做什么,因?yàn)門ornado就是一個(gè)很好的server,也是一個(gè)開(kāi)發(fā)框架。

下面以這個(gè)非常簡(jiǎn)單的網(wǎng)站為例,對(duì)用tornado做的網(wǎng)站的基本結(jié)構(gòu)進(jìn)行解釋。

WEB服務(wù)器工作流程

任何一個(gè)網(wǎng)站都離不開(kāi)Web服務(wù)器,這里所說(shuō)的不是指那個(gè)更計(jì)算機(jī)一樣的硬件設(shè)備,是指里面安裝的軟件,有時(shí)候初次接觸的看官容易搞混。就來(lái)偉大的維基百科都這么說(shuō)

有時(shí),這兩種定義會(huì)引起混淆,如Web服務(wù)器。它可能是指用于網(wǎng)站的計(jì)算機(jī),也可能是指像Apache這樣的軟件,運(yùn)行在這樣的計(jì)算機(jī)上以管理網(wǎng)頁(yè)組件和回應(yīng)網(wǎng)頁(yè)瀏覽器的請(qǐng)求。

在具體的語(yǔ)境中,看官要注意分析,到底指的是什么。

關(guān)于Web服務(wù)器比較好的解釋,推薦看看百度百科的內(nèi)容,我這里就不復(fù)制粘貼了,具體可以點(diǎn)擊連接查閱:WEB服務(wù)器

在WEB上,用的最多的就是輸入網(wǎng)址,訪問(wèn)某個(gè)網(wǎng)站。全世界那么多網(wǎng)站網(wǎng)頁(yè),如果去訪問(wèn),怎么能夠做到彼此互通互聯(lián)呢。為了協(xié)調(diào)彼此,就制定了很多通用的協(xié)議,其中http協(xié)議,就是網(wǎng)絡(luò)協(xié)議中的一種。關(guān)于這個(gè)協(xié)議的介紹,網(wǎng)上隨處就能找到,請(qǐng)自己google.

網(wǎng)上偷來(lái)的一張圖(從哪里偷來(lái)的,我都告訴你了,多實(shí)在呀。哈哈。),顯示在下面,簡(jiǎn)要說(shuō)明web服務(wù)器的工作過(guò)程

偷個(gè)徹底,把原文中的說(shuō)明也貼上:

  1. 創(chuàng)建listen socket, 在指定的監(jiān)聽(tīng)端口, 等待客戶端請(qǐng)求的到來(lái)
  2. listen socket接受客戶端的請(qǐng)求, 得到client socket, 接下來(lái)通過(guò)client socket與客戶端通信
  3. 處理客戶端的請(qǐng)求, 首先從client socket讀取http請(qǐng)求的協(xié)議頭, 如果是post協(xié)議, 還可能要讀取客戶端上傳的數(shù)據(jù), 然后處理請(qǐng)求, 準(zhǔn)備好客戶端需要的數(shù)據(jù), 通過(guò)client socket寫給客戶端

引入模塊

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

這四個(gè)都是Tornado的模塊,在本例中都是必須的。它們四個(gè)在一般的網(wǎng)站開(kāi)發(fā)中,都要用到,基本作用分別是:

  • tornado.httpserver:這個(gè)模塊就是用來(lái)解決web服務(wù)器的http協(xié)議問(wèn)題,它提供了不少屬性方法,實(shí)現(xiàn)客戶端和服務(wù)器端的互通。Tornado的非阻塞、單線程的特點(diǎn)在這個(gè)模塊中體現(xiàn)。
  • tornado.ioloop:這個(gè)也非常重要,能夠?qū)崿F(xiàn)非阻塞socket循環(huán),不能互通一次就結(jié)束呀。
  • tornado.options:這是命令行解析模塊,也常用到。
  • tornado.web:這是必不可少的模塊,它提供了一個(gè)簡(jiǎn)單的Web框架與異步功能,從而使其擴(kuò)展到大量打開(kāi)的連接,使其成為理想的長(zhǎng)輪詢。

讀者看到這里可能有點(diǎn)莫名其妙,對(duì)一些屬于不理解。沒(méi)關(guān)系,你可以先不用管它,如果愿意管,就把不理解屬于放到google立面查查看。一定要硬著頭皮一字一句地讀下去,隨著學(xué)習(xí)和實(shí)踐的深入,現(xiàn)在不理解的以后就會(huì)逐漸領(lǐng)悟理解的。

還有一個(gè)模塊引入,是用from...import完成的

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

這兩句就顯示了所謂“命令行解析模塊”的用途了。在這里通過(guò)tornado.options.define()定義了訪問(wèn)本服務(wù)器的端口,就是當(dāng)在瀏覽器地址欄中輸入http:localhost:8000的時(shí)候,才能訪問(wèn)本網(wǎng)站,因?yàn)閔ttp協(xié)議默認(rèn)的端口是80,為了區(qū)分,我在這里設(shè)置為8000,為什么要區(qū)分呢?因?yàn)槲业挠?jì)算機(jī)或許你的也是,已經(jīng)部署了別(或許是Nginx、Apache)服務(wù)器了,它的端口是80,所以要區(qū)分開(kāi)(也可能是故意不用80端口),并且,后面我們還會(huì)將tornado和Nginx聯(lián)合起來(lái)工作,這樣兩個(gè)服務(wù)器在同一臺(tái)計(jì)算機(jī)上,就要分開(kāi)嘍。

定義請(qǐng)求-處理程序類

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        greeting = self.get_argument('greeting', 'Hello')
        self.write(greeting + ', welcome you to read: www.itdiffer.com')

所謂“請(qǐng)求處理”程序類,就是要定義一個(gè)類,專門應(yīng)付客戶端(就是你打開(kāi)的那個(gè)瀏覽器界面)向服務(wù)器提出的請(qǐng)求(這個(gè)請(qǐng)求也許是要讀取某個(gè)網(wǎng)頁(yè),也許是要將某些信息存到服務(wù)器上),服務(wù)器要有相應(yīng)的程序來(lái)接收并處理這個(gè)請(qǐng)求,并且反饋某些信息(或者是針對(duì)請(qǐng)求反饋所要的信息,或者返回其它的錯(cuò)誤信息等)。

于是,就定義了一個(gè)類,名字是IndexHandler,當(dāng)然,名字可以隨便取了,但是,按照習(xí)慣,類的名字中的單詞首字母都是大寫的,并且如果這個(gè)類是請(qǐng)求處理程序類,那么就最好用Handler結(jié)尾,這樣在名稱上很明確,是干什么的。

類IndexHandler繼承tornado.web.RequestHandler,其中再定義get()post()兩個(gè)在web中應(yīng)用最多的方法的內(nèi)容(關(guān)于這兩個(gè)方法的詳細(xì)解釋,可以參考:HTTP GET POST的本質(zhì)區(qū)別詳解,作者在這篇文章中,闡述了兩個(gè)方法的本質(zhì))。

在本例中,只定義了一個(gè)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中沒(méi)有提供值,就是Hello.

官方文檔對(duì)這個(gè)方法的描述如下:

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.

接下來(lái)的那句self.write(greeting + ',weblcome you to read: www.itdiffer.com)'中,write()方法主要功能是向客戶端反饋信息。也瀏覽一下官方文檔信息,對(duì)以后正確理解使用有幫助:

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()).

main()方法

if __name__ == "__main__",這個(gè)方法跟以往執(zhí)行python程序是一樣的。

tornado.options.parse_command_line(),這是在執(zhí)行tornado的解析命令行。在tornado的程序中,只要import模塊之后,就會(huì)在運(yùn)行的時(shí)候自動(dòng)加載,不需要了解細(xì)節(jié),但是,在main()方法中如果有命令行解析,必須要提前將模塊引入。

Application類

下面這句是重點(diǎn):

app = tornado.web.Application(handlers=[(r"/", IndexHandler)])

將tornado.web.Application類實(shí)例化。這個(gè)實(shí)例化,本質(zhì)上是建立了整個(gè)網(wǎng)站程序的請(qǐng)求處理集合,然后它可以被HTTPServer做為參數(shù)調(diào)用,實(shí)現(xiàn)http協(xié)議服務(wù)器訪問(wèn)。Application類的__init__方法參數(shù)形式:

def __init__(self, handlers=None, default_host="", transforms=None,**settings):
    pass

在一般情況下,handlers是不能為空的,因?yàn)锳pplication類通過(guò)這個(gè)參數(shù)的值處理所得到的請(qǐng)求。例如在本例中,handlers=[(r"/", IndexHandler)],就意味著如果通過(guò)瀏覽器的地址欄輸入根路徑(http://localhost:8000就是根路徑,如果是http://localhost:8000/qiwsir,就不屬于根,而是一個(gè)子路徑或目錄了),對(duì)應(yīng)著就是讓名字為IndexHandler類處理這個(gè)請(qǐng)求。

通過(guò)handlers傳入的數(shù)值格式,一定要注意,在后面做復(fù)雜結(jié)構(gòu)的網(wǎng)站是,這里就顯得重要了。它是一個(gè)list,list里面的元素是tuple,tuple的組成包括兩部分,一部分是請(qǐng)求路徑,另外一部分是處理程序的類名稱。注意請(qǐng)求路徑可以用正則表達(dá)式書寫(關(guān)于正則表達(dá)式,后面會(huì)進(jìn)行簡(jiǎn)要介紹)。舉例說(shuō)明:

handlers = [
    (r"/", IndexHandlers),              #來(lái)自根路徑的請(qǐng)求用IndesHandlers處理
    (r"/qiwsir/(.*)", QiwsirHandlers),  #來(lái)自/qiwsir/以及其下任何請(qǐng)求(正則表達(dá)式表示任何字符)都由QiwsirHandlers處理
]

注意

在這里我使用了r"/"的樣式,意味著就不需要使用轉(zhuǎn)義符,r后面的都表示該符號(hào)本來(lái)的含義。例如,\n,如果單純這么來(lái)使用,就以為著換行,因?yàn)榉?hào)“\”具有轉(zhuǎn)義功能(關(guān)于轉(zhuǎn)義詳細(xì)閱讀《字符串(1)》),當(dāng)寫成r"\n"的形式是,就不再表示換行了,而是兩個(gè)字符,\和n,不會(huì)轉(zhuǎn)意。一般情況下,由于正則表達(dá)式和 \ 會(huì)有沖突,因此,當(dāng)一個(gè)字符串使用了正則表達(dá)式后,最好在前面加上'r'。

關(guān)于Application類的介紹,告一段落,但是并未完全講述了,因?yàn)檫€有別的參數(shù)設(shè)置沒(méi)有講,請(qǐng)繼續(xù)關(guān)注后續(xù)內(nèi)容。

HTTPServer類

實(shí)例化之后,Application對(duì)象(用app做為標(biāo)簽的)就可以被另外一個(gè)類HTTPServer引用,形式為:

http_server = tornado.httpserver.HTTPServer(app)

HTTPServer是tornado.httpserver里面定義的類。HTTPServer是一個(gè)單線程非阻塞HTTP服務(wù)器,執(zhí)行HTTPServer一般要回調(diào)Application對(duì)象,并提供發(fā)送響應(yīng)的接口,也就是下面的內(nèi)容是跟隨上面語(yǔ)句的(options.port的值在IndexHandler類前面通過(guò)from...import..設(shè)置的)。

http_server.listen(options.port)

這種方法,就建立了單進(jìn)程的http服務(wù)。

請(qǐng)看官牢記,如果在以后編碼中,遇到需要多進(jìn)程,請(qǐng)參考官方文檔說(shuō)明:http://tornado.readthedocs.org/en/latest/httpserver.html#http-server

IOLoop類

剩下最后一句了:

tornado.ioloop.IOLoop.instance().start()

這句話,總是在__main()__的最后一句。表示可以接收來(lái)自HTTP的請(qǐng)求了。

以上把一個(gè)簡(jiǎn)單的hello.py剖析。想必讀者對(duì)Tornado編寫網(wǎng)站的基本概念已經(jīng)有了。

如果一頭霧水,也不要著急,以來(lái)將上面的內(nèi)容多看幾遍。對(duì)整體結(jié)構(gòu)有一個(gè)基本了解,不要拘泥于細(xì)節(jié)或者某些詞匯含義。然后即繼續(xù)學(xué)習(xí)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)