GRPC中的拦截/记录请求和响应

时间:2017-11-07 10:06:39

标签: java android server grpc grpc-java

我正在使用GRPC开发一个聊天应用程序,其中服务器从客户端接收信息并将其发送回连接到它的所有客户端。为此,我使用了 Saturnism的 chat-example作为参考。我已经复制了代码,代码编译并运行,但服务器应该永远不会收到客户端的任何请求。

我的问题是:

  1. 有没有办法在GRPC中启用verbos服务器端和客户端登录,以查看哪些请求和响应进出&什么可能失败?
  2. 我正在为服务器和客户端使用以下代码。以下代码中可能缺少/错误导致客户端和服务器之间无通信。
  3. WingokuServer.java

    public class WingokuServer {
        public static void main(String[] args) throws IOException, InterruptedException {
            Server server = ServerBuilder.forPort(8091)
                    .intercept(recordRequestHeadersInterceptor())
                    .addService(new WingokuServiceImpl())
                    .build();
    
            System.out.println("Starting server...");
            server.start();
            System.out.println("Server started!");
            server.awaitTermination();
        }
    

    WingokuServerSideServiceImplementation:

    public class WingokuServiceImpl extends WingokuServiceGrpc.WingokuServiceImplBase {
        private static Set<StreamObserver<Response>> observers =
                Collections.newSetFromMap(new ConcurrentHashMap<>());
    
        public WingokuServiceImpl() {
            System.out.println("WingokuServiceImp");
        }
    
        @Override
        public StreamObserver<Request> messages(StreamObserver<Response> responseObserver) {
            System.out.println("messages");
            observers.add(responseObserver);
            return new StreamObserver<Request>() {
                @Override
                public void onNext(Request request) {
                    System.out.println("Server onNext: ");
                    System.out.println("request from client is: "+ request.getRequestMessage());
                    Response response = Response.newBuilder().setResponseMessage("new Message From server at time: "+ System.nanoTime()).build();
                    for (StreamObserver<Response> observer : observers) {
                        observer.onNext(response);
                    }
                }
    
                @Override
                public void onError(Throwable throwable) {
                    System.out.println("Server onError: ");
                    throwable.printStackTrace();
                }
    
                @Override
                public void onCompleted() {
                    observers.remove(responseObserver);
                    System.out.println("Server onCompleted ");
                }
            };
        }
    }
    

    WingokuClient:

    public class WingokuClient {
        public static void main(String[] args) {
            ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8091).usePlaintext(true).build();
            WingokuServiceGrpc.WingokuServiceStub asyncStub = WingokuServiceGrpc.newStub(channel);
            StreamObserver<Request> requestStreamObserver = asyncStub.messages(new StreamObserver<Response>() {
                @Override
                public void onNext(Response response) {
                    System.out.println("Client onNext");
                    System.out.println("REsponse from server is: "+ response.getResponseMessage());
                }
    
                @Override
                public void onError(Throwable throwable) {
                    System.out.println("Client onError");
                    throwable.printStackTrace();
                }
    
                @Override
                public void onCompleted() {
                    System.out.println("Client OnComplete");
                }
            });
    
            requestStreamObserver.onNext(Request.newBuilder().setRequestMessage("Message From Client").build());
            requestStreamObserver.onCompleted();
            channel.shutdown();
            System.out.println("exiting client");
        }
    }
    

    修改

    代码没有问题。有用。我只需要将awaitTermination添加到客户端的通道,因为没有它只是立即关闭客户端和服务器之间的连接,甚至可能在请求从客户端进入网络之前。这就是服务器从未收到任何请求的原因。

    然而,我的问题是,启用详细日志记录和/或向服务器端添加某种拦截器仍然没有答案。所以我期待在这里得到专家的一些指示。

4 个答案:

答案 0 :(得分:1)

您可以打开Netty传输中的帧日志记录。首先,创建一个名为logging.properties的文件。在文件中输入以下内容:

handlers=java.util.logging.ConsoleHandler
io.grpc.netty.level=FINE
java.util.logging.ConsoleHandler.level=FINE
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

然后使用jvm标志启动Java二进制文件 -Djava.util.logging.config.file=logging.properties

答案 1 :(得分:0)

此外,如果要打印服务器上显示的消息内容或标题,可以创建ServerInterceptor: https://grpc.io/grpc-java/javadoc/io/grpc/ServerInterceptor.html

您可以查看示例目录,了解ServerInterceptor和ClientInterceptor的工作方式。没有预先存在的拦截器记录网络事件。

答案 2 :(得分:0)

我找到了一种使用拦截器在服务器端和客户端记录请求和响应的方法,它使代码更整洁。 也可以使用侦探来进行跟踪。

请使用spring:

implementation 'io.github.lognet:grpc-spring-boot-starter'

服务器部分

然后您可以使用GRpcGlobalInterceptor批注

import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;

@GRpcGlobalInterceptor
public class GrpcInterceptor implements ServerInterceptor {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public static final Metadata.Key<String> TRACE_ID_KEY = Metadata.Key.of("traceId", ASCII_STRING_MARSHALLER);

