RPC 的原理分析及代码实践

RPC 简介

RPC 的魅力

RPC 全称 Remote Procedure Call, 即远程过程调用,可以实现调用远程接口像本地方法一样方便,但却屏蔽远程调用的复杂性。

RPC 的流程结构

  • 序列化:RPC是一个远程调用,所以必须要通过网络来传输数据。调用方的请求出入参数被封装成对象,就需要序列化对象为可传输的二进制数据。
  • 传输数据:调用方将二进制数据传输给服务提供方,此处一般会通过 TCP 协议进行安全可靠传输。
  • 反序列化:服务提供方也会根据特定的协议,将传输过来的二进制数据进行正确的切分不同请求,同时根据请求类型和序列化类型,把二进制数据的消息体逆向反序列化为真实的请求对象。
  • 方法调用:然后根据反序列化得到的请求对象,找到对应的类,进行具体方法的调用。
  • 返回请求:方法调用完需要将结果进行序列化后,回写到对应的 TCP 请求通道内。而调用方够获取到应答的数据包,也需要进行反序列化对象数据,这样就完成了一次完整的RPC的调用。

在这里插入图片描述

RPC 的兴起

随着目前微服务、分布式应用开发越来越常见,急需一种远程调用的协议来监控各个服务节点之间的调用,以及监控各个服务之间的通信情况。

比如我们经常说的服务治理功能:连接管理、健康检测、负载均衡、优雅启停机、异常重试、业务分组以及熔断限流等等。

而相应的针对微服务的兴起,一些常用的框架也逐渐进入了我们的眼帘,这些框架如果我们仔细分析源码的话,内部逻辑都有 RPC 协议的影子。

常用 RPC 协议框架

  • Dubbo:Dubbo 是一个分布式服务框架,以及 SOA 治理方案。其功能主要包括:高性能 NIO 铜须及多协议集成,服务的动态寻址与路由,软负载均衡与容错,依赖分析与降级等。2011 年阿里将 Dubbo 框架开源了。
  • Spring Cloud:Spring Cloud 由众多子项目组成,如Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul等。提供了搭建分布式系统及微服务常用的所有工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态监控等,满足了构建微服务的所有解决方案。
  • gRPC: 由 Google 开发的一款语言中立、平台中立、开源的远程调用系统。
  • Thrift:最初由 Facebook 开发的内部系统跨语言的 RPC 框架,2007 年开源,支持多种语言。

RPC 技术细节

通过分析 Dubbo 框架底层源码实现,也可以看出一个简单的 RPC 框架需要能够包含服务的暴露、服务的引用、服务的调用几个环节。为了能够直接了解具体的交互原理,可以通过一下流程图进行简单的了解一下。

在这里插入图片描述

手写 RPC 框架

为了更加深入的了解这个框架是否我们可以自己实现一个简易的 RPC 框架呢?答案是肯定的。以下就是鄙人通过研究源码以及看某些大神的讲解,写了一个简单的 demo 代码看下效果。

1. 目标

编写一个SpringBoot启动器类,定义名为"myrpc-spring-boot-starter", 为项目提供发现远程服务的功能。让调用远程服务如调用本地方法一样使用。首先会定义两个注解:

  • @RpcService:被此注解标记的类将提供远程服务的功能;
  • @ReferenceService:被此注解标记的成员变量,具有远程服务引用的功能;

2. 项目的整体结构

在这里插入图片描述

3. 客户端实现

3.1 客户端实现功能

客户端想要远程服务,必须具有服务发现的能力;发现服务后,还必须由服务代理来执行服务调用的能力。客户端想与服务端实现通信,必须具有相同的消息协议;客户端想要调用远程服务,那么必须具备网络请求的能力,即网络层功能。
以下是 pom 文件的相关依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.90.Final</version>
    </dependency>
    <dependency>
        <groupId>com.101tec</groupId>
        <artifactId>zkclient</artifactId>
        <version>0.11</version>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.6</version>
    </dependency>
