欧美亚洲中文,在线国自产视频,欧洲一区在线观看视频,亚洲综合中文字幕在线观看

      1. <dfn id="rfwes"></dfn>
          <object id="rfwes"></object>
        1. 站長(zhǎng)資訊網(wǎng)
          最全最豐富的資訊網(wǎng)站

          Node.js中怎么使用Redis?原來(lái)這么簡(jiǎn)單!

          Node中怎么使用Redis?下面本篇文章給大家介紹一下Node.js中使用Redis的方法,你會(huì)發(fā)現(xiàn)原來(lái)這么簡(jiǎn)單,希望對(duì)大家有所幫助!

          Node.js中怎么使用Redis?原來(lái)這么簡(jiǎn)單!

          node.js極速入門課程:進(jìn)入學(xué)習(xí)

          之前的文章中我們其實(shí)留了兩個(gè)可以用redis優(yōu)化的地方:

          • 一個(gè)是我們的在做登錄時(shí),通過(guò)JWT已經(jīng)實(shí)現(xiàn)了服務(wù)端生成token以及驗(yàn)證客戶端發(fā)送的token信息?!鞠嚓P(guān)教程推薦:nodejs視頻教程 、編程視頻】
          • 實(shí)現(xiàn)對(duì)文章點(diǎn)贊功能,采用的是將點(diǎn)贊數(shù)據(jù)直接寫入數(shù)據(jù)庫(kù)

          JWT token 實(shí)現(xiàn)方式, 將基本信息直接放在token中,以便于分布式系統(tǒng)使用, 但是我們沒有設(shè)置有限期(這個(gè)是可以實(shí)現(xiàn)的),并且服務(wù)端無(wú)法主動(dòng)讓token失效。 而Redis天然支持過(guò)期時(shí)間,也能實(shí)現(xiàn)讓服務(wù)端主動(dòng)使token過(guò)期。

          當(dāng)然并不是說(shuō)JWT token 不如 redis+token實(shí)現(xiàn)方案好, 具體看使用的場(chǎng)景,這里我們并不討論二者孰優(yōu)孰劣,只是提供一種實(shí)現(xiàn)方案,讓大家知道如何實(shí)現(xiàn)。

          1. 認(rèn)識(shí)redis

          對(duì)于前端的小伙伴來(lái)說(shuō),Redis可能相對(duì)比較陌生,首先認(rèn)識(shí)一下

          Redis是什么

          Redis是一個(gè)開源(BSD許可)的,基于內(nèi)存的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)系統(tǒng),它可以用作數(shù)據(jù)庫(kù)、緩存和消息中間件,是現(xiàn)在最受歡迎的 NoSQL 數(shù)據(jù)庫(kù)之一。

          其具備如下特性:

          • 速度快
            • 單節(jié)點(diǎn)讀110000次/s,寫81000次/s
            • 基于內(nèi)存運(yùn)行,性能高效
            • 用 C 語(yǔ)言實(shí)現(xiàn),離操作系統(tǒng)更近
          • 持久化
            • 數(shù)據(jù)的更新將異步地保存到硬盤(RDB 和 AOF
          • 多種數(shù)據(jù)結(jié)構(gòu)
            • 不僅僅支持簡(jiǎn)單的 key-value 類型數(shù)據(jù)
            • 還支持:字符串、hash、列表、集合、有序集合
          • 支持多種編程語(yǔ)言等等

          Redis 典型使用場(chǎng)景

          緩存

          緩存可以說(shuō)是Redis最常用的功能之一了, 合理的緩存不僅可以加快訪問(wèn)的速度,也可以減少后端數(shù)據(jù)庫(kù)的壓力。

          排行系統(tǒng)

          利用Redis的列表和有序集合的特點(diǎn),可以制作排行榜系統(tǒng),而排行榜系統(tǒng)目前在商城類、新聞?lì)?、博客類等等,都是比不可缺的?/p>

          計(jì)數(shù)器應(yīng)用

          計(jì)數(shù)器的應(yīng)用基本和排行榜系統(tǒng)一樣,都是多數(shù)網(wǎng)站的普遍需求,如視頻網(wǎng)站的播放計(jì)數(shù),電商網(wǎng)站的瀏覽數(shù)等等,但這些數(shù)量一般比較龐大,如果存到關(guān)系型數(shù)據(jù)庫(kù),對(duì)MySQL或者其他關(guān)系型數(shù)據(jù)庫(kù)的挑戰(zhàn)還是很大的,而Redis基本可以說(shuō)是天然支持計(jì)數(shù)器應(yīng)用。

          (視頻直播)消息彈幕

          直播間的在線用戶列表,禮物排行榜,彈幕消息等信息,都適合使用Redis中的SortedSet結(jié)構(gòu)進(jìn)行存儲(chǔ)。

          例如彈幕消息,可使用ZREVRANGEBYSCORE排序返回,在Redis5.0中,新增了zpopmax,zpopmin命令,更加方便消息處理。

          Redis的應(yīng)用場(chǎng)景遠(yuǎn)不止這些,Redis對(duì)傳統(tǒng)磁盤數(shù)據(jù)庫(kù)是一個(gè)重要的補(bǔ)充,是支持高并發(fā)訪問(wèn)的互聯(lián)網(wǎng)應(yīng)用必不可少的基礎(chǔ)服務(wù)之一。

          紙上談兵終覺淺,必須實(shí)戰(zhàn)一波~

          Redis的安裝和簡(jiǎn)單使用,我這里就不一一介紹了,這里貼上我之前寫的兩篇文章:

          • Redis 安裝
          • Redis入門篇-基礎(chǔ)使用

          可以快速的安裝、了解Redis數(shù)據(jù)類型以及常用的命令。

          可視化客戶端

          在Windows下使用 RedisClient, 在mac下可以使用Redis Desktop Manager

          RedisClient下載鏈接:https://github.com/caoxinyu/RedisClient

          下載后直接雙擊redisclient-win32.x86.2.0.exe文件運(yùn)行即可

          Node.js中怎么使用Redis?原來(lái)這么簡(jiǎn)單!

          啟動(dòng)后, 點(diǎn)擊server -> add

          Node.js中怎么使用Redis?原來(lái)這么簡(jiǎn)單!

          連接后就可以看到總體情況了:

          Node.js中怎么使用Redis?原來(lái)這么簡(jiǎn)單!

          與SQL型數(shù)據(jù)不同,redis沒有提供新建數(shù)據(jù)庫(kù)的操作,因?yàn)樗詭Я?6(0-15)個(gè)數(shù)據(jù)庫(kù)(默認(rèn)使用0庫(kù))。在同一個(gè)庫(kù)中,key是唯一存在的、不允許重復(fù)的,它就像一把“密鑰”,只能打開一把“鎖”。鍵值存儲(chǔ)的本質(zhì)就是使用key來(lái)標(biāo)識(shí)value,當(dāng)想要檢索value時(shí),必須使用與value對(duì)應(yīng)的key進(jìn)行查找.

          Redis認(rèn)識(shí)作為文章前置條件,到這里及結(jié)束了, 接下來(lái)進(jìn)入正題~

          本文主要使用Redis實(shí)現(xiàn)緩存功能。

          2. 在Nest.js中使用

          版本情況:

          庫(kù) 版本
          Nest.js V8.1.2

          項(xiàng)目是基于Nest.js 8.x版本,與Nest.js 9.x版本使用有所不同, 后面的文章專門整理了兩個(gè)版本使用不同點(diǎn)的說(shuō)明, 以及如何從V8升級(jí)到V9, 這里就不過(guò)多討論。

          首先,我們?cè)贜est.js項(xiàng)目中連接Redis, 連接Redis需要的參數(shù):

          REDIS_HOST:Redis 域名 REDIS_PORT:Redis 端口號(hào) REDIS_DB: Redis 數(shù)據(jù)庫(kù) REDIS_PASSPORT:Redis 設(shè)置的密碼
          登錄后復(fù)制

          將參數(shù)寫入.env.env.prod配置文件中:

          Node.js中怎么使用Redis?原來(lái)這么簡(jiǎn)單!

          使用Nest官方推薦的方法,只需要簡(jiǎn)單的3個(gè)步驟:

          1、引入依賴文件

          npm install cache-manager --save npm install cache-manager-redis-store --save npm install @types/cache-manager -D
          登錄后復(fù)制

          Nest為各種緩存存儲(chǔ)提供統(tǒng)一的API,內(nèi)置的是內(nèi)存中的數(shù)據(jù)存儲(chǔ),但是也可使用 cache-manager來(lái)使用其他方案, 比如使用Redis來(lái)緩存。

          為了啟用緩存, 導(dǎo)入ConfigModule, 并調(diào)用register()或者registerAsync()傳入響應(yīng)的配置參數(shù)。

          2、創(chuàng)建module文件src/db/redis-cache.module.ts, 實(shí)現(xiàn)如下:

          import { ConfigModule, ConfigService } from '@nestjs/config'; import { RedisCacheService } from './redis-cache.service'; import { CacheModule, Module, Global } from '@nestjs/common'; import * as redisStore from 'cache-manager-redis-store';  @Module({   imports: [     CacheModule.registerAsync({       isGlobal: true,       imports: [ConfigModule],       inject: [ConfigService],       useFactory: async (configService: ConfigService) => {         return {           store: redisStore,           host: configService.get('REDIS_HOST'),           port: configService.get('REDIS_PORT'),           db: 0, //目標(biāo)庫(kù),           auth_pass:  configService.get('REDIS_PASSPORT') // 密碼,沒有可以不寫         };       },     }),   ],   providers: [RedisCacheService],   exports: [RedisCacheService], }) export class RedisCacheModule {}
          登錄后復(fù)制

          • CacheModuleregisterAsync方法采用 Redis Store 配置進(jìn)行通信
          • store 屬性值redisStore ,表示'cache-manager-redis-store' 庫(kù)
          • isGlobal 屬性設(shè)置為true 來(lái)將其聲明為全局模塊,當(dāng)我們將RedisCacheModuleAppModule中導(dǎo)入時(shí), 其他模塊就可以直接使用,不需要再次導(dǎo)入
          • 由于Redis 信息寫在配置文件中,所以采用registerAsync()方法來(lái)處理異步數(shù)據(jù),如果是靜態(tài)數(shù)據(jù), 可以使用register

          3、新建redis-cache.service.ts文件, 在service實(shí)現(xiàn)緩存的讀寫

          import { Injectable, Inject, CACHE_MANAGER } from '@nestjs/common'; import { Cache } from 'cache-manager';  @Injectable() export class RedisCacheService {   constructor(     @Inject(CACHE_MANAGER)     private cacheManager: Cache,   ) {}    cacheSet(key: string, value: string, ttl: number) {     this.cacheManager.set(key, value, { ttl }, (err) => {       if (err) throw err;     });   }    async cacheGet(key: string): Promise<any> {     return this.cacheManager.get(key);   } }
          登錄后復(fù)制

          接下來(lái),在app.module.ts中導(dǎo)入RedisCacheModule即可。

          調(diào)整 token 簽發(fā)及驗(yàn)證流程

          我們借助redis來(lái)實(shí)現(xiàn)token過(guò)期處理、token自動(dòng)續(xù)期、以及用戶唯一登錄。

          • 過(guò)期處理:把用戶信息及token放進(jìn)redis,并設(shè)置過(guò)期時(shí)間
          • token自動(dòng)續(xù)期:token的過(guò)期時(shí)間為30分鐘,如果在這30分鐘內(nèi)沒有操作,則重新登錄,如果30分鐘內(nèi)有操作,就給token自動(dòng)續(xù)一個(gè)新的時(shí)間,防止使用時(shí)掉線。
          • 戶唯一登錄:相同的賬號(hào),不同電腦登錄,先登錄的用戶會(huì)被后登錄的擠下線

          token 過(guò)期處理

          在登錄時(shí),將jwt生成的token,存入redis,并設(shè)置有效期為30分鐘。存入redis的key由用戶信息組成, value是token值。

          // auth.service.ts  async login(user: Partial<User>) {     const token = this.createToken({       id: user.id,       username: user.username,       role: user.role,     });  +   await this.redisCacheService.cacheSet( +     `${user.id}&${user.username}&${user.role}`, +     token, +     1800, +   );     return { token };  }
          登錄后復(fù)制

          在驗(yàn)證token時(shí), 從redis中取token,如果取不到token,可能是token已過(guò)期。

          // jwt.strategy.ts + import { RedisCacheService } from './../core/db/redis-cache.service';  export class JwtStrategy extends PassportStrategy(Strategy) {   constructor(     @InjectRepository(User)     private readonly userRepository: Repository<User>,     private readonly authService: AuthService,     private readonly configService: ConfigService, +   private readonly redisCacheService: RedisCacheService,   ) {     super({       jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),       secretOrKey: configService.get('SECRET'), +     passReqToCallback: true,     } as StrategyOptions);   }    async validate(req, user: User) { +   const token = ExtractJwt.fromAuthHeaderAsBearerToken()(req); +   const cacheToken = await this.redisCacheService.cacheGet( +     `${user.id}&${user.username}&${user.role}`, +   ); +   if (!cacheToken) { +     throw new UnauthorizedException('token 已過(guò)期'); +   }     const existUser = await this.authService.getUser(user);     if (!existUser) {       throw new UnauthorizedException('token不正確');     }     return existUser;   } }
          登錄后復(fù)制

          用戶唯一登錄

          當(dāng)用戶登錄時(shí),每次簽發(fā)的新的token,會(huì)覆蓋之前的token, 判斷redis中的token與請(qǐng)求傳入的token是否相同, 不相同時(shí), 可能是其他地方已登錄, 提示token錯(cuò)誤。

          // jwt.strategy.ts   async validate(req, user: User) {     const token = ExtractJwt.fromAuthHeaderAsBearerToken()(req);     const cacheToken = await this.redisCacheService.cacheGet(       `${user.id}&${user.username}&${user.role}`,     );     if (!cacheToken) {       throw new UnauthorizedException('token 已過(guò)期');     } +   if (token != cacheToken) { +     throw new UnauthorizedException('token不正確'); +   }     const existUser = await this.authService.getUser(user);     if (!existUser) {       throw new UnauthorizedException('token不正確');     }     return existUser;   }
          登錄后復(fù)制

          token自動(dòng)續(xù)期

          實(shí)現(xiàn)方案有多種,可以后臺(tái)jwt生成access_token(jwt有效期30分鐘)和refresh_token, refresh_token有效期比access_token有效期長(zhǎng),客戶端緩存此兩種token, 當(dāng)access_token過(guò)期時(shí), 客戶端再攜帶refresh_token獲取新的access_token。 這種方案需要接口調(diào)用的開發(fā)人員配合。

          我這里主要介紹一下,純后端實(shí)現(xiàn)的token自動(dòng)續(xù)期

          實(shí)現(xiàn)流程:

          • ①:jwt生成token時(shí),有效期設(shè)置為用不過(guò)期
          • ②:redis 緩存token時(shí)設(shè)置有效期30分鐘
          • ③:用戶攜帶token請(qǐng)求時(shí), 如果key存在,且value相同, 則重新設(shè)置有效期為30分鐘

          設(shè)置jwt生成的token, 用不過(guò)期, 這部分代碼是在auth.module.ts文件中, 不了解的可以看文章 Nest.js 實(shí)戰(zhàn)系列第二篇-實(shí)現(xiàn)注冊(cè)、掃碼登陸、jwt認(rèn)證

          // auth.module.ts const jwtModule = JwtModule.registerAsync({   inject: [ConfigService],   useFactory: async (configService: ConfigService) => {     return {       secret: configService.get('SECRET', 'test123456'), -     signOptions: { expiresIn: '4h' },  // 取消有效期設(shè)置     };   }, });
          登錄后復(fù)制

          然后再token認(rèn)證通過(guò)后,重新設(shè)置過(guò)期時(shí)間, 因?yàn)槭褂玫?code>cache-manager沒有通過(guò)直接更新有效期方法,通過(guò)重新設(shè)置來(lái)實(shí)現(xiàn):

          // jwt.strategy.ts  async validate(req, user: User) {     const token = ExtractJwt.fromAuthHeaderAsBearerToken()(req);     const cacheToken = await this.redisCacheService.cacheGet(       `${user.id}&${user.username}&${user.role}`,     );     if (!cacheToken) {       throw new UnauthorizedException('token 已過(guò)期');     }     if (token != cacheToken) {       throw new UnauthorizedException('token不正確');     }     const existUser = await this.authService.getUser(user);     if (!existUser) {       throw new UnauthorizedException('token不正確');     } +   this.redisCacheService.cacheSet( +     `${user.id}&${user.username}&${user.role}`, +     token, +     1800, +   );     return existUser;   }
          登錄后復(fù)制

          到此,在Nest中實(shí)現(xiàn)token過(guò)期處理、token自動(dòng)續(xù)期、以及用戶唯一登錄都完成了, 退出登錄時(shí)移除token比較簡(jiǎn)單就不在這里一一上代碼了。

          在Nest中除了使用官方推薦的這種方式外, 還可以使用nestjs-redis來(lái)實(shí)現(xiàn),如果你存token時(shí), 希望存hash結(jié)構(gòu),使用cache-manager-redis-store時(shí),會(huì)發(fā)現(xiàn)沒有提供hash值存取放方法(需要花點(diǎn)心思去發(fā)現(xiàn))。

          注意:如果使用nest-redis來(lái)實(shí)現(xiàn)redis緩存, 在Nest.js 8 版本下會(huì)報(bào)錯(cuò), 小伙伴們可以使用@chenjm/nestjs-redis 來(lái)代替, 或者參考 issue上的解決方案:Nest 8 + redis bug。

          總結(jié)

          源碼地址:https://github.com/koala-coding/nest-blog

          贊(0)
          分享到: 更多 (0)
          網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)