[源码分析] OpenTracing之跟踪Redis

0x00 摘要

本文将通过研究OpenTracing的Redis埋点插件来进一步深入理解OpenTracing。

0x01 总体逻辑

1.1 相关概念

Tracer是用来管理Span的统筹者,负责创建span和传播span。它表示从头到尾的一个请求的调用链,是一次完整的跟踪,从请求到服务器开始,服务器返回response结束,跟踪每次rpc调用的耗时。它的标识符是“traceID”。

Span是一个更小的单位,表示一个RPC调用过程。一个“trace”包含有许多跨度(span),每个跨度捕获调用链内的一个工作单元(系统或服务节点),并由“spanId”标识。 每个跨度具有一个父跨度,并且一个“trace”的所有跨度形成有向无环图(DAG)。

1.2 埋点插件

对一个应用的跟踪要关注的无非就是 客户端——>web 层——>rpc 服务——>dao 后端存储、cache 缓存、消息队列 mq 等这些基础组件。OpenTracing 插件的作用实际上也就是对不同组件进行埋点,以便基于这些组件采集应用的链路数据。

不同组件有不同的应用场景和扩展点,因此针对不同的框架,需要开发对应的OpenTracing API 插件用来实现自动埋点。

对于Redis来说各种插件更是层出不穷,所以OpenTracing 对与 Redis 各种插件也做了不同处理,比如 Jedis,Redisson,Spring Data Redis 2.x。本文主要是以 Redisson 为例说明,最后用spring-cloud-redis进行补充对照**。

1.3 总体逻辑

总体思路是使用代理模式。因为 Redis 并没有提供像 Servlet 那样的过滤器或者拦截器,所以 Redis OpenTracing 插件没有进行常规埋点,而是通过组合的方式自定义若干代理类,比如 TracingRedissonClient 和 TracingRList .....。

  • TracingRedissonClient 代理了 Redis Client。
  • TracingRList 代理了Redis List 数据结构。
  • 还有其他类代理其他Redis数据结构,比如TracingRMap。

这些代理类将具体完成Tracing 功能。比如代理类 TracingRedissonClient 包含了两个成员变量:

  • private final RedissonClient redissonClient; 是真正的 Redis Client。
  • private final TracingRedissonHelper tracingRedissonHelper; 是具体针对 Redission 的Tracing 功能类,比如构建Span。

最后各种代理对 Redis 进行拦截:

  • 在执行具体的连接操作之前创建相关的 Span。
  • 在操作结束之后结束 Span,并进行上报。

具体可以见下图

  1. +--------------------------+ +-------------------------+ +-------------------------+
  2. | TracingRedissonClient | | TracingRMap | | TracingRList |
  3. | +----------------------+ | | +---------------------+ | | +---------------------+ |
  4. | | RedissonClient | | | | RMap | | | | RList | | ....
  5. | | | | | | | | | | | |
  6. | | TracingRedissonHelper| | | |TracingRedissonHelper| | | |TracingRedissonHelper| |
  7. | +----------------------+ | | +---------------------+ | | +---------------------+ |
  8. +--------------------------+ +-------------------------+ +-------------------------+
  9. | | |
  10. | | |
  11. | | |
  12. | | |
  13. | v |
  14. | +---------------+-----------------+ |
  15. +-----------> | TracingRedissonHelper | <--------+
  16. | +-----------------------------+ |
  17. | | Tracer +-----+
  18. | +-----------------------------+ | |
  19. +---------------------------------+ |
  20. |
  21. +---------------------------------+ |
  22. | TracingConfiguration | |
  23. | +----------------------------+ | |
  24. | | Tracer <-------+
  25. | +----------------------------| |
  26. +---------------------------------+

下图是为了手机观看。

0x02 示例代码

