Original address: haifeiWu and his friends blog address: www.hchstudio.cn welcome to reprint, reprint please indicate the author and source, thank you!

Server-side development will more or less involve the use of RPC, of course, if you stop at the use of their growth is very unfavorable, so the Lord of the building today in the spirit of knowing, and know why to explore the RPC this thing.

The child – the RPC model

Child-rpc uses direct socket connection to implement remote invocation of the service, and then uses JDK dynamic proxy to make the caller unaware of the remote invocation.

Child-rpc unboxed

Publishing service

The RPC service class listens to the specified IP port, sets the implementation of the service to be published and the reference of its interface, and specifies the serialization mode. Currently, child-RPC supports Hessian and JACKSON serialization modes.

/ * * *@author wuhf
 * @Date2018/9/1 18:30 * * /
public class ServerTest {

    public static void main(String[] args) {
        ServerConfig serverConfig = new ServerConfig();
        serverConfig.setSerializer(Serializer.SerializeEnum.HESSIAN.serializer)
                .setPort(5201)
                .setInterfaceId(HelloService.class.getName())
                .setRef(HelloServiceImpl.class.getName());
        ServerProxy serverProxy = new ServerProxy(new NettyServer(),serverConfig);
        try {
            serverProxy.export();
            while (true) {}}catch(Exception e) { e.printStackTrace(); }}}Copy the code

Reference service

The RPC client links to the remote IP port, registers the service to reference, and then invokes the sayHi method to output the result

/ * * *@author wuhf
 * @Date2018/9/1 when * * /
public class ClientTest {

    public static void main(String[] args) {
        ClientConfig clientConfig = new ClientConfig();
        clientConfig.setHost("127.0.0.1")
                .setPort(5201)
                .setTimeoutMillis(100000)
                .setSerializer(Serializer.SerializeEnum.HESSIAN.serializer);
        ClientProxy clientProxy = new ClientProxy(clientConfig,new NettyClient(),HelloService.class);
        for (int i = 0; i < 10; i++) { HelloService helloService = (HelloService) clientProxy.refer(); System.out.println(helloService.sayHi()); }}}Copy the code

run

The server end output

The client end output

Child-rpc concrete implementation

RPC request, response message entity definition

Defines the message request response format, the message type, the unique ID of the message, and the json serialized string content of the message. The message unique ID is used by the client to verify that the server request and response match.

/ / RPC requests
public class RpcRequest implements Serializable {
    private static final long serialVersionUID = -4364536436151723421L;

    private String requestId;
    private long createMillisTime;
    private String className;
    private String methodName;
    privateClass<? >[] parameterTypes;private Object[] parameters;

    // the set get method is omitted
}
/ / RPC response
public class RpcResponse implements Serializable {
    private static final long serialVersionUID = 7329530374415722876L;

    private String requestId;
    private Throwable error;
    private Object result;
    // the set get method is omitted
}
Copy the code

Encoding and decoding during network transmission

Message encoding and decoding using a custom codec, according to the service initialization is used to serialize the data into a byte stream, the unpacking strategy is to set the specified length of the packet, socket sticky packet, unpacking interested friends please move to socket sticky packet problem analysis and solution

The following is the decoder code implementation:

public class NettyDecoder extends ByteToMessageDecoder {

    privateClass<? > genericClass;private Serializer serializer;

    public NettyDecoder(Class
        genericClass, Serializer serializer) {
        this.genericClass = genericClass;
        this.serializer = serializer;
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        if (byteBuf.readableBytes() < 4) {
            return;
        }

        byteBuf.markReaderIndex();
        // Read the message length
        int dataLength = byteBuf.readInt();
        
        if (dataLength < 0) {
            channelHandlerContext.close();
        }

        if (byteBuf.readableBytes() < dataLength) {
            byteBuf.resetReaderIndex();
            return;
        }

        try {
            byte[] data = new byte[dataLength];
            byteBuf.readBytes(data);
            Object object = serializer.deserialize(data,genericClass);
            list.add(object);
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

The following is the implementation of the encoder:

public class NettyEncoder extends MessageToByteEncoder<Object> {

    privateClass<? > genericClass;private Serializer serializer;

    public NettyEncoder(Class
        genericClass,Serializer serializer) {
        this.serializer = serializer;
        this.genericClass = genericClass;
    }

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object object, ByteBuf byteBuf) throws Exception {
        if (genericClass.isInstance(object)) {
            byte[] data = serializer.serialize(object); byteBuf.writeInt(data.length); byteBuf.writeBytes(data); }}}Copy the code

RPC business logic handles handlers

Server-side business processing handler implementation: the main business logic is through Java reflection method invocation.

public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> {

    private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcRequest rpcRequest) throws Exception {
        // Invoke retrieves rpcResponse by calling the reflection method
        RpcResponse response = RpcInvokerHandler.invokeService(rpcRequest);
        channelHandlerContext.writeAndFlush(response);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.error(">>>>>>>>>>> child-rpc provider netty server caught exception", cause); ctx.close(); }}public class RpcInvokerHandler {
    public static Map<String, Object> serviceMap = new HashMap<String, Object>();
    public static RpcResponse invokeService(RpcRequest request) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Object serviceBean = serviceMap.get(request.getClassName());

        RpcResponse response = new RpcResponse();
        response.setRequestId(request.getRequestId());
        try{ Class<? > serviceClass = serviceBean.getClass(); String methodName = request.getMethodName(); Class<? >[] parameterTypes = request.getParameterTypes(); Object[] parameters = request.getParameters(); Method method = serviceClass.getMethod(methodName, parameterTypes); method.setAccessible(true);
            Object result = method.invoke(serviceBean, parameters);

            response.setResult(result);
        } catch (Throwable t) {
            t.printStackTrace();
            response.setError(t);
        }
        returnresponse; }}Copy the code

The main service implementation of the client is to wait for the response from the server. The code is relatively simple, I will not post the code, please see the github link given below for details.

The RPC server and client are started

Because the server and client start are Netty template code, because of space reasons will not post out, interested partners please move to build a wheel -RPC start implementation.

summary

There are a lot of implementation details that haven’t been carefully worked out just to understand the nature of RPC. However, the purpose of RPC is to allow remote services to be invoked as if they were local services, and to be transparent to callers, so we used dynamic proxies. Netty’s handler is used to send data and response data. In general, the framework implements simple RPC calls. The code is relatively simple, mainly ideas, and understand the implementation of RPC bottom.

Refer to the article

  • Build a wheel -RPC hands-on implementation
  • Analysis of sticky packet problem in Socket and its solution
Pay attention to our