Sentinel实战:一个简单案例

启动Nacos

[xuej@localhost bin]$ ./startup.sh -m standalone
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.352.b08-2.el7_9.x86_64/bin/java -Djava.ext.dirs=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.352.b08-2.el7_9.x86_64/jre/lib/ext:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.352.b08-2.el7_9.x86_64/lib/ext  -Xms512m -Xmx512m -Xmn256m -Dnacos.standalone=true -Dnacos.member.list= -Xloggc:/home/xuej/Downloads/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dloader.path=/home/xuej/Downloads/nacos/plugins,/home/xuej/Downloads/nacos/plugins/health,/home/xuej/Downloads/nacos/plugins/cmdb,/home/xuej/Downloads/nacos/plugins/selector -Dnacos.home=/home/xuej/Downloads/nacos -jar /home/xuej/Downloads/nacos/target/nacos-server.jar  --spring.config.additional-location=file:/home/xuej/Downloads/nacos/conf/ --logging.config=/home/xuej/Downloads/nacos/conf/nacos-logback.xml --server.max-http-header-size=524288
nacos is starting with standalone
nacos is starting,you can check the /home/xuej/Downloads/nacos/logs/start.out

启动Sentinel Dashboard

INFO: Sentinel log output type is: file
INFO: Sentinel log charset is: utf-8
INFO: Sentinel log base directory is: /home/xuej/logs/csp/
INFO: Sentinel log name use pid is: false
INFO: Sentinel log level is: INFO

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v2.5.12)

2023-12-04 23:37:48.933  INFO 17286 --- [           main] c.a.c.s.dashboard.DashboardApplication   : Starting DashboardApplication using Java 1.8.0_352 on localhost.localdomain with PID 17286 (/home/xuej/Documents/workspace-github/Sentinel/sentinel-dashboard/target/classes started by xuej in /home/xuej/Documents/workspace-github/Sentinel/sentinel-dashboard)
2023-12-04 23:37:48.951  INFO 17286 --- [           main] c.a.c.s.dashboard.DashboardApplication   : No active profile set, falling back to 1 default profile: "default"
2023-12-04 23:37:50.734  INFO 17286 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-12-04 23:37:50.744  INFO 17286 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-12-04 23:37:50.745  INFO 17286 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.60]
2023-12-04 23:37:50.810  INFO 17286 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-12-04 23:37:50.810  INFO 17286 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1609 ms
2023-12-04 23:37:50.886  INFO 17286 --- [           main] c.a.c.s.dashboard.config.WebConfig       : Sentinel servlet CommonFilter registered
2023-12-04 23:37:52.161  INFO 17286 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-12-04 23:37:52.185  INFO 17286 --- [           main] c.a.c.s.dashboard.DashboardApplication   : Started DashboardApplication in 3.852 seconds (JVM running for 4.598)
2023-12-04 23:38:11.378  INFO 17286 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-12-04 23:38:11.379  INFO 17286 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2023-12-04 23:38:11.380  INFO 17286 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms

启动本地服务

如下代码工程,展示的是如何对本地接口或本地服务做流控。更多流控场景代码示例,可参考后续发布的一系列实战文章。