我们使用代码自带的test来做说明。我们可以看到有两个代理类 TracingRedissonClientTracingRList

  • beforeClass 起到了系统启动的作用。

    • 首先定义了一个tracer(这里是MockTracer,真正使用时候会用到其他Tracer)。
    • 然后使用这个Tracer来构建一个代理类 TracingRedissonClient
  • 后续各种测试操作都是使用这个client在进行Redis操作。
    • 会通过代理类 TracingRedissonClient 得到一个 org.redisson.api.RList 以备后续操作。这个 RList 实际是OpenTracing 进行修改的另一个代理类 TracingRList
    • 会对这个 TracingRList 进行操作 :list.add("key");
    • 针对 Redisson 的异步操作,也进行了操作测试。

具体代码如下:

  1. public class TracingRedissonTest {
  2. private static final MockTracer tracer = new MockTracer();
  3. private static RedisServer redisServer;
  4. private static RedissonClient client;
  5. @BeforeClass
  6. public static void beforeClass() {
  7. redisServer = RedisServer.builder().setting("bind 127.0.0.1").build();
  8. redisServer.start();
  9. Config config = new Config();
  10. config.useSingleServer().setAddress("redis://127.0.0.1:6379");
  11. client = new TracingRedissonClient(Redisson.create(config),
  12. new TracingConfiguration.Builder(tracer).build());
  13. }
  14. @Test
  15. public void test_list() {
  16. RList<Object> list = client.getList("list");
  17. list.add("key");
  18. assertTrue(list.contains("key"));
  19. List<MockSpan> spans = tracer.finishedSpans();
  20. assertEquals(2, spans.size());
  21. checkSpans(spans);
  22. assertNull(tracer.activeSpan());
  23. }
  24. @Test
  25. public void test_config_span_name() throws Exception {
  26. ......
  27. final MockSpan parent = tracer.buildSpan("test").start();
  28. try (Scope ignore = tracer.activateSpan(parent)) {
  29. RMap<String, String> map = customClient.getMap("map_config_span_name");
  30. map.getAsync("key").get(15, TimeUnit.SECONDS);
  31. }
  32. parent.finish();
  33. ......
  34. }
  35. }

0x03 Redis代理

前面我们提到了对于Redis是使用了代理来完成功能,下面我们具体来讲解。

3.1 Client 代理类

TracingRedissonClient 实现了 Redis Client 代理功能,其包含两个成员变量。

  • RedissonClient redissonClient; 是真正的Redis Client,代理类最终是通过此Client进行Redis操作。
  • TracingRedissonHelper tracingRedissonHelper; 完成了Tracing 功能。

具体在使用中,比如在测试代码会通过 Client 代理类得到一个 TracingRList 以备后续操作(这是另一个代理类)。

  • TracingRList 实现了 org.redisson.api.RList 接口。
  • 在构建 TracingRList 会把 TracingRedissonHelper 作为参数传递进去。
  1. RList<Object> list = client.getList("list");

具体代码如下:

  1. public class TracingRedissonClient implements RedissonClient {
  2. private final RedissonClient redissonClient;
  3. private final TracingRedissonHelper tracingRedissonHelper;
  4. public TracingRedissonClient(RedissonClient redissonClient, TracingConfiguration configuration) {
  5. this.redissonClient = redissonClient;
  6. this.tracingRedissonHelper = new TracingRedissonHelper(configuration);
  7. }
  8. @Override
  9. public <V> RList<V> getList(String name) {
  10. // 通过代理生成
  11. return new TracingRList<>(redissonClient.getList(name), tracingRedissonHelper);
  12. }
  13. // 其他操作
  14. ......
  15. }

3.2 List 代理类

TracingRList 是Redis List代理类(Redis插件还有其他代理类,代理其他Redis数据结构)。里面也是两个变量:

  • RList
  • TracingRedissonHelper 完成 Tracing 功能。

