sentinel service fault tolerance

jmeter interface test

download

jmeter.bat startup file
jmeter.properties configuration file

Modify the configuration file to Chinese

Interface test



Modify the maximum number of threads in tomcat

server:
  port: 8091
  tomcat:
    max-threads: 10

Start test


Sentinel service

Dependency injection

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

Monitoring platform jar Download

Start command

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

Access link http://localhost:8080/

The account password is sentinel by default


Modify configuration

spring:
  cloud:
   sentinel:
      transport:
        port: 9999  #The port for communicating with the console can be arbitrarily specified as an unused port
        dashboard: localhost:8080 #Specify the address of the console service

Because sentinel console is soft loaded, data display can only be performed after accessing the interface

The console implements interface flow control

sentinel rule

Flow control rules


Direct flow control mode

  • Direct flow control mode is the simplest mode. When the specified interface reaches the current limiting conditions, the current limiting is turned on

Associated flow control mode

  • Associated flow control mode means that when the interface associated with the specified interface reaches the current limiting condition, the current limiting is enabled for the specified interface.
  • The association in the flow control mode can make other requested services reach the threshold, and this service can flow control.

link

The first way

<spring-cloud-alibaba.version>2.1.1.RELEASE</spring-cloud-alibaba.version>
@Override
//Define the resource, and value specifies the resource name
@SentinelResource("message")
public String message() {
    return "message";
}
@GetMapping("/message1")
public String message1() {
    return orderService.message();
}

@GetMapping("/message2")
public String message2() {
    return orderService.message();
}

Modify the configuration and add filter enabled=false

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        port: 9999  #The port for communicating with the console can be arbitrarily specified as an unused port
        dashboard: localhost:8080 #Specify the address of the console service
      filter:
        enabled: false

Add profile

@Configuration
public class FilterContextConfig {
    @Bean
    public FilterRegistrationBean sentinelFilterRegistration(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new CommonFilter());
        registrationBean.addUrlPatterns("/*");
        //Portal resource close aggregation
        registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY,"false");
        registrationBean.setName("sentinelFilter");
        registrationBean.setOrder(1);
        return registrationBean;
    }
}

The second way

<spring-cloud-alibaba.version>2.1.3.RELEASE</spring-cloud-alibaba.version>

Modify configuration

server:
  port: 8091
spring:
  application:
    name: service-order
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        port: 9999  #The port for communicating with the console can be arbitrarily specified as an unused port
        dashboard: localhost:8080 #Specify the address of the console service
      web-context-unify: false


Flow control effect

Degradation rule



Hotspot rule

Hotspot parameter flow control rule is a more fine-grained flow control rule, which allows rules to be specific to parameters

@GetMapping("/message3")
//This annotation must be used, otherwise the hotspot rule will not work
@SentinelResource("message3")
public String message3(String name, Integer age) {
    return "message3" + name + age;
}

The second parameter age is limited. When age is 15, the lifting threshold is 1000



Authorization rules

@Component
public class RequestOriginParserDefinition implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        String serviceName = httpServletRequest.getParameter("serviceName");
        return serviceName;
    }
}


System rules

Custom exception return

@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        httpServletResponse.setContentType("application/json;charset=utf-8");
        ResponseData responseData = null;
        if (e instanceof FlowException) {
            responseData = new ResponseData(-1, "The interface is current limited");
        } else if (e instanceof DegradeException) {
            responseData = new ResponseData(-2, "The interface has been degraded");
        } else if (e instanceof ParamFlowException) {
            responseData = new ResponseData(-3, "Interface current limited by parameters");
        } else if (e instanceof AuthorityException) {
            responseData = new ResponseData(-4, "Interface authorization exception");
        } else if (e instanceof SystemBlockException) {
            responseData = new ResponseData(-5, "Abnormal system load");
        } else {
            responseData = new ResponseData(-6, e.toString());
        }
        httpServletResponse.getWriter().write(JSON.toJSONString(responseData));
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
class ResponseData {
    private int code;
    private String message;
}

@SentinelResource

usage method

/**
 * value            Define a resource
 * blockHandler     Defines the method that should be entered when BlockException occurs inside the resource
 * fallback         Define the method that should be entered when Throwable occurs inside the resource
 */
@SentinelResource(
        value = "message2",
        blockHandler = "blockHandler",
        fallback = "fallback"
)
public String message2(String message) {
    return message;
}


/**
 * The return value and parameters of the current method should be consistent with the original method
 * It is allowed to add a BlockException parameter at the end of the parameter list to receive exceptions in the original method
 */
public String blockHandler(String message, BlockException e){
    return "blockException";
}

public String fallback(String message, Throwable e){
    return "throwable";
}

Use class

@SentinelResource(
        value = "message2",
        blockHandlerClass = OrderServiceImplBlockHandler.class,
        blockHandler = "blockHandler",
        fallbackClass = OrderServiceImplFallback.class,
        fallback = "fallback"
)
public String message2(String message) {
    return message;
}
public class OrderServiceImplBlockHandler {
    public static String blockHandler(String message, BlockException e){
        return "blockException";
    }
}
public class OrderServiceImplFallback {
    public static String fallback(String message, Throwable e){
        return "throwable";
    }
}