</dependencies>
3.2 客户端服务发现

由上述引入的 Jar 包也可了解,此处的服务的发现与暴露,是通过 Zookeeper 创建节点的方法来实现的,代码如下:

/**
* 服务发现接口,定义服务发现的规范
*/
public interface ServiceDiscoverer {
   List<ServiceInfo> getServices(String name);
}
/**
* Zookeeper 服务发现者
*/
public class ZookeeperServiceDiscoverer implements ServiceDiscoverer {
   private ZkClient zkClient;
   public ZookeeperServiceDiscoverer(String zkAddress) {
      this.zkClient = new ZkClient(zkAddress);
      zkClient.setZkSerializer(new ZookeeperSerializer());
   }
   /**
    * 使用Zookeeper客户端,根据服务名获取服务列表
    *
    * @param name 服务名
    * @return 服务列表
    */
   @Override
   public List<ServiceInfo> getServices(String name) {
      String servicePath = RpcConstant.ZK_SERVICE_PATH + RpcConstant.PATH_DELIMITER + name + RpcConstant.PATH_SUFFIX;
      List<String> children = zkClient.getChildren(servicePath);
      return Optional.ofNullable(children).orElse(new ArrayList<>()).stream().map(str -> {
         String deCh = null;
         try {
            deCh = URLDecoder.decode(str, RpcConstant.UTF_8);
         } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
         }
         return JSON.parseObject(deCh, ServiceInfo.class);
      }).collect(Collectors.toList());
   }
}

/**
* <p>Title:自定义RPC框架</p>
* Description: Zookeeper序列化器</br>
*/
public class ZookeeperSerializer implements ZkSerializer {
   @Override
   public byte[] serialize(Object data) throws ZkMarshallingError {
      return String.valueOf(data).getBytes(StandardCharsets.UTF_8);
   }

   @Override
   public Object deserialize(byte[] bytes) throws ZkMarshallingError {
      return new String(bytes, StandardCharsets.UTF_8);
   }
}

/**
* <p>Title:自定义RPC框架</p>
* Description: 服务信息,记录注册服务的一些信息</br>
*/
public class ServiceInfo {
   /**
    * 服务名称
    */
   private String name;
   /**
    * 注册使用的协议
    */
   private String protocol;
   /**
    * 注册的服务地址,IP:Port
    */
   private String address;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public String getProtocol() {
      return protocol;
   }

   public void setProtocol(String protocol) {
      this.protocol = protocol;
   }

   public String getAddress() {
      return address;
   }

   public void setAddress(String address) {
      this.address = address;
   }
}

public class RpcConstant {
    /**
     * Zookeeper服务注册地址根路径
     */
    public static String ZK_SERVICE_PATH = "/my-rpc";


    /**
     * 路径的分隔符
     */
    public static String PATH_DELIMITER = "/";


    /**
     * 编码
     */
    public static String UTF_8 = "UTF-8";
    /**
     * 路径的后缀
     */
    public static String PATH_SUFFIX = "/service";
}
3.3 网络请求客户端

此处通过 Netty 框架实现网络客户端,实现请求数据的发送及响应数据的获取。

/**
* <p>Title:自定义RPC框架</p>
* Description: 网络请求客户端, 定义网络请求规范</br>
*/
public interface NetClient {
   byte[] sendRequest(byte[] data, ServiceInfo serviceInfo) throws InterruptedException;
}
/**
* <p>Title:自定义RPC框架</p>
* Description: 网络请求客户端实现</br>
*/
public class NettyNetClient implements NetClient {
   private static Logger logger = LoggerFactory.getLogger(NettyNetClient.class);
   /**
    * 发送请求数据
    *
    * @param data        请求数据
    * @param serviceInfo 服务信息
    * @return 响应数据
    * @throws InterruptedException
    */
   @Override
   public byte[] sendRequest(byte[] data, ServiceInfo serviceInfo) throws InterruptedException {
      String[] addressArray = serviceInfo.getAddress().split(":");
      String address = addressArray[0];
      String port = addressArray[1];
      SendHandler sendHandler = new SendHandler(data);
      byte[] respData;
      EventLoopGroup group = new NioEventLoopGroup();
      try {
         Bootstrap b = new Bootstrap();
         b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
               .handler(new ChannelInitializer<SocketChannel>() {
                  @Override
                  protected void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline pipeline = ch.pipeline();
                     pipeline.addLast(sendHandler);
                  }
               });
         b.connect(address, Integer.parseInt(port)).sync();
         respData = (byte[]) sendHandler.rspData();
         logger.info("SendRequest get reply: {}", respData);
      } finally {
         // 释放线程资源
         group.shutdownGracefully();
      }
      return respData;
   }
}

