Handwritten an RPC remote call (based netty, reflection and agents)

emmm, yesterday mushroom Street side, I feel especially good interviewer, asked me a few relatively open question, did not ask how the foundation. But I feel I'm not a very good answer, the first large companies face a little nervous. Hope had been too! ! ! There is a question, the interviewer said you had to manually set up springboo + zookeeper + dubbo right, then dubbo principle of what is it, or you want to manually set up a dubbo you do it? For this reason, today I spent half a day to do a remote call rpc through to find information.

First of all, let this project is based on, if there are children's shoes are not familiar with the contents of this local knowledge to achieve netty, dynamic agency, reflection, it is recommended to first look at this knowledge, look at this article, I think It makes sense.

Project structure:

There are three module, start with common, inside it contains our main client and server things in common (one would elaborate), which is dependent on the form of pom.xml in our other two subprojects inside. server, which contains one of our servers, specific serviceimpl implementation class, client, which contains our client. These two methods is the server client calls (note this is not achieved through the rest of the interface). Both projects can be independently deployed on the server is also possible.

common

ClassInfo here is the information that we want to pass the class, it will be a very clear (quietly disclose, is the following two methods specific service information). It is built on the core of our call! ! ! The following two long-distance call service is what we want to achieve their class.

package com.rpc.common.enity;

import lombok.Data;

import java.io.Serializable;

@Data
public class ClassInfo implements Serializable {


    private String className;

    private String methodName;

    private Class<?>[] types;

    private Object[] objects;

}

The brother on a detailed record of the class name of the interface method we call the method, the method name, parameter types and parameter s. Austrian to! !

package com.rpc.common.service;

public interface HelloService {
    String helloRPC();
}
package com.rpc.common.service;

ublic interface HiService {
    String HiRPC(String name);
}

Two is the common interface is the familiar framework in dubbo @Service following interface. In dubbo Lane is registered by the registration center.

Server

Next we together Kankan Server

Take a look at these two nothing to say right implementation class

package com.rpc.server.impl;

import com.rpc.common.service.HelloService;

public class HelloServiceImpl implements HelloService {

    @Override
    public String helloRPC() {
        return "helloRPC";
    }
}


package com.rpc.server.impl;

import com.rpc.common.service.HiService;

public class HiServiceImpl implements HiService {

    @Override
    public String HiRPC(String name) {
        return "Hi" + name;
    }
}

Then is our netty it really convenient and secure framework! !

package com.rpc.server.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;


public class NettyServer {

    private int port;

    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();

    public NettyServer(int port){
        this.port = port;
    }

    public void start(){
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .localAddress(port)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //编码器
                            pipeline.addLast("encoder",new ObjectEncoder());
                            //解码器
                            pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE,
                                    ClassResolvers.cacheDisabled(null)));
                            //服务器端业务处理类
                            pipeline.addLast(new InvokeHandle());
                        }
                    });
            ChannelFuture future = serverBootstrap.bind(port).sync();
            System.out.println("服务启动-------------");
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new NettyServer(9999).start();
    }
}

This long list I do not specifically say if contact with netty Xiaopen friends, because that are not unfamiliar. Encoding and decoding, our target is to pass over to decode or encode, in fact, serialization. netty has helped us to do the work.

package com.rpc.server.netty;

import com.rpc.common.enity.ClassInfo;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.reflections.Reflections;

import java.lang.reflect.Method;
import java.util.Set;


public class InvokeHandle extends ChannelInboundHandlerAdapter {

    private static String interfacePath = "com.rpc.common.service";
    private static String implPath="com.rpc.server.impl";

