Computer >> Máy Tính >  >> Lập trình >> Redis

Xây dựng Phòng chờ của riêng bạn cho trang web của bạn với Cloudflare worker và Serverless Redis

Trong bài đăng trên blog này, chúng tôi sẽ triển khai một trang phòng chờ cho trang web của bạn.

Tại sao?

Số lượng khách truy cập cao trên trang web của bạn nói chung là một điều tốt nhưng không phải lúc nào cũng vậy. Lưu lượng truy cập cao đột ngột có thể dễ dàng áp đảo các ứng dụng của bạn, điều này có thể làm gián đoạn các dịch vụ của bạn hoàn toàn. Phòng chờ là giải pháp giúp bạn kiểm soát lưu lượng và bảo vệ tài nguyên của mình trong thời gian giao thông tăng đột biến. Phòng chờ Cloudflare là một giải pháp tốt nhưng nó chỉ khả dụng cho các tài khoản doanh nghiệp và doanh nghiệp. Đừng lo lắng, trong blog này, chúng tôi sẽ xây dựng một phòng chờ cho bất kỳ loại trang web nào sử dụng Cloudflare worker và Upstash Redis.

Làm thế nào?

Có hai tham số để kiểm soát phòng chờ.

  • Thời lượng phiên tối đa :Khách truy cập có thể ở lại trang web trong bao lâu?
  • Dung lượng trang web tối đa :Trang web có thể cho phép bao nhiêu khách truy cập cùng một lúc?

Khi khách truy cập vào trang web của chúng tôi, chúng tôi sẽ tạo một khóa duy nhất (như khóa phiên) và giữ nó trong Redis. Chúng tôi sẽ đặt khóa này cho Redis với thời gian hết hạn là thời lượng phiên tối đa . Trước khi cho phép khách truy cập vào trang web, chúng tôi sẽ kiểm tra kích thước dbsize của Redis. Nếu kích thước cao hơn ** dung lượng trang web tối đa, ** thì chúng tôi sẽ chuyển khách đến phòng chờ. Phòng chờ là một html tĩnh nhưng làm mới sau mỗi 30 giây. Tức là cứ sau 30 giây một lần, khách truy cập có thể được phép vào trang web nếu có sẵn.

Khóa duy nhất mà chúng tôi đã tạo cũng được ghi vào các yêu cầu của khách truy cập dưới dạng cookie. Vì vậy các yêu cầu của cùng một người truy cập sẽ có cùng một khóa. Điều này là cần thiết để kiểm tra xem khách truy cập đã có phiên bên trong trang web khi dung lượng đầy hay chưa. Vì vậy, với tư cách là khách truy cập, bạn được phép truy cập trang web ngay cả khi dung lượng đầy miễn là khóa của bạn tồn tại trong không gian phím Redis. Nếu bạn không sử dụng lâu hơn thời lượng phiên tối đa, thì khóa của bạn sẽ bị xóa khỏi Redis. Và nếu dung lượng đầy khi bạn gửi yêu cầu, bạn sẽ được chuyển tiếp đến phòng chờ.

Chúng tôi sẽ triển khai logic này tại Cloudflare worker. Chúng tôi sẽ sử dụng Upstash như một cửa hàng Redis. Bây giờ, hãy giải thích nền tảng của những quyết định công nghệ này.

Tại sao sử dụng Cloudflare worker?

Việc triển khai phòng chờ sẽ chặn tất cả các yêu cầu đến trang web của bạn. Vì vậy, chi phí hiệu suất của nó phải ở mức tối thiểu. Công nhân Cloudflare tận dụng cơ sở hạ tầng cạnh Cloudflare, vì vậy nó mang lại cho bạn độ trễ tối thiểu trên toàn cầu. Cũng không giống như AWS Lambda, nó không có vấn đề khởi động nguội. Đây là công nghệ không có máy chủ nên khả năng mở rộng cũng không phải là vấn đề.

Tại sao lại Upstash Redis?

Bạn cần kiểm tra kích thước hiện tại của trang web trước khi cho phép mỗi người truy cập mới. Cloudflare worker là không trạng thái, vì vậy bạn cần lưu giữ thông tin này bên ngoài. Redis là lựa chọn tốt nhất với độ trễ thấp. Nhưng các dịch vụ của Redis yêu cầu kết nối dựa trên TCP không được Cloudflare worker hỗ trợ. Upstash là sản phẩm duy nhất của Redis có API REST tích hợp sẵn. Ngoài ra, nhờ tính năng Global replication, nó cung cấp cho bạn độ trễ thấp trên toàn thế giới.

Thực hiện từng bước

Chúng tôi sẽ thực hiện dự án từng bước dưới đây. Nếu bạn muốn sao chép trực tiếp dự án và thiết lập phòng chờ cho trang web của mình, bạn có thể làm theo các bước trong readme của mã nguồn.

1 Thiết lập dự án

Tạo một dự án bằng wrangler.


wrangler generate waiting-room

Sau đó, cài đặt các phụ thuộc:


npm install cookie upstash@redis

2 Cập nhật wrangler.toml

Cập nhật loại:

type = "webpack"

Đặt id tài khoản Cloudflare của bạn. Kiểm tra điều này để tìm id tài khoản của bạn.

account_id = "REPLACE_HERE"

Thêm các biến sau:

[vars]
UPSTASH_REDIS_REST_TOKEN = "REPLACE_HERE"
UPSTASH_REDIS_REST_URL = "REPLACE_HERE"
TOTAL_ACTIVE_USERS = 10
SESSION_DURATION_SECONDS = 30

Bạn cần tạo cơ sở dữ liệu Toàn cầu từ bảng điều khiển Upstash. Bạn chỉ cần sao chép và dán mã thông báo REST và URL từ bảng điều khiển. Cơ sở dữ liệu Redis ban đầu phải trống và chỉ được sử dụng bởi ứng dụng này.

Bạn nên đặt TOTAL_ACTIVE_USERS và SESSION_DURATION_SECONDS tùy thuộc vào yêu cầu của riêng bạn.

3 index.js

Index.js là tệp triển khai Cloudflare worker. Vì vậy, chúng tôi sẽ đặt tất cả logic bên trong nó. Sao chép và dán đoạn mã dưới đây vào đó:

import { parse } from "cookie";
import { Redis } from "@upstash/redis/cloudflare";

const redis = Redis.fromEnv();

addEventListener("fetch", (event) => {
  event.respondWith(
    handleRequest(event.request).catch(
      (err) => new Response(err.stack, { status: 500 })
    )
  );
});

const COOKIE_NAME_ID = "__waiting_room_id";
const COOKIE_NAME_TIME = "__waiting_room_last_update_time";

const init = {
  headers: {
    Authorization: "Bearer " + UPSTASH_REDIS_REST_TOKEN,
  },
};

async function handleRequest(request) {
  const { pathname } = new URL(request.url);
  if (!pathname.startsWith("/favicon")) {
    const cookie = parse(request.headers.get("Cookie") || "");
    let userId;

    if (cookie[COOKIE_NAME_ID] != null) {
      userId = cookie[COOKIE_NAME_ID];
    } else {
      userId = makeid(8);
    }

    const size = await redis.dbsize();
    console.log("current capacity:" + size);
    // there is enough capacity
    if (size < TOTAL_ACTIVE_USERS) {
      return getDefaultResponse(request, cookie, userId);
    } else {
      // site capacity is full
      const user = await redis.get(userId);
      if (user === "1") {
        // the user has already active session
        return getDefaultResponse(request, cookie, userId);
      } else {
        // capacity is full so the user is forwarded to waiting room
        return getWaitingRoomResponse(userId);
      }
    }
  } else {
    return fetch(request);
  }
}

async function getDefaultResponse(request, cookie, userId) {
  // uncomment below to test the function with a static html content
  // const newResponse = new Response(default_html)
  // newResponse.headers.append('content-type', 'text/html;charset=UTF-8')

  const response = await fetch(request);
  const newResponse = new Response(response.body, response);

  const now = Date.now();
  let lastUpdate = cookie[COOKIE_NAME_TIME];
  if (!lastUpdate) lastUpdate = 0;
  const diff = now - lastUpdate;
  const updateInterval = (SESSION_DURATION_SECONDS * 1000) / 2;
  if (diff > updateInterval) {
    await redis.setex(userId, SESSION_DURATION_SECONDS, 1);
    newResponse.headers.append(
      "Set-Cookie",
      `${COOKIE_NAME_TIME}=${now}; path=/`
    );
  }

  newResponse.headers.append(
    "Set-Cookie",
    `${COOKIE_NAME_ID}=${userId}; path=/`
  );
  return newResponse;
}

async function getWaitingRoomResponse(userId) {
  const newResponse = new Response(waiting_room_html);
  newResponse.headers.set("content-type", "text/html;charset=UTF-8");
  return newResponse;
}

function makeid(length) {
  let result = "";
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  const charactersLength = characters.length;
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
}

