手把手教你写个RPC应用-服务端

125 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

我们基于netty构建一个RPC的服务端,进而了解一个RPC运行的基本原理。

定义rpc服务代理注解

用于标识需要作为远程调用的接口,应用于类上。

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@Component 
public @interface RpcService { }

定义handler处理器

这里只列出接收数据关键处理代码,rpcServices为所有需要rpc调用的实例类对象

private Map<String, Object> rpcServices;
public RpcServerInboundHandler(Map<String, Object> rpcServices) {    
    this.rpcServices = rpcServices;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {    
        RpcRequest rpcRequest = (RpcRequest) msg; 
        RpcResponse response = new RpcResponse(); 
        response.setRequestType(rpcRequest.getRequestType());    
        try {        
            if(0 == rpcRequest.getRequestType()){            
                log.info("接收到客户端请求, 请求接口 -> {}, 请求方法 -> {}", rpcRequest.getClassName(), rpcRequest.getMethodName()); 
                response.setRequestId(rpcRequest.getRequestId());            
                this.handleRequest(rpcRequest, response); 
            }else{            
                log.info("服务器收到心跳"); 
            }    
         } catch (Exception e) {        
             e.printStackTrace(); 
             response.setSuccess(false); 
             response.setErrorMessage(e.getMessage()); 
         }    
         log.info("服务器响应 -> {}", JSON.toJSONString(response)); 
         ctx.channel().writeAndFlush(response);
}
private void handleRequest(RpcRequest rpcRequest, RpcResponse response) throws Exception {    
    Object bean = rpcServices.get(rpcRequest.getClassName());    
    if (bean == null) {        
        throw new RuntimeException("未找到对应的服务: " + rpcRequest.getClassName()); 
    }    
    Method method = bean.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes()); 
    method.setAccessible(true); 
    response.setReturnType(method.getReturnType()); 
    response.setResult(method.invoke(bean, rpcRequest.getParameters()));
}

定义编码解码器

解码器

public class JsonDecoder extends LengthFieldBasedFrameDecoder {     
    public JsonDecoder() {         
        super(Integer.MAX_VALUE, 0, 4, 0, 4);     
    }     
    
    @Override     
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {         
        ByteBuf msg = (ByteBuf) super.decode(ctx, in);         
        byte[] bytes = new byte[msg.readableBytes()];         
        msg.readBytes(bytes);         
        RpcRequest rpcRequest = JSON.parseObject(bytes, RpcRequest.class);         
        // 类型转换         
        if(null != rpcRequest.getParameters() && rpcRequest.getParameters().length > 0){             
            for(int n =0; n < rpcRequest.getParameters().length; n++){                 
                if(null == rpcRequest.getParameters()[n]){                     
                    continue;                 
                }                 
                rpcRequest.getParameters()[n] = ((JSONObject) rpcRequest.getParameters()[n]).toJavaObject(rpcRequest.getParameterTypes()[n]);             
            }         
        }         
        return rpcRequest;     
    }
}

编码器

public class JsonEncoder extends MessageToByteEncoder<RpcResponse> {     
    @Override     
    protected void encode(ChannelHandlerContext ctx, RpcResponse rpcResponse, ByteBuf out) {         
        byte[] bytes = JSON.toJSONBytes(rpcResponse);         
        // 将消息体的长度写入消息头部         
        out.writeInt(bytes.length);         
        // 写入消息体         
        out.writeBytes(bytes);     
    } 
}

定义消息结构体

RpcRequest

/**  * 0 正常消息,1 心跳检查  */ 
private Integer requestType; 
/**  * 请求ID 用来标识本次请求以匹配RPC服务器的响应  */ 
private String requestId; 
/**  * 调用的类(接口)权限定名称  */ 
private String className; 
/**  * 调用的方法名  */ 
private String methodName; 
/**  * 方法参类型列表  */ 
private Class<?>[] parameterTypes; 
/**  * 方法参数  */ 
private Object[] parameters;

RpcResponse

/**  * 0 正常消息,1 心跳检查  */ 
private Integer requestType; 
/**  * 响应对应的请求ID  */ 
private String requestId; 
/**  * 调用是否成功的标识  */ 
private boolean success = true; 
/**  * 调用错误信息  */ 
private String errorMessage; 
/**  * 返回类型  */ 
private Class<?> returnType; 
/**  * 调用结果  */ 
private Object result;

启动rpc服务

启动netty服务,监听指定端口

@Component 
@Slf4j 
public class RpcServiceConfig {     
    @Value("${rpc.server.port}")     
    private int port;  
            
    @Bean     
    public CustomRpcServer serverStart(){         
        CustomRpcServer customRpcServer = new CustomRpcServer();         
        customRpcServer.setPort(port);         
        log.info("port={}", port);         
        return customRpcServer;     
    } 
}

获取spring上下文容器中带有rpcservice注解的对象

@Slf4j 
public class CustomRpcServer implements ApplicationContextAware, InitializingBean {     
    @Autowired     
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;     
    
    private Map<String, Object> rpcServices = new HashMap<>(5);     
    private int port;     
    
    public void setPort(int port) {         
        this.port = port;     
    }     
    
    @Override     
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {         
        Map<String, Object> services = applicationContext.getBeansWithAnnotation(RpcService.class);         
        for (Map.Entry<String, Object> entry : services.entrySet()) {             
            Object bean = entry.getValue();             
            Class<?>[] interfaces = bean.getClass().getInterfaces();             
            for (Class<?> inter : interfaces) {                 
                rpcServices.put(inter.getName(), bean);             
            }         
        }         
        log.info("加载RPC服务数量:{}", rpcServices.size());     
    }     
    
    @Override     
    public void afterPropertiesSet() {         
        threadPoolTaskExecutor.submit(() -> {             
        // 监听连接的 parent channel 的线程组             
        EventLoopGroup boss = new NioEventLoopGroup(1);             
        // 负责客户端连接读写的 child channel 线程组            
        EventLoopGroup worker = new NioEventLoopGroup();             
        try {                 
            ServerBootstrap bootstrap = new ServerBootstrap();                 
            // 设置reactor 线程                 
            bootstrap.group(boss, worker)                         
            // 装配流水线                   
            .childHandler(new ChannelInitializer<SocketChannel>() {                             
                @Override                             
                protected void initChannel(SocketChannel ch) throws Exception {                                 
                ChannelPipeline pipeline = ch.pipeline();                                 
                // 超时机制                                 
                pipeline.addLast(new IdleStateHandler(0, 0, 30));                                 
                pipeline.addLast(new JsonDecoder());                                 
                pipeline.addLast(new JsonEncoder());                                 
                // pipeline管理channel中的Handler                                 
                // 在channel队列中添加一个handler来处理业务                                 
                pipeline.addLast(new RpcServerInboundHandler(rpcServices));                             
             }})                         
             // 设置nio类型的channel                         
             .channel(NioServerSocketChannel.class);                 
             // 开始绑定server                 
             // 通过调用sync同步方法阻塞直到绑定成功                 
             ChannelFuture future = bootstrap.bind(port).sync();                 
             log.info("RPC 服务器启动, 监听端口:" + port);                 
             // 监听通道关闭事件                 
             // 应用程序会一直等待,直到channel关闭                 
             future.channel().closeFuture().sync();             
         } catch (Exception e) {                 
             e.printStackTrace();                 
             boss.shutdownGracefully();                 
             worker.shutdownGracefully();             
         }         
       });     
    } 
}