1. 程式人生 > >Python Web 框架:Tornado

Python Web 框架:Tornado

efi 文件名 files 負載能力 重啟 defined tornado blog 方式

1.Tornado

  • Tornado:python編寫的web服務器兼web應用框架

1.1.Tornado的優勢

  • 輕量級web框架
  • 異步非阻塞IO處理方式
  • 出色的抗負載能力
  • 優異的處理性能,不依賴多進程/多線程,一定程度上解決C10K問題
  • WSGI全棧替代產品,推薦同時使用其web框架和HTTP服務器

1.2.Tornado VS Django

  • Django:重量級web框架,功能大而全,註重高效開發
  • 內置管理後臺
  • 內置封裝完善的ORM操作
  • session功能
  • 後臺管理
  • 缺陷:高耦合
  • Tornado:輕量級web框架,功能少而精,註重性能優越
  • HTTP服務器
  • 異步編程
  • WebSocket
  • 缺陷:入門門檻較高

2.安裝

輸入命令:

pip install tornado

備註:
Tornado應該運行在類Unix平臺,為了達到最佳的性能和擴展性,僅推薦Linux和BSD(充分利用Linux的epoll工具和BSD的kqueue達到高性能處理的目的)

3.使用

3.1.Tornado入門程序 - (一)

#-*- coding:utf-8 -*-
import tornado.web
import tornado.ioloop


#定義處理類型
class IndexHandler(tornado.web.RequestHandler):
    #
添加一個處理get請求方式的方法 def get(self): #向響應中,添加數據 self.write(好看的皮囊千篇一律,有趣的靈魂萬裏挑一。) if __name__ == __main__: #創建一個應用對象 app = tornado.web.Application([(r/,IndexHandler)]) #綁定一個監聽端口 app.listen(8888) #啟動web程序,開始監聽端口的連接 tornado.ioloop.IOLoop.current().start()

1 .在pycharm中直接運行代碼
2 .如果是在ubuntu,在命令窗口輸入

python 文件名.py
技術分享
使用瀏覽器訪問

4.Tornado 代碼解析

4.1.入門程序代碼解析

  • tornado.web:tornado的基礎web框架

    • RequestHandler:封裝對請求處理的所有信息和處理方法
    • get/post/..:封裝對應的請求方式
    • write():封裝響應信息,寫響應信息的一個方法
  • tornado.ioloop:核心io循環模塊,封裝linux的epoll和BSD的kqueue, tornado高性能處理的核心。

    • current()返回當前線程的IOLoop實例對象
    • start()啟動IOLoop實力對象的IO循環,開啟監聽

4.2.httpserver底層處理

  • httpserver監聽端口
tornado.httpserver.HTTPServer(app)
httpserver.listen(port)
  • httpserver實現多進程操作
tornado.httpserver.HTTPServer(app)
httpserver.bind(port)
httpserver.start(0/None/<0/num)
# -*- coding:utf-8 -*-
from tornado.web import Application,RequestHandler
from tornado.ioloop import IOLoop
from tornado.httpserver import HTTPServer

class IndexHandler(RequestHandler):
    def get(self):
        self.write(給自己一點時間,理清所有的荒唐與期望。)

if __name__ == __main__:
    app = Application([(r/,IndexHandler)])
    http_server = HTTPServer(app)
    #最原始的方式
    http_server.bind(8888)
    http_server.start(1)

    #啟動Ioloop輪循監聽
    IOLoop.current().start()

技術分享
同時打開兩個窗口測試發現實現了多進程

4.3.options配置

  • 全局配置
tornado.options.define(
    name, default, type, multiple, help
)
  • 命令行參數轉換
tornado.options.parse_command_line()
#-*- coding:utf-8 -*-

from tornado.web import RequestHandler,Application
from tornado.ioloop import IOLoop
from tornado.httpserver import HTTPServer
import tornado.options

#定義變量
tornado.options.define(port,default=8000,type=int,help="this is the port >for application")

class IndexHandler(RequestHandler):
   def get(self):
       self.write(我們既然改變不了規則,那就做到最好)

if __name__ == __main__:
   app = Application([(r/,IndexHandler)])
   tornado.options.parse_command_line()

   http_server = HTTPServer(app)
   http_server.bind(tornado.options.options.port)
   http_server.start(1)
   #啟動IOLoop輪循監聽
   IOLoop.current().start()

技術分享
通過命令窗口輸入port來訪問
技術分享
通過使用我們命令窗口設定的port進行訪問
  • 配置文件
