自定义元数据传输
了解如何通过 gRPC 请求和响应发送及接收作为元数据的自定义键值对
自定义元数据传输 是 CoddyKit 上的免费 gRPC & High Performance APIs 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 gRPC & High Performance APIs 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 gRPC & High Performance APIs 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
What is gRPC Metadata?
In gRPC, metadata refers to key-value pairs that are attached to an RPC call, similar to HTTP headers.
Unlike the main message payload, metadata carries information about the call itself, rather than the application data being transmitted.
- It's for auxiliary data.
- It travels with requests and responses.
- It's separate from your protobuf messages.
Why Use Metadata?
Metadata is incredibly useful for carrying information that doesn't belong in your service's primary request or response messages.
Common use cases include:
- Authentication Tokens: Sending JWTs or API keys.
- Tracing IDs: Propagating unique request IDs for distributed tracing.
- Custom Headers: Any other contextual information needed by your services.
Metadata Structure & Types
Metadata consists of a list of key-value pairs.
- Keys: Are case-insensitive ASCII strings.
- Values: Can be either ASCII strings or binary data.
For binary values, the key must end with -bin (e.g., auth-token-bin). gRPC handles the encoding for these.
Client: Sending Request Metadata
Clients attach metadata to outgoing requests using the gRPC Metadata class. This object is then added to the gRPC call stub.
You create Metadata.Key objects to define your header keys and their marshallers (how they are converted to/from strings or bytes).
Client: Sending Metadata Example
This Java client snippet shows how to create a Metadata object and attach it to your stub before making an RPC call. Ensure your `hello.proto` is compiled.
package com.coddykit.grpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import io.grpc.StatusRuntimeException;
import java.util.concurrent.TimeUnit;
public class GrpcClient {
private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;
public GrpcClient(String host, int port) {
this.channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
blockingStub = GreeterGrpc.newBlockingStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
public void sayHello(String name, String customValue) {
System.out.println("Sending custom-key: " + customValue);
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
// 1. Create Metadata object
Metadata headers = new Metadata();
// 2. Define a Metadata.Key for your header
Metadata.Key<String> customKey = Metadata.Key.of(
"custom-key", Metadata.ASCII_STRING_MARSHALLER);
// 3. Put the key-value pair into Metadata
headers.put(customKey, customValue);
// 4. Attach metadata to the stub
GreeterGrpc.GreeterBlockingStub stubWithMetadata =
MetadataUtils.attachHeaders(blockingStub, headers);
try {
HelloReply response = stubWithMetadata.SayHello(request);
System.out.println("Greeting: " + response.getMessage());
} catch (StatusRuntimeException e) {
System.err.println("RPC failed: " + e.getStatus());
}
}
public static void main(String[] args) throws Exception {
GrpcClient client = new GrpcClient("localhost", 50051);
try {
client.sayHello("CoddyKit User", "my-session-id-123");
} finally {
client.shutdown();
}
}
}Server: Receiving Request Metadata
On the server side, incoming metadata is typically accessed using a ServerInterceptor.
An interceptor sits between the gRPC runtime and your service implementation, allowing you to inspect and modify calls.
- It receives a
Metadataobject. - You can extract values using
Metadata.Key. - Often, metadata is then added to the
Contextfor easy access within service methods.
Server: Receiving Metadata Example
This Java server example shows a ServerInterceptor extracting a custom header and making it available to the service via Context. Ensure your `hello.proto` is compiled.
package com.coddykit.grpc;
import io.grpc.Context;
import io.grpc.Metadata;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
public class GrpcServer {
private Server server;
// Context.Key to store the custom value for the service method
private static final Context.Key<String> CUSTOM_VALUE_CTX_KEY = Context.key("custom-value");
private void start() throws IOException {
int port = 50051;
server = ServerBuilder.forPort(port)
.addService(new GreeterService())
.intercept(new CustomHeaderInterceptor()) // Add our interceptor
.build()
.start();
System.out.println("Server started, listening on " + port);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.err.println("*** shutting down server");
try { GrpcServer.this.stop(); }
catch (InterruptedException e) { e.printStackTrace(System.err); }
System.err.println("*** server shut down");
}));
}
private void stop() throws InterruptedException {
if (server != null) { server.shutdown().awaitTermination(); }
}
private void blockUntilShutdown() throws InterruptedException {
if (server != null) { server.awaitTermination(); }
}
public static void main(String[] args) throws Exception {
final GrpcServer server = new GrpcServer();
server.start();
server.blockUntilShutdown();
}
static class GreeterService extends GreeterGrpc.GreeterImplBase {
@Override
public void SayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
// Retrieve custom value from Context
String customValue = CUSTOM_VALUE_CTX_KEY.get();
System.out.println("Service received custom-key: " + customValue);
HelloReply reply = HelloReply.newBuilder()
.setMessage("Hello " + request.getName() +
"! Custom value: " + customValue)
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
static class CustomHeaderInterceptor implements ServerInterceptor {
// Define the Metadata.Key for our custom header
private static final Metadata.Key<String> CUSTOM_KEY =
Metadata.Key.of("custom-key", Metadata.ASCII_STRING_MARSHALLER);
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
Metadata headers,
ServerCallHandler<ReqT, RespT> next) {
// Get the custom value from incoming headers
String customValue = headers.get(CUSTOM_KEY);
System.out.println("Interceptor received custom-key: " + customValue);
// Store the custom value in the Context for the service to access
Context context = Context.current().withValue(CUSTOM_VALUE_CTX_KEY, customValue);
// Proceed with the call within the new context
return Context.current().call(() -> next.startCall(call, headers));
}
}
}Server: Sending Response Metadata
Servers can also send metadata back to clients, either as response headers or response trailers.
- Response Headers: Sent before any response messages. Use
ServerCall.sendHeaders(Metadata). - Response Trailers: Sent at the end of the RPC, after all response messages. Often used for status or final context.
Both are handled within the ServerCall object, which is available in interceptors or advanced service implementations.
Client: Receiving Response Metadata
Clients can access the response metadata (headers and trailers) through the ClientCall.Listener interface, typically used with asynchronous (non-blocking) stubs.
The listener provides callbacks like onHeaders(Metadata) and onTrailers(Metadata) where you can inspect the incoming metadata.
Metadata Use Cases
Which of the following is NOT a typical use case for gRPC metadata?
Recap: Metadata in gRPC
You've learned how gRPC metadata acts like HTTP headers, carrying essential non-application data alongside your RPC calls.
- Clients send metadata with requests.
- Servers receive and process request metadata (often via interceptors).
- Servers can send response metadata (headers/trailers) back to clients.
- Metadata is crucial for cross-cutting concerns like authentication and tracing.
Mastering metadata allows for more robust and observable gRPC services!
常见问题解答
「自定义元数据传输」课时是免费的吗?
是的 — 「自定义元数据传输」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 gRPC & High Performance APIs 课程的其余内容,请升级到 CoddyKit PRO。 gRPC & High Performance APIs 课程共包含 4 节课。
「自定义元数据传输」这节课中我会学到什么?
了解如何通过 gRPC 请求和响应发送及接收作为元数据的自定义键值对 你通过在浏览器中直接运行的动手代码来练习 gRPC & High Performance APIs,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 gRPC & High Performance APIs 需要有经验吗?
无需任何先前经验。CoddyKit 上的 gRPC & High Performance APIs 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「自定义元数据传输」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 gRPC & High Performance APIs 课中编写并运行代码吗?
能。每节 gRPC & High Performance APIs 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。