/**
* <p>Title:自定义RPC框架</p>
* Description: 发送处理类,定义Netty入站处理规则</br>
*/
public class SendHandler extends ChannelInboundHandlerAdapter {
   private static Logger logger = LoggerFactory.getLogger(SendHandler.class);
   private CountDownLatch cdl;
   private Object readMsg = null;
   private byte[] data;
   public SendHandler(byte[] data) {
      cdl = new CountDownLatch(1);
      this.data = data;
   }

   /**
    * 发送请求数据
    *
    * @param ctx 通道上下文
    * @throws Exception
    */
   @Override
   public void channelActive(ChannelHandlerContext ctx) throws Exception {
      logger.info("Successful connection to server: {}", ctx);
      ByteBuf reqBuf = Unpooled.buffer(data.length);
      reqBuf.writeBytes(data);
      logger.info("Client sends message: {}", reqBuf);
      ctx.writeAndFlush(reqBuf);
   }

   /**
    * 读取数据,数据读取完毕释放CD锁
    *
    * @param ctx 通道上下文
    * @param msg 接收的数据
    * @throws Exception
    */
   @Override
   public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      logger.info("Client reads message: {}", msg);
      ByteBuf msgBuf = (ByteBuf) msg;
      byte[] resp = new byte[msgBuf.readableBytes()];
      msgBuf.readBytes(resp);
      readMsg = resp;
      cdl.countDown();
   }

   @Override
   public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
      ctx.flush();
   }

   @Override
   public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
      cause.printStackTrace();
      logger.error("Exception occurred: {}", cause.getMessage());
      ctx.close();
   }

   /**
    * 等待读取数据完成
    *
    * @return 响应数据
    * @throws InterruptedException
    */
   public Object rspData() throws InterruptedException {
      cdl.await();
      return readMsg;
   }
}
3.4 服务代理

服务代理类客户端代理工厂类产生,代理方式通过Java的动态代理实现。在代理类ClientInvocationHandler的invoke方法中,主要实现了服务的获取、选择服务提供者、构造请求对象、请求对象的序列化、网络请求客户端的发送请求、响应消息的反序列化、异常处理等。

/**
* <p>Title:自定义RPC框架</p>
* Description: 客户端代理工厂,用于创建远程服务代理类
* 封装编组请求、请求发送、编组响应等操作</br>
*/
public class ClientProxyFactory {
   private ServiceDiscoverer serviceDiscoverer;
   private Map<String, MessageProtocol> supportMessageProtocols;
   private NetClient netClient;
   private Map<Class<?>, Object> objectCache = new HashMap<>();

