用,现在我们就来动手自己编写一个RPC框架,通过这篇文章的学习,你将学习到
- 分布式系统的概念
- RPC远程方法调用的应用
- Dubbo的原理深入理解
当然,如果要完全自己编写一个RPC框架,我们需要掌握以下知识点
- 网络编程(网络通信) 本文将使用netty4网络通信框架
- 多线程相关知识
- 反射相关知识
- jdk的动态代理
- Spring框架的相关知识
如果对于上述的知识点有一部分不是很理解,也不会影响你阅读本文和对Dubbo的RPC调用原理的理解
好了,我们先来简单的描述一下整个RPC调用的业务流程图
为了可以实现上面的RPC调用,我们创建的RPC框架的模块之间的关系图如下:
对于上面的每个模块的具体作用,使用一个表格简单的进行描述
模块名称 | 主要功能 |
---|---|
rpc-register | 主要完成可注册中心Zookeeper的交互<br />RPC服务端使用该模块往注册中心注册地址和端口<br />RPC客户端通过该模块获取实时已近注册的服务地址和端口 |
rpc-common | 定义RPC通信的请求消息和响应消息的规则,以及消息的序列化和反序列化的帮助类 |
rpc-server | RPC服务端,启动RPC服务,扫描app-server中的所有可以提供的服务列表并保存<br />接受RPC客户端的消息并且通过反射调用具体的方法<br/>响应RPC客户端,把方法执行结果返回到RPC客户端 |
rpc-client | RPC客户端,通过网络通信往RPC服务端发送请求调用消息<br/>接受服务端的响应消息<br/>配置动态代理类,所有的方法调用都通过网络调用发送到RPC服务端 |
app-common | 具体的应用中的接口和JavaBean对象,类似于service模块和bean模块 |
app-server | 通过Spring的配置启动SpringContext,并且配置RpcServer和RpcRegistry Bean对象的创建<br />实现app-common中的接口,并且在接口上添加注解@RpcService(IProductService.class)可以让RPCServer识别到该服务<br />启动服务 |
app-client | 通过Spring的配置创建RpcDiscover对象和RpcProxy对象,其中RpcDiscover用于从注册中心获取到服务的地址信息,RpcProxy用于创建类的动态代理对象 |
接下来我们来看一下具体的实现代码
-
rpc-register
这个模块用户和注册中心进行交互,主要包括三个类
- Constant常量定义,设置连接ZKServer的相关参数
- RpcRegistry:往注册中心ZKServer设置地址信息,RPC-Server需要使用
- RpcDiscover: 从注册中心ZKServer获取服务端的网络地址信息 RPC-client需要使用
具体的实现代码
package cn.wolfcode.rpc.register; public interface Constant { //定义客户端连接session会话超时时间,单位为毫秒,该值的设置和zkServer设置的心跳时间有关系 int SESSION_TIMEOUT=4000; // 定义用于保存rpc通信服务端的地址信息的目录 String REGISTRY_PATH="/rpc"; // 定义数据存放的具体目录 String DATA_PATH=REGISTRY_PATH+"/data"; }
package cn.wolfcode.rpc.register; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Setter@Getter @AllArgsConstructor() @NoArgsConstructor public class RpcRegistry { public static final Logger LOGGER=LoggerFactory.getLogger(RpcRegistry.class); //zkServer的地址信息 private String registryAddress; //zk客户端程序 private ZooKeeper zooKeeper; public void createNode(String data) throws Exception{ //创建一个客户端程序, 对于注册可以不用监听事件 zooKeeper= new ZooKeeper(registryAddress, Constant.SESSION_TIMEOUT, new Watcher() { @Override public void process(WatchedEvent event) { } }); if(zooKeeper!=null){ try{ //判断注册的目录是否存在 Stat stat = zooKeeper.exists(Constant.REGISTRY_PATH, false); if(stat==null){ //如果不存在, 创建一个持久的节点目录 zooKeeper.create(Constant.REGISTRY_PATH,null,ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); } //创建一个临时的序列节点,并且保存数据信息 zooKeeper.create(Constant.DATA_PATH,data.getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL); }catch (Exception e){ LOGGER.error("",e); e.printStackTrace(); } }else{ LOGGER.debug("zooKeeper connect is null"); } } //测试程序 public static void main(String[] args) throws Exception { RpcRegistry rpcRegistry = new RpcRegistry(); rpcRegistry.setRegistryAddress("192.168.158.151:2181"); rpcRegistry.createNode("testdata"); //让程序等待输入,程序一直处于运行状态 System.in.read(); } }
package cn.wolfcode.rpc.register; import lombok.Getter; import lombok.Setter; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.Random; @Setter @Getter //地址发现,用于实时的获取最新的RPC服务信息 public class RpcDiscover { public static final Logger LOGGER=LoggerFactory.getLogger(RpcRegistry.class); //服务端地址 zkServer的地址 private String registryAddress; //获取到的所有提供服务的服务器列表 private volatile List<String> dataList=new ArrayList<>(); private ZooKeeper zooKeeper=null; //初始化zkClient客户端 public RpcDiscover(String registryAddress) throws Exception { this.registryAddress = registryAddress; zooKeeper=new ZooKeeper(registryAddress, Constant.SESSION_TIMEOUT, new Watcher() { @Override public void process(WatchedEvent watchedEvent) { if(watchedEvent.getType()==Event.EventType.NodeChildrenChanged){ //监听zkServer的服务器列表变化 watchNode(); } } }); //获取节点相关数据 watchNode(); } // 从dataList列表随机获取一个可用的服务端的地址信息给rpc-client public String discover(){ int size=dataList.size(); if(size>0){ int index= new Random().nextInt(size); return dataList.get(index); } throw new RuntimeException("没有找到对应的服务器"); } //监听服务端的列表信息 private void watchNode(){ try{ //获取子节点信息 List<String> nodeList = zooKeeper.getChildren(Constant.REGISTRY_PATH, true); List<String> dataList=new ArrayList<>(); for (String node : nodeList) { byte[] bytes = zooKeeper.getData(Constant.REGISTRY_PATH + "/" + node, false, null); dataList.add(new String(bytes)); } this.dataList=dataList; }catch (Exception e){ LOGGER.error("",e); e.printStackTrace(); } } //测试程序 public static void main(String[] args) throws Exception { //打印获取到的连接地址信息 System.out.println(new RpcDiscover("192.168.158.151:2181").discover()); System.in.read(); } }
-
rpc-common
定义RPC通信的请求消息和响应消息的规则,以及消息的序列化和反序列化的帮助类,主要包括
- RpcReques