Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

Real-Time Emergency Response System: ใช้ประโยชน์จาก Upstash, Redis และ QStash

ในบทความวันนี้ เราจะพูดถึงวิธีใช้ประโยชน์จาก Upstash เก็บและเข้าถึงข้อมูล shelter map ของประเทศอย่างปลอดภัยด้วย Redis และอัปเดต database realtime ผ่าน QStash

บทนำ

ในบริบทโลกปัจจุบัน ภัยธรรมชาติและภัยคุกคามทางทหารกลายเป็น prevalent มากขึ้น จึงต้องการ digitalisation ใน social services

ตั้งแต่ emergency broadcast systems เช่น AMBER alerts ไปจนถึง COVID-19 tracking apps และ SOS systems เราเห็นเทคโนโลยีหลากหลายถูกใช้ตอบสนองสถานการณ์อันตรายที่ส่งผลต่อประเทศ

ตอนนี้ เราจะดูว่า Redis และ serverless workloads เล่นบทบาทสำคัญในการสร้าง realtime emergency response system ช่วยพลเมืองหา bunkers ใกล้ที่สุด พร้อมแจ้ง capacity, features (เช่น disabled persons facilities) และ resources (น้ำ, ไฟฟ้า, ยา ฯลฯ) ที่มีอยู่

Real-Time Emergency Response System: ใช้ประโยชน์จาก Upstash, Redis และ QStash Courtesy of Project Cloud4

ทำไมถึง Serverless?

Serverless concept ได้รับความนิยมในหมู่นักพัฒนาหลายปี เห็น startup เพิ่มขึ้นเลือก architecture นี้ และ large corporations ก็ adopt มากขึ้น

โดยเฉพาะพูดถึง state actors rapid development environment และ auto-managed services ลด complexity และ overhead ของการสร้าง emergency response systems อย่างมาก เราต้องพิจารณา 2 factors อื่น:

  1. Budget: ถ้าประเทศไม่ผ่าน emerging disaster คนใน decision-making role จะ approve payments สำหรับ unused compute power ได้ไหม? ผมสงสัย
  2. Traffic spikes: จินตนาการ alarm triggered ล้านคนเข้าถึง web platform หา shelter ใกล้ที่สุด REST API จะจัดการ sudden rps (request-per-second) increase อย่างไร? Essential realtime components จะเป็นยังไง?

คำถามต่างๆ นี้ตอบได้ด้วย true serverless platforms อย่าง Upstash และ Ably ขับเคลื่อน heavy-lifting servers ที่ auto-scale และคิดค่าใช้จ่ายแค่ resources ที่ใช้

ทำไมถึง Redis?

Data requirements ของโปรเจกต์รวม algorithmic operations ต่างๆ ที่ต้อง perform ด้วย highest possible speed จาก basic data structures represent shelter info ไปจนถึง simple way sort available shelters ใน location ด้วย criteria ต่างๆ Redis ให้ flexibility ของ NoSQL database พร้อม functions หลากหลาย simplify และ accelerate development

ทำไมถึง QStash?

Traditional message queues ให้ solid way architect event-driven systems แต่มี caveat: พวกมัน fully depend on stateful protocols เช่น AMPQ หมายถึงไม่ ready สำหรับ short-running environments เช่น serverless functions

QStash แก้ปัญหานี้โดยอนุญาต requests ผ่าน HTTP stateless protocol ที่ใช้ใน environments เหล่านี้ รวมกับ webhooks สร้าง realtime pipelines update และ fetch data จากและเข้า database ได้

Requirements

P.S.: ถ้าอยากดู samples ข้าม requirements และ setup section ได้ พวกมันแสดงว่าเราใช้อะไรและอย่างไร

อยากสร้างระบบแบบนี้เอง ต้องมี:

  • Upstash account สร้าง Redis database และใช้ QStash endpoint หรือ topic
  • Ably account และ webhook integration setup
  • Next.js project
  • Tunneling service อย่าง ngrok proxy local requests ไป internet URL (optional)
  • Package manager รองรับ libraries ที่ขอ ใช้ npm ในบทความ แต่ไม่ mandatory

