Microservice-Sentinel-Basisfall, Persistenz

Wächter

Funktion

  1. Direkte Ablehnung: Lösen Sie den Schwellenwert aus und verwerfen Sie ihn direkt.
  2. Kaltstart: Langsames Erhöhen der Anzahl von Prozessen über einen bestimmten Zeitraum als Reaktion auf starken Datenverkehr.
    3) Uniformizer: Aufforderung, mit gleichmäßiger Geschwindigkeit zu passieren.
  • Downgrade Downgrade-Dokument
    1) Wenn die statistische RT-Zeit größer als die voreingestellte Anzahl von Anforderungen ist und die langsamen Anforderungen größer als dieses voreingestellte Verhältnis sind, wird der Leistungsschalter für einen bestimmten Zeitraum abgelehnt.
    2) Abnormaler Anteil. Wenn während des statistischen Zeitraums die Gesamtzahl der Anforderungen größer als die voreingestellte Anzahl von Anforderungen ist und der abnormale Anteil größer als der voreingestellte Anteil ist, wird der Leistungsschalter für einen bestimmten Zeitraum abgelehnt.
    3) Anzahl der Ausnahmen. Wenn während des statistischen Zeitraums die Gesamtzahl der Anforderungen größer als die voreingestellte Anzahl von Anforderungen ist und die Ausnahmen größer als der voreingestellte Wert sind, wird der Leistungsschalter für einen bestimmten Zeitraum abgelehnt.
Manuelle Strombegrenzungsfall-Demo
  • Begrenzen Sie den aktuellen Fluss basierend auf der aktiven Anzahl von Threads
/**
 * 根据并发数量进行限流
 */
public class Flow_1_Thread_Demo {
    
    

    /** 资源 methodA 所有业务逻辑处理完成的数量 **/
    private static AtomicInteger pass = new AtomicInteger();
    /** 资源 methodA 被拒绝的数量 **/
    private static AtomicInteger block = new AtomicInteger();
    /** 资源 methodA 接收到请求的总数量 **/
    private static AtomicInteger total = new AtomicInteger();
    /** 资源 methodA 真正开始干活时就开始计数,表示 methodA 的任务活跃数 **/
    private static AtomicInteger activeThread = new AtomicInteger();

    private static volatile boolean stop = false;
    private static final int threadCount = 100;

    /** 资源 methodA 总共被发起调用的持续时间 **/
    private static int seconds = 15;
    /** 资源 methodB 处理业务时需要消耗的时间,单位:毫秒 **/
    private static volatile int methodBRunningTime = 2000;

    /**
     * 并发数限流
     * 当并发数大于 5 时则进行阻断
     * 反正一直保持最大活跃任务数为 5
     */
    private static void initFlowRule() {
    
    
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule rule = new FlowRule();

        // 设置资源名称为:methodA
        rule.setResource("methodA");

        // 设置限流类型:并发数限流
        rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);

        // 设置并发数限流,对应的限制数值
        rule.setCount(5);

        // 设置需要限制哪些应用,如果没有想好需要限制谁的话,那么采用 default 方式
        rule.setLimitApp("default");

