接着上一篇,继续解析一下 HttpLongPollingDataChangedListener 类。
- org.dromara.soul.admin.listener.http.HttpLongPollingDataChangedListener
1、提供给网关测,负责长轮询调用。
// 执行http长轮询
public void doLongPolling(final HttpServletRequest request, final HttpServletResponse response) {
......
// AsyncContext.settimeout() does not timeout properly, so you have to control it yourself
asyncContext.setTimeout(0L);
// block client's thread,执行一个线程任务
scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT));
}
2、创建并执行在给定延迟后启用的一次性操作,这里延迟60s
// 延迟执行
this.asyncTimeoutFuture = scheduler.schedule(() -> {
clients.remove(LongPollingClient.this);
List<ConfigGroupEnum> changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest());
sendResponse(changedGroups);
}, timeoutTime, TimeUnit.MILLISECONDS);
3、在类加载完成后,调用此方法执行定时任务,获取数据库中的数据刷新到缓存,初始化需要执行。
@Override
protected void afterInitialize() {
long syncInterval = httpSyncProperties.getRefreshInterval().toMillis();
// Periodically check the data for changes and update the cache
// 检查数据库的数据变化,并更新至内存,这里的定时任务是5分钟执行一次
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);
}
private void refreshLocalCache() {
this.updateAppAuthCache();
this.updatePluginCache();
this.updateRuleCache();
this.updateSelectorCache();
this.updateMetaDataCache();
}
4、通过事件触发 afterAppAuthChanged 执行,来解析一下执行逻辑:
先看一下类的继承关系:
1)、后端更改插件信息后,会发布一个变更事件
2)、DataChangedListener 监听到事件后会执行 afterAppAuthChanged 方法
// org.dromara.soul.admin.service.impl.PluginServiceImpl
// 发布数据变更事件
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType,
Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO))));
// 监听事件,执行此方法
@Override
protected void afterAppAuthChanged(final List<AppAuthData> changed, final DataEventTypeEnum eventType) {
scheduler.execute(new DataChangeTask(ConfigGroupEnum.APP_AUTH));
}
5、事件变更的执行由DataChangeTask来执行,此类会通过创建线程来执行。根据 groupKey 从 BlockingQueue 阻塞队列中取出client,异步通知某个网关测client发生了数据变更。
/**
* When a group's data changes, the thread is created to notify the client asynchronously.
*/
class DataChangeTask implements Runnable {
......
DataChangeTask(final ConfigGroupEnum groupKey) {
this.groupKey = groupKey;
}
@Override
public void run() {
for (Iterator<LongPollingClient> iter = clients.iterator(); iter.hasNext();) {
LongPollingClient client = iter.next();
iter.remove();
client.sendResponse(Collections.singletonList(groupKey));
log.info("send response with the changed group,ip={}, group={}, changeTime={}", client.ip, groupKey, changeTime);
}
}
}
6、总结一下 HttpLongPollingDataChangedListener 执行的一些功能
- 1)、初始化,soul-admin 启动后会读取数据库网关数据同步值内存,例如:
afterInitialize() --> refreshLocalCache() --> updateAppAuthCache()
-
2)、执行http长轮询处理
-
1)阻塞 soul-bootstrap 长轮询调用,等待数据变更进行响应,否则超时返回。
-
2)监听数据变更事件,收到变更事件后更新缓存并响应给 soul-bootstrap。
-
7、总结一下用到的技术点
- ApplicationEventPublisher 、ApplicationListener
- InitializingBean
- ScheduledThreadPoolExecutor
- ThreadPoolExecutor
- ReentrantLock
- AtomicBoolean
- BlockingQueue
- ConcurrentHashMap
官网:soul 数据同步原理