文章目录
RPC 简介
RPC 的魅力
RPC 全称 Remote Procedure Call, 即远程过程调用,可以实现调用远程接口像本地方法一样方便,但却屏蔽远程调用的复杂性。
RPC 的流程结构
- 序列化:RPC是一个远程调用,所以必须要通过网络来传输数据。调用方的请求出入参数被封装成对象,就需要序列化对象为可传输的二进制数据。
- 传输数据:调用方将二进制数据传输给服务提供方,此处一般会通过 TCP 协议进行安全可靠传输。
- 反序列化:服务提供方也会根据特定的协议,将传输过来的二进制数据进行正确的切分不同请求,同时根据请求类型和序列化类型,把二进制数据的消息体逆向反序列化为真实的请求对象。
- 方法调用:然后根据反序列化得到的请求对象,找到对应的类,进行具体方法的调用。
- 返回请求:方法调用完需要将结果进行序列化后,回写到对应的 TCP 请求通道内。而调用方够获取到应答的数据包,也需要进行反序列化对象数据,这样就完成了一次完整的RPC的调用。
RPC 的兴起
随着目前微服务、分布式应用开发越来越常见,急需一种远程调用的协议来监控各个服务节点之间的调用,以及监控各个服务之间的通信情况。
比如我们经常说的服务治理功能:连接管理、健康检测、负载均衡、优雅启停机、异常重试、业务分组以及熔断限流等等。
而相应的针对微服务的兴起,一些常用的框架也逐渐进入了我们的眼帘,这些框架如果我们仔细分析源码的话,内部逻辑都有 RPC 协议的影子。
常用 RPC 协议框架
- Dubbo:Dubbo 是一个分布式服务框架,以及 SOA 治理方案。其功能主要包括:高性能 NIO 铜须及多协议集成,服务的动态寻址与路由,软负载均衡与容错,依赖分析与降级等。2011 年阿里将 Dubbo 框架开源了。
- Spring Cloud:Spring Cloud 由众多子项目组成,如Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul等。提供了搭建分布式系统及微服务常用的所有工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态监控等,满足了构建微服务的所有解决方案。
- gRPC: 由 Google 开发的一款语言中立、平台中立、开源的远程调用系统。
- Thrift:最初由 Facebook 开发的内部系统跨语言的 RPC 框架,2007 年开源,支持多种语言。
RPC 技术细节
通过分析 Dubbo 框架底层源码实现,也可以看出一个简单的 RPC 框架需要能够包含服务的暴露、服务的引用、服务的调用几个环节。为了能够直接了解具体的交互原理,可以通过一下流程图进行简单的了解一下。
手写 RPC 框架
为了更加深入的了解这个框架是否我们可以自己实现一个简易的 RPC 框架呢?答案是肯定的。以下就是鄙人通过研究源码以及看某些大神的讲解,写了一个简单的 demo 代码看下效果。
1. 目标
编写一个SpringBoot启动器类,定义名为"myrpc-spring-boot-starter", 为项目提供发现远程服务的功能。让调用远程服务如调用本地方法一样使用。首先会定义两个注解:
- @RpcService:被此注解标记的类将提供远程服务的功能;
- @ReferenceService:被此注解标记的成员变量,具有远程服务引用的功能;
2. 项目的整体结构
3. 客户端实现
3.1 客户端实现功能
客户端想要远程服务,必须具有服务发现的能力;发现服务后,还必须由服务代理来执行服务调用的能力。客户端想与服务端实现通信,必须具有相同的消息协议;客户端想要调用远程服务,那么必须具备网络请求的能力,即网络层功能。
以下是 pom 文件的相关依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.90.Final</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.6</version>
</dependency>
</dependencies>
3.2 客户端服务发现
由上述引入的 Jar 包也可了解,此处的服务的发现与暴露,是通过 Zookeeper 创建节点的方法来实现的,代码如下:
/**
* 服务发现接口,定义服务发现的规范
*/
public interface ServiceDiscoverer {
List<ServiceInfo> getServices(String name);
}
/**
* Zookeeper 服务发现者
*/
public class ZookeeperServiceDiscoverer implements ServiceDiscoverer {
private ZkClient zkClient;
public ZookeeperServiceDiscoverer(String zkAddress) {
this.zkClient = new ZkClient(zkAddress);
zkClient.setZkSerializer(new ZookeeperSerializer());
}
/**
* 使用Zookeeper客户端,根据服务名获取服务列表
*
* @param name 服务名
* @return 服务列表
*/
@Override
public List<ServiceInfo> getServices(String name) {
String servicePath = RpcConstant.ZK_SERVICE_PATH + RpcConstant.PATH_DELIMITER + name + RpcConstant.PATH_SUFFIX;
List<String> children = zkClient.getChildren(servicePath);
return Optional.ofNullable(children).orElse(new ArrayList<>()).stream().map(str -> {
String deCh = null;
try {
deCh = URLDecoder.decode(str, RpcConstant.UTF_8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return JSON.parseObject(deCh, ServiceInfo.class);
}).collect(Collectors.toList());
}
}
/**
* <p>Title:自定义RPC框架</p>
* Description: Zookeeper序列化器</br>
*/
public class ZookeeperSerializer implements ZkSerializer {
@Override
public byte[] serialize(Object data) throws ZkMarshallingError {
return String.valueOf(data).getBytes(StandardCharsets.UTF_8);
}
@Override
public Object deserialize(byte[] bytes) throws ZkMarshallingError {
return new String(bytes, StandardCharsets.UTF_8);
}
}
/**
* <p>Title:自定义RPC框架</p>
* Description: 服务信息,记录注册服务的一些信息</br>
*/
public class ServiceInfo {
/**
* 服务名称
*/
private String name;
/**
* 注册使用的协议
*/
private String protocol;
/**
* 注册的服务地址,IP:Port
*/
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
public class RpcConstant {
/**
* Zookeeper服务注册地址根路径
*/
public static String ZK_SERVICE_PATH = "/my-rpc";
/**
* 路径的分隔符
*/
public static String PATH_DELIMITER = "/";
/**
* 编码
*/
public static String UTF_8 = "UTF-8";
/**
* 路径的后缀
*/
public static String PATH_SUFFIX = "/service";
}
3.3 网络请求客户端
此处通过 Netty 框架实现网络客户端,实现请求数据的发送及响应数据的获取。
/**
* <p>Title:自定义RPC框架</p>
* Description: 网络请求客户端, 定义网络请求规范</br>
*/
public interface NetClient {
byte[] sendRequest(byte[] data, ServiceInfo serviceInfo) throws InterruptedException;
}
/**
* <p>Title:自定义RPC框架</p>
* Description: 网络请求客户端实现</br>
*/
public class NettyNetClient implements NetClient {
private static Logger logger = LoggerFactory.getLogger(NettyNetClient.class);
/**
* 发送请求数据
*
* @param data 请求数据
* @param serviceInfo 服务信息
* @return 响应数据
* @throws InterruptedException
*/
@Override
public byte[] sendRequest(byte[] data, ServiceInfo serviceInfo) throws InterruptedException {
String[] addressArray = serviceInfo.getAddress().split(":");
String address = addressArray[0];
String port = addressArray[1];
SendHandler sendHandler = new SendHandler(data);
byte[] respData;
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
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(sendHandler);
}
});
b.connect(address, Integer.parseInt(port)).sync();
respData = (byte[]) sendHandler.rspData();
logger.info("SendRequest get reply: {}", respData);
} finally {
// 释放线程资源
group.shutdownGracefully();
}
return respData;
}
}
/**
* <p>Title:自定义RPC框架</p>
* Description: 发送处理类,定义Netty入站处理规则</br>
*/
public class SendHandler extends ChannelInboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(SendHandler.class);
private CountDownLatch cdl;
private Object readMsg = null;
private byte[] data;
public SendHandler(byte[] data) {
cdl = new CountDownLatch(1);
this.data = data;
}
/**
* 发送请求数据
*
* @param ctx 通道上下文
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("Successful connection to server: {}", ctx);
ByteBuf reqBuf = Unpooled.buffer(data.length);
reqBuf.writeBytes(data);
logger.info("Client sends message: {}", reqBuf);
ctx.writeAndFlush(reqBuf);
}
/**
* 读取数据,数据读取完毕释放CD锁
*
* @param ctx 通道上下文
* @param msg 接收的数据
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.info("Client reads message: {}", msg);
ByteBuf msgBuf = (ByteBuf) msg;
byte[] resp = new byte[msgBuf.readableBytes()];
msgBuf.readBytes(resp);
readMsg = resp;
cdl.countDown();
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
logger.error("Exception occurred: {}", cause.getMessage());
ctx.close();
}
/**
* 等待读取数据完成
*
* @return 响应数据
* @throws InterruptedException
*/
public Object rspData() throws InterruptedException {
cdl.await();
return readMsg;
}
}
3.4 服务代理
服务代理类客户端代理工厂类产生,代理方式通过Java的动态代理实现。在代理类ClientInvocationHandler的invoke方法中,主要实现了服务的获取、选择服务提供者、构造请求对象、请求对象的序列化、网络请求客户端的发送请求、响应消息的反序列化、异常处理等。
/**
* <p>Title:自定义RPC框架</p>
* Description: 客户端代理工厂,用于创建远程服务代理类
* 封装编组请求、请求发送、编组响应等操作</br>
*/
public class ClientProxyFactory {
private ServiceDiscoverer serviceDiscoverer;
private Map<String, MessageProtocol> supportMessageProtocols;
private NetClient netClient;
private Map<Class<?>, Object> objectCache = new HashMap<>();
/**
* 通过Java动态代理获取代理类
*
* @param clazz 被代理类class
* @param <T> 泛型
* @return
*/
public <T> T getProxy(Class<T> clazz) {
return (T) this.objectCache.computeIfAbsent(clazz,
cls -> Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls}, new ClientInvocationHandler(cls)));
}
public void setSid(ServiceDiscoverer serviceDiscoverer) {
this.serviceDiscoverer = serviceDiscoverer;
}
public NetClient getNetClient() {
return netClient;
}
public void setNetClient(NetClient netClient) {
this.netClient = netClient;
}
public Map<String, MessageProtocol> getSupportMessageProtocols() {
return supportMessageProtocols;
}
public void setSupportMessageProtocols(Map<String, MessageProtocol> supportMessageProtocols) {
this.supportMessageProtocols = supportMessageProtocols;
}
/**
* 客户端服务器代理类
*/
private class ClientInvocationHandler implements InvocationHandler {
private Class<?> clazz;
private Random random = new Random();
public ClientInvocationHandler(Class<?> clazz) {
super();
this.clazz = clazz;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("toString")) {
return proxy.getClass().toString();
}
if (method.getName().equals("hashCode")) {
return 0;
}
// 1.获取服务信息
String serviceName = this.clazz.getName();
List<ServiceInfo> services = serviceDiscoverer.getServices(serviceName);
if (services == null || services.isEmpty()) {
throw new RpcException("No provider available!");
}
// 随机选取一个服务提供者(软负载均衡)
ServiceInfo serviceInfo = services.get(random.nextInt(services.size()));
// 2.构造request对象
RpcRequest req = new RpcRequest();
req.setServiceName(serviceInfo.getName());
req.setMethod(method.getName());
req.setParameterTypes(method.getParameterTypes());
req.setParameters(args);
// 3.协议层编码
MessageProtocol protocol = supportMessageProtocols.get(serviceInfo.getProtocol());
byte[] data = protocol.marshallingRequest(req);
// 4.调用网络层发送请求
byte[] repData = netClient.sendRequest(data, serviceInfo);
// 5.响应消息的解码
RpcResponse rpcResponse = protocol.unmarshallingResponse(repData);
// 6.结果处理
if (rpcResponse.getException() != null) {
return rpcResponse.getException();
}
return rpcResponse.getReturnValue();
}
}
}
3.5 消息协议的定义
消息协议主要定义了请求消息序列化与反序列化,以及响应消息的序列化与反序列化。
/**
* <p>Title:自定义RPC框架</p>
* Description: 消息协议,定义请求及响应的编解码</br>
*/
public interface MessageProtocol {
/**
* 请求编码
*
* @param request 请求对象
* @return 请求字节数组
* @throws Exception
*/
byte[] marshallingRequest(RpcRequest request) throws Exception;
/**
* 请求解码
*
* @param data 请求字节数组
* @return 请求对象
* @throws Exception
*/
RpcRequest unmarshallingRequest(byte[] data) throws Exception;
/**
* 响应编码
*
* @param response 响应对象
* @return 响应字节数组
* @throws Exception
*/
byte[] marshallingResponse(RpcResponse response) throws Exception;
/**
* 响应解码
*
* @param data 响应的字节数组
* @return 响应对象
* @throws Exception
*/
RpcResponse unmarshallingResponse(byte[] data) throws Exception;
}
/**
* <p>Title:自定义RPC框架</p>
* Description: Java序列化消息协议</br>
*/
public class JavaSerializerMessageProtocol implements MessageProtocol {
private byte[] serialize(Object obj) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject(obj);
return bout.toByteArray();
}
@Override
public byte[] marshallingRequest(RpcRequest request) throws Exception {
return this.serialize(request);
}
@Override
public RpcRequest unmarshallingRequest(byte[] data) throws Exception {
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));
return (RpcRequest) in.readObject();
}
@Override
public byte[] marshallingResponse(RpcResponse response) throws Exception {
return this.serialize(response);
}
@Override
public RpcResponse unmarshallingResponse(byte[] data) throws Exception {
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));
return (RpcResponse) in.readObject();
}
}
/**
* <p>Title:自定义RPC框架</p>
* Description: 请求消息的封装类</br>
*/
public class RpcRequest implements Serializable {
private static final long serialVersionUID = 7865935599988450842L;
private String serviceName;
private String method;
private Map<String, String> headers = new HashMap<>();
private Class<?>[] parameterTypes;
private Object[] parameters;
// 省略 getter 和 setter 方法
}
/**
* <p>Title:自定义RPC框架</p>
* Description: 响应消息的封装</br>
*/
public class RpcResponse implements Serializable {
private static final long serialVersionUID = -3631682513172006486L;
private RpcStatus status;
private Map<String, String> headers = new HashMap<>();
private Object returnValue;
private Exception exception;
public RpcResponse(RpcStatus status) {
this.status = status;
}
// 省略部分 getter 和 setter 方法
public String getHeader(String name) {
return this.headers == null ? null : this.headers.get(name);
}
}
/**
* <p>Title:自定义RPC框架</p>
* Description: 响应状态码</br>
*/
public enum RpcStatus {
SUCCESS(200, "SUCCESS"),
ERROR(500, "ERROR"),
NOT_FOUND(404, "NOT FOUND");
private int code;
private String message;
RpcStatus(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
4. 服务端的实现
4.1 实现功能
首先,服务端要提供远程服务,必须具备服务注册及暴露的能力;在这之后,需要开启网络服务,供客户端连接。这里引用一个RPC处理者的概念,由它来帮助我们开启服务,以及注入服务。
4.2 注解实现
/**
* RPC 框架暴露服务注解
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RpcService {
String value() default "";
}
/**
* RPC 引用服务注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReferenceService {
}
4.3 服务注册(暴露)
通过Zookeeper将要暴露的服务保存一个节点信息,供客户端进行发现使用。
/**
* <p>Title:自定义RPC框架</p>
* Description: 服务注册器,定义服务注册规范</br>
*/
public interface ServiceRegister {
void register(ServiceObject so) throws Exception;
ServiceObject getServiceObject(String name) throws Exception;
}
/**
* <p>Title:自定义RPC框架</p>
* Description: 默认注册器</br>
*/
public class DefaultServiceRegister implements ServiceRegister{
private Map<String, ServiceObject> serviceMap = new HashMap<>();
protected String protocol;
protected Integer port;
@Override
public void register(ServiceObject so) throws Exception {
if (so == null){
throw new IllegalArgumentException("Parameter cannot be empty.");
}
this.serviceMap.put(so.getName(), so);
}
@Override
public ServiceObject getServiceObject(String name) throws Exception {
return this.serviceMap.get(name);
}
}
/**
* <p>Title:自定义RPC框架</p>
* Description: Zookeeper服务注册器</br>
*/
public class ZookeeperExportServiceRegister extends DefaultServiceRegister implements ServiceRegister {
private ZkClient client;
public ZookeeperExportServiceRegister(String zkAddress, String protocol, Integer port) {
this.client = new ZkClient(zkAddress);
this.client.setZkSerializer(new ZookeeperSerializer());
this.protocol = protocol;
this.port = port;
}
@Override
public void register(ServiceObject so) throws Exception {
super.register(so);
ServiceInfo serviceInfo = new ServiceInfo();
String host = InetAddress.getLocalHost().getHostAddress();
String address = host + ":" + port;
serviceInfo.setAddress(address);
serviceInfo.setName(so.getClazz().getName());
serviceInfo.setProtocol(protocol);
this.exportService(serviceInfo);
}
private void exportService(ServiceInfo serviceInfo) {
String serviceName = serviceInfo.getName();
String uri = JSON.toJSONString(serviceInfo);
try {
uri = URLEncoder.encode(uri, RpcConstant.UTF_8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String servicePath = RpcConstant.ZK_SERVICE_PATH + RpcConstant.PATH_DELIMITER + serviceName + RpcConstant.PATH_SUFFIX;
if (!client.exists(servicePath)) {
client.createPersistent(servicePath, true);
}
String uriPath = servicePath + RpcConstant.PATH_DELIMITER + uri;
if (client.exists(uriPath)) {
client.delete(uriPath);
}
client.createEphemeral(uriPath);
}
}
4.4 网络服务端
依然是通过Netty实现网络服务端,并暴露服务端口供客户端进行连接。
/**
* <p>Title:自定义RPC框架</p>
* Description: RPC 服务抽象类</br>
*/
public abstract class RpcServer {
/**
* 服务端口
*/
protected int port;
/**
* 服务协议
*/
protected String protocol;
/**
* 请求处理者
*/
protected RequestHandler requestHandler;
public RpcServer(int port, String protocol, RequestHandler requestHandler) {
this.port = port;
this.protocol = protocol;
this.requestHandler = requestHandler;
}
/**
* 开启服务
*/
public abstract void start();
/**
* 停止服务
*/
public abstract void stop();
}
/**
* <p>Title:自定义RPC框架</p>
* Description: Netty RPC服务端,提供Netty网络服务开启、关闭的功能</br>
*/
public class NettyRpcServer extends RpcServer {
private Logger logger = LoggerFactory.getLogger(NettyRpcServer.class);
private Channel channel;
public NettyRpcServer(int port, String protocol, RequestHandler requestHandler) {
super(port, protocol, requestHandler);
}
@Override
public void start() {
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
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ChannelRequestHandler());
}
});
ChannelFuture f = b.bind(port).sync();
logger.info("Server started successfully.");
channel = f.channel();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
@Override
public void stop() {
this.channel.close();
}
private class ChannelRequestHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("Channel active: {}", ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.info("The server receives a message: {}", msg);
ByteBuf msgBuf = (ByteBuf) msg;
byte[] req = new byte[msgBuf.readableBytes()];
msgBuf.readBytes(req);
byte[] res = requestHandler.handleRequest(req);
logger.info("Send response: {}", msg);
ByteBuf rspBuf = Unpooled.buffer(res.length);
rspBuf.writeBytes(res);
ctx.write(rspBuf);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
logger.error("Exception occurred: {}", cause.getMessage());
ctx.close();
}
}
}
/**
* <p>Title:自定义RPC框架</p>
* Description: 请求处理器</br>
*/
public class RequestHandler {
private MessageProtocol protocol;
private ServiceRegister register;
public RequestHandler(MessageProtocol protocol, ServiceRegister register) {
super();
this.protocol = protocol;
this.register = register;
}
public byte[] handleRequest(byte[] data) throws Exception {
// 1.请求消息解码
RpcRequest req = this.protocol.unmarshallingRequest(data);
// 2.查找服务对象
ServiceObject so = this.register.getServiceObject(req.getServiceName());
RpcResponse rsp = null;
if (so == null) {
rsp = new RpcResponse(RpcStatus.NOT_FOUND);
} else {
// 3.反射调用对应的过程方法
try {
Method m = so.getClazz().getMethod(req.getMethod(), req.getParameterTypes());
Object returnValue = m.invoke(so.getObj(), req.getParameters());
rsp = new RpcResponse(RpcStatus.SUCCESS);
rsp.setReturnValue(returnValue);
} catch (Exception e) {
rsp = new RpcResponse(RpcStatus.ERROR);
rsp.setException(e);
}
}
// 4.对响应消息编码
return this.protocol.marshallingResponse(rsp);
}
public MessageProtocol getProtocol() {
return protocol;
}
public void setProtocol(MessageProtocol protocol) {
this.protocol = protocol;
}
public ServiceRegister getRegister() {
return register;
}
public void setRegister(ServiceRegister register) {
this.register = register;
}
}
4.5 RPC 处理器
通过Spring框架实现ApplicationListener,监听ContextRefreshEvent事件,实现Spring启动完毕后收到事件通知,然后注册服务及注入服务。
/**
* <p>Title:自定义RPC框架</p>
* Description: RPC处理器,支持服务启动暴露、自动注入Service</br>
*/
public class DefaultRpcProcessor implements ApplicationListener<ContextRefreshedEvent> {
@Resource
private ClientProxyFactory clientProxyFactory;
@Resource
private ServiceRegister serviceRegister;
@Resource
private RpcServer rpcServer;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (Objects.isNull(event.getApplicationContext().getParent())) {
ApplicationContext context = event.getApplicationContext();
// 开启并暴露服务
startServer(context);
// 注入服务
injectService(context);
}
}
private void startServer(ApplicationContext context) {
Map<String, Object> beans = context.getBeansWithAnnotation(RpcService.class);
if (beans.size() != 0) {
boolean startServerFlag = true;
for (Object obj : beans.values()) {
try {
Class<?> aClass = obj.getClass();
Class<?>[] interfaces = aClass.getInterfaces();
ServiceObject so;
if (interfaces.length != 1) {
RpcService rpcService = aClass.getAnnotation(RpcService.class);
String value = rpcService.value();
if (StringUtils.isBlank(value)) {
startServerFlag = false;
throw new UnsupportedOperationException("The exposed interface is not specific with '" + obj.getClass().getName() + "'");
}
so = new ServiceObject(value, Class.forName(value), obj);
} else {
Class<?> superClass = interfaces[0];
so = new ServiceObject(superClass.getName(), superClass, obj);
}
serviceRegister.register(so);
} catch (Exception e) {
e.printStackTrace();
}
}
if (startServerFlag) {
rpcServer.start();
}
}
}
private void injectService(ApplicationContext context) {
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
Class<?> clazz = context.getType(name);
if (!Objects.isNull(clazz)) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
ReferenceService ref = field.getAnnotation(ReferenceService.class);
if (Objects.isNull(ref)) continue;
Class<?> fieldClass = field.getType();
Object object = context.getBean(name);
field.setAccessible(true);
try {
field.set(object, clientProxyFactory.getProxy(fieldClass));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
5. Spring Boot启动器配置
5.1 配置属性类
/**
* <p>Title:自定义RPC框架</p>
* Description: 参数配置类,实现用户自定义参数</br>
*/
@EnableConfigurationProperties(MyRpcProperty.class)
@ConfigurationProperties("my.rpc")
public class MyRpcProperty {
/**
* 服务注册中心
*/
private String registerAddress = "127.0.0.1:2181";
/**
* 服务端暴露端口
*/
private Integer serverPort = 21000;
/**
* 服务协议
*/
private String protocol = "myrpc";
// 省略 getter 和 setter 方法
}
5.2 自动配置类
/**
* <p>Title:自定义RPC框架</p>
* Description: Spirng boot 自动配置</br>
*/
@Configuration
public class MyRpcEnableAutoConfiguration {
@Bean
public MyRpcProperty myRpcProperty() {
return new MyRpcProperty();
}
@Bean
public DefaultRpcProcessor defaultRpcProcessor() {
return new DefaultRpcProcessor();
}
@Bean
public ClientProxyFactory clientProxyFactory(@Autowired MyRpcProperty myRpcProperty) {
ClientProxyFactory clientProxyFactory = new ClientProxyFactory();
// 设置服务发现者
clientProxyFactory.setSid(new ZookeeperServiceDiscoverer(myRpcProperty.getRegisterAddress()));
// 设置支持的协议
Map<String, MessageProtocol> supportMessageProtocols = new HashMap<>();
supportMessageProtocols.put(myRpcProperty.getProtocol(), new JavaSerializerMessageProtocol());
clientProxyFactory.setSupportMessageProtocols(supportMessageProtocols);
// 设置网络层实现
clientProxyFactory.setNetClient(new NettyNetClient());
return clientProxyFactory;
}
@Bean
public ServiceRegister serviceRegister(@Autowired MyRpcProperty myRpcProperty) {
return new ZookeeperExportServiceRegister(
myRpcProperty.getRegisterAddress(),
myRpcProperty.getProtocol(),
myRpcProperty.getServerPort()
);
}
@Bean
public RequestHandler requestHandler(@Autowired ServiceRegister serviceRegister){
return new RequestHandler(new JavaSerializerMessageProtocol(), serviceRegister);
}
@Bean
public RpcServer rpcServer(@Autowired RequestHandler requestHandler,
@Autowired MyRpcProperty myRpcProperty){
return new NettyRpcServer(myRpcProperty.getServerPort(), myRpcProperty.getProtocol(), requestHandler);
}
}
RPC 框架的使用
1. 引入依赖
<dependency>
<groupId>com.xquant.rpc</groupId>
<artifactId>myrpc-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
2. 远程服务暴露
import com.xquant.rpc.demo.annotation.RpcService;
@RpcService
public class UserServiceImpl implements UserService {
@Override
public User getUser(Long id) {
return new User(10001L, "ZhangSan", 29, "SH");
}
}
3. 服务注入调用
import com.xquant.rpc.demo.annotation.ReferenceService;
@RestController
@RequestMapping("user")
public class UserController {
@ReferenceService
private UserService userService;
@GetMapping("/getUser")
public User getUser(Long id) {
return userService.getUser(id);
}
}
小结
综上所述,RPC 框架是一个很复杂的处理流程。包括了服务的暴露、服务的发现、网络服务的处理、对象的序列化与发序列化、Java代理与反射等多方面知识的总结。其中Spring Boot 的 Starter 插件的自动装配实现也很重要,它也大大简化了自制框架的快速实现。