Весеннее облако Alibaba-Sentinel (15)

Spring Cloud

Введение

Легкий мощный компонент управления потоком, обеспечивающий надежность и мониторинг для микросервисов (облегченный контроль потока, понижение версии библиотеки Java).китайский документ

Лавинный эффект

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

Общие отказоустойчивые решения

  • тайм-аут
  • Ограничение
  • Режим Bulkhead: каждый контроллер настраивает пул потоков независимо, не мешая друг другу.
  • Режим автоматического выключателя: обслуживание ниже порогового значения (определяемого количеством ошибок/коэффициентом ошибок и т. д.), автоматический выключатель размыкается, и через некоторое время разрешается вызов службы. В случае успеха автоматический выключатель восстановлен.

Интеграция Sentinel

  • добавить зависимость
        <!--sentinel-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sentinel</artifactId>
        </dependency>
  • писать заметки
  • записать конфигурацию

исполнительный/сторожевой узел

Откройте все конечные точки, чтобы увидеть, работает ли это

# actuator
management:
  endpoints:
    web:
      exposure:
        # 生产不能全部放开且需配置安全措施
        include: '*'

Следующий контент доказывает, что интеграция прошла успешно

Консоль Стража

Загрузите ту же версию Sentinel, которая указана в нашем файле pom.ссылка на скачивание

#启动
java -jar XXX.jar
  • Посетите интерфейс входа http://localhost:8080/#/login

  • Пароль учетной записи по умолчанию дозорный/дозорный
  • Интеграция проекта
spring:
  cloud:
    sentinel:
      transport:
        # 指定sentinel控制台地址
        dashboard: localhost:8080
  • ленивая загрузка Поскольку Sentinel по умолчанию загружается лениво, он будет отображаться только после запроса любого интерфейса.

Описание функции

  • правила управления потоком

    • режим управления потоком
      • непосредственный

      • Ассоциация: связанный ресурс достигает порога и ограничивает себя

      • ссылка: трафик по указанной ссылке

        • код
        // 定义一个common服务
        package com.virgo.user.service;
        
        import com.virgo.entity.TblCar;
        
        /**
         * @author zhaozha
         * @date 2019/10/15 下午4:27
         */
        public interface CommonService {
            TblCar common();
        }
        
        package com.virgo.user.service;
        
        import com.alibaba.csp.sentinel.annotation.SentinelResource;
        import com.virgo.entity.TblCar;
        import org.springframework.stereotype.Service;
        
        /**
         * @author zhaozha
         * @date 2019/10/15 下午4:28
         */
        @Service
        public class CommonServiceImpl implements CommonService {
        
            @Override
            @SentinelResource("common")
            public TblCar common() {
                return TblCar.builder().id(1L).build();
            }
        }
        
        // controller层同时调用这个服务
            @GetMapping("/test/a")
        public String testA() {
            commonServiceImpl.common();
            return "testA";
        }
        
        @GetMapping("/test/b")
        public String testB() {
            commonServiceImpl.common();
            return "testB";
        }
        
        
        • Настройка: ограничивать только ток /test/a, не влияет на /test/b.

    • Эффект управления потоком
      • провалиться быстро
        • Сбой напрямую, выдать исключение
        • Исходный код: com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
      • Warm Up
        • Начальное значение — порог/коэффициент кода (по умолчанию — 3), пороговое значение достигается после времени прогрева.
        • официальная документация
        • Источник: com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController.
      • ждать в очереди
  • правила перехода на более раннюю версию

    • стратегия понижения рейтинга
      • RT (среднее время ответа в секундах)
        • Запросы со средним временем ответа, превышающим пороговое значение и проходящим в пределах временного окна >= 5
        • Включение автоматического выключателя после оконного периода
        • Максимум RT 4900 (большее значение должно пройти -Dcsp.sentinel.statistic.max.rt=XXXX, чтобы оно вступило в силу)
      • Аномальное соотношение (второй уровень)
      • Количество исключений (минутный уровень)
  • Правила точки доступа

    • Правила управления потоком на уровне параметров
    • Текущий лимит может быть ограничен указанным параметром/указанным значением указанного параметра
    • Требуется @SentinelResource
  • системные правила

    • LOAD
      • Срабатывает, когда загрузка системы1 (загрузка за 1 минуту) превышает пороговое значение, а количество одновременных потоков превышает емкость системы, рекомендуется установить количество ядер процессора * 2 (действительно для Linux/Unix-подобных машин)
      • Емкость системы = maxQps (максимальное количество запросов в секунду в секундах) * minRt (минимальное время ответа в секундах)
      • команда uptime -> 1 5 15 (средняя загрузка системы/нагрузка)
    • RT
      • Среднее время восстановления всего входящего трафика достигает порогового значения
    • Потоки
      • Количество одновременных потоков для всего входящего трафика достигает порогового значения
    • Входящие запросы в секунду
      • Число запросов в секунду для всего входящего трафика достигло порогового значения
  • Правила авторизации

    • Настройка черных и белых списков для доступа к микросервисам

