rpc注册中心优化

心跳检测

给注册的结点信息一个倒计时,让结点定期的续期,重置倒计时,如果节点宕机了,一直不续期,etcd就会进行key的过期删除

  1. 服务者想etcd注册自己的服务信息,在注册时设置过期时间
  2. etcd收到服务方的信息后一直维持这个过期时间,并在过期后删除key
  3. 服务方定期向etcd发起请求续签自己的注册信息(续期时间小于过期时间)
java 复制代码
// 创建一个 30 秒的租约
        long leaseId = leaseClient.grant(30).get().getID();

        // 设置要存储的键值对
        String registerKey = ETCD_ROOT_PATH + serviceMetaInfo.getServiceNodeKey();
        ByteSequence key = ByteSequence.from(registerKey, StandardCharsets.UTF_8);
        ByteSequence value = ByteSequence.from(JSONUtil.toJsonStr(serviceMetaInfo), StandardCharsets.UTF_8);

        // 将键值对与租约关联起来,并设置过期时间
        PutOption putOption = PutOption.builder().withLeaseId(leaseId).build();
        kvClient.put(key, value, putOption).get();

实现心跳检测

java 复制代码
 for(String key : loaclRegistryNodeKeySet) {
                    try{
                        List<KeyValue> keyValues = kvClient.get(ByteSequence.from(key, StandardCharsets.UTF_8))
                                .get().getKvs();
                        // 如果节点过期,需要重启节点才能注册
                        if(CollUtil.isEmpty(keyValues)){
                            continue;
                        }
                        // 节点为过期,重新注册
                        KeyValue keyValue = keyValues.get(0);
                        String value = keyValue.getValue().toString(StandardCharsets.UTF_8);
                        ServiceMetaInfo serviceMetaInfo = JSONUtil.toBean(value, ServiceMetaInfo.class);
                        register(serviceMetaInfo);
                    }catch (Exception e){
                        throw new RuntimeException(key+"续签失败",e);
                    }
                }

服务节点下线机制

  • 主动下线:关闭了provider的链接
  • 被动下线:服务者出现异常后,将该节点剔除

我们需要完善的是主动下线机制,利用jvm中的shutdownHook实现,允许开发者在jvm关闭前完成一些必要操作,例如数据库的关闭,释放资源等

java 复制代码
@Override
    public void destroy() {
        System.out.println("当前节点下线");
        // 下线节点,遍历所有的key
        for(String key: loaclRegistryNodeKeySet) {
            try{
                kvClient.delete(ByteSequence.from(key, StandardCharsets.UTF_8));
            }catch (Exception e){
                throw new RuntimeException(key+"下线失败");
            }

        }
        // 释放资源
        if (kvClient != null) {
            kvClient.close();
        }
        if (client != null) {
            client.close();
        }
    }

消费者服务缓存

由于服务节点的更新频率不是很高,所以在注册后完完全可以缓存在本地,下次就不需要在注册中心获取了,此时我们通过本地缓存实现

java 复制代码
public class RegistryServiceCache {

    /**
     * 服务缓存
     */
    List<ServiceMetaInfo> serviceCache;

    /**
     * 写缓存
     *
     * @param newServiceCache
     * @return
     */
    void writeCache(List<ServiceMetaInfo> newServiceCache) {
        this.serviceCache = newServiceCache;
    }

    /**
     * 读缓存
     *
     * @return
     */
    List<ServiceMetaInfo> readCache() {
        return this.serviceCache;
    }

    /**
     * 清空缓存
     */
    void clearCache() {
        this.serviceCache = null;
    }
}

修改我们EtcdRegistry的判断逻辑

java 复制代码
// 优先从缓存获取服务
    List<ServiceMetaInfo> cachedServiceMetaInfoList = registryServiceCache.readCache();
    if (cachedServiceMetaInfoList != null) {
        return cachedServiceMetaInfoList;
    }
......
    
    
     // 写入服务缓存
        registryServiceCache.writeCache(serviceMetaInfoList);

服务端缓存更新机制

当注册信息发生改变后(例如节点下线),需要及时更新消费缓存,此时采用etcd的watch机制,当监听到某个key发生变化后,触发事件

java 复制代码
/**
     * 只监听首次加入到监听集合中的key,防止重复
     * @param serviceNodeKey
     */
    @Override
    public void watch(String serviceNodeKey) {
        Watch watchClient = client.getWatchClient();
        // 开启监听
        boolean newWatch = watchKeySet.add(serviceNodeKey);
        if (newWatch) {
            watchClient.watch(ByteSequence.from(serviceNodeKey,StandardCharsets.UTF_8),response->{
                for(WatchEvent event : response.getEvents()){
                    switch (event.getEventType()){
                        // key 删除时触发
                        case DELETE:
                            // 清除缓存
                            registryServiceCache.clearCache();
                            break;
                        case PUT:
                        default:
                            break;
                    }
                }
            });
        }
    }

tip:ETCD集群的高可用保障

  1. 多节点部署,确保集群中有足够的健康节点
  2. raft一致性算法:通过选举和复制机制,确保集群中节点状态保持一致
  3. 自动选举和复制
  4. 负载均衡:分发请求到每个节点中,确保了高可用
  5. 监控和告警
  6. 通过WAL机制进行日志记录,即使在系统崩溃后也能进行数据恢复
相关推荐
懒大王就是我14 分钟前
C语言网络编程 -- TCP/iP协议
c语言·网络·tcp/ip
Elaine20239127 分钟前
06 网络编程基础
java·网络
海绵波波1072 小时前
Webserver(4.3)TCP通信实现
服务器·网络·tcp/ip
热爱跑步的恒川5 小时前
【论文复现】基于图卷积网络的轻量化推荐模型
网络·人工智能·开源·aigc·ai编程
云飞云共享云桌面5 小时前
8位机械工程师如何共享一台图形工作站算力?
linux·服务器·网络
音徽编程8 小时前
Rust异步运行时框架tokio保姆级教程
开发语言·网络·rust
幺零九零零9 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
23zhgjx-NanKon9 小时前
华为eNSP:QinQ
网络·安全·华为
23zhgjx-NanKon9 小时前
华为eNSP:mux-vlan
网络·安全·华为
点点滴滴的记录9 小时前
RPC核心实现原理
网络·网络协议·rpc