IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 从零实现RPC框架之 6: 注册中心模块 -> 正文阅读

[网络协议]从零实现RPC框架之 6: 注册中心模块

1.注册中心的作用

如你要给一位以前从未合作过的同事发邮件请求帮助,但你却没有他的邮箱地址。这个时候你会怎么办呢?如果是我,我会选择去看公司的企业“通信录”。

对于服务调用方和服务提供方来说,其契约就是接口,相当于“通信录”中的姓名,服务节点就是提供该契约的一个具体实例。服务 IP 集合作为“通信录”中的地址

RPC 中服务消费端(Consumer) 需要请求服务提供方(Provider)的接口,必须要知道 Provider 的地址才能请求到。
那么,Consumer 要从哪里获取 Provider 的地址呢?

可以直接在代码或者配置文件中写死服务端的IP和端口 因为RPC调用也是基于socket通信的。

这样做有两个坏处:

  1. 如果服务端节点挂掉了,客户端就直接不可用了
  2. 如果想要更换地点,则需要暂停服务,重新配置,代码扩展性低。

RPC框架最简单的结构

注册中心负责服务地址的查找与注册,相当于服务目录。

简单来说注册中心就像一个中转站,提供的作用就是根据调用的名称找到远程服务的地址。

2.注册中心的特性

一个合格的注册中心,需要有以下的特性:

1. 存储

可以简单地将注册中心理解为一个存储系统,存储着服务与服务提供方的映射表。一般注册中心对存储没有太多特别的要求,甚至夸张一点,你可以基于数据库 或者Redis 来实现一个注册中心都没问题 Dubbo官方也提供了Redis作为注册中心的用法。

2. 高可用

注册中心一旦挂掉,Consumer 将无法获取 Provider 的地址,整个微服务将无法运转。因此注册中心可以采用集群架构
当然 Consumer 可以添加本地缓存,从某种角度上看,是允许注册中心短暂挂掉的。

3.注册中心集群中数据一致性

4. 健康检查

Provider 向注册中心注册服务之后,注册中心需要定时向 Provider 发起健康检查,当 Provider 宕机的时候,注册中心能更快发现 ,从而将宕机的 Provider 从注册表中移除。
这特性数据库、Redis 都不具有,因此他们不适合做注册中心。

5. 监听状态

当服务增加、减少 Provider 的时候,注册中心除了能及时更新,还要能主动通知 Consumer,以便 Consumer 能快速更新本地缓存,减少错误请求的次数。这一特性同样数据库、Redis都不具有。

目前主流的注册中心有:ZookeeperEurekaNacosConsul 等。
本文主要是讲注册中心的实现,就不详细讲各种注册中心的差异、优缺点了,有兴趣的同学可以看这里

3.CAP理论与BASE理论

CAP 也就是 Consistency(一致性)Availability(可用性)Partition Tolerance(分区容错性) 这三个单词首字母组合。

img

在理论计算机科学中,CAP 定理(CAP theorem)指出对于一个分布式系统来说,当设计读写操作时,只能同时满足以下三点中的两个:

  • 一致性(Consistency) : 所有节点访问同一份最新的数据副本
  • 可用性(Availability): 非故障的节点在合理的时间内返回合理的响应(不是错误或者超时的响应)。
  • 分区容错性(Partition tolerance) : 分布式系统出现网络分区的时候,仍然能够对外提供服务。

什么是网络分区?

分布式系统中,多个节点之前的网络本来是连通的,但是因为某些故障(比如部分节点网络出了问题)某些节点之间不连通了,整个网络就分成了几块区域,这就叫网络分区

CP or AP?

当发生网络分区的时候,如果我们要继续服务,那么强一致性和可用性只能 2 选 1。也就是说当网络分区之后 P 是前提,决定了 P 之后才有 C 和 A 的选择。也就是说分区容错性(Partition tolerance)我们是必须要实现的。

因此,分布式系统理论上不可能选择 CA 架构,只能选择 CP 或者 AP 架构。 比如 ZooKeeper、HBase 就是 CP 架构,Cassandra、Eureka 就是 AP 架构,Nacos 不仅支持 CP 架构也支持 AP 架构。

为啥不可能选择 CA 架构呢? 举个例子:若系统出现“分区”,系统中的某个节点在进行写操作。为了保证 C, 必须要禁止其他节点的读写操作,这就和 A 发生冲突了。如果为了保证 A,其他节点的读写操作正常的话,那就和 C 发生冲突了。

选择 CP 还是 AP 的关键在于当前的业务场景,没有定论,比如对于需要确保强一致性的场景如银行一般会选择保证 CP 。

BASE 理论三要素

BASE理论三要素

1. 基本可用

基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。但是,这绝不等价于系统不可用。

什么叫允许损失部分可用性呢?

  • 响应时间上的损失: 正常情况下,处理用户请求需要 0.5s 返回结果,但是由于系统出现故障,处理用户请求的时间变为 3 s。
  • 系统功能上的损失:正常情况下,用户可以使用系统的全部功能,但是由于系统访问量突然剧增,系统的部分非核心功能无法使用。

