soul网关学习15-插件实现1-Divide1-后端节点探活

前面几篇我们重点去挖掘了网关配置数据的同步,接下来我们会去分析soul网关的插件体系,就开始吧。

前言

我们知道网关最核心的能力是进行http请求的转发。那在我们的soul网关中又是如何实现这一功能的?这里的实现就是我们今天要分析的主题divide插件。

分析

  1. 先从configuration入手,找到divide插件的配置类DividePluginConfiguration
    DividePluginConfiguration
  2. 从上图中可以看到配置类创建了几个关键的bean。我们先来分析DividePlugin,核心逻辑doExecute
    protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
        // context模式
        final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
        assert soulContext != null;
        // 获取分流规则handle
        final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class);
        // 根据当前选择器获取到后台节点list
        // TODO 需分析后台节点获取的实现,会涉及到后台节点的探活,增加活剔除节点
        final List<DivideUpstream> upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId());
        // 如果获取到的后台节点为空,则直接返回
        if (CollectionUtils.isEmpty(upstreamList)) {
            log.error("divide upstream configuration error: {}", rule.toString());
            Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        // 获取ip
        final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
        // 根据远程客户端ip获取后台节点,经过负载均衡策略
        // TODO 需分析负载均衡的实现
        DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);
        // 经过负载均衡策略后,未选择到节点则返回错误
        if (Objects.isNull(divideUpstream)) {
            log.error("divide has no upstream");
            Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);
            return WebFluxResultUtils.result(exchange, error);
        }
        // set the http url
        // 根据后台节点对象构建服务地址,并将其放于交换器,传递下去,进行转发
        String domain = buildDomain(divideUpstream);
        String realURL = buildRealURL(domain, soulContext, exchange);
        // 调用的几个关键参数:服务地址 请求参数 超时 重试次数
        exchange.getAttributes().put(Constants.HTTP_URL, realURL);
        // set the http timeout
        exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout());
        exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry());
        return chain.execute(exchange);
    }
DividePlugin.doExecute
  1. 从上图中我们可以继续跟踪两个子逻辑【后端节点列表的获取】以及【负载均衡算法】

后端节点列表的获取

soul-bootstrap后端节点探活

  1. UpstreamCacheManager实现,其中后端节点探活的结构图如下
    UpstreamCacheManager.check
  2. 我们来分解一下这个图
    • 存放后端节点数据有两个 mapUPSTREAM_MAPUPSTREAM_MAP_TEMP。至于为什么会有两个map,主要是前者会存储所有的后端节点,由soul-admin那边同步过来,不管其是否存活;而后者则只存储探活成功的节点,为网关负载的有效节点。
    • 还有个scheduler,该scheduler开启了一个线程scheduled-upstream-task执行调度任务scheduled,默认探活调度的间隔为30s,建议设置为1s,需手设置
    • scheduled逻辑是遍历所有后端节点UPSTREAM_MAP,通过挨个探活check,完成有效后端节点UPSTREAM_MAP_TEMP的替换与剔除
    • 这里探活check的实现方式:后端节点upstream服务地址url是否为ip,为ip则用ip+port建立socket连接进行探测;否则直接判断节点host是否可达;采用socket方式探活时没有显式的超时设置,而host是否可达则会1s超时
  3. 以上只是分析了upstream的探活实现,当然还有所有后端节点UPSTREAM_MAP数据的初始化与变更
  4. 这个逻辑就比较简单,通过公共的数据变更机制handler,在选择器selector配置发生变化的时候,进行相应选择器后端节点列表的添加add与移除remove

补充soul-admin端的后端节点探活

  1. 关键类UpstreamCheckService,我们看到这个类在实例化之后的时候就会去执行后端节点探活的逻辑
    @PostConstruct
    public void setup() {
        // 从数据库中获取到所有的后端节点数据,初始逻辑
        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);
                }
            }
        }
        // 如果探活开启,则会执行探活,可配置,默认是开启的
        if (check) {
            // 开启调度线程池默认每10s执行一次check
            new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), SoulThreadFactory.create("scheduled-upstream-task", false))
                    .scheduleWithFixedDelay(this::scheduled, 10, scheduledTime, TimeUnit.SECONDS);
        }
    }
  1. 从上得知启动过程会开启调度线程池,默认每10s执行后端探活的逻辑scheduled方法,探活线程的个数为cpu的核心数
  2. 这里对后端节点的探活逻辑与上述soul-bootstrap端类似,只是在得到存活节点的list之后,会判断是否存在后端节点数目的变化(这里的探活不会增加新节点,只有可能剔除一些不存活的节点,所以只要存在存活节点数与所有节点不一致的情况,则就发生了变更
  3. 如果存在变化,则会执行updateSelectorHandler的逻辑,该逻辑主要会做两件事情:
    • 将存活节点list更新到soul-admin数据库,保存起来
    • 同时发布selector配置变更的事件给到soul-bootstrapsoul-bootstrap端就会更新其后端节点的数据,完成探活节点的同步

负载均衡算法

下篇讲解,To be contined...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容