#即在當前py文件目錄創建config文件,並在py代碼中加入以下代碼,
tornado.options.parse_config_file("./config")
  • 配置模塊:跟配置文件類似

4.4.application配置

  • 程序調試之debug配置
#自動重啟+取消緩存模板+取消緩存靜態文件+提供追蹤信息
tornado.web.Application([(..)], debug=True)
註:開發之初可以設置debug=True方便調試,開發完畢改為False.
  • 路由信息初始化參數配置
tonado.web.Application([(r””, Handler, {k:v})])
def initialize(self, k)
  • 路由名稱設置及反解析
#名稱設置
tornado.web.Application([
    url(r””, handler, {k,v}, name=“”)
])

#反解析操作
reverse_url(name)

實例

# -*- coding:utf-8 -*-

from tornado.web import Application, RequestHandler, url
from tornado.ioloop import IOLoop
from tornado.httpserver import HTTPServer


class IndexHandler(RequestHandler):

    def get(self):
        self.write("<a href=‘"+self.reverse_url("login")+"‘>用戶登錄</a>")


class RegistHandler(RequestHandler):
    def initialize(self, title):
        self.title = title

    def get(self):
        self.write("註冊業務處理:" + str(self.title))


class LoginHandler(RequestHandler):
    def get(self):
        self.write("用戶登錄頁面展示")

    def post(self):
        self.write("用戶登錄功能處理")


if __name__ == "__main__":
    app = Application(
        [
            (r"/", IndexHandler),
            (r"/regist", RegistHandler, {"title": "會員註冊"}),
            url(r"/login", LoginHandler, name="login"),
        ]
    )

    http_server = HTTPServer(app)
    http_server.listen(8000)

    IOLoop.current().start()

4.5.參數傳遞

  • get方式傳遞參數
get_query_arguments(name,default=_ARG_DEFAULT,strip=True)
get_query_argument(name ,strip=True)
  • post方式傳遞參數
get_body_arguments(name, default=_ARG_DEFAULT,strip=True)
get_body_argument(name ,strip=True)

實例

# -*- coding:utf-8 -*-

from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop
from tornado.httpserver import HTTPServer


class IndexHandler(RequestHandler):

    def get(self):
        # 獲取get方式傳遞的參數
        username = self.get_query_argument("username")
        usernames = self.get_query_arguments("username")

        print (username)
        print (usernames)

    def post(self):
        # 獲取post方式傳遞的參數
        username = self.get_body_argument("username")
        usernames = self.get_body_arguments("username")

        print (username)
        print (usernames)

if __name__ == "__main__":
    app = Application([(r"/",IndexHandler)])

    app.listen(8000)

    IOLoop.current().start()

#網頁運行時需要傳入參數
#192.168.11.79:8000/?username=123
  • 混合方式
get_arguments(..)/get_argument(..)

實例

# -*- coding:utf-8 -*-

from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


class IndexHandler(RequestHandler):

    def get(self):
        # 獲取get方式的參數
        user = self.get_argument("user")
        print("get方式獲取參數:" + str(user))

    def post(self):
        # 獲取post方式的參數
        user = self.get_argument("user")
        print("post方式獲取參數:" + user.encode("utf-8"))


if __name__ == "__main__":
    app = Application([(r"/", IndexHandler)])

    app.listen(8000)

    IOLoop.current().start()
  • 其他參數
通過request獲取參數數據
method/host/uri/path/query/version/headers/body/remote_ip/files

實例

  • request/json
# -*- coding:utf-8 -*-

from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


class IndexHandler(RequestHandler):
    def get(self):
        print self.request

        json_str = {"username": "admin", "password": "123123"}
        self.write(json.dumps(json_str))



if __name__ == "__main__":
    app = Application([(r"/", IndexHandler)])

    app.listen(8000)

    IOLoop.current().start()
  • header
  • .add_header() .set_header() .set_default_headers()
    • 設置響應HTTP頭, 前兩者的不同點在於多次設置同一個項時, .add_header()會疊加參數, 而.set_header()則以最後一次為準.
    • .set_default_headers()比較特殊, 是一個空方法, 可根據需要重寫, 作用是在每次請求初始化RequestHandler時設置默認headers.
  • .clear_header() .clear()
    • .clear_header()清除指定的headers, 而.clear()清除.set_default_headers()以外所有的headers設置.
# add_header
self.add_header(Foo, one)
self.add_header(Foo, two)
# set_header
self.set_header(Bar, one)
self.set_header(Bar, two)

# HTTP頭的設置結果
# Foo → one, two
# Bar → two


# -*- coding:utf-8 -*-

