话不多说,上来就干
路由注册注册
admin服务的 UpstreamCheckService 类,是被 @Component 修饰的业务层,作为bean注入,有一个 setUp() 方法
// 注解 @PostConstruct 表示在服务启动时,执行该方法且只执行一次
@PostConstruct
public void setup() {
//从database获取插件具体的选择器数据,放入map中
PluginDO pluginDO = pluginMapper.selectByName(PluginEnum.DIVIDE.getName());
if (pluginDO != null) {
List<SelectorDO> selectorDOList = selectorMapper.findByPluginId(pluginDO.getId());
for (SelectorDO selectorDO : selectorDOList) {
List<DivideUpstream> divideUpstreams = GsonUtils.getInstance().fromList(selectorDO.getHandle(), DivideUpstream.class);
if (CollectionUtils.isNotEmpty(divideUpstreams)) {
UPSTREAM_MAP.put(selectorDO.getName(), divideUpstreams);
}
}
}
// check= @Value("${soul.upstream.check:true}") 开启定时,时间间隔需要自行配置,如果有数据更新,定时获取数据库数据刷到缓存
if (check) {
new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), SoulThreadFactory.create("scheduled-upstream-task", false))
.scheduleWithFixedDelay(this::scheduled, 10, scheduledTime, TimeUnit.SECONDS);
}
}
check()方法提供了再次同步数据库数据和缓存数据的功能
private void check(final String selectorName, final List<DivideUpstream> upstreamList) {
//构建空列表
List<DivideUpstream> successList = Lists.newArrayListWithCapacity(upstreamList.size());
// 遍历已知节点的url
for (DivideUpstream divideUpstream : upstreamList) {
// 探活方法
final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl());
if (pass) {
if (!divideUpstream.isStatus()) {
divideUpstream.setTimestamp(System.currentTimeMillis());
divideUpstream.setStatus(true);
log.info("UpstreamCacheManager check success the url: {}, host: {} ", divideUpstream.getUpstreamUrl(), divideUpstream.getUpstreamHost());
}
successList.add(divideUpstream);
} else {
divideUpstream.setStatus(false);
log.error("check the url={} is fail ", divideUpstream.getUpstreamUrl());
}
}
if (successList.size() == upstreamList.size()) {
return;
}
// 更新map
if (successList.size() > 0) {
UPSTREAM_MAP.put(selectorName, successList);
updateSelectorHandler(selectorName, successList);
} else {
UPSTREAM_MAP.remove(selectorName);
updateSelectorHandler(selectorName, null);
}
}
除了更新缓存map外,还通过发布事件,更新数据更新事件
private void updateSelectorHandler(final String selectorName, final List<DivideUpstream> upstreams) {
SelectorDO selector = selectorService.findByName(selectorName);
if (Objects.nonNull(selector)) {
SelectorData selectorData = selectorService.buildByName(selectorName);
if (upstreams == null) {
selector.setHandle("");
selectorData.setHandle("");
} else {
String handler = GsonUtils.getInstance().toJson(upstreams);
selector.setHandle(handler);
selectorData.setHandle(handler);
}
selectorMapper.updateSelective(selector);
// publish change event.
// 发布数据变更事件
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE,
Collections.singletonList(selectorData)));
}
}
ip探活
在探活方法 UpstreamCheckUtils.checkUrl(...)中,具体执行了探活的逻辑
public static boolean checkUrl(final String url) {
if (StringUtils.isBlank(url)) {
return false;
}
if (checkIP(url)) {
String[] hostPort;
//拆分节点地址
if (url.startsWith(HTTP)) {
final String[] http = StringUtils.split(url, "\\/\\/");
hostPort = StringUtils.split(http[1], Constants.COLONS);
} else {
hostPort = StringUtils.split(url, Constants.COLONS);
}
// 入参ip和端口
return isHostConnector(hostPort[0], Integer.parseInt(hostPort[1]));
} else {
return isHostReachable(url);
}
}
最终执行的是 isHostConnector(...)方法,通过
private static boolean isHostConnector(final String host, final int port) {
try (Socket socket = new Socket()) {
// new一个socket,直接建立连接,看是否异常
socket.connect(new InetSocketAddress(host, port));
} catch (IOException e) {
return false;
}
return true;
}
``