最近公司某个项目涉及到了视频直播的模块,看了网上许多案例都是基于rtmp或hls的方案进行直播的,考虑到日后chrome不再支持flash,于是试着结合MSE和node中间层进行直播流的推送和解析操作,其中就涉及到了node操作redis的环节。
一般来说,无论服务端还是中间层,针对mysql和mongoDB这种数据库都会使用ORM进行操作,redis也一样,因此在开始使用redis之前,我们首先应该下载好一个提供ORM操作层的第三方包,我这里选择了ioredis这个库:(当然,前提是本地已经安装好了redis并且成功开启,具体教程这里就不阐述了)
npm install ioredis
编写配置文件是为了免去每次进行数据库操作或连接数据库都重写port、host等参数的麻烦,而且尽可能地保持“一库一连”,即全局连接一次,处处都运用这个实例进行读写,这样可以最大限度保证性能。
本人的redis工具配置目录如下:
--redis
|--redisConnection.ts //连接逻辑
|--redisOperate.ts //数据库操作逻辑
考虑到生产与开发的环境不同,连接redis的端口和地址也不同,建议为两个环境各自写一个config。
redisConnection.ts
内容如下:
import * as ioredis from 'ioredis';
import { resolve } from 'dns';
interface redisConfig {
port:number,
host:string,
password?:string
db?:number //连接的数据库id,默认为0,即第一个数据库
family?:number //可填参数:4、6 (ipv4\ipv6)
}
let clientCreate = (config:redisConfig,callback_:Function) => {
let redis:ioredis.Redis = new ioredis(config);
redis.on('connect',()=>{ //根据 connect 事件判断连接成功
callback_(null,redis) //链接成功, 返回 redis 连接对象
})
redis.on('error',(err)=>{ //根据 error 事件判断连接失败
callback_(err,null) //捕捉异常, 返回 error
})
}
let redisConn = (options?:redisConfig) => {
let config = options
return new Promise((resolve,reject) => { //返回API调用方 一个 promise 对象
clientCreate(config,(err:any,conn:ioredis.Redis) => {
if(err) {
reject(err)
}
resolve(conn) //返回连接的redis对象
})
})
}
export default redisConn
这样就完成了一个连接redis的通用实例了,这样做的好处是可操作空间大,也利于后续封装
由于ioredis这个第三方库的很多操作返回都是Promise
对象,如果直接在项目里使用很容易会造成回调过多的情况,因此封装成async/await
的方式会更利于开发,那么我们可以这样来编写redisOperate.ts
文件:
import redisConnect from './redisConnection'
import * as ioredis from 'ioredis'
import getConfig from '../../config/index';
export interface redisTool {
setString(key:string,value:any):Promise<string|void>
getString(key:any):Promise<string>
delString(key:string):Promise<number|null>
getDbSize():Promise<number>
connToRedis():Promise<unknown>
}
interface redisConfig {
port:number,
host:string,
password?:string
db?:number
family?:number
}
const env = process.env.NODE_ENV;
let redisConfig:redisConfig = {
port: getConfig(env).port_redis,
host: getConfig(env).host_redis
}
class RedisTool implements redisTool {
redis:ioredis.Redis
config:redisConfig
constructor(opt?:any){
this.redis = null;
if(opt){
this.config = Object.assign(redisConfig,opt)
}else{
this.config = redisConfig
}
// this.connToRedis()
this.connToRedis().then(res=>{
if(res){
console.log('redis connet success')
}
}).catch(e=>{
console.error('The Redis Can not Connect:'+e)
})
}
/**连接redis */
connToRedis(){
return new Promise((resolve, reject) => {
if(this.redis) {
resolve(true) //已创建
} else {
redisConnect(this.config).then((resp:ioredis.Redis) => {
this.redis = resp
resolve(true) }
).catch(err => { reject(err) })
}
})
}
/**存储string类型的key-value */
async setString(key:string,value:any){
let val:string = typeof(value)!=='string'?JSON.stringify(value):value;
let k:string = typeof(value)!=='string'?JSON.stringify(key):key;
try {
const res = await this.redis.set(k, val);
return res;
}
catch (e) {
console.error(e);
}
}
/**获取string类型的key-value */
async getString(key:any){
let id:string = typeof(key)!=='string'?JSON.stringify(key):key;
try{
const res = await this.redis.get(id);
return res
}catch(e){
console.error(e);
return null
}
}
/**删除string类型的key-value */
async delString(key:string){
let id:string = typeof(key)!=='string'?JSON.stringify(key):key;
try{
const res = await this.redis.del(id);
return res
}catch(e){
console.error(e);
return null
}
}
/**获取当前数据库key的数量 */
async getDbSize(){
try{
const res = await this.redis.dbsize();
return res
}catch(e){
console.error(e);
return null
}
}
}
export const default_redis = new RedisTool();
/*
需要用到多少个数据库,就定义多少个实例常量,这样的好处是:
每次个模块调用redis的时候,始终是取第一次生成的实例,避免了多次连接redis的尴尬
*/
export const redis_db1 = new RedisTool({db:1})
// export const redis_db2 = new RedisTool({db:2})
// export const redis_db3 = new RedisTool({db:3})
// export const redis_db4 = new RedisTool({db:4})
export default default_redis
当我们把所有redis
配置和操作层逻辑都封装好之后,就可以在任意需要用到的redis直接使用生成的实例了,比如在某一模块下进行读写操作:
import {default_redis} from '../../utils/redis/redisOperate'
//引入我们编写好的操作逻辑文件,导出我们提前生成好的redis实例
let redisTool_db0 = default_redis
const test = async (str:string):Promise<void> => {
let key:string = str;
let res:string = await redisTool_db.setString(key,'这是存入的value')
if(res!==null){console.log('写入成功')}
else{console.log('写入失败')}
let res2:string = await redisTool_db.getString(key)
if(res2!==null){console.log('读取成功,value是:'+ res2)}
else{console.log('读取失败')}
}
test('hello')
//打印
//写入成功
//读取成功,value是:这是存入的value