Apache 神禹(shenyu)源码阅读(二)——Admin 向 Gateway 的数据同步(Gateway 端)

源码版本:2.6.1

前言

上一篇Apache 神禹(shenyu)源码阅读(一)------Admin向Gateway的数据同步(Admin端)写了Admin 端在接收到程序员对 Divide 插件的选择器 Selector 作出新增操作时,Admin 端是如何将要同步的数据发布给 Gateway 端的。

本篇介绍 Gateway 端是如何接收 Admin 端发布的数据的。

本文介绍的数据同步(sync data)在 Shenyu 架构图中的位置

正文

1. Gateway 端通过网络接收 Admin 端要同步的数据

  • ShenyuWebsocketClient.onMessage()
    由 Admin 端的 WebsocketCollector.send() 通过网络发送数据后(上一篇的内容),Gateway 端的 ShenyuWebsocketClient.onMessage() 收到数据,onMessage() 是 Spring 框架抽象类 WebSocketClient 的一个方法,在 ShenyuWebsocketClient 中实现了这个方法。
java 复制代码
public final class ShenyuWebsocketClient extends WebSocketClient {
	// ...
    @Override
    public void onMessage(final String result) {
        handleResult(result);
    }
    private void handleResult(final String result) {
        // 1. 打印日志
        LOG.info("handleResult({})", result);
        // 2. 调用 Gson 包,将 Json 字符串转换为 WebsocketData
        WebsocketData<?> websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class);
        // 3. 因为我们是新增的 Selector,所以这里 groupEnum 为 ConfigGroupEnum.SELECTOR
        ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType());
        // 4. 事件是 UPDATE
        String eventType = websocketData.getEventType();
        // 5. 再转成 Json 字符串
        String json = GsonUtils.getInstance().toJson(websocketData.getData());
        // 6. 交给 WebsocketDataHandler 处理数据
        websocketDataHandler.executor(groupEnum, json, eventType);
    }
}
  • ShenyuWebsocketClient.handleResult()

    如上面那段代码,

    1. 打印日志
    2. 调用 Gson 包,将 Json 字符串转换为 WebsocketData
    3. 因为我们是新增的 Selector,所以这里 groupEnumConfigGroupEnum.SELECTOR
    4. 事件是 UPDATE
    5. 再转成 Json 字符串
    6. 交给 WebsocketDataHandler 处理数据
  • WebsocketDataHandler.executor()

    WebsocketDataHandler 的一个 EnumMap 类型的成员变量存储了 ConfigGroupEnum -> DataHandler 的映射。在 executor 方法里拿到 ConfigGroupEnum 对应的 DataHandler 去处理数据

java 复制代码
public class WebsocketDataHandler {
	// ...
    private static final EnumMap<ConfigGroupEnum, DataHandler> ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class);    

	public void executor(final ConfigGroupEnum type, final String json, final String eventType) {
        ENUM_MAP.get(type).handle(json, eventType);
    }
}

2 交由 SelectorDataHandler 处理数据

  • DataHandler.handle

    DataHandler 是个接口:

java 复制代码
public interface DataHandler {

    /**
     * Handle.
     *
     * @param json  the data for json
     * @param eventType the event type
     */
    void handle(String json, String eventType);
}

其继承关系如下图:

  • AbstractDataHandler.handle()

    这里 handle() 用到了一个设计模式------模板方法,里面用到的方法都是交由子类根据自己的逻辑去实现

    事件类型为 UPDATE 和 CREATE 的事件都由 doUpdate 方法处理

java 复制代码
public abstract class AbstractDataHandler<T> implements DataHandler {

    /**
     * Convert list.
     *
     * @param json the json
     * @return the list
     */
    protected abstract List<T> convert(String json);

    /**
     * Do refresh.
     *
     * @param dataList the data list
     */
    protected abstract void doRefresh(List<T> dataList);

    /**
     * Do update.
     *
     * @param dataList the data list
     */
    protected abstract void doUpdate(List<T> dataList);

    /**
     * Do delete.
     *
     * @param dataList the data list
     */
    protected abstract void doDelete(List<T> dataList);

