Spring Cloud: Sentinel hotspot key current limit + @ SentinelResource

Posted by cabaz777 on Tue, 08 Mar 2022 20:51:46 +0100

1. Basic introduction


Official website
https://github.com/alibaba/Sentinel/wiki/ Hot spot parameter current limiting

2. @SentinelResource

Bottom covering method
There are two types: system default and customer defined
In the previous case s, the default prompt of sentinel system is used after the outflow problem is limited
Use @ SentinelResource to customize the degradation method

code
Add controller method

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                         @RequestParam(value = "p2", required = false) String p2) {
    return "------testHotKey";
}

public String deal_testHotKey(String p1, String p2, BlockException execetion) {
    return "------deal_testHotKey";// The default prompt of sentinel system: Blocked by sentinel (flow limiting)
}

com.alibaba.csp.sentinel.slots.block.BlockException

to configure
@SentinelResource(value = "testHotKey")

@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")

The above case demonstrates the first parameter p1. When the QPS is clicked once for more than 1 second, the current is limited immediately

exceptional case
Normal: after more than one second, the current will be limited immediately after reaching the threshold of 1
We expect that when p1 parameter is a special value, its current limit value is not the same as usual
Special case: if the value of p1 is equal to 5, its threshold can reach 200

test
http://localhost:8401/testHotKey?p1=1

http://localhost:8401/testHotKey?p1=5

When p1 is equal to 5, the threshold becomes 200. When p1 is not equal to 5, the threshold is the usual 1

Note that the hotspot parameter must be a basic type or String

other
Add exception
int i = 10/0;

3. System rules

https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81

3.1 configure global QPS

Description of various configuration parameters

4. Current limit by resource name + subsequent processing

Nacos started successfully
Sentinel started successfully
Module

Business class RateLimitController

package com.lele.springcloud.alibaba.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.lele.springcloud.entities.CommonResult;
import com.lele.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author: lele
 * @date: 2021/4/10 19:15
 * @description:
 */
@RestController
public class RateLimitController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource", blockHandler = "handleException")
    public CommonResult byResource() {
        return new CommonResult(200, "Current limit test by resource name OK", new Payment(2020L, "serial001"));
    }

    public CommonResult handleException(BlockException exception) {
        return new CommonResult(444, exception.getClass().getCanonicalName() + "\t Service Unavailable");
    }
}

Configure flow control rules
Configuration steps
It means that if the number of queries is greater than 1 in one second, it will run to our custom flow and limit the current.

test

Additional questions
At this time, close the microservice 8401, and the flow control rules disappear, temporarily.

5. Limit current according to Url address + subsequent processing

If you limit the flow by accessing the URL, the default flow limiting processing information of Sentinel will be returned.

Business class RateLimitController add

@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl() {
    return new CommonResult(200, "Press url Current limiting test OK", new Payment(2020L, "serial002"));
}

Sentinel console configuration:


Return the self-contained current limiting processing result

Problems faced by the above method

5.1 customer defined current limiting processing logic

Create a customerBlockHandler class to customize the flow limiting processing logic

Custom current limiting processing class

package com.lele.springcloud.alibaba.myhandler;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.lele.springcloud.entities.CommonResult;

/**
 * @author: lele
 * @date: 2021/4/10 21:55
 * @description:
 */
public class CustomerBlockHandler {
    public static CommonResult handleException(BlockException exception) {
        return new CommonResult(2020, "Custom current limiting processing information....CustomerBlockHandler");

    }
}

RateLimitController add method

 @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class,
            blockHandler = "handleException")
    public CommonResult customerBlockHandler() {
        return new CommonResult(200, "Customized by customer", new Payment(2020L, "serial003"));
    }

Call the microservice once after starting it
http://localhost:8401/rateLimit/customerBlockHandler

Sentinel console configuration

Further explanation

Sentinel mainly has three core API s

  • SphU define resources
  • Tracer definition statistics
  • ContextUtil defines the context

6. Service fuse function

6.1 Ribbon series

Start nacos and sentinel

Provider 9003 / 9004
Create a new cloudalibaba provider payment9003 / 9004

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-demo</artifactId>
        <groupId>com.lele.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-provider-payment9003</artifactId>

    <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.lele.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- SpringBoot integration Web assembly -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--Daily general jar Package configuration-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

yml

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Configure Nacos address

management:
  endpoints:
    web:
      exposure:
        include: '*'

Main start:

package com.lele.springcloud.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @author: lele
 * @date: 2021/4/11 6:42
 * @description:
 */
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9003.class, args);
    }
}

Business class:

package com.lele.springcloud.alibaba.controller;

import com.lele.springcloud.entities.CommonResult;
import com.lele.springcloud.entities.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

/**
 * @author: lele
 * @date: 2021/4/11 6:44
 * @description:
 */
