Spring Cloud Alibaba | Sentinel: Traffic Defense Advanced combat soldiers distributed systems

Spring Cloud Alibaba | Sentinel: Traffic Defense Advanced combat soldiers distributed systems

Before reading this article, it is recommended to read the "Spring Cloud Alibaba | Sentinel: Traffic Distributed Systems defensive combat soldiers basis" .

1. Sentinel integration Feign and RestTemplate

Sentinel now supports both Feign and RestTemplate, we need to introduce corresponding dependence, need to open Sentinel Feign support in the configuration file when using Feign: the feign.sentinel.enabled=truesame time need to join openfeign starterrely on the sentinel starterautomated configuration class effect. When using RestTemplate you need to add at construction RestTemplate of Bean's @SentinelRestTemplatecomment, open the Sentinel's support for RestTemplate.

1.1 Creating a parent project sentinel-springcloud-high:

Parent project pom.xml as follows:

Listing: Alibaba / Sentinel-springcloud-High / pom.xml
***

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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-discovery</artifactId>
</dependency>

Public Sentinel introduced in components do flow control, the introduction of Nacos do service center.

1.2 create a sub-project provider_server:

Profile application.yml as follows:

Listing: Alibaba / Sentinel-springcloud-High / provider_server / pom.xml
***

server:
  port: 8000
spring:
  application:
    name: spring-cloud-provider-server
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.44.129:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8720
management:
  endpoints:
    web:
      cors:
        allowed-methods: '*'

Interface test class HelloController.java follows:

代码清单:Alibaba/sentinel-springcloud-high/provider_server/src/main/java/com/springcloud/provider_server/controller/HelloController.java
***

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(HttpServletRequest request) {
        return "Hello, port is: " + request.getLocalPort();
    }
}

1.3 create a sub-project consumer_server:

Sub-project relies pom.xml as follows:

Listing: Alibaba / Sentinel-springcloud-High / consumer_server / pom.xml
***

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

Profile application.yml as follows:

代码清单:Alibaba/sentinel-springcloud-high/consumer_server/src/main/resources/application.yml
***

server:
  port: 9000
spring:
  application:
    name: spring-cloud-consumer-server
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.44.129:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719
management:
  endpoints:
    web:
      cors:
        allowed-methods: '*'
feign:
  sentinel:
    enabled: true

Here the use of feign.sentinel.enabled=trueopen support for the Sentinel's Feign.

Interface Test class HelloController.java

代码清单:Alibaba/sentinel-springcloud-high/consumer_server/src/main/java/com/springcloud/consumer_server/controller/HelloController.java
***

@RestController
public class HelloController {
    @Autowired
    HelloRemote helloRemote;

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/helloByFeign")
    public String helloByFeign() {
        return helloRemote.hello();
    }

    @GetMapping("/helloByRestTemplate")
    public String helloByRestTemplate() {
        return restTemplate.getForObject("http://spring-cloud-provider-server/hello/", String.class);
    }
}

Sentinel has done for integration, we use Feign place without additional comment. At the same time, @FeignClientnotes of all properties, Sentinel are made compatible.

Start main class Ch122ConsumerServerApplication.java follows:

代码清单:Alibaba/sentinel-springcloud-high/consumer_server/src/main/java/com/springcloud/consumer_server/ConsumerServerApplication.java
***

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class Ch122ConsumerServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(Ch122ConsumerServerApplication.class, args);
    }

    @Bean
    @LoadBalanced
    @SentinelRestTemplate
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

When using RestTemplate need to be increased @SentinelRestTemplateto support RestTemplate of open Sentinel.

1.4 Test

Start engineering provider_server and consumer_server, provider_server modify the startup configuration, start two instances, open the browser to access: http: // localhost: 9000 / helloByFeign and http: // localhost: 9000 / helloByRestTemplate , refresh a few times, you can see the page alternately display Hello, port is: 8000and Hello, port is: 8001, indicating the current load balancing normal, now view Sentinel console, as shown:

1.5 Flow Control Test

At this point in the stream select a cluster control left, click flow control as shown:

Here we configure a simple rule, configured QPS limit of 1, click Add, as shown:

