Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【node实战系列】自行实现应用缓存 #53

Open
yeyeye0525 opened this issue Jun 29, 2021 · 2 comments
Open

【node实战系列】自行实现应用缓存 #53

yeyeye0525 opened this issue Jun 29, 2021 · 2 comments

Comments

@yeyeye0525
Copy link
Contributor

yeyeye0525 commented Jun 29, 2021

file

本文首发于:https://github.com/bigo-frontend/blog/ 欢迎关注、转载。

【node实战系列】自行实现应用缓存

背景

bigo前端开始推广bff,hello农场作为首个bff落地项目,历经2个月,完成了从0-1的落地实践。

【node实战系列】按照小模块拆分,从开发者的角度讲叙,如何进行bff高可用编码。

本系列文章,基于eggjs框架编码,使用ts语法,为了提升阅读体验,建议大家先了解一下eggjs。

系列文章

  • 【node实战系列】编写一个重试装饰器
  • 【node实战系列】自行实现应用缓存
  • 【node实战系列】异步并发,自定义Promise.allSettled
  • 【node实战系列】rpc与http协议通讯
  • 【node实战系列】使用reqId跟踪请求全流程日志
  • 【node实战系列】入参校验validate
  • 【node实战系列】异常中断
  • 【node实战系列】base64编码
  • 【node实战系列】服务发现
  • 【node实战系列】编码与约定
  • 【node实战系列】监控告警
  • 【node实战系列】单元测试
  • 【node实战系列】压测
  • 【node实战系列】灰度
  • 【node实战系列】文档
  • 【node实战系列】系列小结

欢迎大家关注我们的github blog,持续更新。
https://github.com/bigo-frontend/blog/issues

缓存基本原理

缓存主要用来存放那些读写比很高、很少变化的数据,如字典信息,用户信息,组织机构信息等。应用程序读取数据时,先到缓存中读取,如果读取不到或数据已失效,再访问数据库,并将数据写入缓存。

其实我们经常接触缓存,譬如浏览器的localStorage、vue的computer、keep-alive等等。

在服务端也常用redis作为分布式缓存。

应用缓存

常用的分布式缓存包括Redis、Memcached,因为Redis提供的数据结构比较丰富且简单易用,所以Redis的使用广泛。

但是使用分布式缓存还是有一层外链调用,如果是使用应用缓存,存储在内存里,速度是最快的,我们可以把与用户信息无关的冷数据存储在应用缓存。

合理使用缓存,可以更好的改善系统性能,提高数据读取速度。

自定义应用缓存

nodejs没有提供缓存api,查阅资料发现一个缓存npm包node-cache,但是很久没人维护 了,并且缺少ts定义。

nodejs作为动态语言,对key-value天然支持,不考虑数据淘汰策略,可以考虑自己手撸一个简单应用缓存。

开撸

eggjs支持对application进行扩展,我们可以在app/extend/application.ts定义缓存

1.setCache

import { PlainObject } from "egg";
const CACHE = Symbol('Application#cache');
const CACHECOUNT = Symbol('Application#cachecount');

setCache(key: string, obj: PlainObject, expire: '1m'|'5m'|'10m'|'25m'|'1d'|'7d' = '10m'): boolean {
  try {
    if (!this[CACHE]) { // 不存在
      this[CACHECOUNT] = 0;
      this[CACHE] = {};
    }
    // 优化为计数器
    // const size = JSON.stringify(this[CACHE]).length;
    // if (size > 1024 * 1024 * 20) { // 20M
    if (this[CACHECOUNT] > 50 * 1000) { // 限制5w条数据
      console.log('超出存储限制!');
      this[CACHE] = {}; // 清空数据
      this[CACHECOUNT] = 0;
      return false;
    }
    const timeOV = {
      '1m': 60 * 1000,
      '5m': 5 * 60 * 1000,
      '10m': 10 * 60 * 1000,
      '25m': 25 * 60 * 1000,
      '1d': 24 * 60 * 60 * 1000,
      '7d': 7 * 24 * 60 * 60 * 1000,
    };
    this[CACHE][key] = JSON.stringify({
      expire: Date.now() + timeOV[expire],
      ...obj,
    });
    this[CACHECOUNT]++;
    console.log('设置缓存数据成功');
    return true;
  } catch (error) {
    return false;
  }
}

2.getCache

/**
  * 获取本地缓存
  *
  * @param {*} key 缓存key
  * @returns
  */
getCache(key: string) {
  if (!this[CACHE] || !this[CACHE][key]) { // 不存在
    return null;
  }
  try {
    const data = JSON.parse(this[CACHE][key]);
    if (Date.now() - data.expire > 0) { // 已过期
      this[CACHE][key] = '{}'; // 清空数据
      this[CACHECOUNT]--;
      return null;
    }
    delete data.expire; // 不返回过期时间
    console.log('获取缓存数据成功');
    return data;
  } catch (error) {
    return null;
  }
}

3.cleanCache

cleanCache(key) {
  if (!this[CACHE]) { // 不存在
    return false;
  }
  delete this[CACHE][key];
  this[CACHECOUNT]--;
  return true;
},
cleanCacheAll() {
  if (!this[CACHE]) { // 不存在
    return false;
  }
  this[CACHE] = {};
  this[CACHECOUNT] = 0;
  return true;
}

业务使用示例

const {ctx} = this;
let res: HttpRes<LevelAwardRes>;
const cacheData = ctx.app.getCache(EcacheKey.GETLEVELAWARDINFO); 
if (cacheData) { // 获取缓存
  res = cacheData;
} else {
  res = await this.request();
  if (res.code === 0) { // 设置缓存时间为5分钟
    ctx.app.setCache(EcacheKey.GETLEVELAWARDINFO, res, '5m');
  }
}
const {code, message, data} = res;
if (code !== 0) {
  this.ctx.throwErr(message, code, data);
}

小结

综上,一个简单又实用的缓存功能就实现了。

数据访问通常遵循二八定律,即80%的访问落在20%的数据上,因此利用应用缓存的内存髙速访问特性,将这20%的数据缓存起来,可很好地改善系统性能,提高数据读取速度,降低存储访问压力。希望大家做好高可用实践,给用户带来顺滑体验。

欢迎大家留言讨论,祝工作顺利、生活愉快!

我是bigo前端,下期见。

@webjohnjiang
Copy link

计数器来限制5w数据量会不会没办法防止某个key的size超大的情况。感觉这个库也能适用这种场景:https://www.npmjs.com/package/lru-cache

@yeyeye0525
Copy link
Contributor Author

计数器来限制5w数据量会不会没办法防止某个key的size超大的情况。感觉这个库也能适用这种场景:https://www.npmjs.com/package/lru-cache

平均一条数据1kb,5w条数据不会超过50M,当然如果size很大,就要考虑是否适用缓存,一般业务场景不会存在这种情况。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants