Implemente un marco RPC simple con Netty

Netty es un marco de aplicación de red NIO asíncrono y controlado por eventos que se puede utilizar para desarrollar programas de E/S de red de alto rendimiento y alta confiabilidad. La comunicación IO subyacente de muchos marcos RPC en el mercado se implementa con Netty. El objetivo principal de este proyecto es fortalecer el dominio de las aplicaciones Netty a través de la práctica. Además, este proyecto también se puede utilizar para profundizar la comprensión de RPC.

que es RPC

RPC es la abreviatura de llamada a procedimiento remoto. Es una solución para llamadas entre aplicaciones, al establecer una conexión TCP entre el cliente y el servidor, enviando solicitudes para serializar y deserializar datos de intercambio, facilitando a las personas que llaman el uso de API locales. La gente a menudo se pregunta por qué no se usa HTTP, de hecho, HTTP también es una forma de implementar RPC.

Proceso de implementación

Protocolos
Antes de hablar sobre las implementaciones de servidor y cliente, defina los protocolos RPC relevantes.
SwiftMessage , el mensaje POJO que fluye en la canalización:

public class SwiftMessage {

    private int length;
    private byte[] content;

    public SwiftMessage(int length, byte[] content) {
        this.length = length;
        this.content = content;
    }
    
    // 省略setter、getter
}
复制代码

RpcRequest , RpcResponse , el objeto de respuesta de solicitud procesado en el controlador:

public class RpcRequest implements Serializable {

    private static final long serialVersionUID = 2104861261275175620L;

    private String id;
    private String className;
    private String methodName;
    private Object[] parameters;
    private Class<?>[] parameterTypes;
}
复制代码
public class RpcResponse implements Serializable {

    private static final long serialVersionUID = -1921327887856337850L;

    private String requestId;
    private int code;
    private String errorMsg;
    private Object data;
}
复制代码

Codificador y decodificador para la conversión de ByteBuf a SwiftMessage :

public class SwiftMessageEncoder extends MessageToByteEncoder<SwiftMessage> {

    @Override
    protected void encode(ChannelHandlerContext ctx, SwiftMessage msg, ByteBuf out) throws Exception {
        out.writeInt(msg.getLength());
        out.writeBytes(msg.getContent());
    }
}
复制代码
public class SwiftMessageDecoder extends ReplayingDecoder<Void> {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int length = in.readInt();
        byte[] content = new byte[length];
        in.readBytes(content);
        out.add(new SwiftMessage(length, content));
    }
}
复制代码

Servidor
De hecho, el servidor RPC es más simple, solo necesita hacer lo siguiente:

  1. Inicie el servicio y regístrese en el centro de servicio de RPC (para que el cliente pueda encontrar y establecer una conexión)
@Component
public class SwiftServerRunner implements ApplicationRunner, ApplicationContextAware {

    private void start(int port, String serverName) {
        if (isRunning.get()) {
            LOGGER.info("SwiftRPC服务已经在运行中");
            return;
        }

        final NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        final NioEventLoopGroup workerGroup = new NioEventLoopGroup(16);
        try {
            SwiftServerHandler handler = new SwiftServerHandler(rpcServiceMap);
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            pipeline.addLast(new SwiftMessageDecoder());
                            pipeline.addLast(new SwiftMessageEncoder());
                            pipeline.addLast(handler);
                        }
                    });
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            isRunning.set(true);

            String rpcServer = "127.0.0.1:" + port;
            registerRpcServer(serverName, rpcServer);

            LOGGER.info("SwiftRPC服务已经启动,端口:{}", port);

            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            isRunning.set(false);
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();

            throw new RuntimeException("启动SwiftRPC服务失败", e);
        }
    }

    private void registerRpcServer(String serverName, String rpcServer) throws Exception {
        // 简单的将rpc server列表保存到redis中
        redisTemplate.opsForHash().put("rpc-server", serverName, rpcServer);
    }
}
复制代码
  1. Supervise las conexiones de los clientes, reciba datos de solicitudes y deserialice en RpcRequest
@Override
protected void channelRead0(ChannelHandlerContext ctx, SwiftMessage msg) throws Exception {
    byte[] content = msg.getContent();
    RpcRequest request = Kryos.deserialize(content, RpcRequest.class);
}
复制代码
  1. Use los parámetros en RpcRequest para ejecutar métodos de implementación de negocios específicos a través de la reflexión y luego escriba los resultados en la conexión con el cliente.
@ChannelHandler.Sharable
public class SwiftServerHandler extends SimpleChannelInboundHandler<SwiftMessage> {
    
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, SwiftMessage msg) throws Exception {
        byte[] content = msg.getContent();
        RpcRequest request = Kryos.deserialize(content, RpcRequest.class);

        RpcResponse response = new RpcResponse();
        response.setRequestId(request.getId());

        try {
            Object result = handler(request);
            response.setData(result);
        } catch (Exception e) {
            response.setCode(-1);
            response.setErrorMsg(e.toString());
        }

        byte[] resContent = Kryos.serialize(response);
        SwiftMessage message = new SwiftMessage(resContent.length, resContent);
        ctx.writeAndFlush(message);
    }

    private Object handler(RpcRequest request) throws Exception {
        String className = request.getClassName();
        Object rpcService = rpcServiceMap.get(className);
        if (rpcService == null) {
            throw new RuntimeException("未找到RPC服务类:" + className);
        }

        // 通过反射调用业务层
        String methodName = request.getMethodName();
        Object[] parameters = request.getParameters();
        Class<?>[] parameterTypes = request.getParameterTypes();
        Class<?> clazz = rpcService.getClass();
        Method method = clazz.getMethod(methodName, parameterTypes);

        return method.invoke(rpcService, parameters);
    }
}
复制代码

