alibaba / Sentinel

A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)
https://sentinelguard.io/
Apache License 2.0
22.31k stars 8k forks source link

INTERVAL的动态调整 #141

Open aBreaking opened 6 years ago

aBreaking commented 6 years ago

我尝试着手动能够手动设置时间周期,但是有个问题让我百思不得解。 我动态修改了这个周期,只能针对没有过之前,如果之前该资源有应用了Sentinel,那么修改就无效。 比如,原始Interval默认为1,对于一个资源A,之前进行访问过,能够正常进行的流量控制。这时,我通过命令的方式修改了INTERVAL,比如修改为5,再访问A,但是A的INTERVAL还是1秒,并没有改变。但如果我访问资源B,B之前没有访问过,修改后的INTERVAL对B有效,B的流控规则就是我想要的效果。

我不知道哪里出的问题,贴出我修改的代码,能帮我看下吗?谢谢! 1、为IntervalProperty类增加一个专用于流量控制的 时间周期的规则修改的属性

public class IntervalProperty {

    public static volatile int INTERVAL = 1;

    //定义了FLOW_INTERVAL,专用于进行流量控制的  时间周期的规则。
    public static volatile int FLOW_INTERVAL = 1;

    public static void init(SentinelProperty<Integer> dataSource) {
        dataSource.addListener(new FlowIntervalPropertyListener());
    }
    private static class FlowIntervalPropertyListener implements PropertyListener<Integer> {

        public void configUpdate(Integer value) {
            if (value == null) {
                value = 1;
            }
            //目前只是想先动态修改FLOW_INTERVAL,下同
            FLOW_INTERVAL = value;
            RecordLog.info("Init flow interval: " + FLOW_INTERVAL);
        }

        public void configLoad(Integer value) {
            if (value == null) {
                value = 1;
            }
            FLOW_INTERVAL = value;
            for (ClusterNode node : ClusterBuilderSlot.getClusterNodeMap().values()) {
                node.reset();
            }
            RecordLog.info("Flow interval change received: " + FLOW_INTERVAL);
        }
    }
}

2、StatisticNode,仿照原来的逻辑增加了一个属性rollingCounterInSecond4Flow,修改了三个方法, 大致同旧,只是我想只是针对Flow的Interval。。省略其他方法

private transient Metric rollingCounterInSecond4Flow = new ArrayMetric(1000 / SampleCountProperty.sampleCount, IntervalProperty.FLOW_INTERVAL);

//我想这个方法就是重置统计吧
public void reset() {
        rollingCounterInSecond = new ArrayMetric(1000 / SampleCountProperty.sampleCount,IntervalProperty.INTERVAL);
        rollingCounterInSecond4Flow = new ArrayMetric(1000 / SampleCountProperty.sampleCount, IntervalProperty.FLOW_INTERVAL);

    public long passQps() {
        return rollingCounterInSecond4Flow.pass() / IntervalProperty.INTERVAL;
    }

public void addPassRequest() {
        rollingCounterInSecond.addPass();
        rollingCounterInMinute.addPass();
        rollingCounterInSecond4Flow.addPass();
    }

3、一个简单的servlet能够通过url的方式修改Interval,使用了DynamicSentinelProperty。

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        DynamicSentinelProperty<Integer> sentinelProperty = new DynamicSentinelProperty<Integer>(Integer.parseInt(request.getParameter("interval")));
        IntervalProperty.init(sentinelProperty);
        response.getWriter().println(IntervalProperty.FLOW_INTERVAL);
    }

主要逻辑为上 那么现在一个主要问就是,如果之前,我访问过某个服务。我动态修改了Interval后,并不起作用,就好像没有修改一样。 我修改了这个Interval之后,访问一个之前没有访问过的服务,那么这个Interval就生效了,能够达到我想要的效果。 所以,我现在就是想不明白,为什么修改之后,之前访问过的资源就不起作用呢?是不是原Sentinel代码某处,还有什么地方保留者缓存什么? 请指教! 谢谢!!

aBreaking commented 6 years ago

@CarpenterLee @sczyh30

CarpenterLee commented 6 years ago

Thanks for your patient test. The INTERVAL changing will take effect only when the StatisticNode of the resource doesn't be created, for that once the StatisticNode is created, it will never be recreated. This is mainly for performance considerations. See NodeSelectorSlot for StatisticNode creation.

Frankly speaking, Sentinel doesn't support change INTERVAL dynamically, or set different interval for different resource, but we may support it latter.

By the way, please change the title to a more meaningful one.

aBreaking commented 6 years ago

@CarpenterLee Thanks for your your advice!!! 我今天终于把这个问题给解决了,能够实现动态调整INTERVAL了。我的方法如下: 1、设置一个标志位,当我通过命令的方式改变规则时,该标志位就随之改变。我设置在IntervalProperty类中:

//添加了一个新的属性,用它来指定是否进行reset node.
 public static boolean RESET = false;
//每当调用了匿名内部类的configLoad方法时,就重置 RESET
private static class FlowIntervalPropertyListener implements PropertyListener<Integer> {
        public void configLoad(Integer value) {
            if (value == null) {
                value = 1;
            }
            FLOW_INTERVAL = value;
            RESET = true;
            for (ClusterNode node : ClusterBuilderSlot.getClusterNodeMap().values()) {
                node.reset();
            }
            RecordLog.info("Flow interval change received: " + FLOW_INTERVAL);
        }
    }

2、在创建StatisticNode处时,根据RESET,是否重新创建一个StatisticNode。 在ClusterBuilderSlotl类的entry方法,获取originNode的逻辑修改如下:

Node originNode;
 if(IntervalProperty.RESET){
         originNode = node.getClusterNode().resetOriginNode(context.getOrigin());
         IntervalProperty.RESET = false;
}else{
        originNode = node.getClusterNode().getOriginNode(context.getOrigin());
}

3、ClusterNode的resetOriginNode方法仿照getOriginNode方法,用于创建一个新的StatisticNode

public Node resetOriginNode(String origin) {
        try {
            lock.lock();
            StatisticNode statisticNode  = new StatisticNode();
            HashMap<String, StatisticNode> newMap = new HashMap<String, StatisticNode>(
                    originCountMap.size() + 1);
            newMap.putAll(originCountMap);
            newMap.put(origin, statisticNode);
            originCountMap = newMap;
            return statisticNode;
        } finally {
            lock.unlock();
        }
    }