在具体 add 函数中:

  • 在执行具体的命令前先通过 tracingRedissonHelper.buildSpan 构建 Span 进行埋点操作。
  • 然后添加 Tag。
  • 最后通过 tracingRedissonHelper.decorate 进行实际 Redis 操作。

具体代码如下:

  1. public class TracingRList<V> extends TracingRExpirable implements RList<V> {
  2. private final RList<V> list;
  3. private final TracingRedissonHelper tracingRedissonHelper;
  4. @Override
  5. public boolean add(V element) {
  6. Span span = tracingRedissonHelper.buildSpan("add", list);
  7. span.setTag("element", nullable(element));
  8. return tracingRedissonHelper.decorate(span, () -> list.add(element));
  9. }
  10. // 其他操作
  11. .....
  12. }

0x04 Tracing功能类

前面一直在提TracingRedissonHelper是Tracing功能类,下面我们就深入研究下 tracingRedissonHelper.decorate(span, () -> list.add(element)); 做了什么。

4.1 配置类

在初始化 Redis Client时候,生成了 TracingConfiguration。

  1. client = new TracingRedissonClient(Redisson.create(config),
  2. new TracingConfiguration.Builder(tracer).build());

TracingConfiguration 之中就定义了io.opentracing.Tracer以及其他配置项。

具体类定义如下:

  1. public class TracingConfiguration {
  2. static final int DEFAULT_KEYS_MAX_LENGTH = 100;
  3. private final Tracer tracer;
  4. private final boolean traceWithActiveSpanOnly;
  5. private final int keysMaxLength;
  6. private final Function<String, String> spanNameProvider;
  7. private final Map<String, String> extensionTags;
  8. // 其他操作
  9. ......
  10. }

4.2 Tracing基础功能类

io.opentracing.contrib.redis.common.TracingHelperOpenTracing 通用的 Redis Tracing 功能类,我们看到里面有 Tracer 变量(就是TracingConfiguration之中的Tracer),也有 SpanBuilder 这样的helper 函数。

业务逻辑具体在 decorate 函数中有体现。参数 Supplier

  1. return tracingRedissonHelper.decorate(span, () -> list.add(object));

Supplier 是 JAVA8 提供的接口,这个接口是一个提供者的意思,只有一个get的抽象类,没有默认的方法以及静态的方法。get方法返回一个泛型T,这就是一个创建对象的工厂。

所以decorate的作用在我们这里就是:

  • tracer.scopeManager().activate(span) 来激活当前span。
  • 返回对象,执行Redis操作,我们例子就是 () -> list.add(element)
  • 调用 span.finish(); 完成了结束操作,如果采样就会上报。

测试代码 执行流程图如下:

  1. TracingRList TracingHelper
  2. + +
  3. +---+--+ |
  4. | add | begin |
  5. +---+--+ |
  6. | |
  7. | invoke |
  8. | v
  9. | ----------------> +-------+------+
  10. | | buildSpan |
  11. | <---------------+ +-------+------+
  12. | Return |
  13. | |
  14. +---+-------+ |
  15. |span.setTag| |
  16. +---+-------+ |
  17. | |
  18. | |
  19. | |
  20. | invoke +-------------v-------------------------+
  21. | -----------> |decorate(span, () -> list.add(element))|
  22. | +-------------+-------------------------+
  23. | |
  24. | |
  25. | |
  26. | v begin tracing
  27. | +-------------+----------------------+
  28. | |tracer.scopeManager().activate(span)|
  29. | +-------------+----------------------+
  30. | |
  31. | |
  32. | |
  33. | v Real Redis action
  34. +-----+------------+ <----+ +-----+--------+
  35. | list.add(element)| |supplier.get()|
  36. +-----+------------+ +----> +-----+--------+
  37. | |
  38. | |
  39. | v end tracing
  40. | decorate Return +-----+-------+
  41. | <----------------+ |span.finish()|
  42. | +-------------+
  43. +--+---+
  44. | add | end
  45. +--+---+
  46. |
  47. |
  48. |
  49. v