        // 将设置好的规则,添加至列表中,并且加载到限流管理器中
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
        System.out.println("Flow_1_Thread rule loaded: " + rules);
    }

    public static void main(String[] args) throws Exception {
    
    
        System.out.println(
            "MethodA will call methodB. After running for a while, methodB becomes fast, "
                + "which make methodA also become fast ");
        // 开启一个线程打印统计信息
        tick();
        // 设置规则
        initFlowRule();
        // 启动现场 不停的发送请求
        for (int i = 0; i < threadCount; i++) {
    
    
            Thread entryThread = new Thread(new ThreadRunTask());
            entryThread.setName("working thread");
            entryThread.start();
        }
    }

    private static void tick() {
    
    
        Thread timer = new Thread(new TimerTask());
        timer.setName("sentinel-timer-task");
        timer.start();
    }

    /**
     * 打印统计信息 每秒钟输出
     */
    static class TimerTask implements Runnable {
    
    

        @Override
        public void run() {
    
    
            long start = System.currentTimeMillis();
            System.out.println("begin to statistic!!!");

            long oldTotal = 0;
            long oldPass = 0;
            long oldBlock = 0;
            
            while (!stop) {
    
    
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                }
                long globalTotal = total.get();
                long oneSecondTotal = globalTotal - oldTotal;
                oldTotal = globalTotal;

                long globalPass = pass.get();
                long oneSecondPass = globalPass - oldPass;
                oldPass = globalPass;

                long globalBlock = block.get();
                long oneSecondBlock = globalBlock - oldBlock;
                oldBlock = globalBlock;

                System.out.print("【秒】倒计时: " + seconds + ", ");
                System.out.println((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date()))
                    + ", 每秒总请求量:" + oneSecondTotal
                    + ", 每秒完成量:" + oneSecondPass
                    + ", 每秒拒绝量:" + oneSecondBlock
                    + ", 每秒活跃量:" + activeThread.get());
                System.out.println();
                if (seconds-- <= 0) {
    
    
                    stop = true;
                }
                // 倒数5秒的时候提高MethodB 方法的效率
                if (seconds == 5) {
    
    
                    System.out.println("method B 原来执行需要花费 2000 毫秒,改造后只需要花费 20 毫秒,系统即将能处理更多的请求。\n");
                    methodBRunningTime = 20;
                }
            }

            long cost = System.currentTimeMillis() - start;
            System.out.println("time cost: " + cost + " ms");
            System.out.println("total:" + total.get() + ", pass:" + pass.get()
                + ", block:" + block.get());
            System.exit(0);
        }
    }

    static class ThreadRunTask implements Runnable {
    
    

        @Override
        public void run() {
    
    
            while (true) {
    
    
                Entry methodA = null;
                try {
    
    
                    TimeUnit.MILLISECONDS.sleep(5);
                    // 抢占资源A
                    methodA = SphU.entry("methodA");
                    activeThread.incrementAndGet();
                    // 抢占资源B
                    Entry methodB = SphU.entry("methodB");
                    TimeUnit.MILLISECONDS.sleep(methodBRunningTime);
                    methodB.exit();
                    pass.addAndGet(1);
                } catch (BlockException e1) {
    
    
                    // 阻塞的数量
                    block.incrementAndGet();
                } catch (Exception e2) {
    
    
                    // biz exception
                } finally {
    
    
                    // 请求总数量
                    total.incrementAndGet();
                    if (methodA != null) {
    
    
                        methodA.exit();
                        // 活跃数量减
                        activeThread.decrementAndGet();
                    }
                }
            }
        }
    }
}
  • Strombegrenzung basierend auf QPS
/**
 * 根据QPS进行限流
 */
public class Flow_2_Qps_Demo {
    
    
    private static final String KEY = "methodA";
    /** 资源 methodA 所有业务逻辑处理完成的数量 **/
    private static AtomicInteger pass = new AtomicInteger();
    /** 资源 methodA 被拒绝的数量 **/
    private static AtomicInteger block = new AtomicInteger();
    /** 资源 methodA 接收到请求的总数量 **/
    private static AtomicInteger total = new AtomicInteger();
    private static volatile boolean stop = false;
    private static final int threadCount = 32;
    /** 资源 methodA 总共被发起调用的持续时间 **/
    private static int seconds = 15;
    private static void initFlowQpsRule() {
    
    
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule rule1 = new FlowRule();
        // 设置资源名称为:methodA
        rule1.setResource(KEY);
        // 设置限流类型:QPS 限流
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置 QPS 限流,对应的限制数值
        rule1.setCount(5);
        // 设置 QPS 限流数值满后的应对策略:直接拒绝(该策略为默认策略,可以从 setControlBehavior 方法跟踪进去看)
        // public static final int CONTROL_BEHAVIOR_DEFAULT = 0;
        // public static final int CONTROL_BEHAVIOR_WARM_UP = 1;
        // public static final int CONTROL_BEHAVIOR_RATE_LIMITER = 2;
        // public static final int CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER = 3;
        // rule1.setControlBehavior()
        
        // 设置需要限制哪些应用,如果没有想好需要限制谁的话,那么采用 default 方式
        rule1.setLimitApp("default");
        // 将设置好的规则,添加至列表中,并且加载到限流管理器中
        rules.add(rule1);
        FlowRuleManager.loadRules(rules);
        System.out.println("Flow_2_Qps rule loaded: " + rules);
    }
    public static void main(String[] args) throws Exception {
    
    
        initFlowQpsRule();

        tick();
        // first make the system run on a very low condition
        simulateTraffic();
        System.out.println("===== begin to do flow control");
        System.out.println("only 5 requests per second can pass");
    }
    private static void simulateTraffic() {
    
    
        for (int i = 0; i < threadCount; i++) {
    
    
            Thread t = new Thread(new RunTask());
            t.setName("simulate-traffic-Task");
            t.start();
        }
    }
    private static void tick() {
    
    
        Thread timer = new Thread(new TimerTask());
        timer.setName("sentinel-timer-task");
        timer.start();
    }
    static class TimerTask implements Runnable {
    
    
        @Override
        public void run() {
    
    
            long start = System.currentTimeMillis();
            System.out.println("begin to statistic!!!");

            long oldTotal = 0;
            long oldPass = 0;
            long oldBlock = 0;
            while (!stop) {
    
    
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                }
                long globalTotal = total.get();
                long oneSecondTotal = globalTotal - oldTotal;
                oldTotal = globalTotal;

                long globalPass = pass.get();
                long oneSecondPass = globalPass - oldPass;
                oldPass = globalPass;

                long globalBlock = block.get();
                long oneSecondBlock = globalBlock - oldBlock;
                oldBlock = globalBlock;

                System.out.print("【秒】倒计时: " + seconds + ", ");
                System.out.println((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date()))
                        + ", 每秒总请求量:" + oneSecondTotal
                        + ", 每秒完成量:" + oneSecondPass
                        + ", 每秒拒绝量:" + oneSecondBlock);
                System.out.println();
                if (seconds-- <= 0) {
    
    
                    stop = true;
                }
            }

