RPC框架-Gitee代码(麻烦点个Starred, 支持一下吧)
RPC框架-GitHub代码(麻烦点个Starred, 支持一下吧)
9.心跳动态上下线
a.实现心跳动态下线
在core模块下com.dcyrpc.loadbalancer.impl
包下
修改MinResponseTimeLoadBalancer
类的静态内部类MyTimerTask
的run()
方法
- 添加重试连接的次数
- 通过while循环,当发生问题,需要优先重试,重试3次后,将失效的地址移除服务列表
@Override
public void run() {
// 每次启动将响应时长的map清空
DcyRpcBootstrap.ANSWER_TIME_CHANNEL_CACHE.clear();
// 遍历所有的channel
Map<InetSocketAddress, Channel> channelCache = DcyRpcBootstrap.CHANNEL_CACHE;
for (Map.Entry<InetSocketAddress, Channel> entry : channelCache.entrySet()) {
// 重试的次数
int tryTimes = 3;
while(tryTimes > 0) {
Channel channel = entry.getValue();
long start = System.currentTimeMillis();
// 构建心跳请求
DcyRpcRequest dcyRpcRequest = DcyRpcRequest.builder()
.requestId(DcyRpcBootstrap.ID_GENERATOR.getId())
.compressType(CompressorFactory.getCompressor(DcyRpcBootstrap.COMPRESS_TYPE).getCode())
.serializeType(SerializerFactory.getSerializer(DcyRpcBootstrap.SERIALIZE_TYPE).getCode())
.requestType(RequestType.HEART_BEAT.getId())
.timeStamp(start)
.build();
CompletableFuture<Object> completableFuture = new CompletableFuture<>();
DcyRpcBootstrap.PENDING_REQUEST.put(dcyRpcRequest.getRequestId(), completableFuture);
channel.writeAndFlush(dcyRpcRequest).addListener((ChannelFutureListener) promise -> {
if (!promise.isSuccess()) {
completableFuture.completeExceptionally(promise.cause());
}
});
long endTime = 0L;
try {
completableFuture.get(1, TimeUnit.SECONDS);
endTime = System.currentTimeMillis();
} catch (InterruptedException | ExecutionException | TimeoutException e) {
// 一旦发生问题,需要优先重试
tryTimes--;
log.error("和地址为【{}】的主机发生异常.正在进行【{}】次重试........", channel.remoteAddress(), 3 - tryTimes);
// 重试3次后,将失效的地址移除服务列表
if (tryTimes == 0) {
DcyRpcBootstrap.CHANNEL_CACHE.remove(entry.getKey());
}
// 尝试等待一段时间后重试
try {
Thread.sleep(10*(new Random().nextInt(5)));
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
continue;
}
Long time = endTime - start;
// 使用TreeMap进行缓存
DcyRpcBootstrap.ANSWER_TIME_CHANNEL_CACHE.put(time, channel);
log.info("和【{}】服务器的响应时间是【{}】", entry.getKey() ,time);
break;
}
}
}
b.动态感知服务上线
通过Zookeeper的Watcher机制来进行动态上线:
- 1.调用方拉取服务列表时,注册一个watcher关注该服务节点的变化
- 2.当服务提供方上线或线下时会触发watcher机制(节点发生了变化)
- 3.通知调用方,执行动态上下线的操作。
在core模块下的com.dcyrpc
包下,创建watcher
包
在该包下创建UpAndDownWatcher
类:
- 实现 Watcher 接口
- 处理新增的节点:发现有节点不在缓存里(证明是新增节点),先建立连接,在缓存
/**
* 动态上下线的Watcher
*/
@Slf4j
public class UpAndDownWatcher implements Watcher {
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeChildrenChanged) {
log.info("检测到服务【{}】有节点上/下线,将重新拉取服务列表..", event.getPath());
String serviceName = getServiceName(event.getPath());
// 重新拉取服务列表
Registry registry = DcyRpcBootstrap.getInstance().getRegistry();
List<InetSocketAddress> addressList = registry.lookup(serviceName);
// 处理新增的节点
// 新增的节点,会在addressList,不在CHANNEL_CACHE中
for (InetSocketAddress address : addressList) {
if (!DcyRpcBootstrap.CHANNEL_CACHE.containsKey(address)) {
// 根据地址建立连接,并缓存
Channel channel = null;
try {
channel = NettyBootstrapInitializer.getBootstrap().connect(address).sync().channel();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
DcyRpcBootstrap.CHANNEL_CACHE.put(address, channel);
}
}
}
}
private String getServiceName(String path) {
String[] split = path.split("/");
return split[split.length - 1];
}
}
在core模块下的com.dcyrpc.discovery.impl
包中,修改ZookeeperRegistry
类的lookup()
方法
- 增加Watcher机制:只有子节点有改变,就重新拉取一下服务的子节点
// 略...
// 2.从zk中获取它的子节点,
List<String> children = ZookeeperUtils.getChildren(zooKeeper, serviceNode, new UpAndDownWatcher());
// 略...
c.动态感知服务下线
在core模块下的com.dcyrpc
包下,修改UpAndDownWatcher
类的process()
方法:新增代码
- 处理下线的节点:一定不在addressList,可能会在CHANNEL_CACHE中
// 略...
// 处理下线的节点
// 下线的节点,一定不在addressList,可能会在CHANNEL_CACHE中
for (Map.Entry<InetSocketAddress, Channel> entry : DcyRpcBootstrap.CHANNEL_CACHE.entrySet()) {
if (!addressList.contains(entry.getKey())) {
DcyRpcBootstrap.CHANNEL_CACHE.remove(entry.getKey());
}
}
d.实现重新的负载均衡reLoadBalance
当有节点上下线时,要对节点进行重新的负载均衡
-
在core模块下的
com.dcyrpc
包下,修改UpAndDownWatcher
类的process()
方法:新增代码- 重新的负载均衡:获得负载均衡器,进行重新的loadBalance
// 略...
// 获得负载均衡器,进行重新的loadBalance
LoadBalancer loadBalancer = DcyRpcBootstrap.LOAD_BALANCER;
loadBalancer.reLoadBalance(serviceName, addressList);
把原来在LoadBalancer
接口中定义的reBalance()
方法全部去掉
在LoadBalancer
接口中,创建reLoadBalance()
接口
/**
* 当感知节点发生了动态上下线,我们需要重新进行负载均衡
* @param serviceName 服务名称
*/
void reLoadBalance(String serviceName, List<InetSocketAddress> addressList);
在AbstractLoadBalancer
抽象类中,实现该方法
@Override
public synchronized void reLoadBalance(String serviceName, List<InetSocketAddress> addressList) {
// 根据新的服务列表生成新的selector
cache.put(serviceName, getSelector(addressList));
}