Trong bài đăng này, tôi nói về cách tôi tạo thông báo theo thời gian thực bằng cách sử dụng Sự kiện do máy chủ gửi với Upstash Redis, Hành động máy chủ Next.js và Vercel. Việc tận dụng các kênh tin nhắn trong Upstash Redis có thể nâng cao đáng kể kiến trúc giao tiếp trong ứng dụng của bạn, khiến chúng phản hồi nhanh hơn và năng động hơn.
Bản trình diễn
Những gì chúng tôi sẽ sử dụng
- Next.js (Front-end và Back-end)
- Upstash Redis (Sự kiện do máy chủ gửi bằng lệnh XUẤT BẢN)
- CSS Tailwind (Tạo kiểu)
- Vercel (Triển khai)
Những gì bạn cần
- Node.js 18
- Tài khoản Upstash
- Tài khoản Vercel
Thiết lập Upstash Redis
Khi bạn đã tạo tài khoản Upstash và đăng nhập, bạn sẽ chuyển tới tab Redis và tạo cơ sở dữ liệu.


Sau khi tạo cơ sở dữ liệu, bạn sẽ chuyển đến tab Chi tiết. Cuộn xuống cho đến khi bạn tìm thấy phần Kết nối cơ sở dữ liệu của bạn. Sao chép URL Redis và lưu nó ở nơi an toàn, chúng tôi sẽ sử dụng nó dưới dạng UPSTASH_REDIS_URL làm biến môi trường của chúng tôi.

Ngoài ra, hãy cuộn xuống cho đến khi bạn tìm thấy phần REST API và chọn .env nút. Sao chép nội dung và lưu nó ở nơi an toàn, chúng tôi sẽ sử dụng các biến thu được dưới dạng UPSTASH_REDIS_REST_URL và UPSTASH_REDIS_REST_TOKEN .

Thiết lập dự án
Để thiết lập, chỉ cần sao chép kho ứng dụng và làm theo hướng dẫn này để tìm hiểu mọi thứ có trong đó. Để phân nhánh dự án, hãy chạy:
git clone https://github.com/rishi-raj-jain/upstash-nextjs-publish-messages-with-sse-example
cd upstash-nextjs-publish-messages-with-sse-example
npm install Khi bạn đã sao chép kho lưu trữ, bạn sẽ tạo một tệp .env. Bạn sẽ thêm các mục chúng tôi đã lưu từ các phần trên.
Nó sẽ trông giống như thế này:
# .env
# Obtained from the steps as above
# Upstash Redis Secrets
UPSTASH_REDIS_URL="rediss://default:...@...-...-...-....upstash.io:..."
UPSTASH_REDIS_REST_URL="https://...-...-...-....upstash.io"
UPSTASH_REDIS_REST_TOKEN="...="
Lưu ý cách thực hiện tại UPSTASH_REDIS_URL biến cho biết "rediss" chứ không phải "redis", nghĩa là sử dụng tùy chọn TLS/SSL.
Sau các bước này, bạn sẽ có thể khởi động môi trường cục bộ bằng lệnh sau:
npm run dev Cấu trúc kho lưu trữ
Đây là cấu trúc thư mục chính của dự án. Tôi đã đánh dấu màu đỏ các tệp sẽ được thảo luận thêm trong bài đăng này liên quan đến những vấn đề sau:
- Tìm hiểu các kênh tin nhắn trong Upstash Redis
- Tạo API sự kiện do máy chủ gửi trong Bộ định tuyến ứng dụng Next.js
- Thiết lập tác vụ máy chủ Next.js với Upstash Redis để xuất bản thông báo
- Thiết lập giao diện Next.js để liên tục nghe và hiển thị thông báo trong thời gian thực

