Springcloud-alibaba集成sentinel(ribbon+openFeign+优化错误页面)
每天多学一点点~
话不多说,这就开始吧…
文章目录
1.前言
不得不说,Hystrix再sentinel面前就是个弟弟~
2.下载启动
- dashboard下载
我这里选择1.7.0 版本

- 启动
java -jar sentinel-dashboard-1.7.0.jar --server.port=9999 启动(就是一个Springboot工程)
默认账号密码是 sentinel
3.与springcloud整合
其实与这些第三方组件整合,无非就是三板斧
- 加依赖
<!--spring cloud 公共 依赖 -->
<!--引入springcloud的版本-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--加入nocas-client-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
<!--加入sentinel-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--加入actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- yml配置
首先,添加Sentinel后,会暴露/actuator/sentinel端点http://localhost:8080/actuator/sentinel
而Springboot默认是没有暴露该端点的,所以我们需要自己设置
其次,加入 dashboard 的地址
spring:
datasource:
xxxxxx : 这里就不写了 db的配置
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:9999
#namespace: bc7613d2-2e22-4292-a748-48b78170f14c #指定namespace的id
application:
name: order-center
main:
allow-bean-definition-overriding: true
server:
port: 8080
management:
endpoints:
web:
exposure:
include: '*'
4.测试 sentinel
启动项目+nacos+sentinel dashboard 三个,随便访问一个项目接口,比如我这里的
http://localhost:8080/saveOrder