Принцип взаимодействия микросервиса и консоли

Микросервис регистрирует/отправляет сообщения на консоль, а консоль получает/отправляет сообщения через API.

YML-конфигурация

spring:
  cloud:
    sentinel:
      transport:
        控制台地址
        dashboard:
        和控制台通信的ip
        client-ip:
        和控制通信端口(默认8719,如果已经占用,+1直到找到)
        port:
        心跳毫秒
        heartbeat-interval-ms:

Параметры запуска консоли

элемент конфигурации По умолчанию описывать
server.port 8080 назначенный порт
csp.sentinel.dashboard.server localhost:8080 указанный адрес
project.name - -
sentinel.dashboard.auth.username[1.6] sentinel Учетная запись для входа в панель управления
sentinel.dashboard.auth.password[1.6] sentinel Пароль для входа в личный кабинет
server.servlet.session.timeout[1.6] 30 минут Время истечения сеанса входа в систему (7200: 7200 секунд/60m: 60 минут)

API (может защищать произвольные ресурсы)

    @GetMapping("/test/sentinel/api")
    public String testSentinelApi(@RequestParam(required = false) String a) {
        String resourceName = "test-sentinel-api";
        ContextUtil.enter(resourceName, "test");
        Entry entry = null;
        try {
            entry = SphU.entry(resourceName);
            if (StringUtils.isBlank(a)) {
                throw new IllegalArgumentException("参数不可为空");
            }
            return a;
        } catch (BlockException e) {
            return "限流/降级了";
        } catch (IllegalArgumentException e) {
            Tracer.trace(e);
            return "参数非法";
        } finally {
            if (entry != null) {
                entry.exit();
            }
            ContextUtil.exit();
        }
    }

@SentinelResource

Упростите свой код с помощью @SentinelResource

    package com.virgo.user.sentinel;

    import com.alibaba.csp.sentinel.slots.block.BlockException;
    
    /**
     * @author zhaozha
     * @date 2019/10/16 下午3:11
     */
    public class ControllerBlockHandlerClass {
        /**
         * 处理限流/降级
         *
         * @param a
         * @param e
         * @return
         */
        public static String block(String a, BlockException e) {
            return "降级/限流了";
        }
    }

    @GetMapping("/test/sentinel/api")
    @SentinelResource(
            value = "test-sentinel-api",
            blockHandler = "block",
            blockHandlerClass = com.virgo.user.sentinel.ControllerBlockHandlerClass.class
    )
    public String testSentinelApi(@RequestParam(required = false) String a) {
        if (StringUtils.isBlank(a)) {
            throw new IllegalArgumentException("参数不可为空");
        }
        return a;
    }

Интеграция RestTemplate

  • аннотация
    @Bean
    @LoadBalanced
    @SentinelRestTemplate
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
  • выключатель
resttemplate:
  sentinel:
    enabled: 

Feign интегрирует Sentinel

feign:
  #feign整合sentinel
  sentinel
    enable: true

Пользовательское ответное сообщение

  • fallback
package com.virgo.user.feignclient;

import com.virgo.entity.TblCar;
import com.virgo.user.configuration.GlobalFeignConfiguration;
import com.virgo.user.feignclient.fallback.LockCenterFeignClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @author zhaozha
 * @date 2019/10/11 下午12:52
 */
@FeignClient(name = "lock-center", configuration = GlobalFeignConfiguration.class,fallback = LockCenterFeignClientFallback.class)
public interface LockCenterFeignClient {

    @GetMapping("/lock/test/{id}")
    TblCar findById(@PathVariable(value="id") Long id);
}


package com.virgo.user.feignclient.fallback;

