RPC全称Remote Procedure Call,即远程过程调用,对于调用者无感知这是一个远程调用。目前流行的开源 RPC 框架有阿里的Dubbo、Google 的 gRPC、Twitter 的Finagle 等。本次RPC框架的设计主要参考的是阿里的Dubbo,这里Netty 基本上是作为架构的技术底层而存在的,主要完成高性能的网络通信,从而实现高效的远程调用。
Dubbo的架构与Spring
其实在之前的文章中《谈谈京东的服务框架》,探讨过Dubbo的组成和架构。
节点 | 说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
![](https://i-blog.csdnimg.cn/blog_migrate/f1daaafe5c4448034a41957f018a2792.png)
另外使用Dubbo最方便的地方在于它可以和Spring非常方便的集成,Dubbo对于配置的优化也是随着Spring一脉相承的,从最早的XML形式到后来的注解方式以及自动装配,都是在不断地简化开发过程来提高开发效率。
Dubbo在Spring框架中的工作流程:
1、Spring的IOC容器启动
2、把服务注册到注册中心(zookeeper软件)中
3、消费者启动时会把它需要用到的服务从注册中心拉取下来
4、提供者的地址发生改变时,注册中心会马上通知消费者
5、根据注册中心中的服务地址直接可以调用提供者了,如果调用了提供者,就会把提供者的地址主动缓存起来
6、监控消费者调用提供者的次数
RPC实现的关键
1、序列化与反序列化
在远程过程调用时,客户端跟服务端是不同的进程,甚至有时候客户端用Java,服务端用C++。这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式,这个过程叫序列化和反序列化,同理,从服务端返回的值也需要序列化反序列化的过程。在序列化的时候,我们选择Netty自身的对象序列化器。
![mark](https://i-blog.csdnimg.cn/blog_migrate/c2e8aee7609a2fe2ae408edd480a9629.png)
2、数据网络传输
解决了序列化的问题,那么剩下的就是如何把数据参数传到生产者,网络传输层需要把序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端,虽然大部分RPC框架都采用了TCP作为传输协议,其实UDP也可以作为传输协议的,基于TCP和UDP我们可以自定义任意规则的协议,加之我们要使用NIO通信方式作为高性能网络服务的前提,于是Netty似乎更符合我们Java程序员的口味,Netty真香!
3、告诉注册中心我要调谁
现在调用参数的序列化和网络传输都已经具备,但是还有个问题,那就是消费者要调用谁的问题,一个函数或者方法,我们可以理解为一个服务,这些服务注册在注册中心上面,只有当消费者告诉注册中心要调用谁,才可以进行远程调用。所以不但要把将要调用的服务的参数传过去,也要把要调用的服务信息传过去。
简易RPC框架的架构
![mark](https://i-blog.csdnimg.cn/blog_migrate/8d532ee558bbedac467ab8e0b64ef5ef.png)
Dubbo 核心模块主要有四个:Registry 注册中心、Provider 服务提供者、Consumer 服务消费者、Monitor监控,为了方便直接砍掉了监控模块,同时把服务提供者模块与注册中心模块写在一起,通过实现自己的简易IOC容器,完成对服务提供者的实例化。
关于使用Netty进行Socket编程的部分可以参考Netty的官网 或者我之前的博客《Netty编码实战与Channel生命周期》,在这里Netty的编码技巧和方式不作为本文的重点。
RPC框架编码实现
首先需要引入的依赖如下(Netty + Lombok):
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
</dependency>
1、Registry与Provider
目录结构如下:
───src
└─main
├─java
│ └─edu
│ └─xpu
│ └─rpc
│ ├─api
│ │ IRpcCalc.java
│ │ IRpcHello.java
│ │
│ ├─core
│ │ InvokerMessage.java
│ │
│ ├─provider
│ │ RpcCalcProvider.java
│ │ RpcHelloProvider.java
│ │
│ └─registry
│ MyRegistryHandler.java
│ RpcRegistry.java
│
└─resources
───pom.xml
IRpcCalc.java与IRpcHello.java是两个Service接口。IRpcCalc.java内容如下,完成模拟业务加、减、乘、除运算
public interface IRpcCalc {
// 加
int add(int a, int b);
// 减
int sub(int a, int b);
// 乘
int mul(int a, int b);
// 除
int div(int a, int b);