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

Các ứng dụng sử dụng hàm thế hệ thứ 1 nên cân nhắc di chuyển sang thế hệ thứ 2 theo hướng dẫn trong tài liệu này. Hàm thế hệ thứ 2 sử dụng Cloud Run để mang lại hiệu suất, cấu hình, khả năng giám sát tốt hơn và nhiều tính năng 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 (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.

Quá trình di chuyển

Hàm thế hệ thứ 1 và thế hệ thứ 2 có thể cùng tồn tại trong cùng một tệp. Điều này cho phép bạn dễ dàng di chuyển từng phần khi đã sẵn sàng. Bạn nên di chuyển từng hàm một, 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 ít nhất Firebase CLI phiên bản 12.00firebase-functions phiên bản 4.3.0. Mọi phiên bản mới hơn đều sẽ hỗ trợ cả thế hệ thứ 1 và thế hệ thứ 2.

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

Hàm thế hệ thứ 2 nhập 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 có triển khai mã hàm của bạn dưới dạng hàm thế hệ thứ nhất hay thế hệ thứ 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ệ thứ 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 về điều kiện kích hoạt

Vì SDK thế hệ thứ 2 ưu tiên các lệnh nhập mô-đun, hãy cập nhật định nghĩa về đ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ó 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/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ố hoá

Hàm thế hệ thứ 2 ngừng hỗ trợ functions.config để chuyển sang một giao diện an toàn hơn nhằm xác định các tham số cấu hình một cách khai báo bên trong cơ sở mã của bạn. Với mô-đun params mới, CLI sẽ chặn việc 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 với cấu hình bị thiếu.

Trước: thế hệ thứ 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());
    // ...
  }
);

Nếu bạn có cấu hình môi trường hiện tại với functions.config, hãy di chuyển cấu hình này trong quá trình nâng cấp lên thế hệ thứ 2.

API functions.config không được dùng nữa và sẽ ngừng hoạt động vào tháng 3 năm 2027. Sau ngày đó, các hoạt động triển khai bằng functions.config sẽ không thành công.

