Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix the potential concurrence issue when rules updates #1783

Merged
merged 8 commits into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
package com.alibaba.csp.sentinel.slots.block.flow;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
import com.alibaba.csp.sentinel.log.RecordLog;
Expand All @@ -43,10 +45,11 @@
*
* @author jialiang.linjl
* @author Eric Zhao
* @author Weihua
*/
public class FlowRuleManager {

private static final Map<String, List<FlowRule>> flowRules = new ConcurrentHashMap<String, List<FlowRule>>();
private static final AtomicReference<Map<String, List<FlowRule>>> flowRules = new AtomicReference<Map<String, List<FlowRule>>>();

private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();
Expand All @@ -56,6 +59,7 @@ public class FlowRuleManager {
new NamedThreadFactory("sentinel-metrics-record-task", true));

static {
flowRules.set(Collections.<String, List<FlowRule>>emptyMap());
currentProperty.addListener(LISTENER);
SCHEDULER.scheduleAtFixedRate(new MetricTimerListener(), 0, 1, TimeUnit.SECONDS);
}
Expand Down Expand Up @@ -83,7 +87,7 @@ public static void register2Property(SentinelProperty<List<FlowRule>> property)
*/
public static List<FlowRule> getRules() {
List<FlowRule> rules = new ArrayList<FlowRule>();
for (Map.Entry<String, List<FlowRule>> entry : flowRules.entrySet()) {
for (Map.Entry<String, List<FlowRule>> entry : flowRules.get().entrySet()) {
rules.addAll(entry.getValue());
}
return rules;
Expand All @@ -99,19 +103,19 @@ public static void loadRules(List<FlowRule> rules) {
}

static Map<String, List<FlowRule>> getFlowRuleMap() {
return flowRules;
return flowRules.get();
}

public static boolean hasConfig(String resource) {
return flowRules.containsKey(resource);
return flowRules.get().containsKey(resource);
}

public static boolean isOtherOrigin(String origin, String resourceName) {
if (StringUtil.isEmpty(origin)) {
return false;
}

List<FlowRule> rules = flowRules.get(resourceName);
List<FlowRule> rules = flowRules.get().get(resourceName);

if (rules != null) {
for (FlowRule rule : rules) {
Expand All @@ -129,21 +133,17 @@ private static final class FlowPropertyListener implements PropertyListener<List
@Override
public void configUpdate(List<FlowRule> value) {
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
if (rules != null) {
flowRules.clear();
flowRules.putAll(rules);
}
RecordLog.info("[FlowRuleManager] Flow rules received: {}", flowRules);
//the rules was always not null, it's no need to check nullable
//remove checking to avoid IDE warning
flowRules.set(rules);
RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
}

@Override
public void configLoad(List<FlowRule> conf) {
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);
if (rules != null) {
flowRules.clear();
flowRules.putAll(rules);
}
RecordLog.info("[FlowRuleManager] Flow rules loaded: {}", flowRules);
flowRules.set(rules);
RecordLog.info("[FlowRuleManager] Flow rules loaded: {}", rules);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.slots.block.flow;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertEquals;

/**
* @author Weihua
*/
public class FlowRuleManagerTest {

public static final List<FlowRule> STATIC_RULES_1 = new ArrayList<FlowRule>();
public static final List<FlowRule> STATIC_RULES_2 = new ArrayList<FlowRule>();

static {
FlowRule first = new FlowRule();
first.setResource("/a/b/c");
first.setCount(100);
STATIC_RULES_1.add(first);

FlowRule second = new FlowRule();
second.setResource("/a/b/c");
second.setCount(200);
STATIC_RULES_2.add(second);

}

@Test
public void testLoadAndGetRules(){
FlowRuleManager.loadRules(STATIC_RULES_1);
assertEquals(1, FlowRuleManager.getRules().size()); // the initial size
new Thread(loader, "Loader").start();

for(int i = 0; i < 10000; i++){
//The initial size is 1, and the size after updating should also be 1,
//if the actual size is 0, that must be called after clear(),
// but before putAll() in FlowPropertyListener.configUpdate
assertEquals(1, FlowRuleManager.getRules().size());
}
}

public Runnable loader = new Runnable() {
@Override
public void run() {
for(int i = 0; i < 10000; i++){
//to guarantee that they're different and change happens
FlowRuleManager.loadRules(i % 2 == 0 ? STATIC_RULES_2 : STATIC_RULES_1);
}
}
};

}