from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


class IndexHandler(RequestHandler):
    def set_default_headers(self):
        # 第二種響應頭設置方式
        print("---------> 響應頭set_default_headers()執行")
        self.set_header("Content-type", "application/json; charset=utf-8")
        self.set_header("qiku", "奇酷信息")

    def get(self):
        # 第一種操作響應頭的方式:
        # self.set_header("Content-type", "application/json")
        print("---------->get方法執行")
        self.write("{‘name‘:‘jerry‘}")
        self.set_header("qiku", "qikuedu.com")


if __name__ == "__main__":
    app = Application([(r"/", IndexHandler)])

    app.listen(8000)

    IOLoop.current().start()
  • writerror
    • .send_error()用於發送HTTP錯誤頁(狀態碼). 該操作會調用.clear() .set_status()
    • .write_error()用於清除headers, 設置狀態碼, 發送錯誤頁. 重寫.write_error()可以自定義錯誤頁.
# -*- coding:utf-8 -*-

from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


class IndexHandler(RequestHandler):

    def get(self):
        self.write("hello qikuedu.com")

        self.send_error(404, msg="頁面丟失", info="家裏服務器搞對象去了")

    def write_error(self, status_code, **kwargs):
        self.write("<h1>出錯啦,工程師MM正在趕來的途中...</h1>")
        self.write("<p>錯誤信息:%s</p>" % kwargs["msg"])
        self.write("<p>錯誤描述:%s</p>" % kwargs["info"])


if __name__ == "__main__":
    app = Application([(r"/", IndexHandler)])

    app.listen(8000)

    IOLoop.current().start()

數據流

  • .write()

    • 將數據寫入輸出緩沖區. 如果直接傳入dict, 那Tornado會自動將其識別為json, 並把Content-Type設置為application/json, 如果你不想要這個Content-Type, 那麽在.write()之後, 調用.set_header()重新設置就好了. 需要註意的是, 如果直接傳入的是list, 考慮到安全問題(json數組會被認為是一段可執行的JavaScript腳本, 且<script src="*/secret.json">可以繞過跨站限制),list將不會被轉換成json.
  • .flush()

    • 將輸出緩沖區的數據寫入socket. 如果設置了callback, 會在完成數據寫入後回調. 需要註意的是, 同一時間只能有一個"等待"的flush callback, 如果"上一次"的flush callback還沒執行, 又來了新的flush, 那麽"上一次"的flush callback會被忽略掉.
  • .finish()

    • 完成響應, 結束本次請求. 通常情況下, 請求會在return時自動調用.finish(), 只有在使用了異步裝飾器@asynchronous或其他將._auto_finish設置為False的操作, 才需要手動調用.finish().
  • cookie
# -*- coding:utf-8 -*-

from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop


class IndexHandler(RequestHandler):

    def get(self):
        self.write("hello qikuedu.com")

        self.set_cookie("loginuser", "admin老王")

        print self.get_cookie("loginuser")

        print self.cookies


if __name__ == "__main__":
    app = Application([(r"/", IndexHandler)])

    app.listen(8000)

    IOLoop.current().start()

頁面

  • .render()

    • 返回渲染完成的html. 調用後不能再進行輸出操作.
  • .redirect()

    • 重定向, 可以指定3xx重定向狀態碼. 調用後不能再進行輸出操作.
# 臨時重定向 301
self.redirect(/foo)
# 永久重定向 302
self.redirect(/foo, permanent=True)
# 指定狀態碼, 會忽略參數 permanent
self.redirect(/foo, status=304)
  • redirect
# -*- coding:utf-8 -*-

from tornado.web import Application, RequestHandler, url
from tornado.ioloop import IOLoop
from tornado.httpserver import HTTPServer


class IndexHandler(RequestHandler):

    def get(self):
        self.write("<a href=‘"+self.reverse_url("login")+"‘>用戶登錄</a>")


class RegistHandler(RequestHandler):
    def initialize(self, title):
        self.title = title

    def get(self):
        self.write("註冊業務處理:" + str(self.title))


class LoginHandler(RequestHandler):
    def get(self):
        self.write("用戶登錄頁面展示")

    def post(self):
        self.write("用戶登錄功能處理")


if __name__ == "__main__":
    app = Application(
        [
            (r"/", IndexHandler),
            (r"/regist", RegistHandler, {"title": "會員註冊"}),
            url(r"/login", LoginHandler, name="login"),
        ]
    )

    http_server = HTTPServer(app)
    http_server.listen(8000)

    IOLoop.current().start()

Python Web 框架:Tornado