Анализ принципа адаптивного ограничения тока Sentinel System

Java

Я думаю, что система адаптивного ограничения тока по-прежнему является относительно практичной и ценной функцией в реальной производственной среде.В реальной производственной среде нам на самом деле трудно иметь четкое представление о нашей системе, например, о количестве запросов в секунду, которое может быть выполнено. время отклика на запросы и другие показатели. Конечно, вы можете сказать, что мы можем провести стресс-тестирование системы для получения соответствующих показателей, но эти показатели на самом деле связаны с тестовым сервером, средой размещения службы, нагрузкой и другими факторами в данный момент, поэтому мы можем получить только индикаторы измерения стресса Общее понимание в сочетании с соответствующим опытом для оценки производительности системы. Поэтому, если вы не уверены в количестве запросов в секунду, которое может выдержать система, и сосредоточены на балансировке производительности системы с точки зрения нагрузки на систему, адаптивное ограничение тока на самом деле является хорошим выбором.

Самоадаптация системы, «система» просто означает выбор нескольких показателей системы, «адаптивная» — это использование определенного алгоритма для расчета результата на основе этих показателей, а затем сравнение результата с заданным пороговым значением, чтобы определить, является ли необходимо запустить механизм ограничения тока. В качестве критериев оценки в Sentinel выбраны следующие системные индикаторы, которые можно установить независимо или одновременно:

  • системная нагрузка
  • использование процессора
  • Среднее время ответа на запрос
  • Количество одновременных потоков
  • Входящие запросы в секунду

В настоящее время адаптация запускается в соответствии с нагрузкой на систему. Он заимствует идею алгоритма TCP BBR (вы можете составить свое собственное мнение), и его цель — сделать так, чтобы ваша система по-прежнему имела высокую пропускную способность в условиях высокой нагрузки. В реализации Sentinel фактически происходит ситуация загрузки системы сбора времени, если значение нагрузки выше установленного порога, сработает проверка BBR, о которой подробно будет рассказано далее. Адаптивное ограничение тока предназначено для входящего трафика, то есть entryType ресурса — EntryType.IN. На самом деле ограничений на этот вид настройки нет, использовать можно на любом ресурсе, конечно, лучше всего все же на входе трафика в систему. На практике я создам перехватчик, чтобы весь трафик проходил через этот перехватчик, чтобы в этом перехватчике можно было выполнять адаптивное дросселирование потока.

Вышеприведенное кратко описывает некоторые ситуации системного адаптивного ограничения тока.Если вам нужно подробно разобраться в соответствующем контексте, вы можете обратиться к официальномуДокументация. Далее будет введен исходный код для детального анализа. Адаптивное ограничение тока системы выполняется в классе метода входа в классе SystemSlot.

SpiOrder(-5000)
public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> {

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable {
        //系统规则校验
        SystemRuleManager.checkSystem(resourceWrapper);
        fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }

    @Override
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
        fireExit(context, resourceWrapper, count, args);
    }

}

Введите метод SystemRuleManager.checkSystem:

public static void checkSystem(ResourceWrapper resourceWrapper) throws BlockException {
        if (resourceWrapper == null) {
            return;
        }
        //系统全局检测开关是否开启
        if (!checkSystemStatus.get()) {
            return;
        }

        // 判断是否为入口流量
        if (resourceWrapper.getEntryType() != EntryType.IN) {
            return;
        }

        // 获取当前总的qps
        double currentQps = Constants.ENTRY_NODE == null ? 0.0 : Constants.ENTRY_NODE.successQps();
        //如果大于了设置了系统qps,则抛出异常
        if (currentQps > qps) {
            throw new SystemBlockException(resourceWrapper.getName(), "qps");
        }

        //如果当前系统的线程数大于设置值,则抛出异常
        int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum();
        if (currentThread > maxThread) {
            throw new SystemBlockException(resourceWrapper.getName(), "thread");
        }
		//如果当前系统的平均响应时间大于设置值,则抛出异常
        double rt = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.avgRt();
        if (rt > maxRt) {
            throw new SystemBlockException(resourceWrapper.getName(), "rt");
        }

        // 获取当前系统负载,如果当前负载大于设置值,则会根据BBR算法来进一步判断是否需要限流
        if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
            if (!checkBbr(currentThread)) {
                throw new SystemBlockException(resourceWrapper.getName(), "load");
            }
        }

        //如果当前系统cpu使用值大于设置值,则抛出异常
        if (highestCpuUsageIsSet && getCurrentCpuUsage() > highestCpuUsage) {
            throw new SystemBlockException(resourceWrapper.getName(), "cpu");
        }
    }

