【ソウルゲートウェイ秘匿】divideプラグインの原理

9469 ワード

一、プラグインの概要
プラグインの位置付け
divideプラグインはhttp順方向プロキシプラグインであり、http要求はすべてこのプラグインによって負荷均衡処理が行われます。
発効のタイミング
要求ヘッダのrpcType=httpでプラグインがオンになると、要求パラメータに従って規則に一致し、最終的に下流のプラグインによって応答式プロキシ呼び出しを行います。
二、プラグイン処理フロー
1)まず要求された処理類プラグインの汎用フローを振り返ってみます。
public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
    //       
    String pluginName = named();
    final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
    if (pluginData != null && pluginData.getEnabled()) {
        //        
        final Collection selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
        ...
        //      
        final SelectorData selectorData = matchSelector(exchange, selectors);
        ...
        //       
        final List rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
        ...
        //     
        RuleData rule;
        if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
            //get last
            rule = rules.get(rules.size() - 1);
        } else {
            rule = matchRule(exchange, rules);
        }
        ...
        //        
        return doExecute(exchange, chain, selectorData, rule);
    }
    //          
    return chain.execute(exchange);
}
AbstractSoulPluginは、まず対応するセレクタとルールにマッチし、プラグインのカスタム処理を実行する。
2)divideプラグインのカスタム処理の流れを見に来てください。
protected Mono doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
    ...
    //         (    :       、          )
    final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class);
    //               
    final List upstreamList = UpstreamCacheManager.getInstance().findUpstreamListBySelectorId(selector.getId());
    ...
    //            ip(    )
    final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
    DivideUpstream divideUpstream = LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);
    ...
    //   http url、          
    String domain = buildDomain(divideUpstream);
    String realURL = buildRealURL(domain, soulContext, exchange);
    exchange.getAttributes().put(Constants.HTTP_URL, realURL);
    exchange.getAttributes().put(Constants.HTTP_TIME_OUT, ruleHandle.getTimeout());
    exchange.getAttributes().put(Constants.HTTP_RETRY, ruleHandle.getRetry());
    //          
    return chain.execute(exchange);
}
DividePluginは、セレクタに対応する対応サービス一覧を取得してから、配信される対象サーバインスタンスipを負荷等化して選択し、最終的なurl、タイムアウト時間、および再試行回数を設定して、プラグインチェーンの下流で処理を行う。
注意:
divideプラグイン自体は、セレクタ、規則および負荷バランスポリシーに従って配信されるサーバのインスタンスを選択するだけであり、バックエンドサービスに直接http要求を開始しない。
三、ホストの活動
上に述べたように、divideはサービスリストを取得する必要があります。取得の実現を見てください。
public List findUpstreamListBySelectorId(final String selectorId) {
    return UPSTREAM_MAP_TEMP.get(selectorId);
}
内部はUSTREAM_を通ります。地図TEMPは生存サービス一覧を取得します。
UpsteamCacheManagerの内部は2つの発散リストを維持しました。
  • USTREAM_MAP:フルボリュームのサービス分散リストは、フル量の上流サービス情報を保存する責任があり、keyはセレクタIDであり、valueは同じセレクタを使用するサービスリストである。
  • USTREAM_地図TEMP:一時サービス分散リストで、活動の上流サービス情報を保存します。keyはセレクタID、valueは同じセレクタを使用するサービスリストです。
  • 前の章で述べたように、データ同期時に、submit方法は同時にUSTREAM_を更新しました。MAPとUP STREAM_地図TEMPですが、その後のサービスはどうやってUSTREAM_を維持しますか?地図TEMPは、すべてip探知から話さなければなりません。
    3.1仕事のタイミングを探る
    仕事を探すタイミングはUpsteamCacheManagerから初期化して言わなければなりません。
    private UpstreamCacheManager() {
        //       
        boolean check = Boolean.parseBoolean(System.getProperty("soul.upstream.check", "false"));
        if (check) {
            //         
            new ScheduledThreadPoolExecutor(1, SoulThreadFactory.create("scheduled-upstream-task", false))
                    .scheduleWithFixedDelay(this::scheduled,
                            30, Integer.parseInt(System.getProperty("soul.upstream.scheduledTime", "30")), TimeUnit.SECONDS);
        }
    }
    Upstream CacheManagerが初期化された時に、探知スイッチがオンになるとタイミングサーチタスクが作成されます。ここではデフォルトで30秒に一回実行されます。
    ここでは2つの構成パラメータに関連します。
  • soul.upstream.cchockの活動スイッチ:デフォルトはtureで、falseに設定して
  • を検出しないことを表します。
  • soul.up stream.scheduledTime偵察時間間隔、デフォルト10秒
  • 3.2仕事を探す
    1)次は偵察任務の実現を見てみます。
    private void scheduled() {
        if (UPSTREAM_MAP.size() > 0) {
            UPSTREAM_MAP.forEach((k, v) -> {
                //     
                List result = check(v);
                if (result.size() > 0) {
                    UPSTREAM_MAP_TEMP.put(k, result);
                } else {
                    UPSTREAM_MAP_TEMP.remove(k);
                }
            });
        }
    }
    タスクは、プログレッシブ登録されたフルサービスのリストを巡回し、サービスの活性を確認することに責任を負います。
  • 生存数が0より大きい場合、生存サービス一覧
  • を更新する。
  • そうでなければ、生存サービス一覧の対応する内容を削除する

  • 2)サービスリストの活性チェック処理を継続して見ます。
    private List check(final List upstreamList) {
        List resultList = Lists.newArrayListWithCapacity(upstreamList.size());
        for (DivideUpstream divideUpstream : upstreamList) {
            //       
            final boolean pass = UpstreamCheckUtils.checkUrl(divideUpstream.getUpstreamUrl());
            if (pass) {
                //       
                if (!divideUpstream.isStatus()) {
                    divideUpstream.setTimestamp(System.currentTimeMillis());
                    divideUpstream.setStatus(true);
                    ...
                }
                //        
                resultList.add(divideUpstream);
            } else {
                //       
                divideUpstream.setStatus(false);
                ...
            }
        }
        return resultList;
    }
    サービスリストを巡回して、urlによって各サービスの活性を検査して、生存のサービスを登録します。
    3.3活性検査
    1)サービス活性検査の実現(Upsteam CheckUtils菗checkUrl):
    public static boolean checkUrl(final String url) {
        ...
        //   url   ip+    
        if (checkIP(url)) {
            //    ip    
            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);
            }
            //          
            return isHostConnector(hostPort[0], Integer.parseInt(hostPort[1]));
        } else {
            //         
            return isHostReachable(url);
        }
    }
    urlがip+portフォーマットかどうかを確認します。
  • ip+ポートフォーマットであれば、ホストが
  • に接続可能かどうかをテストする。
  • そうでなければ、ホストが
  • に達するかどうかをテストします。
    2)ホストが接続できるかどうかをテストします。
    private static boolean isHostConnector(final String host, final int port) {
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(host, port));
        } catch (IOException e) {
            return false;
        }
        return true;
    }
    socketのconnectionでipの接続性をテストします。
    3)ホストが到達するかどうかをテストします。
    private static boolean isHostReachable(final String host) {
        try {
            return InetAddress.getByName(host).isReachable(1000);
        } catch (IOException ignored) {
        }
        return false;
    }
    非ip+ポートフォーマットurlはドメイン名フォーマットを使ってホストが到達するかどうかをテストしようと試みる。
    全体的に見れば、divideプラグインがキャッシュから取得したサーバ情報は、データ同期に由来し、定期的にアクティブジョブによって自動更新されます。
    四、負荷バランス
    上に述べたように、divideは負荷均衡アルゴリズムによって最終的に配信されたサービスipを選択し、負荷均衡の実現を見ています。
    public static DivideUpstream selector(final List upstreamList, final String algorithm, final String ip) {
        LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm);
        return loadBalance.select(upstreamList, ip);
    }
    内部は拡張キャリアを利用して、対応する負荷均衡アルゴリズムをアルゴリズム名でロードし、最終的に配信されたサービスipの負荷均衡計算を実行する。
    ソウルゲートウェイではデフォルトで3つの負荷バランスポリシーをサポートします。
  • HASH
  • RANDOM(最も簡単で最も速く、大量の要求の下でほぼ平均)
  • ROUND_ROBIN(記録状態が必要で、一定の影響があり、大データ量でランダムとポーリングはあまり結果的な差異がない)
  • デフォルトはRANDDOMのランダムアルゴリズムであり、アルゴリズム処理は以下の通りである。
    public DivideUpstream doSelect(final List upstreamList, final String ip) {
        int totalWeight = calculateTotalWeight(upstreamList);
        boolean sameWeight = isAllUpStreamSameWeight(upstreamList);
        //       ,       
        if (totalWeight > 0 && !sameWeight) {
            return random(totalWeight, upstreamList);
        }
        //       
        return random(upstreamList);
    }
    サービスリスト内のサービスの重み付けが一致しているかどうかを判断する。
  • 重みが一致しない場合、総重みに応じてランダムに
  • そうでないと、サービス数によってランダムに
  • です。
    総重量に応じてランダムな詳細(Random LoadBalance墭蘭dom):
    private DivideUpstream random(final int totalWeight, final List upstreamList) {
        //         
        int offset = RANDOM.nextInt(totalWeight);
        //            
        for (DivideUpstream divideUpstream : upstreamList) {
            offset -= getWeight(divideUpstream);
            if (offset < 0) {
                return divideUpstream;
            }
        }
        return upstreamList.get(0);
    }
    五、まとめ
    divideプラグインの処理フロー:
  • 利用可能サービス一覧を取得する。
  • サービスリストは、最初にsoul-adminからのデータ同期
  • です。
  • 利用可能なサービス一覧は、デフォルトでは30秒ごとにアクティブに更新されます。
  • 負荷バランス
  • 拡張キャリアローディングターゲット負荷等化アルゴリズム
  • 具体的な均衡ポリシーを実行する
  • は、最終的に選択されたサービス情報
  • を返す。
  • 最終サービスのurl情報
  • を設定する。
  • は、プラグインチェーンの下流で処理される
  • である。