หมายเหตุ: บทความให้ basic proof-of-concept samples สาธิตใช้ technologies ใน architecture ของแอป โค้ดที่ให้มา NOT production ready ลดทอนและ shorten เพื่ออ่านง่าย ต้อง modify ก่อน deploy (exception handling, security, ฯลฯ)

Setup

Serverless Functions

ใช้ Next.js edge API routes รัน serverless workloads ใกล้ user location

สร้าง Next.js project รัน npx create-next-app@latest <your-app-name> ใน parent directory รัน npm run dev dev server, npm run build production bundle, npm run start production server

Next.js ใช้ node environment สำหรับ API routes default เปลี่ยนได้ 2 วิธี:

  1. Per-route approach: เพิ่ม export ใน route ที่ต้องการ:
    export const config = { runtime: "experimental-edge", };
  2. Global approach: ทำ edge เป็น default runtime เพิ่มใน next.config.js:
    const nextConfig = {
      // ...
      experimental: { runtime: "experimental-edge", },
      // ...
    };

Upstash

สร้าง Upstash provider ตาม guide นี้

Setup QStash-receiver route ตาม guide นี้ Ably ให้ 2 options webhook message encoding JSON และ MessagePack ถ้าอยาก用前者 อาจต้องใช้ authorization header แทน JWT เพื่อ encryption compatibility problems เราจะใช้ JSON เพื่อความเรียบง่าย

Ably

Realtime functionality ใช้ Ably protocols default WebSockets เริ่มตาม guide นี้ ใช้ Next.js ดู @ably-labs/react-hooks npm package

Setup webhook integration point ไป QStash URL เพิ่ม headers ที่จำเป็น

ถ้า production เช็ก authentication และ security ด้วย

Optional: Local Tunneling

เริ่ม ngrok ตาม guide นี้

Architecture

Flow

เข้าใจ app flow ดู diagram (สร้างด้วย excalidraw.com):

Real-Time Emergency Response System: ใช้ประโยชน์จาก Upstash, Redis และ QStash

มี 2 user types:

  1. the citizen เข้าถึง shelter หรือ location info และประกาศ intention ไป bunker
  2. the admin แก้ไข logistics data ของ bunker และ availability (เช่น confirm คน/ครอบครัว arrived)

พยายามทำ data available realtime รักษา frontend และ backend decoupled ใช้ Ably channel communicate กับ client trigger webhook ทุกครั้ง availability เปลี่ยน

ใช้ Ably REST API publish messages จาก backend ไม่ควรใช้ stateful connection

Data

ต้องเก็บ 2 main data types:

  • Shelter information เป็น Redis hash properties:

    • Key name: country-city-number (ex: RO-CJ-01)
    • Availability (ex: 300)
    • Features (ex: ["disabled persons special acces", "counseling"])
    • Resources:
      { resource: quantity, or resource: list ... }
      
      ex:
      { "water": "200 liters", "medicine": ["insuline", ...] }
      
    • Electricity: yes/no
    • Heating: yes/no
    • Position: [longitude, latitude] หรือ
      { longitude: number, latitude: number }
    • _id: generate randomly
  • Location information

    เรา สามารถ nested query หา shelters available สำหรับ location แต่อาจดีกว่าเก็บเป็น separate data structure simplify และ optimize query ใช้ Redis set ชื่อ elements <geographical-unit>-<name> (ex: country-RO หรือ city-CJ) ป้องกัน duplication

    ex:

    city-CJ : ["RO-CJ-01", "RO-CJ-02", "RO-CJ-03"]
    

แต่ยังมีปัญหา: แยกความต่างระหว่าง citizen's intention ไป shelter และ actual occupancy update จาก admin confirmation ได้ยังไง? 2 วิธี:

  1. Constantly update shelter's availability key: User ประกาศ intention มา (คนเดียว/ครอบครัว) => increment by x; Admin พบ confirmation ไม่ valid => decrement by x (หรือ increment by -x)

    ปัญหา: Admin ต้องเป็น source of truth track citizen location; shelter data ไม่ไว้ใจได้ทันที

  2. สร้าง separate string key ชื่อ shelter-"availability" (ex: RO-CJ-01-availability) เก็บ realtime-updated value ทำให้ update realtime ได้ และเก็บ hash's availability key เป็น source of truth

    Client side fetch ทั้ง hash และ string key แสดงเป็น actual availability และ projected alternative approach fetch แค่ string revert ไป source of truth เมื่อต้องการ แม้ลด network load แต่ admin ต้อง verify data ตลอด (เหมือน scenario แรก) และ fetch น้อยจริงๆ ต้อง store shelter's availability key แยก (ยังต้องได้ info อื่นๆ!)