Here to explain what the QPS, QPS is it simply a number of visits per second, fast refresh needs to be repeated when we tested here http: // localhost: 9000 / helloByFeign and http: // localhost: 9000 / helloByRestTemplate , in the course of refreshing we can see that the page will display an error message such as: Blocked by Sentinel (flow limiting), that we have to configure Sentinel limiting success, then let's look at Sentinel console, you can see the number of successful and we have just limiting access, as Figure:

2. Service downgraded

On a summary, we introduced the use Feign and RestTemplate integrate Sentinel, and made QPS current limit on the Sentinel console and limiting success, after the success of current limiting, by default, Sentinel process of limiting resources is controlled direct throw. In no reasonable business undertaking or docking at the front end of the case may be so, but normal circumstances in order to better service users, will achieve some special treatment after being limiting, we do not want to show a blunt error. This section, we introduce the service degradation process.

2.1 create a sub-project consumer_fallback

Feign service degradation class HelloRemoteFallBack.java as follows:

代码清单:Alibaba/sentinel-springcloud-high/consumer_fallback/src/main/java/com/springcloud/consumer_fallback/fallback/HelloRemoteFallBack.java
***

@Component
public class HelloRemoteFallBack implements HelloRemote {
    @Override
    public String hello() {
        return "Feign FallBack Msg";
    }
}

Correspondingly, there needs to be done on the part of HelloRemote.java configured such that the current limit, triggering the implementation of our service degradation of service degradation classes, as follows:

代码清单:ch12_2/ch12_2_consumer_fallback/src/main/java/com/springcloud/book/ch12_2_consumer_fallback/remote/HelloRemote.java
***

@FeignClient(name = "spring-cloud-provider-server", fallback = HelloRemoteFallBack.class)
public interface HelloRemote {
    @GetMapping("/hello")
    String hello();
}

fallback = HelloRemoteFallBack.classDesignated service degradation handling class HelloRemoteFallBack.class.

RestTemplate service degradation Tools ExceptionUtil.java as follows:

代码清单:Alibaba/sentinel-springcloud-high/consumer_fallback/src/main/java/com/springcloud/consumer_fallback/remote/HelloRemote.java
***

public class ExceptionUtil {

    private final static Logger logger = LoggerFactory.getLogger(ExceptionUtil.class);

    public static SentinelClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
        logger.error(ex.getMessage(), ex);
        return new SentinelClientHttpResponse("RestTemplate FallBack Msg");
    }
}

Here, too, need to modify RestTemplate registered as Bean's place, making RestTemplate trigger handler class service degradation code execution after we wrote it, Ch122ConsumerFallbackApplication.java code is as follows:

代码清单:Alibaba/sentinel-springcloud-high/consumer_fallback/src/main/java/com/springcloud/consumer_fallback/ConsumerFallbackApplication.java
***

@Bean
@LoadBalanced
@SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
public RestTemplate restTemplate() {
    return new RestTemplate();
}

It should be noted, @SentinelRestTemplateannotation property supports limiting ( blockHandler, blockHandlerClass) and downgrading ( fallback, fallbackClass) processing.

Wherein blockHandleror fallbackcorresponding to the attribute must be a corresponding method blockHandlerClassor fallbackClassattribute static method.

@SentinelRestTemplateNotes limiting ( blockHandler, blockHandlerClass) and downgrading ( fallback, fallbackClass) property is not mandatory to fill in.

When using the Sentinel is fused RestTemplate call, returns RestTemplate request block by sentinelthe method information, or may be prepared by the process returns information corresponding to itself. Here provides SentinelClientHttpResponsefor constructing return information.

2.2 Test

Sequential start provider_server and consumer_fallback two sub-projects. First alternate browser to http: // localhost: 9090 / helloByFeign and http: // localhost: 9090 / helloByRestTemplate, and then open the Sentinel console, increase the current limit information on the two interfaces, note that you want to limit here information added to the stream of resources, particularly as shown:

Refresh in the browser two links, two limiting information can be displayed properly in the browser, the test is successful, see the Sentinel console again, we can see-rejected traffic statistics, as shown:

3. Sentinel Integrated Service Gateway limiting

Sentinel currently supports mainstream Spring Cloud Gateway, Zuul, etc. API Gateway be limiting. Look at the official chart, as shown:

From the official view of this, we can see, Sentinel of Zuul limiting mainly through three Filter accomplished, for Spring Cloud Gateway is through a SentinleGatewayFilterand a BlockRequestHandlerto complete.

