Trong bài viết trước, chúng ta đã thảo luận về cách có thể sử dụng Redis để lưu trữ trạng thái hạn ngạch người dùng của bạn trên ứng dụng Next.js đầy đủ. Việc làm này cho phép chúng tôi cải thiện hiệu suất của ứng dụng vì Redis nói chung nhanh hơn nhiều so với cơ sở dữ liệu.
Upstash Redis đặc biệt là một lựa chọn tuyệt vời cho trường hợp sử dụng này, vì đây là cơ sở dữ liệu Redis được quản lý hoàn toàn và có thể truy cập qua HTTP. Điều này có nghĩa là chúng tôi thậm chí có thể sử dụng các thời gian chạy biên như Vercel để chạy mã gần với người dùng với độ trễ tối thiểu.
QStash là gì?
QStash là giải pháp lập lịch và nhắn tin dựa trên HTTP dành cho thời gian chạy không máy chủ và biên . Nói cách khác, nó cho phép bạn chạy các công việc CRON bằng cách gửi yêu cầu HTTP .
Công việc CRON là một cách tuyệt vời để lên lịch thực hiện các tác vụ vào một thời điểm cụ thể. Công việc CRON này có thể mất nhiều thời gian để chạy mà không ảnh hưởng đến hiệu suất của ứng dụng hoặc người dùng cuối của bạn.
Mục tiêu của công việc CRON của chúng tôi trong bài viết này là đồng bộ hóa trạng thái hạn ngạch của người dùng từ Redis với cơ sở dữ liệu của chúng tôi. Chúng tôi sẽ thực hiện nhiệm vụ này chạy mỗi giờ .
Tạo điểm cuối công việc CRON bằng QStash
Dưới đây là bản tóm tắt nhanh về bài viết trước về cách chúng tôi xử lý trạng thái hạn ngạch người dùng trong Redis:
// Key stored in Redis. A key is created for each user, and made unique by their ID.
const quotaKey = `user:${userId}:quota`;
// Call the INCR command to increment the value of the key by 1.
const quota = await redis.incr(quotaKey);
// If the value of the key is 1, it means the key has been created.
// We can set an expiration date of 24 hours to this key by using the EXPIRE command.
if (quota === 1) {
await redis.expire(quotaKey, 60 * 60 * 24);
}
Với mã này, giờ đây chúng ta có thể tạo điểm cuối công việc CRON bằng QStash. Điểm cuối công việc CRON của chúng tôi sẽ được kích hoạt tại URL /api/cron/update-usage .
Hãy tạo một tệp mới cho điểm cuối Next.js của chúng tôi:
touch pages/api/cron/update-usage.ts
Sau đó, chúng tôi muốn đảm bảo rằng chỉ QStash mới có thể kích hoạt điểm cuối này, đây là @upstash/qstash xử lý cho chúng tôi. Nếu chúng ta không làm điều này thì bất kỳ ai cũng có thể có khả năng kích hoạt điểm cuối công việc CRON có thể dẫn đến các vấn đề bảo mật.
Hãy cài đặt @upstash/qstash :
npm install @upstash/qstash Bây giờ chúng ta có thể tập trung vào mã của điểm cuối công việc CRON.
Dưới đây là bản tóm tắt nhanh về những gì chúng tôi sẽ làm:
- Nhận khóa hạn ngạch của tất cả người dùng từ Redis bằng cách sử dụng
SCAN(phân trang dựa trên con trỏ hiệu quả) . - Đối với mỗi khóa được tìm thấy từ
SCAN, tạousersmảng đối tượng chứa ID người dùng và hạn ngạch của họ. - Tạo giao dịch cho mỗi người dùng bên trong
usersarray để cập nhật hạn ngạch của họ trong cơ sở dữ liệu. Việc này được thực hiện bằng Prisma. - Xuất hàm điểm cuối bằng
verifySignaturechức năng từ@upstash/qstash/nextjsđể đảm bảo chỉ QStash mới có thể kích hoạt điểm cuối.
Điểm cuối mã đầy đủ có ở đây, tôi đảm bảo nhận xét mọi khía cạnh của mã:
// /pages/api/cron/update-usage.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { verifySignature } from "@upstash/qstash/nextjs";
import prisma from "@/lib/prisma";
// Generate an Upstash Redis instance using environment variables.
// Make sure those are defined in your .env file.
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
// Required by `@upstash/qstash`.
export const config = { api: { bodyParser: false } };
// Update the usage of every user by pulling the data from the Redis database into the Prisma database.
async function handler(req: NextApiRequest, res: NextApiResponse) {
// Contains all the keys found during the `SCAN`.
const keys: string[] = [];
// Current position of the cursor, updated after each `SCAN`.
let cursor = 0;
// Execute the cursor-based pagination using the `SCAN` command.
// Redis return `0` when the cursor is at the end of the pagination.
do {
const [nextCursor, newKeys] = await redis.scan(cursor, {
// Match the pattern of the keys we want to find, where `*` is a wildcard.
match: "user:*:quota",
// Limit the number of keys returned by the `SCAN` command.
count: 10,
});
cursor = nextCursor;
keys.push(...newKeys);
} while (cursor !== 0);
// Array of objects containing the user's ID and their quota.
const users: { id: number; quota: number }[] = [];
// Get the usage of each user from Redis based on the keys found.
for (const key of keys) {
// A key should be in the format `user:${id}:quota`. Split the key to get the ID.
const id = key.split(":")[1];
// Get the current quota of the user using the `GET` command.
const quota = await redis.get(`user:${id}:quota`);
users.push({ id: parseInt(id, 10), quota });
}
// Create a transaction for each user to update their quota in the database.
const promises = users.map((user) =>
prisma.user.update({
where: { id: user.id },
data: { quota: user.quota },
}),
);
// Run all transactions.
await Promise.all(promises);
return res.status(200).json({ message: "OK" });
}
// Export the handler with the `verifySignature` function to make sure only
// QStash can authenticate to trigger the CRON job.
export default verifySignature(handler); Phần khó nhất đã xong!
Thiết lập QStash
Bây giờ trước khi đẩy mã của chúng tôi, chúng tôi cần thiết lập QStash. QStash cung cấp cấp độ miễn phí hào phóng, chúng tôi có thể sử dụng tới 500 yêu cầu mỗi ngày .
Đăng nhập vào Bảng điều khiển Upstash của bạn và nhấp vào QStash tab.
Sau đó, hãy tạo một công việc CRON được lên lịch mới bằng cách sử dụng Trình tạo yêu cầu . Rất đơn giản nhờ giao diện người dùng trực quan:

