Write a rpc framework from 0.5 to 1-2: remote service call (grpc)

Write a rpc framework from 0.5 to 1-2: remote service call (grpc)

In order to implement remote service calls for microservices, in addition to directly using the ribbon and feign modules in the spring coud family bucket, you can also try other excellent frameworks, such as Google's gRPC, based on which to implement your own service invocation module.

gRPC is Google’s open source cross-language remote service call (RPC) framework. The communication protocol uses HTTP/2 and the default protocol buffers for data transmission (a lightweight and efficient structured data storage format, which is smaller and faster than json. But there is no readability).

You need to master the basic usage of grpc : gRPC-Java example

Project structure

-acuprpc
    + acuprpc-core//server/client core processing logic
    + acuprpc-protocol-grpc//Realize remote call based on grpc
    + acuprpc-spring-boot-starter//server-side service scan, client-side dynamic proxy, service registration/discovery

grpc communication

Interface definition

Define the data structure used by the service provider (server) and the service caller (client) to communicate. The client needs to tell the server the class name, method name, and parameters (string in json format, deserialized on the server side) to be called.

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.acupt.acuprpc.protocol.grpc.proto";
option java_outer_classname = "GrpcServiceProto";

package com.acupt.acuprpc.protocol.grpc.proto;

service GrpcService {
    rpc invokeMethod (InvokeRequest) returns (InvokeResponse) {
    }
}

message InvokeRequest {
    string appName = 1;
    string serviceName = 2;
    string methodName = 3;
    repeated string orderedParameter = 4;
    map<string, string> namedParameter = 5;
}

message InvokeResponse {
    int32 code = 1;
    string message = 2;
    string result = 3;
}

grpc-service

This class is responsible for receiving the request sent by grpc-client, taking out the parameters in the request, converting it into a common structure, passing it to the RpcServer of the core layer to execute the corresponding method, and then serializing the return value into json and returning it to grpc-client.

public class GrpcService extends GrpcServiceGrpc.GrpcServiceImplBase {

    private RpcServer rpcServer;

    public GrpcService(RpcServer rpcServer) {
        this.rpcServer = rpcServer;
    }

    @Override
    public void invokeMethod(InvokeRequest request, StreamObserver<InvokeResponse> responseObserver) {
        RpcRequest rpcRequest = new RpcRequest(
                request.getAppName(),
                request.getServiceName(),
                request.getMethodName(),
                request.getOrderedParameterList(),
                request.getNamedParameterMap());
        RpcResponse rpcResponse = rpcServer.execute(rpcRequest);
        InvokeResponse response = InvokeResponse.newBuilder()
                .setCode(rpcResponse.getCode())
                .setMessage(rpcResponse.getMessage())
                .setResult(rpcResponse.getResultString())
                .build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

grpc-server

The specific implementation class of the crop service provider only needs to implement two methods: start the service and close the service, and leave the rest to the parent class of the core layer.

public class GrpcServer extends RpcServer {

    private Server server;

    public GrpcServer(RpcInstance rpcInstance) {
        super(rpcInstance);
    }

    @SneakyThrows
    @Override
    protected void startRpc() {
        server = ServerBuilder
                .forPort(getRpcInstance().getRpcConf().getPort())
                .addService(new GrpcService(this))
                .build().start();
    }

    @Override
    protected void shutdownRpc() {
        if (server != null) {
            server.shutdown();
        }
    }
}

grpc-client

As a service caller, you need to package the request information from the dynamic proxy class into a structure supported by grpc, call the grpc request method, and then return the result returned by the remote service to the proxy class.

public class GrpcClient extends RpcClient implements RpcCode {

    private AtomicReference<GrpcServiceGrpc.GrpcServiceFutureStub> stubRef;

    public GrpcClient(NodeInfo nodeInfo) {
        super(nodeInfo);
        this.stubRef = new AtomicReference<>(getStub(nodeInfo));
    }

    @Override
    protected String remoteInvoke(RpcRequest rpcRequest) {
        InvokeRequest.Builder builder = InvokeRequest.newBuilder()
                .setAppName(rpcRequest.getAppName())
                .setServiceName(rpcRequest.getServiceName())
                .setMethodName(rpcRequest.getMethodName());
       //...
        ListenableFuture<InvokeResponse> future = stubRef.get().invokeMethod(builder.build());
        InvokeResponse response = null;
       //...
        return response.getResult();
    }

    @Override
    @SneakyThrows
    protected NodeInfo reconnectRpc(NodeInfo nodeInfo) {
       //...Use the ip and port in the parameters to establish a new connection and disconnect the old connection, which can be used to reload and retry abnormal nodes
    }

    @Override
    @SneakyThrows
    public void shutdownRpc() {
       //... Actively disconnect from the server
    }

    private GrpcServiceGrpc.GrpcServiceFutureStub getStub(NodeInfo nodeInfo) {
       //...Establish a connection with the server, using the ip and port in the parameters
    }