春天侦探拉链没有嵌套在Kotlin协程中

时间:2017-10-31 01:36:36

标签: kotlin zipkin spring-cloud-sleuth kotlinx.coroutines

我可以通过以下Spring Boot控制器代码看到Zipkin UI中记录的跨度:

@RestController
class ConcurrentController {
    @Autowired
    lateinit var restTemplate : RestTemplate
    val urls = arrayListOf<String>("http://www.google.com","http://www.facebook.com")
    private val logger = Logger.getLogger(this::class.java.getName())
    @RequestMapping("/concurrent1")
    fun endPoint1() : String {
        logger.info("/concurrent1")
        var s = ""
        runBlocking {
            val a = urls.map { url ->
                logger.info(url)
                async(CommonPool) {
                logger.info("getting $url")
                restTemplate.getForObject(url, String::class.java)
                sleep(5000)
                logger.info("got $url")
                }
            }
            val b = a.map { it.await() }
            s = b.joinToString { "" }
        }
        return s
    }
}

,日志输出如下:

2017-10-30 20:56:04.525  INFO [coroutinesDemo,eb6fd4fedb6f3a6d,eb6fd4fedb6f3a6d,true] 13547 --- [nio-8080-exec-1] c.e.coroutines.ConcurrentController      : /concurrent1
2017-10-30 20:56:04.543  INFO [coroutinesDemo,eb6fd4fedb6f3a6d,eb6fd4fedb6f3a6d,true] 13547 --- [nio-8080-exec-1] c.e.coroutines.ConcurrentController      : http://www.google.com
2017-10-30 20:56:04.548  INFO [coroutinesDemo,eb6fd4fedb6f3a6d,eb6fd4fedb6f3a6d,true] 13547 --- [nio-8080-exec-1] c.e.coroutines.ConcurrentController      : http://www.facebook.com
2017-10-30 20:56:04.548  INFO [coroutinesDemo,,,] 13547 --- [onPool-worker-9] c.e.coroutines.ConcurrentController      : getting http://www.google.com
2017-10-30 20:56:04.549  INFO [coroutinesDemo,,,] 13547 --- [onPool-worker-2] c.e.coroutines.ConcurrentController      : getting http://www.facebook.com
2017-10-30 20:56:09.703  INFO [coroutinesDemo,,,] 13547 --- [onPool-worker-2] c.e.coroutines.ConcurrentController      : got http://www.facebook.com
2017-10-30 20:56:09.703  INFO [coroutinesDemo,,,] 13547 --- [onPool-worker-9] c.e.coroutines.ConcurrentController      : got http://www.google.com

但跟踪在UI中是独立的。

我希望通过调用/concurrent1端点,可以同时嵌入两个对Google和Facebook网址的调用。

我怀疑这是由于线程的协程执行与弹簧应用程序启动时不同但我不知道如何继续使用Spring Sleuth!

2 个答案:

答案 0 :(得分:0)

我使用“ spring-cloud-sleuth”库中的“ TraceCallable”类在我的代码中解决它。

我的代码示例是:

    @Component
    class TracingCallableSupplier(
        private val tracing: Tracing,
        private val spanNamer: SpanNamer
    ) {

        /**
         * Supply callable which will use tracing from parent while performing jobs.
         */
        fun <T : Any?> supply(function: () -> T): Callable<T> {
            return TraceCallable(tracing, spanNamer, Callable(function))
        }
    }    

    @Service
    class MyBean{
        fun myMethod {
        val callable = tracingCallableSupplier.supply {
              ... some code to be called asynchronous...
        }

        val deferred = (0 until 10).map {
            async {
                callable.call()
            }
        }

        runBlocking {
           deferred.map {
               val result = it.await()
                   ... some processing ...
               }
           }
        }
    }

答案 1 :(得分:0)

注意:此解决方案确实可用于日志记录目的,但不适用于其他Sleuth功能,例如检测RestTemplates以将跟踪​​标头发送到其他服务。因此,不幸的是,这不是一个完整的解决方案。 :(

采用@Baca的解决方案一段时间后,我发现Kotlin Coroutines提供了与slf4j的直接集成,而这正是Spring Sleuth的基础。 Sleuth将属性X-B3-TraceIdtraceIdX-B3-SpanIdspanId添加到线程的MDC。

您可以使用以下代码为协程保留父线程的MDC。每当协程被执行/恢复时,协程框架将负责在工作线程上恢复MDC上下文。到目前为止,这是我发现的最简单的解决方案。 :)

// add your own properties or use the ones already added by Sleuth
MDC.put("someLoggerProperty", "someValue")

GlobalScope.launch(MDCContext()) {
    // your code goes here
}

启动方法采用可选的CoroutineContext,并且coroutine-slf4j集成实现MDCContext。此类捕获调用线程的MDC上下文(创建副本),并将其用于协程执行。

将此依赖项添加到您的build.gradle中:

implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-slf4j', version: '1.3.9'

项目:https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-slf4j 文档:https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-slf4j/index.html