Nâng cấp hàm Node.js thế hệ 1 lên thế hệ 2

Các ứng dụng đang dùng các hàm thế hệ 1 nên cân nhắc chuyển sang thế hệ 2 theo hướng dẫn này. Các hàm thế hệ thứ 2 sử dụng Cloud Run để mang lại hiệu suất tốt hơn, cấu hình tốt hơn, giám sát hiệu quả hơn và nhiều lợi ích khác.

Các ví dụ trên trang này giả định rằng bạn đang sử dụng JavaScript với các mô-đun CommonJS (nhập kiểu require), nhưng những nguyên tắc tương tự cũng áp dụng cho JavaScript với ESM (nhập kiểu import … from) và TypeScript.

Quá trình di chuyển

Các hàm thế hệ 1 và thế hệ 2 có thể cùng tồn tại cạnh nhau trong cùng một tệp. Thao tác này cho phép di chuyển dễ dàng từng phần khi bạn đã sẵn sàng. Bạn nên di chuyển lần lượt từng hàm, thực hiện kiểm thử và xác minh trước khi tiếp tục.

Xác minh phiên bản Firebase CLI và firebase-function

Đảm bảo bạn đang sử dụng Firebase CLI phiên bản 12.00 trở lên và firebase-functions phiên bản 4.3.0. Mọi phiên bản mới hơn đều sẽ hỗ trợ thế hệ thứ 2 cũng như thế hệ 1.

Cập nhật lệnh nhập

Nhập các hàm thế hệ thứ 2 từ gói con v2 trong SDK firebase-functions. Đường dẫn nhập khác này là tất cả những gì Giao diện dòng lệnh (CLI) của Firebase cần để xác định xem nên triển khai mã hàm của bạn dưới dạng hàm thế hệ 1 hay 2.

Gói con v2 là mô-đun và bạn chỉ nên nhập mô-đun cụ thể mà mình cần.

Trước: thế hệ thứ 1

const functions = require("firebase-functions");

Sau: thế hệ thứ 2

// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

Cập nhật định nghĩa của điều kiện kích hoạt

Vì SDK thế hệ thứ 2 ưu tiên tính năng nhập mô-đun, hãy cập nhật các định nghĩa của điều kiện kích hoạt để phản ánh các lệnh nhập đã thay đổi từ bước trước.

Các đối số được truyền đến lệnh gọi lại cho một số điều kiện kích hoạt đã thay đổi. Trong ví dụ này, hãy lưu ý rằng các đối số cho lệnh gọi lại onDocumentCreated đã được hợp nhất thành một đối tượng event duy nhất. Ngoài ra, một số điều kiện kích hoạt có tính năng cấu hình mới thuận tiện, chẳng hạn như tuỳ chọn cors của điều kiện kích hoạt onRequest.

Trước: thế hệ thứ 1

const functions = require("firebase-functions");

exports.date = functions.https.onRequest((req, res) => {
  // ...
});

exports.uppercase = functions.firestore
  .document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

Sau: thế hệ thứ 2

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

Sử dụng cấu hình có tham số

Các hàm thế hệ 2 ngừng hỗ trợ functions.config và thay vào đó là một giao diện an toàn hơn để xác định các tham số cấu hình theo cách khai báo bên trong cơ sở mã của bạn. Với mô-đun params mới, CLI chặn hoạt động triển khai trừ phi tất cả tham số đều có giá trị hợp lệ, đảm bảo rằng một hàm sẽ không được triển khai khi thiếu cấu hình.

Di chuyển sang gói con params

Nếu đang sử dụng cấu hình môi trường với functions.config, bạn có thể di chuyển cấu hình hiện có sang cấu hình có tham số.

Trước: thế hệ thứ 1

const functions = require("firebase-functions");

exports.date = functions.https.onRequest((req, res) => {
  const date = new Date();
  const formattedDate =
date.toLocaleDateString(functions.config().dateformat);

  // ...
});

Sau: thế hệ thứ 2

const {onRequest} = require("firebase-functions/v2/https");
const {defineString} = require("firebase-functions/params");

const dateFormat = defineString("DATE_FORMAT");

exports.date = onRequest((req, res) => {
  const date = new Date();
  const formattedDate = date.toLocaleDateString(dateFormat.value());

  // ...
});

Đặt giá trị thông số

Trong lần đầu tiên bạn triển khai, Firebase CLI sẽ nhắc tất cả các giá trị của tham số và lưu các giá trị vào tệp dotenv. Để xuất các giá trị function.config, hãy chạy firebase functions:config:export.

Để tăng mức độ an toàn, bạn cũng có thể chỉ định loại tham số và quy tắc xác thực.

Trường hợp đặc biệt: Khoá API

Mô-đun params tích hợp với Cloud Secret Manager, cung cấp khả năng kiểm soát quyền truy cập chi tiết vào các giá trị nhạy cảm như khoá API. Hãy xem các tham số bí mật để biết thêm thông tin.

Trước: thế hệ thứ 1

const functions = require("firebase-functions");

exports.getQuote = functions.https.onRequest(async (req, res) => {
  const quote = await fetchMotivationalQuote(functions.config().apiKey);
  // ...
});

Sau: thế hệ thứ 2

const {onRequest} = require("firebase-functions/v2/https");
const {defineSecret} = require("firebase-functions/params");

