首页 > 其他分享 >Hystrix传播ThreadLocal对象,Feign调用返回null问题

Hystrix传播ThreadLocal对象,Feign调用返回null问题

时间:2023-07-11 23:57:33浏览次数:32  
标签:Feign HystrixPlugins Hystrix requestAttributes getInstance ThreadLocal public

微服务与微服务之间相互调用,你是否有过使用Hystrix时,该传播ThreadLocal对象的困惑?

 

 

我们知道Hystrix有隔离策略:

THREAD(线程池隔离):即:每个实例都增加个线程池进行隔离

SEMAPHORE(信号量隔离):适应非网络请求,因为是同步的请求,无法支持超时,只能依靠协议本身

 

 

 

现在有如下两种隔离机制的场景,先来看下默认隔离机制Thread

用户已登录的情况

通过Feign调用的auth用户登录认证服务 (被调用方)

 调用方

 结果,userinfo服务无法获取到用户登录的信息

 

切换组策略SEMAPHORE,并重启

重新进行请求(可通过ThreadLocal获取)

 

 

 

 

使用Hystrix时,获取ThreadLocal的两种方式

 1、将隔离策略设为SEMAPHORE即可:

hystrix.command.default.execution.isolation.strategy: SEMAPHORE

这样配置后,Feign可以正常工作。

但该方案不是特别好。原因是Hystrix官方强烈建议使用THREAD作为隔离策略!

2、自定义并发策略(博主采用方式)

@Slf4j
@Component
public class RequestAttributeHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
    private HystrixConcurrencyStrategy delegate;
    public RequestAttributeHystrixConcurrencyStrategy() {
        try {
            log.info("加载RequestAttributeHystrixConcurrencyStrategy");
            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 <T> Callable<T> wrapCallable(Callable<T> callable) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        return new WrappedCallable<>(callable, requestAttributes);
    }
    @Override
    public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                                            HystrixProperty<Integer> corePoolSize,
                                            HystrixProperty<Integer> maximumPoolSize,
                                            HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
                                            BlockingQueue<Runnable> 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<Runnable> getBlockingQueue(int maxQueueSize) {
        return this.delegate.getBlockingQueue(maxQueueSize);
    }
    @Override
    public <T> HystrixRequestVariable<T> getRequestVariable(
            HystrixRequestVariableLifecycle<T> rv) {
        return this.delegate.getRequestVariable(rv);
    }
    static class WrappedCallable<T> implements Callable<T> {

        private final Callable<T> target;
        private final RequestAttributes requestAttributes;

        public WrappedCallable(Callable<T> 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();
            }
        }
    }
}
/**
 * 
 * 需要一个或者多个RequestInterceptor去配置诸如请求头信息,还给出了授权的头部配置。
 * 到这里我们就明白了,我们需要实现一个RequestInterceptor,在里面将原来的请求头信息付给下游请求,实际上就是Cookie信息,
 * 这样sessionId就传到下游了,也就实现了共享。
 */

@Configuration
public class FeignRequestIntercepter implements RequestInterceptor {

    @Autowired
    RequestAttributeHystrixConcurrencyStrategy requestAttributeHystrixConcurrencyStrategy;
    @Override
    public void apply(RequestTemplate requestTemplate) {
        /**
         * 使用 RequestContextHolder.getRequestAttributes() 静态方法获得Request。 (但仅限于Feign不开启Hystrix支持时。)
         * 当Feign开启Hystrix支持时,获取值为null
         * 原因在于,Hystrix的默认隔离策略是THREAD 。而 RequestContextHolder 源码中,使用了两个ThreadLocal 。
         * 解决方案一:调整隔离策略 将隔离策略设为SEMAPHORE即可
         * hystrix.command.default.execution.isolation.strategy: SEMAPHORE
         * 这样配置后,Feign可以正常工作。但该方案不是特别好。原因是Hystrix官方强烈建议使用THREAD作为隔离策略!
         *
         * 解决方案二:自定义并发策略
         * 既然Hystrix不太建议使用SEMAPHORE作为隔离策略,那么是否有其他方案呢?
         * 答案是自定义并发策略,目前,Spring Cloud Sleuth以及Spring Security都通过该方式传递 ThreadLocal 对象。
         * 编写自定义并发策略比较简单,只需编写一个类,让其继承HystrixConcurrencyStrategy ,并重写wrapCallable 方法即可。
         *
         */
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null){
            System.out.println("requestAttributes为null");
            return;
        }
        //获取本地线程绑定的请求对象
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        System.out.println("获取本地线程绑定的请求对象:"+request);
        //给请求模板附加本地线程头部信息,主要是cookie信息
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()){
            String name =(String) headerNames.nextElement();
            System.out.println("name:"+name);
            requestTemplate.header(name,request.getHeader(name));
        }
    }
}

 

 