Cliente
El cliente es un poco más complicado, porque el cliente necesita recibir solicitudes de llamadas externas (como una solicitud de una interfaz de descanso), seleccionar diferentes servidores RPC para enviar solicitudes de acuerdo con diferentes solicitudes y, finalmente, debe esperar los resultados devueltos. .

  1. 注册RPC接口。因为要想通过注入的方式调用service的方法,就需要将service注册成Spring的bean
@Autowired
private UserService userService;
复制代码

但是UserService具体的实现类在服务端,所以需要自定义一个ComponentScan,扫描所有的RPC接口,注册成Spring的bean,并设置对应的FactoryBean来完成实例化工作,可以参考Mybatis的执行过程。在FactoryBean中用动态代理来生成每个接口的代理类执行各个方法。所以最终的RPC调用是在这个代理类的invoke方法中发起的。

public class SwiftRpcServiceRegister implements ImportBeanDefinitionRegistrar {

    private static final Logger LOGGER = LoggerFactory.getLogger(SwiftRpcServiceRegister.class);

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(SwiftRpcServiceScan.class.getName());
        if (annotationAttributes != null) {
            String[] packages = (String[]) annotationAttributes.get("packages");
            SwiftRpcClassPathBeanDefinitionScanner scanner = new SwiftRpcClassPathBeanDefinitionScanner(registry);
            scanner.addIncludeFilter(new AnnotationTypeFilter(SwiftRpcService.class));

            if (packages == null || packages.length == 0) {
                LOGGER.warn("扫描RPC目录为空");
                return;
            }

            Set<BeanDefinitionHolder> definitionHolders = scanner.doScan(packages);
            for (BeanDefinitionHolder holder : definitionHolders) {
                GenericBeanDefinition beanDefinition = (GenericBeanDefinition) holder.getBeanDefinition();
                String className = beanDefinition.getBeanClassName();
                if (className == null) {
                    continue;
                }

                beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(className);
                beanDefinition.setBeanClass(SwiftRpcFactoryBean.class);
                beanDefinition.getPropertyValues().add("rpcInterface", className);
                beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            }
        }
    }

    static class SwiftRpcClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

        SwiftRpcClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
            super(registry, false);
        }

        @Override
        protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
            AnnotationMetadata metadata = beanDefinition.getMetadata();
            return metadata.isInterface() && metadata.isIndependent();
        }

        @Override
        protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
            return super.doScan(basePackages);
        }
    }
}
复制代码

通过rpcInterface的代理来执行请求的调用。

public class SwiftRpcFactory<T> implements InvocationHandler {

    private Class<T> rpcInterface;
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String className = rpcInterface.getName();
        String methodName = method.getName();

        SwiftRpcService rpcService = rpcInterface.getAnnotation(SwiftRpcService.class);
        String rpcServer = rpcService.server();
        RpcRequest request = new RpcRequest();
        request.setId(UUID.randomUUID().toString());
        request.setClassName(rpcInterface.getName());
        request.setMethodName(methodName);
        request.setParameters(args);
        request.setParameterTypes(method.getParameterTypes());

        SwiftClientRunner swiftClient = ApplicationContextHolder.getApplicationContext().getBean(SwiftClientRunner.class);
        return swiftClient.sendRequest(request, rpcServer);
    }
}
复制代码
  1. 选择对应RPC Server的连接,发送请求,并等待结果返回
public Object sendRequest(RpcRequest request, String rpcServer) throws InterruptedException, IOException {
    Channel channel = getChannel(rpcServer);
    if (channel != null && channel.isActive()) {
        // 发送请求后阻塞等待返回结果
        SynchronousQueue<Object> queue = swiftClientHandler.sendRequest(request, channel);
        return queue.poll(60, TimeUnit.SECONDS);
    }

    return null;
}
复制代码
@Component
@ChannelHandler.Sharable
public class SwiftClientHandler extends SimpleChannelInboundHandler<SwiftMessage> {

    private final ConcurrentHashMap<String, SynchronousQueue<Object>> requests = new ConcurrentHashMap<>();

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, SwiftMessage swiftMessage) throws Exception {
        byte[] content = swiftMessage.getContent();
        RpcResponse response = Kryos.deserialize(content, RpcResponse.class);
        String requestId = response.getRequestId();
        SynchronousQueue<Object> queue = requests.get(requestId);
        queue.put(response.getData());
    }

    SynchronousQueue<Object> sendRequest(RpcRequest request, Channel channel) throws IOException {
        SynchronousQueue<Object> queue = new SynchronousQueue<>();
        requests.put(request.getId(), queue);

        byte[] content = Kryos.serialize(request);
        SwiftMessage message = new SwiftMessage(content.length, content);
        channel.writeAndFlush(message);

        return queue;
    }
}
复制代码

总结

这里只是实现了一个很简单的RPC调用,很多地方都是简单处理了,比如用redis的hash存储代替服务注册中心。但是可以借助这个项目对RPC的工作流程有一个全面的了解,在学习和使用其他RPC框架也很有帮助。下面是对服务端和客户端工作流程的总结: 服务端:启动Netty服务,注册服务信息到注册中心,接收请求反序列化成RpcRequest,通过反射执行本地业务代码,将结果写回到Socket。 客户端:扫描RpcInterface列表,注册到Spring容器中心,启动服务,读取注册中心的RPC Server地址,建立连接,通过代理发起远程调用,等待结果返回。

项目地址:github.com/Phantom0103…
参考&感谢:
github.com/mybatis/spr…
juejin.cn/post/684490…

Supongo que te gusta

Origin juejin.im/post/7079780597691400206
Recomendado
Clasificación