sentinel整合feign
文章目录
- 前言
- 一、未处理的时候返回
- 二、操作步骤
- 1.引入库
- 2.编写代码
- 服务提供者:
- 服务消费者:
- 3.效果
- 三、处理思路
- ①: 第一步
- ②: 第二步
- ③: 第三步
- ④: 第四步
- ⑤: 第五步
前言
- sentinel整合feign, 主要处理限流熔断等异常的处理
- 主要实现能够正常判断出是限流还是熔断等导致
- Spring Cloud Alibaba: 2.2.1.RELEASE
- sentinel: 1.7.1
- 以及总结了一下我的处理思路
一、未处理的时候返回
二、操作步骤
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相关推荐
- 笔记:sentinel整合feign报org.springframework.beans.factory.UnsatisfiedDependencyException问题
开始使用的是Hoxton.SR1,整合sentinel报org.springframework.beans.factory.UnsatisfiedDependencyException 去网上查了一下 ...
- sentinel接入网关应用_阿里Sentinel整合Zuul网关详解
前面我们讲解了Sentinel整合Spring Cloud Gateway,详细请查看文章:阿里Sentinel支持Spring Cloud Gateway啦 目前来说,大部分公司线上的网关应该是Zu ...
- Sentinel整合Dubbo限流实战
Sentinel整合Dubbo限流实战 创建provider项目 添加jar依赖 <dependency><artifactId>sentinel-api</artifa ...
- sentinel 整合dubbo限流
sentinel 整合dubbo限流 相关依赖 <!-- 服务注册与发现 --><dependency><groupId>com.alibaba.cloud< ...
- Spring Cloud【Finchley】-06服务消费者整合Feign
文章目录 概述 实例 新建工程 增加maven依赖 创建一个Feign接口,并添加@FeignClient注解 修改Controller层,将RestTemplate改为调用Feign接口 启动类增加 ...
- 5.Spring Cloud Alibaba教程:Nacos整合Feign
概述 Feign是一个声明式的http客户端.使用Feign只需要创建接口并加上对应的注解,就可以实现类似RestTemplate方式的调用,只是它将底层的http请求代码隐藏起来.另外,Feign默 ...
- 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 ...
- Spring Cloud Alibaba 实战 | 第十二篇: 微服务整合Sentinel的流控、熔断降级,赋能拥有降级功能的Feign新技能熔断,实现熔断降级双剑合璧(JMeter模拟测试)
文章目录 一. Sentinel概念 1. 什么是Sentinel? 2. Sentinel功能特性 3. Sentinel VS Hystrix 二. Docker部署Sentinel Dashbo ...
- Feign的构建过程及自定义扩展功能
spring-cloud-openfeign-core-2.1.1.RELEASE.jar 中 HystrixFeign 的详细构建过程: @EnableFeignClients -> Feig ...
最新文章
- 数据结构与算法:算法简介
- centos创建vsftpd虚拟用户
- Java 10.switch语句
- App乱世,3721离我们有多远
- free mybatis 不生效_关于 Mybatis 设置懒加载无效的问题
- java将HTML文件转化为pdf文件,如何使用Java将HTML网页转换为PDF文件
- 神舟笔记本风扇控制软件_十代酷睿+RTX2070加持 高性能游戏本首选神舟战神G9CU7PK...
- 【clickhouse】clickhouse 表引擎之 null
- 玩转python网络爬虫-清华大学出版社-图书详情-《玩转Python网络爬虫》
- python textrank_TextRank算法提取文本摘要
- TypeScript接口用法(基础)
- 解决【npm ERR! Unexpected end of JSON input while parsing near '...sh_time:141072930277'】方案...
- Python路飞学城老男孩内部书籍,Python全栈开发实战pdf
- linux进程管理工具:supervisor
- 游戏模型提取工具NinjaRipper
- ERROR: sdl2 requested but not found
- FindWithTag用法
- 音频怎么转文字?学会这3招,轻松拉满你的工作效率
- 【名企秋招】360公司2017年秋季校招开始喽~ 立即报名
- Numbers for Mac(电子表格制作)