            long cost = System.currentTimeMillis() - start;
            System.out.println("time cost: " + cost + " ms");
            System.out.println("total:" + total.get() + ", pass:" + pass.get()
                + ", block:" + block.get());
            System.exit(0);
        }
    }

    static class RunTask implements Runnable {
    
    
        @Override
        public void run() {
    
    
            while (!stop) {
    
    
                Entry entry = null;

                try {
    
    
                    entry = SphU.entry(KEY);
                    // token acquired, means pass
                    pass.addAndGet(1);
                } catch (BlockException e1) {
    
    
                    block.incrementAndGet();
                } catch (Exception e2) {
    
    
                    // biz exception
                } finally {
    
    
                    total.incrementAndGet();
                    if (entry != null) {
    
    
                        entry.exit();
                    }
                }

                Random random2 = new Random();
                try {
    
    
                    TimeUnit.MILLISECONDS.sleep(random2.nextInt(50));
                } catch (InterruptedException e) {
    
    
                    // ignore
                }
            }
        }
    }
}
  • Kaltstart
    bedeutet, dass wir innerhalb eines bestimmten Zeitraums langsam den Höhepunkt der Anforderungsverarbeitung erreichen können.
public class Flow_3_WarmUp_Demo {
    
    
    private static final String KEY = "methodA";
    /** 资源 methodA 所有业务逻辑处理完成的数量 **/
    private static AtomicInteger pass = new AtomicInteger();
    /** 资源 methodA 被拒绝的数量 **/
    private static AtomicInteger block = new AtomicInteger();
    /** 资源 methodA 接收到请求的总数量 **/
    private static AtomicInteger total = new AtomicInteger();
    private static volatile boolean stop = false;
    private static final int threadCount = 100;
    /** 资源 methodA 总共被发起调用的持续时间 **/
    private static int seconds = 16;
    private static void initFlowRule() {
    
    
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule rule1 = new FlowRule();
        // 设置资源名称为:methodA
        rule1.setResource(KEY);
        // 设置限流类型:QPS 限流
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置 QPS 限流,对应的限制数值
        rule1.setCount(20);
        // 设置 QPS 限流数值满后的应对策略:冷启动,即让通过的流量缓慢增加,直到增加到限制数值上限
        rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
        // 既然设置了冷启动缓慢增长,那么这个缓慢增长到限制数值上限的时间为:10 秒
        rule1.setWarmUpPeriodSec(10);
        // 设置需要限制哪些应用,如果没有想好需要限制谁的话,那么采用 default 方式
        rule1.setLimitApp("default");
        // 将设置好的规则,添加至列表中,并且加载到限流管理器中
        rules.add(rule1);
        FlowRuleManager.loadRules(rules);
        System.out.println("Flow_3_WarmUp rule loaded: " + rules);
    }
    public static void main(String[] args) throws Exception {
    
    
        initFlowRule();
        // trigger Sentinel internal init
        Entry entry = null;
        try {
    
    
            entry = SphU.entry(KEY);
        } catch (Exception e) {
    
    
        } finally {
    
    
            if (entry != null) {
    
    
                entry.exit();
            }
        }

        Thread timer = new Thread(new TimerTask());
        timer.setName("sentinel-timer-task");
        timer.start();

        //first make the system run on a very low condition
        for (int i = 0; i < 3; i++) {
    
    
            // WarmUpTask 控制住了调用资源 methodA 的频率,让系统处于一个低水平调用状态
            Thread t = new Thread(new WarmUpTask());
            t.setName("sentinel-warmup-task");
            t.start();
        }
        Thread.sleep(5000);

        /*
         * Start more thread to simulate more qps. Since we use {@link RuleConstant.CONTROL_BEHAVIOR_WARM_UP} as
         * {@link FlowRule#controlBehavior}, real passed qps will increase to {@link FlowRule#count} in
         * {@link FlowRule#warmUpPeriodSec} seconds.
         */
        for (int i = 0; i < threadCount; i++) {
    
    
            // RunTask 稍微加大了调用资源 methodA 的频率,让系统处于一个相对原来处于一个较高水平调用状态
            Thread t = new Thread(new RunTask());
            t.setName("sentinel-run-task");
            t.start();
        }
    }