Hiểu các kênh tin nhắn trong Upstash Redis
Trong Upstash Redis, mô hình xuất bản/đăng ký là cốt lõi của các kênh tin nhắn. Nhà xuất bản phát tin nhắn đến các kênh được đặt tên và người đăng ký có thể nhận tin nhắn từ các kênh cụ thể trong thời gian thực. Mô hình này cho phép giao tiếp liền mạch giữa các phần khác nhau của ứng dụng.
Đây là cách có thể xuất bản tin nhắn lên một kênh bằng thư viện tương thích với Edge, @upstash/redis 👇🏻
import { Redis } from '@upstash/redis'
// Connect to an Upstash Redis instance
const redis = Redis.fromEnv()
// Publish a message to the Upstash Redis instance
await redis.publish('posts', JSON.stringify({ date: new Date().toString(), message: "I am a new message." }))
Đây là cách người đăng ký có thể nghe kênh Upstash Redis (tại đây, posts ) với thư viện tương thích với Node, ioredis 👇🏻
// Use ioredis to be able to subscribe to an Upstash Redis instance
import Redis from 'ioredis'
// Create an Upstash Redis Subscriber instance
const redisSubscriber = new Redis(process.env.UPSTASH_REDIS_URL)
// Define the key to listen and publish messages to
const setKey = 'posts'
// Subscribe to Redis updates for the key: "posts"
// In case of any error, just log it
redisSubscriber.subscribe(setKey, (err) => {
if (err) console.log(err)
})
// Listen for new posts from Redis
redisSubscriber.on('message', (channel, message) => {
// Log the data when the channel message is received is same as the message is published to
if (channel === setKey) console.log(mesage)
}) Khi xuất bản tin nhắn lên một kênh, tất cả người đăng ký sẽ nhận được tin nhắn ngay lập tức, cho phép liên lạc hiệu quả và theo thời gian thực trong Upstash Redis.
Tạo API sự kiện do máy chủ gửi trong Bộ định tuyến ứng dụng Next.js
Sự kiện do máy chủ gửi là một cách mạnh mẽ để gửi dữ liệu mới trong thời gian thực mà không cần nhiều yêu cầu của khách hàng. Không giống như các cơ chế phản hồi yêu cầu truyền thống, SSE cho phép luồng dữ liệu một chiều từ máy chủ đến máy khách qua một kết nối HTTP tồn tại lâu dài.
Dưới đây là cách bạn có thể triển khai Sự kiện do máy chủ gửi trong Bộ định tuyến ứng dụng Next.js 👇🏻
// File: app/api/stream/route.js
// Prevents this route's response from being cached on Vercel
export const dynamic = 'force-dynamic'
export async function GET() {
const encoder = new TextEncoder()
// Create a streaming response
const customReadable = new ReadableStream({
start(controller) {
const message = 'Hey, I am a message.'
controller.enqueue(encoder.encode(`data: ${message}\n\n`))
},
})
// Return the stream response and keep the connection alive
return new Response(customReadable, {
// Set the headers for Server-Sent Events (SSE)
headers: {
'Content-Type': 'text/event-stream; charset=utf-8',
Connection: 'keep-alive',
'Cache-Control': 'no-cache, no-transform',
'Content-Encoding': 'none'
},
})
} Xuất bản tin nhắn lên Upstash Redis bằng cách sử dụng Tác vụ máy chủ Next.js
Tác vụ máy chủ Next.js cho phép bạn xác định logic phía máy chủ trực tiếp trong mã giao diện người dùng của bạn trong Next.js. Điều này giúp tiết kiệm quá trình tạo các Tuyến API theo cách thủ công cũng như những rắc rối khi gửi và theo dõi (các) trạng thái gửi biểu mẫu.
Với use server ở đầu hàm, chúng tôi có thể đảm bảo rằng các hàm này chỉ chạy phía máy chủ. Bên trong Hành động máy chủ gửi biểu mẫu của chúng tôi, chúng tôi trích xuất message giá trị từ biểu mẫu, hãy sử dụng Tiêu đề Vercel để lấy quốc gia của người dùng và xuất bản thông tin dưới dạng tin nhắn tới posts Kênh tin nhắn Upstash Redis.
// File: app/actions.jsx
'use server'
import { Redis } from '@upstash/redis'
import { headers } from 'next/headers'
// The function that takes care of obtaining the country code from Vercel headers
// And publishing messages to the Upstash Redis database with the current timestamp
export async function publishNotification(formData) {
'use server'
const redis = Redis.fromEnv()
// Extract the message in the form submitted
const message = formData.get('message')
// Obtain country of the user using Vercel's x-vercel-ip-country header
const headersList = headers()
const country = headersList.get('x-vercel-ip-country')
// Publish the message to the "posts" channel in Upstash Redis
await redis.publish(
'posts',
JSON.stringify({
message,
country,
date: new Date().toString(),
}))
}
Để gọi Hành động máy chủ này khi biểu mẫu được gửi, chúng tôi chuyển nó làm trình xử lý cho biểu mẫu action sự kiện.
// File: app/page.jsx
// Import the server action
import { publishNotification } from './actions'
const Home = () => {
return (
<>
<div>
<form
/* set the server action to invoked as form is submitted */
action={publishNotification}
>
{/* Place a client side form component here */}
</form>
</div>
</>
)
}
export default Home Thiết lập giao diện người dùng Next.js để hiển thị trạng thái đang chờ xử lý trong quá trình Gửi biểu mẫu bằng hook useFormStatus của React
Đoạn mã sau đây minh hoạ cách thiết lập thành phần phía máy khách Biểu mẫu Next.js để xử lý việc gửi biểu mẫu và hiển thị pending trạng thái sử dụng useFormStatus của React cái móc. Hãy chia nhỏ các thành phần chính của mã:
- Nhập
[useFormStatusđược phát hành gần đây hook từ React](https://react.dev/reference/react-dom/hooks/useFormStatus), cung cấp cho bạn thông tin trạng thái của lần gửi biểu mẫu gần đây nhất. - Sử dụng
pendingbiến phản ứng trạng thái cho biết liệu quá trình gửi biểu mẫu có đang được tiến hành hay không. - Nếu bài gửi không đang chờ xử lý,
resetbiểu mẫu. - Sử dụng
pendingboolean để hiển thị các trạng thái có điều kiện của biểu mẫu.
'use client'
import { useEffect } from 'react'
import { useFormStatus } from 'react-dom'
const Form = () => {
// Use React's useFormStatus hook to detect form submission state
const { pending } = useFormStatus()
useEffect(() => {
// If the form is not pending, reset the form
if (!pending) document.getElementById('publish-form').reset()
}, [pending])
return (
<>
<input placeholder="Your message" className="border rounded px-3 outline-none focus:border-black/50 py-2" type="text" name="message" required />
<button
/* Disable button click while the form submission is pending */
disabled={pending}
className="hover:border-black/50 max-w-max border rounded py-1 px-3" type="submit"
>
{/* Display "pending" state placeholder */}
{pending ? (
<div className="flex flex-row gap-x-2 items-center">
<div className="animate-spin border border-gray-800 rounded-full h-[15px] w-[15px]"></div>
<span>Publishing</span>
</div>
) : (
<>Publish Notification →</>
)}
</button>
</>
)
}
export default Form Thiết lập giao diện Next.js để liên tục lắng nghe các Sự kiện do máy chủ gửi
Trong phần này, chúng ta sẽ tìm hiểu cách thiết lập trình xử lý tối thiểu cho các thông báo API Sự kiện do máy chủ gửi và cách tiếp cận để duy trì kết nối với API SSE.
Lắng nghe API sự kiện do máy chủ gửi trong React Frontend
Để nghe API SSE trong thành phần phía máy khách của chúng tôi trong React, chúng tôi sử dụng useEffect cái móc. Để thiết lập kết nối với API SSE, hãy tạo EventSource mới dụ trỏ đến /api/stream điểm cuối. Sau đó đính kèm trình xử lý sự kiện cho message sự kiện, trong đó dữ liệu đến từ luồng được phân tích cú pháp dưới dạng JSON và được xử lý hoặc hiển thị thêm trong thành phần.
Cuối cùng, mã này bao gồm chức năng dọn dẹp để đóng kết nối SSE khi thành phần này chưa được kết nối, ngăn chặn khả năng rò rỉ bộ nhớ.
import { useEffect, useState } from 'react'
const ClientSideComponent = () => {
useEffect(() => {
// Initiate the first call to connect to SSE API
const eventSource = new EventSource('/api/stream')
eventSource.addEventListener('message', (event) => {
// Parse the data received from the stream into JSON
// Add it the list of messages seen on the page
const tmp = JSON.parse(event.data)
// Do something with the obtained message
})
// As the component unmounts, close listener to SSE API
return () => {
eventSource.close()
}
}, [])
return <></>
}
export default ClientSideComponent Kết nối liên tục với API sự kiện do máy chủ gửi trong React Frontend
Trong phiên bản nâng cao của thành phần React, chúng tôi đã triển khai cơ chế đảm bảo kết nối liên tục và liên tục với API SSE bằng cách xử lý lỗi và tự động kết nối lại.
Điều này đạt được thông qua connectToStream chức năng chịu trách nhiệm thiết lập và duy trì kết nối với API SSE.
Đây là bảng phân tích các chức năng của nó 👇🏻
- Xử lý tin nhắn và kết nối ban đầu:
Hàm tạo EventSource mới ví dụ, kết nối với /api/stream điểm cuối.
// Function to take care of initial connect to the SSE API
// Also, it reconnects to the SSE API as soon as it shuts down
// This keeps the connection alive - forever with micro second delays
const connectToStream = () => {
// Connect to /api/stream as the SSE API source
const eventSource = new EventSource('/api/stream')
// ..
}
Hơn nữa, nó còn thiết lập một trình xử lý sự kiện cho message sự kiện, trong đó dữ liệu đến từ luồng được phân tích cú pháp dưới dạng JSON. Dữ liệu được phân tích cú pháp sau đó có thể được xử lý hoặc hiển thị trong thành phần React.
const connectToStream = () => {
// ...
eventSource.addEventListener('message', (event) => {
// Parse the data received from the stream into JSON
// Add it the list of messages seen on the page
const tmp = JSON.parse(event.data)
setPosts((prevPosts) => [...prevPosts, tmp])
})
// ...
} - Xử lý lỗi và tự động kết nối lại:
Trình xử lý sự kiện bổ sung được đặt cho error sự kiện. Trong trường hợp có bất kỳ lỗi nào, chẳng hạn như lỗi kết nối, nguồn sự kiện sẽ bị đóng.
Sau khi đóng, hàm sử dụng setTimeout để kích hoạt kết nối lại sau độ trễ tối thiểu là 1 mili giây. Độ trễ nhỏ này giúp tạo ra quá trình kết nối lại mượt mà và liên tục hơn mà không làm máy chủ bị quá tải khi cố gắng kết nối nhanh
// In case of any error, close the event source
// So that it attempts to connect again
eventSource.addEventListener('error', () => {
eventSource.close()
setTimeout(connectToStream, 1)
}) - Xử lý việc đóng nguồn API SSE:
onclose sự kiện được sử dụng để phát hiện khi nguồn API SSE bị đóng. Sau khi đóng, hàm này sẽ lên lịch một nỗ lực khác để kết nối với luồng sau một khoảng thời gian trễ ngắn.
// As soon as SSE API source is closed, attempt to reconnect
eventSource.onclose = () => {
setTimeout(connectToStream, 1)
} Bằng cách kết hợp các chiến lược này, chức năng này đảm bảo rằng kết nối với API SSE vẫn được duy trì liên tục. Ngay cả khi gặp lỗi hoặc bị đóng, thành phần React sẽ tiếp tục cố gắng kết nối lại với độ trễ tối thiểu, duy trì kết nối liên tục một cách hiệu quả.
Triển khai lên Vercel
Kho lưu trữ đã sẵn sàng để triển khai lên Vercel. Hãy làm theo các bước bên dưới để triển khai liền mạch với Vercel 👇🏻
- Tạo một
GitHub Repositorybằng mã ứng dụng - Tạo một
New Projecttrong Bảng điều khiển Vercel - Liên kết
GitHub Repositoryđã tạo làm dự án mới của bạn - Cuộn xuống và cập nhật
Environment Variablestừ.envtại địa phương - Triển khai! 🚀