pom.xml

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>tech.foolfish</groupId>
    <artifactId>charge-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>charge-service</name>
    <description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
		<spring-cloud-alibaba.version>2.2.9.RELEASE</spring-cloud-alibaba.version>
		<spring-cloud.version>Hoxton.SR10</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>com.alibaba.nacos</groupId>
			<artifactId>nacos-client</artifactId>
			<version>1.4.2</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
			<exclusions>
				<exclusion>
					<groupId>com.alibaba.nacos</groupId>
					<artifactId>nacos-client</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
			<exclusions>
				<exclusion>
					<groupId>com.alibaba.nacos</groupId>
					<artifactId>nacos-client</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba.csp</groupId>
			<artifactId>sentinel-cluster-client-default</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba.csp</groupId>
			<artifactId>sentinel-datasource-nacos</artifactId>
			<exclusions>
				<exclusion>
					<groupId>com.alibaba.nacos</groupId>
					<artifactId>nacos-client</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</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>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>${spring-boot.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>com.alibaba.cloud</groupId>
				<artifactId>spring-cloud-alibaba-dependencies</artifactId>
				<version>${spring-cloud-alibaba.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<version>${spring-boot.version}</version>
				<configuration>
					<mainClass>tech.foolfish.demo.ChargeServiceApplication</mainClass>
					<skip>true</skip>
				</configuration>
				<executions>
					<execution>
						<id>repackage</id>
						<goals>
							<goal>repackage</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

</project>

application.properties

server.port=8081
spring.application.name=charge-service
spring.profiles.active=dev

spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos

spring.cloud.nacos.config.server-addr=localhost:8848
spring.cloud.nacos.config.group=DEFAULT_GROUP
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.config.extension-configs[0].data-id=charge-service
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true

#spring.cloud.sentinel.enabled=false
spring.cloud.sentinel.log.switch-pid=true
spring.cloud.sentinel.transport.dashboard=localhost:8080

spring.cloud.sentinel.datasource.ds0.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds0.nacos.username=nacos
spring.cloud.sentinel.datasource.ds0.nacos.password=nacos
spring.cloud.sentinel.datasource.ds0.nacos.data-id=${spring.application.name}-flow-rules
spring.cloud.sentinel.datasource.ds0.nacos.group-id=SENTINEL_GROUP
spring.cloud.sentinel.datasource.ds0.nacos.rule-type=flow

spring.cloud.sentinel.datasource.ds1.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds1.nacos.username=nacos
spring.cloud.sentinel.datasource.ds1.nacos.password=nacos
spring.cloud.sentinel.datasource.ds1.nacos.data-id=${spring.application.name}-degrade-rules
spring.cloud.sentinel.datasource.ds1.nacos.group-id=SENTINEL_GROUP
spring.cloud.sentinel.datasource.ds1.nacos.rule-type=degrade

spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds2.nacos.username=nacos
spring.cloud.sentinel.datasource.ds2.nacos.password=nacos
spring.cloud.sentinel.datasource.ds2.nacos.data-id=${spring.application.name}-system-rules
spring.cloud.sentinel.datasource.ds2.nacos.group-id=SENTINEL_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.rule-type=system

spring.cloud.sentinel.datasource.ds3.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds3.nacos.username=nacos
spring.cloud.sentinel.datasource.ds3.nacos.password=nacos
spring.cloud.sentinel.datasource.ds3.nacos.data-id=${spring.application.name}-param-rules
spring.cloud.sentinel.datasource.ds3.nacos.group-id=SENTINEL_GROUP
spring.cloud.sentinel.datasource.ds3.nacos.rule-type=param-flow

spring.cloud.sentinel.datasource.ds4.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds4.nacos.username=nacos
spring.cloud.sentinel.datasource.ds4.nacos.password=nacos
spring.cloud.sentinel.datasource.ds4.nacos.data-id=${spring.application.name}-auth-rules
spring.cloud.sentinel.datasource.ds4.nacos.group-id=SENTINEL_GROUP
spring.cloud.sentinel.datasource.ds4.nacos.rule-type=authority

Java代码

package tech.foolfish.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ChargeServiceApplication {

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

}
package tech.foolfish.demo.local;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/demo")
@SuppressWarnings("rawtypes")
public class DemoController {

	@Autowired
	private DemoService demoService;

	@GetMapping(value = "/hello")
	public ResponseData hello() {
		return demoService.hello();
	}

}
package tech.foolfish.demo.local;

import org.springframework.stereotype.Service;

import com.alibaba.csp.sentinel.annotation.SentinelResource;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class DemoService {

	@SentinelResource
	public ResponseData hello() {
		ResponseData responseData = new ResponseData("0", "成功", "你好 Sentinel");
		log.info("responseData = {}", responseData);
		return responseData;
	}

}
package tech.foolfish.demo.local;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
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.alibaba.fastjson.JSON;
import com.google.common.base.Throwables;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) {
		ResponseData<?> restObject = null;
		if (e instanceof FlowException) {
			// 限流
			restObject = new ResponseData("107002", "BlockExceptionHandler-接口限流", null);
		} else if (e instanceof DegradeException) {
			// 降级
			restObject = new ResponseData("107003", "BlockExceptionHandler-服务降级", null);
		} else if (e instanceof ParamFlowException) {
			// 热点参数
			restObject = new ResponseData("107004", "BlockExceptionHandler-热点参数限流", null);
		} else if (e instanceof SystemBlockException) {
			// 系统保护
			restObject = new ResponseData("107005", "BlockExceptionHandler-触发系统保护规则", null);
		} else if (e instanceof AuthorityException) {
			// 授权规则
			restObject = new ResponseData("107006", "BlockExceptionHandler-授权规则不通过", null);
		} else {
			restObject = new ResponseData("107001", "BlockExceptionHandler-接口异常", null);
		}
		// 返回json数据
		response.setCharacterEncoding("UTF-8");
		response.setHeader("Content-Type", "application/json;charset=UTF-8");
        PrintWriter out;
		try {
			out = response.getWriter();
			out.print(JSON.toJSON(restObject));
			out.flush();
			out.close();
		} catch (IOException e1) {
			log.error(Throwables.getStackTraceAsString(e));
		}
        
	}
}
package tech.foolfish.demo.local;

import lombok.*;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponseData<T> {

	private String errorId;
	
	private String message;
	
	private T data;
	
}

发起接口请求

配置规则

发起接口请求后,可以从簇点链路页面看到该接口请求的完整链路结构。

在簇点链路页面,可以快速新增规则。如下只是流控规则的示例,熔断、热点、授权,按需新增即可。

查看规则数据源

测试流控规则的效果