    @Override
    public void handle(final String json, final String eventType) {
        List<T> dataList = convert(json);

        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }

        DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType);
        switch (eventTypeEnum) {
            case REFRESH:
            case MYSELF:
                doRefresh(dataList);
                break;
            case UPDATE:
            case CREATE:
            	// 事件类型为 UPDATE 和 CREATE 的事件都由 doUpdate 方法处理
                doUpdate(dataList);
                break;
            case DELETE:
                doDelete(dataList);
                break;
            default:
                break;
        }
    }
}
  • SelectorDataHandler.doUpdate()

    由插件数据订阅者 pluginDataSubscriber 去完成 Selector 数据的订阅和处理

java 复制代码
 public class SelectorDataHandler extends AbstractDataHandler<SelectorData> {
    // ...
    private final PluginDataSubscriber pluginDataSubscriber;
    
    @Override
    protected void doUpdate(final List<SelectorData> dataList) {
        dataList.forEach(pluginDataSubscriber::onSelectorSubscribe);
    }
}
  • CommonPluginDataSubscriber.onSelectorSubscribe()

    CommonPluginDataSubscriber 是 PluginDataSubscriber 的唯一一个实现类:

java 复制代码
public class CommonPluginDataSubscriber implements PluginDataSubscriber {
	// ...
    @Override
    public void onSelectorSubscribe(final SelectorData selectorData) {
        LOG.info("subscribe select data for selector: [id: {}, pluginName: {}, name: {}]", selectorData.getId(), selectorData.getPluginName(), selectorData.getName());
        subscribeDataHandler(selectorData, DataEventTypeEnum.UPDATE);
    }
    
    private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
        if (dataType == DataEventTypeEnum.UPDATE) {
            Optional.ofNullable(classData)
            		// 如果要更新的数据不为空,则更新缓存数据
                    .ifPresent(data -> updateCacheData(classData));
        } else if (dataType == DataEventTypeEnum.DELETE) {
            Optional.ofNullable(classData)
                    .ifPresent(data -> removeCacheData(classData));
        }
    }

    private <T> void updateCacheData(@NonNull final T data) {
        if (data instanceof PluginData) {
            PluginData pluginData = (PluginData) data;
            final PluginData oldPluginData = BaseDataCache.getInstance().obtainPluginData(pluginData.getName());
            BaseDataCache.getInstance().cachePluginData(pluginData);
            Optional.ofNullable(handlerMap.get(pluginData.getName()))
                    .ifPresent(handler -> handler.handlerPlugin(pluginData));

            // update enabled plugins
            PluginHandlerEventEnum state = Boolean.TRUE.equals(pluginData.getEnabled())
                    ? PluginHandlerEventEnum.ENABLED : PluginHandlerEventEnum.DISABLED;
            eventPublisher.publishEvent(new PluginHandlerEvent(state, pluginData));
            // sorted plugin
            sortPluginIfOrderChange(oldPluginData, pluginData);
            
            final String pluginName = pluginData.getName();
            // if update plugin, remove selector and rule match cache/trie cache
            if (selectorMatchConfig.getCache().getEnabled()) {
                MatchDataCache.getInstance().removeSelectorData(pluginName);
            }
            if (ruleMatchCacheConfig.getCache().getEnabled()) {
                MatchDataCache.getInstance().removeRuleData(pluginName);
            }
        } else if (data instanceof SelectorData) {
            SelectorData selectorData = (SelectorData) data;
            // BaseDataCache 缓存
            BaseDataCache.getInstance().cacheSelectData(selectorData);
            Optional.ofNullable(handlerMap.get(selectorData.getPluginName()))
                    .ifPresent(handler -> handler.handlerSelector(selectorData));
            // remove match cache
            if (selectorMatchConfig.getCache().getEnabled()) {
                MatchDataCache.getInstance().removeSelectorData(selectorData.getPluginName(), selectorData.getId());
                MatchDataCache.getInstance().removeEmptySelectorData(selectorData.getPluginName());
            }
            if (ruleMatchCacheConfig.getCache().getEnabled()) {
                MatchDataCache.getInstance().removeRuleDataBySelector(selectorData.getPluginName(), selectorData.getId());
                MatchDataCache.getInstance().removeEmptyRuleData(selectorData.getPluginName());
            }
            updateSelectorTrieCache(selectorData);
        } else if (data instanceof RuleData) {
            RuleData ruleData = (RuleData) data;
            BaseDataCache.getInstance().cacheRuleData(ruleData);
            Optional.ofNullable(handlerMap.get(ruleData.getPluginName()))
                    .ifPresent(handler -> handler.handlerRule(ruleData));
            if (ruleMatchCacheConfig.getCache().getEnabled()) {
                MatchDataCache.getInstance().removeRuleData(ruleData.getPluginName(), ruleData.getId());
                MatchDataCache.getInstance().removeEmptyRuleData(ruleData.getPluginName());
            }
            updateRuleTrieCache(ruleData);
        }
    }
}