Sorting

ใช้ Redis data type อีกอย่าง叫 sorted set อยาก sort bunkers ใน location ด้วย availability, facilities หรือ resources: nested query ทำได้แต่ cost เวลาและ data load ใหญ่ better solution สร้าง sorted sets สำหรับ filtering criteria (ชื่อ <geographical-unit>-<name>-<criteria> เช่น city-CJ-availability หรือ country-RO-food)

ex: city-B-availability

ScoreContent
200RO-B-02
100RO-B-01

ในตัวอย่าง availability score คำนวณจาก total seats - current occupation

Examples

เขียน basic code สาธิต API และ client behavior

Endpoints

หลักๆ ต้องมี:

  • Data endpoint serve client requested information
  • Endpoint create/update data types

Client

หลัง track user location auto connect "availability" channel publish message ถ้า citizen intends ไป bunker

ก่อนอื่น สร้าง connection จาก /_app.js:

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({
      // Production system ใช้ authentication key
      key: process.env.NEXT_PUBLIC_ABLY_API_KEY,
    });
    setLoaded(true);
  }, []);
  if (!loaded) return <div>loading...</div>;
  return <Component {...pageProps} />;
}

ต่อไป connect channel ใน component ที่ต้องการ:

import { useChannel } from "@ably-labs/react-hooks";

export default function Test() {
  const [availability] = useChannel("availability", (msg) => {
    console.log(msg);
  });
  return <></>;
}

ทำงานกับ Database

เพิ่มข้อมูลใหม่

เพิ่ม data ใหม่ใน Redis ใช้ HSET, SET, SADD commands

export default async (req) => {
  let data = await req.json();
  if (data.type === "shelter") {
    // Remove 'type' key ไม่จำเป็นแล้ว
    delete data.type;
    const { name: shelter, availability } = data;
    let shelterData = data;
    // Generate random id
    shelterData._id = Math.random()
      .toString(36)
      .replace(/[^a-z]+/g, "")
      .substring(0, 7);
    // Store shelter info as hash
    await redis.hset(shelter, shelterData);
    // Store realtime-updated availability as 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");
  }
};

อัปเดตข้อมูลที่มีอยู่

นอกจาก basic UD (update-delete) operations ใช้ Redis INCRBY และ HINCRBY commands แก้ไข shelter's availability

ถ้า citizen ประกาศ intention ไป bunker publish message จาก client app

availability.publish("update", {
  shelter: "RO-CJ-01",
  availability: 1, // หรือ -x ถ้า user cancel
});

ตาม architecture จะ trigger webhook ดึง data อัปเดต shelter-"availability" key

await redis.incrby(shelter + "-availability", value);

ถ้าอัปเดตจาก server (ex: admin confirmations) อย่าลืมใช้ REST API แทน

ดึงข้อมูล

บน server ใช้ GET, SMEMBERS, HGETALL commands

บน client listen messages ใน channel อัปเดต state ตามนั้น

สรุป

วันนี้เราดูการใช้ powerful serverless architecture สร้าง real-world emergency tracking app แม้มี multiple approaches ทำได้ ผมแนะนำ flow แบบนี้เพราะ simplified developer experience

อาจเป็น senior developer 習慣建構 distributed computing systems รู้สึกต้องการ managed service หรือ beginner มี idea ที่เปลี่ยนโลกให้ดีขึ้น หา right tools เป็น essential part ของ process เสมอ

บอกความคิดเห็นเกี่ยวกับบทความนี้ และถ้ามีคำถาม message ผม LinkedIn หรือเช็ก codes อื่นๆ บน Github