Để ngăn lỗi triển khai, hãy di chuyển cấu hình sang Cloud Secret Manager bằng Firebase CLI. Bạn nên thực hiện việc này vì đây là cách hiệu quả và an toàn nhất để di chuyển cấu hình.

  1. Xuất cấu hình bằng Firebase CLI

    Sử dụng lệnh config export để xuất cấu hình môi trường hiện tại sang một khoá bí mật mới trong Cloud Secret Manager:

    $ firebase functions:config:export
    i  This command retrieves your Runtime Config values (accessed via functions.config())
       and exports them as a Secret Manager secret.
    
    i  Fetching your existing functions.config() from your project...     Fetched your existing functions.config().
    
    i  Configuration to be exported:
    ⚠  This may contain sensitive data. Do not share this output.
    
    {
       ...
    } What would you like to name the new secret for your configuration? RUNTIME_CONFIG
    
    ✔  Created new secret version projects/project/secrets/RUNTIME_CONFIG/versions/1```
    
  2. Cập nhật mã hàm để liên kết khoá bí mật

    Để sử dụng cấu hình được lưu trữ trong khoá bí mật mới trong Cloud Secret Manager, hãy sử dụng API defineJsonSecret trong nguồn hàm của bạn. Ngoài ra, hãy đảm bảo rằng các khoá bí mật được liên kết với tất cả các hàm cần đến chúng.

    Trước

    const functions = require("firebase-functions/v1");
    
    exports.myFunction = functions.https.onRequest((req, res) => {
      const apiKey = functions.config().someapi.key;
      // ...
    });
    

    Sau

    const { onRequest } = require("firebase-functions/v2/https");
    const { defineJsonSecret } = require("firebase-functions/params");
    
    const config = defineJsonSecret("RUNTIME_CONFIG");
    
    exports.myFunction = onRequest(
      // Bind secret to your function
      { secrets: [config] },
      (req, res) => {
        // Access secret values via .value()
        const apiKey = config.value().someapi.key;
        // ...
    });
    
  3. Triển khai hàm

    Triển khai các hàm đã cập nhật để áp dụng các thay đổi và liên kết các quyền đối với khoá bí mật.

    firebase deploy --only functions:<your-function-name>
    

Đặt các 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ứ nhất và thế hệ 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/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) => {
  /* ... */
});

Cập nhật tài khoản dịch vụ mặc định (không bắt buộc)

Trong khi hàm thế hệ thứ 1 sử dụng tài khoản dịch vụ mặc định của Google App Engine để uỷ quyền truy cập vào các API Firebase, thì hàm thế hệ thứ 2 sử dụng tài khoản dịch vụ mặc định của Compute Engine. Sự khác biệt này có thể dẫn đến các vấn đề về quyền đối với các hàm được di chuyển sang thế hệ thứ 2 trong trường hợp bạn đã cấp các quyền đặc biệt cho tài khoản dịch vụ thế hệ thứ 1. Nếu bạn chưa thay đổi bất kỳ quyền nào đối với tài khoản dịch vụ, bạn có thể bỏ qua bước này.

Giải pháp được đề xuất là chỉ định rõ tài khoản dịch vụ mặc định hiện có của App Engine thế hệ thứ 1 cho các hàm mà bạn muốn di chuyển sang thế hệ thứ 2, ghi đè giá trị mặc định của thế hệ thứ 2. Bạn có thể thực hiện việc này bằng cách đảm bảo rằng mỗi hàm được di chuyển đều đặt giá trị chính xác cho serviceAccountEmail:

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

// Use the App Engine default service account for all functions
setGlobalOptions({serviceAccountEmail: '<my-project-number>@<wbr>appspot.gserviceaccount.com'});

// Now I use the App Engine default service account.
exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

// I do too!
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  // ...
});

Ngoài ra, bạn có thể đảm bảo sửa đổi thông tin chi tiết về tài khoản dịch vụ để khớp với tất cả các quyền cần thiết trên cả tài khoản dịch vụ mặc định của App Engine (cho thế hệ thứ nhất) và tài khoản dịch vụ mặc định của Compute Engine (cho thế hệ thứ hai).

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

Một ưu điểm đáng kể của hàm thế hệ thứ 2 là khả năng một thực thể hàm duy nhất phục vụ nhiều yêu cầu cùng một lúc. Điều này có thể 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, tính năng đồ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 hàm. Tìm hiểu thêm về tính năng đồng thời trong 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ệ thứ 1 được viết mà không tính đến tính năng đồ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 đặt 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ệ 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, bạn không nên dùng cách này làm giải pháp lâu dài vì cách này sẽ làm mất đi những lợi thế về hiệu suất của hàm thế hệ thứ hai. Thay vào đó, hãy kiểm tra việc sử dụng các biến toàn cục 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

Giống 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ệ thứ 2 và di chuyển dầ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ệ thứ nhất lên thế hệ thứ 2 bằng cùng một tên và chạy firebase deploy. Việ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à hàm luỹ đẳng, 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ệ thứ 1 phản hồi các sự kiện ghi trong Firestore, hãy đảm bảo rằng việc phản hồi một lần ghi hai lần, một lần bằng hàm thế hệ thứ 1 và một lần bằng hàm thế hệ thứ 2, để phản hồi các sự kiện đó sẽ giúp ứng dụng của bạn ở 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ệ thứ 1 ban đầu và hàm thế hệ thứ 2 đều đang chạy.
    1. Trong trường hợp điều kiện 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ả các ứng dụng đến hàm thế hệ thứ 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ệ thứ 2.
    2. Với điều kiện kích hoạt nền, cả hàm thế hệ thứ 1 và thế hệ thứ 2 sẽ phản hồi mọi sự kiện ngay lập tức sau 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ệ thứ 1 bằng lệnh firebase functions:delete của Giao diện dòng lệnh (CLI) của Firebase.
    1. Bạn có thể đổi tên hàm thế hệ thứ 2 để khớp với tên của hàm thế hệ thứ 1.