前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微服务全链路跟踪:jaeger集成hystrix

微服务全链路跟踪:jaeger集成hystrix

作者头像
一笠风雨任生平
发布2020-05-04 14:31:26
8700
发布2020-05-04 14:31:26
举报
文章被收录于专栏:服务化进程服务化进程

微服务全链路跟踪:grpc集成zipkin

微服务全链路跟踪:grpc集成jaeger

微服务全链路跟踪:springcloud集成jaeger

微服务全链路跟踪:jaeger集成istio,并兼容uber-trace-id与b3

微服务全链路跟踪:jaeger集成hystrix

背景

当springcloud服务集成hystrix,并且用了hystrixCommend注解到方法上时,jaeger链路会断掉

方案

在网上搜索到了大量jaeger遇到多线程时的处理方式,都是包装线程池来做到ThreadLocal传递,有很多都用到了阿里开源的transmittable-thread-local

下面说一下当集成hystrix时,jaeger链路丢失问题,大家都知道hystrix默认是线程池隔离,所以归根结底还是遇到多线程线程变量没有共享的问题,网上也罗列了几种方案:

方案一:变更隔离方式
hystrix.command.default.execution.isolation.strategy: SEMAPHORE

当并发高时这里设置信号量隔离是有风险的,可以根据情况优化断路器配置来降低风险

方案二:自定义隔离策略

隔离策略官方文档有定义:

原先我就定义了一个feign传递request中header信息的策略,在原有的隔离策略下面参考https://github.com/opentracing-contrib/java-concurrent项目的代码:

将原有feign自定义隔离策略做了响应变动,代码如下

/**
 * 自定义Feign的隔离策略:
 *   在转发Feign的请求头的时候, 如果开启了Hystrix, 
 *      Hystrix的默认隔离策略是Thread(线程隔离策略), 因此转发拦截器内是无法获取到请求的请求头信息的, 
 *      可以修改默认隔离策略为信号量模式:hystrix.command.default.execution.isolation.strategy=SEMAPHORE, 
 *      这样的话转发线程和请求线程实际上是一个线程, 这并不是最好的解决方法, 信号量模式也不是官方最为推荐的隔离策略;
 *   另一个解决方法就是自定义Hystrix的隔离策略:
 *      思路是将现有的并发策略作为新并发策略的成员变量,在新并发策略中, 
 *      返回现有并发策略的线程池、Queue;将策略加到Spring容器即可;
 */
@Component
@Slf4j
public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
 
    private HystrixConcurrencyStrategy delegate;
    @Lazy
    @Autowired
    private Tracer tracer;
 
    public FeignHystrixConcurrencyStrategy() {
        try {
            this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
            if (this.delegate instanceof FeignHystrixConcurrencyStrategy) {
                // 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 instance = HystrixPlugins.getInstance();
            instance.registerConcurrencyStrategy(this);
            instance.registerCommandExecutionHook(commandExecutionHook);
            instance.registerEventNotifier(eventNotifier);
            instance.registerMetricsPublisher(metricsPublisher);
            instance.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&lt;&gt;(callable, requestAttributes,tracer,tracer.activeSpan());
    }
 
    @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;
        private final Span span;
        private final Tracer tracer;
 
        public WrappedCallable(Callable<t> target, RequestAttributes requestAttributes,Tracer tracer, Span span) {
            this.target = target;
            this.requestAttributes = requestAttributes;
            this.tracer=tracer;
            this.span=span;
        }
 
        @Override
        public T call() throws Exception {
            Scope scope = span == null ? null : tracer.scopeManager().activate(span);
            try {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                return target.call();
            } finally {
                RequestContextHolder.resetRequestAttributes();
                if (scope != null) {
                    scope.close();
                }
            }
        }
    }
}

其中改动部分就是在原有的WrappedCallable中增加了span、trace的注入。

至于自定义隔离策略以及Callable是可以支持多个链的,这里不做详细描述,大家有兴趣可以参考,下面的链接:

https://blog.csdn.net/songhaifengshuaige/article/details/80345012

https://www.jianshu.com/p/c60fe209a799

https://www.cnblogs.com/duanxz/p/10949816.html

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 方案
    • 方案一:变更隔离方式
      • 方案二:自定义隔离策略
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档