   /**
    * 通过Java动态代理获取代理类
    *
    * @param clazz 被代理类class
    * @param <T>   泛型
    * @return
    */
   public <T> T getProxy(Class<T> clazz) {
      return (T) this.objectCache.computeIfAbsent(clazz,
            cls -> Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls}, new ClientInvocationHandler(cls)));
   }

   public void setSid(ServiceDiscoverer serviceDiscoverer) {
      this.serviceDiscoverer = serviceDiscoverer;
   }

   public NetClient getNetClient() {
      return netClient;
   }

   public void setNetClient(NetClient netClient) {
      this.netClient = netClient;
   }

   public Map<String, MessageProtocol> getSupportMessageProtocols() {
      return supportMessageProtocols;
   }

   public void setSupportMessageProtocols(Map<String, MessageProtocol> supportMessageProtocols) {
      this.supportMessageProtocols = supportMessageProtocols;
   }

   /**
    * 客户端服务器代理类
    */
   private class ClientInvocationHandler implements InvocationHandler {
      private Class<?> clazz;
      private Random random = new Random();

      public ClientInvocationHandler(Class<?> clazz) {
         super();
         this.clazz = clazz;
      }

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         if (method.getName().equals("toString")) {
            return proxy.getClass().toString();
         }

         if (method.getName().equals("hashCode")) {
            return 0;
         }

         // 1.获取服务信息
         String serviceName = this.clazz.getName();
         List<ServiceInfo> services = serviceDiscoverer.getServices(serviceName);
         if (services == null || services.isEmpty()) {
            throw new RpcException("No provider available!");
         }

         // 随机选取一个服务提供者(软负载均衡)
         ServiceInfo serviceInfo = services.get(random.nextInt(services.size()));

         // 2.构造request对象
         RpcRequest req = new RpcRequest();
         req.setServiceName(serviceInfo.getName());
         req.setMethod(method.getName());
         req.setParameterTypes(method.getParameterTypes());
         req.setParameters(args);

         // 3.协议层编码
         MessageProtocol protocol = supportMessageProtocols.get(serviceInfo.getProtocol());
         byte[] data = protocol.marshallingRequest(req);

         // 4.调用网络层发送请求
         byte[] repData = netClient.sendRequest(data, serviceInfo);
         // 5.响应消息的解码
         RpcResponse rpcResponse = protocol.unmarshallingResponse(repData);
         // 6.结果处理
         if (rpcResponse.getException() != null) {
            return rpcResponse.getException();
         }
         return rpcResponse.getReturnValue();
      }
   }
}
3.5 消息协议的定义

消息协议主要定义了请求消息序列化与反序列化,以及响应消息的序列化与反序列化。

/**
* <p>Title:自定义RPC框架</p>
* Description: 消息协议,定义请求及响应的编解码</br>
*/
public interface MessageProtocol {
   /**
    * 请求编码
    *
    * @param request 请求对象
    * @return 请求字节数组
    * @throws Exception
    */
   byte[] marshallingRequest(RpcRequest request) throws Exception;

   /**
    * 请求解码
    *
    * @param data 请求字节数组
    * @return 请求对象
    * @throws Exception
    */
   RpcRequest unmarshallingRequest(byte[] data) throws Exception;

   /**
    * 响应编码
    *
    * @param response 响应对象
    * @return 响应字节数组
    * @throws Exception
    */
   byte[] marshallingResponse(RpcResponse response) throws Exception;

   /**
    * 响应解码
    *
    * @param data 响应的字节数组
    * @return 响应对象
    * @throws Exception
    */
   RpcResponse unmarshallingResponse(byte[] data) throws Exception;
}
/**
* <p>Title:自定义RPC框架</p>
* Description: Java序列化消息协议</br>
*/
public class JavaSerializerMessageProtocol implements MessageProtocol {
   private byte[] serialize(Object obj) throws Exception {
      ByteArrayOutputStream bout = new ByteArrayOutputStream();
      ObjectOutputStream out = new ObjectOutputStream(bout);
      out.writeObject(obj);
      return bout.toByteArray();
   }

   @Override
   public byte[] marshallingRequest(RpcRequest request) throws Exception {
      return this.serialize(request);
   }

   @Override
   public RpcRequest unmarshallingRequest(byte[] data) throws Exception {
      ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));
      return (RpcRequest) in.readObject();
   }

   @Override
   public byte[] marshallingResponse(RpcResponse response) throws Exception {
      return this.serialize(response);
   }

   @Override
   public RpcResponse unmarshallingResponse(byte[] data) throws Exception {
      ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));
      return (RpcResponse) in.readObject();
   }
}

/**
* <p>Title:自定义RPC框架</p>
* Description: 请求消息的封装类</br>
*/
public class RpcRequest implements Serializable {
   private static final long serialVersionUID = 7865935599988450842L;
   private String serviceName;
   private String method;
   private Map<String, String> headers = new HashMap<>();
   private Class<?>[] parameterTypes;
   private Object[] parameters;
   // 省略 getter 和 setter 方法
}

/**
* <p>Title:自定义RPC框架</p>
* Description: 响应消息的封装</br>
*/
public class RpcResponse implements Serializable {
   private static final long serialVersionUID = -3631682513172006486L;
   private RpcStatus status;
   private Map<String, String> headers = new HashMap<>();
   private Object returnValue;
   private Exception exception;

   public RpcResponse(RpcStatus status) {
      this.status = status;
   }
   // 省略部分 getter 和 setter 方法
   public String getHeader(String name) {
      return this.headers == null ? null : this.headers.get(name);
   }
}

/**
* <p>Title:自定义RPC框架</p>
* Description: 响应状态码</br>
*/
public enum RpcStatus {
   SUCCESS(200, "SUCCESS"),
   ERROR(500, "ERROR"),
   NOT_FOUND(404, "NOT FOUND");

   private int code;
   private String message;

   RpcStatus(int code, String message) {
      this.code = code;
      this.message = message;
   }

   public int getCode() {
      return code;
   }

   public String getMessage() {
      return message;
   }
}

4. 服务端的实现

4.1 实现功能

首先,服务端要提供远程服务,必须具备服务注册及暴露的能力;在这之后,需要开启网络服务,供客户端连接。这里引用一个RPC处理者的概念,由它来帮助我们开启服务,以及注入服务。

4.2 注解实现
/**
* RPC 框架暴露服务注解
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RpcService {
   String value() default "";
}
/**
* RPC 引用服务注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReferenceService {
}
4.3 服务注册(暴露)

通过Zookeeper将要暴露的服务保存一个节点信息,供客户端进行发现使用。

/**
* <p>Title:自定义RPC框架</p>
* Description: 服务注册器,定义服务注册规范</br>
*/
public interface ServiceRegister {
   void register(ServiceObject so) throws Exception;
   ServiceObject getServiceObject(String name) throws Exception;
}

/**
* <p>Title:自定义RPC框架</p>
* Description: 默认注册器</br>
*/
public class DefaultServiceRegister implements ServiceRegister{
   private Map<String, ServiceObject> serviceMap = new HashMap<>();
   protected String protocol;
   protected Integer port;
   @Override
   public void register(ServiceObject so) throws Exception {
      if (so == null){
         throw new IllegalArgumentException("Parameter cannot be empty.");
      }
      this.serviceMap.put(so.getName(), so);
   }

   @Override
   public ServiceObject getServiceObject(String name) throws Exception {
      return this.serviceMap.get(name);
   }
}
/**
* <p>Title:自定义RPC框架</p>
* Description: Zookeeper服务注册器</br>
*/
public class ZookeeperExportServiceRegister extends DefaultServiceRegister implements ServiceRegister {
   private ZkClient client;

   public ZookeeperExportServiceRegister(String zkAddress, String protocol, Integer port) {
      this.client = new ZkClient(zkAddress);
      this.client.setZkSerializer(new ZookeeperSerializer());
      this.protocol = protocol;
      this.port = port;
   }

   @Override
   public void register(ServiceObject so) throws Exception {
      super.register(so);
      ServiceInfo serviceInfo = new ServiceInfo();
      String host = InetAddress.getLocalHost().getHostAddress();
      String address = host + ":" + port;
      serviceInfo.setAddress(address);
      serviceInfo.setName(so.getClazz().getName());
      serviceInfo.setProtocol(protocol);
      this.exportService(serviceInfo);
   }

   private void exportService(ServiceInfo serviceInfo) {
      String serviceName = serviceInfo.getName();
      String uri = JSON.toJSONString(serviceInfo);
      try {
         uri = URLEncoder.encode(uri, RpcConstant.UTF_8);
      } catch (UnsupportedEncodingException e) {
         e.printStackTrace();
      }
      String servicePath = RpcConstant.ZK_SERVICE_PATH + RpcConstant.PATH_DELIMITER + serviceName + RpcConstant.PATH_SUFFIX;
      if (!client.exists(servicePath)) {
         client.createPersistent(servicePath, true);
      }
      String uriPath = servicePath + RpcConstant.PATH_DELIMITER + uri;
      if (client.exists(uriPath)) {
         client.delete(uriPath);
      }
      client.createEphemeral(uriPath);
   }
}
4.4 网络服务端

依然是通过Netty实现网络服务端,并暴露服务端口供客户端进行连接。

/**
* <p>Title:自定义RPC框架</p>
* Description: RPC 服务抽象类</br>
*/
public abstract class RpcServer {
   /**
    * 服务端口
    */
   protected int port;
   /**
    * 服务协议
    */
   protected String protocol;
   /**
    * 请求处理者
    */
   protected RequestHandler requestHandler;

   public RpcServer(int port, String protocol, RequestHandler requestHandler) {
      this.port = port;
      this.protocol = protocol;
      this.requestHandler = requestHandler;
   }

   /**
    * 开启服务
    */
   public abstract void start();

   /**
    * 停止服务
    */
   public abstract void stop();
}
/**
* <p>Title:自定义RPC框架</p>
* Description: Netty RPC服务端,提供Netty网络服务开启、关闭的功能</br>
*/
public class NettyRpcServer extends RpcServer {
   private Logger logger = LoggerFactory.getLogger(NettyRpcServer.class);
   private Channel channel;

   public NettyRpcServer(int port, String protocol, RequestHandler requestHandler) {
      super(port, protocol, requestHandler);
   }

   @Override
   public void start() {
      EventLoopGroup bossGroup = new NioEventLoopGroup(1);
      EventLoopGroup workerGroup = new NioEventLoopGroup();
      try {
         ServerBootstrap b = new ServerBootstrap();
         b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
               .option(ChannelOption.SO_BACKLOG, 100)
               .handler(new LoggingHandler(LogLevel.INFO))
               .childHandler(new ChannelInitializer<SocketChannel>() {
                  @Override
                  protected void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline pipeline = ch.pipeline();
                     pipeline.addLast(new ChannelRequestHandler());
                  }
               });
         ChannelFuture f = b.bind(port).sync();
         logger.info("Server started successfully.");
         channel = f.channel();
         f.channel().closeFuture().sync();
      } catch (InterruptedException e) {
         e.printStackTrace();
      } finally {
         bossGroup.shutdownGracefully();
         workerGroup.shutdownGracefully();
      }
   }

   @Override
   public void stop() {
      this.channel.close();
   }

   private class ChannelRequestHandler extends ChannelInboundHandlerAdapter {
      @Override
      public void channelActive(ChannelHandlerContext ctx) throws Exception {
         logger.info("Channel active: {}", ctx);
      }

      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
         logger.info("The server receives a message: {}", msg);
         ByteBuf msgBuf = (ByteBuf) msg;
         byte[] req = new byte[msgBuf.readableBytes()];
         msgBuf.readBytes(req);
         byte[] res = requestHandler.handleRequest(req);
         logger.info("Send response: {}", msg);
         ByteBuf rspBuf = Unpooled.buffer(res.length);
         rspBuf.writeBytes(res);
         ctx.write(rspBuf);
      }

      @Override
      public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
         ctx.flush();
      }

      @Override
      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
         cause.printStackTrace();
         logger.error("Exception occurred: {}", cause.getMessage());
         ctx.close();
      }
   }
}

/**
* <p>Title:自定义RPC框架</p>
* Description: 请求处理器</br>
*/
public class RequestHandler {
   private MessageProtocol protocol;
   private ServiceRegister register;

