Files
zhuangang/topics/backend/grpc-microservices/examples/grpc-user/demo.py
T

82 lines
3.1 KiB
Python

# requires: grpc, grpc_tools
"""gRPC 演示:从 user.proto 生成代码 → 启动服务端 → 客户端调用(一元、服务端流、超时、错误码)。
运行:pip install grpcio grpcio-tools && python demo.py
生成的代码放在临时目录,不污染仓库。
"""
import importlib
import sys
import tempfile
import time
from concurrent import futures
from pathlib import Path
import grpc
from grpc_tools import protoc
HERE = Path(__file__).resolve().parent
def generate(out: Path):
rc = protoc.main(["protoc", f"-I{HERE}", f"--python_out={out}", f"--grpc_python_out={out}", str(HERE / "user.proto")])
assert rc == 0, "protoc 生成失败"
sys.path.insert(0, str(out))
return importlib.import_module("user_pb2"), importlib.import_module("user_pb2_grpc")
def main() -> None:
with tempfile.TemporaryDirectory() as tmp:
pb, pb_grpc = generate(Path(tmp))
users = {i: pb.User(id=i, name=f"用户{i}", email=f"u{i}@example.com") for i in range(1, 6)}
class UserService(pb_grpc.UserServiceServicer):
def GetUser(self, request, context):
if request.id == 99:
time.sleep(0.5) # 模拟慢请求
user = users.get(request.id)
if user is None:
context.abort(grpc.StatusCode.NOT_FOUND, f"用户 {request.id} 不存在")
return user
def ListUsers(self, request, context):
for i, u in enumerate(users.values()):
if i >= request.page_size:
break
yield u
server = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
pb_grpc.add_UserServiceServicer_to_server(UserService(), server)
port = server.add_insecure_port("127.0.0.1:0") # 0 = 随机可用端口
server.start()
try:
with grpc.insecure_channel(f"127.0.0.1:{port}") as ch:
stub = pb_grpc.UserServiceStub(ch)
u = stub.GetUser(pb.GetUserRequest(id=1), timeout=1.0) # 每次调用都带超时
print("一元调用:", u.id, u.name)
assert u.name == "用户1"
names = [x.name for x in stub.ListUsers(pb.ListUsersRequest(page_size=3), timeout=1.0)]
print("服务端流:", names)
assert len(names) == 3
try:
stub.GetUser(pb.GetUserRequest(id=42), timeout=1.0)
raise AssertionError("应该返回 NOT_FOUND")
except grpc.RpcError as e:
print("错误码:", e.code().name, e.details())
assert e.code() == grpc.StatusCode.NOT_FOUND
try:
stub.GetUser(pb.GetUserRequest(id=99), timeout=0.1)
raise AssertionError("应该超时")
except grpc.RpcError as e:
print("超时:", e.code().name)
assert e.code() == grpc.StatusCode.DEADLINE_EXCEEDED
finally:
server.stop(grace=None)
print("gRPC 演示通过")
if __name__ == "__main__":
main()