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

Các ứng dụng hiện đang sử dụng hàm thế hệ 1 nên cân nhắc việc di chuyển sang hàm thế hệ 2 bằng cách làm 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 tốt hơn, cấu hình tốt hơn, khả năng giám sát tốt hơn, v.v.

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 (lệnh nhập kiểu require), nhưng các nguyên tắc tương tự cũng áp dụng cho JavaScript với ESM (lệnh nhập kiểu import … from) và TypeScript.

Quy 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 cho phép bạn di chuyển từng phần một cách dễ dàng khi đã sẵn sàng. Bạn nên di chuyển từng hàm một, 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

Các hàm thế hệ 2 nhập từ gói con v2 trong SDK firebase-functions. Đường dẫn nhập khác biệt này là tất cả những gì mà Firebase CLI cần để xác định xem có triển khai mã hàm dưới dạng hàm thế hệ 1 hay 2 hay không.

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ệ 1

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

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 điều kiện kích hoạt

Vì SDK thế hệ 2 ưu tiên các lệnh nhập mô-đun, hãy cập nhật định nghĩ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ố trình 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ố trình kích hoạt có các tính năng cấu hình mới rất tiện lợi, chẳng hạn như tuỳ chọn cors của trình kích hoạt onRequest.

Trước: thế hệ 1

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

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ệ thứ 2 ngừng hỗ trợ functions.config để chuyển sang giao diện an toàn 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ã. Với mô-đun params mới, CLI sẽ chặn quá trình triển khai trừ phi tất cả cá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 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ệ 1

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

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

Để tăng cường độ 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ệ 1

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

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ệ 1 và 2. Thế hệ 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ệ 1

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

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 ư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 tí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 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 ý đế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 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 thiết lập và đọc các biến toàn cục cùng một lú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 dùng cách này để khắc phục lâu dài vì cách này sẽ làm mất các lợi thế về hiệu suất của các hàm thế hệ 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 khu vực hoặc loại điều kiện kích hoạt của hàm, bạn cần đặt tên mới cho hàm thế hệ 2 và dần dần di chuyển lưu lượng truy cập sang 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 và 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 làm theo các bước này, trước tiên, hãy đảm bảo rằng hàm của bạn là không đổi, vì cả phiên bản mới và phiên bản cũ của hàm sẽ chạy cùng một 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 để viết 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 để cả hàm thế hệ 1 và hàm thế hệ 2 đều đang chạy.
    1. Trong trường hợp các trình kích hoạt có thể gọi, Hàng đợi tác vụ và HTTP, hãy bắt đầu trỏ tất cả ứng dụng đến hàm thế hệ 2 bằng cách cập nhật mã ứng dụng bằng 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 đã được di chuyển, 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).