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

สร้าง Real-Time Chat App ด้วย Ably, Upstash Redis และ Node.js

ในบล็อกนี้ เราจะสร้าง real-time chat application ให้ผู้ใช้ join chat group และสื่อสารแบบเรียลไทม์

ใช้ Ably สำหรับ real-time messaging across users low latency, Upstash Redis เก็บ messages persistently และ Node.js สร้างแอปพลิเคชัน

Ably

Ably เป็น real-time experience platform เปิด two-way communication ระหว่างผู้ใช้

สำหรับ chat app นี้ ใช้ Pub/Sub channels ของ Ably: users ส่งข้อความโดย "publish" ไป channel และรับข้อความโดย "subscribe"

Ably ให้ Pub/Sub channels โดยเพิ่ม abstract layer บน WebSockets พร้อมฟีเจอร์เพิ่มเติม:

  • Presence of subscribers
  • Authentication
  • Heartbeat mechanism
  • Queues
  • ส่ง messages ตามลำดับที่ publish
  • Integrations เรียก functions จาก events บน Ably
  • Stream messages/presence/metadata ไป Kafka

สร้างแอปพลิเคชันบน Ably: สร้างบัญชี Ably แล้วสร้างแอปเลือก "Live Chat"

สร้าง Real-Time Chat App ด้วย Ably, Upstash Redis และ Node.js

Upstash Redis

ใช้ Upstash Redis เก็บ chat messages persistently ให้ผู้ใช้ดึง chat history เมื่อ join ได้ Upstash Redis เก็บ messages เป็น sorted list ช่วยส่ง messages จาก database ไป clients โดยไม่ต้อง re-sort

สร้าง Upstash Redis database: ไป Upstash Console login สร้าง Redis database

สร้าง Real-Time Chat App ด้วย Ably, Upstash Redis และ Node.js

สถาปัตยกรรม Chat App

Design ง่ายตรงไปตรงมา: สร้าง Real-Time Chat App ด้วย Ably, Upstash Redis และ Node.js

Demo project นี้สร้าง 1 Ably channel Clients publish messages ไป channel อื่นๆ receive ทันทีผ่าน subscription

นอกจาก real-time messaging ระหว่าง clients ต้องเก็บ messages ใน Upstash Redis Ably channel มี subscriber เพิ่มอีก 1 คือ server ของเรา รับ messages จาก clients ส่งไป Upstash Redis เก็บไว้

ใช้ chat history ใน Upstash Redis ผ่าน server สร้าง endpoint "/history" คืนค่า chat history จาก Redis Clients เรียก endpoint นี้ตอน load app เพื่อดึง history

Client Side

สร้าง basic chat UI: index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" type="text/css" href="index.css">
  <title>Chat App</title>
</head>
<body>
  <div class="container">
    <p class="msg">Messages:</p>
    <div id="messages" class="messages"></div>
    <form id="msgForm" class="msgForm">
      <input type="text" placeholder="Send message" class="input" id="inputBox" />
      <input type="submit" class="btn" value="Send">
    </form>
  </div>
  <script src="https://cdn.ably.io/lib/ably.min-1.js"></script>
  <script src="app.js"></script>
</body>
</html>

ใน browser: input field พิมพ์ข้อความ, submit button ส่ง, message boxes แสดงข้อความก่อนหน้า

เขียน app.js (JavaScript รันใน web page):

สร้าง Ably channel: กลับไป Ably dashboard สร้าง API key ใหม่ที่ "API Keys" เลือก capabilities "Publish" และ "Subscribe"

สร้าง Real-Time Chat App ด้วย Ably, Upstash Redis และ Node.js

สร้าง Ably client ใน JavaScript:

const ably = new Ably.Realtime('<Ably API Key>');

Warning: ให้ API key ตรงๆ ให้ clients INSECURE Ably มี "TokenRequest" mechanism เนื่องจากเป็น demo ให้ API key ตรงๆ สำหรับ production ดู Ably token docs

ดึง Ably channel:

const channel = ably.channels.get('chat');

Channel สร้างอัตโนมัติเมื่อมีการ publish

ขอ username จาก user (simple, ไม่ focus authentication):

let name = window.prompt("Please enter your name.", "Anonymous");

ส่งข้อความ