2. 软状态

软状态指允许系统中的数据存在中间状态(CAP 理论中的数据不一致),并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。

3. 最终一致性

最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

4.基于Zookeeper的服务注册与发现

接口定义

为了减缓注册中心的压力,需要加上本地缓存,减少请求。同时也可以增加可用性,当注册中心挂的时候,本地还可以使用缓存中的数据。这部分逻辑否装在 AbstractRegistry 中,其他的实现都继承 AbstractRegistry

变量 registered 将服务信息缓存在 Map 中,服务名为 Key,Value 则是该服务注册的 Provider 列表

@SPI
public interface ServiceDiscovery {
    /**
     * lookup service by rpcServiceName
     *
     * @param rpcRequest rpc service pojo
     * @return service address
     */
    InetSocketAddress lookupService(RpcRequest rpcRequest);
}
@SPI
public interface ServiceRegistry {
    /**
     * register service
     *
     * @param rpcServiceName    rpc service name
     * @param inetSocketAddress service address
     */
    void registerService(String rpcServiceName, InetSocketAddress inetSocketAddress);

}

监听 以及更新本地缓存

private static final Map<String, List<String>> SERVICE_ADDRESS_MAP = new ConcurrentHashMap<>();

private static void registerWatcher(String rpcServiceName, CuratorFramework zkClient) throws Exception {
        //拿到zookeeper路径结点
        String servicePath = ZK_REGISTER_ROOT_PATH + "/" + rpcServiceName;
        //A utility that attempts to keep all data from all children of a ZK path locally cached. This class
        // will watch the ZK path, respond to update/create/delete events, pull down the data, etc.
        // You caregister a listener that will get notified when changes occur
        PathChildrenCache pathChildrenCache = new PathChildrenCache(zkClient, servicePath, true);
        //创建一个监听器 监听上面那个路径孩子缓存的变化
        PathChildrenCacheListener pathChildrenCacheListener = (curatorFramework, pathChildrenCacheEvent) -> {
            //当有变化的时候 就拿到 这个路径下的服务地址
            List<String> serviceAddresses = curatorFramework.getChildren().forPath(servicePath);
            //更新一下注册中心的缓存
            SERVICE_ADDRESS_MAP.put(rpcServiceName, serviceAddresses);
        };
        //注册这个监听器
        pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
        pathChildrenCache.start();
    }

Zookeeper 实现

Netty-rpc 中,注册中心实现了 zookeeper
Zookeeper 客户端使用的是 Curator 框架,比官方的好用多了。

1. 注册

服务注册的时候,会在 /my-rpc/${serviceName} 下创建一个临时节点
为什么是临时节点呢?临时节点有个功能就是,当客户端断开连接的时候,该客户端创建的节点都会自动删除,这个特性非常适合注册中心。

public void registerService(String rpcServiceName, InetSocketAddress inetSocketAddress) {
    String url = toURL(rpcServiceName, inetSocketAddress);
    zkClient.createEphemeralNode(toUrlPath(url));
    watch(url);
}

2. 查找

Consumer 直接获取服务路径下的所有子节点即可。

public List<URL> doLookup(URL condition) {
    List<String> children = zkClient.getChildren(toServicePath(condition));
    List<URL> urls = children.stream()
            .map(s -> URLParser.toURL(URLDecoder.decode(s, charset)))
            .collect(Collectors.toList());
    return urls;
}

3. 监听

Zookeeper提供了 watch机制

对这个服务名称下的 所有结点进行监听

/**
     * Registers to listen for changes to the specified node
     *
     * @param rpcServiceName rpc service name eg:github.javaguide.HelloServicetest2version
     */
    private static void registerWatcher(String rpcServiceName, CuratorFramework zkClient) throws Exception {
        //拿到zookeeper路径结点
        String servicePath = ZK_REGISTER_ROOT_PATH + "/" + rpcServiceName;
        //A utility that attempts to keep all data from all children of a ZK path locally cached. This class
        // will watch the ZK path, respond to update/create/delete events, pull down the data, etc.
        // You caregister a listener that will get notified when changes occur
        PathChildrenCache pathChildrenCache = new PathChildrenCache(zkClient, servicePath, true);
        //创建一个监听器 监听上面那个路径孩子缓存的变化
        PathChildrenCacheListener pathChildrenCacheListener = (curatorFramework, pathChildrenCacheEvent) -> {
            //当有变化的时候 就拿到 这个路径下的服务地址
            List<String> serviceAddresses = curatorFramework.getChildren().forPath(servicePath);
            //更新一下注册中心的缓存
            SERVICE_ADDRESS_MAP.put(rpcServiceName, serviceAddresses);
        };
        //注册这个监听器
        pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
        pathChildrenCache.start();
    }
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 11:56:47  更:2022-05-05 12:00:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/29 11:19:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计