   public RequestHandler(MessageProtocol protocol, ServiceRegister register) {
      super();
      this.protocol = protocol;
      this.register = register;
   }

   public byte[] handleRequest(byte[] data) throws Exception {
      // 1.请求消息解码
      RpcRequest req = this.protocol.unmarshallingRequest(data);
      // 2.查找服务对象
      ServiceObject so = this.register.getServiceObject(req.getServiceName());
      RpcResponse rsp = null;
      if (so == null) {
         rsp = new RpcResponse(RpcStatus.NOT_FOUND);
      } else {
         // 3.反射调用对应的过程方法
         try {
            Method m = so.getClazz().getMethod(req.getMethod(), req.getParameterTypes());
            Object returnValue = m.invoke(so.getObj(), req.getParameters());
            rsp = new RpcResponse(RpcStatus.SUCCESS);
            rsp.setReturnValue(returnValue);
         } catch (Exception e) {
            rsp = new RpcResponse(RpcStatus.ERROR);
            rsp.setException(e);
         }
      }
      // 4.对响应消息编码
      return this.protocol.marshallingResponse(rsp);
   }

   public MessageProtocol getProtocol() {
      return protocol;
   }

   public void setProtocol(MessageProtocol protocol) {
      this.protocol = protocol;
   }

   public ServiceRegister getRegister() {
      return register;
   }

   public void setRegister(ServiceRegister register) {
      this.register = register;
   }
}
4.5 RPC 处理器

通过Spring框架实现ApplicationListener,监听ContextRefreshEvent事件,实现Spring启动完毕后收到事件通知,然后注册服务及注入服务。

/**
* <p>Title:自定义RPC框架</p>
* Description: RPC处理器,支持服务启动暴露、自动注入Service</br>
*/
public class DefaultRpcProcessor implements ApplicationListener<ContextRefreshedEvent> {
   @Resource
   private ClientProxyFactory clientProxyFactory;

   @Resource
   private ServiceRegister serviceRegister;

   @Resource
   private RpcServer rpcServer;

   @Override
   public void onApplicationEvent(ContextRefreshedEvent event) {
      if (Objects.isNull(event.getApplicationContext().getParent())) {
         ApplicationContext context = event.getApplicationContext();
         // 开启并暴露服务
         startServer(context);

         // 注入服务
         injectService(context);
      }
   }

   private void startServer(ApplicationContext context) {
      Map<String, Object> beans = context.getBeansWithAnnotation(RpcService.class);
      if (beans.size() != 0) {
         boolean startServerFlag = true;
         for (Object obj : beans.values()) {
            try {
               Class<?> aClass = obj.getClass();
               Class<?>[] interfaces = aClass.getInterfaces();
               ServiceObject so;
               if (interfaces.length != 1) {
                  RpcService rpcService = aClass.getAnnotation(RpcService.class);
                  String value = rpcService.value();
                  if (StringUtils.isBlank(value)) {
                     startServerFlag = false;
                     throw new UnsupportedOperationException("The exposed interface is not specific with '" + obj.getClass().getName() + "'");
                  }
                  so = new ServiceObject(value, Class.forName(value), obj);
               } else {
                  Class<?> superClass = interfaces[0];
                  so = new ServiceObject(superClass.getName(), superClass, obj);
               }
               serviceRegister.register(so);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
         if (startServerFlag) {
            rpcServer.start();
         }
      }
   }

   private void injectService(ApplicationContext context) {
      String[] names = context.getBeanDefinitionNames();
      for (String name : names) {
         Class<?> clazz = context.getType(name);
         if (!Objects.isNull(clazz)) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
               ReferenceService ref = field.getAnnotation(ReferenceService.class);
               if (Objects.isNull(ref)) continue;
               Class<?> fieldClass = field.getType();
               Object object = context.getBean(name);
               field.setAccessible(true);
               try {
                  field.set(object, clientProxyFactory.getProxy(fieldClass));
               } catch (IllegalAccessException e) {
                  e.printStackTrace();
               }
            }
         }
      }
   }
}

