本文首发于:github.com/bigo-fronte… 欢迎关注、转载。
背景
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,持续更新。
github.com/bigo-fronte…
缓存基本原理
缓存主要用来存放那些读写比很高、很少变化的数据,如字典信息,用户信息,组织机构信息等。应用程序读取数据时,先到缓存中读取,如果读取不到或数据已失效,再访问数据库,并将数据写入缓存。
其实我们经常接触缓存,譬如浏览器的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前端,下期见。