    static class TimerTask implements Runnable {
    
    

        @Override
        public void run() {
    
    
            long start = System.currentTimeMillis();
            System.out.println("begin to statistic!!!");
            long oldTotal = 0;
            long oldPass = 0;
            long oldBlock = 0;
            while (!stop) {
    
    
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                }

                long globalTotal = total.get();
                long oneSecondTotal = globalTotal - oldTotal;
                oldTotal = globalTotal;

                long globalPass = pass.get();
                long oneSecondPass = globalPass - oldPass;
                oldPass = globalPass;

                long globalBlock = block.get();
                long oneSecondBlock = globalBlock - oldBlock;
                oldBlock = globalBlock;

                System.out.print("【秒】倒计时: " + seconds + ", ");
                System.out.println((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date()))
                        + ", 每秒总请求量:" + oneSecondTotal
                        + ", 每秒完成量:" + oneSecondPass
                        + ", 每秒拒绝量:" + oneSecondBlock);
                System.out.println();

                if (seconds-- <= 0) {
    
    
                    stop = true;
                }
            }

            long cost = System.currentTimeMillis() - start;
            System.out.println("time cost: " + cost + " ms");
            System.out.println("total:" + total.get() + ", pass:" + pass.get()
                + ", block:" + block.get());
            System.exit(0);
        }
    }

    static class WarmUpTask implements Runnable {
    
    
        @Override
        public void run() {
    
    
            while (!stop) {
    
    
                Entry entry = null;
                try {
    
    
                    entry = SphU.entry(KEY);
                    // token acquired, means pass
                    pass.addAndGet(1);
                } catch (BlockException e1) {
    
    
                    block.incrementAndGet();
                } catch (Exception e2) {
    
    
                    // biz exception
                } finally {
    
    
                    total.incrementAndGet();
                    if (entry != null) {
    
    
                        entry.exit();
                    }
                }
                Random random2 = new Random();
                try {
    
    
                    TimeUnit.MILLISECONDS.sleep(random2.nextInt(2000));
                } catch (InterruptedException e) {
    
    
                    // ignore
                }
            }
        }
    }

    static class RunTask implements Runnable {
    
    
        @Override
        public void run() {
    
    
            while (!stop) {
    
    
                Entry entry = null;
                try {
    
    
                    entry = SphU.entry(KEY);
                    pass.addAndGet(1);
                } catch (BlockException e1) {
    
    
                    block.incrementAndGet();
                } catch (Exception e2) {
    
    
                    // biz exception
                } finally {
    
    
                    total.incrementAndGet();
                    if (entry != null) {
    
    
                        entry.exit();
                    }
                }
                Random random2 = new Random();
                try {
    
    
                    TimeUnit.MILLISECONDS.sleep(random2.nextInt(50));
                } catch (InterruptedException e) {
    
    
                    // ignore
                }
            }
        }
    }
}
Demo zum manuellen Downgrade-Fall
  • Die Begrenzung des Flusses basierend auf der Antwortzeit
    bedeutet, dass wir innerhalb der von uns festgelegten Zeit mit der Arbeit aufhören, sobald die Statistik langsamer Anforderungen einen bestimmten Anteil überschreitet. Wir arbeiten weiter, bis die Verarbeitungszeit der nächsten Anforderung kürzer als die von uns festgelegte Zeit für langsame Anforderungen ist .
public class Degrade_1_RT_Demo {
    
    

    private static final String KEY = "methodA";

    private static volatile boolean stop = false;
    private static int seconds = 120;

