实现跨应用链路追踪
在上篇文章使用 opentelemetry 与 jaeger 实现 flask 应用的链路追踪 | 那时难决 (duyixian.cn)中,我们介绍了如何使用 opentelemetry 与 jaeger 对 flask 应用进行链路跟踪。
在真实的业务场景下多个服务间互相调用是十分常见的,在进行一些问题排查的时候有必要跟踪一个请求链路在各个服务中细节。
使用 opentelemetry 与 jaeger 同样可以实现跨应用的链路追踪。
原理
进行链路跟踪的核心概念是 trace,trace 是一个具有开始时间和结束时间的操作,它可以包含若干个 span,形成一个树状结构。 每一个 trace 都有一个唯一的 traceId,用于标识一个请求链路。在跨服务的场景下,我们可以通过 traceId 将一个请求链路中的所有 span 关联起来。
回到上一篇文章的场景,编辑main.py
,定义/headers
路由,使用 requests 库请求 https://httpbin.org/headers ,返回 requests 发起请求时的 header 信息。
@app.get("/headers")
def headers():
return requests.get("https://httpbin.org/headers").json()
可以看到 headers 中有一个Traceparent
,携带了 TraceId 信息。调用其他服务时,我们也需要将这个Traceparent
传递给下游服务。
实现跨服务链路追踪
首先我们实现一个上游服务,用于演示跨应用链路追踪。这次我们使用 asyncio + FastAPI 来实现。asyncio 生态下的 httpx 和 fastapi 都有 opentelemetry 的支持。
先安装依赖:
pip install fastapi opentelemetry-instrumentation-fastapi
pip install httpx opentelemetry-instrumentation-httpx
编辑upstream.py
:
import fastapi
import httpx
from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
def configure_trace(app: fastapi.FastAPI):
trace.set_tracer_provider(
TracerProvider(resource=Resource.create({SERVICE_NAME: app.title}))
)
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(
JaegerExporter(
agent_host_name="localhost",
agent_port=6831,
)
)
)
FastAPIInstrumentor.instrument_app(app)
HTTPXClientInstrumentor().instrument()
app = fastapi.FastAPI(title=__name__)
configure_trace(app)
def get_client():
return httpx.AsyncClient()
@app.get("/api/check-baidu")
async def check_baidu(client: httpx.AsyncClient = fastapi.Depends(get_client)):
resp = await client.head("https://www.baidu.com")
return {"connected": resp.status_code == 200}
在upstream.py
中,我们定义了一个 FastAPI 应用,它有一个/api/check-baidu
路由,用于检查是否能够访问百度。
并且进行了 opentelemetry 和 jaeger 的初始化配置
接下来使用 uvicorn 启动upstream
服务:
uvicorn upstream:app --port 5001 --reload
编辑main.py
,在/check
路由中,使用 requests 库请求http://localhost:5001/api/check-baidu
,返回请求结果。
@app.get("/check-baidu")
def check_baidu():
return requests.get("http://localhost:5001/api/check-baidu").json()
访问 http://localhost:5000/check-baidu ,可以看到请求成功:
访问 Jaeger UI ,查看链路追踪信息:
查看 trace 详情:
可以看到,我们的请求链路中包含了两个 span,分别是/check-baidu
和/api/check-baidu
,分别来自于两个服务。
从Jaeger UI也可以直接查看服务间的调用关系:
并且可以查看具体是哪些请求链路构成的服务间的调用关系(在 Layout 配置中开启 operations):
更复杂的场景
我们定义了五个服务,分别是main
、upstream
、service1
、service2
、log
:
- main:主服务,提供
/check-sites
路由,请求 upstream 服务的/api/check-sites
接口。 - upstream:上游服务,提供
/api/check-sites
路由,请求 service1 和 service2 服务的/api/check
接口。 - services1 与 service2:提供
/api/check
路由,分别检查百度和腾讯网的可连接性,在请求中请求 log 服务的/api/log
接口。 - log:提供
/api/log
路由,持久化 services1 与 service2 的请求日志。
访问 main 服务的/check-sites
路由,可以看到请求成功:
刷新 Jaeger UI ,查看链路追踪信息:
从 trace 信息中可以直观了解到请求链路的细节,比如 upstream 两次请求 service1 和 service2 服务的/api/check
接口是串行的,改成并行的话可以有效减少请求时间。
@app.get("/api/check-sites")
async def check_sites(client: httpx.AsyncClient = fastapi.Depends(get_client)):
baidu = (await client.get("http://localhost:5002/api/check")).json()['connected']
qq = (await client.get("http://localhost:5003/api/check")).json()['connected']
return {"baidu": baidu, "qq": qq}
@app.get("/api/check-sites-v2")
async def check_sites_v2(client: httpx.AsyncClient = fastapi.Depends(get_client)):
baidu , qq = await asyncio.gather(check("baidu",client),check("qq",client))
return {"baidu": baidu, "qq": qq}
async def check(site:str,client: httpx.AsyncClient):
match site:
case "baidu":
return (await client.get("http://localhost:5002/api/check")).json()['connected']
case "qq":
return (await client.get("http://localhost:5003/api/check")).json()['connected']
case _:
return False
实现一个并行的/api/check-sites-v2
接口,可以看到请求时间从 374ms 减少到 195ms,提升十分明显。
可以看看目前的服务间调用关系:
总结
本文介绍了如何使用 opentelemetry 和 jaeger 来实现跨服务链路追踪,以及如何使用 opentelemetry 的自动化追踪功能来实现对异步应用的链路追踪。 在真实的复杂场景中,跨服务链路追踪可以帮助我们快速定位问题,提高服务的可用性和稳定性。
相关文章
- 一个提供公告和打赏功能的 django 应用插件 django-tctip
- 定时器setTimeout和setInterval的简单应用
- 【MySQL高级】应用优化及Mysql中查询缓存优化以及Mysql内存管理及优化
- 应用Redis:实现数据库高效管理(redis实例)
- Facebook 考虑在苹果健康应用中整合 Oculus 锻炼数据
- 提高效率:借助Redis开发多线程应用(redis多线程)
- 实现群晖上MySQL新一代应用(群晖mysql应用)
- MySQL 游标应用:实现基于行级操作的完美实现(mysql游标)
- PHPMySQL实战:实现简单的动态数据库应用(phpmysql例子)
- 实现更高效率:Redis 应用二级缓存(redis二级缓存)
- 扩展MySQL中的二进制数据扩展应用(mysql二进制数据)
- LINUX网卡驱动移植:实现商业化应用(linux网卡驱动移植)
- 用Oracle虚拟机运行XP系统,方便实现多平台应用兼容(oracle虚拟机xp)
- Neo4j助力应用程序实现高效数据管理与查询(neo4j应用)
- MySQL:推动信息发展的动力(mysql 的应用)
- MSSQL的存储过程:简介与应用(mssql的存储过程包括)
- 篇mysql与java实现长篇应用:新常态的挑战(mysqljava长)
- MySQL中的接口解析实现及应用(mysql中什么是接口)
- 深度解析MySQL中全连接的应用和实现技巧(mysql 中全连接)
- Redis开发与运维实现完美的应用体验(《redis开发与运维》)
- 单机Redis的应用实现简单高效的数据存储(单机redis应用)
- Oracle数据库的持久特性DURDE的应用(oracle中durde)
- 利用Redis实现队列机制的研究与应用(redis 队列应用场景)
- oracle NS在企业中的应用之路(oracle ns)
- Oracle AQ功能实现应用间异步通信(oracle aq作用)
- 使用Redis节点实现高性能的应用(redis 节点用来干嘛)
- 联想推出 AR 眼镜 New Glass C200,瞄准商业应用|CES 2017
- JavaScript中SQL语句的应用实现
- php中error与exception的区别及应用
- jQuery复合选择器应用的几个例子