feign 序列化_Spring Cloud(Feign+Hystrix)微服务之间参数传递

本文介绍了在Spring Cloud微服务架构中,使用Feign进行服务间调用时如何处理参数传递的问题,特别是在Hystrix采用线程隔离策略时的挑战。通过自定义`RequestInterceptor`和`HystrixConcurrencyStrategy`,解决了在高并发下ThreadLocal无法正确获取值的问题,实现了参数的传递。同时,文章提到了在AOP处理中使用`InheritableThreadLocal`和调整注解为`@After`以避免ThreadLocal清理不及时的问题。
摘要由CSDN通过智能技术生成

起因

公司项目开发用的比较早的Spring Cloud全家桶(Zuul、Eureka、Feign、Hystrix、Spring Cloud Config、Spring Boot),新的一个项目开发时候因为涉及一些老的微服务接口改造,增加了较大量的公共参数改造,接口批量修改影响的范围比较大,所以参考调用链的方式,通过实现feign.RequestInterceptor拦截器来修改RequestTemplate header参数的方式将公共参数传递给下一级微服务。

问题

因为Hystrix默认而且官方强烈建议使用Thread隔离级别,所以在RequestInterceptor的拦截器中无法直接获取到当前的Request对象,也就无法将请求中的公共参数进行传递

根据一些资料可以比较出,信号量默认因为不涉及到线程调度,开销小,但硬伤也比较明显:不支持异步请求、无法限制请求超时时间

在高并发、大流量情况下,还是默认的Thread隔离级别相对合适一些

解决方案

修改Hystrix隔离级别为信号量(Semaphore)模式

自定义Hystrix隔离隔离策略1

最终我决定采用第二种方案

踩坑记录

首先,我封装了一个公共的Spring Boot Starter来处理公共参数及参数传递的工作。创建了一些AOP对于所有Controller参数进行拦截,并且放在自定义的ThreadLocal对象中,在请求结束的时候再从ThreadLocal中移除对象,防止内存泄露

然后实现feign.RequestInterceptor拦截器,从ThreadLocal中获取参数,放在RequestTemplate的header传递给下一层。

第一坑

然后就踩到了第一个坑:feign.RequestInterceptor拦截器调试时候都很好,测试时候发现随着请求量的上升,ThreadLocal中经常出现获取不到内容的情况

一轮日志打印+Google后发现,FeignClient调用下级微服务的时候是从线程池中拿到一个新的线程来执行Feign调用,此时无法正确的拿到当前的线程,造成ThreadLocal获取值失败

尝试了阿里开源的TransmittableThreadLocal类后,发现没有像阿里说的那样『在使用线程池等会池化复用线程的执行组件情况下传递ThreadLocal』则是TransmittableThreadLocal目标场景,能解决ThreadLocal获取不到值的情况

第二坑

因为AOP偷懒直接用了@Around注解拦截请求,请求结束后调用ThreadLocal remove方法移除数据,实际因为公司的项目采用了抛出异常的方式来处理返回错误,在出现参数错误等场景的时候,会造成TreadLocal没有正常remove,给排查问题造成了很大困扰

最终解决方案

TreadLocal使用JDK自带的InheritableThreadLocal

AOP调整为@After中统一remove ThreadLocal数据

AOP拦截到参数后,将参数塞进ThreadLocal的同时,通过request.setAttribute方式临时将序列化好的参数保存在当前Request的Attribute中(需注意调用request.setAttribute前最好先request.removeAttribute掉)

feign.RequestInterceptor拦截器获取当前Request对象,从Attribute中获取数据放入RequestTemplate的header中传递给下一级

附:自定义Hystrix隔离隔离策略代码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102

public class RequestAttributeHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {

private static final Log log = LogFactory

.getLog(RequestAttributeHystrixConcurrencyStrategy.class);

private HystrixConcurrencyStrategy delegate;

public RequestAttributeHystrixConcurrencyStrategy() {

log.info("注入 Hystrix 熔断策略");

try {

this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();

if (this.delegate instanceof RequestAttributeHystrixConcurrencyStrategy) {

// Welcome to singleton hell... return;

}

HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins

.getInstance().getCommandExecutionHook();

HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance()

.getEventNotifier();

HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance()

.getMetricsPublisher();

HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()

.getPropertiesStrategy();

this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher,

propertiesStrategy);

HystrixPlugins.reset();

HystrixPlugins.getInstance().registerConcurrencyStrategy(this);

HystrixPlugins.getInstance()

.registerCommandExecutionHook(commandExecutionHook);

HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);

HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);

HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);

} catch (Exception e) {

log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);

}

}

private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,

HystrixMetricsPublisher metricsPublisher,

HystrixPropertiesStrategy propertiesStrategy) {

if (log.isDebugEnabled()) {

log.debug("Current Hystrix plugins configuration is ["

+ "concurrencyStrategy [" + this.delegate + "]," + "eventNotifier ["

+ eventNotifier + "]," + "metricPublisher [" + metricsPublisher + "],"

+ "propertiesStrategy [" + propertiesStrategy + "]," + "]");

log.debug("Registering Sleuth Hystrix Concurrency Strategy.");

}

}

@Override

public Callable wrapCallable(Callable callable) {

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

return new WrappedCallable<>(callable, requestAttributes);

}

@Override

public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,

HystrixProperty corePoolSize,

HystrixProperty maximumPoolSize,

HystrixProperty keepAliveTime, TimeUnit unit,

BlockingQueue workQueue) {

return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize,

keepAliveTime, unit, workQueue);

}

@Override

public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,

HystrixThreadPoolProperties threadPoolProperties) {

return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);

}

@Override

public BlockingQueue getBlockingQueue(int maxQueueSize) {

return this.delegate.getBlockingQueue(maxQueueSize);

}

@Override

public HystrixRequestVariable getRequestVariable(

HystrixRequestVariableLifecycle rv) {

return this.delegate.getRequestVariable(rv);

}

static class WrappedCallable implements Callable {

private final Callable target;

private final RequestAttributes requestAttributes;

public WrappedCallable(Callable target, RequestAttributes requestAttributes) {

this.target = target;

this.requestAttributes = requestAttributes;

}

@Override

public T call() throws Exception {

try {

RequestContextHolder.setRequestAttributes(requestAttributes);

return target.call();

} finally {

RequestContextHolder.resetRequestAttributes();

}

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值