【想进大厂还不会阅读源码】ShenYu源码-重构同步数据服务

相信大家碰到源码时经常无从下手🙃,不知道从哪开始阅读,面对大量代码晕头转向,索性就读不下去了,又浪费了一次提升自己的机会😭。

我认为有一种方法 ,可以解决大家的困扰!那就是通过阅读某一次开源的【PR】、【ISSUE】,从这个入口出发去阅读源码!!

至此,我们发现自己开始从大量堆砌的源码中脱离开来😀。豁然开朗,柳暗花明又一村。

ShenYu是一个异步的,高性能的,跨语言的,响应式的 API 网关。有关ShenYu的介绍可以戳这

一、前瞻

好了,今天我们来看看本次的PR提交

我们看下PR的标题和Concersation的头一句,大概意思就是重构注册中心数据同步到ShenYu网关的方式

阅读源码前,我们先思考我们本次要阅读的线索:

  1. 贡献者是怎么重构的
  2. 重构了相对于原先旧方式有什么好处

二、探索

本次PR代码量还是很巨大 了,从PR提交可以看到一共有82次提交!!!不要怕,我们先整体浏览下整个提交,在高的维度去看贡献者具体做了什么动作。

总的来说可以分为两部分,重构提供同步数据的客户端、重构使用同步数据的Admin调用端(准确来说是监听端,下文都书写为调用端更容易理解)。

我们先看看Nacos的重构步骤,其他注册中心重构逻辑大致也是一致的。

java 复制代码
public class NacosSyncDataService extends AbstractNodeDataSyncService implements SyncDataService {
}

Nacos同步数据的NacosSyncDataService继承了AbstractNodeDataSyncService,我们应该重点阅读父类约定的规则和提供的实现。

上图可以看到有两个抽象类AbstractNodeDataSyncService、AbstractPathDataSyncService,NodeData、PataData顾名思义前者是处理Nacos这类节点类型的注册中心,后者就是处理ZooKeeper这类的路径节点注册中心。

大家思考下,本次PR提交一直提到同步数据 ,那究竟靠什么来同步数据?其实Nacos有配置中心 的功能,同步数据也就是同步Nacos配置中心的数据,方便ShenYu修改数据就立刻生效而不用重启

可以看到AbstractNodeDataSyncServicestartWatch方法,各个watch方法来监听配置中心的数据。

java 复制代码
    protected void startWatch() {
        try {
            final List<String> pluginNames = getConfigListOnWatch(changeData.getPluginDataId() + DefaultNodeConstants.POINT_LIST, updateData -> {
                List<String> changedPluginNames = GsonUtils.getInstance().fromList(updateData, String.class);
                watcherPlugin(changedPluginNames);
            });

            watcherPlugin(pluginNames);

            watchCommonList(changeData.getAuthDataId() + DefaultNodeConstants.JOIN_POINT, this::cacheAuthData, this::unCacheAuthData);

            watchCommonList(changeData.getMetaDataId() + DefaultNodeConstants.JOIN_POINT, this::cacheMetaData, this::unCacheMetaData);

        } catch (Exception e) {
            throw new ShenyuException(e);
        }
    }

其中getServiceConfig就是留给各个子类来实现的功能,不同的配置中心,不同获取配置的方法。

java 复制代码
protected abstract String getServiceConfig(String key, Consumer<String> updateHandler, Consumer<String> deleteHandler);

我们来看看获取Nacos配置的子类NacosSyncDataService是怎么获取的。

java 复制代码
public class NacosSyncDataService extends AbstractNodeDataSyncService implements SyncDataService {
    
    private final ConfigService configService;

    @Override
    protected String getServiceConfig(final String key, final Consumer<String> updateHandler, final Consumer<String> deleteHandler) {
        try {
            if (watchCache.containsKey(key)) {
                return null;
            }
            final Listener listener = new Listener() {
                @Override
                public Executor getExecutor() {
                    return null;
                }

                @Override
                public void receiveConfigInfo(final String configInfo) {
                    try {
                        if (StringUtils.isBlank(configInfo) && deleteHandler != null) {
                            deleteHandler.accept(key);
                        } else {
                            updateHandler.accept(configInfo);
                        }
                    } catch (Exception e) {
                        LOG.error("nacos sync listener receiveConfigInfo error", e);
                    }
                }
            };
            final String serviceConfig = configService.getConfigAndSignListener(key, NacosPathConstants.GROUP, 3000, listener);
            watchCache.put(key, listener);
            return serviceConfig;
        } catch (Exception e) {
            throw new ShenyuException(e);
        }
    }
}

可以看到该类通过调用Nacos提供的ConfigService服务来进行获取配置,同时Listener就是监听器的作用。

