# java-grpc-python
**Repository Path**: duxvfeng/java-grpc-python
## Basic Information
- **Project Name**: java-grpc-python
- **Description**: Java → gRPC → Python 通用调用框架
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-09-07
- **Last Updated**: 2026-09-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Java → gRPC → Python 通用调用框架
Java 客户端通过 **一套固定 proto** 以 `service + method + JSON 参数` 的形式调用 Python 端注册的任意处理器。
**Python 新增业务方法时:零 proto 改动、零 Java 重编译**,写一个函数加一个装饰器即自动生效。
> 📖 **完整使用文档(Python handler / Java API / Spring Boot Starter / 协议与错误码参考 / FAQ)**:[docs/USAGE.md](docs/USAGE.md)
```mermaid
flowchart LR
J["Java 客户端
GenericGrpcClient
invoke / invokeAsync / invokeStream"] -- "GenericRequest
service+method+payload_json" --> S["Python 服务端
GenericServiceServicer
统一 dispatch"]
S -- "路由" --> H1["@rpc_handler math.*"]
S -- "路由" --> H2["@rpc_handler string.*"]
S -- "路由" --> H3["你的业务 handler
(新增无需改框架)"]
```
## 目录结构
```
java-grpc-python/
├── pom.xml # Maven 聚合根(3 个模块)
├── proto/generic_service.proto # 两端共用的唯一协议(通用信封 + 流式 + ListMethods)
├── python-server/ # Python 服务端
│ ├── .venv/ # ★ 虚拟环境(python3.12),由 setup.sh 创建
│ ├── requirements.txt
│ ├── main.py # 启动入口
│ ├── scripts/{setup,start,stop}.sh
│ ├── generic_rpc/ # ★ 通用框架核心(与业务无关,可直接复用)
│ │ ├── registry.py # @rpc_handler 注册表;生成器 => 自动识别为流式
│ │ ├── servicer.py # dispatch、JSON 编解码、异常 → ErrorInfo、耗时统计
│ │ ├── server.py # 线程池、keepalive、健康检查、gRPC 反射、优雅关闭
│ │ ├── context.py / errors.py # RpcContext / RpcError
│ ├── gen/ # grpcio-tools 生成的 stub(提交,运行无需重生成)
│ └── app/ # 业务处理器:每个 .py 自动加载,_开头除外
├── java-client/ # 核心客户端库(Maven,stub 由插件自动生成)
│ └── src/main/java/com/xyyh/rpc/
│ ├── GenericGrpcClient.java # ★ 通用客户端:同步/异步/流式/原始 JSON
│ ├── GrpcClientConfig.java # host/port/deadline/keepalive/重试
│ ├── GenericRpcException.java# 业务错误与传输错误统一出口
│ └── demo/ClientDemo.java # 纯 Java 端到端演示
├── spring-boot-starter/ # ★ Spring Boot Starter(generic-rpc-spring-boot-starter)
│ └── src/main/java/com/xyyh/rpc/spring/
│ ├── GenericRpcAutoConfiguration.java # 自动装配 genericGrpcClient + 健康检查
│ ├── GenericRpcProperties.java # generic.rpc.* 配置(含命名多客户端)
│ ├── GenericRpcBeanRegistrar.java # @GenericRpc 接口扫描 + 命名客户端注册
│ ├── GenericRpc / RpcMethod / EnableGenericRpcClients # Feign 风格注解
│ └── GenericRpcFactoryBean / GenericRpcInvocationHandler / GenericRpcHealthIndicator
└── demo-spring-app/ # Spring Boot 演示应用(REST + actuator)
```
## 快速开始
```bash
# 1. Python 侧:创建 .venv + 装依赖 + 生成 stub(首次执行一次)
cd python-server && ./scripts/setup.sh
# 2. 启动服务(默认 50051;日志在 python-server/logs/server.log)
./scripts/start.sh # 停止: ./scripts/stop.sh
# 3. Java 侧(全模块):编译并安装到本地仓库
cd .. && mvn -B install -DskipTests
# 4a. 纯 Java 演示
cd java-client && mvn -q compile exec:java
# 4b. Spring Boot 演示(REST + actuator,默认 8080 端口)
cd ../demo-spring-app && mvn -B spring-boot:run
# curl "http://127.0.0.1:8080/api/sum?a=12&b=30"
```
gRPC 反射已开启,调试方便:`grpcurl -plaintext 127.0.0.1:50051 list`。
## Spring Boot Starter(推荐用法)
在你的 Spring Boot 3 应用中引入 starter,即可零样板代码使用:
```xml
com.xyyh
generic-rpc-spring-boot-starter
1.0.0
```
`application.yml`:
```yaml
generic:
rpc:
host: 127.0.0.1
port: 50051
deadline-ms: 5000
retry-attempts: 0 # UNAVAILABLE 重试次数
metadata: # 透传给 Python 的公共 metadata
client: my-app
clients: # 可选:命名多客户端(多 Python 服务)
backup:
host: 127.0.0.2
port: 50051
```
Starter 自动装配提供:
- **`genericGrpcClient` Bean**:`GenericGrpcClient` 单例,应用关闭时优雅 shutdown;
自定义同名 Bean 即可覆盖。
- **接口代理(Feign 风格)**:在主应用包(或 `@EnableGenericRpcClients(basePackages=...)`
指定的包)下声明接口,Starter 扫描后自动生成实现并注册为 Bean:
```java
@GenericRpc(service = "math")
public interface MathApi {
@RpcMethod("sum")
SumResult sum(SumParams params); // 一元
@RpcMethod("sum")
CompletableFuture sumAsync(SumParams p); // 异步
@RpcMethod("fibonacci")
List fibonacci(FibParams params); // 服务端流式自动收集为 List
}
// 直接注入使用
@Autowired MathApi mathApi;
mathApi.sum(new SumParams(1, 2)).sum();
```
映射规则:第一个参数即 payload;返回 `T` 为一元、`CompletableFuture` 为异步、
`Iterator` / `List` 为流式、`void` 为触发即忘;`@RpcMethod` 省略时用方法名。
多客户端:接口上 `@GenericRpc(service = "order", client = "backup")`。
- **健康检查**:应用引入 actuator 后自动注册 `genericRpc` 组件
(`/actuator/health` 会真实执行一次 ListMethods,展示已注册方法数)。
完整可运行示例见 `demo-spring-app`(REST 接口 + 异常 → HTTP 502 映射)。
## Java 端用法
```java
try (GenericGrpcClient client = GenericGrpcClient.newBuilder()
.host("127.0.0.1").port(50051)
.deadlineMs(5000) // 每次调用超时
.retryAttempts(2) // UNAVAILABLE 自动重试(默认 0)
.metadata(Map.of("tenant", "acme")) // 透传给 Python 的公共 metadata
.build()) {
// 一元调用,结果映射为 POJO / Map
SumResult r = client.invoke("math", "sum", Map.of("a", 12, "b", 30), SumResult.class);
// 原始 JSON 字符串
String json = client.invokeForJson("string", "reverse", Map.of("text", "abc"));
// 异步
CompletableFuture f = client.invokeAsync("math", "sum",
Map.of("a", 1, "b", 2), SumResult.class);
// 服务端流式:Python handler 每次 yield 即一帧
client.invokeStream("math", "fibonacci", Map.of("n", 8), FibItem.class)
.forEachRemaining(item -> System.out.println(item.value()));
// 服务发现
List methods = client.listMethods();
}
```
错误统一抛 `GenericRpcException`:`getCode()` 为稳定错误码,
`isTransportError()` 区分服务端业务错误与传输层错误(超时/断连)。
## Python 端:新增一个业务方法(就这两步)
1. 在 `app/` 下新建任意 `.py`(或改现有文件),写 handler:
```python
from generic_rpc import RpcContext, RpcError, rpc_handler
@rpc_handler("order", "create", description='创建订单,入参 {"sku": "A001", "qty": 2}')
def create_order(params: dict, ctx: RpcContext) -> dict:
if params.get("qty", 0) <= 0:
raise RpcError("数量必须为正数", code="INVALID_QTY", detail=params)
return {"order_id": "O-2024-001", "operator": ctx.get_metadata("tenant")}
```
2. 重启服务(`./scripts/stop.sh && ./scripts/start.sh`)即生效——
Java 端直接 `client.invoke("order", "create", ..., Map.class)`,无需改任何 proto/代码。
规则速记:
| 写法 | 行为 |
|---|---|
| 普通函数 `def h(params)` / `def h(params, ctx)` | 一元调用(Invoke) |
| 生成器函数(函数体含 `yield`) | 服务端流式(InvokeStream),每个 `yield` 一帧 |
| 返回值任意可 JSON 序列化对象 | 自动写入 `result_json` |
| 抛 `RpcError(code=..., detail=...)` | Java 端收到 `success=false` + ErrorInfo |
| 抛其他异常 | 归为 `INTERNAL_ERROR`,日志含堆栈 |
`params` 是解码后的 JSON(通常是 dict);`ctx` 提供 `request_id`、`metadata`、`streaming`。
## 协议要点(proto/generic_service.proto)
- `GenericRequest{request_id, service, method, payload_json, metadata}` →
`GenericResponse{request_id, success, result_json, error{code,message,detail_json}, cost_ms}`
- 错误模型:业务/框架错误走 `success=false + ErrorInfo` 的**正常响应**;
传输层错误走 gRPC Status。Java 端两者统一为 `GenericRpcException`。
- `ListMethods` 可在运维/自检时枚举服务端全部已注册方法。
## 生产化建议(框架已留扩展点)
- **TLS**:服务端 `add_secure_port` + SSL 凭据;客户端去掉 `usePlaintext()`
(见 `GenericGrpcClient` 构造器)。
- **鉴权**:metadata 已全程透传,可在 Java 端配置公共 metadata,
Python 端在 dispatch 前统一校验(扩展 `GenericServiceServicer`)。
- **注册中心/负载均衡**:客户端用 gRPC 原生 `xds:`/`dns:` target 替换
`forAddress(host, port)` 即可接入。
- **可观测性**:`GenericResponse.cost_ms`、request_id 已内置;
服务端日志格式见 `generic_rpc/servicer.py`。
- Python 侧默认 32 工作线程,流式/长任务可改 `main.py --workers`;
消息上限两端默认 32MiB。
## 环境要求
- Java 17+ 与 Maven 3.8+(stub 由 protobuf-maven-plugin 在编译期自动生成)
- Python 3.9+(`setup.sh` 优先用 python3.12 创建 `.venv`,可用
`PYTHON_BIN=python3 ./scripts/setup.sh` 指定解释器)
- 已验证版本:grpcio 1.83.1 / grpc-java 1.66.0 / protobuf 3.25.5 & 6.33.6