Распределенная система управления потоком и автоматический выключатель: использование Sentinel

Java

предисловие

С ростом популярности микросервисов стабильность между сервисами и сервисами становится все более важной. Sentinel использует трафик в качестве точки входа для защиты стабильности сервисов от нескольких измерений, таких как управление трафиком, переход на более раннюю версию автоматического выключателя и защита системной нагрузки.

Sentinel обладает следующими характеристиками:

  • Разнообразные сценарии приложений: за последние 10 лет Sentinel реализовал основные сценарии Double Eleven от Alibaba, такие как всплески (т. кластерный контроль трафика, предохранители в реальном времени, нисходящие недоступные приложения и т. д.

  • Полный мониторинг в реальном времени: Sentinel также предоставляет функции мониторинга в реальном времени. В консоли можно увидеть данные второго уровня отдельной машины, подключенной к приложению, и даже агрегированную работу кластера с масштабом менее 500.

  • Обширная экосистема с открытым исходным кодом: Sentinel предоставляет готовые модули интеграции с другими платформами/библиотеками с открытым исходным кодом, такие как интеграция с Spring Cloud, Dubbo, gRPC. Вам нужно только ввести соответствующие зависимости и выполнить простую настройку, чтобы быстро получить доступ к Sentinel.

  • Полные точки расширения SPI: Sentinel предоставляет простые в использовании и полные интерфейсы расширения SPI. Вы можете быстро настроить логику, реализовав интерфейсы расширения. Например, управление пользовательскими правилами, адаптация источников динамических данных и т. д.

Приведенный выше контент взят из официального представления Sentinel. В этой статье автор изучит использование Sentinel с точки зрения практического применения.

1. Первое знакомство со Sentinel

Во-первых, нам нужно импортировать зависимости Sentinel.

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.7.2</version>
</dependency>

Sentinel поддерживает следующие правила: правила управления потоком, правила перехода на более раннюю версию автоматического выключателя, правила защиты системы, правила управления доступом к источнику и правила параметров точки доступа.

Здесь давайте покажем пример управления потоком и деградации автоматического выключателя.

1. Управление потоком

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

Давайте возьмем QPS в качестве примера, чтобы сначала определить его правила См. комментарии для значения связанных атрибутов.

/**
 * 加载限流规则
 * @param resource
 */
public static void loadFlowRules(String resource){
    FlowRule rule = new FlowRule();
    //资源名称,可以是任意字符串
    rule.setResource(resource);
    //限流阈值
    rule.setCount(5);
    //限流阈值类型,设置为QPS。即每秒QPS大于5时,触发限流
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    //针对的调用来源
    rule.setLimitApp("default");
    //调用关系限流策略,默认按照资源本身
    rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
    //限流效果,默认直接拒绝
    rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
    //是否集群限流
    rule.setClusterMode(false);
    FlowRuleManager.loadRules(Collections.singletonList(rule));
}

Как показано в приведенном выше коде, когда количество запросов в секунду достигает 5, последующие запросы в текущем временном окне будут напрямую отклонены.

Далее мы используем Sentinel API для кода, который должен управлять трафиком.SphU.entry("resource") 和 entry.exit()Просто окружите его.

public static void main(String[] args) throws InterruptedException {
    loadFlowRules("orderService");
    while (!stop){
        count.incrementAndGet();
        Entry entry = null;
        try {
            entry = SphU.entry(resource);
            logger.info("业务操作...{}",count.get());
        } catch (BlockException e) {
            logger.error("请求被限流...{}",count.get());
            Thread.sleep(1000);
        } finally {
            if (entry != null) {
                entry.exit();
            }
            if (count.get()>=20){
                stop = true;
            }
        }
    }
}

Как и в приведенном выше коде, мы сначала передаемloadFlowRules()метод для загрузки текущих ограничивающих правил. Затем окружите бизнес-операции Sentinel API.

Текущий предельный порог, который мы определили, равен 5, и здесь всего 20 запросов. После запуска дросселирования наш поток приостанавливается на 1 секунду, чтобы пройти текущее временное окно, поэтому будет 3 запроса, которые будут дросселированы.

Запустив код, мы можем получить следующие результаты:

14:38:00.463  - 业务操作...1
14:38:00.465  - 业务操作...2
14:38:00.465  - 业务操作...3
14:38:00.465  - 业务操作...4
14:38:00.465  - 业务操作...5
14:38:00.494  - 请求被限流...6
14:38:01.494  - 业务操作...7
14:38:01.494  - 业务操作...8
14:38:01.495  - 业务操作...9
14:38:01.495  - 业务操作...10
14:38:01.495  - 业务操作...11
14:38:01.496  - 请求被限流...12
14:38:02.497  - 业务操作...13
14:38:02.497  - 业务操作...14
14:38:02.497  - 业务操作...15
14:38:02.497  - 业务操作...16
14:38:02.497  - 业务操作...17
14:38:02.497  - 请求被限流...18
14:38:03.498  - 业务操作...19
14:38:03.498  - 业务操作...20

2. Фьюзинг

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

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

Так как же измерить стабильность ресурсов?

Sentinel предоставляет три метода: среднее время отклика, коэффициент аномалий и количество аномалий.

Давайте возьмем в качестве примера среднее время отклика и сначала определим его правила.

/**
 * 1秒内的5个请求,平均响应时间大于10ms,接下来的3秒内都会自动熔断。
 * @param resourceName
 */
public static void loadDegradeRule(String resourceName){
    List<DegradeRule> rules = new ArrayList<>();
    DegradeRule rule = new DegradeRule();
    //资源名称
    rule.setResource(resourceName);
    //阈值 - 10ms
    rule.setCount(10);
    //熔断策略 - RT模式
    rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
    //时间窗口 - 3s
    rule.setTimeWindow(3);
    //RT模式下,1秒内连续多少个请求的平均RT超出阈值,才可以触发熔断
    rule.setRtSlowRequestAmount(5);
    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

Как показано в приведенном выше коде, мы определяем правила слияния, смотрим комментарии для значения атрибутов, а затем смотрим на тестовые примеры.

public static void main(String[] args)throws InterruptedException {
	loadDegradeRule(resource);
	while (!stop){
		count.incrementAndGet();
		Entry entry = null;
		try {
			entry = SphU.entry(resource);
			logger.info("业务操作...{}",count.get());
			Thread.sleep(15);
		} catch (BlockException e) {
			if (e instanceof DegradeException){
				logger.error("触发熔断机制...{}",count.get());
				Thread.sleep(500);
			}
		} finally {
			if (entry != null) {
				entry.exit();
			}
			if (count.get()>=20){
				stop = true;
			}
		}
	}
	logger.info("----------------------------");
}

В приведенном выше коде у нас всего 20 запросов. Мы остановили поток на 15 мс, чтобы среднее значение RT превысило пороговое значение, превышающее 10 мс.

Правила, которые мы определяем,1秒内连续5个请求的平均RT超出阈值,就可以触发熔断, поэтому при поступлении 6-го запроса сработает автоматический выключатель.

Как долго работает предохранитель? Как раз в 3-секундном временном окне.

В приведенном выше тестовом коде после срабатывания автоматического выключателя мы вручную приостанавливаем поток на 1000 мс, поэтому для каждого автоматического выключателя будет 3 запроса.

Так ли это, давайте запустим код и посмотрим результат:

10:56:20.022 [main] INFO orderService - 业务操作...1
10:56:20.040 [main] INFO orderService - 业务操作...2
10:56:20.056 [main] INFO orderService - 业务操作...3
10:56:20.072 [main] INFO orderService - 业务操作...4
10:56:20.088 [main] INFO orderService - 业务操作...5
10:56:20.127 [main] ERROR orderService - 触发熔断机制...6
10:56:21.128 [main] ERROR orderService - 触发熔断机制...7
10:56:22.128 [main] ERROR orderService - 触发熔断机制...8
10:56:23.129 [main] INFO orderService - 业务操作...9
10:56:23.145 [main] INFO orderService - 业务操作...10
10:56:23.160 [main] INFO orderService - 业务操作...11
10:56:23.176 [main] INFO orderService - 业务操作...12
10:56:23.192 [main] INFO orderService - 业务操作...13
10:56:23.207 [main] ERROR orderService - 触发熔断机制...14
10:56:24.208 [main] ERROR orderService - 触发熔断机制...15
10:56:25.208 [main] ERROR orderService - 触发熔断机制...16
10:56:26.209 [main] INFO orderService - 业务操作...17
10:56:26.224 [main] INFO orderService - 业务操作...18
10:56:26.240 [main] INFO orderService - 业务操作...19
10:56:26.255 [main] INFO orderService - 业务操作...20
10:56:26.271 [main] INFO orderService - ----------------------------

На данный момент мы можем сказать, что Sentinel работает нормально.

2. Системная интеграция

Вышеприведенный пример — очень простой демонстрационный пример. Если мы хотим использовать Sentinel в нашем проекте SpringBoot, потребуется некоторая работа.

1. Консоль Sentinel

Sentinel предоставляет облегченную консоль с открытым исходным кодом, разработанную с использованием SpringBoot.

Он обеспечивает обнаружение компьютеров и управление их работоспособностью, мониторинг (отдельных компьютеров и кластеров), управление правилами и функции push-уведомлений.

Итак, давайте сначала запустим эту консоль.

Первый шаг требуетhttps://github.com/alibaba/Sentinel/releasesС этого адреса загрузите последнюю версию пакета jar консоли.

Второй шаг, используйте команду для запуска консольной программы, где-Dserver.port=9080Используется для указания порта консоли Sentinel.

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

На третьем этапе наша бизнес-система представляет модуль Transport для связи с консолью Sentinel.

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.7.2</version>
</dependency>

Четвертый шаг, в нашей бизнес-системе, установите параметры запуска JVM, чтобы указать адрес консоли Sentinel.

-Dcsp.sentinel.dashboard.server=127.0.0.1:9080

Наконец, запустите нашу бизнес-систему, затем откройте консоль Sentinel, если вы видите список машин, все.

2. Определите правила

Перед определением правил нам необходимо спланировать объем ресурсов.

Что это обозначает? Например, если мы возьмем в качестве примера бизнес-заказ, все ли операции с заказами считаются ресурсом? Или разделите его, создание заказа считается одним ресурсом, а запрос заказа считается другим ресурсом.

Поэтому мы можем сначала определить имя ресурса, потоком которого мы хотим управлять.

public final class ResourceConstants {
    public static final String ORDER_SERVICE = OrderService.class.getName();
    public static final String ORDER_SERVICE_ORDERS = ORDER_SERVICE+".orders";
    public static final String ORDER_SERVICE_CREATE = ORDER_SERVICE+".create";
}

Поскольку это проект SpringBoot, мы можем загружать правила управления потоком при запуске системы.

@Component
public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        initFlowRule(ResourceConstants.ORDER_SERVICE,5);
        initFlowRule(ResourceConstants.ORDER_SERVICE_ORDERS,5);
    }
    public void initFlowRule(String resourceName,int count) {
        FlowRule flowRule = new FlowRule(resourceName)
                .setCount(count)
                .setGrade(RuleConstant.FLOW_GRADE_QPS);
        List<FlowRule> list = new ArrayList<>();
        list.add(flowRule);
        FlowRuleManager.loadRules(list);
    }
}

