在这篇推文中,小媛将为大家推荐一个Java项目——手写RPC框架。
前言
RPC框架代码量较多,将仅对核心过程进行梳理,完整代码见:https://github.com/wdw87/wRpc
在这篇推文中,将对项目本身以及技术栈进行进行简单介绍,并且给出框架测试Demo。
在接下来的系统推文中,将对项目进行详细的介绍。
主要将按照下面的内容进行分配:
手写RPC框架(一) | RPC简介、技术栈介绍、测试Demo |
---|---|
手写RPC框架(二) | 远程通信实现 |
手写RPC框架(三) | 制定协议与编解码器、动态代理 |
手写RPC框架(四) | 注册中心 |
一、什么是RPC
RPC(Remote Procedure Call)即远程过程调用,是一种计算的通讯框架。该框架允许运行与一台计算机上的程序调用另一台计算机的程序,而程序员无需为这个交互过程做额外的编程。
RPC框架有很多,例如阿里的Dubbo,谷歌的gRPC等
一个简单的RPC框架主要涉及如下内容
- 动态代理
- 反射
- 序列化和反序列化
- 编码与解码
- 网络通信
- 服务注册与服务发现
- ...
完整代码见:https://github.com/wdw87/wRpc
二、技术栈
- Spring框架,项目的基础框架,方便打包,调试,并且基于Spring实现框架配置
- Netty,使用基于Netty的异步IO,提高网络通信性能
- zookeeper,基于zookeeper实现服务注册与服务发现
- fastjson,实现序列化和反序列化
- cglib,实现动态代理
三、Demo
框架介绍完毕,接下来测试一下框架:
在框架注册两个服务:
- calculate:实现加法减法运算
- multiply:实现乘法运算
完整代码见:https://github.com/wdw87/wRpc
1. 服务端
配置
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wrpc="https://www.wdw87.com/schema/wrpc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
https://www.wdw87.com/schema/wrpc
https://www.wdw87.com/schema/wrpc/wrpc.xsd
">
<wrpc:application name="rpc_provider"/>
<!--服务器提供服务的端口-->
<wrpc:server port="8000" />
<!--注册中心-->
<wrpc:registry registry_address="123.57.175.207:2181" connection_timeout="20000" session_timeout="20000"/>
<!--服务一-->
<wrpc:service id="calculate" name="com.wdw.wrpc.service.CalculateInterFace" ref="CalculateInterFaceImpl"/>
<bean name="CalculateInterFaceImpl" class="com.wdw.wrpc.service.impl.CalculateImpl"/>
<!--服务二-->
<wrpc:service id = "multiply" name="com.wdw.wrpc.service.MultiplyInterface" ref="MultiplyImpl"/>
<bean name="MultiplyImpl" class="com.wdw.wrpc.service.impl.MultiplyImpl"/>
</beans>
服务类
代码语言:javascript复制/**
* 服务一
*/
public interface CalculateInterFace {
int add(int a, int b);
int dec(int a, int b);
}
public class CalculateImpl implements CalculateInterFace {
@Override
public int add(int a, int b) {
return a b;
}
@Override
public int dec(int a, int b) {
return a - b;
}
}
/**
* 服务二
*/
public interface MultiplyInterface {
Double multiply(Double a, Double b);
}
public class MultiplyImpl implements MultiplyInterface {
@Override
public Double multiply(Double a, Double b) {
return a * b;
}
}
服务端编程
代码语言:javascript复制public class Server {
public static void main(String[] args) throws InterruptedException {
//Spring框架读取配置文件,启动
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
context.start();
}
}
2. 客户端
配置
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wrpc="https://www.wdw87.com/schema/wrpc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
https://www.wdw87.com/schema/wrpc
https://www.wdw87.com/schema/wrpc/wrpc.xsd
">
<wrpc:application name="rpc_consumer"/>
<!--负载均衡策略和动态代理方式-->
<wrpc:client load_balance="random" proxy="cglib"/>
<!--注册中心-->
<wrpc:registry registry_address="123.57.175.207:2181" connection_timeout="20000" session_timeout="20000"/>
<!--引用服务一-->
<wrpc:reference id="calculate" name="com.wdw.wrpc.service.CalculateInterFace"/>
<!--引用服务一-->
<wrpc:reference id="multiply" name="com.wdw.wrpc.service.MultiplyInterface"/>
</beans>
服务接口
接口与服务端相互统一
代码语言:javascript复制/**
* 服务一
*/
public interface CalculateInterFace {
int add(int a, int b);
int dec(int a, int b);
}
/**
* 服务二
*/
public interface MultiplyInterface {
Double multiply(Double a, Double b);
}
客户端编程
代码语言:javascript复制public class Client {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("consumer.xml");
applicationContext.start();
//调用服务一
CalculateInterFace bean = (CalculateInterFace)SpringUtil.getApplicationContext().getBean("calculate");
System.out.println(bean.add(1,2));
System.out.println(bean.dec(10,2));
//调用服务二
MultiplyInterface bean1 = (MultiplyInterface)SpringUtil.getApplicationContext().getBean("multiply");
System.out.println(bean1.multiply(3.5,5.5));
}
}
3. 启动测试
服务端
服务端启动后,首先启动了Netty服务器,然后连接了Zookeeper,并将两个服务注册到Zookeeper,日志如下:
代码语言:javascript复制...
[2020-03-29 17:55:48 下午]:INFO com.wdw.wrpc.server.netty.NettyServer.start(NettyServer.java:46)服务端启动中...
[2020-03-29 17:55:48 下午]:INFO com.wdw.wrpc.server.netty.NettyServer$2.operationComplete(NettyServer.java:70)server boot successful, bind port [8000]
[2020-03-29 17:55:49 下午]:INFO com.wdw.wrpc.server.registry.ServiceRegistry.connectServer(ServiceRegistry.java:51)zookeeper 已连接
[2020-03-29 17:55:50 下午]:INFO com.wdw.wrpc.server.registry.ServiceRegistry.register(ServiceRegistry.java:73)注册了服务:/wrpc/com.wdw.wrpc.service.CalculateInterFace/providers/192.168.1.46:8000
[2020-03-29 17:55:50 下午]:INFO com.wdw.wrpc.server.registry.ServiceRegistry.register(ServiceRegistry.java:73)注册了服务:/wrpc/com.wdw.wrpc.service.MultiplyInterface/providers/192.168.1.46:8000
...
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelActive(ServiceRequestHandler.java:30)客户端建立了连接
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelRead0(ServiceRequestHandler.java:64)Service invoked : ServiceRequestPacket{id='0082455e56de40e69dce96a43ded890d', className='com.wdw.wrpc.service.CalculateInterFace', methodName='add', parameterTypes=[int, int], args=[1, 2]}
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelRead0(ServiceRequestHandler.java:64)Service invoked : ServiceRequestPacket{id='da862a30b3bf4e7f93f1ee2776cb5444', className='com.wdw.wrpc.service.CalculateInterFace', methodName='dec', parameterTypes=[int, int], args=[10, 2]}
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelActive(ServiceRequestHandler.java:30)客户端建立了连接
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.server.netty.ServiceRequestHandler.channelRead0(ServiceRequestHandler.java:64)Service invoked : ServiceRequestPacket{id='5b0eac41b43a430f9df91846f2e696a5', className='com.wdw.wrpc.service.MultiplyInterface', methodName='multiply', parameterTypes=[class java.lang.Double, class java.lang.Double], args=[3.5, 5.5]}
...
然后,客户端发起调用请求时,服务端调用了相应服务的实现类,并且相应调用结果
客户端
客户端启动后,日志如下:
针对两个服务,经历了如下过程:
- 连接Zookeeper
- 发布客户端引用
- 获取服务列表
- 监听服务变化
[2020-03-29 17:55:55 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.connectServer(ServiceDiscovery.java:70)zookeeper 已连接
[2020-03-29 17:55:55 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.registerReference(ServiceDiscovery.java:103)客户端引用发布成功:[/wrpc/com.wdw.wrpc.service.CalculateInterFace/consumers/192.168.1.46]
[2020-03-29 17:55:55 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:113)正在获取服务引用[com.wdw.wrpc.service.CalculateInterFace]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:122)发现服务器列表[192.168.1.46:8000]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:124)引用服务获取完成[/wrpc/com.wdw.wrpc.service.CalculateInterFace/providers]:com.wdw.wrpc.service.CalculateInterFace
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.registerReference(ServiceDiscovery.java:103)客户端引用发布成功:[/wrpc/com.wdw.wrpc.service.MultiplyInterface/consumers/192.168.1.46]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:113)正在获取服务引用[com.wdw.wrpc.service.MultiplyInterface]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:122)发现服务器列表[192.168.1.46:8000]
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.registry.ServiceDiscovery.getReference(ServiceDiscovery.java:124)引用服务获取完成[/wrpc/com.wdw.wrpc.service.MultiplyInterface/providers]:com.wdw.wrpc.service.MultiplyInterface
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.netty.ServiceResponseHandler.channelActive(ServiceResponseHandler.java:25)Client connected.
3
8
[2020-03-29 17:55:56 下午]:INFO com.wdw.wrpc.client.netty.ServiceResponseHandler.channelActive(ServiceResponseHandler.java:25)Client connected.
19.25
当客户端执行调用时,客户端连接服务器,得到调用结果。
完整代码见:https://github.com/wdw87/wRpc
作者:好吃懒做贪玩东
编辑:西瓜媛