    /** 资源 methodA 接收到请求的总数量 **/
    private static AtomicInteger total = new AtomicInteger();
    /** 资源 methodA 被拒绝的数量 **/
    private static AtomicInteger block = new AtomicInteger();
    /** 资源 methodA 真正开始干活时就开始计数,表示 methodA 的任务活跃数 **/
    private static AtomicInteger activeThread = new AtomicInteger();

    private static void initDegradeRule() {
    
    
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();

        // 设置资源名称为:methodA
        rule.setResource(KEY);

        // 设置熔断策略:慢调用比例策略
        rule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType());

        // 既然策略为慢调用比例,那么设置当请求的响应时间大于 50 毫秒时,则统计为慢调用
        rule.setCount(50);

        // 触发熔断条件 1:最小请求数,若【统计时长】内请求数小于该值时,即使【异常比率】超出 count 阈值也不会熔断
        rule.setMinRequestAmount(10);

        // 触发熔断条件 2:所谓的【统计时长】是多少,即到底在多少时间内进行统计计数
        rule.setStatIntervalMs(8000);
        // 触发熔断条件 3:所谓的【异常比率】其实就是一个 0 到 1 之间的数值,异常数 = minRequestAmount * slowRatioThreshold
        rule.setSlowRatioThreshold(0.2);
        // 当熔断触发后,熔断时长(10 秒)内请求会自动被熔断
        // 经过熔断时长后,若接下来的一个请求响应时间小于 RT 则结束熔断
        rule.setTimeWindow(10);
        // 将设置好的规则,添加至列表中,并且加载到熔断降级管理器中
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
        System.out.println("Degrade rule loaded: " + rules);
    }
    public static void main(String[] args) throws Exception {
    
    
        initDegradeRule();
        registerStateChangeObserver();
        startTick();

        int concurrency = 8;
        for (int i = 0; i < concurrency; i++) {
    
    
            Thread entryThread = new Thread(new DegradeRTTask());
            entryThread.setName("sentinel-simulate-traffic-task-" + i);
            entryThread.start();
        }
    }
    private static void registerStateChangeObserver() {
    
    
        EventObserverRegistry.getInstance().addStateChangeObserver("logging",
            (prevState, newState, rule, snapshotValue) -> {
    
    
                if (newState == State.OPEN) {
    
    
                    System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(),
                        TimeUtil.currentTimeMillis(), snapshotValue));
                } else {
    
    
                    System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(),
                        TimeUtil.currentTimeMillis()));
                }
            });
    }
    private static void sleep(int timeMs) {
    
    
        try {
    
    
            TimeUnit.MILLISECONDS.sleep(timeMs);
        } catch (InterruptedException e) {
    
    
            // ignore
        }
    }

    private static void startTick() {
    
    
        Thread timer = new Thread(new TimerTask());
        timer.setName("sentinel-timer-tick-task");
        timer.start();
    }

    static class TimerTask implements Runnable {
    
    
        @Override
        public void run() {
    
    
            long start = System.currentTimeMillis();
            System.out.println("Begin to run! Go go go!");
            System.out.println("See corresponding metrics.log for accurate statistic data");

            long oldTotal = 0;
            long oldActivePass = 0;
            long oldBlock = 0;

            while (!stop) {
    
    
                sleep(1000);

                long globalTotal = total.get();
                long oneSecondTotal = globalTotal - oldTotal;
                oldTotal = globalTotal;

                long currActivePass = activeThread.get();
                long oneSecondPass = currActivePass - oldActivePass;
                oldActivePass = currActivePass;

                long globalBlock = block.get();
                long oneSecondBlock = globalBlock - oldBlock;
                oldBlock = globalBlock;

                System.out.print("【秒】倒计时: " + seconds + ", ");
                System.out.println((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date()))
                        + ", 每秒总请求量:" + oneSecondTotal
                        + ", 每秒活跃量:" + oneSecondPass
                        + ", 每秒拒绝量:" + oneSecondBlock);
                if (seconds-- <= 0) {
    
    
                    stop = true;
                }
            }

            long cost = System.currentTimeMillis() - start;
            System.out.println("time cost: " + cost + " ms");
            System.out.println("total: " + total.get() + ", pass:" + activeThread.get()
                + ", block:" + block.get());
            System.exit(0);
        }
    }

    static class DegradeRTTask implements Runnable {
    
    

        @Override
        public void run() {
    
    
            while (true) {
    
    
                Entry entry = null;
                try {
    
    
                    entry = SphU.entry(KEY);
                    activeThread.incrementAndGet();
                    // RT: [40ms, 80ms)
                    sleep(ThreadLocalRandom.current().nextInt(40, 80));
                } catch (BlockException e) {
    
    
                    block.incrementAndGet();
                    sleep(ThreadLocalRandom.current().nextInt(5, 10));
                } finally {
    
    
                    total.incrementAndGet();
                    if (entry != null) {
    
    
                        entry.exit();
                    }
                }
            }
        }
    }
}
  • Abnormales Verhältnis
    bedeutet, dass wir, wenn die Ausnahme unser voreingestelltes Verhältnis überschreitet, nicht innerhalb der von uns festgelegten Zeit arbeiten, bis wir eine Anfrage erfolgreich verarbeitet und mit der Arbeit fortfahren.