Затем мы добавляем код Sentinel в контроллер, чтобы добиться эффекта управления потоком.

@RequestMapping("/getOrders")
public ResponseEntity getOrders(){
    Entry entry = null;
    try {
        entry = SphU.entry(ResourceConstants.ORDER_SERVICE_ORDERS);
        return ResponseEntity.ok(orderService.orders());
    } catch (BlockException e) {
        logger.error("请求被限流...{}",e.getRule().getResource());
        return ResponseEntity.badRequest().body(e.getRule());
    } finally {
        if (entry != null) {
            entry.exit();
        }
    }
}

Теперь давайте проверим его с помощью JMeter и запустим 10 потоков для запроса этого интерфейса. Только 5 запросов будут переданы и 5 запросов будут отклонены.

Пока что мы можем просто использовать Sentinel в проекте SpringBoot, но на данный момент есть две очевидные проблемы.

  • Везде, где требуется управление потоком, слишком навязчиво и неудобно, чтобы его жестко запрограммировали через API;
  • Правила управления потоком хранятся только в памяти и исчезают после перезапуска системы, а постоянные данные правил отсутствуют.

Далее решаем две вышеуказанные задачи.

3. Адаптация кадра

Благодаря обширной экосистеме с открытым исходным кодом Sentinel предоставляет готовые модули интеграции с другими платформами/библиотеками с открытым исходным кодом. Нам нужно только ввести соответствующие зависимости и выполнить простую настройку для быстрого доступа к Sentinel.

