soul源码解读(七)-- 数据同步之http长轮询初体验

17 篇文章 0 订阅

数据同步之http长轮询-admin端

官网介绍

zookeeper、websocket 数据同步的机制比较简单,而 http 同步会相对复杂一些。Soul 借鉴了 Apollo、Nacos 的设计思想,取其精华,自己实现了 http 长轮询数据同步功能。注意,这里并非传统的 ajax 长轮询!

下面是 soul 官网上的图
在这里插入图片描述
http 长轮询机制如上所示,soul-web 网关请求 admin 的配置服务,读取超时时间为 90s,意味着网关层请求配置服务最多会等待 90s,这样便于 admin 配置服务及时响应变更数据,从而实现准实时推送。

http 请求到达 sou-admin 之后,并非立马响应数据,而是利用 Servlet3.0 的异步机制,异步响应数据。首先,将长轮询请求任务 LongPollingClient 扔到 BlocingQueue 中,并且开启调度任务,60s 后执行,这样做的目的是 60s 后将该长轮询请求移除队列,即便是这段时间内没有发生配置数据变更。因为即便是没有配置变更,也得让网关知道,总不能让其干等吧,而且网关请求配置服务时,也有 90s 的超时时间。

启动服务

我们启动服务,来看下这套机制是怎么运行的。

1.修改 admin 同步配置,启动服务

  sync:
      http:
        enabled: true

2.修改 bootstrap 同步配置

    sync:
        http:
             url : http://localhost:9095

3.然后看下 pom 文件里有没有 http 同步的 jar,然后启动 bootstrap 服务

<!--soul data sync start use http-->
        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>soul-spring-boot-starter-sync-data-http</artifactId>
            <version>${project.version}</version>
        </dependency>
<!-- soul sign plugin start-->

我们可以在 admin 启动日志里看到这么一句

2021-01-21 23:33:33.296  INFO 18328 --- [           main] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh interval: 300000ms

然后找到这句日志打印的地方

    // HttpLongPollingDataChangedListener.java
    protected void afterInitialize() {
        long syncInterval = httpSyncProperties.getRefreshInterval().toMillis();
        // Periodically check the data for changes and update the cache
        // 设置一个延迟执行的线程池,刷新本地缓存
        scheduler.scheduleWithFixedDelay(() -> {
            log.info("http sync strategy refresh config start.");
            try {
                this.refreshLocalCache();
                log.info("http sync strategy refresh config success.");
            } catch (Exception e) {
                log.error("http sync strategy refresh config error!", e);
            }
        }, syncInterval, syncInterval, TimeUnit.MILLISECONDS);
        log.info("http sync strategy refresh interval: {}ms", syncInterval);
    }

然后我们找到了一个非常关键的类 HttpLongPollingDataChangedListener ,看名字就知道这个类应该就是 http 长轮询的类。

在我写文章的时候,看到控制台又打了日志

2021-01-21 23:38:33.296  INFO 18328 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config start.
...
2021-01-21 23:38:33.476  INFO 18328 --- [-long-polling-2] a.l.h.HttpLongPollingDataChangedListener : http sync strategy refresh config success.

很明显可以看出,这段日志和上面的日志相差5分钟,说明 admin 会每隔5分钟从数据库刷新一次配置。正好对应了上面那段代码。

然后我们往上找,看下 afterInitialize() 是重写了 AbstractDataChangedListener 里的方法,afterPropertiesSet()里面调用了 afterInitialize() ,AbstractDataChangedListener 又实现了 InitializingBean。
它们的继承关系如下:
在这里插入图片描述
然后我们继续找下 HttpLongPollingDataChangedListener 是什么被注册的。

关键代码如下:

// DataSyncConfiguration.java
@Configuration
    @ConditionalOnProperty(name = "soul.sync.http.enabled", havingValue = "true")
    @EnableConfigurationProperties(HttpSyncProperties.class)
    static class HttpLongPollingListener {
        @Bean
        @ConditionalOnMissingBean(HttpLongPollingDataChangedListener.class)
        public HttpLongPollingDataChangedListener httpLongPollingDataChangedListener(final HttpSyncProperties httpSyncProperties) {
            return new HttpLongPollingDataChangedListener(httpSyncProperties);
        }

    }

程序在启动的时候,会先去加载 DataSyncConfiguration (这个类配置了 @Configuration),然后会执行上面的代码,判断 soul.sync.http.enabled = true 的话,就初始化一个 HttpLongPollingDataChangedListener ,HttpLongPollingDataChangedListener 初始化完成之后,就会调用上面的 afterInitialize() 。

4.在 admin 后台更新一个插件的状态,然后 debug 跟踪下调用链。

admin 端同步数据流程

PluginServiceImpl
DataChangedEventDispatcher
DataChangedListener
AbstractDataChangedListener
HttpLongPollingDataChangedListener
// HttpLongPollingDataChangedListener.java
protected void afterPluginChanged(final List<PluginData> changed, final DataEventTypeEnum eventType) {
        scheduler.execute(new DataChangeTask(ConfigGroupEnum.PLUGIN));
    }

接着我们看下 DataChangeTask的 run()

public void run() {
            for (Iterator<LongPollingClient> iter = clients.iterator(); iter.hasNext();) {
                //其他代码省略
                client.sendResponse(Collections.singletonList(groupKey));
            }
        }

LongPollingClient 实现了 runable ,我们继续看里面的 run()

public void run() {
            this.asyncTimeoutFuture = scheduler.schedule(() -> {
                clients.remove(LongPollingClient.this);
                List<ConfigGroupEnum> changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest());
                sendResponse(changedGroups);
            }, timeoutTime, TimeUnit.MILLISECONDS);
            clients.add(this);
        }

可以看到上面也是创建一个延迟线程池去获取配置数据
我们接着往下看compareChangedGroup()

private List<ConfigGroupEnum> compareChangedGroup(final HttpServletRequest request) {
            //其他代码省略
            if (this.checkCacheDelayAndUpdate(serverCache, clientMd5, clientModifyTime)) {
            	//如果数据需要更新,就返回更新数据
                changedGroup.add(group);
            }
        }
        return changedGroup;
    }

接下来 checkCacheDelayAndUpdate 是重点,我们下一篇接着分析。

总结

今天主要了解了下官网介绍的 http 长轮询机制

然后自己启动服务,了解了 admin 端发送消息的流程。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值