Springcloud-alibaba集成sentinel(ribbon+openFeign+优化错误页面)

Springcloud-alibaba集成sentinel(ribbon+openFeign+优化错误页面)

每天多学一点点~
话不多说,这就开始吧…

1.前言

不得不说,Hystrix再sentinel面前就是个弟弟~

sentinel git地址

2.下载启动

  1. dashboard下载
    我这里选择1.7.0 版本

在这里插入图片描述

  1. 启动
    java -jar sentinel-dashboard-1.7.0.jar --server.port=9999 启动(就是一个Springboot工程)
    在这里插入图片描述

默认账号密码是 sentinel

3.与springcloud整合

其实与这些第三方组件整合,无非就是三板斧

  1. 加依赖
			<!--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>
  1. 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上自己去看看,这里就不演示了

sentinel git地址

5.与ribbon整合

  1. 加依赖
		<!--加入ribbon-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
		</dependency>
  1. yml配置
spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:9999
#是否开启@SentinelRestTemplate注解
resttemplate:
  sentinel:
    enabled: true
  1. 加注解
    (在 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整合了
在这里插入图片描述

  1. 加依赖
<!--加入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>
  1. 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 才会生效

  1. 在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!!!


版权声明:本文为weixin_42437633原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。