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

Các ứng dụng đang dùng hàm thế hệ 1 nên cân nhắc việc chuyển sang thế hệ thứ 2 theo hướng dẫn trong 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 cao hơn, cấu hình tốt hơn, giám sát hiệu quả hơn và nhiều tính năng khác.

Các ví dụ trên trang này giả định bạn đang sử dụng JavaScript với các mô-đun CommonJS (nhập kiểu require), nhưng các nguyên tắc tương tự cũng áp dụng cho JavaScript có 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. Điều này giúp bạn dễ dàng di chuyển từng phần khi bạn đã sẵn sàng. Bạn nên di chuyển mỗi lần một chức năng, 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

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

Cập nhật các lệnh nhập

Nhập 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ả mà 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à bạn 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 ưa chuộng tính năng nhập mô-đun, nên hãy cập nhật các định nghĩa về điều kiện kích hoạt để phản ánh các lệnh nhập đã thay đổi ở 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ó các tính năng cấu hình mới tiện lợi, 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ố

Hàm thế hệ thứ 2 ngừng hỗ trợ functions.config để thay vào đó sử dụng một giao diện bảo mật hơn nhằm 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 việc triển khai trừ khi tất cả tham số đều có giá trị hợp lệ, đảm bảo rằng một hàm không được triển khai khi cấu hình bị thiếu.

Di chuyển sang gói con params

Nếu đang sử dụng cấu hình môi trường bằng 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, Giao diện dòng lệnh (CLI) của Firebase sẽ nhắc tất cả các giá trị của tham số và lưu các giá trị đó trong 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, cho phép 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. 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 các tuỳ chọn thời gian chạy đã thay đổi giữa thế hệ thứ 1 và thứ 2. Thế hệ thứ 2 cũng bổ sung một tính năng mới để đặt các 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) => {
  /* ... */
});

Sử dụng tính năng đồng thời

Một lợi thế đáng kể của hàm thế hệ thứ 2 là khả năng một thực thể hàm phân phát nhiều yêu cầu cùng một 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, chế độ đồng thời được đặt ở mức 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 tính năng đồ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 yêu cầu đồng thời.

Kiểm tra mứ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 ý tính năng đồng thời có thể sử dụng các biến toàn cục được đặt và đọc theo mỗi yêu cầu. Khi tính năng đồng thời được bật và một thực thể bắt đầu xử lý nhiều yêu cầu cùng một lúc, điều này có thể gây ra lỗi trong hàm của bạn khi các yêu cầu đồng thời bắt đầu cài đặt và đọc các biến toàn cục đồng thời.

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ệ thứ 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, đây không phải là cách khắc phục lâu dài vì sẽ làm mất các ưu điểm về hiệu suất của hàm thế hệ thứ 2. Thay vào đó, hãy kiểm tra việc sử dụng các biến toàn cục trong hàm 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

Giống như khi thay đổi khu vực của hàm hoặc loại điều kiện kích hoạt, bạn cần đặt tên mới cho hàm thế hệ thứ 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ệ thứ 2 có cùng tên và chạy firebase deploy. Làm như vậ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 bạn làm theo 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 đồng thời trong quá trình thay đổi. Ví dụ: nếu bạn có hàm thế hệ 1 phản hồi các sự kiện ghi trong Firestore, hãy đảm bảo phản hồi với một lượt ghi hai lần, một lần theo hàm thế hệ 1 và một lần ở hàm thế hệ 2, để phản hồi các sự kiện đó, ứng dụng của bạn sẽ ở một 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 để cả hàm thế hệ 1 ban đầu và hàm thế hệ 2 đều chạy.
    1. Trong trường hợp có thể gọi, hàng đợi tác vụ và điều kiện kích hoạt HTTP, hãy bắt đầu trỏ tất cả ứng dụng đến hàm thế hệ thứ 2 bằng cách cập nhật mã ứng dụng có tên hoặc URL của hàm thế hệ thứ 2.
    2. Với trình kích hoạt trong nền, cả chức năng 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ệ thứ 2 cho khớp với tên của hàm thế hệ 1 (không bắt buộc).