我可以通过以下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!
答案 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-TraceId
,traceId
,X-B3-SpanId
和spanId
添加到线程的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