【高性能网关soul学习】5. soul-admin 数据配置变更及 Websocket 方式数据同步流程

【高性能网关soul学习】5. soul-admin 数据配置及Websocket 方式数据同步

本文主要目标:
本篇是对admin后台操作数据以后,同步到网关的流程介绍


  • 用户可以在 soul-admin 后台任意修改数据,并马上同步到网关的jvm内存中。
  • 同步soul的插件数据,选择器,规则数据,元数据,签名数据等等。
  • 所有插件的选择器,规则都是动态配置,立即生效,不需要重启服务。
  • 数据配置的流程图如下所示:

【高性能网关soul学习】5. soul-admin 数据配置变更及 Websocket 方式数据同步流程_第1张图片


打开浏览器的检查选项查看请求,然后访问admin控制台的插件列表,我们可以看到选择器的相关请求都包含 /selector ,到源码项目中全局搜索可以发现,具体的实现为SelectorController.java 类作为入口。在此查看该类所在的目录,我们可以发现admin项目的所有controller的,这里作为crud的入口。
【高性能网关soul学习】5. soul-admin 数据配置变更及 Websocket 方式数据同步流程_第2张图片

这里主要简单介绍下和 网关服务soul-bootstrap 相关的几个配置项

  1. ConfigController:处理长轮询的数据同步
  2. MetaDataController:处理元数据的crud
  3. PluginController:处理插件的crud
  4. PluginHandleController:系统管理>>>插件处理管理 的crud
  5. SelectorController:选择器的crud
  6. RuleController:选择器下属规则的crud
  7. SoulClientController:之前分析过的,处理被代理服务器的注册信息
  8. SoulDictController:字典的crud

MetaDataController、PluginController、SelectorController、RuleController、SoulClientController等进行更新和创建之后,都会发送一个数据变更事件DataChangedEvent 事件。例如:

public class DataChangedEvent extends ApplicationEvent {
     
  	// 指定数据变更类型:是插件、选择器、规则还是其他类型数据变更
    private ConfigGroupEnum groupKey;
  	// 指定数据变更的动作:新增还是更新还是删除
    private DataEventTypeEnum eventType;
  	// 父类EventObject中,用来传输具体的变更数据
  	protected transient Object  source;
}

eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, DataEventTypeEnum.UPDATE,
                Collections.singletonList(RuleDO.transFrom(ruleDO, pluginDO.getName(), conditionDataList))));

全局搜索 ApplicationListener 我们可以发现,具体的实现类为DataChangedEventDispatcher

public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {
     
  
	  private List<DataChangedListener> listeners;
  
    @Override
    @SuppressWarnings("unchecked")
    public void onApplicationEvent(final DataChangedEvent event) {
     
        for (DataChangedListener listener : listeners) {
     
            switch (event.getGroupKey()) {
     
                case APP_AUTH:
                    listener.onAppAuthChanged((List<AppAuthData>) event.getSource(), event.getEventType());
                    break;
                case PLUGIN:
                    listener.onPluginChanged((List<PluginData>) event.getSource(), event.getEventType());
                    break;
                case RULE:
                    listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());
                    break;
                case SELECTOR:
                    listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());
                    break;
                case META_DATA:
                    listener.onMetaDataChanged((List<MetaData>) event.getSource(), event.getEventType());
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
            }
        }
    }
}

其中DataChangedListener的实现类有四种,默认情况下是使用的WebsocketDataChangedListener进行数据同步

  1. WebsocketDataChangedListener
  2. HttpLongPollingDataChangedListener
  3. NacosDataChangedListener
  4. ZookeeperDataChangedListener
soul
  sync:
    websocket:
      enabled: true
#      zookeeper:
#          url: localhost:2181
#          sessionTimeout: 5000
#          connectionTimeout: 2000
#      http:
#        enabled: true
  #    nacos:
  #      url: localhost:8848
  #      namespace: 1c10d748-af86-43b9-8265-75f487d20c6c
  #      acm:
  #        enabled: false
  #        endpoint: acm.aliyun.com
  #        namespace:
  #        accessKey:
  #        secretKey:

下面对于默认的Websocket为例,简单介绍下

WebsocketDataChangedListener
  • 以选择器数据变更事件为例,处理的时候首先将json转换成 WebsocketData 的内部封装数据,然后调用 WebsocketCollector 发送更新消息
  • 一般的更新事件(除MYSELF类型之外),会遍历注册在 SESSION_SET 中的 Websocket 的 session,然后send消息,同步到 soul-bootstrap
listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());


@Override
    public void onSelectorChanged(final List<SelectorData> selectorDataList, final DataEventTypeEnum eventType) {
     
        WebsocketData<SelectorData> websocketData =
                new WebsocketData<>(ConfigGroupEnum.SELECTOR.name(), eventType.name(), selectorDataList);
        WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType);
    }

public class WebsocketCollector {
     
  if (StringUtils.isNotBlank(message)) {
     
            if (DataEventTypeEnum.MYSELF == type) {
     
                try {
     
                    Session session = (Session) ThreadLocalUtil.get(SESSION_KEY);
                    if (session != null) {
     
                        session.getBasicRemote().sendText(message);
                    }
                } catch (IOException e) {
     
                    log.error("websocket send result is exception: ", e);
                }
                return;
            }
            for (Session session : SESSION_SET) {
     
                try {
     
                    session.getBasicRemote().sendText(message);
                } catch (IOException e) {
     
                    log.error("websocket send result is exception: ", e);
                }
            }
        }
}

  • soul-bootstrap 即soul网关这边会启动则会启动 WebsocketSyncDataService 来处理数据同步的服务
    • 每隔30秒会进行一次 websocket 的心跳检测,如果断开了,则进行重连
