首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Tornado 非同步 HTTP 反向代理實作:封裝上游 API、Header 透傳與錯誤處理

     編輯:更多程式設計知識

Tornado 非同步特性使其成為實作 HTTP 反向代理的優秀選擇。本文實作一個通用代理處理器,支援 GET/POST 透傳、Header 重寫、重導向控制、錯誤碼映射,適用於微服務閘道、API 聚合、跨域代理等場景。

核心代理處理器


# -*- coding: utf-8 -*-
import logging
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.httpclient
from tornado.web import HTTPError, asynchronous
from tornado.httpclient import HTTPRequest
from tornado.options import define, options

try:
    from tornado.curl_httpclient import CurlAsyncHTTPClient as AsyncHTTPClient
except ImportError:
    from tornado.simple_httpclient import SimpleAsyncHTTPClient as AsyncHTTPClient

define("port", default=8888, help="run on the given port", type=int)
define("api_protocol", default="http")
define("api_host", default="feilong.me")
define("api_port", default="80")
define("debug", default=True, type=bool)

class ProxyHandler(tornado.web.RequestHandler):
    @asynchronous
    def get(self):
        if options.debug:
            return self.post()
        else:
            raise HTTPError(405)

    @asynchronous
    def post(self):
        protocol = options.api_protocol
        host = options.api_host
        port = "" if options.api_port == "80" else ":%s" % options.api_port

        uri = self.request.uri
        url = "%s://%s%s%s" % (protocol, host, port, uri)

        # 重寫 Host Header 指向上游
        headers = dict(self.request.headers)
        headers["Host"] = host

        try:
            AsyncHTTPClient().fetch(
                HTTPRequest(
                    url=url,
                    method="POST",
                    body=self.request.body,
                    headers=headers,
                    follow_redirects=False
                ),
                self._on_proxy
            )
        except tornado.httpclient.HTTPError as x:
            if hasattr(x, "response") and x.response:
                self._on_proxy(x.response)
            else:
                logging.error("Tornado signalled HTTPError %s", x)

    def _on_proxy(self, response):
        if response.error and not isinstance(response.error, tornado.httpclient.HTTPError):
            raise HTTPError(500)
        else:
            self.set_status(response.code)
            for header in ("Date", "Cache-Control", "Server", "Content-Type", "Location"):
                v = response.headers.get(header)
                if v:
                    self.set_header(header, v)
            if response.body:
                self.write(response.body)
            self.finish()

def main():
    tornado.options.parse_command_line()
    application = tornado.web.Application([
        (r"/.*", ProxyHandler),
    ])
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()

啟動與驗證


python proxy.py --port=8888 --api_host=feilong.me
# 訪問 https://localhost:8888/ 顯示 feilong.me 首頁

關鍵設計點

功能實作方式
非同步代理@asynchronous + AsyncHTTPClient.fetch() 回調
Header 透傳複製 self.request.headers,覆寫 Host
Body 透傳self.request.body 直接轉發
重導向控制follow_redirects=False 避免循環
錯誤處理捕獲 HTTPError,有回應則代理回應,否則 500
回應 Header 選擇性複製僅複製 Date、Cache-Control、Server、Content-Type、Location

擴充方向

  • URL 重寫與路由規則(如 /api/v1/* → 不同上游)
  • 請求/回應內容修改(JSON 轉換、欄位過濾、加密解密)
  • 速率限制、熔斷、快取、認證授權
  • WebSocket 代理支援
  • 負載均衡:多上游輪詢、權重、健康檢查

為何選擇程式代理而非 Nginx?

  • 動態控制哪些 API 需代理/屏蔽/重寫
  • 登入流程需混合本地邏輯與上游 API
  • 請求/回應需業務層面處理(簽名驗證、資料轉換)
  • Python 生態整合:日誌、監控、配置中心
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved