PinkHello
做一个快乐的程序猿
04 如何构建一个简单的RPC调用

1、什么叫RPC?

RPC调用服务过程

RPC构成

  • RPC Consumer
  • RPC Provider
  • ConfigServer
  • 1、Provider 启动 ConfigServer 注册服务
  • 2、Consumer 启动 ConfigServer 订阅服务,
  • 3、发起调用 Consumer —> Provider
  • 4、响应调用 Consumer <— Provider

2、什么是 Netty ? https://netty.io/

netty框架图

3、现有的开源的项目是否使用了 Netty ?

  • Dubbo
  • Grpc
  • Spark
  • ….

4、RPC Provider 启动

  • Netty Server 方式启动
  • Rpc 服务的注册 RPC与Netty结合Provider调用过程

5、RPC Consumer 启动

  • Netty Client 方式启动
  • RPC 泛化调用、通过字节码基于反射来实现远程调度
  • Consumer 服务订阅
  • 启动时建立长连接 RPC与Netty结合Consumer调用过程

6、从第四可以看出,多个 Provider 是由一个 NettyServer 提供的,通过 HandlerMap 映射找到对应的 Ioc Bean,完成服务调用

服务端

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
        public void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline p = ch.pipeline();
            p.addLast(new RpcEncoder(RpcRequest.class));
            p.addLast(new RpcDecoder(RpcResponse.class));
            p.addLast(new RpcHandler());
        }});
     // Start the server.
     ChannelFuture f = b.bind(PORT).sync();
     // Wait until the server socket is closed.
     f.channel().closeFuture().sync();
} finally {
    // Shut down all event loops to terminate all threads.
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}

Netty如何处理客户端TCP链接上的请求

NettyReactor工作框架图

  • BossGroup -> 处理客户端的请求
  • EventGroup —> 处理IO Read/Write 操作、执行任务系统Task、定时任务
  • ChildChannelHandler 方式是对 ChannelPipeline 的设置、
  • ChannelPipeline 是相当于任务链的模式, 是一串 ChannelHandler 的实例
  • ChannelHandlerContextChannelPipelineChannelHandler 的关系
  • 每个链接对于 Sever 端都会创建一个 Channel ,可以将 Channel 理解为 Connection (其实真正的是 Connection 属于 Channel 的一部分)
  • 每个 Channel 都有一个自己的唯一的 ChannelPipeline 操作,对于其他的 ChannelChannelPipeline 是隔离的
  • RPC Handler 是我们对于自己的找寻 RPC 服务处理的 Handler 实现
  • RPC Encoder 是我们对于自己的找寻 RPC 序列化的编码的 Handler 实现
  • RPC Decoder 是我们对于自己的找寻 RPC 序列化的解码的 Handler 实现
客户端
 
 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
        public void initChannel(SocketChannel ch) throws Exception {
         ChannelPipeline p = ch.pipeline();
         p.addLast(new RpcEncoder(RpcResponse.class));
         p.addLast(new RpcDecoder(RpcRequest.class));
         p.addLast(new RpcClientHandler());
        }});
     // Start the client.
     ChannelFuture f = b.connect(HOST, PORT).sync();
     // Wait until the connection is closed.
     f.channel().closeFuture().sync();
 } finally {
     // Shut down the event loop to terminate all threads.
     group.shutdownGracefully();
 }

7、RPC 序列化

  • 二进制数据
  • Hessian
  • Avro
  • ProtoBuffer (Protobuf)
  • JSON
  • XML

8、关于 RPC 的实现思考与技术讨论

  • 业务方法、因为是收到请求消息而触发的后续动作调用,不做额外设置,肯定是使用的 WorkGroup 里面的线程操作的。 而作为业务层,不应该与底层关联,应该切割开来,势必会引入真的业务侧线程池。 那么如何引用、怎么引用?(关于 业务线程池 与 WorkGroupEvenLoop 的思考 )
  • 关于 RPC 调用大部分是同步的调用,而 Netty 底层是完全异步事件机制,在RPC框架层面如何实现同步的调用方式的?
  • 基于 TCP 的长链接调用,在 RPC 上你会想到其他的哪些东东?
  • 在此环境下,我没有介绍 RPC 服务于 IOC容器的结合,可以思考一下,如何做到 注解机制、JAVA CONFIG 机制、XML SCHEMA 机制来做?
    • GUICE
    • SPRING
    • SPI
    • ….

最后修改于 2019-02-13