在 express 项目中接入 OpenTelemetry

高勇
2025-06-22 05:16
阅读 509

技术探索,不止是写代码

技术探索,不止是写代码

记得刚入行那会儿,我满脑子都是“我要成为一个技术大牛”。当时觉得,只要能把代码写得又快又好,能看懂各种算法和设计模式,就能在技术这条路上走得远。但工作几年之后我发现,真正的技术成长,远远不只是敲代码这么简单。

这篇文章我想从一个真实项目经历讲起,谈谈我在技术探索与实践中的一些感受和思考。希望它能给正在这条路上前行的你一些启发,或者至少让你觉得:原来大家都一样,也曾踩过坑、也曾在深夜里怀疑自己。


项目背景:一场线上突发故障

去年年底,我在一家做在线教育的公司负责后端架构优化。我们的主平台是一个基于 Node.js 的服务集群,承载了数万并发用户的访问流量。那天晚上十点左右,突然收到报警:用户登录接口响应时间暴增到几秒甚至超时,前端报错一片。

我们第一时间查看日志,发现数据库连接池被打满了。进一步分析后发现问题出在一个新上线的功能模块——为了提升用户体验,我们在用户首次登录时新增了一个数据预加载机制,提前把后续要用的数据拉取好缓存起来。

按说这个逻辑是没问题的,但问题在于,这个预加载操作是串行执行的。也就是说,用户登录请求来了以后,必须等所有预加载任务完成才能返回响应。这导致每个登录请求处理时间变长,进而拖慢整个系统响应速度,最终形成连锁反应。


挑战来了:如何在不影响业务的前提下快速修复?

这个问题看似简单,实则牵一发而动全身:

  • 如果直接回滚版本,虽然能解决当前问题,但会影响用户体验;
  • 如果只是去掉预加载部分,那相当于牺牲了原本的设计初衷;
  • 更重要的是,我们需要搞清楚:为什么这个改动会引发严重性能问题?是系统承受不了并发压力,还是代码结构存在隐患?

我花了一晚上的时间分析整个调用链路,发现有几个关键问题:

  1. 没有异步处理机制:预加载全部是同步请求,严重影响主线程响应速度;
  2. 缺乏熔断与限流策略:当某个外部服务响应慢或不可用时,没有自动降级机制;
  3. 监控体系不完善:虽然有监控大盘,但对具体接口调用耗时和内部链路追踪覆盖不足。

解决方案:技术不是孤立的工具,而是协作的艺术

第二天早上,我和团队召开了紧急会议,决定采取以下几个措施:

1. 引入异步任务队列

我们将原本在登录过程中触发的预加载逻辑,改为通过消息队列(MQ)异步处理。这样用户的登录流程可以在主线程中快速完成,具体的预加载任务由独立的工作进程来执行。

// 原始同步预加载(伪代码)
async function login(req) {
    const user = await db.getUser(req.body);
    const data1 = await loadResourceA(); // 资源 A
    const data2 = await loadResourceB(); // 资源 B
    await cacheDataToRedis(data1, data2); // 缓存资源
    return res.send({ user });
}