Sentinel 1.6.0 introduced Sentinel API Gateway Adapter Common module, this module contains rules limiting the gateway API and custom entities and management logic:

  • GatewayFlowRule: Gateway limiting rules for the API Gateway scene customized limiting rules can be limiting for API grouping different route or custom supports customized current limiting for the request parameters, Header, Source IP, etc. .
  • ApiDefinition: API defines user-defined packets, it can be seen as some combination of matching the URL. For example, we can define an API called my_api, requested path mode / foo / ** and / baz / ** returned into my_api the API grouped below. When the current limit can be limiting for this API custom grouping dimension.

3.1 Zuul 1.x

Sentinel provides Zuul 1.x adapter module, can provide current limiting and resources dimensions of Zuul Gateway:

  • Dimensions route: i.e., disposed in a routing entry Spring configuration file, the resource name corresponding route ID (corresponding to the proxy field RequestContext)
  • Custom API dimensions: the user can use the Sentinel API provided from the API to define some packets

3.1.1 create a sub-project zuul_server

Project relies pom.xml as follows:

Listing: Alibaba / Sentinel-springcloud-High / zuul_server / pom.xml
***

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-zuul-adapter</artifactId>
</dependency>

Because here sentinel-zuul-adapterit is not included in spring-cloud-starter-alibaba-sentinelnecessary to introduce a separate manually.

3.1.2 Profile application.yml as follows:

Listing: Alibaba / Sentinel-springcloud-High / zuul_server / src / main / Resources / application.yml
***

server:
  port: 18080
spring:
  application:
    name: spring-cloud-zuul-server
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.44.129:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8720
zuul:
  routes:
    consumer-route:
      path: /consumer/**
      serviceId: spring-cloud-consumer-fallback

3.1.3 downgrade defined class ZuulFallbackProvider.java follows:

代码清单:Alibaba/sentinel-springcloud-high/zuul_server/src/main/java/com/springcloud/zuul_server/fallback/ZuulFallbackProvider.java
***

public class ZuulFallbackProvider implements ZuulBlockFallbackProvider {
    @Override
    public String getRoute() {
        return "*";
    }

    @Override
    public BlockResponse fallbackResponse(String route, Throwable cause) {
        RecordLog.info(String.format("[Sentinel DefaultBlockFallbackProvider] Run fallback route: %s", route));
        if (cause instanceof BlockException) {
            return new BlockResponse(429, "Sentinel block exception", route);
        } else {
            return new BlockResponse(500, "System Error", route);
        }
    }
}

3.1.4 At the same time, we need to 3 injection of Sentinel Filter Spring, configuration class as follows:

代码清单:Alibaba/sentinel-springcloud-high/zuul_server/src/main/java/com/springcloud/zuul_server/config/ZuulConfig.java
***

@Configuration
public class ZuulConfig {
    @Bean
    public ZuulFilter sentinelZuulPreFilter() {
        // We can also provider the filter order in the constructor.
        return new SentinelZuulPreFilter();
    }

    @Bean
    public ZuulFilter sentinelZuulPostFilter() {
        return new SentinelZuulPostFilter();
    }

    @Bean
    public ZuulFilter sentinelZuulErrorFilter() {
        return new SentinelZuulErrorFilter();
    }

    /**
     * 注册 ZuulFallbackProvider
     */
    @PostConstruct
    public void doInit() {
        ZuulBlockFallbackManager.registerProvider(new ZuulFallbackProvider());
    }

}

Ultimately, you need to configure JVM startup parameters before starting to increase -Dcsp.sentinel.app.type=1, to tell us start the Sentinel Console service for the API Gateway type.

3.1.5 Testing

Sequential engineering promoter provider_server, consumer_fallback, zuul_server, open the browser to access: http: // localhost: 18080 / consumer / helloByFeign, then we open the Sentinel console to see zuul_server services as:

We customize limiting policy, is still QPS 1, we once again set http: // localhost: 18080 / consumer / helloByFeign page, then, the page already produced positive current limit, and after limiting the content of the display is as follows:

{"code":429, "message":"Sentinel block exception", "route":"consumer-route"}

Note here that the definition of limiting resources, do not define the wrong place, limiting the definition as shown:

3.2 Spring Cloud Gateway