大致看完了客户端的重构 ,我们来看看ShenYu Admin调用端的重构 。既然客户端是同步配置中心的数据,那调用端也就是起到创建、更新、删除配置的作用

回顾下上文提到的AbstractNodeDataSyncService和AbstractPathDataSyncService ,上图的AbstractNodeDataChangeListener和AbstractPathDataChangeListener同样是实现某些具体的功能,需要子类实现的延迟到子类实现。

我们看下子类NacosDataChangedListener实现的功能。

java 复制代码
public class NacosDataChangedListener extends AbstractNodeDataChangedListener {

    private static final Logger LOG = LoggerFactory.getLogger(NacosDataChangedListener.class);

    private final ConfigService configService;

    public NacosDataChangedListener(final ConfigService configService) {
        super(new ChangeData(NacosPathConstants.PLUGIN_DATA_ID, NacosPathConstants.SELECTOR_DATA_ID,
                NacosPathConstants.RULE_DATA_ID, NacosPathConstants.AUTH_DATA_ID, NacosPathConstants.META_DATA_ID,
                NacosPathConstants.PROXY_SELECTOR_DATA_ID, NacosPathConstants.DISCOVERY_DATA_ID));
        this.configService = configService;
    }

    @Override
    public void doPublishConfig(final String dataId, final Object data) {
        try {
            configService.publishConfig(
                    dataId, 
                    NacosPathConstants.GROUP, 
                    GsonUtils.getInstance().toJson(data),
                    ConfigType.JSON.getType());
        } catch (NacosException e) {
            LOG.error("Publish data to nacos error.", e);
            throw new ShenyuException(e.getMessage());
        }
    }

    @Override
    public void doDelConfig(final String dataId) {
        try {
            configService.removeConfig(
                    dataId,
                    NacosPathConstants.GROUP);
        } catch (NacosException e) {
            LOG.error("Publish data to nacos error.", e);
            throw new ShenyuException(e.getMessage());
        }
    }

    @Override
    public String getConfig(final String dataId) {
        try {
            return configService.getConfig(dataId, NacosPathConstants.GROUP, NacosPathConstants.DEFAULT_TIME_OUT);
        } catch (NacosException e) {
            LOG.error("Get data from nacos error.", e);
            throw new ShenyuException(e.getMessage());
        }
    }

}

正如上文我们所说的:

既然客户端是同步配置中心的数据,那调用端也就是起到创建、更新、删除配置的作用

三、拓展

不会吧,大家还记得我们的阅读线索2吗,重构了相对于原先旧方式有什么好处?

我们仔细看下上文提到的AbstractNodeDataSyncService和AbstractPathDataSyncService等这几个抽象类就知道了,抽象父类把重复的功能 实现,而获取配置 这种各个配置中心有不同的获取方式,就留给了各个配置中心的子类实现

而原先的旧代码,每个不同的配置中心都要实现一套重复的方法代码,代码耦合且不易扩展。大家看看重构了有没好处呢。

咦?仔细看了下,贡献者的类注释写错了啊。

开心又获得了一次开源贡献!想看下开源PR戳这

好了,今天的分享就到这了。大家能否感受到通过PR这种方式来阅读源码的乐趣呢!不仅获得了知识,还获得了一次开源贡献,何乐而不为呢

博主的GitHub也有一些读者感兴趣的知识可以学习😍,GitHub地址戳这

创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️

相关推荐
木雷坞5 小时前
Go 项目实战:用 MLiev IAM 落地企业认证中心
后端·golang·认证
Moment9 小时前
长上下文会最终杀死 Rag 吗?
前端·javascript·后端
蝎子莱莱爱打怪10 小时前
🚀 🚀🚀2026年5月GitHub月榜精选:17个项目中挑出10个推荐,实操4个!
人工智能·后端·ai编程
砍材农夫11 小时前
物联网实战:Spring Boot MQTT | MQTT 设备模拟器演示(附源码)
java·spring boot·后端·物联网·spring·netty
我叫黑大帅11 小时前
解决聊天页内部滚轮改为页面滚动问题
javascript·后端·面试
IT_陈寒11 小时前
Python的线程池居然把我坑在了垃圾回收这块
前端·人工智能·后端
zhangxingchao12 小时前
AI应用开发八:RAG相关技术总结
前端·人工智能·后端
吴佳浩12 小时前
Go史上最大“打脸”现场来了:泛型方法终于实现了
后端·go
Huyuejia12 小时前
runtime-ask
后端
Rust研习社12 小时前
90% 的 Rust 新手都不知道的 3 个实用开发技巧
后端·rust·编程语言