const form = document.getElementById('msgForm');
form.addEventListener('submit', (event) => {
  event.preventDefault();
  const message = document.getElementById('inputBox').value;
  if (message.trim() !== '') {
    const messageData = { name: name, message: message }
    channel.publish('message', messageData);
    document.getElementById('inputBox').value = '';
  }
});

ส่ง message 簡単: สร้าง object มี username และ message publish ไป channel

รับข้อความ

channel.subscribe('message', (message) => {
  console.log("Client received: ", message);
  displayMessage(message.data);
});

function displayMessage(message) {
  const incomingName = message.name;
  const incomingMessage = message.message;
  const messageElement = document.createElement('div');
  const messageValue = document.createElement('div');
  const messageWriter = document.createElement('div');
  if(incomingName !== name){
    messageElement.classList.add('msgSent');
  } else {
    messageElement.classList.add('msgReceived');
  }
  messageWriter.classList.add('msgWriter');
  messageValue.classList.add('msgValue');
  messageWriter.textContent = incomingName;
  messageValue.textContent = incomingMessage;
  messageElement.appendChild(messageWriter);
  messageElement.appendChild(messageValue);
  const list = document.getElementById('messages');
  list.appendChild(messageElement);
}

ดึง chat history ตอนโหลดหน้า

document.addEventListener("DOMContentLoaded", function() {
  fetchChatHistory();
});

function fetchChatHistory() {
  fetch('/history')
    .then(response => {
      if (!response.ok) throw new Error('Failed to fetch chat history');
      return response.json();
    })
    .then(data => {
      const history = data.history;
      console.log(history);
      if (history && history.length > 0) {
        history.forEach(message => {
          displayMessage(JSON.parse(message));
        });
      }
    })
    .catch(error => {
      console.error('Error fetching chat history:', error);
    });
}

Server Side

Server subscribe Ably channel push ไป Upstash Redis คืน chat history จาก Redis ตอน client request

Config server ใน app.js:

var express = require('express');
var path = require('path');
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
var router = express.Router();
router.get('/', function(req, res, next) {
  res.render('index', { title: 'ChatApp' });
});
app.use('/', router);
module.exports = app;

สร้าง server ใน server.js subscribe Ably channel เชื่อม Upstash Redis:

var app = require('../app');
var http = require('http');
const redis = require('redis');
const Ably = require('ably');
const port = process.env.PORT || '3000';
app.set('port', port);
var server = http.createServer(app);
server.listen(port);

const redisClient = redis.createClient({ url : "<Upstash Redis Endpoint>" });
redisClient.on("error", function(err) { throw err; });
redisClient.connect().then(r => { console.log("Connected to Redis.") })

const ably = new Ably.Realtime({ key: '<Ably API Key>', });
const channel = ably.channels.get('chat');

Push incoming messages ไป Upstash Redis:

channel.subscribe('message', async (message) => {
  const convertedMessage = JSON.stringify(message.data);
  console.log('Received message:', convertedMessage);
  await redisClient.LPUSH("AblyChatList", convertedMessage);
});

Implement endpoint /history ดึง chat history จาก Upstash Redis:

app.get('/history', async (req, res) => {
  const messages = await redisClient.LRANGE("AblyChatList", 0, -1);
  messages.reverse();
  console.log("history api: ", messages);
  res.json({ history: messages });
});

Run the App

ไป directory ของ server.js รัน:

node server.js

เปิด localhost:3000 ใน browser ขอ username ก่อน

สร้าง Real-Time Chat App ด้วย Ably, Upstash Redis และ Node.js

ใส่ username แล้วเปิด chat ได้ เปิด tab ใหม่ username ต่างกัน จะเห็น messages จาก tab ก่อนหน้า

สร้าง Real-Time Chat App ด้วย Ably, Upstash Redis และ Node.js

ขอบคุณ Upstash Redis ดึง chat history ทุกครั้งที่เปิดแอป

สรุป

Ably มีฟีเจอร์มากสำหรับ real-time communication ใช้ได้หลาย use cases

ในบล็อกนี้ ใช้ Ably สร้าง real-time chat app ด้วย Pub/Sub channels เก็บ messages ใน Upstash Redis ทั้งคู่ทำให้แอปง่ายและเร็ว

โปรเจกต์นี้เป็น demo scope เรียบง่าย ถ้าสนใจ สร้าง robust, scalable, secure real-time apps ได้ด้วยฟีเจอร์ของ Ably และ Upstash Redis