具体 TracingHelper 代码如下:

  1. public class TracingHelper {
  2. public static final String COMPONENT_NAME = "java-redis";
  3. public static final String DB_TYPE = "redis";
  4. protected final Tracer tracer;
  5. private final boolean traceWithActiveSpanOnly;
  6. private final Function<String, String> spanNameProvider;
  7. private final int maxKeysLength;
  8. private final Map<String, String> extensionTags;
  9. public Span buildSpan(String operationName) {
  10. if (traceWithActiveSpanOnly && tracer.activeSpan() == null) {
  11. return NoopSpan.INSTANCE;
  12. } else {
  13. return builder(operationName).start();
  14. }
  15. }
  16. private SpanBuilder builder(String operationName) {
  17. SpanBuilder sb = tracer.buildSpan(spanNameProvider.apply(operationName))
  18. .withTag(Tags.COMPONENT.getKey(), COMPONENT_NAME)
  19. .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
  20. .withTag(Tags.DB_TYPE.getKey(), DB_TYPE);
  21. extensionTags.forEach(sb::withTag);
  22. return sb;
  23. }
  24. public <T> T decorate(Span span, Supplier<T> supplier) {
  25. try (Scope ignore = tracer.scopeManager().activate(span)) { // 激活span
  26. return supplier.get(); // 执行Redis操作
  27. } catch (Exception e) {
  28. onError(e, span);
  29. throw e;
  30. } finally {
  31. span.finish();// 完成了结束操作,如果采样就会上报。
  32. }
  33. }
  34. // 其他操作
  35. .....
  36. }

4.3 Redission专用Tracing功能类

TracingRedissonHelper 是具体实现了Redission 的 Tracing 功能,主要是针对异步操作。

4.3.1 测试代码

官方测试代码如下

  1. final MockSpan parent = tracer.buildSpan("test").start();
  2. try (Scope ignore = tracer.activateSpan(parent)) {
  3. RMap<String, String> map = customClient.getMap("map_config_span_name");
  4. map.getAsync("key").get(15, TimeUnit.SECONDS); // Redis异步操作
  5. }
  6. parent.finish();

能看到,测试的思路是:

  • 生成一个parent Span
  • 然后使用redis map进行异步操作,getAsync这里会生成一个 client span。
  • parent span结束

具体下面我们会讲解。

4.3.2 TracingRedissonHelper

TracingRedissonHelper 需要针对 Redisson 来进行特殊设置,就是因为Redisson同时还为分布式锁提供了异步执行的相关方法

所以需要对异步操作进行处理。其中:

  • RFuture 是 org.redisson.api 包下面的类,
  • CompletableRFuture 是 io.opentracing.contrib.redis.redisson 包下面的类,针对 RFuture 做了特殊处理。

prepareRFuture函数是执行Redis具体操作的函数,其作用如下:

  • 通过 futureSupplier.get(); 获取redisFuture( prepareRFuture的参数span是之前 getAsync 生成的child span )。
  • 设置 redisFuture 的 whenComplete函数,在whenComplete函数中会对传入的Span进行 finish操作 ,这个span 其实是child span。这样异步的Tracing通过Client Span完成
  • 继续操作,恢复parent span,在redisFuture基础上生成CompletableRFuture,然后继续设置redisFuture.whenComplete,如果redisFuture完成,则调用 customRedisFuture.complete。
  • 返回,外界测试函数会finish parent span

