RPC框架以及实现

前言:

首先提出一个问题:为什么需要使用RPC,而不是简单的http接口?

下面就说一下原因
http接口在接口不多、系统与系统交互较少的情况下,解决信息孤岛初期常使用的一种通信手段,优点就是简单、直接、开发方便。但是如果是一个大型的系统,内部子系统较多、接口非常多的情况下,RPC框架的好处就显现出来了。如下:

  • 首先是长链接。不必每次通信都要像http一样去进行3次握手和4次挥手,减少了网络开销。

  • 其次就是RPC框架一般都有注册中心,有丰富的监控管理。发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。

  • 再次来说就是安全性。服务架构、服务化治理,RPC框架是一个强力的支撑。

分布式架构作为大型系统架构,高性能分布式调用离不开RPC框架,我们常用的有Dubbo、gRPC、RestTemplate、webservice等。RPC框架看上去高深莫测,其实就三个核心点:

  1. 动态代理技术,像调用本地服务一样调用远程服务,需要对使用者透明
  2. 网络通信框架,用以开发高性能、高可靠的网路服务器和客户端程序。例如BIO(ServerSocket),NIO(ServerSocketChannel/selector)
  3. 序列化,也就是数据包传输格式,将数据转为可传输对象,如Dubbo、json、xml、protobuf、avro、hessian

今天就先说下接口动态代理调用以及网络通信框架,然后再实现一个RPC框架实例。

一 接口调用动态代理

基于JDK的动态代理是基于接口的动态代理。在JDK动态代理机制中,有两个重要的类和接口,一个是InvocationHandler、另一个则是Proxy类,这个类和接口是实现动态代理必须用到的。InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作。而Proxy则用来创建动态代理类实例对象,因为只有得到了这个对象我们才能调用那些需要代理的方法。

下面直接上代码

1、InvocationHandler接口实现类

RPC框架以及实现
当接口代理类调用接口方法时,实际上就是调用InterfaceInvokeHandler.invoke方法。所以在这个方法中就可写自己想做的一些事情,比如建立远程连接、发起远程调用等。

2、Proxy创建接口代理对象

使用工厂模式,传入要代理的 接口Class对象,就会创建出接口代理对象。
RPC框架以及实现
大家能看到代理实际处理类以及方法使我们上面定义的InterfaceInvokeHandler.invoke()。

3、 测试接口代理调用

接口类:
RPC框架以及实现
测试代码

RPC框架以及实现 用接口代理工程类产品接口代理对象,再调用接口方法。运行后从输出接口看出我们已经代理了这个接口的调用。

RPC框架以及实现

二、网络通信框架Netty

netty是jboss提供的一个java开源框架,netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可用性的网络服务器和客户端程序。也就是说netty是一个基于nio的编程框架,使用netty可以快速的开发出一个网络应用。

相比JDK原生NIO,Netty提供了相对简单易用的API,非常适合网络编程。Netty是完全基于NIO实现的,所以Netty是异步的。无疑是NIO的老大,它的健壮性、功能、性能、可定制性和可扩展性在同类框架都是首屈一指的。它已经得到成百上千的商业以及商用项目的验证,如Hadoop的RPC框架Avro、RocketMQ以及主流的分布式调用框架Dubbo等。

1、JDK 的 NIO 类库缺点

  • NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、
    ServerSocketChannel、SocketChannel、ByteBuffer等。
  • 需要具备其他的额外技能做铺垫,例如熟悉Java多线程编程。这是因为
    NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能 编写出高质量的NIO程序。
  • 可靠性靠能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网 络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等问题,NIO
    编程的特点是功能开发相对容易,但是可靠性能力补齐的工作量和难度都 非常大。
  • JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询, 最终导致CPU100%。官方声称在JDK1.6版本的update18修复了该问题,但
    是直到JDK1.7版本该问题仍旧存在,只不过该BUG发生概率降低了一些而 已,它并没有被根本解决。

