前面几篇我们系统梳理了 Tornado 的异步内核与核心特性,本篇我们正式进入实战环节,手把手带你从零搭建一个可运行的 Tornado Web 应用,串联起之前所学的全部知识点。
一个标准的 Tornado Web 应用通常由三部分构成:
- 一个或多个
RequestHandler子类,承载具体业务逻辑 - 负责全局配置与请求分发的
Application实例 - 用于启动服务、维持事件循环的
main()函数
下面我们从最简示例入手,逐步拆解每个组件的用法与工程最佳实践。
一、最简实战:Hello World 示例
我们先从最经典的 Hello World 开始,直观感受 Tornado 应用的完整结构,以下代码可直接运行:
bash
import asyncio
import tornado.web
# 自定义请求处理器
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
# 构造应用实例、注册路由
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
# 异步主函数
async def main():
app = make_app()
app.listen(8888) # 监听8888端口
shutdown_event = asyncio.Event()
await shutdown_event.wait() # 永久阻塞保持服务运行
if __name__ == "__main__":
asyncio.run(main())
运行代码后,在浏览器访问 http://localhost:8888,即可看到页面输出 Hello, world。短短十几行代码,就完成了一个完整的异步 Web 服务。
二、核心组件深度解析
2.1 main 协程:服务启动与常驻机制
从 Tornado 6.2 + Python 3.10 开始,官方推荐将服务启动逻辑封装为异步 main 协程,再通过 asyncio.run() 驱动事件循环。
- 旧版本写法说明 :传统写法是在普通函数中完成初始化后,调用
IOLoop.current().start()启动事件循环。该写法在 Python 3.10 及以上版本会抛出废弃警告,未来 Python 版本将直接失效,不建议新项目继续使用。 - 服务常驻原理 :协程函数执行完毕后程序就会退出,而 Web 服务需要持续运行。代码中通过
await shutdown_event.wait()让协程永久阻塞 ------ 这是一种优雅的常驻方案,该事件默认永远不会触发set(),因此服务会一直保持运行。 - 优雅停机能力 :如果需要实现平滑停机,只需在信号处理逻辑中主动调用
shutdown_event.set(),即可唤醒等待逻辑,让程序正常退出、释放端口与连接资源。
2.2 Application 对象:全局配置与路由分发
Application 是 Tornado 应用的全局管理者,核心职责是维护路由表,将不同 URL 的请求分发到对应的处理器类。
路由表核心规则
路由表由多个路由项组成(可以是元组,也可以是 URLSpec 对象),每条路由至少包含「正则匹配规则 + 处理器类」,遵循以下规则:
- 匹配优先级:从上到下依次匹配,命中第一条规则后立即终止,不会继续匹配后续路由。
- 路径参数传递:正则中的捕获分组会作为路径参数,自动传递给处理器对应的 HTTP 方法。
- 初始化传参 :路由的第三个参数传入字典时,字典内容会作为初始化参数,注入
RequestHandler.initialize()方法。 - 反向生成链接 :可以为路由设置
name别名,后续通过reverse_url()反向生成链接地址,避免硬编码 URL。
路由进阶完整示例
下面的示例展示了路径参数、初始化传参、反向 URL 的组合用法:
bash
from tornado.web import RequestHandler, Application, url
class MainHandler(RequestHandler):
def get(self):
# 通过路由别名反向拼接访问链接
self.write('<a href="%s">link to story 1</a>' %
self.reverse_url("story", "1"))
class StoryHandler(RequestHandler):
# 接收路由传入的db参数
def initialize(self, db):
self.db = db
def get(self, story_id):
self.write("this is story %s" % story_id)
# 构造应用与路由表
app = Application([
url(r"/", MainHandler),
url(r"/story/([0-9]+)", StoryHandler, dict(db="database_conn"), name="story")
])
除了路由配置外,Application 构造函数还支持大量关键字参数,用于定制应用行为、开启扩展能力,完整配置清单可查阅官方 Application.settings 文档。
2.3 RequestHandler:业务逻辑的核心载体
Tornado 的绝大多数业务逻辑都写在 RequestHandler 的子类中:
- 处理器按照 HTTP 请求方法定义同名函数(
get()、post()、put()、delete()等),不同函数处理对应类型的请求。 - 路由正则捕获的分组参数,会自动按顺序传入对应 HTTP 方法的形参中。
两种主流响应输出方式
render():根据模板名加载 HTML 模板,填充变量后渲染完整页面返回,适合服务端渲染场景。write():无模板直接输出内容,支持字符串、字节、字典三种类型;传入字典时会自动序列化为 JSON 格式,非常适合后端接口开发。
工程最佳实践:BaseHandler 基类封装
RequestHandler 提供了大量可重写的钩子方法,在实际项目中推荐先自定义一个 BaseHandler 基类,统一重写通用逻辑:
write_error():自定义统一错误页面与错误返回格式get_current_user():实现全局用户鉴权逻辑- 统一响应格式、跨域处理、日志埋点等通用能力
后续所有业务处理器都继承自这个 BaseHandler,可以极大减少重复代码,提升项目可维护性。
三、本篇小结
本篇我们从最简的 Hello World 示例入手,完整讲解了 Tornado Web 应用的三大核心组件,涵盖了服务启动、路由配置、请求处理的全流程规范与工程最佳实践。掌握这些基础后,我们就可以在此之上扩展数据库交互、用户认证、模板渲染等更复杂的业务能力。