    @Override
    public <M, R> ServerCall.Listener<M> interceptCall(
            ServerCall<M, R> call, Metadata headers, ServerCallHandler<M, R> next) {
        String traceId = headers.get(TRACE_ID_KEY);
        // TODO: Add traceId to sleuth
        logger.warn("traceId from client: {}. TODO: Add traceId to sleuth", traceId);

        GrpcServerCall grpcServerCall = new GrpcServerCall(call);

        ServerCall.Listener listener = next.startCall(grpcServerCall, headers);

        return new GrpcForwardingServerCallListener<M>(call.getMethodDescriptor(), listener) {
            @Override
            public void onMessage(M message) {
                logger.info("Method: {}, Message: {}", methodName, message);
                super.onMessage(message);
            }
        };
    }

    private class GrpcServerCall<M, R> extends ServerCall<M, R> {

        ServerCall<M, R> serverCall;

        protected GrpcServerCall(ServerCall<M, R> serverCall) {
            this.serverCall = serverCall;
        }

        @Override
        public void request(int numMessages) {
            serverCall.request(numMessages);
        }

        @Override
        public void sendHeaders(Metadata headers) {
            serverCall.sendHeaders(headers);
        }

        @Override
        public void sendMessage(R message) {
            logger.info("Method: {}, Response: {}", serverCall.getMethodDescriptor().getFullMethodName(), message);
            serverCall.sendMessage(message);
        }

        @Override
        public void close(Status status, Metadata trailers) {
            serverCall.close(status, trailers);
        }

        @Override
        public boolean isCancelled() {
            return serverCall.isCancelled();
        }

        @Override
        public MethodDescriptor<M, R> getMethodDescriptor() {
            return serverCall.getMethodDescriptor();
        }
    }

    private class GrpcForwardingServerCallListener<M> extends io.grpc.ForwardingServerCallListener.SimpleForwardingServerCallListener<M> {

        String methodName;

        protected GrpcForwardingServerCallListener(MethodDescriptor method, ServerCall.Listener<M> listener) {
            super(listener);
            methodName = method.getFullMethodName();
        }
    }
}

客户部分

拦截器:

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;

@Component
public class BackendInterceptor implements ClientInterceptor {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    public static final Metadata.Key<String> TRACE_ID_KEY = Metadata.Key.of("traceId", ASCII_STRING_MARSHALLER);

    @Override
    public <M, R> ClientCall<M, R> interceptCall(
            final MethodDescriptor<M, R> method, CallOptions callOptions, Channel next) {
        return new BackendForwardingClientCall<M, R>(method,
                next.newCall(method, callOptions.withDeadlineAfter(10000, TimeUnit.MILLISECONDS))) {

            @Override
            public void sendMessage(M message) {
                logger.info("Method: {}, Message: {}", methodName, message);
                super.sendMessage(message);
            }

            @Override
            public void start(Listener<R> responseListener, Metadata headers) {
                // TODO: Use the sleuth traceId instead of 999
                headers.put(TRACE_ID_KEY, "999");

                BackendListener<R> backendListener = new BackendListener<>(methodName, responseListener);
                super.start(backendListener, headers);
            }
        };
    }

    private class BackendListener<R> extends ClientCall.Listener<R> {

        String methodName;
        ClientCall.Listener<R> responseListener;

        protected BackendListener(String methodName, ClientCall.Listener<R> responseListener) {
            super();
            this.methodName = methodName;
            this.responseListener = responseListener;
        }

        @Override
        public void onMessage(R message) {
            logger.info("Method: {}, Response: {}", methodName, message);
            responseListener.onMessage(message);
        }

        @Override
        public void onHeaders(Metadata headers) {
            responseListener.onHeaders(headers);
        }

        @Override
        public void onClose(Status status, Metadata trailers) {
            responseListener.onClose(status, trailers);
        }

        @Override
        public void onReady() {
            responseListener.onReady();
        }
    }

    private class BackendForwardingClientCall<M, R> extends io.grpc.ForwardingClientCall.SimpleForwardingClientCall<M, R> {

        String methodName;

        protected BackendForwardingClientCall(MethodDescriptor<M, R> method, ClientCall delegate) {
            super(delegate);
            methodName = method.getFullMethodName();
        }
    }
}

将拦截器添加到频道:

ManagedChannel managedChannel = ManagedChannelBuilder
                .forAddress(_URL_, _PORT_).usePlaintext().intercept(backendInterceptor).build();

答案 3 :(得分:0)

多年后让我也回答一下这个问题(希望对有同样问题的人有用)。 我以Shoohei的响应为例,并尽量压缩它,基本上解决了。

服务器拦截器

public class ServerLogInterceptor implements ServerInterceptor {

@Override
public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {

    ServerCall<ReqT, RespT> listener = new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(call) {

        @Override
        public void sendMessage(RespT message) {
            log.debug("Sending message to cliens: {}",  message);
            super.sendMessage(message);
        }
    };

    return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(next.startCall(listener, headers)) {

        @Override
        public void onMessage(ReqT message) {
            log.debug("Received message from cliens: {}", message);
            super.onMessage(message);
        }

    };
}}

客户端拦截器

    public class ClientLogInterceptor  implements ClientInterceptor {

    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
        MethodDescriptor<ReqT, RespT> method,
        CallOptions callOptions,
        Channel next
    ) {
        return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
            @Override
            public void sendMessage(ReqT message) {
                log.debug("Sending message to modules: {}", message);
                super.sendMessage(message);
            }

            @Override
            public void start(Listener<RespT> responseListener, Metadata headers) {
                super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {

                    @Override
                    public void onMessage(RespT message) {
                        log.debug("Received message from modules: {}", message);
                        super.onMessage(message);
                    }

                }, headers);
            }

        };
    }

}

(我不确定我是否正确粘贴了代码,以防只是添加或删除一些括号)