Проверка BBR делается в методе checkBbr Код на самом деле очень простой:

    private static boolean checkBbr(int currentThread) {
        if (currentThread > 1 &&
            currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) {
            return false;
        }
        return true;
    }

Как видно из приведенного выше кода, Sentinel сравнивает текущее количество потоков с произведением максимального количества запросов в секунду и минимального времени отклика системы. Что касается того, почему это сделано, вы можете обратиться к официальной документации. Эта связь объясняется здесь из самой формулы. Мы предполагаем, что maxQps = 100, minRt = 10 мс, если система работает с этим показателем, количество необходимых потоков в секунду является их произведением 10 = 100 * 10 / 1000, этот результат представляетМаксимальная пропускная способность системы. Если количество запущенных в данный момент потоков превышает это значение, система достигла своего «предела». Нагрузка на систему и связанные с ней показатели производительности на самом деле меняются.Sentinel собирает соответствующие показатели системы через временной интервал задачи 1 с. Код определяет класс SystemStatusListener, который реализует интерфейс Runable и отвечает за сбор системных метрик:

private static SystemStatusListener statusListener = null;
private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1,
        new NamedThreadFactory("sentinel-system-status-record-task", true));
static {
        statusListener = new SystemStatusListener();
        //当前类创建的时候就会采集系统指标,间隔1s
        scheduler.scheduleAtFixedRate(statusListener, 0, 1, TimeUnit.SECONDS);
    }

Глядя на метод запуска SystemStatusListener, вы можете узнать, какие индикаторы собираются.Получение этих индикаторов на самом деле зависит от интерфейса, предоставляемого JMX.

@Override
    public void run() {
        try {
           //获取操作系统相关信息
            OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
            currentLoad = osBean.getSystemLoadAverage();

         	//获取系统负载
            double systemCpuUsage = osBean.getSystemCpuLoad();
			//获取jvm相关信息
            RuntimeMXBean runtimeBean = ManagementFactory.getPlatformMXBean(RuntimeMXBean.class);
            //jvm进程占用cpu的时间
            long newProcessCpuTime = osBean.getProcessCpuTime();
            //jvm启动时间
            long newProcessUpTime = runtimeBean.getUptime();
            //可用处理器数量
            int cpuCores = osBean.getAvailableProcessors();
            //两次采集间隔间,jvm占用的cpu时间
            long processCpuTimeDiffInMs = TimeUnit.NANOSECONDS
                    .toMillis(newProcessCpuTime - processCpuTime);
            //两次采集间隔间,jvm运行时间
            long processUpTimeDiffInMs = newProcessUpTime - processUpTime;
            //获取jvm进程的cup使用率
            double processCpuUsage = (double) processCpuTimeDiffInMs / processUpTimeDiffInMs / cpuCores;
            processCpuTime = newProcessCpuTime;
            processUpTime = newProcessUpTime;
			//取系统和jvm cpu使用率中的较大者
            currentCpuUsage = Math.max(processCpuUsage, systemCpuUsage);

        } catch (Throwable e) {
            RecordLog.warn("[SystemStatusListener] Failed to get system metrics from JMX", e);
        }
    }

Как видно из приведенного выше кода, существует только два индикатора, которые Sentinel получает от системы: загрузка системы и использование чашки. Тут у вас может возникнуть вопрос.При получении загрузки системы берется большая из системы и jvm. По здравому смыслу, jvm это просто процесс в системе, и нагрузка на систему должна быть больше, чем нагрузка на jvm.Почему бы просто не получить нагрузку на систему напрямую? На самом деле причина в том, что показатели нашей системы собираются дискретно, возможно, что в момент сбора нагрузка на систему как раз относительно невелика, а в период может быть высокая нагрузка, полученные значения не являются репрезентативными, поэтому принимается больший из двух, который имеет лучшее представительство.

Приведенный выше код представляет собой весь процесс адаптивного ограничения тока Ключом к адаптивности является выполнение проверки BBR, когда нагрузка системы превышает установленный порог.