Sau khi nhấp vào Lịch trình nút, nếu bạn cuộn xuống, bạn sẽ thấy công việc CRON của mình trên Công việc đã lên lịch phần:

Bước cuối cùng, chúng ta cần thiết lập 2 biến môi trường trong ứng dụng Next.js của chúng tôi. Các biến đó sẽ được thêm vào nơi ứng dụng Next.js của chúng tôi được lưu trữ, trong trường hợp này là trên Vercel.
Trong Trình tạo yêu cầu phần này, có 2 biến môi trường sẵn sàng để sao chép:
QSTASH_CURRENT_SIGNING_KEYQSTASH_NEXT_SIGNING_KEY
Đây là những điều cần thiết để xác thực tin nhắn đến. Nếu không thì bất kỳ ai cũng có thể gọi tới điểm cuối của bạn.

Sau khi có các biến đó, bạn có thể thêm chúng vào dự án Vercel của mình.
Để thực hiện việc này, hãy truy cập dự án Vercel của bạn, nhấp vào Cài đặt rồi vào Biến môi trường :

Sau khi thêm 2 biến môi trường, bây giờ bạn có thể đẩy mã của mình.
QStash sẽ định kỳ kích hoạt điểm cuối công việc CRON và cập nhật mức sử dụng của mỗi người dùng mỗi giờ.
Kết luận
Upstash cung cấp rất nhiều công cụ cho môi trường serverless. Chúng ta có thể sử dụng QStash và Redis cùng nhau để tạo ra các ứng dụng có hiệu suất cao và có thể mở rộng cho nhiều trường hợp sử dụng.
Nếu bạn muốn xem trực tiếp, hệ thống tương tự đã được triển khai trên fastsheet, một công cụ biến bảng tính của bạn thành API.