Мы надеемся, что сможем контролировать поток веб-запросов, поэтому нам нужно ввести Sentinel, чтобы обеспечить интеграцию с Servlet.

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
    <version>1.7.2</version>
</dependency>

1. Конфигурация фильтра

Поскольку это приложение SpringBoot, мы настраиваем его через Configuration.

@Configuration
public class SentinelFilterConfig {
    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new CommonFilter());
        registration.addUrlPatterns("/*");
        registration.setName("sentinelFilter");
        registration.setOrder(1);
        return registration;
    }
}

В нашем собственном бизнес-коде часть Sentinel API может быть опущена.

@RequestMapping("/getOrders")
public ResponseEntity getOrders(){
    return ResponseEntity.ok(orderService.orders());
}

При условии, что правила управления потоком остаются неизменными, мы используем JMeter для запуска 10 потоков для запроса этого интерфейса. То же самое будет передавать только 5 запросов и отклонять 5 запросов.

2. Обработчик блоков URL

По умолчанию страница приглашения по умолчанию будет возвращена при регулировании запроса.

Мы можем вызвать код WebServletConfig.setBlockPage(blockPage)Метод устанавливает настраиваемый URL-адрес перехода, и когда запрос ограничен, он автоматически переходит на заданный URL-адрес.

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