3. BaseDataCache 根据数据更新缓存

网关的 SELECTOR_MAP 等缓存是由 ConcurrentMap 实现的。

  1. 筛选出不是这个 selectorId 的选择器数据,保存到 resultList 中
  2. 向 resultList 加入要更新的数据。1、2 两步相当于先删除了原 selectorId 的数据,然后再添加进新的数据
  3. 然后将更新后的 selectorData 集合排序
  4. 更新 SELECTOR_MAP
java 复制代码
public final class BaseDataCache {
	// ...
   	private static final ConcurrentMap<String, List<SelectorData>> SELECTOR_MAP = Maps.newConcurrentMap();

	// 我觉得这个方法名可能是敲错了,应该是 cacheSelectorData 才对
    public void cacheSelectData(final SelectorData selectorData) {
        Optional.ofNullable(selectorData).ifPresent(this::selectorAccept);
    }
    
	private void selectorAccept(final SelectorData data) {
        String key = data.getPluginName();
        synchronized (SELECTOR_MAP) {
            if (SELECTOR_MAP.containsKey(key)) {
                // 存在 key,说明为更新操作
                List<SelectorData> existList = SELECTOR_MAP.get(key);
                // 1. 筛选出不是这个 selectorId 的选择器数据,保存到 resultList 中
                final List<SelectorData> resultList = existList.stream().filter(r -> !r.getId().equals(data.getId())).collect(Collectors.toList());
                // 2. 向 resultList 加入要更新的数据。1、2 两步相当于先删除了原 selectorId 的数据,然后再添加进新的数据
                resultList.add(data);
                // 3. 然后将更新后的 selectorData 集合排序
                final List<SelectorData> collect = resultList.stream().sorted(Comparator.comparing(SelectorData::getSort)).collect(Collectors.toList());
                // 4. 更新 SELECTOR_MAP
                SELECTOR_MAP.put(key, collect);
            } else {
                // 不存在 key,说明为新增操作
                SELECTOR_MAP.put(key, Lists.newArrayList(data));
            }
        }
    }
}

一张图总结

相关推荐
想你依然心痛18 小时前
大数据时代时序数据库选型指南:Apache IoTDB 的实战进阶与避坑法则
大数据·apache·时序数据库
接着奏乐接着舞19 小时前
gateway
gateway
李白你好20 小时前
ActiveMQ-EXPtools支持检测和利用Apache ActiveMQ漏洞
apache·activemq
俺爱吃萝卜2 天前
开源贡献指南:如何给Apache或Linux内核提PR?
linux·开源·apache
一个public的class2 天前
前后端 + Nginx + Gateway + K8s 全链路架构图解
前端·后端·nginx·kubernetes·gateway
uNke DEPH3 天前
SpringCloud Gateway 集成 Sentinel 详解 及实现动态监听Nacos规则配置实时更新流控规则
spring cloud·gateway·sentinel
ERBU DISH3 天前
当遇到 502 错误(Bad Gateway)怎么办
gateway
Ulyanov3 天前
Apache Kafka在雷达仿真数据流处理中的应用
分布式·python·kafka·apache·雷达电子战
anzhxu4 天前
防火墙安全策略(基本配置)
服务器·php·apache
小郑加油4 天前
python学习Day6-7天:条件判断与基本综合应用
java·服务器·apache