跳转到内容

客户端

服务器在 8000 端口提供 HTTP,在 50051 端口提供 gRPC,由同一个二进制同时服务。HTTP 是默认选项,95% 的用户用的就是它 —— 下面这些范例覆盖持久连接和并发分发。gRPC 放在页面底部的进阶章节。

每个 endpoint、查询参数和响应 schema 都在 API 参考 里。本页讲 API 参考讲不到的:keep-alive 设置、并发分发、protobuf 代码生成。

HTTP

请使用长生命周期的客户端对象。每次都新建请求会反复付出 TCP 握手成本,并可能压垮服务器。只要复用客户端,所有标准 HTTP 库都会默认复用底层连接。

Python

进程启动时构造一个 requests.Session 并全局共享。Session 内部维护一个 TCP 连接池并跨调用复用,因此密集循环里 TCP 握手只发生一次。

Terminal window
pip install "requests>=2.32"
import requests
SESSION = requests.Session()
SESSION.headers.update({"Connection": "keep-alive"})
BASE_URL = "http://localhost:8000"
def ocr_raw(path: str, layout: bool = False) -> dict:
with open(path, "rb") as f:
data = f.read()
r = SESSION.post(
f"{BASE_URL}/ocr/raw",
data=data,
headers={"Content-Type": "image/png"},
params={"layout": 1} if layout else None,
timeout=30,
)
r.raise_for_status()
return r.json()
def ocr_pdf(path: str, mode: str = "ocr", dpi: int = 100) -> dict:
with open(path, "rb") as f:
data = f.read()
r = SESSION.post(
f"{BASE_URL}/ocr/pdf",
data=data,
params={"mode": mode, "dpi": dpi},
timeout=120,
)
r.raise_for_status()
return r.json()
print(ocr_raw("invoice.png"))

要并发分发,把同一个 SESSION 通过 ThreadPoolExecutor 派发即可。在下面这种简单 POST 模式下 Session 是线程安全的,worker 数量也直接决定服务器同时看到的在途请求数。

from concurrent.futures import ThreadPoolExecutor
def ocr_many(paths: list[str], workers: int = 8) -> list[dict]:
with ThreadPoolExecutor(max_workers=workers) as pool:
return list(pool.map(ocr_raw, paths))
results = ocr_many(["a.png", "b.png", "c.png", "d.png"])

进阶 —— gRPC

如果你需要流式传输、在极高 QPS 下进一步压缩线缆开销,或者你的服务体系本身就是 protobuf,再考虑 gRPC。绝大多数场景下 HTTP 更简单,吞吐也旗鼓相当。服务定义在 ocr.proto 里 —— 下载后用对应语言的代码生成器跑一遍即可。

Python (gRPC)

Terminal window
pip install "grpcio>=1.68" "grpcio-tools>=1.68" "protobuf>=5.28"
python -m grpc_tools.protoc -I proto \
--python_out=. --grpc_python_out=. \
proto/ocr.proto

会生成 ocr_pb2.pyocr_pb2_grpc.py。整个进程复用同一个 channel,它内部会复用 HTTP/2 流。

import grpc
from concurrent.futures import ThreadPoolExecutor
import ocr_pb2
import ocr_pb2_grpc
CHANNEL = grpc.insecure_channel(
"localhost:50051",
options=[
("grpc.keepalive_time_ms", 30_000),
("grpc.max_receive_message_length", 50 * 1024 * 1024),
],
)
STUB = ocr_pb2_grpc.OCRServiceStub(CHANNEL)
def recognize(path: str) -> ocr_pb2.OCRResponse:
with open(path, "rb") as f:
return STUB.Recognize(
ocr_pb2.OCRRequest(image=f.read(), layout=False),
timeout=30,
)
def recognize_many(paths: list[str], workers: int = 8):
with ThreadPoolExecutor(max_workers=workers) as pool:
return list(pool.map(recognize, paths))
results = recognize_many(["a.png", "b.png", "c.png"])

每个 endpoint 的请求与响应细节,请见 API 参考