@RestController
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    public static HashMap<Long, Payment> hashMap = new HashMap<>();
    static{
        hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
        hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
        hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
    }

    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        Payment payment = hashMap.get(id);
        CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);
        return result;
    }
}

Refer to 9003 to create a new 9004 module

Test address
http://localhost:9003/paymentSQL/1

Consumer 84
Create a new cloudalibaba-consumer-nacos-order84

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-demo</artifactId>
        <groupId>com.lele.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-consumer-nacos-order84</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.lele.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

yml

server:
  port: 84
spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

service-url:
  nacos-user-service: http://nacos-payment-provider

Main startup class

package com.lele.springcloud.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author: lele
 * @date: 2021/4/11 7:01
 * @description:
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderNacosMain84 {
    public static void main(String[] args) {
        SpringApplication.run(OrderNacosMain84.class, args);
    }
}

ApplicationContextConfig

package com.lele.springcloud.alibaba.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
 * @author: lele
 * @date: 2021/4/11 7:03
 * @description:
 */
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

Business class

package com.lele.springcloud.alibaba.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.lele.springcloud.entities.CommonResult;
import com.lele.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @author: lele
 * @date: 2021/4/11 7:05
 * @description:
 */
@RestController
@Slf4j
public class CircleBreakerController {

    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;



    @RequestMapping("/consumer/fallback/{id}")
    //@SentinelResource(value = "fallback") / / not configured
    //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback is only responsible for business exceptions
    //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler is only responsible for sentinel console configuration violations
    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
            exceptionsToIgnore = {IllegalArgumentException.class})
    public CommonResult<Payment> fallback(@PathVariable Long id) {
        CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);

        if (id == 4) {
            throw new IllegalArgumentException ("IllegalArgumentException,Illegal parameter exception....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,Should ID No corresponding record,Null pointer exception");
        }

        return result;
    }

    //fallback
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"Fundus abnormality handlerFallback,exception content  "+e.getMessage(),payment);
    }

    //blockHandler
    public CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel Current limiting,No such flow: blockException  "+blockException.getMessage(),payment);
    }
    
}

objective
fallback tube runs abnormally

blockHandler pipe configuration violation

Test address: http://localhost:84/consumer/fallback/1


6.2 Feign series

Modify module 84
yml

PaymentFallbackService implementation class

package com.lele.springcloud.alibaba.feign;

import com.lele.springcloud.alibaba.service.PaymentService;
import com.lele.springcloud.entities.CommonResult;
import com.lele.springcloud.entities.Payment;
import org.springframework.stereotype.Component;

/**
 * @author: lele
 * @date: 2021/4/11 7:16
 * @description:
 */
@Component
public class PaymentFallbackService implements PaymentService {
    @Override
    public CommonResult<Payment> paymentSQL(Long id)
    {
        return new CommonResult<>(44444,"Service degradation return,---PaymentFallbackService",new Payment(id,"errorSerial"));
    }
}

Business interface with @ FeignClient annotation

package com.lele.springcloud.alibaba.service;

import com.lele.springcloud.alibaba.feign.PaymentFallbackService;
import com.lele.springcloud.entities.CommonResult;
import com.lele.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @author: lele
 * @date: 2021/4/11 7:14
 * @description:
 */
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

controller

http://lcoalhost:84/consumer/openfeign/1
http://lcoalhost:84/consumer/paymentSQL/1

Test 84 calls 9003. At this time, the 9003 micro service provider is deliberately closed to see that the 84 consumer side is automatically degraded and will not be consumed

7. Comparison of fuse frames


8. Rule persistence

Once we restart the application, Sentinel rules will disappear, and the production environment needs to persist the configuration rules
Persist the flow restriction configuration rules into Nacos and save them. As long as you refresh a rest address of 8401, you can see the flow control rules on sentinel console. As long as the configuration in Nacos is not deleted, the flow control rules on sentinel on 8401 will remain valid

Modify cloudalibaba-sentinel-service8401
yml

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        #nacos service registry address
        server-addr: localhost:8848
    sentinel:
      transport:
        #Configure sentinel dashboard address
        dashboard: localhost:8080
        #The default port is 8719. If it is occupied, it will automatically start + 1 scanning from 8719 until the unoccupied port is found
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow
        
        
management:
  endpoints:
    web:
      exposure:
        include: '*'

Add Nacos business rule configuration:

[
    {
         "resource": "/retaLimit/byUrl",
         "limitApp": "default",
         "grade":   1,
         "count":   1,
         "strategy": 0,
         "controlBehavior": 0,
         "clusterMode": false    
    }
]


After starting 8401, refresh sentinel and find that the business rules have changed

Stop 8401 and look at sentinel

Multiple calls http://localhost:8401/rateLimit/byUrl

The reconfiguration occurred and the persistence verification passed

Topics: Spring Cloud