executor.scheduleAtFixedRate(() -> {
     
                    try {
     
                        if (client.isClosed()) {
     
                            boolean reconnectSuccess = client.reconnectBlocking();
                            if (reconnectSuccess) {
     
                                log.info("websocket reconnect is successful.....");
                            } else {
     
                                log.error("websocket reconnection is error.....");
                            }
                        }
                    } catch (InterruptedException e) {
     
                        log.error("websocket connect is error :{}", e.getMessage());
                    }
                }, 10, 30, TimeUnit.SECONDS);
}
  • soul-bootstrap端的 其中 client 类型为 SoulWebsocketClient ,实际上收到event时就是进行反序列化,然后让websocketDataHandler进行处理

    • websocketDataHandler实际上就是一个枚举工厂,根据数据的groupEnum类型,选择实际的 DataHandler
    • websocketDataHandler 实例化的时候还把 DataSubscriber 注入到了DataHandler中,当DataHandler处理事件时,回调DataSubscriber来进行实际的处理

    @Override
    public void onMessage(final String result) {
     
        handleResult(result);
    }

    @SuppressWarnings("ALL")
    private void handleResult(final String result) {
     
        WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class);
        ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType());
        String eventType = websocketData.getEventType();
        String json = GsonUtils.getInstance().toJson(websocketData.getData());
        websocketDataHandler.executor(groupEnum, json, eventType);
    }

public class WebsocketDataHandler {
     

    private static final EnumMap<ConfigGroupEnum, DataHandler> ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class);

    public WebsocketDataHandler(final PluginDataSubscriber pluginDataSubscriber,
                                final List<MetaDataSubscriber> metaDataSubscribers,
                                final List<AuthDataSubscriber> authDataSubscribers) {
     
        ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataHandler(pluginDataSubscriber));
        ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataHandler(pluginDataSubscriber));
        ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataHandler(pluginDataSubscriber));
        ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AuthDataHandler(authDataSubscribers));
        ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataHandler(metaDataSubscribers));
    }

    public void executor(final ConfigGroupEnum type, final String json, final String eventType) {
     
        ENUM_MAP.get(type).handle(json, eventType);
    }
}
  • Datahandler 这里使用了模板方法模式,抽象类AbstractDataHandler定义了根据事件类型处理的框架,然后 PluginDataHandler、SelectorDataHandler、RuleDataHandler、AuthDataHandler、MetaDataHandler 只需要是实现 doRefresh、doUpdate、doDelette等方法即可

    public abstract class AbstractDataHandler<T> implements DataHandler {
           
    
        protected abstract List<T> convert(String json);
    
        protected abstract void doRefresh(List<T> dataList);
    
        protected abstract void doUpdate(List<T> dataList);
    
        protected abstract void doDelete(List<T> dataList);
    
        @Override
        public void handle(final String json, final String eventType) {
           
            List<T> dataList = convert(json);
            if (CollectionUtils.isNotEmpty(dataList)) {
           
                DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType);
                switch (eventTypeEnum) {
           
                    case REFRESH:
                    case MYSELF:
                        doRefresh(dataList);
                        break;
                    case UPDATE:
                    case CREATE:
                        doUpdate(dataList);
                        break;
                    case DELETE:
                        doDelete(dataList);
                        break;
                    default:
                        break;
                }
            }
        }
    }
    
  • 以SelectorDataHandler的都doUpdate为例继续分析,进入到SelectorDataHandler,可以看到该类主要就是对 pluginDataSubscriber进行的回调操作

    @Override
    protected void doUpdate(final List<SelectorData> dataList) {
           
        dataList.forEach(pluginDataSubscriber::onSelectorSubscribe);
    }
    
  • 这里进入到 PluginDataSubscriber,查看源码可以看到实际上是在CommonPluginDataSubscriber 中处理的

// CommonPluginDataSubscriber.java
private final Map<String, PluginDataHandler> handlerMap;

private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
     
        Optional.ofNullable(classData).ifPresent(data -> {
     
          // 先忽略别的逻辑,只看选择器数据的操作
            if (data instanceof SelectorData) {
     
                SelectorData selectorData = (SelectorData) data;
                if (dataType == DataEventTypeEnum.UPDATE) {
     
                  	// 修改了缓存 BaseDataCache
                    BaseDataCache.getInstance().cacheSelectData(selectorData);
                  	// 然后根据插件名称,找到插件处理器 PluginDataHandler 处理选择器的更新数据
                    Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData));
                } else if (dataType == DataEventTypeEnum.DELETE) {
     
                    BaseDataCache.getInstance().removeSelectData(selectorData);
                    Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.removeSelector(selectorData));
                }
            } 
        });
    }

总结

本文简单介绍了以下几点:

  1. admin 部分 controller的功能
  2. 然后跟踪了在websocket 配置下的 soul-admin 和 soul-bootstrap 的数据同步机制

你可能感兴趣的:(Soul,java,spring,网关,设计模式)