文章目录

  • 前言
  • 一、未处理的时候返回
  • 二、操作步骤
    • 1.引入库
    • 2.编写代码
      • 服务提供者:
      • 服务消费者:
    • 3.效果
  • 三、处理思路
    • ①: 第一步
    • ②: 第二步
    • ③: 第三步
    • ④: 第四步
    • ⑤: 第五步

前言

  1. sentinel整合feign, 主要处理限流熔断等异常的处理
  2. 主要实现能够正常判断出是限流还是熔断等导致
  3. Spring Cloud Alibaba: 2.2.1.RELEASE
  4. sentinel: 1.7.1
  5. 以及总结了一下我的处理思路

一、未处理的时候返回

二、操作步骤

1.引入库

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.编写代码

服务提供者:

package com.chaim.common.sentinel.provider;import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
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 com.alibaba.fastjson.JSONObject;
import com.chaim.common.core.util.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import static com.chaim.common.core.constant.HttpStatus.SentinelConstants.*;/*** 由于:* 服务提供者在经过sentinel限流等操作后, 返回数据不符合我们要求, 同时消费者无法区分是限流还是熔断等操作(FeignException.errorStatus)* 故:* 实现BlockExceptionHandler, 对返回数据进行重写, 从而符合我们的要求* 注:* 返回状态码需为 STATUS, 否者消费者那边不会进行处理** @author Chaim* @date 2021/11/4 15:11*/
@Component
@Slf4j
public class MyBlockExceptionHandler implements BlockExceptionHandler {# 这个只是我这边的定义public static final int STATUS = 606;@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException ex) throws Exception {CommonResult commonResult = CommonResult.fail(DATA_LOGIC_FAIL, "FEIGN_SENTINEL_FAIL");if (ex instanceof FlowException) {log.debug("限流: ", ex);commonResult = CommonResult.fail(DATA_CURRENT_LIMITING_FAIL, "请稍后再试!");} else if (ex instanceof DegradeException) {log.debug("降级", ex);commonResult = CommonResult.fail(DATA_RELEGATION_FAIL, "请稍后再试!");} else if (ex instanceof ParamFlowException) {log.debug("热点参数限流", ex);commonResult = CommonResult.fail(HOTSPOT_PARAMETER_FAIL, "请稍后再试!");} else if (ex instanceof SystemBlockException) {log.debug("系统规则(负载/...不满足要求)", ex);commonResult = CommonResult.fail(SYSTEM_RULES_FAIL, "请稍后再试!");} else if (ex instanceof AuthorityException) {log.debug("授权规则不通过", ex);commonResult = CommonResult.fail(AUTHORIZATION_RULES_FAIL, "请稍后再试!");}// http状态码response.setStatus(STATUS);response.setCharacterEncoding("utf-8");response.setHeader("Content-Type", "application/json;charset=utf-8");response.setContentType("application/json;charset=utf-8");response.getWriter().write(JSONObject.toJSONString(commonResult));}
}

服务消费者:

