首頁  >  文章  >  web前端  >  Node.js中怎麼使用Redis?原來這麼簡單!

Node.js中怎麼使用Redis?原來這麼簡單!

青灯夜游
青灯夜游轉載
2022-11-07 20:04:132790瀏覽

Node中怎麼使用Redis?以下這篇文章跟大家介紹一下Node.js中使用Redis的方法,你會發現原來這麼簡單,希望對大家有幫助!

Node.js中怎麼使用Redis?原來這麼簡單!

之前的文章中我們其實留了兩個可以用redis優化的地方:

  • 一個是我們的在做登入時,透過JWT已經實作了服務端產生token以及驗證客戶端發送的token訊息。 【相關教學推薦:nodejs影片教學 、程式設計影片
  • 實作文章按讚功能,採用的是將按讚資料直接寫入資料庫

JWT token 實作方式, 將基本資訊直接放在token中,以便於分散式系統使用, 但是我們沒有設定有限期(這個是可以實現的),且服務端無法主動讓token失效。而Redis天然支援過期時間,也能實現讓服務端主動使token過期。

當然不是說JWT token 不如 redis token實作方案好, 具體看使用的場景,這裡我們並不討論二者孰優孰劣,只是提供一種實現方案,讓大家知道如何實現。

1. 認識redis

對前端的小夥伴來說,Redis可能相對比較陌生,首先認識

Redis是什麼

Redis是一個開源(BSD授權)的,基於記憶體的資料結構儲存系統,它可以用作資料庫、快取和訊息中間件,是現在最受歡迎的NoSQL 資料庫之一。

其具備以下特性:

  • 速度快
    • 單一節點讀110000次/s,寫81000次/s
    • 基於記憶體運行,效能高效
    • 用C 語言實現,離作業系統更近
  • 持久化
    • 資料的更新將異步地保存到硬碟(RDB和AOF
  • 多種資料結構
    • 不只是支援簡單的key-value 類型資料
    • 也支援:字串、hash、列表、集合、有序集合
  • 支援多種程式語言等等

#Redis 典型使用場景

快取

快取可以說是Redis最常用的功能之一了,合理的快取不僅可以加快存取的速度,也可以減少後端資料庫的壓力。

排行系統

利用Redis的列表和有序集合的特點,可以製作排行榜系統,而排行榜系統目前在商城類、新聞類、博客類等等,都是比不可缺的。

計數器應用

計數器的應用基本上和排行榜系統一樣,都是多數網站的普遍需求,如視頻網站的播放計數,電器網站的瀏覽數等等,但這些數量一般比較龐大,如果存到關係型資料庫,對MySQL或其他關係型資料庫的挑戰還是很大的,而Redis基本上可以說是天然支援計數器應用。

(視訊直播)訊息彈幕

直播間的線上用戶列表,禮物排行榜,彈幕訊息等信息,都適合使用Redis中的SortedSet結構進行存儲。

例如彈幕訊息,可使用ZREVRANGEBYSCORE排序返回,在Redis5.0中,新增了zpopmaxzpopmin指令,更方便訊息處理。

Redis的應用程式場景遠不止這些,Redis對傳統磁碟資料庫是一個重要的補充,是支援高並發存取的網路應用必不可少的基礎服務之一。

紙上談兵終覺淺,必須實戰一波~

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

可以快速的安裝、了解Redis資料型別以及常用的命令。

視覺化客戶端

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

RedisClient下載連結:https://github.com/caoxinyu/RedisClient

下載後直接雙擊redisclient-win32.x86.2.0.exe檔案執行即可

Node.js中怎麼使用Redis?原來這麼簡單!

啟動後, 點選server -> add

Node.js中怎麼使用Redis?原來這麼簡單!##

連線後就可以看到整體情況了:

Node.js中怎麼使用Redis?原來這麼簡單!

與SQL型資料不同,redis沒有提供新建資料庫的操作,因為它自帶了16(0 -15)個資料庫(預設使用0庫)。在同一個庫中,key是唯一存在的、不允許重複的,它就像一把“密鑰”,只能打開一把“鎖”。鍵值儲存的本質就是使用key來標識value,當想要檢索value時,必須使用與value對應的key進行查找.

Redis 認識作為文章前置條件,到這裡及結束了, 接下來進入正題~

本文主要使用Redis實作快取功能。

2. 在Nest.js中使用

版本狀況:

##函式庫版本Nest.jsV8.1.2

專案是基於Nest.js 8.x版本,與Nest.js 9.x版本使用有所不同, 後面的文章專門整理了兩個版本使用不同點的說明, 以及如何從V8升級到V9, 這裡就不過多討論。

首先,我們在Nest.js專案中連接Redis, 連接Redis需要的參數:

REDIS_HOST:Redis 域名
REDIS_PORT:Redis 端口号
REDIS_DB: Redis 数据库
REDIS_PASSPORT:Redis 设置的密码

將參數寫入.env.env. prod設定檔中:

Node.js中怎麼使用Redis?原來這麼簡單!

使用Nest官方推薦的方法,只需要簡單的3個步驟:

1、引入依賴檔

npm install cache-manager --save
npm install cache-manager-redis-store --save
npm install @types/cache-manager -D

Nest為各種快取存儲提供統一的API,內建的是記憶體中的資料存儲,但是也可使用cache-manager來使用其他方案,例如使用Redis來快取。

為了啟用緩存,導入ConfigModule, 並呼叫register()registerAsync()傳入回應的設定參數。

2、建立module檔案src/db/redis-cache.module.ts, 實作如下:

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, //目标库,
          auth_pass:  configService.get('REDIS_PASSPORT') // 密码,没有可以不写
        };
      },
    }),
  ],
  providers: [RedisCacheService],
  exports: [RedisCacheService],
})
export class RedisCacheModule {}
  • CacheModuleregisterAsync方法採用Redis Store 設定進行通訊
  • store 屬性值redisStore ,表示'cache-manager-redis-store' 函式庫
  • isGlobal 屬性設定為true 來將其宣告為全域模組,當我們將RedisCacheModuleAppModule中匯入時, 其他模組就可以直接使用,不需要再次導入
  • 由於Redis 資訊寫在設定檔中,所以採用registerAsync()方法來處理異步數據,如果是靜態數據, 可以使用register

