1.1 - 基本的rpc调用

1.2 - 引入netty框架

Client
客户端有服务端的功能接口,因此通过此接口创建代理对象,此对象内部会有netty的客户端,当用户调用代理对象的方法时,netty的客户端会向服务端发送请求并返回数据
Server
在服务端启动后,会把自身放入一个map中,在收到请求后且是RpcRequest时,会触发NettyRPCServerHandler处理器,根据RpcRequest中的interfaceName信息,找到服务端的实例,再调用相关的方法,最后返回结果
1.3 - 引入zookeeper作为注册中心

2.1 - 自定义编码器
解码器和序列化器
BUG
fastJson: 接口的方法的参数类型要用包装类,负责会出现类型不匹配的bug
1 | MyDecoder解码失败java.lang.ClassCastException: class java.lang.Integer cannot be cast to class com.alibaba.fastjson.JSONObject (java.lang.Integer is in module java.base of loader 'bootstrap'; com.alibaba.fastjson.JSONObject is in unnamed module of loader 'app') |
2.2 - 在客户端建立本地服务缓存并实现动态更新

3.1 - 负载均衡

负载均衡算法
1 | //虚假节点的数量 一个真实节点对应5个虚拟节点 |
3.2 - 超时重试&白名单
使用 guavaRetry 实现超时重连
在 zk 上新增节点 MyRPC/CanRetry 代表可以超时重试的服务
Client

Server

4.1 - 服务-限流

4.2 - 服务-熔断

6.1 - 日志链路追踪
使用 Slf4j 的 MDC 日志记录时提供额外的上下文信息,
客户端在进行方法调用前会将 traceID 保存在 MDC 种,之后在正式发送请求前,会将 traceID 相关内容存入保存到Channel属性,因为在 channel 发送数据时, netty 内部会新起一个 channel 线程用于发送数据,如果不把 traceID 相关内容存入保存到Channel属性,那么在编码过程种会无法获取 traceID 相关内容,那么服务端就会无法实现日志链路追踪
BUG
反序列化出现错误, 无法返回Resonse.fail()
错误代码
1 | public Object deserialize(byte[] bytes, int messageType) { |
正确代码
public Object deserialize(byte[] bytes, int messageType) {
Object obj = null;
// 传输的消息分为request与response
switch (messageType){
case 0:
RpcRequest request = JSON.parseObject(bytes, RpcRequest.class);
Object[] objects = new Object[request.getParams().length];
// 把json字串转化成对应的对象, fastjson可以读出基本数据类型,不用转化
// 对转换后的request中的params属性逐个进行类型判断
for(int i = 0; i < objects.length; i++){
Class<?> paramsType = request.getParamType()[i];
//判断每个对象类型是否和paramsTypes中的一致
if (!paramsType.isAssignableFrom(request.getParams()[i].getClass())){
//如果不一致,就行进行类型转换
objects[i] = JSONObject.toJavaObject((JSONObject) request.getParams()[i],request.getParamType()[i]);
}else{
//如果一致就直接赋给objects[i]
objects[i] = request.getParams()[i];
}
}
request.setParams(objects);
obj = request;
break;
case 1:
RpcResponse response = JSON.parseObject(bytes, RpcResponse.class);
// 如果类型为空,说明返回错误
if(response.getDataType()==null){
obj = RpcResponse.fail();
break;
}
Class<?> dataType = response.getDataType();
//判断转化后的response对象中的data的类型是否正确
if(!dataType.isAssignableFrom(response.getData().getClass())){
response.setData(JSONObject.toJavaObject((JSONObject) response.getData(),dataType));
}
obj = response;
break;
default:
System.out.println("暂时不支持此种消息");
throw new RuntimeException();
}
return obj;
}
);
}
obj = response;
break;
default:
System.out.println("暂时不支持此种消息");
throw new RuntimeException();
}
return obj;
}