feat(master):sentinel 相关逻辑

代码完成
master
土豆兄弟 4 days ago
parent 17f4bc0752
commit 07e7e978dc

@ -37,4 +37,5 @@
- [dev-protocol-springcloud-project-goods-service](dev-protocol-springcloud-project-goods-service)
- 订单微服务
- [dev-protocol-springcloud-project-order-service](dev-protocol-springcloud-project-order-service)
- 物流微服务
- 物流微服务
- [dev-protocol-springcloud-project-logistics-service](dev-protocol-springcloud-project-logistics-service)

@ -0,0 +1,76 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example</groupId>
<artifactId>dev-protocol</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>dev-protocol-springcloud-sentinel</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 模块名及描述信息 -->
<name>dev-protocol-springcloud-sentinel</name>
<description>Sentinel Client</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 创建工程需要的两个依赖 -->
<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>
<!-- Sentinel 适配了 Feign, 可以实现服务间调用的保护 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Sentinel 使用 Nacos 存储规则 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- web 工程 -->
<dependency>
<groupId>org.example</groupId>
<artifactId>dev-protocol-springcloud-project-mvc-config</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<!--
SpringBoot的Maven插件, 能够以Maven的方式为应用提供SpringBoot的支持可以将
SpringBoot应用打包为可执行的jar或war文件, 然后以通常的方式运行SpringBoot应用
-->
<build>
<finalName>${artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,18 @@
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* <h1>Sentinel SpringCloud </h1>
* */
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class SentinelClientApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelClientApplication.class, args);
}
}

@ -0,0 +1,29 @@
package org.example.block_handler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.vo.CommonResponse;
/**
* <h1></h1>
* */
@Slf4j
public class QBlockHandler {
/**
* <h2></h2>
* static
* */
public static CommonResponse<String> qHandleBlockException(BlockException exception) {
log.error("trigger q block handler: [{}], [{}]",
JSON.toJSONString(exception.getRule()), exception.getRuleLimitApp());
return new CommonResponse<>(
-1,
"flow rule trigger block exception",
null
);
}
}