![技术原理图-1](https://code-guide.oss.shanghai.autogptai.club/common/file/download?name=date2025062205/d8a6716a-2bdf-4b3a-b5a7-e103c794fd7f.jpg)


// 改进后的异步方式(使用 RabbitMQ 示例)
const amqp = require('amqplib');

async function login(req) {
    const user = await db.getUser(req.body);

    const conn = await amqp.connect('amqp://localhost');
    const ch = await conn.createChannel();
    await ch.assertQueue('preload_queue');

    ch.sendToQueue('preload_queue', Buffer.from(JSON.stringify({
        userId: user.id,
        timestamp: Date.now()
    })));

    return res.send({ user });
}

这里省略了错误处理和持久化细节,实际中需要加入重试、死信队列等机制。

2. 加入熔断与限流机制

我们引入了 Resilience4JS 和 Express 中间件组合来为关键接口添加容错能力。比如设置接口每秒最多 5000 个请求,并在失败率达到一定阈值时自动进入熔断状态。

const RateLimiter = require('express-rate-limit');

const apiLimiter = RateLimiter({
  windowMs: 60 * 1000, // 1 minute
  max: 5000,
  message: 'Too many requests from this IP.'
});

app.use('/api', apiLimiter);

同时在异步任务处理层加入熔断器:

const { CircuitBreaker } = require('@agile-ts/circuit-breaker');

const cb = new CircuitBreaker(() => fetchExternalResource(), {
  timeout: 3000,
  errorThresholdPercentage: 50
});

3. 完善监控体系

我们在原有的 Prometheus + Grafana 监控基础上,增加了分布式追踪组件 Zipkin,用于记录完整的请求链路。

service:
  name: user-service
exporters:
  zipkin:
    endpoint: http://zipkin:9411/api/v2/spans

这样我们就能直观地看到每个接口的调用时间、异常情况、以及链路依赖关系。


开发过程中的几个坑

说实话,改完这些看起来挺顺的,但在落地过程中我们也踩了不少坑:

坑一:本地调试 MQ 不通

我们刚开始在本地测试消息队列的时候,发现 RabbitMQ 总是连不上。一开始以为是代码写错了,结果折腾了半天才发现是 Docker 容器的网络配置出了问题。后来统一用 Docker Compose 启动整套环境才解决。

教训: 环境一致性很重要,尽量用容器化工具保持本地和线上一致。

坑二:异步任务堆积

上线初期有一段时间,预加载任务积压了很多未被消费的消息,导致 Redis 缓存迟迟没生成。最后查出来是因为工作进程太少了,而且其中一个节点 CPU 被占满后无法继续处理。

解决方案:

  • 扩容消费节点;
  • 使用 Kubernetes 做弹性扩缩容;
  • 给工作进程加健康检查,有问题自动重启。

坑三:监控埋点漏掉关键字段

有一段时间我们看不到完整调用链,后来发现是服务之间传递 Trace ID 的时候格式有问题。虽然用了标准库,但因为中间夹杂了部分非 HTTP 请求(比如 Kafka),导致上下文没正确透传。

经验总结: 分布式系统里,跨协议追踪是个难点,要尽早规划好全链路打通的方式。


效果与收益:稳了!

这一轮整改上线之后,系统稳定性明显上升:

指标 上线前 上线后
登录接口平均响应时间 1800ms 220ms
登录超时率 12% <0.5%
数据预加载完成率 - >98%
报警频率 高频 几乎归零

更难得的是,这次改进也为后续的技术演进打下了基础:

  • 我们开始推动微服务拆分;
  • 异步任务中心成为公共能力模块;
  • 全链路追踪成为每个新服务的标准配置项。

我的经验分享:技术成长离不开三点

经历过这一次项目之后,我对“技术探索与实践”有了更深的认识,这里想跟大家分享几点自己的体会:

1. 不要只盯着代码本身

很多时候,技术瓶颈并不是写不好某段逻辑,而是整体系统设计不合理。代码只是工具,系统的可维护性、可扩展性、可观测性才是真正的挑战。

2. 多看日志,少拍脑袋

遇到问题先别急着改代码,先把日志、监控、调用链理清楚。有时候你以为的问题根本不是问题根源所在。

3. 学会权衡与妥协

理想主义谁都想过,但现实中往往需要做出让步。比如为了尽快止损选择临时降级策略,而不是马上重构整个模块。这是技术人的成熟表现。

4. 持续学习比一次性掌握更重要

现在技术更新太快,很多知识学完两年就过时了。真正有用的,是那种面对新技术能快速上手、理解其核心原理的能力。

5. 技术之外也要懂业务

那次事故之所以会发生,是因为我们没有充分评估预加载功能对系统整体的影响。技术是服务于业务的,脱离业务谈技术就是耍流氓。


最后的小感悟:写代码的人要有敬畏之心

有时候我觉得,写代码就像种树。前期你可以随便挖个坑就埋下去,但如果根基不牢,风一大就倒了。技术探索也是如此——我们要做的不只是堆叠炫酷的新技术,更要关注它们是否适合当前的土壤和气候。

每次写完一段代码,我都习惯性地问自己:“这段代码会不会让人半夜爬起来排查?”如果答案是肯定的,那就要重新考虑设计思路了。

技术探索从来都不是一个人的战场,也不是一蹴而就的事情。希望我们在不断探索的路上,都能保持一份热爱、一份耐心,也保持一点谦卑。

如果你也正走在技术的成长之路上,愿我们共勉!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