2、Netty的优点

  • API使用简单,开发门槛低;
  • 功能强大,预置了多种编解码功能,支持多种主流协议;
  • 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;
  • 性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优; 成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要再为NIO的BUG而烦恼;
  • 社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会加入;
  • 经历了大规模的商业应用考验,质量得到验证。在互联网、大数据、网络游戏、企业应用、电信软件等众多行业得到成功商用,证明了它已经完全能够满足不同行业的商业应用了。

3、netty的核心组件

  • 1、ServerBootstrap是服务器端的辅助启动类,Bootstrap是客户端的辅助启动类。一个Netty应用通常由一个Bootstrap开始,主要作用是配置整个Netty程序,串联各个组件,Netty中Bootstrap类是客户端程序的启动引导类,ServerBootstrap是服务端启动引导类,启动Netty程序,配置连接参数等作用。
  • 2、eventLoop:eventLoop维护了一个线程和任务队列,支持异步提交执行任务。

  • 3、eventLoopGroup:eventLoopGroup
    主要是管理eventLoop的生命周期,可以将其看作是一个线程池,其内部维护了一组eventLoop,每个eventLoop对应处理多个Channel,而一个Channel只能对应一个eventLoop。

  • 4、channelPipeLine:是一个包含channelHandler的list,用来设置channelHandler的执行顺序。
  • 5、Channel:Channel代表一个实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的IO操作的程序组件)的开放链接,如读操作和写操作。

  • 6、Futrue、ChannelFuture:Future提供了另一种在操作完成时通知应用程序的方式。这个对象可以看作是一个异步操作结果的占位符;它将在未来的某个时刻完成,并提供对其结果的访问。netty的每一个出站操作都会返回一个ChannelFuture。future上面可以注册一个监听器,当对应的事件发生后会出发该监听器。

  • 7、ChannelInitializer:它是一个特殊的ChannelInboundHandler,当channel注册到eventLoop上面时,对channel进行初始化

  • 8、ChannelHandler:用来处理业务逻辑的代码,ChannelHandler是一个父接口,ChannelnboundHandler和ChannelOutboundHandler都继承了该接口,它们分别用来处理入站和出站。

  • 9、ChannelHandlerContext:允许与其关联的ChannelHandler与它相关联的ChannlePipeline和其它ChannelHandler来进行交互。它可以通知相同ChannelPipeline中的下一个ChannelHandler,也可以对其所属的ChannelPipeline进行动态修改。

三、序列化报文

当通过Netty发送或者接收一个消息的时候,就将会发生一次数据转换。入站消息会被解码:从字节转换为另一种格式,通常是一个Java对象。如果是出站消息,则会发生编码:将从它的当前格式被编码为字节。

Netty默认已经提供了一堆的编/解码器,一般需求已经可以满足,如果不满足可以通过集成ByteToMessageDecoder或MessageToByteEncoder来实现自己的编/解码器。通过查看类的继承结构可以看出Netty提供的编/解码器适配器类都实现了ChannelOutboundHandler或者ChannellnboundHandler接口。

对于解码器Handler而言,其重写了channelRead方法,对于每个从入站Channel读取的消息,这个方法都将会被调用。随后,它将调用由解码器所提取的decode()方法,并将已经解码的字节转发给ChannelPipeline中的下一个ChannellnboundHandler。

OutboundHandler采用相反的处理方式。

四、实现RPC框架

1、整体工程目录如下:
RPC框架以及实现
整体结构说明:

  • rpc-demo-api:定义一个服务接口。用于服务消费者和提供者之间的服务接口调用约定。
  • rpc-demo-server:服务接口具体实现,并创建一个Rpc服务端,监听消费者的Rpc调用请求,并返回数据。
  • rpc-demo-client:创建一个Rpc客户端,调动Rpc服务接口,通过代理调用完成远程Rpc调用。

API工程定义一个服务接口:

RPC框架以及实现

未完,待续