Sentinel基本概念在此文章就不进行介绍了,没有了解过的,可以参考我的其他文章有介绍Sentinel是解决什么问题的
首先去Sentinel官网上,将它的源码进行下载,因为在它源码上进行修改,当然如果觉得源码不方便,也可以直接下载它官网上的jar包
Sentinel官方地址
代码语言:javascript复制https://github.com/alibaba/Sentinel/wiki/介绍
Sentinel下载地址
代码语言:javascript复制https://github.com/alibaba/Sentinel/releases
我现在下载当前最新版本1.7.2版本
将源码下载下来后,我们向进入dashboard模块(Sentinel控制台模块),配置文件默认为properties格式,我这里自定义改成了yml格式,并自定义端口号为8888,防止8080与其他服务端口冲突
启动控制台:localhost:8888 如下界面,可以看到我们可以配置多个规则
上面我们的Sentinel Server端基本就改造完成了,接下来开始配置我们的客户端 我们先在客户端添加Sentinel依赖
代码语言:javascript复制<!--springboot 整合 sentinel框架-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
<version>0.9.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
接下来添加Sentinel的本地文件持久化代码,代码具体啥意思,注释都有写到了
代码语言:javascript复制/**
* @Author:
* @Date: 2020/3/26 22:00
* @Description: klm
*/
public class DataSourceInitFunc implements InitFunc {
@Override
public void init() throws Exception {
// TIPS: 持久化在本地的目录,如果你对这个路径不喜欢,可修改为你喜欢的路径
String ruleDir = System.getProperty("user.home") "/sentinel/order/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 paramFlowRulePath = ruleDir "/param-flow-rule.json";
this.mkdirIfNotExits(ruleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(paramFlowRulePath);
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
flowRulePath,
flowRuleListParser
);
// 将可读数据源注册至FlowRuleManager
// 这样当规则文件发生变化时,就会更新规则到内存
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
flowRulePath,
this::encodeJson
);
// 将可写数据源注册至transport模块的WritableDataSourceRegistry中
// 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
// 降级规则
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);
// 系统规则
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);
// 授权规则
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);
// 热点参数规则
ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
paramFlowRulePath,
paramFlowRuleListParser
);
ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
paramFlowRulePath,
this::encodeJson
);
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
}
private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<FlowRule>>() {
}
);
private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<DegradeRule>>() {
}
);
private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<SystemRule>>() {
}
);
private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<AuthorityRule>>() {
}
);
private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<ParamFlowRule>>() {
}
);
private void mkdirIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
}
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);
}
}
接下我们进入resources目录
代码语言:javascript复制1.新建META-INF目录
META-INF
2.然后在META-INF目录新建services目录
services
3.在services目录下,新建初始化文件,文件的内容,就是配置文件持久化代码的路径地址
com.alibaba.csp.sentinel.init.InitFunc
修改application.yml配置,在配置里面我们需要连接Sentinel控制台地址,定时向控制台发送心跳,判断服务是否健康状态
代码语言:javascript复制spring:
application:
name: cluster_user_sentinel
cloud:
#sentinel注册地址
sentinel:
transport:
dashboard: localhost:8888 #Sentinel 控制台地址
port: 8720 #客户端监控API的端口
eager: true #取消Sentinel控制台懒加载
log:
dir: ./logs # 默认值${home}/logs/csp/
switch-pid: true # 日志带上线程id
然后在我们的在项目中,创建一个接口,并对这个接口实现降级等操作
接下来我们开始启动项目看看效果 如下图,可以看到我们的服务,已经注册Sentinel里,控制台已经能显示到
现在对刚刚创建的接口,实现流控操作
资源名:就是接口 SentinelResource注解 来源:默认default即可 阈值类型: qps:每秒钟运行N个请求进来 线程数:每秒钟N隔线程处理 阈值:自定义的值 集群:因为我们是单机的,所以就不用选集群了
请求接口,一秒请求了两次,现在就已经限流了 但是英文返回出去,非常不友好,所以我们可以自定义返回内容出去
抽出公共的返回错误码
代码语言:javascript复制public enum ResponseCode {
success(200, "请求成功"),
fail(400, "请求失败"),
error(500, "服务端错误"),
serviceFuse(700, "请求熔断,稍后重试"),
serviceFlow(701, "请求限流,稍后重试"),
serviceHotspot(702, "请求热点参数限流,稍后重试"),
serviceSystem(703, "请求触发系统保护规则,稍后重试"),
serviceRules(704, "请求Sentinel授权规则不通过,稍后重试"),
unkown(999, "未知类型"),
;
private int code;
private String desc;
ResponseCode(int code, String desc) {
this.code = code;
this.desc = desc;
}
public static ResponseCode getByCode(int code) {
for (ResponseCode responseCode : ResponseCode.values()) {
if (responseCode.code() == (code)) {
return responseCode;
}
}
return unkown;
}
public int code() {
return this.code;
}
public String desc() {
return this.desc;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
################################################
public class CommonResult<T> implements Serializable {
public CommonResult() {
}
public CommonResult(T t) {
this(ResponseCode.success.code(), null, t);
}
public CommonResult(int code, T t) {
this(code, null, t);
}
public CommonResult(int code, String msg, T t) {
this.code = code;
this.msg = msg;
this.data = t;
}
/*错误码*/
private int code = ResponseCode.success.code();
/*提示信息*/
private String msg = "";
/*具体的内容*/
private T data = null;
public CommonResult success() {
return this;
}
public CommonResult success(T t) {
this.msg = "请求成功";
this.data = t;
return this;
}
public CommonResult success(int code, T t) {
this.code = code;
this.msg = "请求成功";
this.data = t;
return this;
}
public CommonResult success(int code, String msg, T t) {
this.code = code;
this.msg = msg;
this.data = t;
return this;
}
public CommonResult failed() {
this.code = ResponseCode.error.code();
this.msg = "请求成功,未查询到数据";
return this;
}
public CommonResult failed(int code, T t) {
this.code = code;
this.msg = "请求成功";
return this;
}
public CommonResult failed(int code, String msg, T t) {
this.code = code;
this.msg = msg;
this.data = t;
return this;
}
public CommonResult failed(String msg) {
this.code = ResponseCode.error.code();
this.msg = msg;
return this;
}
public CommonResult saveSuccess(int num) {
this.msg = String.format("成功保存%s条数据", num);
return this;
}
public CommonResult editSuccess(int num) {
this.msg = String.format("成功修改%s条数据", num);
return this;
}
public CommonResult deleteSuccess(int num) {
this.msg = String.format("成功删除%s条数据", num);
return this;
}
public static CommonResult error(String msg) {
return new CommonResult(ResponseCode.error.code(), msg, null);
}
public static CommonResult build(ResponseCode responseCode) {
return new CommonResult(responseCode.code(), responseCode.desc(), null);
}
public CommonResult buildByResponseCode(ResponseCode responseCode) {
this.code = responseCode.getCode();
this.msg = responseCode.getDesc();
return this;
}
/**
* 异常类返回结果处理
*
* @param msg
* @param exception
* @return
*/
public static CommonResult error(String msg, String exception) {
return new CommonResult(ResponseCode.error.code(), msg, exception);
}
public static CommonResult error(Integer code, String msg) {
return new CommonResult(code, msg, null);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
自定义返回内容
代码语言:javascript复制@Slf4j
@Component
public class SentinelUrlBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
CommonResult<String> errorResponse = null;
String serverName = "用户服务,";
// 不同的异常返回不同的提示语
if (e instanceof FlowException) {
errorResponse = new CommonResult<>().failed(ResponseCode.serviceFlow.getCode(), serverName ResponseCode.serviceFlow.getDesc(), null);
} else if (e instanceof DegradeException) {
errorResponse = new CommonResult<>().failed(ResponseCode.serviceFuse.getCode(), serverName ResponseCode.serviceFuse.getDesc(), null);
} else if (e instanceof ParamFlowException) {
errorResponse = new CommonResult<>().failed(ResponseCode.serviceHotspot.getCode(), serverName ResponseCode.serviceHotspot.getDesc(), null);
} else if (e instanceof SystemBlockException) {
errorResponse = new CommonResult<>().failed(ResponseCode.serviceSystem.getCode(), serverName ResponseCode.serviceSystem.getDesc(), null);
} else if (e instanceof AuthorityException) {
errorResponse = new CommonResult<>().failed(ResponseCode.serviceRules.getCode(), serverName ResponseCode.serviceRules.getDesc(), null);
}
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
new ObjectMapper().writeValue(response.getWriter(), errorResponse);
}
}
再次请求,这次就返回自定义的内容了
当然,本文的重点是规则持久化,所以我们可以选择重启服务,看规则是否会消失
我这里选择重启是不会消失的啦,不然也不会发文章哦~
接下来看到我们指定文件存入的规则
如果有伙伴不知道路径在哪里,可如下图找即可~
以上基于本地文件模式持久化规则,到此就结束了。