Win Server 环境部署 FastAPI

1、部署 python 环境

1)下载 python-3.14.2-embed-amd64.zip

2)解压后将 python-3.14.2-embed-amd64 目录放置到服务器。

3)获取 get-pip.py。前往 https://pip.pypa.io/en/stable/installation/ 找到 Download the script, from https://bootstrap.pypa.io/get-pip.py.

4)安装 pip,在 python-3.14.2-embed-amd64 目录执行 python get-pip.py,此时多出 LibScripts 目录。

5)在 python-3.14.2-embed-amd64 目录找到 python314._pth 文件,移除 #import site 的注释,保存。

1
2
3
4
5
python314.zip
.

# Uncomment to run site.main() automatically
import site

6)设置环境变量:python-3.14.2-embed-amd64\ 以及 python-3.14.2-embed-amd64\Scripts\

2、复制项目文件

1)服务器全局安装 poetry 进行依赖管理。

2)项目文件保留 poetry.lockpyproject.toml

3)恢复依赖可选择全局或项目目录中。

3、启动服务端

1)项目代码文件 main.py 如下

1
2
3
4
5
6
7
8
9
10
11
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"message": "Hello!"}

@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}

2)此处选择将依赖放置到项目中,执行 poetry shell 后,通过 uvicorn --host 0.0.0.0 --port 8000 main:app 启动。

3)建立 powershell 脚本,便于执行或添加到计划任务:

1
2
3
4
5
6
7
8
9
10
# start-server.ps1

# 切换到项目目录
Set-Location "C:\Users\test\Desktop\py-play"
# 启动项目。注意不要进入交互式shell
poetry run uvicorn --host 0.0.0.0 --port 8000 main:app

# 暂停
Write-Host "按任意键继续..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

WSGI 和 ASGI

在 Web 开发领域,选择合适的服务器网关接口(Server Gateway Interface,简称 SGI)对于提高 Web 应用程序的性能和并发性至关重要。在 Python 中,有两种常见的 SGI:WSGI 和 ASGI。

WSGI(Web Server Gateway Interface)是一种同步的网关接口,它使用线程来处理每个请求,通过简单的函数调用——一个可调用的对象(通常是一个函数)来处理 HTTP 请求,并返回 HTTP 响应。这意味着在一个请求被处理期间,其他请求必须等待。这种同步处理方式在处理大量并发请求时可能会导致性能问题,因此 WSGI 适用于处理低并发、IO 密集型的应用程序,在这种环境下,同步的请求处理足够高效。由于这种限制,WSGI 的典型实现包括 Gunicorn、uWSGI 和 mod_wsgi 等。

随着异步编程概念的普及,出现了另一种网关接口——ASGI(Asynchronous Server Gateway Interface)。ASGI 是一种异步的网关接口,允许同时处理多个请求。通过使用事件循环和协程,ASGI 可以实现高并发性能,从而更好地应对大量并发请求。ASGI 的典型实现包括 Daphne、Uvicorn 和 Hypercorn 等。

除了处理请求的方式不同,WSGI 和 ASGI 在支持的协议方面也存在差异。 WSGI 是基于 HTTP 协议模式开发的,不支持 WebSocket。这意味着使用 WSGI 的应用程序无法直接处理 WebSocket 连接,需要借助其他库或中间件来实现。

相比之下,ASGI 不仅支持现有的 Web 开发中的一些新的协议标准,还支持原有模式和 WebSocket 的扩展。这使得使用 ASGI 的应用程序可以更灵活地适应不断变化的 Web 技术。

WSGI 和 ASGI 在请求方面的差异,主要体现在同步和异步处理请求上。通过例子来进一步理解 WSGI 和 ASGI 之间的区别。假设有一个 Python Web 应用程序,它需要连接到 Web 服务器并处理来自客户端的请求。我们可以使用 WSGI 或 ASGI 来实现这个连接。

使用 WSGI 的例子:

1
2
3
4
5
6
7
8
9
10
11
from wsgiref.simple_server import make_server

def application(environ, start_response):
status = '200 OK'
headers = [('Content-type', 'text/plain')]
start_response(status, headers)
return [b"Hello World"]

httpd = make_server('', 8000, application)
print("Serving on port 8000...")
httpd.serve_forever()

在上面的例子中,我们使用 WSGI 的典型实现之一——make_server 函数来创建了一个简单的 Web 服务器。我们直接访问 localhost:8000, 可以看到浏览器显示 Hello World 字样。

这个函数接受三个参数:请求处理函数、服务器的地址和端口号。当客户端发送请求时,服务器将调用请求处理函数,并将请求的详细信息传递给它。请求处理函数可以返回一个响应,服务器将将其发送回客户端。

使用 ASGI 的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from asgiref.sync import async_to_sync
from daphne import Daphne
from http import HTTPStatus import HTTPStatus
from channels.layers import get_channel_layer
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import os
import sys
import asyncio

channel_layer = get_channel_layer()
asgi_app = get_asgi_application()

async def application(scope, receive, send):
if scope['type'] == 'http':
await asgi_app(scope, receive, send)
elif scope['type'] == 'websocket':
await async_to_sync(channel_layer)(scope, receive, send)
else:
raise Exception('Unknown scope type')

在上面的例子中,我们使用 ASGI 的典型实现之一——Daphne 来创建了一个异步的 Web 服务器。Daphne 是一个 ASGI 服务器,它能够同时处理 HTTP 和 WebSocket 连接。在应用程序中,我们首先获取了一个 channel layer 对象和一个 ASGI 应用程序对象。然后,我们定义了一个异步的请求处理函数,它根据请求的类型选择使用 ASGI 应用程序或 channel layer 来处理请求。对于 HTTP 请求,我们直接调用 ASGI 应用程序;对于 WebSocket 请求,我们使用 channel layer 来处理。最后,我们将请求处理函数传递给 Daphne 服务器,让它来处理来自客户端的请求。

