Alibaba Nacos CopyOnWrite 提高高并发源码分析

使用到了这个理念的地方就是nacos源码中的:Cluster类的updateIps方法:
该方法是:真正的注册逻辑方法,异步线程中执行的:

public void updateIps(List<Instance> ips, boolean ephemeral) {

        // TODO 将真正的注册表copy一份放到副本注册表中,用于该方法所有的操作,这个注册表只是某个group组下的stockService内部的实例列表
        // 根据传进来的ephemeral来判断返回那个实现注册的实例
        // toUpdateInstances是真正的注册表客户端实例
        Set<Instance> toUpdateInstances = ephemeral ? ephemeralInstances : persistentInstances;

        // 用新传进来的Instance集合和老的Instance进行比对,如果没有就直接加,如果有对应的实例,就需要进行比对看是否有变动有的话就要修改
        // 将真正的注册表客户端实例转换成一个oldIpMap,副本注册表
        HashMap<String, Instance> oldIpMap = new HashMap<>(toUpdateInstances.size());

        /**
         * oldIpMap就是注册表副本
         * 新传进来的实例要进行比对的操作
         */
        for (Instance ip : toUpdateInstances) {
            oldIpMap.put(ip.getDatumKey(), ip);
        }

        List<Instance> updatedIPs = updatedIps(ips, oldIpMap.values());
        if (updatedIPs.size() > 0) {
            for (Instance ip : updatedIPs) {
                Instance oldIP = oldIpMap.get(ip.getDatumKey());

                // do not update the ip validation status of updated ips
                // because the checker has the most precise result
                // Only when ip is not marked, don't we update the health status of IP:
                if (!ip.isMarked()) {
                    ip.setHealthy(oldIP.isHealthy());
                }

                if (ip.isHealthy() != oldIP.isHealthy()) {
                    // ip validation status updated
                    Loggers.EVT_LOG.info("{} {SYNC} IP-{} {}:{}@{}", getService().getName(),
                            (ip.isHealthy() ? "ENABLED" : "DISABLED"), ip.getIp(), ip.getPort(), getName());
                }

                if (ip.getWeight() != oldIP.getWeight()) {
                    // ip validation status updated
                    Loggers.EVT_LOG.info("{} {SYNC} {IP-UPDATED} {}->{}", getService().getName(), oldIP.toString(),
                            ip.toString());
                }
            }
        }

        List<Instance> newIPs = subtract(ips, oldIpMap.values());
        if (newIPs.size() > 0) {
            Loggers.EVT_LOG
                    .info("{} {SYNC} {IP-NEW} cluster: {}, new ips size: {}, content: {}", getService().getName(),
                            getName(), newIPs.size(), newIPs.toString());

            for (Instance ip : newIPs) {
                HealthCheckStatus.reset(ip);
            }
        }

        List<Instance> deadIPs = subtract(oldIpMap.values(), ips);

        if (deadIPs.size() > 0) {
            Loggers.EVT_LOG
                    .info("{} {SYNC} {IP-DEAD} cluster: {}, dead ips size: {}, content: {}", getService().getName(),
                            getName(), deadIPs.size(), deadIPs.toString());

            for (Instance ip : deadIPs) {
                HealthCheckStatus.remv(ip);
            }
        }

        /**
         * TODO 将真正的注册表替换成处理好的注册表副本
         * ips就是处理好的注册表副本,直接将处理好的注册表副本直接赋值给真正的注册表
         * 在赋值给真正的注册表这一大段代码之间,是没有用到toUpdateInstances真正的注册表的
         * 所以这就实现了:copy on write 写时赋值
         */
        toUpdateInstances = new HashSet<>(ips);

        // 实例真正存放的地方 根据ephemeral来判断放入ephemeralInstances还是persistentInstances
        // 这两个set集合都是注册表的一部分
        if (ephemeral) {
            ephemeralInstances = toUpdateInstances;
        } else {
            persistentInstances = toUpdateInstances;
        }
    }

该方法也是nacos源码中的一个嘲点,代码有点多有点杂。
具体解释一下这个方法到底是怎么实现复制的,然后复制后执行完实例的业务操作后,再填入真正的注册表中:
是如何复制的:将真正的注册表list放入一个副本list中,
然后如果是新增:就把新增的实例放入副本list中,不会影响到真正的副本,
如果是修改:将修改的实例直接在副本list中进行覆盖,也不会影响到真正的副本。
如果是删除:将要删除的实例在副本list中进行删除,也不会影响到真正的副本
所以增删改执行完后,都没有影响到真正的副本,然后最后将副本注册表直接复制给真正的注册表进行全量替换,就做到了无感替换

总结:


说白了就是:
将旧的实例信息的list中的数据放入一个新的list中,然后将要删除、增加的实例也在这个新的list中进行操作,比如增加就在这个新的list中增加,不会影响到客户端的读取。删除就在这个新的list中进行删除,也不会影响到客户端的读取,然后将操作好的新list直接赋给真正的注册表集合,就实现了CopyOnWrite理念
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的小松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值