Feign远程调用返回null问题解决 1、首先确保下游服务接收的参数,从上游正常传入 2、添加有关Feign的相关注解  @EnableFeignClients @FeignClient 3、下游若需要返回对象,不要用Object进行返回,将Object改为对象(如UserModel)

 

 

至此,以上问题均得到了解决。(可以请博主喝杯咖啡喔,有需要博主解决的问题,欢迎评论区留言),对了,博主是全能型的喔  

 

 

 

 

   

标签:Feign,HystrixPlugins,Hystrix,requestAttributes,getInstance,ThreadLocal,public
From: https://www.cnblogs.com/cyang-vip/p/17546090.html

相关文章

  • Feign调用注册中心外的服务
    参考博客:https://blog.csdn.net/weixin_43612925/article/details/122923759https://blog.csdn.net/weixin_42825651/article/details/1259961651、@FeignClient()注解的使用1.服务的启动类要有@EnableFeignClients注解才能启用feign客户端2.FeignClient注解被@Target(El......
  • Python 实现 ThreadLocal
    importthreadingfromthreadingimportget_identimporttimeclassContext:def__init__(self):object.__setattr__(self,'__global_context__',dict())object.__setattr__(self,'__get_ident__',get_ident)def......
  • ThreadLocal
    ThreadLocal慕课网教程多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的一种保证一种规避多线程访......
  • ThreadLocal源码
    使用场景ThreadLocal用来提供线程局部变量。每个线程都会有一份独立的副本,副本之间不存在竞争关系,是线程专属的内存空间。例如:publicclassThreadLocalTest{privatestaticfinalThreadLocal<Integer>threadLocal=newThreadLocal<>();publicstaticvoidma......
  • feign 微服务调用,post请求如何在URL 后面带参数
    ​ 在Feign微服务调用中,可以通过在URL后面添加参数来进行POST请求。参数可以以两种方式传递:作为路径参数或查询参数。 路径参数:可以将参数添加到URL的路径中,并使用@PathVariable注解来获取参数的值。例如:@FeignClient(name="example-service")publicinterfaceExample......
  • 记一次扯dan的错误feign.FeignException$NotFound: status 404 reading UserFeign#fin
    feign.FeignException$NotFound:status404readingUserFeign#findByPage()atfeign.FeignException.clientErrorStatus(FeignException.java:165)~[feign-core-10.4.0.jar:na]atfeign.FeignException.errorStatus(FeignException.java:141)~[feign-core-10.4.0.jar:na......
  • hystrix的简单使用
    hystrix是微服务中的一个容错保护组件,用来对调用方请求另一服务时的超时,异常的降级保护。一。.全局配置。在项目中我们不会单独使用hystrix,一般是利用Feign对hystrix的封装。1.开启feign对于hystrix的支持feign.hystrix.enabled=true2.全局配置对feign客户端接口编写......
  • springcloud -hystrix服务熔断机制
    服务熔断:就是在错误率达到规定百分比的时候会开启,然后隔断消费者和服务端,在不断访问提升正确率后将其关闭,回复调用链路servicehystrix-payment-order8001增加方法  //=========服务熔断 @HystrixCommand(fallbackMethod="paymentCircuitBreaker_fallback",commandPro......
  • springcloud- hystrix服务降级简单讲解
    在出现错误的时候我们需要即使进行处理并返回提示信息给用户实现交互友好化,我们使用一下注解来实现服务降级功能,一般我们将服务降级配置在客户端相关注解provider8001 ​ @EnableCircuitBreaker //服务降级激活注解服务端主启动类 @HystrixCommand(fallbackMethod=......
  • springcloud - openFeign的简单配置和使用
    openFeign第一步:导入依赖     <dependency>       <groupId>org.springframework.cloud</groupId>       <artifactId>spring-cloud-starter-openfeign</artifactId>     </dependency>第一步:进行配置 server: port:8......