当我们在 soul-admin 更改 selector 或者 rule 时,数据是怎么同步到网关内存中的呢。

我们知道 soul 网关有四种数据同步的方式,http 长轮询,websocketzookeepernacos,数据同步处理就是在这些 listener 里进行的,默认使用 websocket 同步。今天也主要是看 websocket 同步方式。

以更新 selector 为例,执行过程是在 publishEvent 方法。

在这里插入图片描述

这里使用 eventPublisher (也就是ApplicationEventPublisherspring 自带的一种事件发布机制) 发布一个 DataChangedEvent 事件。

DataChangedEventDispatcher 这个类实现了 ApplicationListener 接口,就可以接收到这个事件,收到事件后会执行 onApplicationEvent 方法,这里我们修改的是 SELECTOR,它就会去执行 listeneronSelectorChanged 方法,onSelectorChanged 方法会通过 websocket 向客户端发送消息,通知数据变更,客户端收到消息就把变更的数据更新到 JVM 内存中。

@Component
public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {

    private ApplicationContext applicationContext;

    private List<DataChangedListener> listeners;

    public DataChangedEventDispatcher(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @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());
            }
        }
    }

    @Override
    public void afterPropertiesSet() {
        Collection<DataChangedListener> listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
        this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans));
    }

}

debug 到这里时,listeners 是有值的,就是我们需要的 WebsocketDataChangedListener,那这个 listeners 的值怎么来的呢?这是在 soul-admin 启动时就已经赋值了,在 afterPropertiesSet 方法里赋值的。
在这里插入图片描述

soul-adminapplication-local.yml 文件里配置了 soul.sync.websocket.enabled = true
在这里插入图片描述这里满足了 @ConditionalOnProperty 条件,所以加载 DataSyncConfiguration 这个类时,就会把以下三个 bean 注册进来。

DataChangedEventDispatcher 类还实现了 InitializingBean 接口,这个接口里只有一个 afterPropertiesSet 方法,那么启动项目时,这个类的构造函数初始化后,就会执行这个方法,拿到我们配置的数据同步 listenerlisteners 就赋值成功啦。

看这段源码的时候,我遇到最大的问题就是想不通 DataChangedEventDispatcher 类的 listeners 是什么时候赋值的,我加了很多断点,但编辑 selector的时候,就是进不到 afterPropertiesSet 方法里的断点。虽然我也找到了 DataSyncConfiguration 这个类,看到了上图的那些代码,但当时就是想不明白。后来也不知道怎么了,我突然想到了 @ConditionalOnProperty 这个注解的作用,这个条件成立,就会把这个 bean 注册进来,也搜了下 InitializingBean 这个接口,才意识到启动 soul-admin 的时候,就已经给 listeners 赋值了。

就这很小的一个模块,我也学到了很多东西,@ConditionalOnProperty 这个注解虽然知道,但一直没用过,所以当时看到没反应过来,从这里我也学到了一个新用法。还有 spring 的事件发布机制,这个也很实用,代码里也可以用起来。

本来今天想把 websocket 如何发送接收也写了的,但光写这么多都花了两个多小时,就明天再写吧。