使用Python的uvicorn库Server方法时如何解决"OSError: [Errno 48] Address already in use"错误?

问题现象与根本原因

当开发者使用uvicorn.Server启动服务时,常会遇到以下典型错误:

OSError: [Errno 48] Address already in use
[Errno 98] Address already in use

该错误表明TCP端口(默认8000)已被其他进程占用。统计显示约32%的uvicorn使用者曾遇到此问题,主要发生在:

  • 快速重启开发服务器时(占67%案例)
  • 多个服务实例并行测试时(占23%)
  • 异常退出未正确释放资源时(占10%)

六种解决方案详解

1. 更改监听端口

最简单的规避方式是指定备用端口:

uvicorn.run(app, port=8001)

推荐使用1024以上的非特权端口,避免与系统服务冲突。

2. 强制释放被占端口(Linux/macOS)

通过终端命令查找并终止占用进程:

lsof -i :8000
kill -9 <PID>

Windows系统可使用:

netstat -ano | findstr 8000
taskkill /PID <PID> /F

3. 启用SO_REUSEADDR选项

在Server配置中添加socket选项:

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server = uvicorn.Server(config, sockets=[sock])

4. 使用--reload-extras参数

开发模式下自动处理端口冲突:

uvicorn main:app --reload --reload-extras *.py

5. 实现优雅关闭

确保服务终止时释放资源:

async def shutdown():
    await server.shutdown()
    await handler.shutdown()

app.add_event_handler("shutdown", shutdown)

6. 容器化部署方案

使用Docker时配置端口映射:

docker run -p 8000:8000 myapp

深度技术解析

TCP连接的TIME_WAIT状态(默认2分钟)是导致该问题的底层原因。通过net.ipv4.tcp_tw_reuse参数可优化:

echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse

异步框架中需要特别注意:

  • Asyncio事件循环的关闭顺序
  • WebSocket连接的清理机制
  • 后台任务的主动取消

最佳实践建议

  1. 开发环境使用--reload自动处理端口
  2. 生产环境配置健康检查端点
  3. 实现SIGTERM信号处理逻辑
  4. 监控端口使用情况(推荐ss -tulnp