Rule persistence

Common implementations of DataSource extension include:

  • Pull mode: the client actively polls a rule management center regularly for pull rules, which can be RDBMS, files, or even VCS. The way to do this is simple, but the disadvantage is that changes cannot be obtained in time.
  • Push mode: the rule center pushes uniformly, and the client monitors changes at any time by registering a listener, such as using Nacos and Zookeeper
    And other configuration centers. This method has better real-time and consistency guarantee.

Sentinel currently supports the following data source extensions:

  • Pull based: file, Consul
  • Push-based: ZooKeeper, Redis, Nacos, Apollo, etcd

Pull mode

Principle:

  • Extended writeable data source: the client actively polls and pulls rules from a rule management center, which can be RDBMS and files
    Wait.
  • The data sources of pull mode (such as local files, RDBMS, etc.) are generally writable. When using, you need to register the data source at the client: register the corresponding read data source with the corresponding
    RuleManager to register the write data source in the writabledatasourcerregistry of transport.

Import dependency

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

Add file

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * FileDataSourceInit for : Custom Sentinel storage file data source load class
 */
public class FileDataSourceInit implements InitFunc {
    @Override
    public void init() throws Exception {
        // TIPS: if you don't like this path, you can change it to the path you like
        String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String hotParamFlowRulePath = ruleDir + "/param-flow-rule.json";

        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(hotParamFlowRulePath);
        // Flow control rules
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        // Register a readable data source with FlowRuleManager
        // In this way, when the rule file changes, the rules will be updated to memory
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        // Register the writable data source in the writabledatasourcerregistry of the transport module
        // In this way, when the rules pushed by the console are received, Sentinel will first update them to memory, and then write the rules to the file
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        // Degradation rule
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        // System rules
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        // Authorization rules
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        // Hotspot parameter rule
        ReadableDataSource<String, List<ParamFlowRule>> hotParamFlowRuleRDS = new FileRefreshableDataSource<>(
                hotParamFlowRulePath,
                hotParamFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(hotParamFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                hotParamFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    /**
     * Flow control rule object conversion
     */
    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );
    /**
     * Degradation rule object conversion
     */
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );
    /**
     * System rule object conversion
     */
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {
            }
    );

    /**
     * Authorization rule object conversion
     */
    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );

    /**
     * Hotspot rule object conversion
     */
    private Converter<String, List<ParamFlowRule>> hotParamFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );

    /**
     * Create directory
     *
     * @param filePath
     */
    private void mkdirIfNotExits(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * create a file
     *
     * @param filePath
     * @throws IOException
     */
    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}

Create the resources/META-INF/services directory under the resources directory and create the file com alibaba. csp. sentinel. init. Initfunc, which reads:

pre.cg.config.FileDataSourceInit

Advantages and disadvantages of Pull

  • advantage
    • Simple without any dependency
    • No additional dependencies
  • shortcoming
    • Consistency is not guaranteed (the rule is to use FileRefreshableDataSource to update regularly, which will be delayed)
    • Real time performance is not guaranteed (the rule is to use FileRefreshableDataSource to update regularly)
    • Pulling too often may also have performance problems
    • Because the file is stored locally, it is easy to lose

Feign integrates Sentinel

Modify profile

#Enable feign support for sentinel    
feign:
  sentinel:
    enabled: true

Establish fault tolerance class

The fault-tolerant class needs to implement the interface where Fegin is located and all methods of the interface. Once there is a problem with the remote call of Fegin, it will enter the method with the same name in the current class and execute the fault-tolerant logic

@Service
public class ProductServiceFallback implements ProductFeignClient {

    @Override
    public Product findByPid(Long pid) {
        Product product = new Product();
        product.setId(250L)
                .setName("fallback");
        return product;
    }
}

Modify @ FeignClient annotation and add fault tolerance class

@FeignClient(value = "service-product",qualifier = "service-product",fallback = ProductServiceFallback.class)
public interface ProductFeignClient {

    @GetMapping("/product/{pid}")
    @LoadBalanced
    Product findByPid(@PathVariable("pid") Long pid);
}

Fault tolerance class and obtain error information (only one of the two methods can be used)

@FeignClient(value = "service-product",qualifier = "service-product"
        ,fallbackFactory = ProductServiceFallbackFactory.class)
public interface ProductFeignClient {

    @GetMapping("/product/{pid}")
    @LoadBalanced
    Product findByPid(@PathVariable("pid") Long pid);
}
@Component
public class ProductServiceFallbackFactory implements FallbackFactory<ProductFeignClient> {
    @Override
    public ProductFeignClient create(Throwable throwable) {
        return new ProductFeignClient() {
            @Override
            public Product findByPid(Long pid) {
                Product product = new Product();
                product.setId(250L)
                        .setName("fallback"+throwable.toString());
                return product;
            }
        };
    }
}

Keywords: Java Stress testing

Added by haaglin on Thu, 06 Jan 2022 14:12:18 +0200