From the start of version 1.6.0, Sentinel offers Spring Cloud Gateway adapter module, two resources can provide current limiting dimensions:

  • Dimensions route: i.e., disposed in a routing entry Spring configuration file, a corresponding resource named routeId
  • Custom API dimensions: the user can use the Sentinel API provided from the API to define some packets

3.2.1 create a sub-project gateway_server

Project relies pom.xml as follows:

Listing: Alibaba / Sentinel-springcloud-High / gateway_server / pom.xml
***

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>

3.2.2 Profile application.yml as follows:

Listing: Alibaba / Sentinel-springcloud-High / gateway_server / src / main / Resources / application.yml
***

server:
  port: 28080
spring:
  application:
    name: spring-cloud-gateway-server
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.44.129:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8720
    gateway:
      enabled: true
      discovery:
        locator:
          lower-case-service-id: true
      routes:
        - id: consumer_server
          uri: lb://spring-cloud-consumer-fallback
          predicates:
            - Method=GET

3.2.3 Global GatewayConfig.java following categories:

Zuul with the previous subsections, where we will also need about two Sentinel Spring Cloud Gateway Filter injection of Spring: SentinelGatewayFilterand SentinelGatewayBlockExceptionHandler, because here was adding support for Spring Cloud Gateway in the Sentinel v1.6.0 version, in many places is not perfect, abnormal treatment SentinelGatewayBlockExceptionHandlercan only return an exception message, not a good combination in our system and downstream, where the author himself re-implemented SentinelGatewayBlockExceptionHandler, and the name JsonSentinelGatewayBlockExceptionHandler, return parameters are defined to be JSON, this is no longer inject Sentinel provided SentinelGatewayBlockExceptionHandler, but instead I own implementation JsonSentinelGatewayBlockExceptionHandler.

代码清单:Alibaba/sentinel-springcloud-high/gateway_server/src/main/java/com/springcloud/gateway_server/config/GatewayConfig.java
***

@Configuration
public class GatewayConfig {
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public JsonSentinelGatewayBlockExceptionHandler jsonSentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new JsonSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
}

3.2.4 Class JsonSentinelGatewayBlockExceptionHandler.java downgrade follows:

代码清单:Alibaba/sentinel-springcloud-high/gateway_server/src/main/java/com/springcloud/gateway_server/exception/JsonSentinelGatewayBlockExceptionHandler.java
***

public class JsonSentinelGatewayBlockExceptionHandler implements WebExceptionHandler {
    private List<ViewResolver> viewResolvers;
    private List<HttpMessageWriter<?>> messageWriters;

    public JsonSentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolvers;
        this.messageWriters = serverCodecConfigurer.getWriters();
    }

    private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] datas = "{\"code\":403,\"msg\":\"Sentinel block exception\"}".getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
        return serverHttpResponse.writeWith(Mono.just(buffer));
    }

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        if (exchange.getResponse().isCommitted()) {
            return Mono.error(ex);
        }
        // This exception handler only handles rejection by Sentinel.
        if (!BlockException.isBlockException(ex)) {
            return Mono.error(ex);
        }
        return handleBlockedRequest(exchange, ex)
                .flatMap(response -> writeResponse(response, exchange));
    }

    private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
        return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
    }

    private final Supplier<ServerResponse.Context> contextSupplier = () -> new ServerResponse.Context() {
        @Override
        public List<HttpMessageWriter<?>> messageWriters() {
            return JsonSentinelGatewayBlockExceptionHandler.this.messageWriters;
        }

        @Override
        public List<ViewResolver> viewResolvers() {
            return JsonSentinelGatewayBlockExceptionHandler.this.viewResolvers;
        }
    };
}

I just to rewrite the writeResponse()method returns information speak simple change has become a json format, you need the reader can modify according to their needs.

3.2.5 Testing

Sequentially starting provider_server, consumer_server and gateway_server, arranged gateway_server jvm startup parameters -Dcsp.sentinel.app.type=1, as shown:

Open your browser and visit: http: // localhost: 28080 / helloByFeign, refresh a few times, the page returns to normal Hello, port is: 8000, open Sentinel console, configure traffic limiting policies, QPS limit is 1, then refresh the browser page, then, we can see limiting the return to the browser information:

{"code":403,"msg":"Sentinel block exception"}

testing successfully.

Guess you like

Origin www.cnblogs.com/babycomeon/p/11516011.html