@ -0,0 +1,44 @@
package org.example.conf;
import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.vo.JwtToken;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
/**
* <h1>RestTemplate </h1>
* */
@Slf4j
public class RestTemplateExceptionUtil {
/**
* <h2></h2>
* */
public static SentinelClientHttpResponse handleBlock(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution,
BlockException ex) {
log.error("Handle RestTemplate Block Exception: [{}], [{}]",
request.getURI().getPath(), ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse(
JSON.toJSONString(new JwtToken("q-block"))
);
}
/**
* <h2></h2>
* */
public static SentinelClientHttpResponse handleFallback(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution,
BlockException ex) {
log.error("Handle RestTemplate Fallback Exception: [{}], [{}]",
request.getURI().getPath(), ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse(
JSON.toJSONString(new JwtToken("q-block"))
);
}
}

@ -0,0 +1,26 @@
package org.example.conf;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* <h1>, RestTemplate </h1>
* */
@Slf4j
@Configuration
public class SentinelConfig {
/**
* <h2> RestTemplate</h2>
* */
@Bean
// @SentinelRestTemplate(
// fallback = "handleFallback", fallbackClass = RestTemplateExceptionUtil.class,
// blockHandler = "handleBlock", blockHandlerClass = RestTemplateExceptionUtil.class
// )
public RestTemplate restTemplate() {
return new RestTemplate(); // 可以对其做一些业务相关的配置
}
}

@ -0,0 +1,81 @@
package org.example.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.block_handler.QBlockHandler;
import org.example.vo.CommonResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
/**
* <h1> Controller</h1>
* */
@Slf4j
@RestController
@RequestMapping("/code")
public class FlowRuleCodeController {
/**
* <h2></h2>
* */
@PostConstruct
public void init() {
// 流控规则集合
List<FlowRule> flowRules = new ArrayList<>();
// 创建流控规则
FlowRule flowRule = new FlowRule();
// 设置流控规则 QPS, 限流阈值类型 (QPS, 并发线程数)
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 流量控制手段
flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
// 设置受保护的资源
flowRule.setResource("flowRuleCode");
// 设置受保护的资源的阈值
flowRule.setCount(1);
flowRules.add(flowRule);
// 加载配置好的规则
FlowRuleManager.loadRules(flowRules);
}
/**
* <h2> Controller </h2>
* */
@GetMapping("/flow-rule")
// @SentinelResource(value = "flowRuleCode")
// @SentinelResource(value = "flowRuleCode", blockHandler = "handleException")
@SentinelResource(
value = "flowRuleCode", blockHandler = "qHandleBlockException",
blockHandlerClass = QBlockHandler.class
)
public CommonResponse<String> flowRuleCode() {
log.info("request flowRuleCode");
return new CommonResponse<>(0, "", "q-ecommerce");
}
/**
* <h2>, </h2>
* , 使
* */
public CommonResponse<String> handleException(BlockException exception) {
log.error("has block exception: [{}]", JSON.toJSONString(exception.getRule()));
return new CommonResponse<>(
-1,
"flow rule exception",
exception.getClass().getCanonicalName()
);
}
}

@ -0,0 +1,43 @@
package org.example.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import lombok.extern.slf4j.Slf4j;
import org.example.block_handler.QBlockHandler;
import org.example.vo.CommonResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <h1> Sentinel </h1>
* Sentinel , 访, Sentinel Dashboard
* */
@Slf4j
@RestController
@RequestMapping("/dashboard")
public class RateLimitController {
/**
* <h2> dashboard "流控规则" </h2>
* */
@GetMapping("/by-resource")
@SentinelResource(
value = "byResource",
blockHandler = "qHandleBlockException",
blockHandlerClass = QBlockHandler.class
)
public CommonResponse<String> byResource() {
log.info("coming in rate limit controller by resource");
return new CommonResponse<>(0, "", "byResource");
}
/**
* <h2> "簇点链路" url </h2>
* */
@GetMapping("/by-url")
@SentinelResource(value = "byUrl")
public CommonResponse<String> byUrl() {
log.info("coming in rate limit controller by url");
return new CommonResponse<>(0, "", "byUrl");
}
}

@ -0,0 +1,72 @@
package org.example.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.fallback_handler.QFallbackHandler;
import org.example.vo.JwtToken;
import org.example.vo.UsernameAndPassword;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
/**
* <h1>Sentinel </h1>
* */
@SuppressWarnings("all")
@Slf4j
@RestController
@RequestMapping("/sentinel-fallback")
public class SentinelFallbackController {
/** 注入没有增强的 RestTemplate */
private final RestTemplate restTemplate;
public SentinelFallbackController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@PostMapping("/get-token")
@SentinelResource(
value = "getTokenFromAuthorityService",
fallback = "getTokenFromAuthorityServiceFallback",
fallbackClass = { QFallbackHandler.class }
)
public JwtToken getTokenFromAuthorityService(
@RequestBody UsernameAndPassword usernameAndPassword) {
String requestUrl =
"http://127.0.0.1:7000/dev-protocol-springcloud-project-authority-center/authority/token";
log.info("RestTemplate request url and body: [{}], [{}]",
requestUrl, JSON.toJSONString(usernameAndPassword));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return restTemplate.postForObject(
requestUrl,
new HttpEntity<>(JSON.toJSONString(usernameAndPassword), headers),
JwtToken.class
);
}
/**
* <h2> Sentinel </h2>
* */
@GetMapping("/ignore-exception")
@SentinelResource(
value = "ignoreException",
fallback = "ignoreExceptionFallback",
fallbackClass = { QFallbackHandler.class },
exceptionsToIgnore = { NullPointerException.class }
)
public JwtToken ignoreException(@RequestParam Integer code) {
if (code % 2 == 0) {
throw new NullPointerException("yout input code is: " + code);
}
return new JwtToken("qinyi-imooc");
}
}

@ -0,0 +1,33 @@
package org.example.controller;
import lombok.extern.slf4j.Slf4j;
import org.example.feign.SentinelFeignClient;
import org.example.vo.CommonResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* <h1>OpenFeign Sentinel </h1>
* */
@Slf4j
@RestController
@RequestMapping("/sentinel-feign")
public class SentinelFeignController {
private final SentinelFeignClient sentinelFeignClient;
public SentinelFeignController(SentinelFeignClient sentinelFeignClient) {
this.sentinelFeignClient = sentinelFeignClient;
}
/**
* <h2> Feign </h2>
* */
@GetMapping("/result-by-feign")
public CommonResponse<String> getResultByFeign(@RequestParam Integer code) {
log.info("coming in get result by feign: [{}]", code);
return sentinelFeignClient.getResultByFeign(code);
}
}

@ -0,0 +1,53 @@
package org.example.controller;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.vo.JwtToken;
import org.example.vo.UsernameAndPassword;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* <h1>使 Sentinel RestTemplate </h1>
* */
@Slf4j
@RestController
@RequestMapping("/sentinel-rest-template")
public class SentinelRestTemplateController {
private final RestTemplate restTemplate;
public SentinelRestTemplateController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* <h2> JwtToken</h2>
* 1. :
* http://127.0.0.1:7000/ecommerce-authority-center/authority/token
* 2. :
* */
@PostMapping("/get-token")
public JwtToken getTokenFromAuthorityService(
@RequestBody UsernameAndPassword usernameAndPassword) {
String requestUrl =
"http://127.0.0.1:7000/dev-protocol-springcloud-project-authority-center/authority/token";
log.info("RestTemplate request url and body: [{}], [{}]",
requestUrl, JSON.toJSONString(usernameAndPassword));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return restTemplate.postForObject(
requestUrl,
new HttpEntity<>(JSON.toJSONString(usernameAndPassword), headers),
JwtToken.class
);
}
}

@ -0,0 +1,33 @@
package org.example.fallback_handler;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.vo.JwtToken;
import org.example.vo.UsernameAndPassword;
/**
* <h1>Sentinel 退</h1>
*
* */
@Slf4j
public class QFallbackHandler {
/**
* <h2>getTokenFromAuthorityService fallback</h2>
* */
public static JwtToken getTokenFromAuthorityServiceFallback(
UsernameAndPassword usernameAndPassword
) {
log.error("get token from authority service fallback: [{}]",
JSON.toJSONString(usernameAndPassword));
return new JwtToken("q-fallback");
}
/**
* <h2>ignoreException fallback</h2>
* */
public static JwtToken ignoreExceptionFallback(Integer code) {
log.error("ignore exception input code: [{}] has trigger exception", code);
return new JwtToken("q-fallback");
}
}

@ -0,0 +1,20 @@
package org.example.feign;
import org.example.vo.CommonResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* <h1> Sentinel OpenFeign </h1>
* */
@FeignClient(
value = "dev-protocol-springcloud-sentinel-null", // 定义不存在的服务名称, 测试是否正常执行
fallback = SentinelFeignClientFallback.class
)
public interface SentinelFeignClient {
@RequestMapping(value = "qinyi", method = RequestMethod.GET)
CommonResponse<String> getResultByFeign(@RequestParam Integer code);
}

@ -0,0 +1,22 @@
package org.example.feign;
import lombok.extern.slf4j.Slf4j;
import org.example.vo.CommonResponse;
import org.springframework.stereotype.Component;
/**
* <h1>Sentinel OpenFeign </h1>
* */
@Slf4j
@Component
public class SentinelFeignClientFallback implements SentinelFeignClient{
@Override
public CommonResponse<String> getResultByFeign(Integer code) {
log.error("request supply for test has some error: [{}]", code);
return new CommonResponse<>(
-1,
"sentinel feign fallback",
"input code: "+ code
);
}
}

@ -0,0 +1,59 @@
server:
port: 8100
servlet:
context-path: /dev-protocol-springcloud-sentinel
spring:
application:
name: dev-protocol-springcloud-sentinel # 应用名称也是构成 Nacos 配置管理 dataId 字段的一部分 (当 config.prefix 为空时)
cloud:
nacos:
# 服务注册发现
discovery:
enabled: true # 如果不想使用 Nacos 进行服务注册和发现, 设置为 false 即可
server-addr: 127.0.0.1:8848
# server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850 # Nacos 服务器地址
namespace: 1ccc74ae-9398-4dbe-b9d7-4f9addf9f40c
metadata:
management:
context-path: ${server.servlet.context-path}/actuator
sentinel:
# 配置 sentinel dashboard 地址
transport:
dashboard: 127.0.0.1:7777
port: 8719 # 会在应用对应的机器上启动一个 Http Server, 该 Server 会与 Sentinel 控制台做交互
datasource:
# 名称任意, 代表数据源
ds:
nacos:
# NacosDataSourceProperties.java 中定义
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-sentinel
namespace: ${spring.cloud.nacos.discovery.namespace}
groupId: DEFAULT_GROUP
data-type: json
# 规则类型: com.alibaba.cloud.sentinel.datasource.RuleType
# FlowRule 就是限流规则
rule-type: flow
# 服务启动直接建立心跳连接
eager: true
# 暴露端点
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
# 打开 Sentinel 对 Feign 的支持
feign:
sentinel:
enabled: true
# 开启或关闭 @SentinelRestTemplate 注解
resttemplate:
sentinel:
enabled: true

@ -0,0 +1,3 @@
### 硬编码的流控规则
GET 127.0.0.1:8100/dev-protocol-springcloud-sentinel/code/flow-rule
Content-Type: application/json

@ -0,0 +1,7 @@
### 根据资源名称限流
GET 127.0.0.1:8100/dev-protocol-springcloud-sentinel/dashboard/by-resource
Content-Type: application/json
### 根据 URL 限流
GET 127.0.0.1:8100/dev-protocol-springcloud-sentinel/dashboard/by-url
Content-Type: application/json

@ -0,0 +1,12 @@
### 获取 token
POST 127.0.0.1:8100/dev-protocol-springcloud-sentinel/sentinel-fallback/get-token
Content-Type: application/json
{
"username": "1@qqq.com",
"password": "25d55ad283aa400af464c76d713c07ad"
}
### 忽略异常
GET 127.0.0.1:8100/dev-protocol-springcloud-sentinel/sentinel-fallback/ignore-exception?code=2
Content-Type: application/json

@ -0,0 +1,3 @@
### Sentinel Feign Client
GET 127.0.0.1:8100/dev-protocol-springcloud-sentinel/sentinel-feign/result-by-feign?code=200
Content-Type: application/json

@ -0,0 +1,8 @@
### 获取 Token
POST 127.0.0.1:8100/dev-protocol-springcloud-sentinel/sentinel-rest-template/get-token
Content-Type: application/json
{
"username": "1@qqq.com",
"password": "25d55ad283aa400af464c76d713c07ad"
}

@ -66,6 +66,7 @@
<module>dev-protocol-springcloud/dev-protocol-springcloud-message-study</module>
<module>dev-protocol-springcloud/dev-protocol-springcloud-project-order-service</module>
<module>dev-protocol-springcloud/dev-protocol-springcloud-project-logistics-service</module>
<module>dev-protocol-springcloud/dev-protocol-springcloud-sentinel</module>
</modules>
<properties>

Loading…
Cancel
Save