import com.virgo.entity.TblCar;
import com.virgo.user.feignclient.LockCenterFeignClient;
import org.springframework.stereotype.Component;

/**
 * @author zhaozha
 * @date 2019/10/16 下午3:36
 */
@Component
public class LockCenterFeignClientFallback implements LockCenterFeignClient {
    @Override
    public TblCar findById(Long id) {
        return TblCar.builder().level(1).build();
    }
}
  • fallbackFactory (может перехватывать исключения)
package com.virgo.user.feignclient;

import com.virgo.entity.TblCar;
import com.virgo.user.configuration.GlobalFeignConfiguration;
import com.virgo.user.feignclient.fallbackFactory.LockCenterFeignClientFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @author zhaozha
 * @date 2019/10/11 下午12:52
 */
@FeignClient(name = "lock-center", configuration = GlobalFeignConfiguration.class,fallbackFactory = LockCenterFeignClientFallbackFactory.class)
public interface LockCenterFeignClient {

    @GetMapping("/lock/test/{id}")
    TblCar findById(@PathVariable(value="id") Long id);
}

package com.virgo.user.feignclient.fallbackFactory;

import com.virgo.entity.TblCar;
import com.virgo.user.feignclient.LockCenterFeignClient;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @author zhaozha
 * @date 2019/10/16 下午4:02
 */
@Component
@Slf4j
public class LockCenterFeignClientFallbackFactory implements FallbackFactory<LockCenterFeignClient> {
    @Override
    public LockCenterFeignClient create(Throwable throwable) {
        return new LockCenterFeignClient() {
            @Override
            public TblCar findById(Long id) {
                log.info("限流/降级",throwable);
                return TblCar.builder().level(1).build();
            }
        };
    }
}

Как использовать

Как использовать Как использовать инструкции
Кодирование API try...catch...finally
Метод аннотации @SentinelResource blockHandler/fallback
RestTemplate @SentinelRestTemplate blockHandler/fallback
Feign feign.sentinel.enabled fallback/fallbackFactory

Сохранение конфигурации

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

официальная документация

оптимизация

  • Ошибка оптимизации страницы (UrlBlockHandler)
package com.virgo.user.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.virgo.dto.CommonResult;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author zhaozha
 * @date 2019/10/17 上午9:49
 */
@Component
public class VirgoUrlBlockHandler implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        // 统一返回对象
        CommonResult commonResult = null;
        // 流控
        if(e instanceof FlowException){
            commonResult = CommonResult.builder().status(100).msg("流控异常").build();
        }
        // 降级
        else if(e instanceof DegradeException){
            commonResult = CommonResult.builder().status(101).msg("降级异常").build();
        }
        // 热点
        else if(e instanceof ParamFlowException){
            commonResult = CommonResult.builder().status(102).msg("热点异常").build();
        }
        // 系统
        else if(e instanceof SystemBlockException){
            commonResult = CommonResult.builder().status(102).msg("系统异常").build();
        }
        // 授权
        else if (e instanceof AuthorityException){
            commonResult = CommonResult.builder().status(102).msg("授权异常").build();
        }

        httpServletResponse.setStatus(500);
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        new ObjectMapper()
                .writeValue(
                        httpServletResponse.getWriter(),
                        commonResult
                );

    }
}

  • Различать происхождение (RequestOriginParser)
package com.virgo.user.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @author zhaozha
 * @date 2019/10/17 上午11:51
 */
@Component
public class MyRequestOriginParser implements RequestOriginParser {

    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        // todo 改成header
        String origin = httpServletRequest.getParameter("origin");
        if (StringUtils.isBlank(origin)) {
            throw new IllegalArgumentException("origin must be specified");
        }
        return origin;
    }
}

  • Поддержка спокойных URL-адресов (UrlCleaner)
package com.virgo.user.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @author zhaozha
 * @date 2019/10/17 下午12:05
 */
@Component
public class VirgoUrlCleaner implements UrlCleaner {
    @Override
    public String clean(String originUrl) {
        // todo 多参数
        String[] split = originUrl.split("/");

        return Arrays.stream(split)
                .map(string -> {
                    if (NumberUtils.isNumber(string)) {
                        return "{number}";
                    }
                    return string;
                })
                .reduce((a, b) -> a + "/" + b)
                .orElse("");
    }
}

настроить

официальная документация

Производственная среда

  • Али АХАС