OpenResty反向代理

通过在 OpenResty 的配置文件中定义不同的 location 块,将匹配特定 URL 路径的请求转发到不同的后端 FastAPI 应用(即使它们运行在不同的端口或甚至是不同的服务器/容器上)。

核心思路:

  1. 多个 FastAPI 应用实例:

    • 你的每个 FastAPI 应用(例如 "Tool App", "ServiceB App")会独立运行,监听在各自的端口上。
    • 例如:
      • Tool App 运行在 127.0.0.1:8001 (或 Docker 容器 tool_app_container:8001)
      • ServiceB App 运行在 127.0.0.1:8002 (或 Docker 容器 serviceb_app_container:8002)
  2. OpenResty 配置:

    • 在 OpenResty 的 server 配置块中,为每个你想映射的 URL 路径创建一个 location 块。
    • 在每个 location 块内部,使用 proxy_pass 指令将请求转发到对应的 FastAPI 应用的地址和端口。

OpenResty 配置示例

假设你有以下需求:

  • XXX.com/tool/ 下的所有请求 -> Tool App (监听在 8001 端口)
  • XXX.com/serviceB/ 下的所有请求 -> ServiceB App (监听在 8002 端口)
  • XXX.com/static/ -> 静态文件
  • XXX.com/ -> 前端单页应用 (SPA) 或其他默认 FastAPI 应用 (监听在 8000 端口)
nginx 复制代码
# /usr/local/openresty/nginx/conf/nginx.conf 或 /etc/nginx/conf.d/your-site.conf

# 为每个 FastAPI 应用定义上游服务器组 (推荐,方便管理和扩展)
upstream fastapi_default_app {
    server 127.0.0.1:8000; # 或者 Docker 服务名:端口,例如 default_fastapi_service:8000
    # server unix:/path/to/default_app.sock;
}

upstream fastapi_tool_app {
    server 127.0.0.1:8001; # 或者 Docker 服务名:端口,例如 tool_fastapi_service:8001
    # server unix:/path/to/tool_app.sock;
}

upstream fastapi_serviceb_app {
    server 127.0.0.1:8002; # 或者 Docker 服务名:端口,例如 serviceb_fastapi_service:8002
    # server unix:/path/to/serviceb_app.sock;
}

server {
    listen 80;
    server_name XXX.com; # 替换为你的域名

    # 通用的代理头部设置,可以在 server 级别或 http 级别设置
    proxy_http_version 1.1; # 推荐,支持 keepalive
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Upgrade $http_upgrade; # 用于 WebSocket
    proxy_set_header Connection "upgrade";    # 用于 WebSocket

    # 静态文件
    location /static/ {
        alias /path/to/your/project/static/;
        expires 30d;
        add_header Cache-Control public;
    }

    # 路由到 Tool App
    location /tool/ { # 注意末尾的斜杠
        # proxy_pass 指令末尾的斜杠很重要,它会影响路径如何传递给后端
        # 当 location 和 proxy_pass 末尾都有斜杠时,location 匹配的路径前缀会被移除
        # 例如:XXX.com/tool/myaction -> 后端收到 /myaction
        proxy_pass http://fastapi_tool_app/;
    }

    # 路由到 ServiceB App
    location /serviceB/ {
        proxy_pass http://fastapi_serviceb_app/;
    }

    # 如果有其他特定路径
    # location /another-path/ {
    #     proxy_pass http://another_backend_service/;
    # }

    # 默认的 FastAPI 应用或前端 SPA
    location / {
        # 如果是 FastAPI 应用作为默认
        # proxy_pass http://fastapi_default_app;

        # 如果是前端 SPA
        root /path/to/your/frontend/dist;
        try_files $uri $uri/ /index.html;
    }

    # Lua 脚本可以在这些 location 块内或 server 块级别使用
    # access_by_lua_block { ... }
}

关于 proxy_pass 和路径重写的重要说明:

  • location /path/ (带斜杠) 和 proxy_pass http://backend/ (带斜杠):

    当请求 XXX.com/path/subpath 时,proxy_pass 会将 /path/ 从请求 URI 中移除,然后将 /subpath 附加到 http://backend/ 后面,所以后端收到的是 /subpath。这通常是你想要的行为,因为后端应用不需要关心外部的 /path/ 前缀。

  • location /path/ (带斜杠) 和 proxy_pass http://backend (不带斜杠):

    当请求 XXX.com/path/subpath 时,整个原始请求 URI (/path/subpath) 会被附加到 http://backend 后面,后端收到的是 /path/subpath

  • location /path (不带斜杠) 和 proxy_pass http://backend (不带斜杠):

    当请求 XXX.com/path/subpath 时,后端收到的是 /path/subpath

