RPC framework based on NetTY

[TOC]

If you already know the following, you are ready to complete an RPC framework

  • Reflection technology in Java
  • Java dynamic proxy mechanism
  • Nio based framework netty
  • The best framework in the world – Spring
  • Serialization of Java

What is RPC?

Implementation approach

  • On the face of it, this is a very difficult task to accomplish. How can a native machine call a remote method? But if we break the task down, we’ll see that it’s actually pretty easy to just do it step by step.

    We can change an idea, since not called directly, we can curve for national salvation, we as long as the method is called the name of the object, the name of the method, the method of the parameters and the method of type is sent over the network to another machine, the other machine after receiving the call the object method according to the request information, And then the execution results through the network directly back not ok. In fact, this is the general idea of the RPC framework.

  • The general implementation process is as follows:

    1. The proxy object created for our UserService through Java’s dynamic proxy mechanism is actually intercepted by our custom method when the proxy object executes the method.
    2. In the interception logic, we get all the interfaces, method names, parameter sets and parameter type sets of the called method and encapsulate them into a JavaBean — Request. Then we serialize this object and transfer it to another machine over the network.
    3. Another machine receives the network Request, deserializes the data into a Request object to learn what method of the object we are requesting, and the server calls it by reflection and returns the execution result through another JavaBean, Response.
    4. The local machine receives a return from the server. The entire RPC call is complete.
  • As shown in the figure below, due to the limited level of drawing, but roughly this meaning:

Code implementation

  1. First we need to separate two Javabeans for our network Request, namely Request and Response.

    Private String requestId; private String className; private String methodName; private Class[] parameterTypes; private Object[] parameters; Private String requestId; private Throwable error; private Object result;Copy the code
  2. Create an RpcClient that encapsulates our network request flow. The most important of these is this method:

    public Response send(Request request) throws InterruptedException { ClientBootstrap bootstrap = new ClientBootstrap(); ExecutorService boss = Executors.newCachedThreadPool(); ExecutorService work = Executors.newCachedThreadPool(); bootstrap.setFactory(new NioClientSocketChannelFactory(boss,work)); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("decoder",new ResponseDecoder()); pipeline.addLast("encoder",new RequestEncoder()); pipeline.addLast("handler",RpcClient.this); return pipeline; }}); ChannelFuture connect = bootstrap.connect(new InetSocketAddress(address, port)).sync(); connect.getChannel().write(request).sync(); Synchronized (obj){obj.wait(); synchronized (obj){obj. } connect.getChannel().close().sync(); return this.response; }Copy the code

    ResponseDecoder and RequestEncoder (ResponseDecoder and RequestEncoder (ResponseDecoder and RequestEncoder)) Implementation of Google’s Protostuff serialization framework (why not use Java’s own serialization tools? Because Java’s custom serialization comes with a lot of additional information, the serialization byte length is several times longer than Google’s, so to save bandwidth, and Protostuff serialization supports multiple programming languages.)

  3. Create a proxy utility class that returns the proxy object.

    public T proxy(Class clazz){ return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Request request = new Request(); request.setClassName(method.getDeclaringClass().getName()); request.setMethodName(method.getName()); request.setParameters(args); request.setRequestId(UUID.randomUUID().toString()); request.setParameterTypes(method.getParameterTypes()); RpcClient client =new RpcClient(address,port); // Network requests are made through the encapsulated network framework. Response Response = client.send(request); if (response.getError()! =null){ throw response.getError(); } else{ return response; }}}); }Copy the code
  4. When the server starts the service, it needs to scan all the service implementation classes through Spring and put them into the Spring container.

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map beansWithAnnotation  = applicationContext.getBeansWithAnnotation(RPCService.class); for(Map.Entry entry :beansWithAnnotation.entrySet()){ String interfaceName = entry.getValue().getClass() .getAnnotation(RPCService.class).value().getName(); serviceMap.put(interfaceName,entry.getValue()); } startServer(); }Copy the code

    Any service class that needs to be published needs to use the @rpcService annotation, which is a custom annotation.

  5. After the server receives the network request from the client, we need to find the requested service class from the Spring container to complete the call and return the execution result.

    @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception { Request request = (Request) event.getMessage(); Response response = new Response(); Object invoke = null; try { Object requestBean = serviceMap.get(request.getClassName()); Class requestClazz = Class.forName(request.getClassName()); Method method = requestClazz.getMethod(request.getMethodName(), request.getParameterTypes()); invoke = method.invoke(requestBean, request.getParameters()); response.setRequestId(UUID.randomUUID().toString()); response.setResult(invoke); } catch (Exception e) { response.setError(e); response.setRequestId(UUID.randomUUID().toString()); } System.out.println(request+""+response); Ctx.getchannel ().write(response); }Copy the code

conclusion

  • The overall process is relatively simple, is the specific implementation of the time there will be some details to deal with. Although this is the first time to write this wheel program, but I still feel good. The complete code has been uploaded to my GitHub repository, interested partners can go to have a look.