针对官方测试代码,执行流程图如下:

  1. +------------+
  2. | Parent Span|
  3. +-----+------+
  4. |
  5. v
  6. TracingRMap TracingRedissonHelper
  7. + +
  8. | |
  9. | v
  10. +----+-----+ Invoke +----+------+
  11. | getAsync | +-----------------------> | buildSpan |create Child Span
  12. +----+-----+ +----+------+
  13. | |
  14. | v
  15. | +-----+--------+
  16. | |prepareRFuture|
  17. | +-----+--------+
  18. | |
  19. | Real redis action v
  20. +-----+----------------+ <--------+ +-------+-------------+
  21. |() -> map.getAsync(key| | futureSupplier.get()|
  22. +-----+----------------+ +--------> +-------+-------------+
  23. | Future |
  24. | |
  25. | +-------v---------+
  26. | |setCompleteAction|
  27. | +-------+---------+
  28. | |
  29. | |
  30. | +------v-------+
  31. | | whenComplete |
  32. | +------+-------+
  33. | |
  34. | v
  35. | +------+-------+
  36. | | span.finish()| Child Span
  37. | +------+-------+
  38. | |
  39. | v
  40. | +--------+----------+
  41. | | continueScopeSpan |
  42. | +--------+----------+
  43. | |
  44. | v
  45. | +---------+----------+
  46. | | tracer.activeSpan()|
  47. | +---------+----------+
  48. | | Parent Span
  49. | |
  50. | v
  51. | +-----+--------+
  52. | |activate(span)|
  53. | +-----+--------+
  54. | |
  55. | |
  56. | v
  57. | return +------------+-----------------+
  58. | <-------------------- | customRedisFuture.complete(v)|
  59. | +------------------------------+
  60. |
  61. +----v----------+
  62. |parent.finish()|
  63. +---------------+

具体代码如下:

  1. class TracingRedissonHelper extends TracingHelper {
  2. TracingRedissonHelper(TracingConfiguration tracingConfiguration) {
  3. super(tracingConfiguration);
  4. }
  5. Span buildSpan(String operationName, RObject rObject) {
  6. return buildSpan(operationName).setTag("name", rObject.getName());
  7. }
  8. private <T> RFuture<T> continueScopeSpan(RFuture<T> redisFuture) {
  9. Span span = tracer.activeSpan();
  10. CompletableRFuture<T> customRedisFuture = new CompletableRFuture<>(redisFuture);
  11. redisFuture.whenComplete((v, throwable) -> {
  12. try (Scope ignored = tracer.scopeManager().activate(span)) {
  13. if (throwable != null) {
  14. customRedisFuture.completeExceptionally(throwable);
  15. } else {
  16. customRedisFuture.complete(v);
  17. }
  18. }
  19. });
  20. return customRedisFuture;
  21. }
  22. private <V> RFuture<V> setCompleteAction(RFuture<V> future, Span span) {
  23. future.whenComplete((v, throwable) -> {
  24. if (throwable != null) {
  25. onError(throwable, span);
  26. }
  27. span.finish();
  28. });
  29. return future;
  30. }
  31. <V> RFuture<V> prepareRFuture(Span span, Supplier<RFuture<V>> futureSupplier) {
  32. RFuture<V> future;
  33. try {
  34. future = futureSupplier.get();
  35. } catch (Exception e) {
  36. onError(e, span);
  37. span.finish();
  38. throw e;
  39. }
  40. return continueScopeSpan(setCompleteAction(future, span));
  41. }
  42. }

4.4 TracingRMap代理类的异步处理

TracingRMap 实现了 org.redisson.api.RMap。这里就使用了上述的异步相关的功能,比如 getAsync。

所以调用了 prepareRFuture 的功能。

  1. public class TracingRMap<K, V> extends TracingRExpirable implements RMap<K, V> {
  2. private final RMap<K, V> map;
  3. private final TracingRedissonHelper tracingRedissonHelper;
  4. @Override
  5. public RFuture<V> getAsync(K key) {
  6. Span span = tracingRedissonHelper.buildSpan("getAsync", map);
  7. span.setTag("key", nullable(key));
  8. return tracingRedissonHelper.prepareRFuture(span, () -> map.getAsync(key));
  9. }
  10. // 其他操作
  11. ......
  12. }

0x05 spring-cloud-redis

opentracing-spring-cloud-redis-starter 实现了对 spring-cloud-redis 的Tracing功能。

Spring Cloud 埋点实现主要实现原理是利用Spring AOP切片技术抽象埋点行为,比如TraceAsyncAspect 切面类,使用@Around 声明拦截规则,后面的逻辑与手动埋点类似,创建一个span,将业务逻辑包围起来即可。

5.1 Bean

首先,利用注解生成一些Bean,比如。

  1. @Configuration
  2. @AutoConfigureAfter({TracerRegisterAutoConfiguration.class, org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class})
  3. @ConditionalOnBean(RedisConnectionFactory.class)
  4. @ConditionalOnProperty(name = "opentracing.spring.cloud.redis.enabled", havingValue = "true", matchIfMissing = true)
  5. @EnableConfigurationProperties(RedisTracingProperties.class)
  6. public class RedisAutoConfiguration {
  7. @Bean
  8. public RedisAspect openTracingRedisAspect(Tracer tracer, RedisTracingProperties properties) {
  9. return new RedisAspect(tracer, properties);
  10. }
  11. }

5.2 拦截规则

其次,使用 @Around 和 @Pointcut 声明拦截规则。具体是通过一层代理来是实现的拦截。对所有的 Redis connection 都通过 TracingRedisConnection 进行了一层包装

  1. @Aspect
  2. public class RedisAspect {
  3. private final Tracer tracer;
  4. private final RedisTracingProperties properties;
  5. RedisAspect(Tracer tracer, RedisTracingProperties properties) {
  6. this.tracer = tracer;
  7. this.properties = properties;
  8. }
  9. @Pointcut("target(org.springframework.data.redis.connection.RedisConnectionFactory)")
  10. public void connectionFactory() {}
  11. @Pointcut("execution(org.springframework.data.redis.connection.RedisConnection *.getConnection(..))")
  12. public void getConnection() {}
  13. @Pointcut("execution(org.springframework.data.redis.connection.RedisClusterConnection *.getClusterConnection(..))")
  14. public void getClusterConnection() {}
  15. @Around("getConnection() && connectionFactory()")
  16. public Object aroundGetConnection(final ProceedingJoinPoint pjp) throws Throwable {
  17. final RedisConnection connection = (RedisConnection) pjp.proceed();
  18. final String prefixOperationName = this.properties.getPrefixOperationName();
  19. final TracingConfiguration tracingConfiguration = new TracingConfiguration.Builder(tracer)
  20. .withSpanNameProvider(RedisSpanNameProvider.PREFIX_OPERATION_NAME(prefixOperationName))
  21. .build();
  22. return new TracingRedisConnection(connection, tracingConfiguration);
  23. }
  24. @Around("getClusterConnection() && connectionFactory()")
  25. public Object aroundGetClusterConnection(final ProceedingJoinPoint pjp) throws Throwable {
  26. final RedisClusterConnection clusterConnection = (RedisClusterConnection) pjp.proceed();
  27. final String prefixOperationName = this.properties.getPrefixOperationName();
  28. final TracingConfiguration tracingConfiguration = new TracingConfiguration.Builder(tracer)
  29. .withSpanNameProvider(RedisSpanNameProvider.PREFIX_OPERATION_NAME(prefixOperationName))
  30. .build();
  31. return new TracingRedisClusterConnection(clusterConnection, tracingConfiguration);
  32. }
  33. }

5.2 埋点

在执行具体的命令前后通过自己提供的 API 进行埋点操作,基本上就是:redisTemplate的操作会在每个操作中调用connect做操作,比如 set操作中调用 connection.set(rawKey, rawValue) ,所以就通过 TracingRedisConnection来做一个封装,在做真正connection操作前后进行tracing

流程图如下:

  1. redisTemplate TracingRedisConnection
  2. + +
  3. | |
  4. | |
  5. v |
  6. +--------------+-----------------+ |
  7. |redisTemplate.opsForValue().set | |
  8. +--------------+-----------------+ |
  9. | |
  10. | |
  11. | |
  12. | |
  13. v |
  14. +-----------+-----------+ |
  15. | RedisTemplate.execute | |
  16. +-----------+-----------+ |
  17. | |
  18. | |
  19. v v
  20. +-------------+-------------+ invoke +-+---+
  21. | DefaultValueOperations.set| +----------------------> | set |
  22. +-------------+-------------+ +-+---+
  23. | |
  24. | |
  25. | v begin tracing
  26. | +---------+--------------+
  27. | | TracingHelper.doInScope|
  28. | +---------+--------------+
  29. | |
  30. | v
  31. | +---+-----+
  32. | |buildSpan|
  33. | +---+-----+
  34. | |
  35. | v
  36. | +---------+-----------+
  37. | |activateAndCloseSpan |
  38. | +---------+-----------+
  39. | |
  40. | |
  41. v Real redis action |
  42. +--------------+-------------------+ <----------------- |
  43. | () -> connection.set(key, value) | |
  44. +--------------+-------------------+ +-----------------> |
  45. | |
  46. | | end tracing
  47. | return +------v--------+
  48. | <--------------------------------+ |span.finish(); |
  49. | +---------------+
  50. |
  51. |
  52. v

代码如下:

  1. public class TracingRedisConnection implements RedisConnection {
  2. private final RedisConnection connection;
  3. private final TracingConfiguration tracingConfiguration;
  4. private final TracingHelper helper;
  5. public TracingRedisConnection(RedisConnection connection,
  6. TracingConfiguration tracingConfiguration) {
  7. this.connection = connection;
  8. this.tracingConfiguration = tracingConfiguration;
  9. this.helper = new TracingHelper(tracingConfiguration);
  10. }
  11. // 在 span 的生命周期内执行具体命令
  12. @Override
  13. public Object execute(String command, byte[]... args) {
  14. // 执行命令
  15. return helper.doInScope(command, () -> connection.execute(command, args));
  16. }
  17. // 其他操作
  18. .....
  19. }

具体Span是在TracingHelper中完成。

  1. public class TracingHelper {
  2. public static final String COMPONENT_NAME = "java-redis";
  3. public static final String DB_TYPE = "redis";
  4. protected final Tracer tracer;
  5. private final boolean traceWithActiveSpanOnly;
  6. private final Function<String, String> spanNameProvider;
  7. private final int maxKeysLength;
  8. private final Map<String, String> extensionTags;
  9. public <T> T doInScope(String command, Supplier<T> supplier) {
  10. Span span = this.buildSpan(command);
  11. return this.activateAndCloseSpan(span, supplier);
  12. }
  13. // 其他操作
  14. .....
  15. }

0xFF 参考

分布式链路组件 SOFATracer 埋点机制解析

蚂蚁金服开源分布式链路跟踪组件 SOFATracer 埋点机制剖析

https://github.com/opentracing/opentracing-java

https://github.com/opentracing-contrib/java-redis-client

opentracing-spring-cloud-redis-starter

[源码分析] OpenTracing之跟踪Redis的更多相关文章

  1. Redis 内存管理 源码分析

    要想了解redis底层的内存管理是如何进行的,直接看源码绝对是一个很好的选择 下面是我添加了详细注释的源码,需要注意的是,为了便于源码分析,我把redis为了弥补平台差异的那部分代码删了,只需要知道有 ...

  2. Redis 专栏(使用介绍、源码分析、常见问题...)

    一.介绍相关 说Redis : 介绍Redis特性,使用场景,使用Jedis操作Redis等. 二.源码分析 1. 数据结构 Redis源码分析(sds):Redis自己封装的C语言字符串类型. Re ...

  3. redis源码分析之事务Transaction(上)

    这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易 ...

  4. Redis学习——ae事件处理源码分析

    0. 前言 Redis在封装事件的处理采用了Reactor模式,添加了定时事件的处理.Redis处理事件是单进程单线程的,而经典Reator模式对事件是串行处理的.即如果有一个事件阻塞过久的话会导致整 ...

  5. 手机自动化测试:Appium源码分析之跟踪代码分析九

    手机自动化测试:Appium源码分析之跟踪代码分析九   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家 ...

  6. 手机自动化测试:Appium源码分析之跟踪代码分析八

    手机自动化测试:Appium源码分析之跟踪代码分析八   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家 ...

  7. 手机自动化测试:Appium源码分析之跟踪代码分析七

    手机自动化测试:Appium源码分析之跟踪代码分析七   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest推出手机自 ...

  8. 手机自动化测试:Appium源码分析之跟踪代码分析六

    手机自动化测试:Appium源码分析之跟踪代码分析六   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest推出手机自 ...

  9. 手机自动化测试:Appium源码分析之跟踪代码分析五

    手机自动化测试:Appium源码分析之跟踪代码分析五   手机自动化测试是未来很重要的测试技术,作为一名测试人员应该熟练掌握,POPTEST举行手机自动化测试的课程,希望可以训练出优秀的手机测试开发工 ...

随机推荐

  1. Python 常用的操作文件代码

    1:统计list中相同的个数,并按大小排序. original_list = ['a', 'b', 'b', 'a', 'd', 'd', 'b', 'z', 'c', 'b', 'r', 's', ...

  2. python 11 类与对象

    给大家介绍对象hhhh 封装 举个例子,把乱七八糟的数据仍到列表,数据层面的封装 把常用的代码打包成一个函数,封装 外观特征(静态) 能做什么(动态) 对象=属性(静态) + 方法(动态) 1.方法多 ...

  3. Javascript模块化编程(一):模块的写法 (转)

    Javascript模块化编程(一):模块的写法 原文作者: 阮一峰 日期: 2012年10月26日 随着网站逐渐变成"互联网应用程序",嵌入网页的Javascript代码越来越庞 ...

  4. HDFS的数据流读写数据 (面试开发重点)

    1 HDFS写数据流程 1.1 剖析文件写入 HDFS写数据流程,如图所示 1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是 ...

  5. cni-ipam-etcd demo

    链接:https://github.com/jeremyxu2010/cni-ipam-etcd 测试demo: package main import ( "fmt" " ...

  6. 2020.5.27 第七篇 Scrum冲刺博客

    Team:银河超级无敌舰队 Project:招新通 项目冲刺集合贴:链接 目录 一.每日站立会议 1.1 会议照片 1.2 项目完成情况 二.项目燃尽图 三.签入记录 3.1 代码/文档签入记录 3. ...

  7. 接口测试中GET方法的获取

    今天在这里给大家介绍一下get方法,其实这些方法大家可以看一下源码里面的介绍只需要在代码中输入: import requests help(requests) 就可以看到带有示例的解释: 现在我们来完 ...

  8. 纯CSS3图片反转

    一些简单实用的小技巧,CSS3对图片进行翻转,显示另一面的文字,或者图片效果,那么具体怎样去做呢?一起来看看吧. 在CSS3中,可以使用transform-style: preserve-3d进行3d ...

  9. Git深入浅出使用教程:Git安装、远程控制、常用命令(全)

    一.软件安装 1.先安装[Git-2.24.1.2-64-bit.exe]软件.(官网下载的很慢,可以在百度云盘下载我的) 链接:https://pan.baidu.com/s/1uoIS9DWSBp ...

  10. MSP430-LED中断闪烁代码详解

    使用MSP430F149的开发板,首先对LED闪烁灯的例程进行讲解,然后下边是自己写的,将部分代码写入了新建的led.c程序中 #include  <msp430x14x.h>       ...