广告服务

此服务根据上下文关键词为用户提供合适的广告。广告内容为商店中可用的商品。

广告服务源码

自动插桩

此服务依赖 OpenTelemetry Java 代理来自动插桩对 gRPC 等库的观测,并配置 OpenTelemetry SDK。 此代理通过 -javaagent 命令行参数传递到进程中。在 Dockerfile 中通过 JAVA_TOOL_OPTIONS 添加命令行参数,并在自动生成的 Gradle 启动脚本中使用。

ENV JAVA_TOOL_OPTIONS=-javaagent:/app/opentelemetry-javaagent.jar

链路

向自动插桩的 Span 添加属性

在自动插桩的代码执行过程中,可以通过上下文获取当前的 Span:

Span span = Span.current();

通过 setAttribute 方法可以向 Span 添加属性。在 getAds 函数中添加多个属性:

span.setAttribute("app.ads.contextKeys", req.getContextKeysList().toString());
span.setAttribute("app.ads.contextKeys.count", req.getContextKeysCount());

添加 Span 事件

可以使用 addEvent 方法向 Span 添加事件。在 getAds 函数中,当捕获异常时添加带属性的事件:

span.addEvent("Error", Attributes.of(AttributeKey.stringKey("exception.message"), e.getMessage()));

设置 Span 状态

当操作结果为错误时,应使用 setStatus 方法相应地设置 Span 的状态。 在 getAds 函数中,当捕获异常时设置 Span 状态:

span.setStatus(StatusCode.ERROR);

新建 Span

可以通过 Tracer.spanBuilder("spanName").startSpan() 创建并启动新的 Span。 新建的 Span 应通过 Span.makeCurrent() 设置到上下文中。在 getRandomAds 函数中, 将创建一个新的 Span,设置到上下文中,执行操作,最后结束此 Span:

// 手动创建并启动一个新的 Span
Tracer tracer = GlobalOpenTelemetry.getTracer("ad");
Span span = tracer.spanBuilder("getRandomAds").startSpan();

// 将 Span 设置到上下文中,这样子 Span 启动时父 Span 会被正确设置
try (Scope ignored = span.makeCurrent()) {

  Collection<Ad> allAds = adsMap.values();
  for (int i = 0; i < MAX_ADS_TO_SERVE; i++) {
    ads.add(Iterables.get(allAds, random.nextInt(allAds.size())));
  }
  span.setAttribute("app.ads.count", ads.size());

} finally {
  span.end();
}

指标

初始化指标

创建指标的第一步类似于创建 Span,需要初始化一个 Meter 实例,例如 GlobalOpenTelemetry.getMeter("ad")。 然后可以使用此 Meter 实例提供的构建方法来创建所需的指标工具,例如:

meter
  .counterBuilder("app.ads.ad_requests")
  .setDescription("Counts ad requests by request and response type")
  .build();

当前生成的指标

注意:以下所有指标名称在 Prometheus/Grafana 中将 . 字符转换为 _

自定义指标

当前可用的自定义指标包括:

  • app.ads.ad_requests:广告请求计数器,包含维度以描述请求是否使用上下文关键词、响应是否为定向广告或随机广告。

自动插桩指标

应用中还提供以下自动插桩的指标:

日志

广告服务使用 Log4J 进行日志记录,此日志系统由 OpenTelemetry Java 代理自动配置。

它会在日志记录中包含链路上下文,从而支持日志与链路的关联。