You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker Compose环境下React通过/api代理连接FastAPI WebSocket的方法

问题:Docker+Nginx环境下,React与FastAPI的WebSocket连接失败

当前技术栈:Python FastAPI(后端)、ReactJS(前端),通过Docker Compose部署多后端副本,使用Nginx实现负载均衡。在建立WebSocket连接时遇到问题,核心需求是通过GUI的/api代理正确连接FastAPI的WebSocket端点。

现有配置文件

Docker Compose配置

version: "3.9"
services:

  api:
    build:
      context: api
    ports:
      - 8000

  load_balance:
    image: nginx:latest
    volumes:
      - ./dimer_api/nginx.conf:/etc/nginx/nginx.conf:rw
    depends_on:
      - api
    ports:
      - 8000:8000

  gui:
    build:
      context: gui
    ports:
      - 80:80
    depends_on:
      - load_balance

FastAPI代码

websockets = []

@router.websocket("/api/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()

    try:
        while True:
            message = await websocket.receive_text()

            data = {"status": "ok", "type": "message", "message": message}

            await websocket.send_text(json.dumps(data))

    except WebSocketDisconnect:
        websockets.remove(message)


def create_app(debug: bool = False) -> FastAPI:
    app = FastAPI(debug=debug)

    app.include_router(router)

    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )

    return app

React的WebSocket连接代码

useEffect(() => {
  const socket = new WebSocket("ws://localhost:8000/api/ws"); // 此处连接失败

  socket.onopen = () => {
    console.log("WebSocket connection opened");
  };

  socket.onmessage = (event) => {
    const newData = JSON.parse(event.data);
    setData(newData);
  };

  return () => {
    socket.close();
  };
}, []);

Nginx负载均衡配置(load_balance服务)

events {
    worker_connections 1024;
}

http {
    server {
        listen 8000;

        location / {
            proxy_pass http://api:8000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
}

React应用的Nginx配置(gui服务)

server {

  listen 80;

  location /api {
    proxy_pass http://load_balance:8000;
  }

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

  error_page   500 502 503 504  /50x.html;

  location = /50x.html {
    root   /usr/share/nginx/html;
  }

}

核心疑问

如何利用GUI的/api代理建立与FastAPI的WebSocket连接?正确的连接地址应该如何设置?


解决方案

1. 修改React的WebSocket连接地址

放弃硬编码localhost:8000,通过前端Nginx的/api代理转发,使用相对路径或自动适配当前域名的写法:

useEffect(() => {
  // 自动适配当前页面的协议(http对应ws,https对应wss)和域名
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
  const socket = new WebSocket(`${protocol}//${window.location.host}/api/ws`);

  socket.onopen = () => {
    console.log("WebSocket connection opened");
  };

  socket.onmessage = (event) => {
    const newData = JSON.parse(event.data);
    setData(newData);
  };

  socket.onerror = (error) => {
    console.error("WebSocket error:", error);
  };

  return () => {
    socket.close();
  };
}, []);

更简洁的相对路径写法(浏览器会自动补全当前域名和协议):

const socket = new WebSocket("/api/ws");

2. 补充前端Nginx的/api代理WebSocket支持配置

当前前端Nginx的/api代理未配置WebSocket升级相关头,需补充:

server {

  listen 80;

  location /api {
    proxy_pass http://load_balance:8000;
    # 添加WebSocket支持的必要配置
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

  error_page   500 502 503 504  /50x.html;

  location = /50x.html {
    root   /usr/share/nginx/html;
  }

}

3. 修复FastAPI代码中的WebSocket断开逻辑bug

原代码中websockets.remove(message)会报错,因为message是接收的文本而非连接对象,正确逻辑应维护连接对象列表:

websockets = []

@router.websocket("/api/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    # 将连接对象加入列表
    websockets.append(websocket)
    try:
        while True:
            message = await websocket.receive_text()
            data = {"status": "ok", "type": "message", "message": message}
            await websocket.send_text(json.dumps(data))
    except WebSocketDisconnect:
        # 移除当前连接对象
        websockets.remove(websocket)

4. (可选)优化Nginx负载均衡配置(多后端场景)

若部署多个API副本,需在Nginx中配置上游服务器组实现负载均衡:

events {
    worker_connections 1024;
}

http {
    # 定义API上游服务器组,可添加多个后端节点
    upstream api_servers {
        server api:8000;
        # server api2:8000;
        # server api3:8000;
    }

    server {
        listen 8000;

        location / {
            proxy_pass http://api_servers;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

内容的提问来源于stack exchange,提问作者kaiserm99

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 16:04:56