Egg.js中如何缓存数据-多进程数据共享

avatar
前端开发 @天鹅到家

image.png

1. 问题描述

在使用 Egg.js 作为后端接口服务,做 Login with Twitter 功能的时候,需要把 Twitter OAth 认证获取到的oauth_token 以及 oauth_token_secret进行缓存,在进行 Twitter 接口调用回调的时候需要用于oauth_access_token以及oauth_access_token_secret获取的参数。

无论是使用node-cache进行数据的缓存,还是用一个对象临时存储数据到内存中,当在回调接口里去获取oauth_token_secret的时候,均获取不到!

2. 问题定位

当写两个接口,一个用于固定数据的缓存,一个接口用于获取缓存数据并输出日志,测试结果为一次请求获取不到数据,一次请求能获取到数据!

在本地测试时,无论如何访问都能够获取到数据!

从测试结果现象来看,一次请求获取不到数据,一次请求能获取到数据!,说明服务一定是存在集群化部署的。

通过官方文档,找到了关于多进程模型和进程间通讯的描述说明。

单个 Node.js 实例在单线程环境下运行。为了更好地利用多核环境,用户有时希望启动一批 Node.js 进程用于加载。 集群化模块使得你很方便地创建子进程,以便于在服务端口之间共享。

由于框架的多进程模型,我们存储在内存中的数据在不同进程中并不是共享的。这就导致了一次请求能获取数据,一次请求获取不到数据。

3. 解决方案

既然了解了问题的根源,那解决问题就简单了。

两种问题解决方案:

  • 实现进程之间的数据共享
  • 持久化数据,而不是存储在内存中,每次访问的时候都重新查询

此处我们主要实现的是进程之间的数据共享。

3.1 Egg 服务创建

$ npm init egg

3.2 添加设置缓存的 Service

创建 app/srvice/cache.js文件,并写入如下内容:

const NodeCache = require('node-cache');
const cache = new NodeCache();
module.exports = {
  get cache() {
    return cache;
  },
  set(key, value, ttl) {
    cache.set(key, value, ttl || 15 * 60);
  },
};

此处使用的是node-cache进行数据缓存的管理。

npm install node-cache --save

3.3 多进程之间缓存数据的更新

创建 app/app.js文件,并写入如下内容:

module.exports = app => {
  app.messenger.on('cache_set', data => {
    const ctx = app.createAnonymousContext();
    ctx.runInBackground(async () => {
      await ctx.service.cache.set(data.key, data.value, data.ttl);
    });
  });
};

app.js 的代码会执行在 Worker 进程上,他们通过框架封装的 messenger 对象进行进程间通讯(IPC)。

oauth_token以及oauth_token_secret存储:

controller的方法里,我们使用ctx.app.messenger.sendToApp来进行消息的发送,发送给所有的 app 进程。

async index() {
    const { ctx } = this;
    const oauth_token = 'QThhDwAAAAABn36WAAASDFSDFFV';
    const oauth_token_secret = 'EI8MYe1AJexCVgWgxqweqwetQWEDGT';
    ctx.app.messenger.sendToApp('cache_set', {
      key: oauth_token,
      value: { oauth_token_secret },
    });
    ctx.body = 'hi, egg';
  }

获取缓存数据:

通过ctx.service.cache来进行缓存数据的获取。

async testCache() {
    const { ctx } = this;
    const myCache = ctx.service.cache.cache;
    const oauth_token = 'QThhDwAAAAABn36WAAASDFSDFFV';
    const { oauth_token_secret } = myCache.get(oauth_token) || {};
    ctx.body = {
      code: 0,
      message: 'SUCCESS'
    };
  }

至此,我们便能够实现了在多进程之间进行数据的共享!