Escrito a mano una llamada RPC remoto (basado Netty, la reflexión y agentes)

emmm, ayer setas lado de la calle, me siento especialmente buen entrevistador, me hizo una pregunta abierta relativamente pocos, no preguntar cómo la fundación. Pero siento que no soy una muy buena respuesta, las primeras grandes compañías se enfrentan a un poco nervioso. La esperanza había sido demasiado! ! ! Hay una pregunta, el entrevistador dijo que tenía que configurar manualmente springboo + + empleado del zoológico dubbo derecha, luego dubbo principio de qué se trata, o si desea configurar manualmente un dubbo lo haces? Por esta razón, hoy he pasado la mitad de un día para hacer una llamada RPC remoto a través de la búsqueda de información.

En primer lugar, que este proyecto se basa en, si hay zapatos para niños no están familiarizados con el contenido de este conocimiento local para lograr Netty, la agencia dinámica, reflexión, se recomienda primer vistazo a este conocimiento, vistazo a este artículo, creo tiene sentido.

Estructura del proyecto:

Hay tres módulos, comenzar con común, en su interior contiene nuestros principales clientes y servidores cosas en común (se podría elaborar), que depende de la forma de pom.xml en las otras dos sub-proyectos en el interior. servidor, que contiene uno de nuestros servidores, clase específica aplicación serviceimpl, cliente, que contiene nuestro cliente. Estos dos métodos son las llamadas de clientes de servidor (tenga en cuenta que esto no se logra a través del resto de la interfaz). Ambos proyectos se pueden implementar de forma independiente en el servidor también es posible.

común

ClassInfo aquí está la información que queremos pasar la clase, va a ser una muy clara (en voz baja dan a conocer, es la siguiente información específica del servicio dos métodos). Está construido sobre la base de nuestra llamada! ! ! El siguiente servicio de llamadas de larga distancia de dos es lo que queremos conseguir su clase.

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;

}

El hermano de un registro detallado del nombre de la clase del método de interfaz que llamamos el método, el nombre del método, los tipos de parámetros y el parámetro s. en Austrian! !

package com.rpc.common.service;

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

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

Dos es la interfaz común es el marco familiar en dubbo @Service siguiente interfaz. En dubbo Lane está registrada por el centro de registro.

Servidor

A continuación vamos juntos Kankan servidor

Echar un vistazo a estos dos nada que decir clase de implementación de la derecha

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;
    }
}

Entonces es nuestro Netty que marco muy práctico y seguro! !

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();
    }
}

Esta larga lista que no dicen específicamente si el contacto con amigos Xiaopen Netty, debido a que no están familiarizados. La codificación y decodificación, nuestro objetivo es pasar a descodificar o codificar, de hecho, la serialización. Netty nos ha ayudado a hacer el trabajo.

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();
    }

}

Puede recibir este método channelRead enviado directamente al cliente a través de la solicitud. Es decir msg. En realidad, ha ayudado a descifrar Bueno, reescribir anti-secuencia en un objeto, y nos lleva directamente similares, y después obtenemos la implementación del objeto de clase de esta clase por el método getImplClassName, no vamos a mirar a este método getImplClassName, suponiendo que obtenemos a la clase de implementación, es decir, nuestro servidor impl siguiendo estos, se puede llamar al método correspondiente, y después de invocación llamada. Por último, devolver los resultados. La tarea se ha completado llamando.

A continuación nos fijamos en getImplClassName método. Es el objeto de conseguir la clase de servicio por forName, a continuación, utilizar los siguientes códigos:

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

Obtenga toda la clase de implementación. La clase de implementación final puede ser tratada. Esto completará todo el curso de nuestra llamada al servidor. Nuestra siguiente preocupación es cómo el cliente ClassInfo nos envía de nuevo.

Cliente

En primer lugar, observamos el objeto de proxy:

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);
    }

}

 

crear método para crear un objeto proxy no quiere decir ah, pensamos en por qué usamos un proxy que? Los agentes pueden obtener parámetros, el tipo y nombre de los métodos para todo nuestro enfoque. Este es el significado de su existencia. Estos son nuestro campo de objeto ClassInfo transmisión, una necesidad! ! !

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();
    }


}

Esto no es complicado, es para obtener un resultado directo del servidor de llamadas de método completo!

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"));

    }
}

Este es un llamado proceso que! ! ! De hecho, se puede pensar, la información del registro de dubbo empleado del zoológico se ha registrado, hay un puerto de servicio de nombres de dominio, y aquí estamos en el proyecto de escribir estas cosas fijas, ClassInfo muy importante. Debido a la apretada agenda, la especificación de código no es muy bueno, perdóname.

El resultado:

Por último, acompañado de un pequeño mapa, buscamos entender!

proceso:

resguardo del cliente puede ser una biografía, nio y Netty.

1, un consumidor de servicios (cliente) para invocar un servicio de llamadas locales.

2, resguardo del cliente es responsable de la llamada después de recibir el método de envasado capaz de parámetros a la red de transmisión de mensajes

3, el mensaje resguardo del cliente se codifica y se envía al servidor

4, se recibe stub servidor de decodificación de un mensaje

5, agrupación de servidor de llamar al servicio local basa en el resultado de la decodificación de j

6, la ejecución del servicio local y devuelve los resultados a la agrupación de servidor

7, stub servidor devuelve el resultado se codifica y se transmite al consumidor

8, resguardo del cliente recibe el mensaje y descodifica

9, el cliente de servicio al consumidor para obtener resultados.

El hacer dubbo es encapsulado 2-8.

 

Dirección del proyecto: [email protected]: Zesystem / -Handwritten-RPC.git

 

 

 

 

Publicados 134 artículos originales · ganado elogios 91 · vistas 160 000 +

Supongo que te gusta

Origin blog.csdn.net/weixin_44588495/article/details/105279242
Recomendado
Clasificación