原视频参考地址:[全网最全的手写RPC教程] 手摸手教你写一个RPC-架构-设计-落地实现_哔哩哔哩_bilibili
项目地址:kkoneone11/kkoneoneRPC-master (github.com)
觉得对你有帮助的帮忙文章给个like和项目给个stars呀!!!
因为我的设计是从顶到底去写代码,因此这一部分的代码量会比往后的多,大家可以分几天去编写
代理层
代理设计模式
代理层用于封装多余的操作,例如:制定协议,编码,从注册中心要服务,负载均衡,调用,容错机制。所有的流程都在代理层中封装好了。调用方只需要进行调用方法即可。(MyBatis在mapper层也是这样)
代码部分
RPC协议
首先需要一个自定义的RPC协议
java
package org.kkoneone.rpc.protocol;
import lombok.Data;
import java.io.Serializable;
/**
* 自定义RPC协议,使用泛型来自定义传入的RPC请求
* @Author:kkoneone11
* @name:RpcProtocol
* @Date:2023/11/26 9:13
*/
@Data
public class RpcProtocol<T> implements Serializable {
//协议头
private MsgHeader header;
//协议体
private T body;
}
定制协议的消息头
java
package org.kkoneone.rpc.protocol;
import lombok.Data;
import java.io.Serializable;
/**
* 消息头
* @Author:kkoneone11
* @name:MsgHeader
* @Date:2023/11/26 9:28
*/
@Data
public class MsgHeader implements Serializable {
private short magic; // 魔数
private byte version; // 协议版本号
private byte msgType; // 数据类型
private byte status; // 状态
private long requestId; // 请求 ID
private int serializationLen;
private byte[] serializations;
private int msgLen; // 数据长度
}
定义RpcRequest 对象。用作协议返回填充相关信息,然后将其序列化并发送到服务器。服务器接收到请求后,反序列化得到 RpcRequest 对象,然后根据其中的信息找到相应的方法并执行,最后将结果返回给客户端
typescript
package org.kkoneone.rpc.common;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* RPC请求类
* @Author:kkoneone11
* @name:RpcRequest
* @Date:2023/11/26 9:39
*/
@Data
public class RpcRequest implements Serializable {
//服务版本号,用于处理同一个接口有多个实现类的情况
private String serviceVersion;
//要调用的远程服务的类名
private String className;
//要调用的远程服务的方法名
private String methodName;
//调用远程方法时传递的参数
private Object[] params;
//这些参数的类型,这是必要的,因为要通过反射来调用方法,需要知道参数的确切类型
private Class<?>[] parameterTypes;
//请求数据
private Object data;
//请求数据的类
private Class dataClass;
//服务端额外配置数据
private Map<String,Object> serviceAttachments;
//客户端额外配置数据
private Map<String,Object> clientAttachments;
}
定义ProtocolConstants网络协议常量。
java
package org.kkoneone.rpc.common.constants;
/**
* 网络协议常量
* @Author:kkoneone11
* @name:ProtocolConstants
* @Date:2023/11/26 12:32
*/
public class ProtocolConstants {
public static final int HEADER_TOTAL_LEN = 18;
public static final short MAGIC = 0x10;
public static final byte VERSION = 0x1;
}
定义RpcProperties。用作RPC的一些配置信息
typescript
package org.kkoneone.rpc.config;
import lombok.Data;
import org.kkoneone.rpc.annotation.PropertiesField;
import org.kkoneone.rpc.annotation.PropertiesPrefix;
import org.kkoneone.rpc.common.constants.RegistryRules;
import org.kkoneone.rpc.common.constants.SerializationRules;
import org.springframework.core.env.Environment;
import java.util.HashMap;
import java.util.Map;
/**
* 配置信息
* @Author:kkoneone11
* @name:RpcProperties
* @Date:2023/11/27 10:56
*/
@PropertiesPrefix("rpc")
@Data
public class RpcProperties {
/**
* netty 端口 两个服务就是两个进程,进程间通信就需要端口
*/
@PropertiesField
private Integer port;
/**
* 注册中心地址
*/
@PropertiesField
private String registerAddr;
/**
* 注册中心类型
*/
@PropertiesField
private String registerType = RegistryRules.ZOOKEEPER;
/**
* 注册中心密码
*/
@PropertiesField
private String registerPsw;
/**
* 序列化
*/
@PropertiesField
private String serialization = SerializationRules.JSON;
/**
* 服务端额外配置数据
*/
@PropertiesField("service")
private Map<String,Object> serviceAttachments = new HashMap<>();
/**
* 客户端额外配置数据
*/
@PropertiesField("client")
private Map<String,Object> clientAttachments = new HashMap<>();
static RpcProperties rpcProperties;
public static RpcProperties getInstance(){
if (rpcProperties == null){
rpcProperties = new RpcProperties();
}
return rpcProperties;
}
private RpcProperties(){}
public void setRegisterType(String registerType) {
if(registerType == null || registerType.equals("")){
registerType = RegistryRules.ZOOKEEPER;
}
this.registerType = registerType;
}
public void setSerialization(String serialization) {
if(serialization == null || serialization.equals("")){
serialization = SerializationRules.JSON;
}
this.serialization = serialization;
}
/**
* 做一个能够解析任意对象属性的工具类
* @param environment
*/
public static void init(Environment environment){
}
}
获取服务节点工具类ServiceMetaRes
java
package org.kkoneone.rpc.router;
import org.kkoneone.rpc.common.ServiceMeta;
import java.util.ArrayList;
import java.util.Collection;
/**
* 服务节点
* @Author:kkoneone11
* @name:ServiceMetaRes
* @Date:2023/12/1 19:43
*/
public class ServiceMetaRes {
// 当前服务节点
private ServiceMeta curServiceMeta;
// 剩余服务节点 方便进行容错服务选择
private Collection<ServiceMeta> otherServiceMeta;
public Collection<ServiceMeta> getOtherServiceMeta() {
return otherServiceMeta;
}
public ServiceMeta getCurServiceMeta() {
return curServiceMeta;
}
public static ServiceMetaRes build(ServiceMeta curServiceMeta, Collection<ServiceMeta> otherServiceMeta){
final ServiceMetaRes serviceMetaRes = new ServiceMetaRes();
serviceMetaRes.curServiceMeta = curServiceMeta;
// 如果只有一个服务
if(otherServiceMeta.size() == 1){
otherServiceMeta = new ArrayList<>();
}else{
otherServiceMeta.remove(curServiceMeta);
}
serviceMetaRes.otherServiceMeta = otherServiceMeta;
return serviceMetaRes;
}
}
ServiceMeta服务元数据
java
package org.kkoneone.rpc.common;
import lombok.Data;
import java.io.Serializable;
import java.util.Objects;
/**
* 服务元数据 服务节点
* @Author:kkoneone11
* @name:ServiceMeta
* @Date:2023/11/27 14:17
*/
@Data
public class ServiceMeta implements Serializable {
//服务名
private String serviceName;
//服务版本
private String serviceVersion;
//服务地址
private String serviceAddr;
//服务端口
private int servicePort;
/**
* 关于redis注册中心的属性
*/
private long endTime;
private String UUID;
/**
* 故障转移需要移除不可用服务
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServiceMeta that = (ServiceMeta) o;
return servicePort == that.servicePort &&
Objects.equals(serviceName, that.serviceName) &&
Objects.equals(serviceVersion, that.serviceVersion) &&
Objects.equals(serviceAddr, that.serviceAddr) &&
Objects.equals(UUID, that.UUID);
}
@Override
public int hashCode() {
return Objects.hash(serviceName, serviceVersion, serviceAddr, servicePort, UUID);
}
}
@PropertiesPrefix 属性前缀注解
其中
- @Retention(RetentionPolicy.RUNTIME): 这指定注解应在运行时保留。这意味着可以在运行时使用反射来检查它。
- @Target(ElementType.TYPE): 这指定该注解可以应用于类型(类,接口,枚举)。
- String value(); 这在注解中声明了一个方法。当你使用注解时,你可以向它传递一个值,如@PropertiesPrefix("some.prefix")。然后可以在运行时访问此值
java
package org.kkoneone.rpc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 前缀
* @Author:kkoneone11
* @name:PropertiesPrefix
* @Date:2023/11/30 13:18
*/
@Retention(RetentionPolicy.RUNTIME) //指定注解应在运行时保留。这意味着可以在运行时使用反射来检查它。
@Target(ElementType.TYPE) //指定该注解可以应用于类型(类,接口,枚举)
public @interface PropertiesPrefix {
String value();
}
@PropertiesField 属性后缀注解
java
package org.kkoneone.rpc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 后缀
* @Author:kkoneone11
* @name:PropertiesField
* @Date:2023/11/30 13:32
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface PropertiesField {
/**
* 默认为属性名 例如: registryType = registry-type 遵守配置文件规则
* @return
*/
String value() default "";
}
RegistryRules
java
package org.kkoneone.rpc.common.constants;
/**
* @Author:kkoneone11
* @name:RegistryRules
* @Date:2023/11/30 14:49
*/
public interface RegistryRules {
String REDIS = "redis";
String ZOOKEEPER = "zookeeper";
}
拦截上下文的数据类FilterData
typescript
package org.kkoneone.rpc.Filter;
import lombok.Data;
import org.kkoneone.rpc.common.RpcRequest;
import org.kkoneone.rpc.common.RpcResponse;
import java.util.Map;
/**
* 上下文数据
* @Author:kkoneone11
* @name:FilterData
* @Date:2023/11/30 15:34
*/
@Data
public class FilterData {
private String serviceVersion;
private long timeout;
private long retryCount;
private String className;
private String methodName;
private Object args;
private Map<String,Object> serviceAttachments;
private Map<String,Object> clientAttachments;
private RpcResponse data; // 执行业务逻辑后的数据
public FilterData(RpcRequest request) {
this.args = request.getData();
this.className = request.getClassName();
this.methodName = request.getMethodName();
this.serviceVersion = request.getServiceVersion();
this.serviceAttachments = request.getServiceAttachments();
this.clientAttachments = request.getClientAttachments();
}
public FilterData(){
}
public RpcResponse getData() {
return data;
}
public void setData(RpcResponse data) {
this.data = data;
}
@Override
public String toString() {
return "调用: Class: " + className + " Method: " + methodName + " args: " + args +" Version: " + serviceVersion
+" Timeout: " + timeout +" ServiceAttachments: " + serviceAttachments +
" ClientAttachments: " + clientAttachments;
}
}
拦截器配置类FilterConfig
java
package org.kkoneone.rpc.Filter;
import lombok.SneakyThrows;
import org.kkoneone.rpc.spi.ExtensionLoader;
import java.io.IOException;
/**
* 拦截器配置类,用于统一管理拦截器
* @Author:kkoneone11
* @name:FilterConfig
* @Date:2023/11/30 17:20
*/
public class FilterConfig {
private static FilterChain serviceBeforeFilterChain = new FilterChain();
private static FilterChain serviceAfterFilterChain = new FilterChain();
private static FilterChain clientBeforeFilterChain = new FilterChain();
private static FilterChain clientAfterFilterChain = new FilterChain();
@SneakyThrows //可以让你在不使用try/catch块的情况下抛出被检查的异常。如果在此方法中有任何被检查的异常被抛出,Lombok将在编译时自动插入一个try/catch块来处理这些异常
public static void initServiceFilter(){
final ExtensionLoader extensionLoader = ExtensionLoader.getInstance();
//加载对应过滤器的实例
extensionLoader.loadExtension(ServiceAfterFilter.class);
extensionLoader.loadExtension(ServiceBeforeFilter.class);
//获取对应类的实例并添加到过滤器当中
serviceBeforeFilterChain.addFilter(extensionLoader.gets(ServiceBeforeFilter.class));
serviceAfterFilterChain.addFilter(extensionLoader.gets(ServiceAfterFilter.class));
}
public static void initClientFilter() throws IOException, ClassNotFoundException {
final ExtensionLoader extensionLoader = ExtensionLoader.getInstance();
extensionLoader.loadExtension(ClientAfterFilter.class);
extensionLoader.loadExtension(ClientBeforeFilter.class);
clientBeforeFilterChain.addFilter(extensionLoader.gets(ClientBeforeFilter.class));
clientAfterFilterChain.addFilter(extensionLoader.gets(ClientAfterFilter.class));
}
public static FilterChain getServiceBeforeFilterChain(){
return serviceBeforeFilterChain;
}
public static FilterChain getServiceAfterFilterChain(){
return serviceAfterFilterChain;
}
public static FilterChain getClientBeforeFilterChain(){
return clientBeforeFilterChain;
}
public static FilterChain getClientAfterFilterChain(){
return clientAfterFilterChain;
}
}
拦截器链FilterChain
java
package org.kkoneone.rpc.Filter;
import java.util.ArrayList;
import java.util.List;
/**
* 拦截器链
* @Author:kkoneone11
* @name:FilterChain
* @Date:2023/11/30 20:30
*/
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
public void addFilter(Filter filter){
filters.add(filter);
}
public void addFilter(List<Object> filters){
for(Object filter : filters){
addFilter((Filter) filter);
}
}
public void doFilter(FilterData data){
for (Filter filter : filters) {
filter.doFilter(data);
}
}
}
拦截器Filter
java
package org.kkoneone.rpc.Filter;
/**
* 拦截器
* @Author:kkoneone11
* @name:Filter
* @Date:2023/11/30 23:14
*/
public interface Filter {
void doFilter(FilterData filterData);
}
定义RpcResponse。用作接收任务处理后的返回结果
java
package org.kkoneone.rpc.common;
import lombok.Data;
import java.io.Serializable;
/**
* @Author:kkoneone11
* @name:RpcResponse
* @Date:2023/11/27 9:28
*/
@Data
public class RpcResponse implements Serializable {
private Object data;
private Class dataClass;
private String message;
private Exception exception;
}
RpcFuture封装一个异步RPC调用的结果和超时时间,并提供一种机制来处理异步操作的结果。
java
package org.kkoneone.rpc.common;
import io.netty.util.concurrent.Promise;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author:kkoneone11
* @name:RpcFuture
* @Date:2023/11/27 9:37
*/
@Data //自动为类的所有字段生成getter和setter方法,以及equals(), hashCode(), toString()等方法
@NoArgsConstructor //生成一个无参的构造函数
@AllArgsConstructor //生成一个全参的构造函数
public class RpcFuture<T> {
//用于处理异步操作,它代表一个尚未完成但预期会完成的操作
private Promise<T> promise;
//待RPC响应的最长时间
private long timeout;
}
定义RpcRequestHolder,主要用于管理RPC(远程过程调用)请求
java
package org.kkoneone.rpc.common;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* RPC请求连接
* @Author:kkoneone11
* @name:RpcRequestHolder
* @Date:2023/11/27 9:23
*/
public class RpcRequestHolder {
// 请求id
//用于生成唯一的请求ID。AtomicLong是一个线程安全的类,可以在多线程环境下安全地进行操作。REQUEST_ID_GEN被初始化为0,每次需要新的请求ID时,可以调用其incrementAndGet()方法,以原子方式将当前值加1,然后返回更新后的值
public final static AtomicLong REQUEST_ID_GEN = new AtomicLong(0);
// 绑定并存储请求
//ConcurrentHashMap是一个线程安全的类,可以在多线程环境下安全地进行操作。REQUEST_MAP的键是请求ID,值是对应的RpcFuture对象
public static final Map<Long, RpcFuture<RpcResponse>> REQUEST_MAP = new ConcurrentHashMap<>();
}
定义一个序列化规则的接口
java
package org.kkoneone.rpc.common.constants;
/**
* @Author:kkoneone11
* @name:SerializationRules
* @Date:2023/11/27 10:57
*/
public interface SerializationRules {
String JSON = "json";
String hessian = "hessian";
}
定义一个枚举类MsgType。存放消息的类型
typescript
package org.kkoneone.rpc.common.constants;
/**
* @Author:kkoneone11
* @name:MsgType
* @Date:2023/11/27 11:54
*/
public enum MsgType {
REQUEST,
RESPONSE,
HEARTBEAT;
public static MsgType findByType(int type) {
return MsgType.values()[type];
}
}
RpcServiceNameBuilder。获取对应的服务key
typescript
package org.kkoneone.rpc.common;
/**
* 服务名拼接
* @Author:kkoneone11
* @name:RpcServiceNameBuilder
* @Date:2023/11/27 13:54
*/
public class RpcServiceNameBuilder {
// key: 服务名 value: 服务提供方s
public static String buildServiceKey(String serviceName, String serviceVersion) {
return String.join("$", serviceName, serviceVersion);
}
}
负载均衡
ServiceMeta
java
package org.kkoneone.rpc.common;
import lombok.Data;
import java.io.Serializable;
import java.util.Objects;
/**
* 服务元数据 服务节点
* @Author:kkoneone11
* @name:ServiceMeta
* @Date:2023/11/27 14:17
*/
@Data
public class ServiceMeta implements Serializable {
//服务名
private String serviceName;
//服务版本
private String serviceVersion;
//服务地址
private String serviceAddr;
//服务端口
private int servicePort;
/**
* 关于redis注册中心的属性
*/
private long endTime;
private String UUID;
/**
* 故障转移需要移除不可用服务
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServiceMeta that = (ServiceMeta) o;
return servicePort == that.servicePort &&
Objects.equals(serviceName, that.serviceName) &&
Objects.equals(serviceVersion, that.serviceVersion) &&
Objects.equals(serviceAddr, that.serviceAddr) &&
Objects.equals(UUID, that.UUID);
}
@Override
public int hashCode() {
return Objects.hash(serviceName, serviceVersion, serviceAddr, servicePort, UUID);
}
}
LoadBalancer
java
package org.kkoneone.rpc.router;
import org.kkoneone.rpc.common.ServiceMeta;
import java.util.List;
/**
* 负载均衡,根据负载均衡获取对应的服务节点(负载均衡包装服务节点)
* @Author:kkoneone11
* @name:LoadBalancer
* @Date:2023/11/27 14:10
*/
public interface LoadBalancer<T> {
/**
* 选择负载均衡策略
* @param params 入参,可自定义拿到入参后自行处理负载策略
* @param serviceName 服务key
* @return
*/
List<ServiceMeta> select(Object[] params, String serviceName);
}
定义一个ExtensionLoader。作为SPI机制。
java
package org.kkoneone.rpc.spi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* SPI机制
* @Author:kkoneone11
* @name:ExtensionLoader
* @Date:2023/11/27 15:04
*/
public class ExtensionLoader {
private Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
private static String SYS_EXTENSION_LOADER_DIR_PREFIX = "META-INF/xrpc/";
private static String DIY_EXTENSION_LOADER_DIR_PREFIX = "META-INF/rpc/";
private static String[] prefixs = {SYS_EXTENSION_LOADER_DIR_PREFIX, DIY_EXTENSION_LOADER_DIR_PREFIX};
//存放bean定义信息 key: 接口 value:子类
private static Map<String, Class> extensionClassCache = new ConcurrentHashMap<>();
//存放bean定义信息 key:接口 value:接口子类s
private static Map<String, Map<String,Class>> extensionClassCaches = new ConcurrentHashMap<>();
// 存放实例化的bean
private static Map<String, Object> singletonsObject = new ConcurrentHashMap<>();
private static ExtensionLoader extensionLoader;
static {
extensionLoader = new ExtensionLoader();
}
public static ExtensionLoader getInstance(){
return extensionLoader;
}
private ExtensionLoader(){
}
/**
* 根据传入的策略来获取实例bean
* @param name
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public <V> V get(String name) {
if (!singletonsObject.containsKey(name)) {
try {
singletonsObject.put(name, extensionClassCache.get(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return (V) singletonsObject.get(name);
}
/**
* 获取给定类的实例列表。当你需要管理不同类的单例对象缓存时,它允许你从缓存中获取特定类的所有实例
*
* @param clazz
* @return
*/
public List<Object> gets(Class clazz){
final String name = clazz.getName();
if(!extensionClassCaches.containsKey(name)){
try{
throw new ClassNotFoundException(clazz + "未找到");
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
final Map<String, Class> stringClassMap = extensionClassCaches.get(name);
List<Object> objects = new ArrayList<>();
if(stringClassMap.size() > 0){
stringClassMap.forEach((k,v)->{
try{
objects.add(singletonsObject.getOrDefault(k,v.newInstance()));
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
}
return objects;
}
/**
* 根据spi机制初始化bean的信息放入map 从SPI配置文件中加载指定类的
* @param clazz
* @throws IOException
* @throws ClassNotFoundException
*/
public void loadExtension(Class clazz) throws IOException, ClassNotFoundException {
if (clazz == null) {
throw new IllegalArgumentException("class 没找到");
}
//1.获取当前类的ClassLoader
ClassLoader classLoader = this.getClass().getClassLoader();
HashMap<String, Class> classMap = new HashMap<>();
//2.遍历前缀列表,将每个前缀添加到类名(clazz.getName())后面,形成SPI配置文件的路径
for (String prefix : prefixs) {
String spiFilePath = prefix + clazz.getName();
//3.根据路径获得SPI配置文件的URL枚举
Enumeration<URL> enumeration = classLoader.getResources(spiFilePath);
//4.对于每个URL,打开一个BufferedReader来逐行读取文件
while (enumeration.hasMoreElements()) {
URL url = enumeration.nextElement();
InputStreamReader inputStreamReader = null;
inputStreamReader = new InputStreamReader(url.openStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
// SPI配置文件中的每一行都应该是key=value的格式
String[] lineArr = line.split("=");
String key = lineArr[0];
String name = lineArr[1];
final Class<?> aClass = Class.forName(name);
extensionClassCache.put(key, aClass);
classMap.put(key,aClass);
logger.info("加载bean key:{} , value:{}",key,name);
}
}
}
extensionClassCaches.put(clazz.getName(),classMap);
}
}
LoadBalancerFactory 加载策略工厂。实例化SPI扩展并指定策略。几乎每层都会有一个工厂,因为要用到里面的init()初始化服务来获取一个对象
java
package org.kkoneone.rpc.router;
import org.kkoneone.rpc.spi.ExtensionLoader;
/**
* @Author:kkoneone11
* @name:LoadBalancerFactory
* @Date:2023/11/27 14:06
*/
public class LoadBalancerFactory {
//先生成一个SPI机制实例然后根据提供的serviceLoadBalancer来决定对应负载均衡策略
public static LoadBalancer get(String serviceLoadBalancer) throws Exception {
return ExtensionLoader.getInstance().get(serviceLoadBalancer);
}
public static void init() throws Exception {
ExtensionLoader.getInstance().loadExtension(LoadBalancer.class);
}
}
服务调用失败规则FaultTolerantRules
java
package org.kkoneone.rpc.common.constants;
/**
* @Author:kkoneone11
* @name:FaultTolerantRules
* @Date:2023/12/1 23:45
*/
public interface FaultTolerantRules {
//故障转移
String Failover = "failover";
//快速失败
String FailFast = "failFast";
//忽视这次错误
String Failsafe = "failsafe";
}
消费方
RpcEncoder编码器
java
package org.kkoneone.rpc.protocol.codec;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.kkoneone.rpc.protocol.MsgHeader;
import org.kkoneone.rpc.protocol.RpcProtocol;
import org.kkoneone.rpc.protocol.serialization.RpcSerialization;
import org.kkoneone.rpc.protocol.serialization.SerializationFactory;
/**
* 编码器
* @Author:kkoneone11
* @name:RpcEncoder
* @Date:2023/11/28 14:35
*/
public class RpcEncoder extends MessageToByteEncoder<RpcProtocol<Object>> {
@Override
protected void encode(ChannelHandlerContext ctx, RpcProtocol<Object> msg, ByteBuf byteBuf) throws Exception {
// 获取消息头类型
MsgHeader header = msg.getHeader();
// 写入魔数(安全校验,可以参考java中的CAFEBABE)
byteBuf.writeShort(header.getMagic());
// 写入版本号
byteBuf.writeByte(header.getVersion());
// 写入消息类型(接收放根据不同的消息类型进行不同的处理方式)
byteBuf.writeByte(header.getMsgType());
// 写入状态
byteBuf.writeByte(header.getStatus());
// 写入请求id(请求id可以用于记录异步回调标识,具体需要回调给哪个请求)
byteBuf.writeLong(header.getRequestId());
// 写入序列化方式(接收方需要依靠具体哪个序列化进行序列化)
byteBuf.writeInt(header.getSerializationLen());
final byte[] ser = header.getSerializations();
byteBuf.writeBytes(ser);
final String serialization = new String(ser);
//获取序列化策略 序列化消息体
RpcSerialization rpcSerialization = SerializationFactory.get(serialization);
byte[] data = rpcSerialization.serialize(msg.getBody());
// 写入数据长度(接收方根据数据长度读取数据内容)
byteBuf.writeInt(data.length);
// 写入数据
byteBuf.writeBytes(data);
}
}
RpcDecoder解码器
ini
package org.kkoneone.rpc.protocol.codec;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.kkoneone.rpc.common.RpcRequest;
import org.kkoneone.rpc.common.RpcResponse;
import org.kkoneone.rpc.common.constants.MsgType;
import org.kkoneone.rpc.common.constants.ProtocolConstants;
import org.kkoneone.rpc.protocol.MsgHeader;
import org.kkoneone.rpc.protocol.RpcProtocol;
import org.kkoneone.rpc.protocol.serialization.RpcSerialization;
import org.kkoneone.rpc.protocol.serialization.SerializationFactory;
import java.util.List;
/**
* 解码器
* @Author:kkoneone11
* @name:RpcDecoder
* @Date:2023/11/28 19:01
*/
public class RpcDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
// 如果可读字节数少于协议头长度,说明还没有接收完整个协议头,直接返回
if(in.readableBytes() < ProtocolConstants.HEADER_TOTAL_LEN){
return;
}
// 标记当前读取位置,便于后面回退
in.markReaderIndex();
// 1.读取魔数字段
short magic = in.readShort();
if (magic != ProtocolConstants.MAGIC) {
throw new IllegalArgumentException("magic number is illegal, " + magic);
}
// 2.读取版本字段
byte version = in.readByte();
// 3.读取消息类型
byte msgType = in.readByte();
// 4.读取响应状态
byte status = in.readByte();
// 5.读取请求 ID
long requestId = in.readLong();
// 6.获取序列化算法长度
final int len = in.readInt();
if(in.readableBytes() < len){
in.resetReaderIndex();
return;
}
//7.序列化数据
final byte[] bytes = new byte[len];
in.readBytes(bytes);
final String serialization = new String(bytes);
// 8.读取消息体长度
int dataLength = in.readInt();
// 如果可读字节数小于消息体长度,说明还没有接收完整个消息体,回退并返回
if(in.readableBytes() < dataLength){
// 回退标记位置
in.resetReaderIndex();
return;
}
byte[] data = new byte[dataLength];
// 读取数据
in.readBytes(data);
// 处理消息的类型
MsgType msgTypeEnum = MsgType.findByType(msgType);
if(msgTypeEnum == null){
return;
}
// 构建消息头
MsgHeader header = new MsgHeader();
header.setMagic(magic);
header.setVersion(version);
header.setStatus(status);
header.setRequestId(requestId);
header.setMsgType(msgType);
header.setSerializations(bytes);
header.setSerializationLen(len);
header.setMsgLen(dataLength);
// 获取序列化器
RpcSerialization rpcSerialization = SerializationFactory.get(serialization);
// 根据消息类型进行处理(如果消息类型过多可以使用策略+工厂模式进行管理)
switch (msgTypeEnum){
//请求消息
case REQUEST:
RpcRequest request = rpcSerialization.deserialize(data, RpcRequest.class);
if (request != null) {
RpcProtocol<RpcRequest> protocol = new RpcProtocol<>();
protocol.setHeader(header);
protocol.setBody(request);
out.add(protocol);
}
break;
//响应消息
case RESPONSE:
RpcResponse response = rpcSerialization.deserialize(data, RpcResponse.class);
if (response != null) {
RpcProtocol<RpcResponse> protocol = new RpcProtocol<>();
protocol.setHeader(header);
protocol.setBody(response);
out.add(protocol);
}
break;
}
}
}
RpcResponseHandler响应。其中 RpcFuture的getPromise().setSuccess()方法可以解除线程的阻塞并返回该管道的消息
scala
package org.kkoneone.rpc.protocol.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.kkoneone.rpc.common.RpcFuture;
import org.kkoneone.rpc.common.RpcRequestHolder;
import org.kkoneone.rpc.common.RpcResponse;
import org.kkoneone.rpc.protocol.RpcProtocol;
/**
* 响应
* @Author:kkoneone11
* @name:RpcResponseHandler
* @Date:2023/11/29 9:47
*/
public class RpcResponseHandler extends SimpleChannelInboundHandler<RpcProtocol<RpcResponse>> {
/**
* 处理传入的RPC响应,将它们与相应的请求匹配,并将表示请求的future的结果设置为响应的主体表示结果处理情况
* @param ctx 关于网络通信的上下文
* @param msg Rpc协议
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, RpcProtocol<RpcResponse> msg) throws Exception {
long requestId = msg.getHeader().getRequestId();
RpcFuture<RpcResponse> future = RpcRequestHolder.REQUEST_MAP.remove(requestId);
//将RpcFuture的结果设置为传入消息的主体。解除任何等待此future结果的线程的阻塞
future.getPromise().setSuccess(msg.getBody());
}
}
SerializationFactory序列化工厂
java
package org.kkoneone.rpc.protocol.serialization;
import org.kkoneone.rpc.spi.ExtensionLoader;
/**
* 序列化工厂
* @Author:kkoneone11
* @name:SerializationFactory
* @Date:2023/11/28 17:52
*/
public class SerializationFactory {
//先生成一个SPI机制实例然后根据提供serializationr来决定对应的序列化方式
public static RpcSerialization get(String serialization) throws Exception {
return ExtensionLoader.getInstance().get(serialization);
}
public static void init() throws Exception {
ExtensionLoader.getInstance().loadExtension(RpcSerialization.class);
}
}
RpcSerialization序列化接口
java
package org.kkoneone.rpc.protocol.serialization;
import java.io.IOException;
/**
* 序列化接口
* @Author:kkoneone11
* @name:RpcSerialization
* @Date:2023/11/28 18:03
*/
public interface RpcSerialization {
//序列化方法
<T> byte[] serialize(T obj) throws IOException;
//反序列化方法
<T> T deserialize(byte[] data,Class<T> clz) throws IOException;
}
RpcConsumer消费者
java
package org.kkoneone.rpc.consumer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.kkoneone.rpc.common.RpcRequest;
import org.kkoneone.rpc.common.ServiceMeta;
import org.kkoneone.rpc.protocol.RpcProtocol;
import org.kkoneone.rpc.protocol.codec.RpcDecoder;
import org.kkoneone.rpc.protocol.codec.RpcEncoder;
import org.kkoneone.rpc.protocol.handler.RpcResponseHandler;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 消费方发送数据
* @Author:kkoneone11
* @name:RpcConsumer
* @Date:2023/11/28 10:40
*/
@Slf4j
public class RpcConsumer {
//帮助设置客户端的辅助类
private final Bootstrap bootstrap;
//处理 I/O 操作的多线程事件循环
private final EventLoopGroup eventLoopGroup;
public RpcConsumer(){
bootstrap = new Bootstrap();
//创建一个4线程的Nio事件循环组
eventLoopGroup = new NioEventLoopGroup(4);
//将 eventLoopGroup 分配给 bootstrap并指定客户端将使用 NioSocketChannel 连接到服务器
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception{
//向每个SocketChannel的ChannelPipeline添加三个处理器
socketChannel.pipeline()
.addLast(new RpcEncoder())
.addLast(new RpcDecoder())
.addLast(new RpcResponseHandler());
}
});
}
public void sendRequest(RpcProtocol<RpcRequest> protocol, ServiceMeta serviceMetadata) throws Exception {
if(serviceMetadata != null){
//和服务建立连接异步数据
ChannelFuture future = bootstrap.connect(serviceMetadata.getServiceAddr(), serviceMetadata.getServicePort()).sync();
//设置事件监听器
future.addListener((ChannelFutureListener) arg0 -> {
if(future.isSuccess()){
log.info("连接 rpc server {} 端口 {} 成功.", serviceMetadata.getServiceAddr(), serviceMetadata.getServicePort());
}else {
log.error("连接 rpc server {} 端口 {} 失败.", serviceMetadata.getServiceAddr(), serviceMetadata.getServicePort());
future.cause().printStackTrace();
eventLoopGroup.shutdownGracefully();
}
});
//向异步写入数据
future.channel().writeAndFlush(protocol);
}
}
}
核心类
RpcInvokerProxy代理对象。当需要调用的接口被注册上来的时候都会被注册为请求来到的时候都会调用里面的invoke方法
ini
package org.kkoneone.rpc.consumer;
import io.netty.channel.DefaultEventLoop;
import io.netty.util.concurrent.DefaultPromise;
import lombok.extern.slf4j.Slf4j;
import org.kkoneone.rpc.Filter.FilterConfig;
import org.kkoneone.rpc.Filter.FilterData;
import org.kkoneone.rpc.common.*;
import org.kkoneone.rpc.common.constants.MsgType;
import org.kkoneone.rpc.common.constants.ProtocolConstants;
import org.kkoneone.rpc.config.RpcProperties;
import org.kkoneone.rpc.consumer.RpcConsumer;
import org.kkoneone.rpc.protocol.MsgHeader;
import org.kkoneone.rpc.protocol.RpcProtocol;
import org.kkoneone.rpc.router.LoadBalancer;
import org.kkoneone.rpc.router.LoadBalancerFactory;
import org.kkoneone.rpc.router.ServiceMetaRes;
import org.springframework.util.ObjectUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import static org.kkoneone.rpc.common.constants.FaultTolerantRules.*;
/**
* @Author:kkoneone11
* @name:RpcInvokerProxy
* @Date:2023/11/25 23:03
*/
@Slf4j
public class RpcInvokerProxy implements InvocationHandler {
private String serviceVersion;
private long timeout;
private String loadBalancerType;
private String faultTolerantType;
private long retryCount;
public RpcInvokerProxy(){}
public RpcInvokerProxy(String serviceVersion, long timeout,String faultTolerantType,String loadBalancerType,long retryCount) throws Exception {
this.serviceVersion = serviceVersion;
this.timeout = timeout;
this.loadBalancerType = loadBalancerType;
this.faultTolerantType = faultTolerantType;
this.retryCount = retryCount;
}
/**
* 接口调用方法的时候都会走到这个invoke方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1创建协议
RpcProtocol<RpcRequest> protocol = new RpcProtocol<>();
//2构建消息头
MsgHeader header = new MsgHeader();
//2.1建立连接 获取请求id
long requestId = RpcRequestHolder.REQUEST_ID_GEN.incrementAndGet();
header.setMagic(ProtocolConstants.MAGIC);
header.setMsgLen(ProtocolConstants.VERSION);
header.setRequestId(requestId);
//2.2获得序列化的字节长度
final byte[] serialization = RpcProperties.getInstance().getSerialization().getBytes();
header.setSerializationLen(serialization.length);
header.setSerializations(serialization);
//ordinal()用来获得枚举值中的位置
header.setMsgType((byte) MsgType.REQUEST.ordinal());
header.setStatus((byte) 0x1);
protocol.setHeader(header);
//3构建请求体
RpcRequest request = new RpcRequest();
request.setServiceVersion(this.serviceVersion);
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameterTypes(method.getParameterTypes());
request.setData(ObjectUtils.isEmpty(args) ? new Object[0] : args);
request.setDataClass(request.getData().getClass());
request.setServiceAttachments(RpcProperties.getInstance().getServiceAttachments());
request.setClientAttachments(RpcProperties.getInstance().getClientAttachments());
//3.1拦截器上下文
final FilterData filterData = new FilterData(request);
try{
FilterConfig.getClientBeforeFilterChain().doFilter(filterData);
}catch (Throwable e){
throw e;
}
protocol.setBody(request);
//4获取负载均衡策略
final LoadBalancer loadBalancer = LoadBalancerFactory.get(loadBalancerType);
//4.1获取对应服务和参数
String serviceName = RpcServiceNameBuilder.buildServiceKey(request.getClassName(), request.getServiceVersion());
Object[] params = {request.getData()};
//4.2根据策略获取对应的服务节点
final ServiceMetaRes serviceMetaRes = loadBalancer.select(params, serviceName);
ServiceMeta curServiceMeta = serviceMetaRes.getCurServiceMeta();
Collection<ServiceMeta> otherServiceMeta = serviceMetaRes.getOtherServiceMeta();
//5容错机制:重试
long count = 1;
long retryCount = this.retryCount;
//服务调用者
RpcConsumer rpcConsumer = new RpcConsumer();
//返回数据
RpcResponse rpcResponse = null;
while(count <= retryCount){
//5.1处理返回数据
//new DefaultPromise()创建了一个DefaultPromise对象,它是Netty库中的一个类,用于表示一个异步操作的结果。
//new DefaultEventLoop()创建了一个新的事件循环,它是Netty中处理并发操作和网络事件的组件
RpcFuture<RpcResponse> future = new RpcFuture<>(new DefaultPromise<>(new DefaultEventLoop()),timeout);
//5.2存储返回结果
RpcRequestHolder.REQUEST_MAP.put(requestId,future);
try{
//服务调用者发送消息
rpcConsumer.sendRequest(protocol,curServiceMeta);
//异步等待数据返回
rpcResponse = future.getPromise().get(future.getTimeout(), TimeUnit.MICROSECONDS);
//如果有异常并且没有其他服务则抛出异常
if(rpcResponse.getException()!=null && otherServiceMeta.size() == 0){
throw rpcResponse.getException();
}
if (rpcResponse.getException()!=null){
throw rpcResponse.getException();
}
log.info("rpc 调用成功, serviceName: {}",serviceName);
try {
FilterConfig.getClientAfterFilterChain().doFilter(filterData);
}catch (Throwable e){
throw e;
}
return rpcResponse.getData();
}catch (Throwable e){
//调用失败则重试
String errorMsg = e.toString();
switch (faultTolerantType){
// 快速失败
case FailFast:
log.warn("rpc 调用失败,触发 FailFast 策略,异常信息: {}",errorMsg);
return rpcResponse.getException();
// 故障转移
case Failover:
log.warn("rpc 调用失败,第{}次重试,异常信息:{}",count,errorMsg);
count++;
if (!ObjectUtils.isEmpty(otherServiceMeta)){
final ServiceMeta next = otherServiceMeta.iterator().next();
curServiceMeta = next;
otherServiceMeta.remove(next);
}else {
final String msg = String.format("rpc 调用失败,无服务可用 serviceName: {%s}, 异常信息: {%s}", serviceName, errorMsg);
log.warn(msg);
throw new RuntimeException(msg);
}
break;
// 忽视这次错误
case Failsafe:
return null;
}
}
}
throw new RuntimeException("rpc 调用失败,超过最大重试次数: {}" + retryCount);
}
}