通过这个例子,我们可以看到 WSGI 和 ASGI 的主要区别在于它们处理请求的方式和性能。WSGI 使用同步的方式处理请求,每个请求都需要在一个单独的线程中执行。而 ASGI 使用异步的方式处理请求,可以同时处理多个请求,并通过事件循环和协程实现高并发性能。因此,选择使用 WSGI 还是 ASGI 取决于你的应用程序的具体需求和性能要求。

Waitress

Waitress 是一款基于 Python 的纯 Python 实现的 WSGI(Web Server Gateway Interface)应用服务器,专为处理 HTTP 请求设计。与 Nginx、Apache 等传统服务器不同,Waitress 以轻量级、易部署为核心优势,尤其适合中小型 Python Web 应用(如 Flask、Django)的本地开发测试或低流量生产环境。其核心特性包括:

  • 纯 Python 实现:无需依赖 C 扩展或外部库,跨平台兼容性强(Windows/Linux/macOS)。
  • 多线程架构:通过多线程处理并发请求,避免单线程阻塞问题。
  • WSGI 标准兼容:严格遵循 PEP 3333 标准,无缝集成 Flask、Django 等框架。
  • 配置灵活:支持通过命令行参数或配置文件动态调整参数(如线程数、端口)。

Waitress 的核心架构由请求处理器(Request Handler)、线程池(Thread Pool)和 WSGI 调用层(WSGI Invoker)三部分组成:

  • 请求处理器:监听指定端口(默认 8080),接收 HTTP 请求并解析头部信息。
  • 线程池管理:默认创建 4 个工作线程(可通过–threads 参数调整),每个线程独立处理请求生命周期(解析 → 路由 → 应用处理 → 响应)。
  • WSGI 调用层:将 HTTP 请求转换为 WSGI 环境字典(environ),调用应用对象的 application 可调用对象,并捕获返回值生成 HTTP 响应。

示例:通过 Flask 集成 Waitress

1
2
3
4
5
6
7
8
9
10
11
from flask import Flask
from waitress import serve

app = Flask(__name__)

@app.route("/")
def hello():
return "Hello, Waitress!"

if __name__ == "__main__":
serve(app, host="0.0.0.0", port=8000)

此示例中,Waitress 作为 WSGI 服务器直接运行 Flask 应用,无需额外配置。Waitress 仅支持 WSGI,若需运行 FastAPI 等 ASGI 应用,需通过适配器转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
from fastapi import FastAPI
from waitress import serve
from a2wsgi import ASGIMiddleware

app = FastAPI()
wsgi_app = ASGIMiddleware(app)

@app.get("/")
def hello():
return "Hello, Waitress!"

if __name__ == "__main__":
serve(wsgi_app, host="0.0.0.0", port=8000)

相关问题可参考文末 __call__() missing 1 required positional argument: 'send' 的处理。上述依赖详见 https://pypi.org/project/a2wsgi/

线程数配置策略

Waitress 的默认线程数(4)适用于低并发场景。对于高并发应用,需根据 CPU 核心数和请求类型调整:

  • CPU 密集型任务:线程数 ≈CPU 核心数(避免过多线程导致上下文切换开销)。
  • IO 密集型任务:线程数可增至 CPU 核心数的 2-4 倍(利用 IO 等待时间并行处理)。

命令行调优示例:

1
waitress-serve --threads=16 myapp:app

保持连接(Keep-Alive)优化

Waitress 默认关闭 Keep-Alive 以减少资源占用。若应用需长期连接(如 WebSocket),可通过–connection-limit 参数限制单个连接的持续时间:

1
waitress-serve --connection-limit=30 myapp:app  # 30秒后关闭空闲连接

缓冲区大小调整

对于大文件传输(如视频流),增大–asyncore-loop-timeout 和–buffer-size 可避免数据截断:

1
waitress-serve --buffer-size=65536 myapp:app  # 设置64KB缓冲区

其他

The error TypeError: __call__() missing 1 required positional argument: 'send' typically occurs when you try to run a FastAPI application using a WSGI server instead of an ASGI server. FastAPI is designed to work with ASGI servers like Uvicorn, Daphne, or Hypercorn.

Example

1
2
3
4
5
6
7
8
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000)

Common Causes and Solutions

Using WSGI Instead of ASGI

FastAPI is an asynchronous web framework and is not compatible with WSGI servers like Gunicorn without an ASGI worker.

Example:

1
gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app

Solution: Ensure you are using an ASGI server or adding an ASGI worker to Gunicorn.

Incorrect Server Configuration

Ensure that your server configuration is set up correctly to use an ASGI server.

Example:

1
uvicorn main:app --host 0.0.0.0 --port 8000

Solution: Use Uvicorn directly or configure Gunicorn to use Uvicorn workers.

Deployment on Platforms Requiring WSGI

If deploying on platforms like Google App Engine, which requires WSGI, you can use a middleware to convert ASGI to WSGI.

Example:

1
2
3
4
5
6
7
from fastapi import FastAPI
from a2wsgi import ASGIMiddleware
app = FastAPI()
wsgi_app = ASGIMiddleware(app)
if __name__ == "__main__":
from waitress import serve
serve(wsgi_app, host="0.0.0.0", port=8080)

Solution: Use ASGIMiddleware to wrap your FastAPI app for compatibility with WSGI servers.

By following these steps, you can resolve the __call__() missing 1 required positional argument: 'send' error and successfully run your FastAPI application.