This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/fory-site.git
commit a76ce1571c7590c0ca5ea6c2004344ef8d265c10 Author: chaokunyang <[email protected]> AuthorDate: Fri Jun 12 07:46:39 2026 +0000 🔄 synced local 'docs/guide/' with remote 'docs/guide/' --- docs/guide/go/grpc-support.md | 241 +++++++++++++++++++++++++++++++++++ docs/guide/go/index.md | 1 + docs/guide/go/troubleshooting.md | 2 +- docs/guide/java/grpc-support.md | 236 ++++++++++++++++++++++++++++++++++ docs/guide/java/index.md | 1 + docs/guide/java/troubleshooting.md | 2 +- docs/guide/python/grpc-support.md | 210 ++++++++++++++++++++++++++++++ docs/guide/python/index.md | 1 + docs/guide/python/troubleshooting.md | 2 +- docs/guide/rust/grpc-support.md | 229 +++++++++++++++++++++++++++++++++ docs/guide/rust/index.md | 1 + docs/guide/rust/troubleshooting.md | 2 +- 12 files changed, 924 insertions(+), 4 deletions(-) diff --git a/docs/guide/go/grpc-support.md b/docs/guide/go/grpc-support.md new file mode 100644 index 0000000000..02418eba1c --- /dev/null +++ b/docs/guide/go/grpc-support.md @@ -0,0 +1,241 @@ +--- +title: gRPC Support +sidebar_position: 13 +id: grpc_support +license: | + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--- + +Fory can generate Go gRPC service companions for schemas that define services. +The generated code uses grpc-go for transport and a Fory-backed `CodecV2` for +request and response payloads. + +Use this mode when every RPC peer is generated from the same Fory IDL, protobuf +IDL, or FlatBuffers IDL and you want gRPC transport semantics with Fory payload +encoding. Use standard protobuf gRPC code generation when clients or tools must +consume protobuf message bytes directly. + +## Add Dependencies + +Add grpc-go to your module. Fory Go packages do not add gRPC as a hard +dependency. + +```bash +go get google.golang.org/grpc +``` + +Your generated code also imports the Fory Go module: + +```bash +go get github.com/apache/fory/go/fory +``` + +## Define a Service + +Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers +`rpc_service` definitions. A Fory IDL service looks like this: + +```protobuf +package demo.greeter; + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string reply = 1; +} + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); +} +``` + +Generate Go model and gRPC companion code with `--grpc`: + +```bash +foryc service.fdl --go_out=./generated/go --grpc +``` + +For this schema, the Go generator emits: + +| File | Purpose | +| ------------------------------ | -------------------------------------------- | +| `greeter/demo_greeter.go` | Fory model types and registration helpers | +| `greeter/demo_greeter_grpc.go` | grpc-go client, server interfaces, and codec | + +Generated Go methods use exported PascalCase names such as `SayHello`. The +underlying gRPC method path keeps the exact schema method name, so names such as +`sayHello` or `say_hello` continue to route by their schema spelling. + +## Implement a Server + +Implement the generated `GreeterServer` interface, create a grpc-go server with +the generated Fory codec, and register the service. + +```go +package main + +import ( + "context" + "log" + "net" + + "google.golang.org/grpc" + + "example.com/app/generated/go/greeter" +) + +type greeterService struct { + greeter.UnimplementedGreeterServer +} + +func (greeterService) SayHello( + ctx context.Context, + request *greeter.HelloRequest, +) (*greeter.HelloReply, error) { + return &greeter.HelloReply{Reply: "Hello, " + request.Name}, nil +} + +func main() { + listener, err := net.Listen("tcp", ":50051") + if err != nil { + log.Fatal(err) + } + + server := grpc.NewServer( + grpc.ForceServerCodecV2(greeter.CodecV2{}), + ) + greeter.RegisterGreeterServer(server, greeterService{}) + + if err := server.Serve(listener); err != nil { + log.Fatal(err) + } +} +``` + +`grpc.ForceServerCodecV2(...)` is required so the server decodes incoming frames +with the generated Fory codec instead of the default protobuf codec. + +Use the zero-value generated `CodecV2{}` for the service schema. The generated +client methods force the same codec for outgoing calls. + +## Create a Client + +The generated client constructor accepts a grpc-go connection. Generated client +methods force the generated Fory codec for each call. + +```go +package main + +import ( + "context" + "fmt" + "log" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "example.com/app/generated/go/greeter" +) + +func main() { + conn, err := grpc.NewClient( + "localhost:50051", + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + client := greeter.NewGreeterClient(conn) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + reply, err := client.SayHello(ctx, &greeter.HelloRequest{Name: "Fory"}) + if err != nil { + log.Fatal(err) + } + fmt.Println(reply.Reply) +} +``` + +## Streaming RPCs + +Fory service definitions can use unary, server-streaming, client-streaming, and +bidirectional streaming RPC shapes: + +```protobuf +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); + rpc LotsOfReplies (HelloRequest) returns (stream HelloReply); + rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply); + rpc Chat (stream HelloRequest) returns (stream HelloReply); +} +``` + +Generated Go code follows grpc-go conventions: + +- Unary methods take `context.Context`, a request pointer, and return a response + pointer plus `error`. +- Server-streaming client methods return a generated stream client. +- Client-streaming server methods receive a generated stream server. +- Bidirectional streaming methods use generated stream client and server + interfaces. +- The generated codec is used for every message frame, including streaming + frames. + +## Operations + +The generated service companion only supplies Fory serialization. Operational +behavior remains standard grpc-go behavior: + +- Deadlines and cancellations +- TLS and credentials +- Unary and stream interceptors +- Status codes and metadata +- Name resolution and load balancing +- Connection lifecycle and backoff + +## Troubleshooting + +### Missing `google.golang.org/grpc` Packages + +Add grpc-go to your module: + +```bash +go get google.golang.org/grpc +``` + +### `grpc: error while marshaling` + +Confirm that both the client and server use the generated `CodecV2{}` and that +the generated model file is compiled into the same package as the gRPC companion. + +### `UNIMPLEMENTED` + +Confirm that the generated service was registered with +`RegisterGreeterServer(...)`, and that the client and server were generated from +the same package, service, and method names. + +### Protobuf Clients Cannot Decode the Service + +Fory gRPC companions do not use protobuf wire encoding for messages. Use a +Fory-generated client for Fory-generated services, or provide a separate +protobuf service endpoint for generic protobuf clients. diff --git a/docs/guide/go/index.md b/docs/guide/go/index.md index 52f162fcd3..2cf4ebaf40 100644 --- a/docs/guide/go/index.md +++ b/docs/guide/go/index.md @@ -150,6 +150,7 @@ See [Xlang Serialization](xlang-serialization.md) for type mapping and compatibi | [Schema Evolution](schema-evolution.md) | Forward/backward compatibility | | [Custom Serializers](custom-serializers.md) | Extend serialization behavior | | [Thread Safety](thread-safety.md) | Concurrent usage patterns | +| [gRPC Support](grpc-support.md) | Fory payloads over grpc-go | | [Troubleshooting](troubleshooting.md) | Common issues and solutions | ## Related Resources diff --git a/docs/guide/go/troubleshooting.md b/docs/guide/go/troubleshooting.md index 7bcfb2d9f8..2c538385e2 100644 --- a/docs/guide/go/troubleshooting.md +++ b/docs/guide/go/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -sidebar_position: 13 +sidebar_position: 14 id: troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/java/grpc-support.md b/docs/guide/java/grpc-support.md new file mode 100644 index 0000000000..bfa1781e3e --- /dev/null +++ b/docs/guide/java/grpc-support.md @@ -0,0 +1,236 @@ +--- +title: gRPC Support +sidebar_position: 17 +id: grpc_support +license: | + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--- + +Fory can generate Java gRPC service companions for schemas that define +services. The generated service code uses normal grpc-java channels, servers, +deadlines, status codes, interceptors, and transport security, while request +and response objects are serialized with Fory instead of protobuf. + +Use this mode when both sides of the RPC are generated from the same Fory IDL, +protobuf IDL, or FlatBuffers IDL and you want gRPC transport semantics with +Fory payload encoding. Use standard protobuf gRPC code generation when your API +must be consumed by generic protobuf clients, reflection tools, or components +that expect protobuf message bytes. + +## Add Dependencies + +The generated Java service files compile against grpc-java. Fory Java artifacts +do not add gRPC as a hard dependency, so add grpc-java dependencies in your +application build and align the version with the rest of your service stack. + +Maven: + +```xml +<dependencies> + <dependency> + <groupId>org.apache.fory</groupId> + <artifactId>fory-core</artifactId> + <version>${fory.version}</version> + </dependency> + <dependency> + <groupId>io.grpc</groupId> + <artifactId>grpc-api</artifactId> + <version>${grpc.version}</version> + </dependency> + <dependency> + <groupId>io.grpc</groupId> + <artifactId>grpc-stub</artifactId> + <version>${grpc.version}</version> + </dependency> + <dependency> + <groupId>io.grpc</groupId> + <artifactId>grpc-netty-shaded</artifactId> + <version>${grpc.version}</version> + </dependency> +</dependencies> +``` + +Gradle: + +```kotlin +dependencies { + implementation("org.apache.fory:fory-core:$foryVersion") + implementation("io.grpc:grpc-api:$grpcVersion") + implementation("io.grpc:grpc-stub:$grpcVersion") + implementation("io.grpc:grpc-netty-shaded:$grpcVersion") +} +``` + +## Define a Service + +Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers +`rpc_service` definitions. A Fory IDL service looks like this: + +```protobuf +package demo.greeter; + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string reply = 1; +} + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); +} +``` + +Generate Java model and gRPC companion code with `--grpc`: + +```bash +foryc service.fdl --java_out=./generated/java --grpc +``` + +For this schema, the Java generator emits: + +| File | Purpose | +| ------------------------ | -------------------------------------------- | +| `HelloRequest.java` | Fory model type for the request | +| `HelloReply.java` | Fory model type for the response | +| `GreeterForyModule.java` | Fory registration module for generated types | +| `GreeterGrpc.java` | grpc-java service base class, stubs, codecs | + +## Implement a Server + +Extend the generated `GreeterGrpc.GreeterImplBase` class and register it with a +standard grpc-java `Server`. + +```java +package demo.greeter; + +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.stub.StreamObserver; + +final class GreeterService extends GreeterGrpc.GreeterImplBase { + @Override + public void sayHello( + HelloRequest request, StreamObserver<HelloReply> responseObserver) { + HelloReply reply = new HelloReply(); + reply.setReply("Hello, " + request.getName()); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } +} + +public final class GreeterServer { + public static void main(String[] args) throws Exception { + Server server = + ServerBuilder.forPort(50051) + .addService(new GreeterService()) + .build() + .start(); + server.awaitTermination(); + } +} +``` + +Generated request and response types are registered by the generated code, so +service implementations do not perform manual serializer registration. + +## Create a Client + +Use the generated stubs with an ordinary grpc-java channel: + +```java +package demo.greeter; + +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; + +public final class GreeterClient { + public static void main(String[] args) { + ManagedChannel channel = + ManagedChannelBuilder.forAddress("localhost", 50051) + .usePlaintext() + .build(); + try { + GreeterGrpc.GreeterBlockingStub stub = + GreeterGrpc.newBlockingStub(channel); + + HelloRequest request = new HelloRequest(); + request.setName("Fory"); + HelloReply reply = stub.sayHello(request); + System.out.println(reply.getReply()); + } finally { + channel.shutdownNow(); + } + } +} +``` + +For asynchronous calls, use `GreeterGrpc.newStub(channel)`. For future-based +unary calls, use `GreeterGrpc.newFutureStub(channel)`. + +## Streaming RPCs + +Fory service definitions can use the same gRPC streaming shapes: + +```protobuf +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); + rpc LotsOfReplies (HelloRequest) returns (stream HelloReply); + rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply); + rpc Chat (stream HelloRequest) returns (stream HelloReply); +} +``` + +Generated Java service methods follow grpc-java conventions: + +- Unary and server-streaming methods receive a request object and a + `StreamObserver` for responses. +- Client-streaming and bidirectional methods return a `StreamObserver` for + incoming requests and receive a `StreamObserver` for outgoing responses. +- Blocking stubs expose the grpc-java blocking APIs for supported streaming + shapes. + +## Operations + +The generated service code only replaces request and response serialization. +All normal gRPC operational features still belong to grpc-java: + +- Deadlines and cancellations +- TLS and authentication +- Name resolution and load balancing +- Client and server interceptors +- Status codes and metadata +- Channel pooling and lifecycle management + +## Troubleshooting + +### Missing `io.grpc` or Guava Classes + +Add the grpc-java dependencies shown above. Generated Fory service files import +grpc-java APIs, but Fory Java artifacts intentionally do not depend on gRPC. + +### `UNIMPLEMENTED` + +Confirm that the generated service implementation is registered with +`ServerBuilder.addService(...)`, and that the client and server were generated +from the same package, service, and method names. + +### Protobuf Clients Cannot Decode the Service + +Fory gRPC companions do not use protobuf wire encoding for messages. Use a +Fory-generated client for Fory-generated services, or provide a separate +protobuf service endpoint for generic protobuf clients. diff --git a/docs/guide/java/index.md b/docs/guide/java/index.md index ac8ed7f460..580701f477 100644 --- a/docs/guide/java/index.md +++ b/docs/guide/java/index.md @@ -244,6 +244,7 @@ ThreadSafeFory threadLocalFory = Fory.builder() - [Object Copy](object-copy.md) - Deep-copy Java object graphs in memory - [Compression](compression.md) - Integer, long, and array compression options - [Virtual Threads](virtual-threads.md) - Virtual-thread usage and pool sizing guidance +- [gRPC Support](grpc-support.md) - Fory payloads over grpc-java - [Type Registration](type-registration.md) - Class registration and security - [Custom Serializers](custom-serializers.md) - Implement custom serializers - [Xlang Serialization](xlang-serialization.md) - Serialize data for other languages diff --git a/docs/guide/java/troubleshooting.md b/docs/guide/java/troubleshooting.md index 141f645b92..2795f83762 100644 --- a/docs/guide/java/troubleshooting.md +++ b/docs/guide/java/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -sidebar_position: 17 +sidebar_position: 18 id: troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/python/grpc-support.md b/docs/guide/python/grpc-support.md new file mode 100644 index 0000000000..c5106f91b0 --- /dev/null +++ b/docs/guide/python/grpc-support.md @@ -0,0 +1,210 @@ +--- +title: gRPC Support +sidebar_position: 13 +id: grpc_support +license: | + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--- + +Fory can generate Python gRPC service companions for schemas that define +services. The generated modules use `grpcio` for transport and use Fory to +serialize request and response objects. + +Use this mode when every RPC peer is generated from the same Fory IDL, protobuf +IDL, or FlatBuffers IDL and you want gRPC transport semantics with Fory payload +encoding. Use standard protobuf gRPC code generation when clients or tools must +consume protobuf message bytes directly. + +## Install Dependencies + +Install `grpcio` alongside `pyfory`. The generated companion imports `grpc`, but +`pyfory` does not add gRPC as a hard dependency. + +```bash +pip install pyfory grpcio +``` + +## Define a Service + +Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers +`rpc_service` definitions. A Fory IDL service looks like this: + +```protobuf +package demo.greeter; + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string reply = 1; +} + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); +} +``` + +Generate Python model and gRPC companion code with `--grpc`: + +```bash +foryc service.fdl --python_out=./generated/python --grpc +``` + +For this schema, the Python generator emits: + +| File | Purpose | +| ---------------------- | ------------------------------------------- | +| `demo_greeter.py` | Fory dataclasses and registration helpers | +| `demo_greeter_grpc.py` | `grpcio` stub, servicer base, and registrar | + +The module name is derived from the Fory package by replacing dots with +underscores. A schema with no package uses `generated.py` and +`generated_grpc.py`. + +## Implement a Server + +Subclass the generated servicer and register it with a normal `grpcio` server. +Generated Python method names use snake_case, while the gRPC wire path keeps the +original IDL method name. + +```python +from concurrent import futures + +import grpc + +import demo_greeter +import demo_greeter_grpc + + +class Greeter(demo_greeter_grpc.GreeterServicer): + def say_hello(self, request, context): + return demo_greeter.HelloReply(reply=f"Hello, {request.name}") + + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=8)) + demo_greeter_grpc.add_servicer(Greeter(), server) + server.add_insecure_port("[::]:50051") + server.start() + server.wait_for_termination() + + +if __name__ == "__main__": + serve() +``` + +Generated request and response types are serialized by the generated companion, +so service implementations do not perform manual Fory registration. + +## Create a Client + +Use the generated stub with a normal `grpcio` channel: + +```python +import grpc + +import demo_greeter +import demo_greeter_grpc + + +def main(): + with grpc.insecure_channel("localhost:50051") as channel: + stub = demo_greeter_grpc.GreeterStub(channel) + reply = stub.say_hello(demo_greeter.HelloRequest(name="Fory")) + print(reply.reply) + + +if __name__ == "__main__": + main() +``` + +`grpcio` still owns channel options, credentials, deadlines, metadata, retries, +and interceptors. + +## Streaming RPCs + +Fory service definitions can use unary, server-streaming, client-streaming, and +bidirectional streaming RPC shapes: + +```protobuf +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); + rpc LotsOfReplies (HelloRequest) returns (stream HelloReply); + rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply); + rpc Chat (stream HelloRequest) returns (stream HelloReply); +} +``` + +Generated Python code follows `grpcio` conventions: + +- Unary stubs call `channel.unary_unary(...)`. +- Server-streaming stubs return an iterator over response objects. +- Client-streaming stubs accept an iterator of request objects. +- Bidirectional stubs accept a request iterator and return a response iterator. +- Servicer methods use snake_case names and return either a single response or + an iterator, depending on the streaming shape. + +Example server-streaming implementation: + +```python +class Greeter(demo_greeter_grpc.GreeterServicer): + def lots_of_replies(self, request, context): + for index in range(3): + yield demo_greeter.HelloReply( + reply=f"Hello {request.name}, response {index}" + ) +``` + +## Operations + +The generated service companion only supplies Fory serialization callbacks. +Operational behavior remains standard `grpcio` behavior: + +- Deadlines and cancellations +- TLS and authentication credentials +- Client and server interceptors +- Status codes, details, and metadata +- Channel and server lifecycle +- Thread pool sizing for synchronous servers + +## Troubleshooting + +### `ModuleNotFoundError: No module named 'grpc'` + +Install `grpcio` in the environment that runs the generated service module: + +```bash +pip install grpcio +``` + +### `TypeError: Unsupported gRPC servicer type` + +Pass an instance of the generated servicer subclass to +`demo_greeter_grpc.add_servicer(...)`. If the schema contains multiple services, +the generated registrar accepts only the matching generated servicer types. + +### `UNIMPLEMENTED` + +Confirm that the generated servicer was registered with the server, and that the +client and server were generated from the same package, service, and method +names. + +### Protobuf Clients Cannot Decode the Service + +Fory gRPC companions do not use protobuf wire encoding for messages. Use a +Fory-generated client for Fory-generated services, or provide a separate +protobuf service endpoint for generic protobuf clients. diff --git a/docs/guide/python/index.md b/docs/guide/python/index.md index 36c0cb9ba2..5b74d7de65 100644 --- a/docs/guide/python/index.md +++ b/docs/guide/python/index.md @@ -159,6 +159,7 @@ See [Native Serialization](native-serialization.md) for Python-only serializatio - [Type Registration](type-registration.md) - User-defined type registration - [Custom Serializers](custom-serializers.md) - Extend serialization behavior - [Row Format](row-format.md) - Zero-copy row format +- [gRPC Support](grpc-support.md) - Fory payloads over grpcio ## Links diff --git a/docs/guide/python/troubleshooting.md b/docs/guide/python/troubleshooting.md index bd4124d490..8dc56c160c 100644 --- a/docs/guide/python/troubleshooting.md +++ b/docs/guide/python/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -sidebar_position: 13 +sidebar_position: 14 id: troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/rust/grpc-support.md b/docs/guide/rust/grpc-support.md new file mode 100644 index 0000000000..3cb6f30117 --- /dev/null +++ b/docs/guide/rust/grpc-support.md @@ -0,0 +1,229 @@ +--- +title: gRPC Support +sidebar_position: 12 +id: grpc_support +license: | + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--- + +Fory can generate Rust gRPC service companions for schemas that define +services. The generated code uses `tonic` for transport and Fory for request and +response payload serialization. + +Use this mode when every RPC peer is generated from the same Fory IDL, protobuf +IDL, or FlatBuffers IDL and you want gRPC transport semantics with Fory payload +encoding. Use standard protobuf gRPC code generation when clients or tools must +consume protobuf message bytes directly. + +## Add Dependencies + +Add `tonic` and `bytes` to the crate that compiles the generated service files. +Fory Rust crates do not add gRPC as a hard dependency. Add `tokio` for async +servers and clients, and `tokio-stream` when your service implementation needs +to build streaming responses. + +```toml +[dependencies] +fory = "1.1.0" +bytes = "1" +tonic = { version = "0.14", features = ["transport"] } +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } +tokio-stream = "0.1" +``` + +Use dependency versions that are compatible with the rest of your service +stack. + +## Define a Service + +Service definitions can come from Fory IDL, protobuf IDL, or FlatBuffers +`rpc_service` definitions. A Fory IDL service looks like this: + +```protobuf +package demo.greeter; + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string reply = 1; +} + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); +} +``` + +Generate Rust model and gRPC companion code with `--grpc`: + +```bash +foryc service.fdl --rust_out=./generated/rust --grpc +``` + +For this schema, the Rust generator emits: + +| File | Purpose | +| ------------------------------ | -------------------------------------------- | +| `demo_greeter.rs` | Fory model types and registration helpers | +| `demo_greeter_service.rs` | Async service trait and gRPC path constants | +| `demo_greeter_service_grpc.rs` | tonic client, server wrapper, and Fory codec | + +Add the generated files to your crate root: + +```rust +pub mod demo_greeter; +pub mod demo_greeter_service; +pub mod demo_greeter_service_grpc; +``` + +## Implement a Server + +Implement the generated async trait and add the generated server wrapper to a +normal `tonic` server. + +```rust +use demo_greeter::{HelloReply, HelloRequest}; +use demo_greeter_service::Greeter; +use demo_greeter_service_grpc::greeter_server::GreeterServer; +use tonic::{Request, Response, Status}; + +#[derive(Default)] +struct MyGreeter; + +#[tonic::async_trait] +impl Greeter for MyGreeter { + async fn say_hello( + &self, + request: Request<HelloRequest>, + ) -> Result<Response<HelloReply>, Status> { + let request = request.into_inner(); + Ok(Response::new(HelloReply { + reply: format!("Hello, {}", request.name), + })) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let addr = "[::1]:50051".parse()?; + tonic::transport::Server::builder() + .add_service(GreeterServer::new(MyGreeter::default())) + .serve(addr) + .await?; + Ok(()) +} +``` + +Generated request and response types are serialized by the generated service +code, so service implementations do not perform manual Fory registration. + +## Create a Client + +Use the generated tonic client: + +```rust +use demo_greeter::HelloRequest; +use demo_greeter_service_grpc::greeter_client::GreeterClient; + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let mut client = GreeterClient::connect("http://[::1]:50051").await?; + let response = client + .say_hello(HelloRequest { + name: "Fory".to_string(), + }) + .await?; + println!("{}", response.into_inner().reply); + Ok(()) +} +``` + +`tonic` still owns channel configuration, TLS, deadlines, metadata, +interceptors, and transport lifecycle. + +## Streaming RPCs + +Fory service definitions can use unary, server-streaming, client-streaming, and +bidirectional streaming RPC shapes: + +```protobuf +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); + rpc LotsOfReplies (HelloRequest) returns (stream HelloReply); + rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply); + rpc Chat (stream HelloRequest) returns (stream HelloReply); +} +``` + +Generated Rust code follows tonic conventions: + +- Unary methods use `tonic::Request<T>` and return `tonic::Response<U>`. +- Server-streaming methods return a response whose inner value is a stream of + `Result<U, tonic::Status>`. +- Client-streaming and bidirectional methods receive `tonic::Streaming<T>`. +- The generated client module exposes matching async methods for each service + method. +- The generated codec is used for every message frame, including streaming + frames. + +Use the generated trait signatures as the source of truth for the concrete +associated stream types in your service implementation. + +## Thread Safety and Payload Types + +Generated Rust gRPC payloads must be `Send + 'static` so tonic can move request +and response values across async tasks. If a schema uses non-thread-safe +reference metadata for a request or response type, Rust gRPC generation rejects +that service. Use thread-safe reference shapes for gRPC payloads, or keep the +non-thread-safe type out of the RPC boundary. + +## Operations + +The generated service companion only supplies Fory serialization and tonic +bindings. Operational behavior remains standard tonic behavior: + +- Deadlines and cancellations +- TLS and authentication +- Tower middleware and interceptors +- Status codes and metadata +- Channel and server lifecycle +- Backpressure through async streams + +## Troubleshooting + +### Missing `tonic` or `bytes` Crates + +Add the dependencies shown above to the crate that compiles the generated +service files. + +### `UNIMPLEMENTED` + +Confirm that the generated server wrapper was added with +`Server::builder().add_service(...)`, and that the client and server were +generated from the same package, service, and method names. + +### Non-Thread-Safe Reference Errors During Code Generation + +Rust gRPC payloads must be `Send + 'static`. Change the request or response +schema to use thread-safe reference shapes, or wrap the non-thread-safe data in a +type that is not part of the gRPC payload. + +### Protobuf Clients Cannot Decode the Service + +Fory gRPC companions do not use protobuf wire encoding for messages. Use a +Fory-generated client for Fory-generated services, or provide a separate +protobuf service endpoint for generic protobuf clients. diff --git a/docs/guide/rust/index.md b/docs/guide/rust/index.md index 93c3537ed2..c2212a1f7b 100644 --- a/docs/guide/rust/index.md +++ b/docs/guide/rust/index.md @@ -193,3 +193,4 @@ fory-derive/ # Procedural macros - [Polymorphism](polymorphism.md) - Trait object serialization - [Custom Serializers](custom-serializers.md) - Extend serialization behavior - [Row Format](row-format.md) - Zero-copy row-based format +- [gRPC Support](grpc-support.md) - Fory payloads over tonic diff --git a/docs/guide/rust/troubleshooting.md b/docs/guide/rust/troubleshooting.md index 6b46d1b6d3..285c99a8da 100644 --- a/docs/guide/rust/troubleshooting.md +++ b/docs/guide/rust/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -sidebar_position: 12 +sidebar_position: 13 id: troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
