Trong bài viết hôm nay, chúng tôi sẽ nói về cách bạn có thể tận dụng Upstash để lưu trữ và truy cập thông tin về bản đồ nơi trú ẩn của một quốc gia một cách an toàn bằng Redis và cập nhật cơ sở dữ liệu theo thời gian thực thông qua QStash.
Giới thiệu
Trong bối cảnh toàn cầu hiện nay, thiên tai và các mối đe dọa quân sự ngày càng trở nên phổ biến. Nhu cầu số hóa trong lĩnh vực dịch vụ xã hội cũng vậy.
Từ các hệ thống phát sóng khẩn cấp như cảnh báo AMBER đến ứng dụng theo dõi COVID-19 và hệ thống SOS, chúng tôi đã thấy rất nhiều công nghệ được sử dụng để ứng phó với bất kỳ loại tình huống nguy hiểm nào ảnh hưởng đến một quốc gia.
Bây giờ, chúng ta sẽ xem xét làm thế nào Redis và khối lượng công việc không có máy chủ có thể đóng vai trò quan trọng trong việc xây dựng hệ thống ứng phó khẩn cấp theo thời gian thực giúp người dân tìm được các boong-ke gần nhất, đồng thời thông báo cho họ về công suất, tính năng (chẳng hạn như cơ sở vật chất dành cho người khuyết tật) và các nguồn lực (nước, điện, thuốc, v.v.) hiện có.
Được phép của Dự án Cloud4
Tại sao không có máy chủ?
Cái gọi là khái niệm không có máy chủ đã được các nhà phát triển phổ biến trong những năm qua. Chúng tôi nhận thấy số lượng công ty khởi nghiệp chọn đi theo cấu trúc này ngày càng tăng và số lượng các tập đoàn lớn áp dụng cũng ngày càng tăng.
Đặc biệt khi nói về các chủ thể nhà nước, môi trường phát triển nhanh chóng và các dịch vụ được quản lý tự động có thể giảm đáng kể độ phức tạp và chi phí xây dựng hệ thống ứng phó khẩn cấp. Chúng tôi cũng phải xem xét hai yếu tố khác:
- Ngân sách :Nếu một quốc gia không trải qua một thảm họa mới xảy ra, liệu có người nào giữ vai trò ra quyết định có thể phê duyệt các khoản thanh toán cho sức mạnh tính toán chưa sử dụng không? Tôi nghi ngờ vậy.
- Lưu lượng truy cập tăng đột biến :Hãy tưởng tượng một cảnh báo được kích hoạt và hàng triệu người truy cập vào nền tảng web để tìm nơi trú ẩn gần nhất. API REST của bạn sẽ xử lý mức tăng rps (yêu cầu mỗi giây) đột ngột này như thế nào? Còn các thành phần thời gian thực thiết yếu thì sao?
Tất cả các câu hỏi khác nhau này có thể được trả lời bằng cách sử dụng các nền tảng không có máy chủ thực sự như Upstash và Ably để tăng sức mạnh cho các máy chủ hạng nặng có khả năng tự động mở rộng quy mô và chỉ tính phí cho những tài nguyên bạn đang sử dụng.
Tại sao lại là Redis?
Yêu cầu dữ liệu của dự án của chúng tôi bao gồm các hoạt động thuật toán khác nhau phải được thực hiện ở tốc độ cao nhất có thể. Từ cấu trúc dữ liệu cơ bản để thể hiện thông tin của nơi trú ẩn cho đến cách đơn giản để sắp xếp những thông tin có sẵn ở một vị trí theo các tiêu chí khác nhau, Redis cho phép chúng tôi duy trì tính linh hoạt của cơ sở dữ liệu NoSQL, đồng thời cung cấp nhiều chức năng khác nhau để đơn giản hóa và tăng tốc độ phát triển.
Tại sao lại là QStash?
Hàng đợi tin nhắn truyền thống cung cấp một giải pháp vững chắc cho việc xây dựng các hệ thống hướng sự kiện, nhưng có một lưu ý:hầu hết chúng hoàn toàn phụ thuộc vào các giao thức có trạng thái như AMPQ, nghĩa là chúng chưa sẵn sàng để sử dụng trong các môi trường chạy ngắn như các chức năng không có máy chủ.
QStash giải quyết vấn đề này bằng cách cho phép thực hiện các yêu cầu bằng HTTP, giao thức không trạng thái thường được sử dụng trong các môi trường như vậy. Kết hợp điều đó với webhooks, chúng tôi có thể tạo quy trình thời gian thực để cập nhật và tìm nạp dữ liệu từ và vào cơ sở dữ liệu.
Yêu cầu
Tái bút .:Nếu bạn chỉ muốn xem qua các mẫu thì bỏ qua phần yêu cầu và thiết lập. Chúng nhằm mục đích hiển thị những gì chúng tôi đang sử dụng và cách sử dụng.
Nếu muốn tự mình xây dựng loại hệ thống này, bạn sẽ cần những thứ sau:
- tài khoản Upstash, từ đó bạn sẽ tạo cơ sở dữ liệu Redis và sử dụng điểm cuối hoặc chủ đề QStash;
- tài khoản Ably và thiết lập tích hợp webhook
- dự án Next.js;
- một dịch vụ đường hầm như ngrok nếu bạn muốn ủy quyền các yêu cầu cục bộ tới một URL có sẵn trên internet;
- bất kỳ trình quản lý gói nào hỗ trợ các thư viện được yêu cầu. Bài viết này sẽ sử dụng npm nhưng không bắt buộc.
LƯU Ý :Bài viết này sẽ cung cấp một số mẫu bằng chứng khái niệm cơ bản để chứng minh việc sử dụng các công nghệ nói trên trong bối cảnh kiến trúc ứng dụng của chúng tôi. Mã đã cho KHÔNG sẵn sàng để sản xuất và đã được đơn giản hóa cũng như rút ngắn để dễ đọc hơn. Đảm bảo thực hiện các sửa đổi cần thiết nếu bạn muốn triển khai(xử lý ngoại lệ, bảo mật, v.v.)
Thiết lập
Chức năng không có máy chủ
Chúng tôi sẽ sử dụng các tuyến API biên của Next.js để chạy khối lượng công việc không có máy chủ ở gần vị trí của người dùng hơn.
Để tạo dự án Next.js, hãy chạy npx create-next-app@latest <your-app-name> trong thư mục mẹ. Sau đó bạn có thể chạy npm run dev để khởi động máy chủ phát triển, npm run build để tạo gói sản xuất và npm run start để khởi động máy chủ sản xuất.
Next.js sử dụng môi trường nút cho các tuyến API làm mặc định. Chúng ta có thể thay đổi điều đó theo hai cách:
- Phương pháp tiếp cận theo từng tuyến đường. Thêm bản xuất sau vào tuyến mong muốn:
export const config = { runtime: "experimental-edge", }; - Phương pháp tiếp cận toàn cầu. Để tạo cạnh thời gian chạy mặc định, hãy thêm phần sau vào tệp next.config,js của bạn:
const nextConfig = { // ... experimental: { runtime: "experimental-edge", }, // ... };
Nâng cấp
Để tạo nhà cung cấp Upstash, hãy làm theo hướng dẫn này.
Để thiết lập lộ trình nhận QStash, hãy làm theo hướng dẫn này. Lưu ý rằng Ably cung cấp cho chúng tôi hai tùy chọn để mã hóa tin nhắn webhook, JSON và MessagePack. Nếu muốn sử dụng tiêu đề trước, bạn có thể cần sử dụng tiêu đề ủy quyền thay vì JWT để giải quyết các vấn đề tiềm ẩn về khả năng tương thích mã hóa. Để đơn giản, chúng tôi sẽ sử dụng JSON.
Có thể
Đối với chức năng thời gian thực, chúng tôi sẽ sử dụng các giao thức của Ably, mặc định là WebSockets nếu có thể. Để bắt đầu, hãy làm theo hướng dẫn này và vì chúng tôi đang sử dụng Next.js, bạn có thể xem gói npm @ively-labs/react-hooks.
Chúng ta cũng nên thiết lập tích hợp webhook để trỏ đến URL QStash của mình và thêm mọi tiêu đề cần thiết.
Nếu bạn chuẩn bị sản xuất, hãy kiểm tra cả xác thực và bảo mật.
Tùy chọn:đào hầm cục bộ
Hãy làm theo hướng dẫn này để bắt đầu với ngrok
Kiến trúc
Dòng chảy
Để hiểu rõ hơn về quy trình ứng dụng của chúng tôi, hãy xem sơ đồ này (được tạo bằng excalidraw.com):

Như bạn có thể thấy, chúng tôi đã xác định hai loại người dùng:
- công dân , người có thể truy cập vào nơi trú ẩn hoặc thông tin về vị trí và có thể thông báo ý định đi đến hầm trú ẩn
- quản trị viên , người có thể sửa đổi dữ liệu hậu cần của hầm trú ẩn và sửa đổi tình trạng sẵn có (ví dụ:xác nhận rằng một người/gia đình đã đến địa điểm)
Chúng tôi đang cố gắng cung cấp dữ liệu theo thời gian thực trong khi vẫn duy trì giao diện người dùng và phần phụ trợ tách biệt nhất có thể. Vì vậy, chúng tôi sẽ sử dụng kênh Ably để liên lạc với khách hàng và kích hoạt webhook mỗi khi tình trạng sẵn có thay đổi.
Ngoài ra, chúng tôi sẽ sử dụng API REST của Ably để xuất bản thông báo từ chương trình phụ trợ vì chúng tôi không nên sử dụng bất kỳ loại kết nối trạng thái nào.
Dữ liệu
Hiện tại chúng ta phải lưu trữ 2 loại dữ liệu chính:
-
thông tin về nơi trú ẩn, có thể ở dạng hàm băm Redis và có các thuộc tính sau:
- tên của khóa là
country-city-number(ví dụ:RO-CJ-01) - tình trạng sẵn sàng (ví dụ:
300) - tính năng (ví dụ:
["disabled persons special acces", "counseling"]) - tài nguyên:
ví dụ:{ resource: quantity, or resource: list ... }{ "water": "200 liters", "medicine": ["insuline", ...] } - điện:
yes/no - sưởi ấm:
yes/no - vị trí:
[longitude, latitude]hoặc{ longitude: number, latitude: number } - _id:chúng tôi sẽ tạo ngẫu nhiên
- tên của khóa là
-
thông tin của một địa điểm
Trong khi chúng tôi có thể thực hiện một truy vấn lồng nhau để tìm nơi trú ẩn có sẵn cho vị trí đã chỉ định, có thể nên lưu trữ chúng dưới dạng cấu trúc dữ liệu riêng biệt để đơn giản hóa và tối ưu hóa truy vấn. Để làm như vậy, chúng ta có thể sử dụng bộ Redis và đặt tên các phần tử là
<geographical-unit>-<name>(ví dụ:country-ROhoặccity-CJ). Bằng cách này, chúng tôi cũng ngăn chặn sự trùng lặpví dụ:
city-CJ : ["RO-CJ-01", "RO-CJ-02", "RO-CJ-03"]
Nhưng vẫn còn một vấn đề:làm thế nào chúng ta có thể phân biệt giữa ý định đi đến nơi trú ẩn của người dân và thực tế cập nhật về sức chứa được kích hoạt bởi xác nhận của quản trị viên nơi trú ẩn? Chúng ta có thể làm như vậy theo hai cách:
-
Cập nhật liên tục khóa sẵn có của nơi trú ẩn:Người dùng thông báo ý định đến (một mình hoặc với gia đình/v.v.) => chúng tôi tăng x;Quản trị viên phát hiện ra xác nhận không hợp lệ => chúng tôi giảm x (hoặc tăng -x)
Vấn đề ở đây là quản trị viên phải đóng vai trò là nguồn cung cấp thông tin sự thật và theo dõi vị trí của công dân; Ngoài ra, dữ liệu của nơi trú ẩn không thể tin cậy được ngay lập tức
-
Tạo khóa chuỗi riêng tên là
shelter-"availability"(ví dụ:RO-CJ-01-availability) và lưu trữ giá trị được cập nhật theo thời gian thực. Bằng cách này, chúng tôi có thể cập nhật theo thời gian thực và giữ khóa khả dụng của hàm băm làm nguồn đáng tin cậy.Lưu ý rằng về phía máy khách, chúng tôi sẽ tìm nạp cả mã băm và khóa chuỗi, đồng thời trình bày chúng dưới dạng tính khả dụng thực tế và tính khả dụng dự kiến. Một cách tiếp cận khác là chỉ tìm nạp chuỗi và hoàn nguyên về nguồn sự thật khi cần. Mặc dù điều này sẽ giảm tải mạng nhưng quản trị viên sẽ cần liên tục xác minh dữ liệu (như trong trường hợp đầu tiên) và thực tế tìm nạp ít hơn, chúng tôi sẽ cần lưu trữ riêng khóa khả dụng của nơi trú ẩn (hãy nhớ rằng chúng tôi vẫn cần lấy các thông tin khác!).
Sắp xếp
Hãy tận dụng một kiểu dữ liệu Redis khác gọi là tập hợp đã sắp xếp. Giả sử chúng ta muốn sắp xếp các boongke ở một vị trí theo tình trạng sẵn có, cơ sở vật chất hoặc tài nguyên sẵn có:như tôi đã đề cập trước đây, chúng ta có thể thực hiện một truy vấn lồng nhau nhưng tốn thời gian và tải dữ liệu lớn hơn nhiều. Một giải pháp tốt hơn là tạo các bộ được sắp xếp cho mọi tiêu chí lọc (chúng ta có thể đặt tên chúng là <geographical-unit>-<name>-<criteria> , như city-CJ-availability hoặc country-RO-food )
ví dụ:city-B-availability
Trong ví dụ này, điểm khả dụng được tính là total seats - current occupation .
Ví dụ
Chúng tôi có thể viết một số mã cơ bản để thể hiện API của chúng tôi và hành vi của khách hàng
Điểm cuối
Về cơ bản chúng ta cần có:
- điểm cuối dữ liệu sẽ cung cấp cho khách hàng mọi thông tin được yêu cầu và
- một edpoint để tạo hoặc cập nhật các kiểu dữ liệu của chúng tôi
Khách hàng
Sau khi theo dõi vị trí của người dùng, chúng tôi sẽ tự động kết nối với kênh "sẵn có" và đăng thông báo nếu người dân có ý định đi xuống hầm.
Trước tiên, hãy tạo kết nối từ /_app.js tập tin:
import { useEffect, useState } from "react";
import "../styles/globals.css";
import { configureAbly } from "@ably-labs/react-hooks";
export default function App({ Component, pageProps }) {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
configureAbly({
// In a production system, you should use authentication
key: process.env.NEXT_PUBLIC_ABLY_API_KEY,
});
setLoaded(true);
}, []);
if (!loaded) return <div>loading...</div>;
return <Component {...pageProps} />;
} Sau đó kết nối với kênh ở bất kỳ thành phần nào chúng ta cần:
import { useChannel } from "@ably-labs/react-hooks";
export default function Test() {
const [availability] = useChannel("availability", (msg) => {
console.log(msg);
});
return <></>;
} Làm việc với cơ sở dữ liệu
Thêm dữ liệu mới
Để thêm dữ liệu mới vào Redis, chúng ta có thể sử dụng lệnh HSET, SET và SADD
export default async (req) => {
let data = await req.json();
if (data.type === "shelter") {
// Remove the 'type' key as it is not neccesary anymore
delete data.type;
const { name: shelter, availability } = data;
let shelterData = data;
// Generate a random id.
shelterData._id = Math.random()
.toString(36)
.replace(/[^a-z]+/g, "")
.substring(0, 7);
// Store the shelter's info as a hash
await redis.hset(shelter, shelterData);
// Store the realtime-updated availability as a string
await redis.set(shelter + "-availability", availability);
return new Response("ok");
} else if (data.type === "location") {
const { name: location, shelters } = data;
await redis.sadd(location, ...shelters);
return new Response("ok");
}
}; Cập nhật dữ liệu hiện có
Ngoài các thao tác UD (cập nhật-xóa) cơ bản, chúng ta có thể sử dụng lệnh Redis INCRBY và HINCRBY để sửa đổi tính khả dụng của nơi trú ẩn.
Nếu một công dân thông báo ý định đi xuống hầm, chúng tôi có thể xuất bản một thông báo từ ứng dụng khách
availability.publish("update", {
shelter: "RO-CJ-01",
availability: 1, // Or -x, if the user cancels.
});
Như đã giải thích trong kiến trúc, điều này sẽ kích hoạt một webhook mà từ đó chúng ta có thể lấy dữ liệu và cập nhật shelter-"availability" chìa khóa.
await redis.incrby(shelter + "-availability", value); Nếu bạn đang cập nhật từ máy chủ (ví dụ:xác nhận của quản trị viên), thay vào đó, đừng quên sử dụng API REST.
Đang lấy dữ liệu
Trên máy chủ, chúng ta có thể sử dụng các lệnh GET , SMEMBERS và HGETALL.
Trên máy khách, chúng tôi có thể nghe tin nhắn trong kênh và cập nhật trạng thái tương ứng.
Tóm lại
Hôm nay, chúng ta đã xem xét cách có thể tận dụng kiến trúc serverless mạnh mẽ để xây dựng một ứng dụng theo dõi tình trạng khẩn cấp trong thế giới thực. Mặc dù có nhiều cách tiếp cận để thực hiện việc này nhưng cá nhân tôi khuyên bạn nên sử dụng loại quy trình này vì trải nghiệm của nhà phát triển được đơn giản hóa.
Có thể bạn là một nhà phát triển cấp cao đã quen với việc xây dựng các hệ thống máy tính phân tán và cảm thấy cần một dịch vụ được quản lý. Hoặc, bạn có thể là người mới bắt đầu nhưng có một ý tưởng có khả năng thay đổi thế giới theo hướng tốt đẹp. Việc tìm kiếm công cụ phù hợp luôn là một phần thiết yếu của quy trình.
Hãy cho tôi biết suy nghĩ của bạn về bài viết này và nếu bạn có bất kỳ câu hỏi nào, vui lòng nhắn tin cho tôi trên LinkedIn hoặc kiểm tra các mã khác của tôi trên Github.