public class Degrade_2_ExceptionRatio_Demo {
    
    

    private static final String KEY = "methodA";

    private static AtomicInteger total = new AtomicInteger();
    private static AtomicInteger pass = new AtomicInteger();
    private static AtomicInteger block = new AtomicInteger();
    private static AtomicInteger bizException = new AtomicInteger();

    private static volatile boolean stop = false;
    private static int seconds = 120;

    private static void initDegradeRule() {
    
    
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();

        // 设置资源名称为:methodA
        rule.setResource(KEY);

        // 设置熔断策略:异常比例策略
        rule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType());

        // 既然策略为异常比例,出现业务异常则统计异常数量,当异常比例大于 50% 时,则触发熔断
        rule.setCount(0.5d);

        // 触发熔断条件 1:最小请求数,若【统计时长】内请求数小于该值时,即使【异常比例】超出 count 阈值也不会熔断
        rule.setMinRequestAmount(10);

        // 触发熔断条件 2:所谓的【统计时长】是多少,即到底在多少时间内进行统计计数
        rule.setStatIntervalMs(10000);

        // 当熔断触发后,熔断时长(10 秒)内请求会自动被熔断
        // 经过熔断时长后,若接下来的一个请求成功返回,则结束熔断
        rule.setTimeWindow(10);

        // 将设置好的规则,添加至列表中,并且加载到熔断降级管理器中
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
        System.out.println("Degrade rule loaded: " + rules);
    }

    public static void main(String[] args) throws Exception {
    
    
        initDegradeRule();
        registerStateChangeObserver();
        startTick();

        final int concurrency = 8;
        for (int i = 0; i < concurrency; i++) {
    
    
            Thread entryThread = new Thread(new DegradeErrorRatioTask());
            entryThread.setName("sentinel-simulate-traffic-task-" + i);
            entryThread.start();
        }
    }

    private static void registerStateChangeObserver() {
    
    
        EventObserverRegistry.getInstance().addStateChangeObserver("logging",
            (prevState, newState, rule, snapshotValue) -> {
    
    
                if (newState == State.OPEN) {
    
    
                    System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(),
                        TimeUtil.currentTimeMillis(), snapshotValue));
                } else {
    
    
                    System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(),
                        TimeUtil.currentTimeMillis()));
                }
            });
    }


    private static void sleep(int timeMs) {
    
    
        try {
    
    
            TimeUnit.MILLISECONDS.sleep(timeMs);
        } catch (InterruptedException e) {
    
    
            // ignore
        }
    }

    private static void startTick() {
    
    
        Thread timer = new Thread(new TimerTask());
        timer.setName("sentinel-timer-tick-task");
        timer.start();
    }

    static class TimerTask implements Runnable {
    
    
        @Override
        public void run() {
    
    
            long start = System.currentTimeMillis();
            System.out.println("Begin to run! Go go go!");
            System.out.println("See corresponding metrics.log for accurate statistic data");

            long oldTotal = 0;
            long oldPass = 0;
            long oldBlock = 0;
            long oldBizException = 0;
            while (!stop) {
    
    
                sleep(1000);

                long globalTotal = total.get();
                long oneSecondTotal = globalTotal - oldTotal;
                oldTotal = globalTotal;

                long globalPass = pass.get();
                long oneSecondPass = globalPass - oldPass;
                oldPass = globalPass;

                long globalBlock = block.get();
                long oneSecondBlock = globalBlock - oldBlock;
                oldBlock = globalBlock;

                long globalBizException = bizException.get();
                long oneSecondBizException = globalBizException - oldBizException;
                oldBizException = globalBizException;

//                System.out.println(TimeUtil.currentTimeMillis() + ", oneSecondTotal:" + oneSecondTotal
//                    + ", oneSecondPass:" + oneSecondPass
//                    + ", oneSecondBlock:" + oneSecondBlock
//                    + ", oneSecondBizException:" + oneSecondBizException);


                System.out.print("【秒】倒计时: " + seconds + ", ");
                System.out.println((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date()))
                        + ", 每秒总请求量:" + oneSecondTotal
                        + ", 每秒完成量:" + oneSecondPass
                        + ", 每秒拒绝量:" + oneSecondBlock
                        + ", 每秒业务异常量:" + oneSecondBizException);

                if (seconds-- <= 0) {
    
    
                    stop = true;
                }
            }
            long cost = System.currentTimeMillis() - start;
            System.out.println("time cost: " + cost + " ms");
            System.out.println("total: " + total.get() + ", pass:" + pass.get()
                + ", block:" + block.get() + ", bizException:" + bizException.get());
            System.exit(0);
        }
    }

    static class DegradeErrorRatioTask implements Runnable {
    
    

        @Override
        public void run() {
    
    
            while (true) {
    
    
                Entry entry = null;
                try {
    
    
                    entry = SphU.entry(KEY);
                    sleep(ThreadLocalRandom.current().nextInt(5, 10));
                    pass.addAndGet(1);

                    // Error probability is 45%
                    if (ThreadLocalRandom.current().nextInt(0, 100) > 55) {
    
    
                        // biz code raise an exception.
                        throw new RuntimeException("oops");
                    }
                } catch (BlockException e) {
    
    
                    block.addAndGet(1);
                    sleep(ThreadLocalRandom.current().nextInt(5, 10));
                } catch (Throwable t) {
    
    
                    bizException.incrementAndGet();
                    // It's required to record exception here manually.
                    Tracer.traceEntry(t, entry);
                } finally {
    
    
                    total.addAndGet(1);
                    if (entry != null) {
    
    
                        entry.exit();
                    }
                }
            }
        }
    }
}
Nutzen Sie das Framework direkt

