首页 > 解决方案 > 为什么在 ORM 挂钩级别访问时,我的 Cls 挂钩当前上下文与以前的上下文相同?

问题描述

我编写了几个微服务,我想在每个请求中将一些用户上下文从一个服务传递到另一个服务,依此类推到下一个。我的目标是在任何服务中存在数据库写入操作的每个地方都使用这个设置的上下文(我基本上想在任何 API 中存储负责在数据库中进行更改的用户)并且我想让它毫不费力所以我尝试编写我在 ORM 钩子中的逻辑(对 MongoDb 使用 Mongoose,对 MySql 使用 Sequelize)。我正在使用 cls-hooked 在服务中传递此用户上下文。

我所做的是,每当一个服务调用另一个服务时,我都会通过中间件将一些用户上下文附加到请求中。现在在下一个服务中,我读取了这些变量并设置了我的上下文。namespace.get在这一点上,只要我在同一个异步上下文中,也就是在同一个 http 请求流中,我希望在我调用我的函数的任何地方获得该设置值。这在大部分情况下都成立,但随着我达到 ORM 级别而失败。

所以我的代码是这样写的,API Controller -> Some utility that make a DB call -> DML -> DBA -> ORM

ORM 进一步称它为钩子,我在其中访问设置的上下文ORM -> ORM Hooks

如果我尝试在这些级别中的任何一个级别访问我的上下文直到 DBA 级别,我会得到准确的值,但是如果我尝试访问我在 ORM 挂钩中设置的上下文,我会得到错误的值(观察到是来自先前上下文的值)。我没有运气调试这个,我什至没有预感为什么会发生这种情况。

我的中间件实现是这样的。

const myMiddleware = () => {
  return function (req, res, next) {
    const sessionContext = cls.getNamespace('user_context');
    sessionContext.bindEmitter(req);
    sessionContext.bindEmitter(res);
    sessionContext.run(function () {
      const contextKeys = ["user_id", "user_name"];

      for (const key of contextKeys) {
        const value = _.get(req, `query.${key}`, 'system');
        sessionContext.set(key, value);
      }

      next();
    })
  }
};

它在控制到达 API 控制器之前被执行。

我编写的用于从上下文访问设置值的代码是这样的。

mySqlModel.addHook('afterCreate', async (instance, options) => {
        try {
            const userId = UserContext.get("user_id");
            const userName = UserContext.get("user_name");
            // utilise them here
        } catch (err) {
             console.error(err);
        }
    });

我在这里附上了我的测试运行的一些日志,这些日志支持我上面提到的观察结果。我还为每个请求附加了一个唯一的 ID,以便在我使用同一用户发出所有请求时使其可区分。

DML:: { _ns_name: 'user_context',
id: 233, // async context ID
user_id: 'johnwick', 
request_id: 'c0b2e4105a562be4c33ef20291cbf286'
}
DBA:: { _ns_name: 'user_context',
id: 233,
user_id: 'johnwick', 
request_id: 'c0b2e4105a562be4c33ef20291cbf286'
}
Hooks:: { _ns_name: 'user_context',
id: 233,
user_id: 'johnwick', 
request_id: 'c0b2e4105a562be4c33ef20291cbf286'
}

DML:: { _ns_name: 'user_context',
id: 565,
user_id: 'johnwick', 
request_id: '54a290e131af1696814bfe1bfb077b1c'
}
DBA:: { _ns_name: 'user_context',
id: 565,
user_id: 'johnwick', 
request_id: '54a290e131af1696814bfe1bfb077b1c'
}
Hooks:: { _ns_name: 'user_context',
id: 233,
user_id: 'johnwick', 
request_id: 'c0b2e4105a562be4c33ef20291cbf286'
}

DML:: { _ns_name: 'user_context',
id: 780,
user_id: 'johnwick', 
request_id: '3fd4e67a6eb633898db2bce8dbc83416'
}
DBA::  johnwick 3fd4e67a6eb633898db2bce8dbc83416 { _ns_name: 'user_context',
id: 780,
user_id: 'johnwick', 
request_id: '3fd4e67a6eb633898db2bce8dbc83416'
}
Hooks:: { _ns_name: 'user_context',
id: 233,
user_id: 'johnwick',
request_id: 'c0b2e4105a562be4c33ef20291cbf286'
}

DML:: { _ns_name: 'user_context',
id: 1134,
user_id: 'johnwick', 
request_id: '748e8b06d93e560ff15486d16a669342'
}
DBA:: { _ns_name: 'user_context',
id: 1134,
user_id: 'johnwick', 
request_id: '748e8b06d93e560ff15486d16a669342'
}
Hooks:: { _ns_name: 'user_context',
id: 233,
user_id: 'johnwick', 
request_id: 'c0b2e4105a562be4c33ef20291cbf286'
}




DML:: { _ns_name: 'user_context',
id: 2087,
user_id: 'johnwick',
request_id: '19898d61be3b7c3c3831266654b9e161'
}
DBA:: { _ns_name: 'user_context',
id: 2087,
user_id: 'johnwick', 
request_id: '19898d61be3b7c3c3831266654b9e161'
}
Hooks:: { _ns_name: 'user_context',
id: 2087,
user_id: 'johnwick', 
request_id: '19898d61be3b7c3c3831266654b9e161'
}


DML:: { _ns_name: 'user_context',
id: 2397,
user_id: 'johnwick', 
request_id: 'b47fb609a0c837f76e32bf6c1872389b'
}
DBA:: { _ns_name: 'user_context',
id: 2397,
user_id: 'johnwick', 
request_id: 'b47fb609a0c837f76e32bf6c1872389b'
}
Hooks:: { _ns_name: 'user_context',
id: 2087,
user_id: 'johnwick', 
request_id: '19898d61be3b7c3c3831266654b9e161'
}

注意request_idORM 钩子的内部。可悲的是,它大部分时间都会中断。我不明白为什么这适用于那些极少数的请求,但在到达 ORM 挂钩之前,它似乎适用于所有请求。

任何帮助将不胜感激。提前致谢 :)

标签: node.jssequelize.jsevent-loopthread-local-storagecls

解决方案


推荐阅读