// Define the secret parameter
const apiKey = defineSecret("API_KEY");

exports.getQuote = onRequest(
  // make the secret available to this function
  { secrets: [apiKey] },
  async (req, res) => {
    // retrieve the value of the secret
    const quote = await fetchMotivationalQuote(apiKey.value());
    // ...
  }
);

Đặt tuỳ chọn thời gian chạy

Cấu hình của tuỳ chọn thời gian chạy đã thay đổi giữa thế hệ 1 và 2. Thế hệ thứ 2 cũng bổ sung một tính năng mới để đặt tuỳ chọn cho tất cả các hàm.

Trước: thế hệ thứ 1

const functions = require("firebase-functions");

exports.date = functions
  .runWith({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  })
  // locate function closest to users
  .region("asia-northeast1")
  .https.onRequest((req, res) => {
    // ...
  });

exports.uppercase = functions
  // locate function closest to users and database
  .region("asia-northeast1")
  .firestore.document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

Sau: thế hệ thứ 2

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");

// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });

exports.date = onRequest({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  }, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

Dùng mô hình đồng thời

Một ưu điểm đáng kể của các hàm thế hệ thứ 2 là khả năng một thực thể hàm duy nhất có thể phân phát nhiều yêu cầu cùng lúc. Điều này có thể làm giảm đáng kể số lần khởi động nguội mà người dùng cuối gặp phải. Theo mặc định, mô hình đồng thời được đặt ở 80, nhưng bạn có thể đặt thành bất kỳ giá trị nào từ 1 đến 1000:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // set concurrency value
    concurrency: 500
  },
  (req, res) => {
    // ...
});

Việc điều chỉnh mô hình đồng thời có thể cải thiện hiệu suất và giảm chi phí của các hàm. Tìm hiểu thêm về tính năng đồng thời trong phần Cho phép các yêu cầu đồng thời.

Kiểm tra việc sử dụng biến toàn cục

Các hàm thế hệ 1 được viết mà không lưu ý đến mô hình đồng thời có thể sử dụng các biến toàn cục được đặt và đọc trên mỗi yêu cầu. Khi tính năng đồng thời được bật và một phiên bản duy nhất bắt đầu xử lý nhiều yêu cầu cùng lúc, việc này có thể gây ra lỗi trong hàm của bạn vì các yêu cầu đồng thời bắt đầu thiết lập và đọc đồng thời các biến toàn cục.

Trong khi nâng cấp, bạn có thể đặt CPU của hàm thành gcf_gen1 và đặt concurrency thành 1 để khôi phục hành vi của thế hệ 1:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // TEMPORARY FIX: remove concurrency
    cpu: "gcf_gen1",
    concurrency: 1
  },
  (req, res) => {
    // ...
});

Tuy nhiên, bạn không nên khắc phục vấn đề này lâu dài vì nó sẽ làm mất lợi thế về hiệu suất của các hàm thế hệ thứ 2. Thay vào đó, hãy kiểm tra việc sử dụng các biến chung trong hàm của bạn và xoá các chế độ cài đặt tạm thời này khi bạn đã sẵn sàng.

Di chuyển lưu lượng truy cập sang các hàm thế hệ thứ 2 mới

Tương tự như khi thay đổi vùng hoặc loại điều kiện kích hoạt của một hàm, bạn cần đặt tên mới cho hàm thế hệ 2 và từ từ di chuyển lưu lượng truy cập đến hàm đó.

Bạn không thể nâng cấp một hàm từ thế hệ 1 lên thế hệ 2 có cùng tên rồi chạy firebase deploy. Thao tác này sẽ dẫn đến lỗi:

Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.

Trước khi thực hiện các bước này, đầu tiên hãy đảm bảo rằng hàm của bạn không thay đổi vì cả phiên bản mới và phiên bản cũ của hàm sẽ chạy cùng lúc trong quá trình thay đổi. Ví dụ: nếu bạn có một hàm thế hệ 1 phản hồi để ghi các sự kiện trong Firestore, hãy đảm bảo rằng việc phản hồi lệnh ghi hai lần, một lần ở hàm thế hệ 1 và một lần ở hàm thế hệ 2, để phản hồi những sự kiện đó, ứng dụng của bạn sẽ ở trạng thái nhất quán.

  1. Đổi tên hàm trong mã hàm. Ví dụ: đổi tên resizeImage thành resizeImageSecondGen.
  2. Triển khai hàm này để cả hàm thế hệ 1 và thế hệ 2 ban đầu đều đang chạy.
    1. Trong trường hợp có thể gọi, Hàng đợi tác vụ và trình kích hoạt HTTP, hãy bắt đầu trỏ tất cả máy khách đến hàm thế hệ 2 bằng cách cập nhật mã máy khách với tên hoặc URL của hàm thế hệ 2.
    2. Với điều kiện kích hoạt ở chế độ nền, cả hàm thế hệ 1 và thế hệ 2 đều sẽ phản hồi mọi sự kiện ngay khi triển khai.
  3. Khi tất cả lưu lượng truy cập bị tắt, hãy xoá hàm thế hệ 1 bằng lệnh firebase functions:delete của firebase CLI.
    1. Bạn có thể đổi tên hàm thế hệ 2 để khớp với tên của hàm thế hệ 1 (không bắt buộc).