在 spring cloud: 微服务环境搭建(alibaba)进行限流、降级、熔断
1. 错误信息配置化(3. 服务容错Sentinel)
目标:把限流返回信息变成可配置,并保存到nacos上
GatewayConfiguration.java
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
/**
* 限流配置
*/
@Value("${spring.cloud.sentinel.flow.status}")
private String flowStatus;
@Value("${spring.cloud.sentinel.flow.message}")
private String flowMsg;
@PostConstruct
public void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add( new GatewayFlowRule("Item_route") //资源名称,对应路由id
.setCount(1) // 限流阈值
.setIntervalSec(1) // 统计时间窗口,单位是秒,默认是 1 秒
);
GatewayRuleManager.loadRules(rules);
}
@PostConstruct
public void initBlockHandlers(){
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map map = new HashMap<>();
map.put("code",flowStatus);
map.put("message",flowMsg);
return ServerResponse
.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
/**
* 增加这个函数的意义:可以从配置文件获取值(如果缺失,取不到)
* @return
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
http://localhost:8848/nacos/ 增加如下配置
执行结果: 频繁访问后
2. Sentinel的限流、降级、熔断
具体参考:cloud.tencent.com/developer/a…
从上面代码可以看出来,initBlockHandlers按照异常类型分装就可
package com.temp.conf;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
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 lombok.Data;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
/**
* 限流
*/
@Value("${spring.cloud.sentinel.flow.status}")
private Integer flowStatus;
@Value("${spring.cloud.sentinel.flow.message}")
private String flowMsg;
/**
* 降级
*/
@Value("${spring.cloud.sentinel.degrade.status}")
private Integer degradeStatus;
@Value("${spring.cloud.sentinel.degrade.message}")
private String degradeMsg;
/**
* 热点参数限流
*/
@Value("${spring.cloud.sentinel.param.flow.status}")
private Integer paramFlowStatus;
@Value("${spring.cloud.sentinel.param.flow.message}")
private String paramFlowMsg;
/**
* 系统规则(负载/...不满足要求)
*/
@Value("${spring.cloud.sentinel.system.block.status}")
private Integer systemBlockStatus;
@Value("${spring.cloud.sentinel.system.block.message}")
private String systemBlockMsg;
@Value("${spring.cloud.sentinel.authority.status}")
private Integer authorityStatus;
@Value("${spring.cloud.sentinel.authority.message}")
private String authorityMsg;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers =
viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* Initialize a current-limiting filter
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
/**
* Configure current limiting exception handler
*
* @return SentinelGatewayBlockExceptionHandler
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(
viewResolvers,
serverCodecConfigurer
);
}
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable e) {
SentinelErrorMsg sentinelErrorMsg = new SentinelErrorMsg();
// 流控异常
if (e instanceof FlowException) {
sentinelErrorMsg.setMsg(flowMsg);
sentinelErrorMsg.setStatus(flowStatus);
// 降级异常
} else if (e instanceof DegradeException) {
sentinelErrorMsg.setMsg(degradeMsg);
sentinelErrorMsg.setStatus(degradeStatus);
// 参数流控异常
} else if (e instanceof ParamFlowException) {
sentinelErrorMsg.setMsg(paramFlowMsg);
sentinelErrorMsg.setStatus(paramFlowStatus);
// 系统堵塞异常
} else if (e instanceof SystemBlockException) {
sentinelErrorMsg.setMsg(systemBlockMsg);
sentinelErrorMsg.setStatus(systemBlockStatus);
// 权限异常
} else if (e instanceof AuthorityException) {
sentinelErrorMsg.setMsg(authorityMsg);
sentinelErrorMsg.setStatus(authorityStatus);
}
return ServerResponse
.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(sentinelErrorMsg));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
/**
* 增加这个函数的意义:可以从配置文件获取值(如果缺失,取不到)
*
* @return
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
/**
* Error message
*/
@Data
static class SentinelErrorMsg {
private Integer status;
private String msg;
}
}
3. Sentinel 控制台 1.8.1控制
Sentinel上应该看到的工程没看到的话,请按照以下方法操作
执行参数:
-Dcsp.sentinel.app.type=1 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=alibaba-gateway
说明:-Dcsp.sentinel.app.type=1 网关限流设置。
如果还未出来,请清理浏览器缓存;因为是Sentinel是懒加载,需要执行具体api,再刷新就出来了。
3.1 流控
说明:这里做网关流控的话,谨记使用定义好的routId而不是对应的API地址
流控规则设定
在访问此routeID所辖链接时,就会有如下限流效果:
3.2 降级
bug:bbs.huaweicloud.com/blogs/20606…
注意:1.8.0 版本的Sentinel dashboard降级页面有个bug,就是统计时长属性维护 丢失,有望再下一个版本中修复
需要切换到1.8.1版本,修复此bug了
3.2.1 修改之前的service:/item/{id}
@RestController
public class ItemController {
@Autowired
private ItemService itemService;
@RequestMapping("/item/{id}")
public Item query(@PathVariable Long id) {
// 让此服务睡眠10秒钟
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return itemService.queryItemById(id);
}
@RequestMapping("/item/change/{id}/{order_id}")
public Item saveOrderId(@PathVariable("id") Long id,@PathVariable("order_id") Long orderId) {
return itemService.saveOrderId(id,orderId);
}
}
重启temp-item工程
3.2.2 降级配置
配置对话框
保存后,降级规则生效
3.2.3 测试
首次访问时,经过10s结果出来了
但是再次刷新后,服务就变成降级了
说明:10s后就能恢复访问
从这部分开始,对这部分配置进行持久化,稍微花的时间多了些。暂时另起一篇