Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions docs/guide/python/grpc-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ encoding. Use standard protobuf gRPC code generation when clients or tools must
consume protobuf message bytes directly.

Generated Python companions currently target the synchronous `grpcio` API. Use
regular `def` servicer methods, `grpc.server(...)`, `grpc.insecure_channel(...)`,
and Python iterators or generators for streaming RPCs. The compiler does not
regular `def` servicer methods, `grpc.server(...)`, standard `grpc.Channel`
instances, and Python iterators or generators for streaming RPCs. The generated
stub accepts any channel configured by your application. The compiler does not
generate `grpc.aio` stubs or service bases, so do not implement generated
servicer methods as `async def` unless you add a custom adapter outside the
generated companion.
generated companion. Python gRPC async support based on `grpc.aio` will be
available in the next Fory release.

## Install Dependencies

Expand Down Expand Up @@ -119,7 +121,8 @@ so service implementations do not perform manual Fory registration.

## Create a Client

Use the generated stub with a normal `grpcio` channel:
Use the generated stub with a normal `grpcio` channel. Production clients
usually pass a TLS/auth-configured channel:

```python
import grpc
Expand All @@ -129,7 +132,8 @@ import demo_greeter_grpc


def main():
with grpc.insecure_channel("localhost:50051") as channel:
credentials = grpc.ssl_channel_credentials()
with grpc.secure_channel("api.example.com:443", credentials) as channel:
stub = demo_greeter_grpc.GreeterStub(channel)
reply = stub.say_hello(demo_greeter.HelloRequest(name="Fory"))
print(reply.reply)
Expand All @@ -139,6 +143,14 @@ if __name__ == "__main__":
main()
```

For local tests and development, an insecure channel can be used explicitly:

```python
# Test-only channel. Use a TLS/auth-configured grpc.Channel in production.
with grpc.insecure_channel("localhost:50051") as channel:
stub = demo_greeter_grpc.GreeterStub(channel)
```

`grpcio` still owns channel options, credentials, deadlines, metadata, retries,
and interceptors.

