【想进大厂还不会阅读源码】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地址戳这

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

相关推荐
紫荆鱼3 小时前
设计模式-命令模式(Command)
c++·后端·设计模式·命令模式
编码追梦人3 小时前
深耕 Rust:核心技术解析、生态实践与高性能开发指南
开发语言·后端·rust
朝新_4 小时前
【SpringBoot】详解Maven的操作与配置
java·spring boot·笔记·后端·spring·maven·javaee
绝无仅有4 小时前
某教育大厂面试题解析:MySQL索引、Redis缓存、Dubbo负载均衡等
vue.js·后端·面试
sean4 小时前
开发一个自己的 claude code
前端·后端·ai编程
追逐时光者5 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 59 期(2025年10.20-10.26)
后端·.net
盖世英雄酱581366 小时前
java深度调试【第三章内存分析和堆内存设置】
java·后端
007php0076 小时前
京东面试题解析:同步方法、线程池、Spring、Dubbo、消息队列、Redis等
开发语言·后端·百度·面试·职场和发展·架构·1024程序员节
程序定小飞6 小时前
基于springboot的电影评论网站系统设计与实现
java·spring boot·后端
码事漫谈6 小时前
高性能推理引擎的基石:C++与硬件加速的完美融合
后端