Star: github.com/yehongzhi/l…
What is Sentinel
Sentinel positioning is the traffic defense of distributed systems. At present, Internet applications basically use micro-services, and the stability of micro-services is a very important problem, and the flow limiting and fuse downgrading are an important means to maintain the stability of micro-services.
Here’s an image from the Sentinel website to see some of the key features:
Hystrix actually did fuse downgrades before Sentinel, and we all know that when something new comes up it’s always something that’s not good enough.
So what’s the downside of Hystrix?
- Hystrix’s common thread pool isolation causes large overhead of thread switching up and down.
- Hystrix didn’t have a surveillance platform, so we had to build it ourselves.
- Hystrix supports fuse downgrading with fewer dimensions, less granularity, and a lack of an administrative console.
What are the components of Sentinel?
- The core library (Java client) is independent of any framework/library, can run in all Java runtime environments, and has good support for frameworks such as Dubbo/Spring Cloud.
- The Console (Dashboard) is based on Spring Boot and can be packaged to run directly without the need for additional application containers such as Tomcat.
What are the characteristics of Sentinel?
-
Rich application scenarios. Controlling burst traffic within manageable limits, message peaking and valley filling, cluster flow control, real-time fusing downstream unavailable applications, etc.
-
Complete real-time monitoring. Sentinel provides real-time monitoring capabilities. From the console, you can see a summary of the performance of a single machine-by-second data, or even a cluster of less than 500 machines, for accessing the application.
-
Extensive open source ecosystem. Sentinel provides out-of-the-box integration modules with other open source frameworks/libraries, such as Spring Cloud, Dubbo, and gRPC. You can quickly access Sentinel by introducing the appropriate dependencies and simple configuration.
-
Complete SPI extension points. Sentinel provides an easy-to-use, comprehensive SPI extension interface. You can quickly customize the logic by implementing an extension interface. For example, customize rule management and adapt dynamic data sources.
Second, Hello World
Generally, if you want to learn a technical framework that you have never touched before, you must first make a Hello World to familiarize yourself with it.
Introducing Maven dependencies
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>, version 1.8.1</version>
</dependency>
Copy the code
As a reminder, Sentinel only supports JDK 1.8 or later
Define the rules
Define rules to control the number of requests allowed to pass through the resource per second. For example, the following code defines a maximum of 20 requests per second for the resource HelloWorld.
private static void initFlowRules(a){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
Copy the code
Write Hello World code
In fact, the code is very simple. You need to define a resource entry, and then surround the code that needs flow control with sphu.entry (“HelloWorld”) and entry.exit(). The code is as follows:
public static void main(String[] args) throws Exception {
initFlowRules();
while (true) {
Entry entry = null;
try {
entry = SphU.entry("HelloWorld");
/* Your business logic - start */
System.out.println("hello world");
/* Your business logic - end */
} catch (BlockException e1) {
/* Flow control logic processing - start */
System.out.println("block!");
/* Flow control logic processing - end */
} finally {
if(entry ! =null) { entry.exit(); }}}}Copy the code
The running results are as follows:
${appName}-metrics.log.xxx: ${appName}-metrics.log.xxx:
|--timestamp-|------date time----|-resource-|p |block|s |e|rt
1616607101000|2021-03-25 01:31:41|HelloWorld|20|11373|20|0|1|0|0|0
1616607102000|2021-03-25 01:31:42|HelloWorld|20|24236|20|0|0|0|0|0
Copy the code
P is for approved request.
Block stands for blocked request.
S represents the number of requests successfully executed.
E stands for user-defined exceptions.
Rt stands for average response time.
The use of Sentinel
Let’s write a Controller interface for demonstration exercise based on a practical case.
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/list")
public List<User> getUserList(a) {
returnuserService.getList(); }}@Service
public class UserServiceImpl implements UserService {
// Query database data and return the result
@Override
public List<User> getList(a) {
List<User> userList = new ArrayList<>();
userList.add(new User("1"."Wai-man Chow".18));
userList.add(new User("2"."Guan Zhilin".20));
userList.add(new User("3"."Joey Wong".21));
returnuserList; }}Copy the code
Suppose we wanted to limit the flow of this query interface, how would we do it?
1) The way an exception is thrown
SphU includes a try-catch style API. In this way, a BlockException is thrown when a resource is restricted. At this point, exceptions can be caught for logical processing after limiting traffic.
@RestController
@RequestMapping("/user")
public class UserController {
// Resource name
public static final String RESOURCE_NAME = "userList";
@Resource
private UserService userService;
@RequestMapping("/list")
public List<User> getUserList(a) {
List<User> userList = null;
Entry entry = null;
try {
// Protected business logic
entry = SphU.entry(RESOURCE_NAME);
userList = userService.getList();
} catch (BlockException e) {
// Resource access blocked, traffic restricted or degraded
return Collections.singletonList(new User("xxx"."Resource access restricted".0));
} catch (Exception e) {
// To configure degradation rules, record service exceptions in this way
Tracer.traceEntry(e, entry);
} finally {
// Be sure to ensure exit. Be sure to pair each entry with exit
if(entry ! =null) { entry.exit(); }}returnuserList; }}Copy the code
I haven’t actually written it yet, but I still need to define the rules for limiting traffic.
@SpringBootApplication
public class SpringmvcApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringmvcApplication.class, args);
// Initialize the traffic limiting rule
initFlowQpsRule();
}
// Defines a maximum of 2 requests per second
private static void initFlowQpsRule(a) {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule(UserController.RESOURCE_NAME);
// set limit qps to 2
rule.setCount(2);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setLimitApp("default"); rules.add(rule); FlowRuleManager.loadRules(rules); }}Copy the code
Then start the project and test it. After a few quick refreshes, we can see the logic that triggers limiting.
2) The way to return a Boolean value
The method of throwing an exception is to perceive when the flow is limited in the form of throwing an exception. We can handle the flow limiting by catching an exception. This method is different from the above in that it does not throw an exception, but returns a Boolean value. This makes it easy to write if-else code.
public static final String RESOURCE_NAME_QUERY_USER_BY_ID = "queryUserById";
@RequestMapping("/get/{id}")
public String queryUserById(@PathVariable("id") String id) {
if (SphO.entry(RESOURCE_NAME_QUERY_USER_BY_ID)) {
try {
// Protected logic
// simulate database query data
return JSONObject.toJSONString(new User(id, "Tom".25));
} finally {
// Close the resourceSphO.exit(); }}else {
// Resource access blocked, traffic restricted or degraded
return "Resource is Block!!!"; }}Copy the code
The code to add the rule is the same as in the previous example, so I won’t write it, and then start the project and test it.
3) The way of annotation
Looking at the above two methods, some people will certainly say that the code is too intrusive, if the original system is to access, to change the original code. As we all know, old code can’t be moved, or there are consequences.
Annotation is a good way to solve this problem. How do I write in annotated form?
@Service
public class UserServiceImpl implements UserService {
// Resource name
public static final String RESOURCE_NAME_QUERY_USER_BY_NAME = "queryUserByUserName";
//value is a mandatory resource name. BlockHandler Specifies the name of the flow limiting method
@Override
@SentinelResource(value = RESOURCE_NAME_QUERY_USER_BY_NAME, blockHandler = "queryUserByUserNameBlock")
public User queryByUserName(String userName) {
return new User("0", userName, 18);
}
// Pay attention to details. Make sure that the function returns the same value and the parameter, and that the parameter ends with a BlockException parameter
// Otherwise an error will be reported. FlowException: null
public User queryUserByUserNameBlock(String userName, BlockException ex) {
// Print exception
ex.printStackTrace();
return new User("xxx"."Username: {" + userName + "}, resource access restricted".0); }}Copy the code
After writing this core code, you need to add a configuration, otherwise it will not take effect.
Introducing Maven dependencies for Sentinel-annotation-AspectJ.
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.81.</version>
</dependency>
Copy the code
Then register SentinelResourceAspect as a Bean.
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect(a) {
return newSentinelResourceAspect(); }}Copy the code
Don’t forget to add rules, so you can refer to the first example instead of writing it here.
Finally, after starting the project, testing, and refreshing the interface a few more times, you can see the following results.
4) Fuse downgrade
In addition to limiting the current on the interface, Sentinel also provides fuse downgrading in the event of an interface failure.
The @SentinelResource annotation has an attribute called fallback, and when a non-blockException exception is thrown, it goes into the Fallback method to implement the circuit breaker mechanism, which is somewhat similar to Hystrix’s Fallback.
As an example, throw a RuntimeException if userName is empty. We then set the value of the fallback property, which is the fallback method, to return a system exception.
@Override
@SentinelResource(value = RESOURCE_NAME_QUERY_USER_BY_NAME, blockHandler = "queryUserByUserNameBlock", fallback = "queryUserByUserNameFallBack")
public User queryByUserName(String userName) {
if (userName == null || "".equals(userName)) {
// Throw an exception
throw new RuntimeException("queryByUserName() command failed, userName is null");
}
return new User("0", userName, 18);
}
public User queryUserByUserNameFallBack(String userName, Throwable ex) {
// Prints logs
ex.printStackTrace();
return new User("1"."Username: {" + userName + "}, system exception, please try again later".0);
}
Copy the code
Fallback = fallback = fallback = fallback = fallback = fallback = fallback
Custom exception information can also be seen on the IDEA console.
Iv. Management console
The basic usage of Sentinel is covered above, but the main focus is on the Sentinel administrative console, which provides many useful functions. So let’s see how it works.
First download the console JAR package, of course you can also download the source code to compile.
// Download the page address
https://github.com/alibaba/Sentinel/releases
Copy the code
Then use the following command to start:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.81..jar
Copy the code
After successful startup, access http://localhost:8080. The default login user name and password are sentinel.
Once you’ve logged in, you’ll see the main page, which has a number of function menus that I won’t cover here.
The client accesses the console
So our own application how access to the console, the use of the console to monitor the flow of applications, guests, please continue to look.
To start by adding maven dependencies, the client needs to introduce the Transport module to communicate with the Sentinel console.
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>, version 1.8.1</version>
</dependency>
Copy the code
Filter is configured to automatically count all accessed Web urls as Sentinel resources.
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean sentinelFilterRegistration(a) {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/ *");
registration.setName("sentinelFilter");
registration.setOrder(1);
returnregistration; }}Copy the code
Join the following configuration in the startup command, – Dcsp. Sentinel. Dashboard. Server = consoleIp: port address and port specified console, -dcsp.sentinel.api. port= XXXX Specifies the port for the client to monitor the API (default: 8019 because the console already uses 8719 and the application uses 8720 to prevent collisions) :
-Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dcsp.sentinel.api.port=8720 -Dproject.name=sentinelDemo
Copy the code
To launch the project, we can see an application called sentinelDemo. Click on the list of machines to see the health.
Request the /user/list interface, and then we can see the QPS status of the interface monitored in real time.
This means that the client is successfully connected to the console!
Dynamic rules
The concept of Sentinel is that developers only need to focus on the definition of the resource, and when the resource definition is successful, various flow control degradation rules can be dynamically added. Sentinel provides two ways to change rules:
- Modify directly through the API (
loadRules
) - through
DataSource
Adapt to changes in different data sources
Defining rules manually through the API, as we saw in the Hello World example, is a hard-coded form that is not flexible enough to be applied to production environments.
Therefore, to introduce DataSource, rule Settings can be stored in the DataSource. By updating the rules stored in the DataSource and pushing them to the Sentinel rule center, the client can obtain the latest rules in real time and perform traffic limiting and degradation according to the latest rules.
Common DataSource extensions are implemented as follows:
- Pull mode: The client periodically polls a certain rule management center for pull rules. The rule center can be SQL or files. The advantage is that it is relatively simple, but the disadvantage is that changes cannot be captured in a timely manner.
- Push mode: the rule center pushes the rules uniformly, and the client monitors the changes constantly by registering listeners, such as using Nacos and Zookeeper configuration centers. This method has better real-time performance and consistency assurance, and is recommended.
Pull mode
Data sources in pull mode are generally writable (such as local files). First, register the data source on the client and register the corresponding read data source with the corresponding RuleManager. Then write data source registered WritableDataSourceRegistry to transport.
As you can see, this is a two-way read and write process. We can either update the rules by modifying the files directly in the application or push the rules through the Sentinel console. The following figure shows the flow chart of the console push rule.
First, introduce maven dependencies.
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
<version>, version 1.8.1</version>
</dependency>
Copy the code
Use the SPI mechanism to extend by creating an implementation class that implements the init() method of the InitFunc interface.
public class FileDataSourceInit implements InitFunc {
public FileDataSourceInit(a) {}@Override
public void init(a) throws Exception {
String filePath = System.getProperty("user.home") + "\\sentinel\\rules\\sentinel.json";
ReadableDataSource<String, List<FlowRule>> ds = new FileRefreshableDataSource<>(
filePath, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
})
);
// Register the readable data source with the FlowRuleManager.
FlowRuleManager.register2Property(ds.getProperty());
WritableDataSource<List<FlowRule>> wds = new FileWritableDataSource<>(filePath, this::encodeJson);
/ / will be written to the data source registered WritableDataSourceRegistry to transport module.
// When Sentinel receives a rule push from the console, it updates the rule to memory and writes the rule to a file.
WritableDataSourceRegistry.registerFlowDataSource(wds);
}
private <T> String encodeJson(T t) {
returnJSON.toJSONString(t); }}Copy the code
In the project’s resources/meta-inf/services directory to create a file, called com. Alibaba. CSP. Sentinel. Init. InitFunc, content is the fully qualified name of FileDataSourceInit:
io.github.yehongzhi.springmvc.config.FileDataSourceInit
Copy the code
Next, in the ${home} directory, create the \sentinel\rules directory and create the sentinel.json file.
The project is then started, the request is sent, and the initialization is triggered when the client receives the request. After initialization we go to the console and set the traffic limiting rules.
The local file sentinel.json also holds the rule content (compressed as a single line of JSON).
[{"clusterConfig": {"acquireRefuseStrategy":0."clientOfflineTime":2000."fallbackToLocalWhenFail":true."resourceTimeout":2000."resourceTimeoutStrategy":0."sampleCount":10."strategy":0."thresholdType":0."windowIntervalMs":1000},"clusterMode":false."controlBehavior":0."count":3.0."grade":1."limitApp":"default"."maxQueueingTimeMs":500."resource":"userList"."strategy":0."warmUpPeriodSec":10}]
Copy the code
We can update the rule content by modifying the file, or we can push the rule to the file through the console, which is called pull mode. The disadvantage is that consistency is not guaranteed, real-time is not guaranteed, too frequent pulling may also have performance problems.
Push mode
Just said that pull mode can not guarantee real-time, push mode solves this problem. In addition, it can be persisted, that is, the data is kept in the data source and the previous configuration is not lost even if the data is restarted. This also solves the problem that the original schema cannot be persisted in memory.
There are many data sources that can be used with Sentinel, such as ZooKeeper, Nacos, Apollo, etc. Here’s how to use Nacos.
Start the Nacos server, then log in to the Nacos console, add a namespace, and add the configuration.
Then we had to modify the Sentinel source code. Because the Sentinel JAR provided by the official website is in the original mode, it needs to be modified, so we need to pull down the source code and transform it, and then compile the JAR package ourselves.
Source code address: github.com/alibaba/Sen…
After pulling it down and importing it into IDEA, we can see the following directory structure.
First modify the Sentinel-Dashboard pom.xml file:
The second step is to move the four Nacos associated classes from the test directory to the Rule directory.
FlowRuleNacosProvider and FlowRuleNacosPublisher don’t need much modification. I don’t like the suffix, so I’ve removed the suffix.
Next, NacosConfig adds the Nacos address configuration.
The most critical is the transformation of FlowControllerV1, which is the interface for adding, deleting, modifying and checking the rule configuration.
Add the two services moved to the Rule directory to the FlowControllerV1 class.
@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
Copy the code
Add private method publishRules() for push configuration:
private void publishRules(/*@NonNull*/ String app) throws Exception {
List<FlowRuleEntity> rules = repository.findAllByApp(app);
rulePublisher.publish(app, rules);
}
Copy the code
Modify the apiQueryMachineRules() method.
Modify the apiAddFlowRule() method.
Modify the apiUpdateFlowRule() method.
Modify the apiDeleteFlowRule() method.
The Sentinel console project was transformed and compiled into JAR packages to run in production environment. If it is learned, it can run directly in IDEA.
We added the dependency to the POM.xml file of the HelloWord project we created earlier.
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>, version 1.8.1</version>
</dependency>
Copy the code
Then add the following configuration to the application.yml file:
spring:
cloud:
sentinel:
datasource:
flow:
nacos:
server-addr: localhost:8848
namespace: 05f447bc-8a0b-4686-9c34-344d7206ea94
dataId: springmvc-sentinel-flow-rules
groupId: SENTINEL_GROUP
# The rule type can be:
# org.springframework.cloud.alibaba.sentinel.datasource.RuleType
rule-type: flow
data-type: json
application:
name: springmvc-sentinel-flow-rules
Copy the code
That completes the configuration and transformation, starting the Sentinel console, and the Java application.
Opening the Nacos console, we added the following flow limiting configuration:
The configuration is as follows:
[{"app":"springmvc-sentinel-flow-rules"."clusterConfig": {"acquireRefuseStrategy":0."clientOfflineTime":2000."fallbackToLocalWhenFail":true."resourceTimeout":2000."resourceTimeoutStrategy":0."sampleCount":10."strategy":0."thresholdType":0."windowIntervalMs":1000},"clusterMode":false."controlBehavior":0."count":1.0."grade":1."limitApp":"default"."maxQueueingTimeMs":500."resource":"userList"."strategy":0."warmUpPeriodSec":10}, {"app":"springmvc-sentinel-flow-rules"."clusterConfig": {"acquireRefuseStrategy":0."clientOfflineTime":2000."fallbackToLocalWhenFail":true."resourceTimeout":2000."resourceTimeoutStrategy":0."sampleCount":10."strategy":0."thresholdType":0."windowIntervalMs":1000},"clusterMode":false."controlBehavior":0."count":3.0."grade":1."limitApp":"default"."maxQueueingTimeMs":500."resource":"queryUserByUserName"."strategy":0."warmUpPeriodSec":10}]
Copy the code
Then we opened the Sentinel console and saw the configuration, proving that the configuration push of Nacos was successful.
We try calling the Java application’s interface to see if it works.
You can see that limiting is in effect, and then look at the QPS that Sentinel monitors.
From the QPS monitoring situation, the highest QPS is only 3, and other requests are rejected, which proves that the traffic limiting configuration takes effect in real time.
The configuration information is also persisted to nacOS-related configuration tables.
At this point, it becomes clear to look back at the Sentinel website’s architecture diagram of the push model.
conclusion
This article mainly introduces the basic usage of Sentinel, as well as the two methods of dynamic rules, in addition to many other functions, there is no space here to introduce one, interested friends can explore. I personally find Sentinel to be a very good component and a very big improvement over the original Hystrix.
We see the list of enterprises registered on the official website, and many well-known enterprises are using Sentinel. We believe Sentinel will become better and better in the future.
That’s all for this article, thank you for reading, I hope you can learn something after reading!
Please give me a thumbs-up if you think it is useful. Your thumbs-up is the biggest motivation for my creation
I’m a programmer who tries to be remembered. See you next time!!
Ability is limited, if there is any mistake or improper place, please criticize and correct, study together!