Например, как показано ниже, после текущего ограничения или предохранителя клиенту будет возвращен неправильный код состояния HTTP и информация о подсказке.

public class SentinelUrlBlockHandler implements UrlBlockHandler {

    public static final String flowMsg = "触发流控机制~";
    public static final String degradeMsg = "触发熔断机制~";
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex){
        logger.error("熔断限流...{}",ex.getRule());
        response.setCharacterEncoding("UTF-8");
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        PrintWriter out = response.getWriter();
        if (ex instanceof FlowException){
            out.print(flowMsg);
        }else if (ex instanceof DegradeException){
            out.print(degradeMsg);
        }
        out.flush();
        out.close();
    }
}

Затем зарегистрируйте его в WebCallbackManager.

WebCallbackManager.setUrlBlockHandler(new SentinelUrlBlockHandler());

3. Уборщик URL

Sentinel Web Filter будет рассматривать каждый входящий URL-адрес как отдельный ресурс.

Например, в бизнесе заказов создание заказа, запрос заказа, удаление заказа и т. д. из-за разных URL-адресов будут рассматриваться как разные ресурсы.

Если мы хотим отнести эти операции к ресурсу заказа/order/*, вам нужно реализовать интерфейс UrlCleaner для очистки ресурсов.

Например, классифицируйте ресурсы следующим образом. Например/order/getOrders和/order/createOrder, станет/order/*.

public class SentinelUrlClean implements UrlCleaner {
    @Override
    public String clean(String originUrl) {
        if (originUrl == null || originUrl.isEmpty()) {
            return originUrl;
        }
        int lastSlashIndex = originUrl.lastIndexOf("/");
        if (lastSlashIndex >= 0) {
            originUrl = originUrl.substring(0, lastSlashIndex) + "/*";
        }
        return originUrl;
    }
}

Затем зарегистрируйте его в WebCallbackManager.

WebCallbackManager.setUrlCleaner(new SentinelUrlClean());

В то время он был более абсолютным, если вся система использует ресурс, то здесь можно вернуть только фиксированный url.

4. Лучшие практики

Как мы упоминали выше, текущие данные правил Sentinel хранятся только в памяти, нет возможности централизованно управлять и продвигать правила, и они не доступны в производственной среде.

Как правило, существует три способа управления и продвижения правил.

  • необработанный режим

Передайте правила клиенту и обновите их непосредственно в памяти. Он исчезает после перезапуска и не рекомендуется для использования в производственной среде.

  • Режим вытягивания

Клиент активно опрашивает и извлекает правила из центра управления правилами, который может быть СУБД, файлами и т. д. Работа в режиме реального времени не гарантируется, а слишком частое извлечение может вызвать проблемы с производительностью.

  • Push-режим

Центр правил передается унифицированным образом, и клиент постоянно отслеживает изменения, регистрируя слушателей, например, используя центры конфигурации, такие как Nacos и Zookeeper, которые лучше работают в режиме реального времени и обеспечивают согласованность. Источник данных в режиме push обычно используется в производственной среде.

В производственной среде обычно чаще используется источник данных в режиме принудительной отправки. Для источников данных в режиме отправки, таких как удаленные центры конфигурации (ZooKeeper, Nacos, Apollo и т. д.), операция отправки не должна выполняться клиентом Sentinel, а должна управляться консолью и передаваться напрямую. отвечает только за получение. Конфигурация, переданная центром конфигурации, обновляется до локальной. Таким образом, правильный способ передачи правил должен быть ** Консоль Configuration Center / Консоль Sentinel → Центр конфигурации → Источники данных Sentinel → Sentinel **, а не отправка в Центр конфигурации через источники данных Sentinel.

Далее реализуем унифицированное управление данными центром конфигурации Nacos.

1. Запустите Нако

В этой статье больше нечего сказать о Nacos, просто скачайте стартап.

2. Введите зависимости

NacosDataSource, предоставленный официальным лицом, мы можем ввести соответствующие зависимости.

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-extension</artifactId>
    <version>1.7.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.7.2</version>
</dependency>

3. Считайте данные правила из источника данных

При инициализации NacosDataSource нам нужно указать адрес сервиса, groupId и dataId Nacos.

Затем подключите Nacos на основе этой информации, чтобы прочитать данные внутри. И зарегистрируйте прослушиватель, чтобы уведомлять клиента, когда данные правила в центре конфигурации Nacos изменяются.

Это может показаться сложным, но при использовании в качестве клиента на самом деле это относительно просто. Делаем класс и подключаем его.

@Component
public class DataSourceRuleManager {

	private static final String remoteAddress = "localhost:8848";
    private static final String groupId = "sentinel.group";
    private static final String flowDataId = "flow.rule";

	@PostConstruct
    public void loadFlowRules() {
        FlowConverter converter = new FlowConverter();
		//连接Nacos,读取配置信息并通过converter将内容转换为对象
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource =
                new NacosDataSource<>(remoteAddress,groupId,flowDataId,converter);
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
    }
	//转换器 从Nacos配置中心读取到的数据转换为对象
    public class FlowConverter implements Converter {
        @Override
        public Object convert(Object source) {
            return JSON.parseArray(source.toString(),FlowRule.class);
        }
    }
}

После завершения настройки мы можем запустить бизнес-систему.

4. Добавьте данные правил из центра конфигурации Nacos.

Теперь вы можете добавлять данные правил в центр конфигурации через консоль Nacos.

Следует отметить, что поскольку наш преобразователь анализирует объект массива типа FlowRule через JSON, формат и имя атрибута в содержимом конфигурации должны совпадать, иначе парсинг не будет выполнен.

Расширяя способ чтения источника данных, когда мы публикуем новый контент в центре конфигурации Nacos, соответствующие правила в нашей бизнес-системе также будут обновляться, и правила в консоли Sentinel также будут обновляться синхронно, реализуя унификацию центр правил. Нажимайте и упорствуйте.

Другой способ - напрямую пройтиКонсоль Sentinel → Центр конфигурации, в этом случае нужно дорабатывать реализацию дашборда.Хотя процесс не сложный, он сложнее.Из-за ограниченного места в этой статье повторять их не буду. Заинтересованные друзья могут оставить сообщение для общения~

Суммировать

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

Эта статья является лишь небольшой частью экологии Sentinel.Дополнительный контент, такой как управление потоком и механизм прерывателя цепи различных стратегий, управление черным и белым списками, адаптация фреймворка, принцип реализации и т. д., будет опубликован позже, когда будет время~

Нелегко быть оригинальным, это понравится приглашенным официальным лицам перед отъездом.Это будет движущей силой для автора продолжать писать~