Sử dụng Caching với Redis và NodeJS

Để lấy data phim hot, mỗi lần bạn gọi API của bên thứ ba để nhận được data, quá trình có thể mất tới 1 phút. Điều này ảnh hưởng rất nhiều đến hiệu suất và có thể phát sinh các vấn đề khác. Có một giải pháp đơn giản cho vấn đề này - Caching (Bộ nhớ đệm). Bài viết sẽ tìm hiểu sử dụng Cache với NodeJS và Redis


1041

Caching(Bộ nhớ đệm)

Bộ nhớ đệm là quá trình lưu trữ dữ liệu trong lớp lưu trữ dữ liệu tốc độ cao (cache). Bộ nhớ cache thường được lưu trữ trong ổ cứng để truy cập nhanh và hiệu quả hơn so với tìm nạp dữ liệu từ kho lưu trữ dữ liệu chính mà app NodeJS sử dụng.

Ở đây, một ví dụ rất cơ bản về bộ nhớ đệm – memoization(ghi nhớ). Lưu ý rằng memoization là một hình thức lưu trữ liên quan đến việc lưu giá trị trả về của hàm dựa trên các tham số của nó.

Ví dụ, Tính số thứ n trong chuỗi Fibonacci.

const fibonacci = (n) => {
  if (n < 2) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

Về cơ bản, đoạn ở trên đệ quy phương thức của chúng ta cho (n – 1), (n – 2) và thêm lại với nhau.

Và nó sẽ được sắp xếp như sau:

nodejs

Như bạn có thể thấy, chúng ta tính fibonacci(2) rất tốn tài nguyên hệ thống. Về cơ bản, chúng ta có thể đã lưu trữ giá trị cho fibonacci(2) khi tính toán lần đầu tiên và sử dụng giá trị lưu trữ lần thứ hai để tăng tốc quá trình với NodeJS và Redis

const fibonacci = (n, cache) => {
  cache = cache || {};
  if (cache[n]) return cache[n];
  if (n < 2) return n;
  return cache[n] = fibonacci(n - 1, cache) + fibonacci(n - 2, cache);
}

Tại đây, ngăn xếp được cập nhật với memoization như sau

nodejs

Như bạn có thể thấy, chúng tôi đã có thể giảm thời gian tính toán với memoization bằng cache. Bây giờ, hãy sử dụng kỹ thuật này để reponse bộ đệm từ API của bên thứ ba bằng Redis với NodeJS

Redis

Redis là một kho lưu trữ cấu trúc dữ liệu trong bộ nhớ, mã nguồn mở, được sử dụng làm cơ sở dữ liệu, bộ đệm và trung gian tin nhắn. Bạn có thể đọc thêm tài liệu tại đây

Demo

Chúng ta setup project nodejs để hiểu rõ về cách hoạt động của caching. Trong thư mục project, gõ lệnh sau

npm init
npm i express redis node-fetch

Chúng ta có endpoint đơn giản cung cấp chi tiết về các lần ra mắt mới nhất của SpaceX.

"use strict";
const express = require("express");
const fetch = require("node-fetch");
const app = express();
const PORT = process.env.PORT || 4000;
app.get("/spacex/launches", (req, res) => {
  fetch("https://api.spacexdata.com/v3/launches/latest")
    .then(res => res.json())
    .then(json => { res.status(200).send(json) })
    .catch(error => {
      console.error(error);
      res.status(400).send(error);
    });
});
app.listen(PORT, () => console.log(`Server up and running on ${PORT}`));

Sau khi bạn deploy, hãy vào browser và gõ localhost: 4000. Sử dụng Postman để kiểm tra API NodeJS

GET localhost:4000/spacex/launches

Lưu ý thời gian phần màu đỏ ở trên mình đã khoanh. Đó là 489ms. Bây giờ, hãy thêm bộ nhớ đệm Redis vào. Bằng câu lệnh sau:

redis-server

Trên terminal, nó sẽ hiện như thế này:

Bây giờ, hãy thêm middleware để kiểm tra xem có tồn tại trong bộ đệm hay không?Nếu không thì hãy lấy nó từ API của bên thứ ba và cập nhật bộ đệm.

"use strict";
const express = require("express");
const fetch = require("node-fetch");
const redis = require("redis");
const PORT = process.env.PORT || 4000;
const PORT_REDIS = process.env.PORT || 6379;
const app = express();
const redisClient = redis.createClient(PORT_REDIS);
const set = (key, value) => {
   redisClient.set(key, JSON.stringify(value));
}
const get = (req, res, next) => {
	let key = req.route.path;
    redisClient.get(key, (error, data) => {
      if (error) res.status(400).send(err);
      if (data !== null) res.status(200).send(JSON.parse(data));
      else next();
 	});
}
app.get("/spacex/launches", get, (req, res) => {
  fetch("https://api.spacexdata.com/v3/launches/latest")
    .then(res => res.json())
    .then(json => {
    	set(req.route.path, json);
    	res.status(200).send(json);
    })
    .catch(error => {
      console.error(error);
      res.status(400).send(error);
    });
});
app.listen(PORT, () => console.log(`Server up and running on ${PORT}`));

Khi GET trên localhost:4000/spacex/launches, vẫn mất khoảng thời gian như lần đầu tiên chạy trước khi thêm Redis trong NodeJS. Điều này là do bộ nhớ cache hiện không có dữ liệu đó và phải cập nhật nó. Và trong lần thứ hai, chúng ta thấy sự khác biệt

Một điều hiển nhiên trong việc này là khi chúng ta thêm nó vào bộ đệm, chúng ta sẽ không bao giờ lấy giá trị cập nhật từ API của bên thứ ba. Một cách để chống lại điều này là sử dụng setex, trong đó chúng ta sử dụng một giá trị thời gian hết hạn. Nó chủ yếu chạy trên hai biến chủ yếu SET  và EXPIRE. Sau khi thời gian hết hạn, chúng ta sẽ lấy lại dữ liệu từ API của bên thứ ba và cập nhật bộ đệm trong project NodeJS.

Kết luận

Bộ nhớ đệm là một công cụ mạnh mẽ khi được sử dụng đúng cách. Xem xét loại dữ liệu và tầm quan trọng của giá trị mới nhất, bộ đệm có thể được thêm vào để cải thiện hiệu suất, độ tin cậy và giảm chi phí.

Tham khảo về cách tối ưu : Tối ưu tốc độ ứng dụng NodeJS khi lập trình


Like it? Share with your friends!

1041