Fügen Sie hier eine Bildbeschreibung ein

Nach dem Herunterladen der JAR-Datei können Sie eine Befehlsstartdatei erstellen: „startup.cmd“.

java -Dserver.port=9999 -Dcsp.sentinel.dashboard.server=localhost:9999 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

Dann gibt es eine Konsole wie diese:
Fügen Sie hier eine Bildbeschreibung ein


  • Übergeordnetes Sentinel Server -POM
 <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${com.alibaba.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${com.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-dubbo -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-dubbo</artifactId>
                <version>${com.dubbo.version}</version>
            </dependency>
        </dependencies>

    </dependencyManagement>

Technisches POM

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Fügen Sie hier eine Bildbeschreibung ein
Fügen Sie hier eine Bildbeschreibung ein

Fügen Sie hier eine Bildbeschreibung ein

Stellen Sie eine Verbindung zu Nacos her

Fügen Sie hier eine Bildbeschreibung ein

Ingenieurwesen

 <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
     </dependency>
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
     </dependency>
     <dependency>
         <groupId>com.alibaba.cloud</groupId>
         <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
         <groupId>com.alibaba.csp</groupId>
         <artifactId>sentinel-datasource-nacos</artifactId>
     </dependency>
spring:
  application:
    name: sentinel-online-flowdeg-consumer
  cloud:
    nacos:
      discovery:
        server-addr: 111.229.199.181:8848
    sentinel:
      transport:
        port: 8719
        dashboard: 127.0.0.1:9999
      datasource:
        r1:
          nacos:
            # nacos地址
            server-addr: 111.229.199.181:8848
            # nacos中配置文件的data-id
            data-id: sentinel-online-flowdeg-consumer
            # nacos 分组
            group-id: DEFAULT_GROUP
            data-type: json
            # 规则类型 流控
            rule-type: flow
            namespace: 05b6571e-7791-4af9-9522-f8097beac3d7

server:
  port: 9064

Nacos-Konfiguration

[
    {
    
    
        "limitApp": "default",
        "resource": "/echo/{string}",
        "grade": 1,
        "count": 20,
        "strategy": 0,
        "refResource": null,
        "controlBehavior": 0,
        "warmUpPeriodSec": null,
        "maxQueueingTimeMs": null,
        "clusterMode": false,
        "clusterConfig":
        {
    
    
            "flowId": null,
            "thresholdType": 0,
            "fallbackToLocalWhenFail": true,
            "strategy": 0,
            "sampleCount": 10,
            "windowIntervalMs": 1000,
            "resourceTimeout": 2000,
            "resourceTimeoutStrategy": 0,
            "acquireRefuseStrategy": 0,
            "clientOfflineTime": 2000
        },
        "gmtCreate": "2024-01-05T06:03:30.663+00:00",
        "gmtModified": "2024-01-05T06:03:30.663+00:00"
    }
]

Fügen Sie hier eine Bildbeschreibung ein

Nacos Sentinel-Beharrlichkeit

Fügen Sie hier eine Bildbeschreibung ein

Nach dem Auslösen eines HTTP-Anforderungsaufrufs wurde festgestellt, dass der Haltepunkt com.alibaba.csp.sentinel.init.InitExecutor#doInit eingegeben wurde.

Analyse zirkulärer Logikcodeblöcke

for (InitFunc initFunc : loader) {
    
    
	RecordLog.info("[InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName());
	insertSorted(initList, initFunc);
}
for (OrderWrapper w : initList) {
    
    
    w.func.init();
    RecordLog.info(String.format("[InitExecutor] Executing %s with order %d",
                                 w.func.getClass().getCanonicalName(), w.order));
}

Die relevante Sende- und Empfangsdatenlogik von com.alibaba.csp.sentinel.transport.command.SimpleHttpCommandCenter#start wurde gefunden

Schlüsselcode 1: Starten Sie den Thread auf Serverebene


socketReference = serverSocket;
executor.submit(new ServerThread(serverSocket));
success = true;
port = serverSocket.getLocalPort();

Schlüsselcode 2: Daten von der Accept-Methode von ServerSocket empfangen

socket = this.serverSocket.accept();
setSocketSoTimeout(socket);
HttpEventTask eventTask = new HttpEventTask(socket);
bizExecutor.submit(eventTask);

Kerncode für die Datenquellenverarbeitung

if (FLOW_RULE_TYPE.equalsIgnoreCase(type)) {
    
    
    List<FlowRule> flowRules = JSONArray.parseArray(data, FlowRule.class);
    FlowRuleManager.loadRules(flowRules);
    if (!writeToDataSource(getFlowDataSource(), flowRules)) {
    
    
        result = WRITE_DS_FAILURE_MSG;
    }
    return CommandResponse.ofSuccess(result);
}
Basierend auf der obigen Quellcode-Analyse

Wir schreiben die folgenden zwei Klassen, um die Persistenz der Strombegrenzungskonfiguration zu vervollständigen.

@Configuration
public class NacosLinkSentinelConfig implements InitializingBean {
    
    

    @Override
    public void afterPropertiesSet() throws Exception {
    
    
    }

    @PostConstruct
    public void init() throws Exception {
    
    
        NacosWritableDataSource ds = new NacosWritableDataSource(
                "sentinel-nacos-persist-consumer-flow.json",
                "DEFAULT_GROUP",
                "ip",

                "05b6571e-7791-4af9-9522-f8097beac3d7"
        );

        WritableDataSourceRegistry.registerFlowDataSource(ds);
    }
}

public class NacosWritableDataSource<T> implements WritableDataSource<T> {
    
    

    private String dataId;
    private String group;
    private ConfigService configService;

    public NacosWritableDataSource(String dataId,
                                   String group,
                                   String serverAddr,
                                   String namespace) throws Exception {
    
    
        this.dataId = dataId;
        this.group = group;

        Properties properties = new Properties();
        properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddr);
        properties.setProperty(PropertyKeyConst.NAMESPACE, namespace);
        properties.setProperty(PropertyKeyConst.USERNAME, "nacos");
        properties.setProperty(PropertyKeyConst.PASSWORD, "nacos");
        configService = NacosFactory.createConfigService(properties);
    }

    @Override
    public void write(T t) throws Exception {
    
    
        configService.publishConfig(this.dataId, this.group, JSON.toJSONString(t),  "json");
    }

    @Override
    public void close() throws Exception {
    
    
    }
}

Auf diese Weise können unsere Änderungen an der Schnittstelle für Nacos beibehalten werden. Hierbei ist zu beachten, dass sie nicht auf der Konsole wahrgenommen werden, wenn wir sie direkt in Nacos ändern.
Fügen Sie hier eine Bildbeschreibung ein
Beim nächsten Neustart der Konsole werden auch die Nacos-Persistenzregeln geladen:
Fügen Sie hier eine Bildbeschreibung ein

Supongo que te gusta

Origin blog.csdn.net/qq_43259860/article/details/135385265
Recomendado
Clasificación