选择哪种方式取决于你的 FastAPI 应用是如何配置路由的。通常推荐第一种方式(location /prefix/proxy_pass http://backend/),这样 FastAPI 应用内部的路由可以从根路径 / 开始定义,而不需要包含 /prefix

FastAPI 应用端的考虑:

如果 OpenResty 在转发时没有 移除路径前缀(例如,location /tool/ 转发后,FastAPI 仍然收到 /tool/myaction),那么你的 FastAPI 路由需要包含这个前缀:

python 复制代码
# tool_app.py
from fastapi import FastAPI

app = FastAPI() # 默认情况下,FastAPI 不知道它被代理在 /tool/ 之下

@app.get("/tool/items/{item_id}") # 路由包含 /tool
async def read_tool_item(item_id: int):
    return {"item_id": item_id, "app": "Tool App"}

如果 OpenResty 在转发时移除 了路径前缀(例如,使用 location /tool/ { proxy_pass http://backend/; }),那么 FastAPI 应用内部的路由就可以从根路径开始定义:

python 复制代码
# tool_app.py
from fastapi import FastAPI

# 如果希望 FastAPI 生成的 URL (例如在 OpenAPI 文档中) 包含 /tool 前缀
# 即使它在运行时不知道这个前缀,可以使用 root_path
# app = FastAPI(root_path="/tool") # 这样 OpenAPI 文档和重定向会正确
app = FastAPI() # 或者不设置 root_path,如果不需要它自动处理前缀

@app.get("/items/{item_id}") # 路由从 / 开始,因为 /tool/ 已被 OpenResty 处理
async def read_item(item_id: int):
    return {"item_id": item_id, "app": "Tool App"}

在多数情况下,让代理(OpenResty)处理路径前缀的剥离,使后端应用(FastAPI)保持简单,是一个更好的做法。如果FastAPI需要生成包含此前缀的URL(例如在OpenAPI文档或重定向中),可以使用FastAPI(root_path="/yourprefix")

使用 Docker Compose 管理多个 FastAPI 服务和 OpenResty

这是一个简化的 docker-compose.yml 示例,展示了如何组织:

yaml 复制代码
version: '3.8'

services:
  fastapi_default:
    build: ./path_to_default_fastapi_app
    container_name: fastapi_default_container
    # command: gunicorn -w 2 -k uvicorn.workers.UvicornWorker main:app -b 0.0.0.0:8000
    expose:
      - "8000"
    networks:
      - web_internal_network

  fastapi_tool:
    build: ./path_to_tool_fastapi_app
    container_name: fastapi_tool_container
    # command: gunicorn -w 2 -k uvicorn.workers.UvicornWorker main:app -b 0.0.0.0:8001
    expose: # 假设它在容器内监听 8001
      - "8001" # 注意:这里是容器内部端口,OpenResty会通过服务名和这个端口访问
    networks:
      - web_internal_network

  fastapi_serviceb:
    build: ./path_to_serviceb_fastapi_app
    container_name: fastapi_serviceb_container
    # command: gunicorn -w 2 -k uvicorn.workers.UvicornWorker main:app -b 0.0.0.0:8002
    expose:
      - "8002"
    networks:
      - web_internal_network

  openresty:
    image: openresty/openresty:alpine # 或者你自定义的 OpenResty 镜像
    container_name: openresty_proxy_container
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./openresty_configs/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro
      - ./openresty_configs/conf.d/:/etc/nginx/conf.d/:ro
      - ./static_files/:/var/www/static/:ro
      # - /path/to/ssl_certs/:/etc/ssl/certs/:ro
    depends_on:
      - fastapi_default
      - fastapi_tool
      - fastapi_serviceb
    networks:
      - web_internal_network

networks:
  web_internal_network:
    driver: bridge

在上述 Docker Compose 配置中,OpenResty 的 nginx.confconf.d/your-site.conf 文件中的 upstreamproxy_pass 指令应该使用 Docker 的服务名:

nginx 复制代码
# 在 OpenResty 的配置文件中
upstream fastapi_default_app {
    server fastapi_default:8000; # 服务名:容器内端口
}

upstream fastapi_tool_app {
    server fastapi_tool:8001; # 服务名:容器内端口
}

upstream fastapi_serviceb_app {
    server fastapi_serviceb:8002; # 服务名:容器内端口
}

# ... 然后在 location 块中使用这些 upstream
location /tool/ {
    proxy_pass http://fastapi_tool_app/;
}
# ...
相关推荐
難釋懷1 天前
OpenResty查询Tomcat
tomcat·firefox·openresty
難釋懷1 天前
OpenResty监听请求
junit·openresty
難釋懷1 天前
OpenResty获取参数的API
junit·openresty
難釋懷2 天前
OpenResty请求参数处理
junit·openresty
難釋懷2 天前
OpenResty获取参数并返回
junit·openresty
難釋懷5 天前
OpenResty快速入门
junit·openresty
難釋懷7 天前
安装OpenResty
openresty
猫头虎1 个月前
web开发常见问题解决方案大全:502/503 Bad Gateway/Connection reset/504 timed out/400 Bad Request/401 Unauthorized
运维·前端·nginx·http·https·gateway·openresty
ZAEQgyKFs1 个月前
基于RBF神经网络的车速预测模型及其在混动汽车能量管理中的应用研究
openresty
FJW0208141 个月前
《Nginx 高级应用:变量、Rewrite、反向代理与 OpenResty 扩展》(3)
运维·nginx·openresty