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.git
The following commit(s) were added to refs/heads/main by this push:
new 680d50f3e docs: refine gRPC support guides (#3767)
680d50f3e is described below
commit 680d50f3eaad8524b04795fdd81abfdaaeebde36
Author: Shawn Yang <[email protected]>
AuthorDate: Tue Jun 16 21:26:49 2026 +0530
docs: refine gRPC support guides (#3767)
## Why?
## What does this PR do?
## Related issues
## AI Contribution Checklist
- [ ] Substantial AI assistance was used in this PR: `yes` / `no`
- [ ] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence of the final clean AI review results
from both fresh reviewers on the current PR diff or current HEAD after
the latest code changes.
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
---
docs/guide/java/grpc-support.md | 170 ++++++++++++++++++++++++++++++++--
docs/guide/javascript/grpc-support.md | 29 ++++++
docs/guide/kotlin/grpc-support.md | 127 ++++++++++++++++++++++---
docs/guide/python/grpc-support.md | 81 +++++++++++++---
docs/guide/rust/grpc-support.md | 115 ++++++++++++++++++++++-
5 files changed, 486 insertions(+), 36 deletions(-)
diff --git a/docs/guide/java/grpc-support.md b/docs/guide/java/grpc-support.md
index 7bebb010f..d1c899329 100644
--- a/docs/guide/java/grpc-support.md
+++ b/docs/guide/java/grpc-support.md
@@ -201,12 +201,170 @@ service Greeter {
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.
+| IDL shape | Server method shape
| Client method shape |
+| ----------------------------------------- |
------------------------------------------------------ |
-------------------------------------- |
+| `rpc A (Req) returns (Res)` | `void a(Req request,
StreamObserver<Res> responses)` | blocking, async, and future unary stub |
+| `rpc A (Req) returns (stream Res)` | `void a(Req request,
StreamObserver<Res> responses)` | blocking iterator or 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 implementations can use the generated streaming method shapes directly:
+
+```java
+package demo.greeter;
+
+import io.grpc.stub.StreamObserver;
+import java.util.ArrayList;
+import java.util.List;
+
+final class GreeterService extends GreeterGrpc.GreeterImplBase {
+ @Override
+ public void lotsOfReplies(
+ HelloRequest request, StreamObserver<HelloReply> responseObserver) {
+ HelloReply first = new HelloReply();
+ first.setReply("Hello, " + request.getName());
+ responseObserver.onNext(first);
+
+ HelloReply second = new HelloReply();
+ second.setReply("Welcome, " + request.getName());
+ responseObserver.onNext(second);
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public StreamObserver<HelloRequest> lotsOfGreetings(
+ StreamObserver<HelloReply> responseObserver) {
+ List<String> names = new ArrayList<>();
+ return new StreamObserver<>() {
+ @Override
+ public void onNext(HelloRequest request) {
+ names.add(request.getName());
+ }
+
+ @Override
+ public void onError(Throwable error) {
+ responseObserver.onError(error);
+ }
+
+ @Override
+ public void onCompleted() {
+ HelloReply reply = new HelloReply();
+ reply.setReply(String.join(", ", names));
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ }
+ };
+ }
+
+ @Override
+ public StreamObserver<HelloRequest> chat(
+ StreamObserver<HelloReply> responseObserver) {
+ return new StreamObserver<>() {
+ @Override
+ public void onNext(HelloRequest request) {
+ HelloReply reply = new HelloReply();
+ reply.setReply("Hello, " + request.getName());
+ responseObserver.onNext(reply);
+ }
+
+ @Override
+ public void onError(Throwable error) {
+ responseObserver.onError(error);
+ }
+
+ @Override
+ public void onCompleted() {
+ responseObserver.onCompleted();
+ }
+ };
+ }
+}
+```
+
+Generated clients return the standard grpc-java call shapes:
+
+```java
+package demo.greeter;
+
+import io.grpc.stub.StreamObserver;
+import java.util.Iterator;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+final class StreamingClient {
+ private final GreeterGrpc.GreeterBlockingStub blockingStub;
+ private final GreeterGrpc.GreeterStub asyncStub;
+
+ StreamingClient(
+ GreeterGrpc.GreeterBlockingStub blockingStub,
+ GreeterGrpc.GreeterStub asyncStub) {
+ this.blockingStub = blockingStub;
+ this.asyncStub = asyncStub;
+ }
+
+ void run() throws InterruptedException {
+ Iterator<HelloReply> replies =
+ blockingStub.lotsOfReplies(newRequest("Fory"));
+ while (replies.hasNext()) {
+ System.out.println(replies.next().getReply());
+ }
+
+ CountDownLatch greetingsDone = new CountDownLatch(1);
+ StreamObserver<HelloRequest> greetings =
+ asyncStub.lotsOfGreetings(new StreamObserver<>() {
+ @Override
+ public void onNext(HelloReply reply) {
+ System.out.println(reply.getReply());
+ }
+
+ @Override
+ public void onError(Throwable error) {
+ greetingsDone.countDown();
+ }
+
+ @Override
+ public void onCompleted() {
+ greetingsDone.countDown();
+ }
+ });
+ greetings.onNext(newRequest("Ada"));
+ greetings.onNext(newRequest("Grace"));
+ greetings.onCompleted();
+ greetingsDone.await(5, TimeUnit.SECONDS);
+
+ CountDownLatch chatDone = new CountDownLatch(1);
+ StreamObserver<HelloRequest> chat =
+ asyncStub.chat(new StreamObserver<>() {
+ @Override
+ public void onNext(HelloReply reply) {
+ System.out.println(reply.getReply());
+ }
+
+ @Override
+ public void onError(Throwable error) {
+ chatDone.countDown();
+ }
+
+ @Override
+ public void onCompleted() {
+ chatDone.countDown();
+ }
+ });
+ chat.onNext(newRequest("Fory"));
+ chat.onCompleted();
+ chatDone.await(5, TimeUnit.SECONDS);
+ }
+
+ private static HelloRequest newRequest(String name) {
+ HelloRequest request = new HelloRequest();
+ request.setName(name);
+ return request;
+ }
+}
+```
+
+The generated descriptors preserve the exact IDL service and method names for
+the gRPC path.
## Operations
diff --git a/docs/guide/javascript/grpc-support.md
b/docs/guide/javascript/grpc-support.md
index 32ca80517..696ecef24 100644
--- a/docs/guide/javascript/grpc-support.md
+++ b/docs/guide/javascript/grpc-support.md
@@ -23,6 +23,12 @@ Fory can generate JavaScript service companions for schemas
that define
services. The generated service code uses normal gRPC transports while request
and response objects are serialized with Fory instead of protobuf.
+Use this mode when both RPC peers are generated from the same Fory IDL,
+protobuf IDL, or FlatBuffers IDL and both sides expect Fory-encoded message
+bodies. Use normal protobuf gRPC generation for APIs that must be consumed by
+generic protobuf clients, reflection tools, or components that expect protobuf
+message bytes.
+
Use `--grpc` for Node.js server and client code. Use `--grpc-web` for browser
clients that call a gRPC-Web compatible server or proxy.
@@ -47,6 +53,9 @@ package used by your application.
## 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;
@@ -292,3 +301,23 @@ gRPC operational features still belong to the transport
package:
- Deadlines and cancellation
- Client and server interceptors
- Load balancing and deployment-specific proxy configuration
+
+## Troubleshooting
+
+### Missing gRPC Packages
+
+Add `@grpc/grpc-js` for Node.js companions or `grpc-web` for browser
+companions. `@apache-fory/core` intentionally does not depend on either
+transport package.
+
+### gRPC-Web Client-Streaming or Bidirectional RPCs Are Rejected
+
+gRPC-Web does not support client-streaming or bidirectional streaming. Generate
+Node.js companions with `--grpc` for those shapes, or expose unary and
+server-streaming methods to browser clients.
+
+### 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/kotlin/grpc-support.md
b/docs/guide/kotlin/grpc-support.md
index ae12f9e0f..cf517df44 100644
--- a/docs/guide/kotlin/grpc-support.md
+++ b/docs/guide/kotlin/grpc-support.md
@@ -23,7 +23,13 @@ Fory IDL can generate Kotlin coroutine gRPC companions. The
generated gRPC
files use normal grpc-java and grpc-kotlin APIs, while each request and
response
message is serialized with Fory.
-## Dependencies
+Use this mode when both RPC peers are generated from the same Fory IDL,
+protobuf IDL, or FlatBuffers IDL and both sides expect Fory-encoded message
+bodies. Use normal protobuf gRPC generation for APIs that must be consumed by
+generic protobuf clients, reflection tools, or components that expect protobuf
+message bytes.
+
+## Add Dependencies
Add Fory Kotlin, KSP, grpc-java, grpc-kotlin, coroutines, and one grpc-java
transport to the application or service module that compiles the generated
@@ -51,9 +57,10 @@ Use a different grpc-java transport if your application
already standardizes on
one. Generated Kotlin Fory gRPC does not require `grpc-protobuf` for payload
encoding.
-## Generate Code
+## Define a Service
-For a schema such as:
+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;
@@ -71,18 +78,27 @@ service Greeter {
}
```
-run:
+Generate Kotlin model and gRPC companion code with `--grpc`:
```bash
foryc service.fdl --kotlin_out=./generated/kotlin --grpc
```
-The compiler writes Kotlin model files, a schema module such as
-`ServiceForyModule.kt`, and one service companion such as `GreeterGrpcKt.kt`.
+For this schema, the Kotlin generator emits:
+
+| File | Purpose |
+| ------------------- | -------------------------------------------- |
+| `HelloRequest.kt` | Fory model type for the request |
+| `HelloReply.kt` | Fory model type for the response |
+| `ServiceForyModule` | Fory registration module for generated types |
+| `GreeterGrpcKt.kt` | Coroutine service base, stubs, and codecs |
+
Run KSP when compiling the generated model files so the schema serializers are
-available at runtime.
+available at runtime. Generated request and response types are registered by
+the generated schema module used by the service companion, so service
+implementations do not perform manual serializer registration.
-## Server
+## Implement a Server
Implement the generated coroutine base class and register it with a normal
grpc-java server.
@@ -108,7 +124,7 @@ val server = ServerBuilder
Unimplemented generated methods fail with gRPC `UNIMPLEMENTED`. Exceptions
thrown by your service method follow grpc-kotlin server behavior.
-## Client
+## Create a Client
Construct the generated coroutine stub directly from a grpc-java channel.
@@ -130,7 +146,18 @@ Channel construction, shutdown, deadlines, credentials,
interceptors, load
balancing, retries, and server lifecycle stay normal grpc-java/grpc-kotlin
responsibilities.
-## Streaming
+## 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);
+}
+```
Streaming RPCs use `kotlinx.coroutines.flow.Flow`.
@@ -144,6 +171,78 @@ Streaming RPCs use `kotlinx.coroutines.flow.Flow`.
The generated method path keeps the exact service and method names from the
schema, for example `/demo.greeter.Greeter/SayHello`.
+Server implementations can return or consume `Flow` values directly:
+
+```kotlin
+import demo.greeter.GreeterGrpcKt
+import demo.greeter.HelloReply
+import demo.greeter.HelloRequest
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.toList
+
+class GreeterService : GreeterGrpcKt.GreeterCoroutineImplBase() {
+ override fun lotsOfReplies(request: HelloRequest): Flow<HelloReply> = flow {
+ emit(HelloReply(reply = "Hello, ${request.name}"))
+ emit(HelloReply(reply = "Welcome, ${request.name}"))
+ }
+
+ override suspend fun lotsOfGreetings(
+ requests: Flow<HelloRequest>
+ ): HelloReply {
+ val names = requests.toList().joinToString(", ") { it.name }
+ return HelloReply(reply = names)
+ }
+
+ override fun chat(requests: Flow<HelloRequest>): Flow<HelloReply> =
+ requests.map { request ->
+ HelloReply(reply = "Hello, ${request.name}")
+ }
+}
+```
+
+Generated clients expose the matching coroutine and Flow APIs:
+
+```kotlin
+import demo.greeter.HelloRequest
+import kotlinx.coroutines.flow.flowOf
+
+stub.lotsOfReplies(HelloRequest(name = "Fory")).collect { reply ->
+ println(reply.reply)
+}
+
+val summary = stub.lotsOfGreetings(
+ flowOf(
+ HelloRequest(name = "Ada"),
+ HelloRequest(name = "Grace"),
+ )
+)
+println(summary.reply)
+
+stub.chat(
+ flowOf(
+ HelloRequest(name = "Fory"),
+ HelloRequest(name = "RPC"),
+ )
+).collect { reply ->
+ println(reply.reply)
+}
+```
+
+## Operations
+
+The generated service code only replaces request and response serialization.
+All normal gRPC operational features still belong to grpc-java and
+grpc-kotlin:
+
+- Deadlines and cancellations
+- TLS and authentication
+- Name resolution and load balancing
+- Client and server interceptors
+- Status codes and metadata
+- Channel pooling and lifecycle management
+
## Interoperability
Generated Kotlin service companions use Fory binary payloads inside gRPC
@@ -158,22 +257,22 @@ messages.
## Troubleshooting
-**Generated service file is missing**
+### Generated service file is missing
Pass `--grpc` together with `--kotlin_out`. Schemas without service definitions
only generate model files and the schema module.
-**Serializer class not found at runtime**
+### Serializer class not found at runtime
Ensure KSP runs for the generated Kotlin model sources and that
`fory-kotlin-ksp` uses the same Fory version as `fory-kotlin`.
-**gRPC classes are unresolved**
+### gRPC classes are unresolved
Add grpc-java and grpc-kotlin dependencies to the application module. Fory
Kotlin artifacts do not add those dependencies automatically.
-**A protobuf client cannot read responses**
+### A protobuf client cannot read responses
Fory gRPC uses Fory binary protocol payloads, not protobuf wire-format
messages.
Use generated Fory gRPC companions on both sides for the same service schema.
diff --git a/docs/guide/python/grpc-support.md
b/docs/guide/python/grpc-support.md
index c5106f91b..cdcff9224 100644
--- a/docs/guide/python/grpc-support.md
+++ b/docs/guide/python/grpc-support.md
@@ -28,6 +28,15 @@ 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.
+Generated Python companions currently target the synchronous `grpcio` API. Use
+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. Python gRPC async support based on `grpc.aio` will be
+available in the next Fory release.
+
## Install Dependencies
Install `grpcio` alongside `pyfory`. The generated companion imports `grpc`,
but
@@ -112,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
@@ -122,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)
@@ -132,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.
@@ -151,22 +170,58 @@ service Greeter {
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.
+| 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 |
-Example server-streaming implementation:
+Servicer methods use snake_case names, while generated descriptors preserve the
+exact IDL service and method names for the gRPC path.
+
+Server implementations can use Python iterators directly:
```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}"
- )
+ yield demo_greeter.HelloReply(reply=f"Hello, {request.name}")
+ yield demo_greeter.HelloReply(reply=f"Welcome, {request.name}")
+
+ def lots_of_greetings(self, request_iterator, context):
+ names = [request.name for request in request_iterator]
+ return demo_greeter.HelloReply(reply=", ".join(names))
+
+ def chat(self, request_iterator, context):
+ for request in request_iterator:
+ yield demo_greeter.HelloReply(reply=f"Hello, {request.name}")
+```
+
+Generated clients use the standard `grpcio` streaming call shapes:
+
+```python
+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(
+ demo_greeter.HelloRequest(name="Fory")
+ ):
+ print(reply.reply)
+
+ def greeting_requests():
+ yield demo_greeter.HelloRequest(name="Ada")
+ yield demo_greeter.HelloRequest(name="Grace")
+
+ summary = stub.lots_of_greetings(greeting_requests())
+ print(summary.reply)
+
+ def chat_requests():
+ yield demo_greeter.HelloRequest(name="Fory")
+ yield demo_greeter.HelloRequest(name="RPC")
+
+ for reply in stub.chat(chat_requests()):
+ print(reply.reply)
```
## Operations
diff --git a/docs/guide/rust/grpc-support.md b/docs/guide/rust/grpc-support.md
index ed636344a..4c9ca55f6 100644
--- a/docs/guide/rust/grpc-support.md
+++ b/docs/guide/rust/grpc-support.md
@@ -33,7 +33,7 @@ consume protobuf message bytes directly.
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.
+to build streaming responses or request streams.
```toml
[dependencies]
@@ -180,8 +180,117 @@ Generated Rust code follows tonic conventions:
- 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.
+Use the generated trait signatures as the source of truth for concrete
+associated stream types in your service implementation:
+
+```rust
+use demo_greeter::{HelloReply, HelloRequest};
+use demo_greeter_service::Greeter;
+use std::pin::Pin;
+use tokio_stream::{self as stream, Stream, StreamExt};
+use tonic::{Request, Response, Status};
+
+#[derive(Default)]
+struct MyGreeter;
+
+type ReplyStream =
+ Pin<Box<dyn Stream<Item = Result<HelloReply, Status>> + Send + 'static>>;
+
+#[tonic::async_trait]
+impl Greeter for MyGreeter {
+ type LotsOfRepliesStream = ReplyStream;
+ type ChatStream = ReplyStream;
+
+ async fn lots_of_replies(
+ &self,
+ request: Request<HelloRequest>,
+ ) -> Result<Response<Self::LotsOfRepliesStream>, Status> {
+ let name = request.into_inner().name;
+ let replies = vec![
+ Ok(HelloReply {
+ reply: format!("Hello, {name}"),
+ }),
+ Ok(HelloReply {
+ reply: format!("Welcome, {name}"),
+ }),
+ ];
+ Ok(Response::new(Box::pin(stream::iter(replies))))
+ }
+
+ async fn lots_of_greetings(
+ &self,
+ request: Request<tonic::Streaming<HelloRequest>>,
+ ) -> Result<Response<HelloReply>, Status> {
+ let mut requests = request.into_inner();
+ let mut names = Vec::new();
+ while let Some(request) = requests.next().await {
+ names.push(request?.name);
+ }
+ Ok(Response::new(HelloReply {
+ reply: names.join(", "),
+ }))
+ }
+
+ async fn chat(
+ &self,
+ request: Request<tonic::Streaming<HelloRequest>>,
+ ) -> Result<Response<Self::ChatStream>, Status> {
+ let replies = request.into_inner().map(|request| {
+ request.map(|request| HelloReply {
+ reply: format!("Hello, {}", request.name),
+ })
+ });
+ Ok(Response::new(Box::pin(replies)))
+ }
+}
+```
+
+Generated clients return tonic streaming responses:
+
+```rust
+use demo_greeter::HelloRequest;
+use demo_greeter_service_grpc::greeter_client::GreeterClient;
+use tokio_stream as stream;
+
+let mut client = GreeterClient::connect("http://[::1]:50051").await?;
+
+let mut replies = client
+ .lots_of_replies(HelloRequest {
+ name: "Fory".to_string(),
+ })
+ .await?
+ .into_inner();
+while let Some(reply) = replies.message().await? {
+ println!("{}", reply.reply);
+}
+
+let greetings = stream::iter(vec![
+ HelloRequest {
+ name: "Ada".to_string(),
+ },
+ HelloRequest {
+ name: "Grace".to_string(),
+ },
+]);
+let summary = client.lots_of_greetings(greetings).await?.into_inner();
+println!("{}", summary.reply);
+
+let chat_requests = stream::iter(vec![
+ HelloRequest {
+ name: "Fory".to_string(),
+ },
+ HelloRequest {
+ name: "RPC".to_string(),
+ },
+]);
+let mut chat = client.chat(chat_requests).await?.into_inner();
+while let Some(reply) = chat.message().await? {
+ println!("{}", reply.reply);
+}
+```
+
+The generated descriptors preserve the exact IDL service and method names for
+the gRPC path.
## Thread Safety and Payload Types
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]