ソリューションのアイデアに興味がない場合は、記録プロセスのダウンを直接確認できます。(この記事には、sentinel-dashboard からの Sentinel-core の呼び出しの分析が含まれています)
前提
最近、ジュニア プログラマーが、ゲートウェイの電流制限を行うときに、以前はあまり使用されていなかった Sentinel を使用しました。OK、コードの使い方は簡単です: (本題に入るために、パッケージのインポート方法についてはあまり話しません。私は Sentinel-core を使用します)
List<FlowRule> flowRules = new ArrayList<>();
FlowRule flowRule = new FlowRule();
// 设置受保护的资源
flowRule.setResource(RESOURCE_NAME);
// 设置流控规则 QPS
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
// Set limit QPS to 1.
flowRule.setCount(1);
flowRules.add(flowRule);
// 加载配置好的规则
FlowRuleManager.loadRules(flowRules);
私の要件: フロー制御ルールはメモリに保存され、インターフェイスは動的に更新される必要があります。
何が問題ですか?問題は、動的更新を必要とする最後のステップにあります。更新。これは、ルールをロードするloadRulesのステップも直接指します。
FlowRuleManager.loadRules(flowRules);
経験の浅いプログラマー (私) は、見てください。彼はリストをどのようにロードしたいのですか? 以前のルールはすべて上書きされないでしょうか?そのような問題は発生しますか?
loadRules が再度呼び出された場合、以前にロードされたルールのウィンドウに影響しますか? たとえば、「a」という名前の 100qps ルールが初めてロードされた場合、「a」リソースが 500 ミリ秒以内に 100 回要求されたとします。このとき、「a」という名前のルールをロードしましたが、このとき、この「a」は最初の 500ms 以内に再度リクエストできますか?
当時私はテストケースを書きましたが、それは何の影響もありませんでしたが、私が依然として非常に偽善的であることを証明する証拠はありません(見せてください)。
FlowRuleManager.loadRules(flowRules) を参照してください。
私の最初のステップは、この方法を検討しました
public static void loadRules(List<FlowRule> rules) {
currentProperty.updateValue(rules);
}
DynamicSentinelProperty::updateValue
public boolean updateValue(T newValue) {
if (isEqual(value, newValue)) {
return false;
}
RecordLog.info("[DynamicSentinelProperty] Config will be updated to: {}", newValue);
value = newValue;
for (PropertyListener<T> listener : listeners) {
listener.configUpdate(newValue);
}
return true;
}
isEqual メソッドがあることに注意してください。同じリスト オブジェクトを 2 回置くと、2 回目には false が返されます。これは主にリスナーの更新に依存し、その後 FlowRuleManager::configUpdate へのデバッグが続きます。
public synchronized void configUpdate(List<FlowRule> value) {
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
if (rules != null) {
flowRules = rules;
}
RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
}
引き続き下に進み、このビルド メソッドをクリックして、FlowRuleUtil::buildFlowRuleMap(List<FlowRule> list, Function<FlowRule, K> groupFunction, Predicate<FlowRule> filter, boolean shouldSort) へのメソッドを入力します。
public static <K> Map<K, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Function<FlowRule, K> groupFunction,
Predicate<FlowRule> filter, boolean shouldSort) {
Map<K, List<FlowRule>> newRuleMap = new ConcurrentHashMap<>();
if (list == null || list.isEmpty()) {
return newRuleMap;
}
Map<K, Set<FlowRule>> tmpMap = new ConcurrentHashMap<>();
for (FlowRule rule : list) {
if (!isValidRule(rule)) {
RecordLog.warn("[FlowRuleManager] Ignoring invalid flow rule when loading new flow rules: " + rule);
continue;
}
if (filter != null && !filter.test(rule)) {
continue;
}
if (StringUtil.isBlank(rule.getLimitApp())) {
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
TrafficShapingController rater = generateRater(rule);
rule.setRater(rater);
K key = groupFunction.apply(rule);
if (key == null) {
continue;
}
Set<FlowRule> flowRules = tmpMap.get(key);
if (flowRules == null) {
// Use hash set here to remove duplicate rules.
flowRules = new HashSet<>();
tmpMap.put(key, flowRules);
}
flowRules.add(rule);
}
Comparator<FlowRule> comparator = new FlowRuleComparator();
for (Entry<K, Set<FlowRule>> entries : tmpMap.entrySet()) {
List<FlowRule> rules = new ArrayList<>(entries.getValue());
if (shouldSort) {
// Sort the rules.
Collections.sort(rules, comparator);
}
newRuleMap.put(entries.getKey(), rules);
}
return newRuleMap;
}
戻り値を直接見ると、新しい RuleMap が返されたことがわかりました。これは、前に定義したルールがまったく新しく、完全に上書きされたという事実に相当します。私は、「もう終わりだ。これは、考えられる問題が発生したことを証明するものではない」と思いました。それは起こりませんでした、バービーQ。
Sentinel-Dashboard がどのように機能するかをご覧ください
私は、これはあってはならない、以前に作成したルールに影響を与えずにルールを動的に更新する方法があるに違いないと思いました(FlowRuleManager.loadRules(flowRules) が影響すると思いました)。ふと思ったのですが、Sentinel にもサーバーがあるのですが、それはクライアントのフロー制御ルールを動的に更新するだけではないでしょうか? それを実行し、sentinel-dashboard を見つけてダウンロードし、実行して確認し、電流制限ルールを追加します (ここで、ダッシュボードの読み込みは遅延読み込みであり、プロジェクトはインターフェイスを要求した後にのみ表示されることに注意してください)
次に、ブラウザは F12 を押して、次のようにどのインターフェイスが要求されているかを確認します。 /v1/flow/rule
次に、sentinel-dashboard プロジェクトでインターフェイスを見つけて、彼が何をしたかを確認してください。
public Result<FlowRuleEntity> apiAddFlowRule(@RequestBody FlowRuleEntity entity) {
Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
}
entity.setId(null);
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
entity.setLimitApp(entity.getLimitApp().trim());
entity.setResource(entity.getResource().trim());
try {
entity = repository.save(entity);
publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(5000, TimeUnit.MILLISECONDS);
return Result.ofSuccess(entity);
} catch (Throwable t) {
Throwable e = t instanceof ExecutionException ? t.getCause() : t;
logger.error("Failed to add new flow rule, app={}, ip={}", entity.getApp(), entity.getIp(), e);
return Result.ofFail(-1, e.getMessage());
}
}
上部をざっと読んで、publishRules を直接確認してください。コードは比較的明確で、何も考えずに下に進み、リターン内をクリックするだけです。ついに SentinelApiClient::executeCommand に来ました
private CompletableFuture<String> executeCommand(HttpUriRequest request) {
CompletableFuture<String> future = new CompletableFuture<>();
httpClient.execute(request, new FutureCallback<HttpResponse>() {
@Override
public void completed(final HttpResponse response) {
int statusCode = response.getStatusLine().getStatusCode();
try {
String value = getBody(response);
if (isSuccess(statusCode)) {
future.complete(value);
} else {
if (isCommandNotFound(statusCode, value)) {
future.completeExceptionally(new CommandNotFoundException(request.getURI().getPath()));
} else {
future.completeExceptionally(new CommandFailedException(value));
}
}
} catch (Exception ex) {
future.completeExceptionally(ex);
logger.error("HTTP request failed: {}", request.getURI().toString(), ex);
}
}
@Override
public void failed(final Exception ex) {
future.completeExceptionally(ex);
logger.error("HTTP request failed: {}", request.getURI().toString(), ex);
}
@Override
public void cancelled() {
future.complete(null);
}
});
return future;
}
これは主に、httpclient リクエストを呼び出して、クライアントのセンチネルコアのフロー制御ルールを変更することです (「無害な」動的変更ルールの発見に近づいていると感じています)。次に、サーバーがクライアントに /setRules を要求するインターフェイスを見つけます。
次に、sentinel-core で ModifyRulesCommandHandler::handle を見つけます (どのメソッドを呼び出すために使用されているのか、真実に近づいていることに興奮しています! )
続いて詳しく見てみました!失くしてしまったし、油断していた(スナイパーでも当たらない男)。それはまだあなたです
FlowRuleManager.loadRules(flowRules);
FlowRuleManager.loadRules(flowRules);
FlowRuleManager.loadRules(flowRules);
!!!!!!!!!!!!!!!!!!
つづく……………