node 异步存储 AsyncLocalStorage

产生背景

  • 日志追踪

  • 当一个 Request 通过层层关卡到 Server,一般会产生多个系统的日志,包括但不限于:访问日志、异常日志、SQL日志、第三方服务日志等,而当发生了线上问题的时候,需要进行溯源排查。一般的做法是在请求之处,生成一个 unique traceId,此 id 在所有日志中携带就可以追踪该请求的所有链路,这就是所谓的全链路日志追踪

解决方案

全局变量

// Raw Node.js HTTP server
const http = require('http');
let globalTraceId // 全局变量

// 0. 处理请求的方法
function handleRequest(req, res) {
  // 生成唯一 traceId,每次请求进入,复写 globalTraceId 的值
  globalTraceId = generateTraceId()

  // 检查用户cookie是否有效
  cookieValidator().then((res) => {
  	// 异步操作
  }).catch((err) => {
    // 把 traceId 连同 error 上报给异常监控系统
    reportError(err, globalTraceId)

    // 写状态码500和错误信息等
    // ...
  });
}

// 1. 创建 server 
const server = http.createServer((req, res) => {
	handleRequest(req, res)
});

// 2. 让 server 和 port:3000 建立 socket 链接,持续接收端口信息
server.listen(3000, () => {
  console.log('Server listening on port 3000');
});
  • 缺点:因为是全局变量,通过异步上报 globalTraceId 时,存在下一次请求更改 globalTraceId,导致数据不一致的问题

通过参数传递

  • 能解决异步导致数据不一致的问题
const http = require('http');

function handleRequest(req, res) {
  const traceId = req.headers['x-trace-id'] || generateTraceId();
  // 把 traceId 写入 req 这个 object,将参数一路带下去
  req.traceId = traceId;

  // 同上
  cookieValidator().then((result) => {
    // 校验成功,返回给用户他需要的内容
  	// ...
  }).catch((err) => {
    // 上报 traceId
    reportError(err, req.traceId)

    // 写状态码500和错误信息等
    // ...
  });
}

function cookieValidator() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // do someting
      // ...
    }, 1000);
  });
}

// ...

依赖注入

  • 具体查看 next.js,底层使用了 AsyncLocalStorage

AsyncLocalStorage

  • AsyncLocalStorage 是基于 node:async_hooks 实现的,并且(比之其他方法)是性能好、内存安全的方法,允许在 Web 请求的整个生命周期或任何其他异步持续时间内存储数据
  • 详情
// How to use AsyncLocalStorage in Node.js
import http from 'node:http';
import { AsyncLocalStorage } from 'node:async_hooks';

const asyncLocalStorage = new AsyncLocalStorage();

let traceId = 0;
http.createServer((req, res) => {
  // 关键的API调用
  asyncLocalStorage.run(traceId++, () => {
	const traceId = asyncLocalStorage.getStore();
	console.log(`${traceId}:`, 'start');
    // Imagine any chain of async operations here
    setImmediate(() => {
	  const traceId = asyncLocalStorage.getStore();
	  console.log(`${traceId}:`, 'finish');
      res.end();
    });
  });
}).listen(8080);

// 并行发送两次异步请求
http.get('http://localhost:8080');
http.get('http://localhost:8080');
// Prints:
//   0: start
//   1: start
//   0: finish
//   1: finish

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值