3、新建redis-cache.service.ts檔, 在service實作快取的讀寫

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 {
    return this.cacheManager.get(key);
  }
}

接下來,在app.module.ts中導入RedisCacheModule即可。

調整 token 簽發及驗證流程

我們借助redis來實現token過期處理、token自動續期、以及用戶唯一登入。

  • 過期處理:把使用者資訊及token放進redis,並設定過期時間
  • token自動續期:token的過期時間為30分鐘,如果在這30分鐘內沒有操作,則重新登錄,如果30分鐘內有操作,就給token自動續一個新的時間,防止使用時斷線。
  • 戶唯一登入:相同的帳號,不同電腦登錄,先登入的用戶會被後登入的擠下線
##token 過期處理

在登錄時,將jwt產生的token,存入redis,並設定有效期限為30分鐘。存入redis的key由使用者資訊組成, value是token值。

// auth.service.ts
 async login(user: Partial) {
    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 };
 }

在驗證token時, 從redis中取token,如果取不到token,可能是token已過期。

// jwt.strategy.ts
+ import { RedisCacheService } from './../core/db/redis-cache.service';

export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository,
    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 已过期');
+   }
    const existUser = await this.authService.getUser(user);
    if (!existUser) {
      throw new UnauthorizedException('token不正确');
    }
    return existUser;
  }
}

用戶唯一登入

當使用者登入時,每次簽發的新的token,會覆蓋先前的token, 判斷redis中的token與請求傳入的token是否相同, 不相同時, 可能是其他地方已登錄, 提示token錯誤。

// 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 已过期');
    }
+   if (token != cacheToken) {
+     throw new UnauthorizedException('token不正确');
+   }
    const existUser = await this.authService.getUser(user);
    if (!existUser) {
      throw new UnauthorizedException('token不正确');
    }
    return existUser;
  }

token自動續期

實作方案有多種,可以後台jwt產生

access_token(jwt有效期30分鐘)和 refresh_token, refresh_token有效期比access_token有效期長,客戶端快取此兩種token, 當access_token過期時, 客戶端再攜帶 refresh_token取得新的access_token。這種方案需要介面呼叫的開發人員配合。

我這裡主要介紹一下,純後端實作的token自動續期

實作流程:

    ①:jwt產生token時,有效期限設定為用不過期
  • ②:redis 快取token時設定有效期限30分鐘
  • ③:使用者攜帶token請求時, 若key存在,且value相同,則重新設定有效期限為30分鐘
設定jwt產生的token, 用不過期, 這部分程式碼是在

auth.module.ts檔中, 不了解的可以看文章Nest.js 實戰系列第二篇-實作註冊、掃碼登陸、jwt認證

// auth.module.ts
const jwtModule = JwtModule.registerAsync({
  inject: [ConfigService],
  useFactory: async (configService: ConfigService) => {
    return {
      secret: configService.get('SECRET', 'test123456'),
-     signOptions: { expiresIn: '4h' },  // 取消有效期设置
    };
  },
});

然後再token認證通過後,重新設定過期時間, 因為使用的

cache-manager沒有透過直接更新有效期限方法,透過重新設定來實現:

// 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 已过期');
    }
    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;
  }

到此,在Nest中實現token過期處理、token自動續期、以及用戶唯一登入都完成了, 登出登入時移除token比較簡單就不在這裡一一上代碼了。

在Nest中除了使用官方推薦的這種方式外, 還可以使用

nestjs-redis來實現,如果你存token時, 希望存hash結構,使用cache-manager-redis-store時,會發現沒有提供hash值存取放方法(需要花點心思去發現)。

注意:如果使用nest-redis來實現redis緩存, 在Nest.js 8 版本下會報錯, 小夥伴可以使用@chenjm/nestjs-redis 來代替, 或參考issue上的解決方案:Nest 8 redis bug

總結

原始碼位址:https://github.com/koala-coding/nest-blog

#更多程式相關知識,請訪問:程式設計教學! !

以上是Node.js中怎麼使用Redis?原來這麼簡單!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除