sentinel的简介和使用的详细文档请参考 https://sentinelguard.io/zh-cn/docs/introduction.html
nacos的搭建和使用介绍请参考 https://nacos.io/zh-cn/docs/quick-start.html
首先我们要明白在使用sentinel的时候有几种模式,具体有哪几种可以参考详细文档,这里我们只说明最适用于生产上的模式。
sentinel-dashboard(sentinel控制台)用于页面化监控服务,页面配置限流、熔断、系统保护、热点、来源访问控制等。
nacos用于持久化和动态配置sentinel的规则。
这里我们直接上操作步骤
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456 -jar sentinel-dashboard-1.8.5.jar
启动参数解释:
-Dserver.port=8080 // 指定启动端口
-Dcsp.sentinel.dashboard.server=localhost:8080 // 指定服务
-Dproject.name=sentinel-dashboard // 指定sentinel控制台服务的名字
-Dsentinel.dashboard.auth.username=sentinel // 指定sentinel登录名
-Dsentinel.dashboard.auth.password=123456 // 指定sentinel登录密码
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-coreartifactId>
<version>1.8.5version>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-transport-simple-httpartifactId>
<version>1.8.5version>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-annotation-aspectjartifactId>
<version>1.8.5version>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
<version>2.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
<version>1.8.5version>
dependency>
这里只配置流控、熔断、授权的配置,其他配置是相同的。
在生产上部署的时候这里需要配置一下心跳地址,注意使用client-ip而不是clientIp
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
port: 8719
client-ip: [ip] # 在生产上部署的时候这里需要配置一下心跳地址,注意使用client-ip而不是clientIp
datasource:
flow:
nacos:
server-addr: localhost:8848 # nacos连接地址
namespace: d95f824e-9eb5-4181-be30-9fc4c5edf1f2
group-id: DEFAULT_GROUP # nacos连接的分组
rule-type: flow # 流控规则 rule-type 配置表示该数据源中的规则属于哪种类型的规则(flow,degrade,authority,system, param-flow, gw-flow, gw-api-group)
data-id: sentinel-flow # 读取配置文件的 data-id
data-type: json # 读取培训文件类型为json
degrade:
nacos:
server-addr: localhost:8848
namespace: d95f824e-9eb5-4181-be30-9fc4c5edf1f2
group-id: DEFAULT_GROUP
rule-type: degrade
data-id: sentinel-degrade
data-type: json
authority:
nacos:
server-addr: localhost:8848
namespace: d95f824e-9eb5-4181-be30-9fc4c5edf1f2
group-id: DEFAULT_GROUP
rule-type: authority
data-id: sentinel-authority
data-type: json
value和sentinel中配置的接口地址值要一致
blockHandler是指定的通用流控异常返回类,如果写在该注解注用的方法的同一个类中,那么blockHandlerClass便不用指定,如果有通用的异常处理类,blockHandlerClass需要指定类
@SentinelResource(value = "xxx", blockHandler = "generalBlockHandler", blockHandlerClass = {BlockHandlerUtils.class})
public class BlockHandlerUtils {
public static RespResult generalBlockHandler(JSONObject jsonObject, BlockException e) {
e.printStackTrace();
String msg = "未知异常";
int status = 429;
if (e instanceof FlowException) {
msg = "请求被限流了";
} else if (e instanceof ParamFlowException) {
msg = "请求被热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求被降级了";
} else if (e instanceof AuthorityException) {
msg = "没有权限访问";
status = 401;
}
return new RespResult(status, msg);
}
}
public static void main(String[] args) {
// 配置规则.
initFlowRules();
while (true) {
// 1.5.0 版本开始可以直接利用 try-with-resources 特性
try (Entry entry = SphU.entry("HelloWorld")) {
// 被保护的逻辑
System.out.println("hello world");
} catch (BlockException ex) {
// 处理被流控的逻辑
System.out.println("blocked!");
}
}
}
private static void initFlowRules(){
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);
}
具体的规则和详细的配置就不在这里赘述了,大家可以自行去sentinel中文官网去查询。
Nacos本身仅支持Mysql数据库,但实际开发中,我们可能要用到其他的数据库进行存储,大家可以根据https://blog.csdn.net/weixin_43148701/article/details/126015374
这片文章进行修改数据库,但是其中可能会遇到不少问题,在这里说明一下:
1、idea要下载一下插件Protobuf,没有这个插件,编译会报错
2、nacos源码中的pom-all.xml中需要修改下面两个依赖的版本号,可根据nacos版本自行进行升级,我这里用的是nacos-2.1.2,源码中本身所用的版本,在我本地编译不过,网上也找了本多解决版本,大多不能生效,进行了版本升级后成功编译通过。
3、打包的时候可能会出现错误,但只要整体打包成功,便没有问题。
4、Nacos启动的时候使用./startup.sh -m standalone,即可启动单机版,默认是集群
1、根据不同的业务流程可以在代码中进行动态发布和删除相关规则,这里我以授权规则为例,进行介绍
@Configuration
@Slf4j
public class NacosConfig {
@Value("${spring.cloud.sentinel.datasource.authority.nacos.server-addr}")
private String serverAddr;
@Value("${spring.cloud.sentinel.datasource.authority.nacos.namespace}")
private String namespace;
@Bean
@Primary
public ConfigService configService() {
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
properties.put("namespace", namespace);
try {
return NacosFactory.createConfigService(properties);
} catch (NacosException e) {
log.error(e.toString(), e);
}
return null;
}
}
@Component
@Slf4j
public class NacosListener {
@Resource
ConfigService configService;
@Value("${spring.cloud.sentinel.datasource.authority.nacos.group-id}")
private String sentinelAuthorityGroupId;
@Value("${spring.cloud.sentinel.datasource.authority.nacos.data-id}")
private String sentinelAuthorityDataId;
/**
* 向nacos发布初始化内容
*
* @param jsonArray 发布的数据
* @throws Exception 抛出的异常
*/
public void insertAuthConfig(JSONArray jsonArray) throws Exception {
// 先查询一下nacos上是否已有数据
String config = configService.getConfig(sentinelAuthorityDataId, sentinelAuthorityGroupId, 5000);
JSONArray parseArray = JSONArray.parseArray(config);
if (parseArray.size() > 0) {
updateAuthConfig(jsonArray);
} else {
// 如果nacos上没有授权规则的数据则进行新增
//发布内容
boolean b = configService.publishConfig(sentinelAuthorityDataId, sentinelAuthorityGroupId, jsonArray.toJSONString());
if (b) {
log.info("Nacos发布成功!");
} else {
log.error("Nacos发布失败!");
}
}
}
/**
* 修改nacos的配置
*
* @param array 发布的数据
*/
public void updateAuthConfig(JSONArray array) throws NacosException {
String config = configService.getConfig(sentinelAuthorityDataId, sentinelAuthorityGroupId, 5000);
JSONArray jsonArray = JSONArray.parseArray(config);
boolean flag = false;
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject json = jsonArray.getJSONObject(i);
for (int j = 0; j < array.size(); j++) {
JSONObject jsonObject = array.getJSONObject(j);
if (json.getString("resource").equalsIgnoreCase(jsonObject.getString("resource"))) {
// 查看一下nacos上是否有关于传入访问地址的白名单如果有,那么就对将传入的IP加到原有的
String limitApp = json.getString("limitApp");
String ip = jsonObject.getString("limitApp");
String[] split = limitApp.split(",");
boolean f = false;
for (String s : split) {
if (s.equalsIgnoreCase(ip)) {
f = true;
break;
}
}
if (!f) {
limitApp += "," + ip;
} else {
log.info("Nacos上已有需要发布的数据!");
return;
}
json.put("limitApp", limitApp);
flag = true;
}
}
}
boolean b;
if (flag) {
// nacos上有需要添加给名单的资源,将ip进行添加
b = configService.publishConfig(sentinelAuthorityDataId, sentinelAuthorityGroupId, jsonArray.toString());
} else {
// nacos没有需要添加的资源,将资源进行添加
jsonArray.addAll(array);
b = configService.publishConfig(sentinelAuthorityDataId, sentinelAuthorityGroupId, jsonArray.toString());
}
if (b) {
log.info("Nacos发布成功!");
} else {
log.error("Nacos发布失败!");
}
}
}
到此基本就差不多啦,有问题的道友们,欢迎评论区留言!