const waiting_room_html = `
<title>Waiting Room</title>
<meta http-equiv='refresh' content='30' />

<style>*{box-sizing:border-box;margin:0;padding:0}body{line-height:1.4;font-size:1rem;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif;padding:2rem;display:grid;place-items:center;min-height:100vh}.container{width:100%;max-width:800px}p{margin-top:.5rem}</style>

<div class='container'>
  <h1>
    <div>You are now in line.</div>
    <div>Thanks for your patience.</div>
  </h1>
  <p>We are experiencing a high volume of traffic. Please sit tight and we will let you in soon. </p>
  <p><b>This page will automatically refresh, please do not close your browser.</b></p>
</div>
`;

const default_html = `
<title>Waiting Room Demo</title>

<style>*{box-sizing:border-box;margin:0;padding:0}body{line-height:1.4;font-size:1rem;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif;padding:2rem;display:grid;place-items:center;min-height:100vh}.container{width:100%;max-width:800px}p{margin-top:.5rem}</style>

<div class="container">
  <h1>
    <div>Waiting Room Demo</div>
  </h1>
    <p>
              Visit this site from a different browser, you will be forwarded to the waiting room when the capacity is full.
    </p>
  <p>  Check <a href={"https://github.com/upstash/waiting-room"} style={{"color": "blue"}}>this project </a> to set up a waiting room for your website.</p>
</div>
`;

Trong đoạn mã trên, bạn có thể chỉnh sửa waiting_room_html Biến đổi. Đây là html tĩnh của trang phòng chờ.

Phương thức handleRequest trả về getDefaultResponse hoặc getWaitingRoomResponse tùy thuộc vào tính khả dụng của trang web của bạn.

4 Chạy cục bộ

Để kiểm tra cục bộ phòng chờ, bạn nên đặt dung lượng thành 1, thời lượng phiên thành 30 giây. Sau đó chạy


wrangler dev

Bây giờ, hãy mở https://127.0.0.1:8787/ trên Chrome, bạn sẽ thấy:

Xây dựng Phòng chờ của riêng bạn cho trang web của bạn với Cloudflare worker và Serverless Redis

Và sau đó mở cùng một url trên Safari (hoặc Chrome ẩn danh), bạn sẽ thấy:

Xây dựng Phòng chờ của riêng bạn cho trang web của bạn với Cloudflare worker và Serverless Redis

Chờ lâu hơn 30 giây. Bạn sẽ thấy trang trong phòng chờ sẽ được làm mới và vào trang web.

Địa phương của chúng tôi không chuyển tiếp đến một trang web thực nên chúng tôi thấy trang 404 của Cloudflare, điều đó không sao cả.

5 Xuất bản

Triển khai chức năng Nhân viên CF của bạn với:


wrangler publish

Nó sẽ cung cấp cho bạn một url như:https://waiting-room.upsdev.workers.dev/

Bây giờ, hãy định tuyến nhân viên của chúng tôi đến trang web của bạn. Đầu tiên, máy chủ định danh miền của bạn phải trỏ đến Cloudflare. Kiểm tra điều này. Sau đó, bạn cần thêm miền của mình làm tuyến và chọn chức năng Công nhân của mình trong bảng điều khiển Công nhân CF.

Xây dựng Phòng chờ của riêng bạn cho trang web của bạn với Cloudflare worker và Serverless Redis

Kết luận

Chúng tôi đã xây dựng thành công phòng chờ mà không cần chạm vào mã ứng dụng của mình nhờ Cloudflare worker và Upstash Redis. Chúng tôi tin rằng đây là một chỉ báo khác về mức độ mạnh mẽ của các chức năng cạnh trở nên cùng với Upstash.

Có những điều cần cải thiện:

  • Thời gian chờ ước tính:Chúng tôi có thể tính toán và hiển thị thời gian chờ trung bình.
  • Xếp hàng công bằng và có thứ tự:Hiện tại, khách đang đợi sẽ vào trang web một cách ngẫu nhiên khi còn chỗ. Chúng tôi có thể giữ một hàng đợi và cố gắng để khách truy cập có thứ tự.

Cả hai cải tiến trên đều yêu cầu giữ nhiều trạng thái hơn và thực hiện nhiều cuộc gọi từ xa hơn. Đó là lý do tại sao chúng tôi muốn bỏ qua những điều đó. Nếu trường hợp sử dụng của bạn yêu cầu nghiêm ngặt những điều đó, bạn có thể kiểm tra blog này để lấy cảm hứng từ công việc của nhóm Cloudflare về giải pháp doanh nghiệp của họ.

Kiểm tra mã nguồn.

Hãy cho chúng tôi biết suy nghĩ của bạn trên Twitter hoặc Discord.