在 sentinel dashboard 中便会又显示(记住,首先要访问一次自己的端口,才会有显示)
我们给其加入 一个 流控规则 阈值为1
多访问几次接口 ,出现 Blocked by Sentinel (flow limiting) 说明被流控,成功
sentinel功能很强大,其他 关于 限流 降级 的配置 可以去git上自己去看看,这里就不演示了
5.与ribbon整合
- 加依赖
<!--加入ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
- yml配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:9999
#是否开启@SentinelRestTemplate注解
resttemplate:
sentinel:
enabled: true
- 加注解
(在 RestTemplate组件上添加 @SentinelRestTemplate注解) 并且我们可以通过在@SentinelRestTemplate 同样的可以指定我们的 blockHandlerClass fallbackClass blockHandler fallback 这四个属性
@Configuration
public class WebConfig {
@Bean
@LoadBalanced
@SentinelRestTemplate(
blockHandler = "handleException",blockHandlerClass = GlobalExceptionHandler.class,
fallback = "fallback",fallbackClass = GlobalExceptionHandler.class
)
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
GlobalExceptionHandler 是自己的全局异常处理类,这里简单的写一下
@Slf4j
public class GlobalExceptionHandler {
/**
* 限流后处理方法
*/
public static SentinelClientHttpResponse handleException(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
ProductInfo productInfo = new ProductInfo();
productInfo.setProductName("被限制流量拉");
productInfo.setProductNo("-1");
ObjectMapper objectMapper = new ObjectMapper();
try {
return new SentinelClientHttpResponse(objectMapper.writeValueAsString(productInfo));
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
/**
* 熔断后处理的方法
*/
public static SentinelClientHttpResponse fallback(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
ProductInfo productInfo = new ProductInfo();
productInfo.setProductName("被降级拉");
productInfo.setProductNo("-1");
ObjectMapper objectMapper = new ObjectMapper();
try {
return new SentinelClientHttpResponse(objectMapper.writeValueAsString(productInfo));
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
}
6.与openFeign整合
sentinel默认与feign整合了
- 加依赖
<!--加入sentinel-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--加入actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- yml配置
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:9999
feign:
httpclient:
enabled: true # #让feign底层使用HttpClient去调用
max-connections: 200 # 最大连接数
max-connections-per-route: 50 # 为每个url请求设置最大连接数
client:
config:
dafult:
connectTimeout: 5000
readTime: 5000
product-center: # 要调用服务的名称
loggerLevel: FULL # 日志级别
sentinel:
enabled: true # 打开 Sentinel 对 Feign 的支持 这样@FeignClient的fallback 才会生效
- 在Feign的声明式接口上添加fallback属性或者 fallbackFactory属 性
这里直接通过 fallbackFactory 声明,因为通过 fallback 无法捕获异常
//@FeignClient(name = "product-center",fallback = ProductCenterFeignApiWithSentinelFallback.class)
@FeignClient(name = "product-center",fallbackFactory = ProductCenterFeignApiWithSentielFallbackFactory.class)
public interface ProductCenterFeignApiWithSentinel {
/**
* 声明式接口,远程调用http://product-center/selectProductInfoById/{productNo}
* @param productNo
* @return
*/
@RequestMapping("/selectProductInfoById/{productNo}")
ProductInfo selectProductInfoById(@PathVariable("productNo") String productNo);
}
FallbackFactory 能区别异常
@Component
@Slf4j
public class ProductCenterFeignApiWithSentielFallbackFactory implements FallbackFactory<ProductCenterFeignApiWithSentinel> {
@Override
public ProductCenterFeignApiWithSentinel create(Throwable throwable) {
return new ProductCenterFeignApiWithSentinel(){
@Override
public ProductInfo selectProductInfoById(String productNo) {
log.error("原因:{}",throwable);
ProductInfo productInfo = new ProductInfo();
productInfo.setProductName("默认商品");
return productInfo;
}
};
}
}
Fallback 不能区别异常
@Component
public class ProductCenterFeignApiWithSentinelFallback implements ProductCenterFeignApiWithSentinel {
@Override
public ProductInfo selectProductInfoById(String productNo) {
ProductInfo productInfo = new ProductInfo();
productInfo.setProductName("默认商品");
return productInfo;
}
}
到此整合结束,但是 ,sentinel不支持持久化 ,即 你重启sentinel dashboard 或者 项目工程,你配置在sentinel中的流控降级等等规则全部 没了,这也是阿里比较坑的地方,为何,因为阿里云上有AHAS。。。
那么 下篇,来分享下 sentinel 持久化的 三种方式
7.优化错误页面
sentinel默认情况下,无论你是限流还是降级,触发了都会返回
这样返回实在太不友好了,所以要加入自己的配置
@Component
public class QiuqiuUrlBlockHandler implements UrlBlockHandler {
public static final Logger log = LoggerFactory.getLogger(QiuqiuUrlBlockHandler.class);
/**
* 接口中加了feign,则 在 控制台中会既会显示接口 也会也是 feign的调用接口
* 看你 限流哪个 ,限流feign 就走feign的fallback
* 限流原来接口,则走下面的逻辑
* @return
*/
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws IOException {
if(ex instanceof FlowException) {
log.warn("触发了流控");
warrperResponse(response, ErrorEnum.FLOW_RULE_ERR);
}else if(ex instanceof ParamFlowException) {
log.warn("触发了参数流控");
warrperResponse(response,ErrorEnum.HOT_PARAM_FLOW_RULE_ERR);
}else if(ex instanceof AuthorityException) {
log.warn("触发了授权规则");
warrperResponse(response,ErrorEnum.AUTH_RULE_ERR);
}else if(ex instanceof SystemBlockException) {
log.warn("触发了系统规则");
warrperResponse(response,ErrorEnum.SYS_RULE_ERR);
}else{
log.warn("触发了降级规则");
warrperResponse(response,ErrorEnum.DEGRADE_RULE_ERR);
}
}
private void warrperResponse(HttpServletResponse httpServletResponse, ErrorEnum errorEnum) throws IOException {
httpServletResponse.setStatus(500);
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setHeader("Content-Type","application/json;charset=utf-8");
httpServletResponse.setContentType("application/json;charset=utf-8");
ObjectMapper objectMapper = new ObjectMapper();
String errMsg =objectMapper.writeValueAsString(new ErrorResult(errorEnum));
httpServletResponse.getWriter().write(errMsg);
}
}
根据异常类型,自定义返回值返给前台
。
注意:若接口中加了feign,则 在 控制台中会既会显示接口 ,也会显示 feign的调用接口
看你限流哪个 ,若限流feign 就走feign的fallback。 若限流原来接口,则走UrlBlockHandler 逻辑。
ribbon同理。
8.结语
世上无难事,只怕有心人,每天积累一点点,fighting!!!