Soul网关(15) - ip端口探活

667 阅读2分钟

话不多说,上来就干

路由注册注册

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;
    }
``