    private String getImplClassName(ClassInfo classInfo)throws Exception{
        int lastDot = classInfo.getClassName().lastIndexOf(".");
        String interfaceName = classInfo.getClassName().substring(lastDot);

        Class superClass = Class.forName(interfacePath + interfaceName);

        Reflections reflections = new Reflections(implPath);
        //得到接口下面的实现类
        Set<Class> implClassSet = reflections.getSubTypesOf(superClass);
        if(implClassSet.size() == 0){
            System.out.println("未找到实现类");
            return null;
        }else if(implClassSet.size() > 1){
            System.out.println("找到多个实现类");
            return null;
        }else{
            Class[] classes = implClassSet.toArray(new Class[0]);
            return classes[0].getName();//实现类的名字
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception {
        ClassInfo classInfo = (ClassInfo)msg;
        Object clazz = null;
        try {
            clazz = Class.forName(getImplClassName(classInfo)).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("类名提取异常");
        }
        Method method = clazz.getClass().getMethod(classInfo.getMethodName(),classInfo.getTypes());
        Object result = method.invoke(clazz,classInfo.getObjects());
        ctx.writeAndFlush(result);
        ctx.close();
    }

}

It can this method receiving channelRead sent directly to the client over the request. That is msg. It has actually helped us decode Well, rewrite anti-sequence into an object, and take us directly like, and then we get the Class object implementation class of this class by getImplClassName method, let's not look at this getImplClassName method, assuming we get to the implementation class, that is, our server impl following these, we can call the corresponding method, and then invoke call. Finally, return the results. The calling task is complete.

Next we look at getImplClassName method. It is to get the service class object by forName, then use the following codes:

 Reflections reflections = new Reflections(implPath);
 //得到接口下面的实现类
Set<Class> implClassSet = reflections.getSubTypesOf(superClass);

Get all the implementation class. The final implementation class can be treated. This will complete the entire course of our server call. Our next concern is how the client ClassInfo send us over.

Client

We first look at the proxy object:

package com.rpc.client.proxy;



import com.rpc.client.netty.NettyClient;
import com.rpc.common.enity.ClassInfo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class NettyRPCProxy implements InvocationHandler {

    private static Class clazz;

    public static Object create(Class target){
        clazz = target;
        return Proxy.newProxyInstance(target.getClassLoader(),new Class[]{target},new NettyRPCProxy());
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //封装Classinfo
        ClassInfo classInfo = new ClassInfo();
        classInfo.setClassName(clazz.getName());
        classInfo.setMethodName(method.getName());
        classInfo.setObjects(args);
        classInfo.setTypes(method.getParameterTypes());

        //开始netty发送数据
        return new NettyClient().start(classInfo);
    }

}

 

create method to create a proxy object is not to say ah, we thought about why we use a proxy it? Agents can obtain parameters, type, and method names for all of our approach. This is the meaning of its existence. These are our transmission ClassInfo object field, a necessity! ! !

package com.rpc.client.netty;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;


public class ResultHandler extends ChannelInboundHandlerAdapter {


    private Object response;
    public Object getResponse() { return response; }

    @Override //读取服务器端返回的数据(远程调用的结果)
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        response = msg;
        ctx.close();
    }


}

This is not complicated, it is to obtain a direct result of the server complete method call!

package com.rpc.client.netty;

import com.rpc.client.proxy.NettyRPCProxy;
import com.rpc.common.enity.ClassInfo;
import com.rpc.common.service.HelloService;
import com.rpc.common.service.HiService;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;


public class NettyClient {

    private  EventLoopGroup group = new NioEventLoopGroup();
    private ResultHandler resultHandler =  new ResultHandler();

    public Object start(ClassInfo classInfo){
        try {
            Bootstrap client = new Bootstrap();

            client.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //编码器
                            pipeline.addLast("encoder",new ObjectEncoder());
                            //解码器
                            pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE,
                                    ClassResolvers.cacheDisabled(null)));
                            //服务器端业务处理类
                            pipeline.addLast("handle",resultHandler);
                        }
                    });
            ChannelFuture future = client.connect("127.0.0.1",9999).sync();
            future.channel().writeAndFlush(classInfo).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {

        }finally {
            group.shutdownGracefully();
        }
        return resultHandler.getResponse();
    }


    public static void main(String[] args) {
        //第一次调用
        HelloService helloService = (HelloService) NettyRPCProxy.create(HelloService.class);
        System.out.println(helloService.helloRPC());
        //第二次调用
        HiService hiService = (HiService) NettyRPCProxy.create(HiService.class);
        System.out.println(hiService.HiRPC("baby"));

    }
}

This is a process we call! ! ! , In fact, you can think about, dubbo zookeeper's registry information is registered, there is a domain name service port, and here we are in the project to write these things fixed, ClassInfo very important. Because of the tight schedule, code specification is not very good, forgive me.

operation result:

Finally, accompanied by a small map, we look to understand!

process:

client stub can be a bio, nio and netty.

1, the service consumer (client) to invoke a local call services.

2, client stub is responsible for the call after receiving the packaging method capable of parameters to the message transmission network

3, client stub message is encoded and sent to the server

4, server stub decoding a message is received

5, server stub calling the local service based on the result of decoding j

6, local service execution and returns the results to the server stub

7, server stub returns the result is encoded and transmitted to the consumer

8, client stub receives the message and decodes

9, the service consumer client to get results.

The dubbo do is encapsulate 2-8.

 

Project Address: [email protected]: Zesystem / -Handwritten-RPC.git

 

 

 

 

Published 134 original articles · won praise 91 · views 160 000 +

Guess you like

Origin blog.csdn.net/weixin_44588495/article/details/105279242