5. Spring Boot启动器配置

5.1 配置属性类
/**
* <p>Title:自定义RPC框架</p>
* Description: 参数配置类,实现用户自定义参数</br>
*/
@EnableConfigurationProperties(MyRpcProperty.class)
@ConfigurationProperties("my.rpc")
public class MyRpcProperty {
   /**
    * 服务注册中心
    */
   private String registerAddress = "127.0.0.1:2181";
   /**
    * 服务端暴露端口
    */
   private Integer serverPort = 21000;
   /**
    * 服务协议
    */
   private String protocol = "myrpc";

   // 省略 getter 和 setter 方法
}
5.2 自动配置类
/**
* <p>Title:自定义RPC框架</p>
* Description: Spirng boot 自动配置</br>
*/
@Configuration
public class MyRpcEnableAutoConfiguration {
   @Bean
   public MyRpcProperty myRpcProperty() {
      return new MyRpcProperty();
   }

   @Bean
   public DefaultRpcProcessor defaultRpcProcessor() {
      return new DefaultRpcProcessor();
   }

   @Bean
   public ClientProxyFactory clientProxyFactory(@Autowired MyRpcProperty myRpcProperty) {
      ClientProxyFactory clientProxyFactory = new ClientProxyFactory();
      // 设置服务发现者
      clientProxyFactory.setSid(new ZookeeperServiceDiscoverer(myRpcProperty.getRegisterAddress()));

      // 设置支持的协议
      Map<String, MessageProtocol> supportMessageProtocols = new HashMap<>();
      supportMessageProtocols.put(myRpcProperty.getProtocol(), new JavaSerializerMessageProtocol());
      clientProxyFactory.setSupportMessageProtocols(supportMessageProtocols);

      // 设置网络层实现
      clientProxyFactory.setNetClient(new NettyNetClient());
      return clientProxyFactory;
   }

   @Bean
   public ServiceRegister serviceRegister(@Autowired MyRpcProperty myRpcProperty) {
      return new ZookeeperExportServiceRegister(
            myRpcProperty.getRegisterAddress(),
            myRpcProperty.getProtocol(),
            myRpcProperty.getServerPort()
      );
   }

   @Bean
   public RequestHandler requestHandler(@Autowired ServiceRegister serviceRegister){
      return new RequestHandler(new JavaSerializerMessageProtocol(), serviceRegister);
   }

   @Bean
   public RpcServer rpcServer(@Autowired RequestHandler requestHandler,
                        @Autowired MyRpcProperty myRpcProperty){
      return new NettyRpcServer(myRpcProperty.getServerPort(), myRpcProperty.getProtocol(), requestHandler);
   }
}

RPC 框架的使用

1. 引入依赖

<dependency>
   <groupId>com.xquant.rpc</groupId>
   <artifactId>myrpc-spring-boot-starter</artifactId>
   <version>1.0.0</version>
</dependency>

2. 远程服务暴露

import com.xquant.rpc.demo.annotation.RpcService;
@RpcService
public class UserServiceImpl implements UserService {
   @Override
   public User getUser(Long id) {
      return new User(10001L, "ZhangSan", 29, "SH");
   }
}

3. 服务注入调用

import com.xquant.rpc.demo.annotation.ReferenceService;
@RestController
@RequestMapping("user")
public class UserController {

   @ReferenceService
   private UserService userService;

   @GetMapping("/getUser")
   public User getUser(Long id) {
      return userService.getUser(id);
   }
}

小结

综上所述,RPC 框架是一个很复杂的处理流程。包括了服务的暴露、服务的发现、网络服务的处理、对象的序列化与发序列化、Java代理与反射等多方面知识的总结。其中Spring Boot 的 Starter 插件的自动装配实现也很重要,它也大大简化了自制框架的快速实现。

  • 33
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值