ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

用 GraalVM Insight 为 Node.js 应用实现 OpenTracing 分布式追踪

用 GraalVM Insight 为 Node.js 应用实现 OpenTracing 分布式追踪 编译器JIT编译语言运行时高性能计算内存管理【免费下载链接】graalGraalVM compiles applications into native executables that start instantly, scale fast, and use fewer compute resources 项目地址https://gitcode.com/gh_mirrors/gr/graal点击查看免费下载GraalVM Insight 是 GraalVM 提供的一套动态、声明式的代码追踪与切面编程工具本指南聚焦于它的典型实战场景借助标准的 OpenTracing API 与 Jaeger 后端为普通的 Node.js HTTP 服务注入热插拔的分布式追踪能力。读完本文你将掌握如何编写基于insight.on(enter/return)钩子的追踪脚本、如何与 Jaeger 客户端协同完成 Span 的开启与关闭、如何在应用启动时或运行中动态装载追踪工具以及底层--insight选项与Insight仪器在源码中的实现机制。GraalVM Insight 与声明式追踪GraalVM Insight 是一个多用途、灵活的运行时工具它允许你以极低的性能损耗把追踪切点选择性地下发到正在运行的应用上。与在业务代码中手工埋点不同Insight 把追踪逻辑完全隔离在独立的 agent 脚本中通过监听enter函数进入与return函数返回两类事件来观察程序执行且可以随时开关。Insight 的典型用法是配合标准的 OpenTracing API 实现平滑、声明式的临时ad hoc追踪追踪逻辑可以在应用运行期间被动态加入或移除并按需定制以提取调查线上故障所需的精确信息。本文以 Jaeger 作为 OpenTracing 的后端实现完整演示这一能力。从源码层面看GraalVM Insight 是注册在 Truffle 运行时中的一个仪器Instrument其 ID 固定为insight见 Insight.java。而--insight脚本路径命令行选项在 InsightInstrument.java 中被声明它接收一个脚本文件路径在onCreate阶段读取该文件、按 MIME 类型自动识别脚本语言可以是 JavaScript、Python、Ruby、R 等任何 GraalVM 支持的语言并以insight全局对象的形式把 API 暴露给脚本见 InsightInstrument.java。Insight 公开 API 的版本号定义在 Insight.javaInsight.VERSION 1.3脚本中可通过insight.version读取。准备工作安装 Jaeger 客户端要开始追踪首先需要为 Node.js 安装 Jaeger 的客户端侧插桩库。在 GraalVM 自带的bin目录下执行./bin/npm install jaeger-client3.17.1安装完成后jaeger-client模块所提供的 OpenTracing API 就能在 instrument 脚本下文中的agent.js里通过tracer对象使用了。这里需要注意一个时序问题tracer对象并不是在 agent 脚本一加载就存在的它要等 Jaeger 客户端真正初始化完成之后才可用这个延迟初始化的机制会在后文专门讲解。编写追踪工具 agent.js追踪工具的核心是一段名为agent.js的 Insight 脚本。它通过initialize函数接收一个已经初始化好的tracer然后注册两组钩子分别在 Node.js 内部处理 HTTP 请求的emit(request, ...)函数进入时创建 Span在res.end()返回时结束 Span。let initialize function(tracer) { var counter 0; insight.on(enter, function(ctx, frame) { const args frame.args; if (request ! frame.type || args.length ! 2 || typeof args[0] ! object || typeof args[1] ! object) { return; } const req args[0]; const res args[1]; const span tracer.startSpan(request); span.setTag(span.kind, server); span.setTag(http.url, req.url); span.setTag(http.method, req.method); res.id counter; res.span span; console.log(agent: handling #${res.id} request for ${req.url}); }, { roots: true, rootNameFilter: emit, at: { sourcePath: .*events.js } }); insight.on(return, function(ctx, frame) { var res frame[this]; if (res.span) { res.span.finish(); console.log(agent: finished #${res.id} request); } else { //Caused, for example, by Tracer itself connecting to the Jaeger server } }, { roots: true, rootNameFilter: end, at: { sourcePath: .*_http_outgoing.js } }); console.log(agent: ready); };这段脚本的关键点在于钩子注册insight.on(enter, handler, filter)与insight.on(return, handler, filter)分别是函数进入与返回时的回调第三个参数是声明式的过滤配置。精确筛选roots: true表示只关心根调用rootNameFilter: emit/end用函数名过滤目标at.sourcePath则用正则限定源代码文件.*events.js与.*_http_outgoing.js从而精准命中 Node.js 内部处理 HTTP 请求的函数。读取实参回调的第二个参数frame暴露了被插桩函数的作用域——frame.args是参数数组frame[this]是this对象frame.type是参数的类型信息。动态对象扩展由于res是动态 JavaScript 对象可以在enter处理器中为它追加id和span两个属性然后在end函数的return处理器中读取并使用它们实现 Span 的跨函数传递。整个机制可以概括为系统在内部挂钩到emit(request, ...)用于初始化对 HTTP 请求的响应与res.end()用于结束响应两个函数。由于 GraalVM Insight 能够访问frame变量及其字段工具可以读取req.url、req.method的值并作为span.setTag的值上报给 OpenTracing 后端。延迟初始化等待 Jaeger 对象可用有了工具之后剩下的就是在对的时机启用它。真正的生产级做法是在运行时通过管理服务器动态下发追踪脚本详见 Embedding Insight into Node.js Application 中如何创建 admin server 并按需应用包括 OpenTracing 在内的任意追踪脚本。本指南采用更简洁的方式当jaeger对象被传入时再启用工具。let initializeJaeger function (ctx, frame) { insight.off(enter, initializeJaeger); let jaeger frame.jaeger; var initTracer jaeger.initTracer; console.log(agent: Jaeger tracer obtained); // See schema https://github.com/jaegertracing/jaeger-client-node/blob/master/src/configuration.js#L37 var config { serviceName: insight-demo, reporter: { // Provide the traces endpoint. This forces the client to connect directly to the Collector and send // spans over HTTP collectorEndpoint: http://localhost:14268/api/traces, // Provide username and password if authentication is enabled in the Collector // username: , // password: , }, sampler: { type : const, param : 1 } }; var options { tags: { insight-demo.version: 1.1.2, }, // metrics: metrics, logger: console, sampler: { type : const, param : 1 } }; var tracer initTracer(config, options); initialize(tracer); }; insight.on(return, initializeJaeger, { roots: true, rootNameFilter: jaegerAvailable });这段代码的核心思路一次性钩子insight.on(return, initializeJaeger, ...)监听名为jaegerAvailable的根调用当该函数返回时initializeJaeger首先调用insight.off(enter, initializeJaeger)把自己卸载确保只执行一次。从 frame 取对象frame.jaeger直接读取jaegerAvailable函数返回时的调用环境拿到主程序传入的jaeger-client模块。Tracer 配置config指定了服务名serviceName: insight-demo、上报端点collectorEndpoint: http://localhost:14268/api/traces直接以 HTTP 方式把 Span 发给 Jaeger Collector以及恒定采样type: const, param: 1即全量采样options里可以附加全局标签如insight-demo.version: 1.1.2和日志器。移交 tracerinitTracer(config, options)创建 tracer 后调用initialize(tracer)正式启用第一节中的追踪钩子。宿主脚本 server.js 的配合这个工具需要主服务脚本的配合让server.js获取jaeger-client模块并通过jaegerAvailable函数把它交给 agent然后创建一个典型的 HTTP 服务器。function jaegerAvailable(jaeger) { console.log(Providing Jaeger object to the agent); } jaegerAvailable(require(jaeger-client)); const http require(http); const srv http.createServer((req, res) { console.log(server: obtained request ${res.id}); setTimeout(() { res.write(OK# ${res.id}); console.log(server: replied to request ${res.id}); res.end(); }, 5); }); srv.listen(8080);注意jaegerAvailable(require(jaeger-client))这一行它在server.js加载时同步执行把jaeger-client模块作为实参传给jaegerAvailable函数agent 脚本正是通过对该函数的return钩子截获这个实参从而拿到 Jaeger 客户端。这展示了 Insight 的一个独特能力——通过插桩宿主语言自身的调用来交换数据agent 与 server 之间无需任何共享模块或全局变量。服务端逻辑本身没有任何埋点代码只是普通的http.createServer收到请求后延迟 5 毫秒写回OK# id并调用res.end()。res.id正是由 agent 在enter钩子中写入的server 与 agent 借此共享了请求编号。启动 Jaeger 并运行带追踪的应用有了agent.js与server.js两个文件就可以启动应用和 agent 了。不过首先要启动 Jaeger 服务器docker run -d --name jaeger \ -e COLLECTOR_ZIPKIN_HTTP_PORT9411 \ -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:latest该命令启动 Jaeger 全功能all-in-one镜像其中16686是 Jaeger UI 端口14268是 HTTP Collector 端点与 agent 的collectorEndpoint对应其余端口用于 UDP 上报、Zipkin 兼容等。然后使用 GraalVM 的node启动器通过--insightagent.js挂载追踪工具./bin/node --insightagent.js server.js Providing Jaeger object to the agent agent: Jaeger tracer obtained Initializing Jaeger Tracer with RemoteReporter and ConstSampler(always) agent: ready日志清晰地展示了整个启动时序server.js首先把 Jaeger 对象交给 agent → agent 拿到tracer→ Jaeger 客户端初始化RemoteReporter 恒定采样→ agent 就绪。这就是前文所述延迟初始化流程在运行时的完整印证。加压验证并观察追踪结果现在可以打开 Jaeger UI地址为http://localhost:16686/并用 Apache Bench 给服务器加压ab -c 10 -n 10000 http://localhost:8080/服务器会在控制台打印处理每个请求的详细信息Jaeger UI 中则会逐步填充出对应的追踪trace该截图来自本仓库 docs/tools/insight/img/Insight-Jaeger.png展示的是经过加压后 Jaeger UI 中呈现的服务insight-demo的 Span 视图——每个 HTTP 请求对应一个request类型的 Span携带http.url、http.method、span.kindserver等标签服务端处理耗时一目了然。至此一个普通的 Node.js 应用就被平滑地增强了分布式追踪能力。值得强调的是追踪逻辑完全隔离在独立的agent.js文件中业务代码零侵入它既可以在启动时通过--insight一次性应用如本指南所示也可以借助 Embedding Insight into Node.js Application 中介绍的 admin server 在运行中动态下发或移除追踪脚本所有追踪 Span 的创建、打标签、结束都由 OpenTracing API 驱动与具体后端解耦——换成其他兼容 OpenTracing 的后端只需调整 Tracer 初始化参数。源码佐证--insight 选项与 Insight 仪器为了让读者对上述机制有更扎实的理解这里补充仓库中的源码证据--insightpath选项的完整声明位于 InsightInstrument.java说明为Use provided file as an insight script (default: no script)属于用户级稳定选项仪器本身以id Insight.ID即insight注册在 Truffle 运行时见 InsightInstrument.java。脚本文件的加载、MIME 类型探测与语言识别发生在onCreate阶段InsightInstrument.java先通过env.getTruffleFile(null, path)定位文件再用file.detectMimeType()探测类型最后在已注册语言中匹配 MIME 并以此决定以哪种语言解释执行 agent 脚本——这正是 Insight 脚本可以用 JavaScript 之外语言编写的基础。jaeger-client的initTracer采用 HTTP 直连 Collector 的RemoteReporterConstSampler(always)组合Span 上报端点指向http://localhost:14268/api/traces与 Jaeger all-in-one 镜像暴露的 Collector 端口一一对应。Insight API 的版本化设计Insight.VERSION 1.3Insight.java新增 API 元素带有since标注脚本中可通过insight.version检查兼容性参见 Insight-Manual 中的示例。若希望在 Java 宿主应用中编程式启用/禁用 Insight而非命令行--insight可以通过context.getEngine().getInstruments().get(insight).lookup(Function.class)获取访问句柄详见 Insight.java 与 Embedding Insight into Applications。延伸阅读本文展示了 Insight 在分布式追踪方向的经典用法。要进一步掌握 Insight 的完整能力包括 source 事件监听、enter/return之外的钩子、对局部变量的读写、异常处理、执行拦截、堆转储--heap.dump以及多语言Python/Ruby/R编写 agent 等请继续阅读 Insight Manual了解如何在 Java 或 Node.js 宿主应用中嵌入 Insight 并动态应用脚本请参考 Embedding Insight into Applications。赞分享编译器JIT编译语言运行时高性能计算内存管理【免费下载链接】graalGraalVM compiles applications into native executables that start instantly, scale fast, and use fewer compute resources 项目地址https://gitcode.com/gh_mirrors/gr/graal点击查看免费下载相关推荐llmfit bench 实测指南3 轮推理量出你硬件的真实 tok/sllmfit bench 实测指南3 轮推理量出你硬件的真实 tok/s llmfit 是一款一条命令找到你能跑的大模型的开源工具。平时它靠公式估算 to编译器JIT编译语言运行时高性能计算内存管理Viewing HTML files in the browserViewing HTML files in the browser The HTML boilerplate in the index.html file is文档教程教育PHP Font Lib 多语言字体处理Unicode 编码与字符映射深度解析PHP Font Lib 多语言字体处理Unicode 编码与字符映射深度解析 在当今全球化的数字世界中多语言字体处理已成为Web开发中不可或缺的一环。PH开发工具上一篇Spinnaker与AWS Lambda Functions集成无服务器工作流全指南下一篇Recompose高级用法自定义转换规则与复杂布局处理方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表