feign:# 为feign整合sentinelsentinel:enabled: true
@FeignClient(name = "test", fallbackFactory = TestServiceFallbackFactory.class)
public interface TestFeignClient extends TestService {}
package com.chaim.feign.ali.fallback.factory;import com.alibaba.fastjson.JSONObject;
import com.chaim.common.core.util.CommonResult;
import com.chaim.common.sentinel.consumer.FallbackFactoryFlowException;
import com.chaim.feign.ali.POJO.DTO.*;
import com.chaim.feign.ali.service.TestService;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import static com.chaim.common.core.constant.HttpStatus.SentinelConstants.DATA_LOGIC_FAIL;/*** 指定feign的FallbackFactory, 对调用失败的接口, 进行逻辑处理** @author Chaim* @date 2021/11/3 14:46*/
@Component
@Slf4j
public class TestServiceFallbackFactory implements FallbackFactory<TestService> {@Overridepublic TestService create(Throwable throwable) {return new TestService() {@Overridepublic CommonResult test(TestScanPayDTO testScanPayDTO) {CommonResult commonResult = CommonResult.fail(DATA_LOGIC_FAIL, "FEIGN_SENTINEL_FAIL");try {String message = throwable.getMessage();log.debug("sentinel 限流熔断等: {}", message);JSONObject jsonObject = JSONObject.parseObject(message);JSONObject body = jsonObject.getJSONObject("body");return CommonResult.fail(body.getInteger("code"), body.getString("message"));} catch (Exception e) {log.error("异常_FallbackFactoryFlowException: ", e);return commonResult;}}};}
}
package com.chaim.common.sentinel.consumer;import com.alibaba.fastjson.JSONObject;
import com.chaim.common.sentinel.provider.MyBlockExceptionHandler;
import feign.*;
import feign.codec.ErrorDecoder;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;import static feign.Util.RETRY_AFTER;
import static feign.Util.checkNotNull;
import static java.util.Locale.US;
import static java.util.concurrent.TimeUnit.SECONDS;/*** 由于:* 当服务提供者通过sentinel进行限流等操作, 消费者这边无法捕获到对应的异常* 故:* 实现 ErrorDecoder, 对服务提供者返回的异常进行处理* 注:* 只会处理sentinel限制后的操作, 同时 response status == MyBlockExceptionHandler.STATUS, 才进行处理, 其余依旧通过sentinel进行处理** @author Chaim* @date 2021/11/5 12:20*/
@Component
public class MyErrorDecoder implements ErrorDecoder {private final RetryAfterDecoder retryAfterDecoder = new RetryAfterDecoder();@Overridepublic Exception decode(String methodKey, Response response) {FeignException exception = errorStatus(methodKey, response);Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER));if (retryAfter != null) {return new RetryableException(response.status(),exception.getMessage(),response.request().httpMethod(),exception,retryAfter,response.request());}return exception;}public static FeignException errorStatus(String methodKey, Response response) {byte[] body = {};try {if (response.body() != null) {body = Util.toByteArray(response.body().asInputStream());}} catch (IOException ignored) { // NOPMD}Map<String, Object> map = new HashMap<>();map.put("status", response.status());if (response.reason() != null) {map.put("reason", response.reason());}map.put("method", response.request().httpMethod());map.put("url", response.request().url());map.put("methodKey", methodKey);map.put("body", new String(body));// 只对返回码为606进行处理if (response.status() == MyBlockExceptionHandler.STATUS) {return new FeignException.FeignServerException(response.status(), JSONObject.toJSONString(map), response.request(), body);}return FeignException.errorStatus(methodKey, response);}private <T> T firstOrNull(Map<String, Collection<T>> map, String key) {if (map.containsKey(key) && !map.get(key).isEmpty()) {return map.get(key).iterator().next();}return null;}/*** 由于ErrorDecoder中的方法无法直接使用, 故复制一份出来*/static class RetryAfterDecoder {static final DateFormat RFC822_FORMAT =new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", US);private final DateFormat rfc822Format;RetryAfterDecoder() {this(RFC822_FORMAT);}RetryAfterDecoder(DateFormat rfc822Format) {this.rfc822Format = checkNotNull(rfc822Format, "rfc822Format");}protected long currentTimeMillis() {return System.currentTimeMillis();}/*** returns a date that corresponds to the first time a request can be retried.** @param retryAfter String in*                   <a href="https://tools.ietf.org/html/rfc2616#section-14.37" >Retry-After format</a>*/public Date apply(String retryAfter) {if (retryAfter == null) {return null;}if (retryAfter.matches("^[0-9]+\\.?0*$")) {retryAfter = retryAfter.replaceAll("\\.0*$", "");long deltaMillis = SECONDS.toMillis(Long.parseLong(retryAfter));return new Date(currentTimeMillis() + deltaMillis);}synchronized (rfc822Format) {try {return rfc822Format.parse(retryAfter);} catch (ParseException ignored) {return null;}}}}}

3.效果


三、处理思路

①: 第一步

首先分析一下, 按我的流程分析: sentinel限流是对服务提供者做的操作
那么限流之后肯定是有一个返回的, 返回的结果是什么了?
postman直接请求服务提供者, 返回状态码429, 因为sentinel是对提供者做的限流

然后我们在通过消费者来调用提供者:

对应的源码是在: feign-core: feign.SynchronousMethodHandler.executeAndDecode, (正常处理: response.status() >= 200 && response.status() < 300)


②: 第二步

通过上面的我们可以确定了, 只要处理提供者的返回状态码, 就可以解决问题了!
于是就有了:

// 以前的版本好像是 UrlBlockHandler
public class MyBlockExceptionHandler implements BlockExceptionHandler

到这一步, 其实我们只需要将response.setStatus(200) 设置成200, 就到此结束!


③: 第三步

但是我们可能想, 限流了就是限流了嘛, 标个200不符合我的出发思路, 继续处理
这是看下消费者的日志:


服务提供者返回的body已经符合了我的想法.

程序到这一步, 就由feign接管处理, 看了一下源码feign是没有处理sentinel返回的异常类型(feign是根据状态码进行的处理)

对应的处理源码: feign-core: feign.FeignException.errorStatus


④: 第四步

这里就得说一下
FallbackFactory

public class TestServiceFallbackFactory implements FallbackFactory<TestService>

这里能拿到Throwable
但是就目前的Throwable也不符合我们的要求:

feign.FeignException: [606] during [POST] to [http://***/pay/scan] [TestFeignClient#123(TestPayDTO)]: [{"code":5504,"message":"请稍后再试!"}]

⑤: 第五步

于是就有了:

public class MyErrorDecoder implements ErrorDecoder

那我就从写一下throwableMessage 让符合我的要求
但是我只想处理sentinel我定义的状态为606的
于是就有了:

if (response.status() == MyBlockExceptionHandler.STATUS) {return new FeignException.FeignServerException(response.status(), JSONObject.toJSONString(map), response.request(), body);
}
// 不是我定义的606, 还是走 FeignException.errorStatus
return FeignException.errorStatus(methodKey, response);

后面只需要对应处理 throwable.getMessage() 即可!

sentinel整合feign相关推荐

  1. 笔记:sentinel整合feign报org.springframework.beans.factory.UnsatisfiedDependencyException问题

    开始使用的是Hoxton.SR1,整合sentinel报org.springframework.beans.factory.UnsatisfiedDependencyException 去网上查了一下 ...

  2. sentinel接入网关应用_阿里Sentinel整合Zuul网关详解

    前面我们讲解了Sentinel整合Spring Cloud Gateway,详细请查看文章:阿里Sentinel支持Spring Cloud Gateway啦 目前来说,大部分公司线上的网关应该是Zu ...

  3. Sentinel整合Dubbo限流实战

    Sentinel整合Dubbo限流实战 创建provider项目 添加jar依赖 <dependency><artifactId>sentinel-api</artifa ...

  4. sentinel 整合dubbo限流

    sentinel 整合dubbo限流 相关依赖 <!-- 服务注册与发现 --><dependency><groupId>com.alibaba.cloud< ...

  5. Spring Cloud【Finchley】-06服务消费者整合Feign

    文章目录 概述 实例 新建工程 增加maven依赖 创建一个Feign接口,并添加@FeignClient注解 修改Controller层,将RestTemplate改为调用Feign接口 启动类增加 ...

  6. 5.Spring Cloud Alibaba教程:Nacos整合Feign

    概述 Feign是一个声明式的http客户端.使用Feign只需要创建接口并加上对应的注解,就可以实现类似RestTemplate方式的调用,只是它将底层的http请求代码隐藏起来.另外,Feign默 ...

  7. spring cloud整合feign和nacos报错:No Feign Client for loadBalancing defined. Did you forget to include

    Did you forget to include spring-cloud-starter-loadbalancer 问题描述 项目环境 解决方案 1.引入eureka依赖--无效 2.降低spri ...

  8. Spring Cloud Alibaba 实战 | 第十二篇: 微服务整合Sentinel的流控、熔断降级,赋能拥有降级功能的Feign新技能熔断,实现熔断降级双剑合璧(JMeter模拟测试)

    文章目录 一. Sentinel概念 1. 什么是Sentinel? 2. Sentinel功能特性 3. Sentinel VS Hystrix 二. Docker部署Sentinel Dashbo ...

  9. Feign的构建过程及自定义扩展功能

    spring-cloud-openfeign-core-2.1.1.RELEASE.jar 中 HystrixFeign 的详细构建过程: @EnableFeignClients -> Feig ...

最新文章

  1. 数据结构与算法:算法简介
  2. centos创建vsftpd虚拟用户
  3. Java 10.switch语句
  4. App乱世,3721离我们有多远
  5. free mybatis 不生效_关于 Mybatis 设置懒加载无效的问题
  6. java将HTML文件转化为pdf文件,如何使用Java将HTML网页转换为PDF文件
  7. 神舟笔记本风扇控制软件_十代酷睿+RTX2070加持 高性能游戏本首选神舟战神G9CU7PK...
  8. 【clickhouse】clickhouse 表引擎之 null
  9. 玩转python网络爬虫-清华大学出版社-图书详情-《玩转Python网络爬虫》
  10. python textrank_TextRank算法提取文本摘要
  11. TypeScript接口用法(基础)
  12. 解决【npm ERR! Unexpected end of JSON input while parsing near '...sh_time:141072930277'】方案...
  13. Python路飞学城老男孩内部书籍,Python全栈开发实战pdf
  14. linux进程管理工具:supervisor
  15. 游戏模型提取工具NinjaRipper
  16. ERROR: sdl2 requested but not found
  17. FindWithTag用法
  18. 音频怎么转文字?学会这3招,轻松拉满你的工作效率
  19. 【名企秋招】360公司2017年秋季校招开始喽~ 立即报名
  20. Numbers for Mac(电子表格制作)

热门文章

  1. APS计划排产在金属加工行业的应用
  2. 开发者不骗开发者,你跟我说这只要100块?
  3. 前端写作能力提升之词句
  4. [转] 写给未来的程序媛 ----- 加油,姑娘~
  5. 与孩子一起学编程代码_这周与您的孩子一起做一个代码小时
  6. 砼匠LED显示屏排队配置文件-LED.ini 屏大小:288-176,生产线横显示
  7. 公司这套架构统一处理 try...catch 真香!
  8. [销售易] 报价单,结算单最高限价
  9. ViewStub的使用
  10. 向量点乘和叉乘的区别