在grpc的報文中可以增加報文頭,用於標注消息的元數據。
服務端攔截器
在服務端可以繼承ServerInterceptor來實現服務端的攔截器,用於操作報文頭:
public class MyServerInterceptor implements ServerInterceptor {
//服務端header的key
static final Metadata.Key<String> CUSTOM_HEADER_KEY =
Metadata.Key.of("serverHeader", Metadata.ASCII_STRING_MARSHALLER);
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
//輸出客戶端傳遞過來的header
System.out.println("header received from client:" + headers);
return next.startCall(new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(call) {
@Override
public void sendHeaders(Metadata responseHeaders) {
//在返回中增加header
responseHeaders.put(CUSTOM_HEADER_KEY, "response");
super.sendHeaders(responseHeaders);
}
}, headers);
}
}
客戶端攔截器
類似的,需要繼承ClientInterceptor實現客戶端的攔截器
public class MyClientInterceptor implements ClientInterceptor {
//客戶端header的key
static final Metadata.Key<String> CUSTOM_HEADER_KEY =
Metadata.Key.of("clientHeader", Metadata.ASCII_STRING_MARSHALLER);
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
//放入客戶端的header
headers.put(CUSTOM_HEADER_KEY, "request");
super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {
@Override
public void onHeaders(Metadata headers) {
//輸出服務端傳遞回來的header
System.out.println("header received from server:" + headers);
super.onHeaders(headers);
}
}, headers);
}
};
}
}
調用
在完成兩端的攔截器的代碼就需要使用它們了,它們的用法是在客戶端channel或服務端的構建過程中將它們注入到對應的客戶端/服務端header的key,下面我們在上次代碼的基礎上進行修改
//服務端
public class IntceptorServer {
private int port;
private Server server;
public IntceptorServer(int port) throws IOException {
this.port=port;
server= ServerBuilder.forPort(port)
//將MyServerInterceptor注冊到服務端
.addService(ServerInterceptors.intercept(new HelloServiceImpl(),new MyServerInterceptor()))
.build();
server.start();
System.out.println("Server started, listening on " + port );
}
private void blockUntilShutdown() throws InterruptedException {
while(true){
server.awaitTermination();
}
}
public static void main(String[] args) throws Exception {
(new IntceptorServer(8080)).blockUntilShutdown();
}
}
//客戶端
public class InterceporClient {
static final Metadata.Key<String> ATTCHED_HEADER =
Metadata.Key.of("attached_header", Metadata.ASCII_STRING_MARSHALLER);
public static void main(String[] args) throws InterruptedException {
final ManagedChannel originalChannel = ManagedChannelBuilder.forAddress("127.0.0.1", 8080).usePlaintext(true).build();
//將MyClientInterceptor和原有的channel結合,生成包含攔截器的channel
Channel channel = ClientInterceptors.intercept(originalChannel, new MyClientInterceptor());
HelloServiceGrpc.HelloServiceBlockingStub blockingStub = HelloServiceGrpc.newBlockingStub(channel);
ProtoObj.Person person = ProtoObj.Person.newBuilder().setMyName("World").build();
System.out.println(blockingStub.simpleHello(person).getString());
//如果只需要在客戶端傳送header,而不需要接受服務端的header可以簡單調用MetadataUtils.attachHeaders注冊meta數據而不用定義Interceptor
blockingStub = HelloServiceGrpc.newBlockingStub(originalChannel);
Metadata meta=new Metadata();
meta.put(ATTCHED_HEADER, "attched");
HelloServiceGrpc.HelloServiceBlockingStub s=MetadataUtils.attachHeaders(blockingStub,meta);
System.out.println(s.simpleHello(person).getString());
//關閉originalChannel
originalChannel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
}
----服務端輸出----
header received from client:Metadata(content-type=application/grpc,user-agent=grpc-java-netty/1.2.0,clientheader=request,grpc-accept-encoding=gzip,grpc-census-bin=)
World calling
header received from client:Metadata(content-type=application/grpc,user-agent=grpc-java-netty/1.2.0,attached_header=attched,grpc-accept-encoding=gzip,grpc-census-bin=)
World calling
----客戶端輸出----
header received from server:Metadata(content-type=application/grpc,serverheader=response,grpc-encoding=identity,grpc-accept-encoding=gzip)
hello, World
hello, World
這樣在兩端就可以看到對應的header
總結
通過header的內容可以對調用的流程進行一定的控制,以達到認證等功能。