Expand All @@ -158,12 +170,12 @@ service Greeter {

Generated Python code follows `grpcio` conventions:

| IDL shape | Servicer method shape | Stub method shape |
| ----------------------------------------- | ------------------------------------------- | ---------------------------------- |
| `rpc A (Req) returns (Res)` | returns one response object | returns one response object |
| `rpc A (Req) returns (stream Res)` | yields response objects | returns an iterator of responses |
| `rpc A (stream Req) returns (Res)` | consumes an iterator and returns a response | accepts an iterator of requests |
| `rpc A (stream Req) returns (stream Res)` | consumes and yields iterators | accepts and returns iterators |
| IDL shape | Servicer method shape | Stub method shape |
| ----------------------------------------- | ------------------------------------------- | -------------------------------- |
| `rpc A (Req) returns (Res)` | returns one response object | returns one response object |
| `rpc A (Req) returns (stream Res)` | yields response objects | returns an iterator of responses |
| `rpc A (stream Req) returns (Res)` | consumes an iterator and returns a response | accepts an iterator of requests |
| `rpc A (stream Req) returns (stream Res)` | consumes and yields iterators | accepts and returns iterators |

Servicer methods use snake_case names, while generated descriptors preserve the
exact IDL service and method names for the gRPC path.
Expand All @@ -188,7 +200,8 @@ class Greeter(demo_greeter_grpc.GreeterServicer):
Generated clients use the standard `grpcio` streaming call shapes:

```python
with grpc.insecure_channel("localhost:50051") as channel:
credentials = grpc.ssl_channel_credentials()
with grpc.secure_channel("api.example.com:443", credentials) as channel:
stub = demo_greeter_grpc.GreeterStub(channel)

for reply in stub.lots_of_replies(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ foryc service.fdl --go_out=./generated/go --grpc

输出包含:

| 文件 | 用途 |
| ------------------------------ | ------------------------------------ |
| `greeter/demo_greeter.go` | Fory model 类型和注册辅助逻辑 |
| 文件 | 用途 |
| ------------------------------ | ----------------------------------------- |
| `greeter/demo_greeter.go` | Fory model 类型和注册辅助逻辑 |
| `greeter/demo_greeter_grpc.go` | grpc-go client、server interface 和 codec |

生成的 Go 方法使用导出的 PascalCase 名称,例如 `SayHello`。底层 gRPC method path 保留
Expand Down Expand Up @@ -175,10 +175,10 @@ Fory service 支持 unary、server-streaming、client-streaming 和 bidirectiona
- Bidirectional streaming 使用生成的 stream client/server interface。
- 每个 message frame 都使用生成 codec。

## 运维语义
## Service 行为

生成的 service companion 只提供 Fory 序列化。deadline、取消、TLS、credential、unary/stream
interceptor、status code、metadata、名称解析、负载均衡、连接生命周期和 backoff 都保持标准 grpc-go 行为
interceptor、status code、metadata、名称解析、负载均衡、连接生命周期和 backoff 等 Service 行为都遵循标准 grpc-go。

## 故障排查

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,12 @@ service Greeter {

生成 Java service 方法遵循 grpc-java 约定:

| IDL shape | Server 方法形态 | Client 方法形态 |
| ----------------------------------------- | ------------------------------------------------------- | ---------------------------------- |
| `rpc A (Req) returns (Res)` | `void a(Req request, StreamObserver<Res> responses)` | blocking、async、future unary stub |
| `rpc A (Req) returns (stream Res)` | `void a(Req request, StreamObserver<Res> responses)` | blocking iterator 或 async observer |
| `rpc A (stream Req) returns (Res)` | `StreamObserver<Req> a(StreamObserver<Res> responses)` | async request observer |
| `rpc A (stream Req) returns (stream Res)` | `StreamObserver<Req> a(StreamObserver<Res> responses)` | async request observer |
| IDL shape | Server 方法形态 | Client 方法形态 |
| ----------------------------------------- | ------------------------------------------------------ | ----------------------------------- |
| `rpc A (Req) returns (Res)` | `void a(Req request, StreamObserver<Res> responses)` | blocking、async、future unary stub |
| `rpc A (Req) returns (stream Res)` | `void a(Req request, StreamObserver<Res> responses)` | blocking iterator 或 async observer |
| `rpc A (stream Req) returns (Res)` | `StreamObserver<Req> a(StreamObserver<Res> responses)` | async request observer |
| `rpc A (stream Req) returns (stream Res)` | `StreamObserver<Req> a(StreamObserver<Res> responses)` | async request observer |

Server 可以直接实现生成的 streaming 方法:

Expand Down Expand Up @@ -354,9 +354,9 @@ final class StreamingClient {

生成 descriptor 会保留 IDL 中的 service 和 method 名称作为 gRPC path。

## 运维语义
## Service 行为

生成的 service code 只替换 request/response 序列化。常规 gRPC 运维能力仍由 grpc-java 提供:
生成的 service code 只替换 request/response 序列化。常规 gRPC service 行为仍由 grpc-java 提供:

- Deadline 和取消
- TLS 和认证
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@ foryc service.fdl --javascript_out=./generated/javascript --grpc --grpc-web

输出包含:

| 文件 | 用途 |
| --------------------- | ------------------------------------- |
| 文件 | 用途 |
| --------------------- | --------------------------------------- |
| `service.ts` | interface、enum、union 和 schema helper |
| `service_grpc.ts` | Node.js `@grpc/grpc-js` server/client |
| `service_grpc_web.ts` | 浏览器 `grpc-web` client |
| `service_grpc.ts` | Node.js `@grpc/grpc-js` server/client |
| `service_grpc_web.ts` | 浏览器 `grpc-web` client |

## 实现 Node.js Server

Expand Down Expand Up @@ -278,9 +278,9 @@ stream.on("end", () => {
});
```

## 运维语义
## Service 行为

生成的 service code 只替换 request/response 序列化。常规 gRPC 运维能力仍由 transport package
生成的 service code 只替换 request/response 序列化。常规 gRPC service 行为仍由 transport package
提供:

- TLS 和 credential
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,12 @@ service Greeter {

Streaming RPC 使用 `kotlinx.coroutines.flow.Flow`。

| IDL shape | Server 方法 | Client 方法 |
| ----------------------------------------- | ---------------------------------------- | ---------------------------------------- |
| `rpc A (Req) returns (Res)` | `suspend fun a(request: Req): Res` | `suspend fun a(request: Req): Res` |
| `rpc A (Req) returns (stream Res)` | `fun a(request: Req): Flow<Res>` | `fun a(request: Req): Flow<Res>` |
| IDL shape | Server 方法 | Client 方法 |
| ----------------------------------------- | ----------------------------------------- | ----------------------------------------- |
| `rpc A (Req) returns (Res)` | `suspend fun a(request: Req): Res` | `suspend fun a(request: Req): Res` |
| `rpc A (Req) returns (stream Res)` | `fun a(request: Req): Flow<Res>` | `fun a(request: Req): Flow<Res>` |
| `rpc A (stream Req) returns (Res)` | `suspend fun a(requests: Flow<Req>): Res` | `suspend fun a(requests: Flow<Req>): Res` |
| `rpc A (stream Req) returns (stream Res)` | `fun a(requests: Flow<Req>): Flow<Res>` | `fun a(requests: Flow<Req>): Flow<Res>` |
| `rpc A (stream Req) returns (stream Res)` | `fun a(requests: Flow<Req>): Flow<Res>` | `fun a(requests: Flow<Req>): Flow<Res>` |

生成 method path 保留 schema 中的 service 和 method 名称,例如 `/demo.greeter.Greeter/SayHello`。

Expand Down Expand Up @@ -219,9 +219,9 @@ stub.chat(
}
```

## 运维语义
## Service 行为

生成的 service code 只替换 request/response 序列化。常规 gRPC 运维能力仍由 grpc-java 和
生成的 service code 只替换 request/response 序列化。常规 gRPC service 行为仍由 grpc-java 和
grpc-kotlin 提供:

- Deadline 和取消
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ Fory 可以为包含 service 定义的 schema 生成 Python gRPC companion modul
message bytes,请使用标准 protobuf gRPC 代码生成。

当前生成的 Python companion 面向同步 `grpcio` API。请使用普通 `def` servicer 方法、
`grpc.server(...)`、`grpc.insecure_channel(...)`,并用 Python iterator/generator 处理
streaming RPC。Compiler 不会生成 `grpc.aio` stub 或 service base,因此不要把生成 servicer
方法实现成 `async def`,除非你在生成 companion 外自行封装 adapter。
`grpc.server(...)`、标准 `grpc.Channel` 实例,并用 Python iterator/generator 处理 streaming RPC。
生成的 stub 可以接收应用自行配置的任意 channel。Compiler 不会生成 `grpc.aio` stub 或 service
base,因此不要把生成 servicer 方法实现成 `async def`,除非你在生成 companion 外自行封装 adapter。
基于 `grpc.aio` 的 Python gRPC async 支持将在下一个 Fory 版本提供。

## 添加依赖

Expand Down Expand Up @@ -66,9 +67,9 @@ foryc service.fdl --python_out=./generated/python --grpc

该 schema 会生成:

| 文件 | 用途 |
| ---------------------- | --------------------------------- |
| `demo_greeter.py` | Fory dataclass 和注册辅助逻辑 |
| 文件 | 用途 |
| ---------------------- | --------------------------------------- |
| `demo_greeter.py` | Fory dataclass 和注册辅助逻辑 |
| `demo_greeter_grpc.py` | `grpcio` stub、servicer base 和注册函数 |

Module 名称来自 Fory package,点号会替换成下划线;没有 package 的 schema 使用 `generated.py` 和
Expand Down Expand Up @@ -104,6 +105,8 @@ if __name__ == "__main__":

## 创建 Client

使用生成的 stub 和普通 `grpcio` channel。生产 client 通常传入配置了 TLS/认证的 channel:

```python
import grpc

Expand All @@ -112,7 +115,8 @@ import demo_greeter_grpc


def main():
with grpc.insecure_channel("localhost:50051") as channel:
credentials = grpc.ssl_channel_credentials()
with grpc.secure_channel("api.example.com:443", credentials) as channel:
stub = demo_greeter_grpc.GreeterStub(channel)
reply = stub.say_hello(demo_greeter.HelloRequest(name="Fory"))
print(reply.reply)
Expand All @@ -122,6 +126,15 @@ if __name__ == "__main__":
main()
```

本地测试和开发可以显式使用 insecure channel:

```python
# 仅用于本地测试和开发。
# 生产环境请使用配置了 TLS/认证的 grpc.Channel。
with grpc.insecure_channel("localhost:50051") as channel:
stub = demo_greeter_grpc.GreeterStub(channel)
```

Channel、credential、deadline、metadata、interceptor、retry 和 server lifecycle 都保持 `grpcio`
行为。

Expand All @@ -140,12 +153,12 @@ service Greeter {

生成 Python companion 遵循 `grpcio` 的 iterator/generator 约定:

| IDL shape | Servicer 方法形态 | Stub 方法形态 |
| ----------------------------------------- | ----------------------------------------- | ------------------------- |
| `rpc A (Req) returns (Res)` | 返回一个 response 对象 | 返回一个 response 对象 |
| `rpc A (Req) returns (stream Res)` | yield 多个 response 对象 | 返回 response iterator |
| `rpc A (stream Req) returns (Res)` | 消费 request iterator 并返回一个 response | 接收 request iterator |
| `rpc A (stream Req) returns (stream Res)` | 消费 request iterator 并 yield response | 接收并返回 iterator |
| IDL shape | Servicer 方法形态 | Stub 方法形态 |
| ----------------------------------------- | ----------------------------------------- | ---------------------- |
| `rpc A (Req) returns (Res)` | 返回一个 response 对象 | 返回一个 response 对象 |
| `rpc A (Req) returns (stream Res)` | yield 多个 response 对象 | 返回 response iterator |
| `rpc A (stream Req) returns (Res)` | 消费 request iterator 并返回一个 response | 接收 request iterator |
| `rpc A (stream Req) returns (stream Res)` | 消费 request iterator 并 yield response | 接收并返回 iterator |

Servicer 方法使用 snake_case 名称;生成 descriptor 会保留 IDL 中的 service 和 method 名称作为
gRPC path。每个 message frame 都通过 Fory serializer/deserializer 编码。
Expand All @@ -170,7 +183,8 @@ class Greeter(demo_greeter_grpc.GreeterServicer):
生成的 client 使用标准 `grpcio` streaming 调用形态:

```python
with grpc.insecure_channel("localhost:50051") as channel:
credentials = grpc.ssl_channel_credentials()
with grpc.secure_channel("api.example.com:443", credentials) as channel:
stub = demo_greeter_grpc.GreeterStub(channel)

for reply in stub.lots_of_replies(
Expand All @@ -193,9 +207,9 @@ with grpc.insecure_channel("localhost:50051") as channel:
print(reply.reply)
```

## 运维语义
## Service 行为

生成的 service companion 只提供 Fory serialization callback。运维行为仍遵循标准 `grpcio`:
生成的 service companion 只提供 Fory serialization callback。Service 行为仍遵循标准 `grpcio`:

- Deadline 和取消
- TLS 和认证 credential
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ foryc service.fdl --rust_out=./generated/rust --grpc

该 schema 会生成:

| 文件 | 用途 |
| ------------------------------ | ----------------------------------- |
| `demo_greeter.rs` | Fory model 类型和注册辅助逻辑 |
| `demo_greeter_service.rs` | 异步 service trait 与 gRPC path 常量 |
| 文件 | 用途 |
| ------------------------------ | ------------------------------------------ |
| `demo_greeter.rs` | Fory model 类型和注册辅助逻辑 |
| `demo_greeter_service.rs` | 异步 service trait 与 gRPC path 常量 |
| `demo_greeter_service_grpc.rs` | tonic client、server wrapper 和 Fory codec |

将生成文件加入 crate root:
Expand Down Expand Up @@ -283,7 +283,7 @@ while let Some(reply) = chat.message().await? {
Rust gRPC payload 必须满足 `Send + 'static`,这样 tonic 才能在线程间移动 request/response。
如果 request 或 response schema 使用非线程安全的引用元信息,Rust gRPC 生成会拒绝该 service。

## 运维语义
## Service 行为

生成的 service companion 只提供 Fory 序列化和 tonic binding。deadline、取消、TLS、认证、
Tower middleware、interceptor、status code、metadata、channel/server 生命周期和 backpressure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ foryc service.fdl --go_out=./generated/go --grpc

输出包含:

| 文件 | 用途 |
| ------------------------------ | ------------------------------------ |
| `greeter/demo_greeter.go` | Fory model 类型和注册辅助逻辑 |
| 文件 | 用途 |
| ------------------------------ | ----------------------------------------- |
| `greeter/demo_greeter.go` | Fory model 类型和注册辅助逻辑 |
| `greeter/demo_greeter_grpc.go` | grpc-go client、server interface 和 codec |

生成的 Go 方法使用导出的 PascalCase 名称,例如 `SayHello`。底层 gRPC method path 保留
Expand Down Expand Up @@ -175,10 +175,10 @@ Fory service 支持 unary、server-streaming、client-streaming 和 bidirectiona
- Bidirectional streaming 使用生成的 stream client/server interface。
- 每个 message frame 都使用生成 codec。

## 运维语义
## Service 行为

生成的 service companion 只提供 Fory 序列化。deadline、取消、TLS、credential、unary/stream
interceptor、status code、metadata、名称解析、负载均衡、连接生命周期和 backoff 都保持标准 grpc-go 行为
interceptor、status code、metadata、名称解析、负载均衡、连接生命周期和 backoff 等 Service 行为都遵循标准 grpc-go。

## 故障排查

Expand Down
Loading
Loading