我是Jaeger的新手,想知道我是否可以跟踪具有父跨度和子跨度的端到端事务,如下所示,从子组件轮询(没有从父组件到子组件的直接调用(和从子组件到父组件的回调。
上下文:
首先,让我们描述一下我想做什么的简化视图。 由多个组件组成的解决方案公开了用于提交事务的 REST API。 它在调用后同步返回事务 ID,并在事务完成或失败时回调调用者。所以我要追踪的是 1 到 3 的整体事务:
- 调用程序使用事务输入参数和回调 URL 调用解决方案 REST API
- 调用程序从 REST API 响应接收 ID
- 解决方案调用具有该 ID 的调用程序回调 URL
引擎盖下:
- 公开 REST API 以提交事务的组件 (C1( 将执行一些操作,然后将事务放入队列中。这是我上面提到的父组件。
- 第二个组件 (C2( 将使用内部 REST API 轮询 C1,以从队列中获取事务,然后调用第三个组件 (C3( 公开的 REST API 以进一步执行与事务相关的操作。在消息中,C1 传播一个回调 url,然后由 C2 传播到 C3。
- 第三个组件 (C3( 将通知 C1 它开始处理事务,方法是使用 IN-PROGRESS 调用传播到 C1 的回调
- 然后 C3 协调许多操作同步调用(request<->response(或异步调用(request<->response,然后是<-callback(不同的其他组件
- 当所有操作都成功完成或一个操作失败并出现不可恢复的错误时,组件 C3 将再次回调 C1 以指示成功或失败。
- 当 C3 调用 C1 时,它会执行一些关闭操作并调用事务调用者的回调。
我假设有可能与Jaeger一起追踪整个交易,但这是我的问题: 问题:
- Q1: 我说得对吗? :-(
- Q2:如何将C1中创建的父跨度传达/传播到C2?(在步骤 1 和 2 之间(。我正在考虑将地图中的 SpanContext 添加为事务的新属性?
- Q3:假设 C2 获得了跨度,它可以将其传播到 C3,并且由于 C3 将调用多次回调到 C1(步骤 3 和 5(,如何将 C1 中的这些回调调用与父跨度相关联?
欢迎任何提示和提示。 感谢。
所以我试了一下,我对上述问题的回答是:
Q1(是的,这是可能的(恭喜Jaeger团队,软件包很容易掌握,有一个好的文档(
Q2( 我确实在这个问题上有点挣扎,多亏 https://github.com/CHOMNANP/jaeger-js-text-map-demo 我通过将格式为"FORMAT_TEXT_MAP"的跨度上下文添加带有引用的"textCarrier"来实现一个解决方案,以传达组件 1 正在向组件 2 发布的消息。
第一次 API 调用时 C1 中的代码狙击器
server.post("/api/vms", (req, res) => {
console.log('Enter /api/vms');
const span = tracer.startSpan(req.path);
// Use the log api to capture a log
span.log({ event: 'request_received' })
txSpan = span;
//console.log("req",span);
// Use the setTag api to capture standard span tags for http traces
span.setTag(opentracing.Tags.HTTP_METHOD, req.method)
span.setTag(opentracing.Tags.SPAN_KIND, opentracing.Tags.SPAN_KIND_RPC_SERVER)
span.setTag(opentracing.Tags.HTTP_URL, req.path)
在 Redis 上发送消息时,后跟这部分:
const textCarrier = getTextCarrierBySpanObject(span);
tracer.inject(span.context(), opentracing.FORMAT_TEXT_MAP, textCarrier)
var vm = req.body;
console.log('Creating a new vm: ', vm);
// Publish a message by specifying a channel name.
try {
Object.assign(vm, { textCarrier });
pub.publish('tasks-queue', JSON.stringify(vm));
} catch(e) {
console.log("App1 Error when publishing task to App2", e);
}
getTextCarrierBySpanObject 函数来自 https://github.com/CHOMNANP/jaeger-js-text-map-demo
function getTextCarrierBySpanObject(_span) {
const spanContext = _span.context();
const traceId = spanContext._traceId.toString('hex');
const spanId = spanContext._spanId.toString('hex');
let parentSpanId = spanContext._parentId;
const flag = _.get(spanContext, '_flags', 1);
if (parentSpanId) {
parentSpanId = parentSpanId.toString('hex');
} else {
parentSpanId = 0;
}
const uberTraceId = `${traceId}:${spanId}:${parentSpanId}:${flag}`;
console.log("uberTraceId===> ", uberTraceId)
let textCarrier = {
"uber-trace-id": uberTraceId
};
return textCarrier
}
C2 中从 redis 接收消息的代码片段
sub.on('message', function(channel, message) {
// message is json string in our case so we are going to parse it.
try {
var json = JSON.parse(message)
console.log("Task received", message);
const tracer = opentracing.globalTracer();
// Extracting the span context from the message
var parentSpan = tracer.extract(opentracing.FORMAT_TEXT_MAP, JSON.parse(message).textCarrier);
console.log("textCarrier=",JSON.parse(message).textCarrier);
const span = tracer.startSpan("/msg", { childOf: parentSpan });
// Use the log api to capture a log
span.log({ event: 'msg_received' })
我用 1.13 版进行了测试
docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 -p 9411:9411 jaegertracing/all-in-one:1.13
- Q3( 这很简单,使用 FORMAT_HTTP_HEADERS 来传达组件 C4 在 C3 上调用回调和组件 C3 在 C1 上调用回调的跨度。我发现的唯一"问题"更多的是"跟踪可读性问题",因为事实上,Spans 以"程序"顺序出现在 Jaeger UI 上,而不是以"计时顺序"出现,这可能有点令人困惑......但是"跟踪图"实验功能允许以正确的出现顺序实际看到跟踪,所以一切都很好。
总而言之,与Jaeger一起进行非常令人信服的原型设计练习,很可能会在实际项目中进行试点,然后再在生产中试用。