Các ứng dụng sử dụng hàm thế hệ thứ 1 nên cân nhắc việ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ứ hai 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 lợi ích khác.
Các ví dụ trong tài liệu này giả định rằng bạn đang sử dụng JavaScript với các mô-đun CommonJS (nhập theo kiểu require), nhưng các nguyên tắc tương tự cũng áp dụng cho JavaScript với ESM (nhập theo 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 di chuyển cơ sở mã từng phần khi bạn đã 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 Firebase CLI và firebase-functions phiên bản
Đảm bảo bạn đang sử dụng ít nhất Firebase CLI phiên bản 12.00 và
firebase-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 các 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ì Firebase CLI cần để xác định xem 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 đây: thế hệ thứ 1
const functions = require("firebase-functions/v1");
Sau này: 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 đây: 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 này: 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) => {
/* ... */
});
Giảm thiểu nỗ lực viết lại bằng cách giải mã JavaScript
Nếu các hàm của bạn có phần nội dung phức tạp, phụ thuộc nhiều vào ngữ cảnh thế hệ thứ 1 hoặc các tham số dành riêng cho nhà cung cấp (như message hoặc snapshot), bạn có thể sử dụng các trình trợ giúp về khả năng tương thích thế hệ thứ 1 được tích hợp trong SDK thế hệ thứ 2.
SDK thế hệ thứ 2 tự động vá đối tượng sự kiện bằng các phương thức getter khớp với chữ ký thế hệ thứ 1. Điều này cho phép bạn sử dụng tính năng giải mã JavaScript để trích xuất trực tiếp các thuộc tính này trong chữ ký trình xử lý, giảm thiểu nhu cầu viết lại logic hàm.
Tài liệu tham khảo về liên kết nhà cung cấp
| Nhà cung cấp | Đối số thế hệ thứ 1 | Giải mã sự kiện đã vá thế hệ thứ 2 |
| Pub/Sub | (message, context)
|
({ message, context }) => { ... }
|
| Firestore | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Bộ nhớ | (object, context)
|
({ object, context }) => { ... }
|
| Cơ sở dữ liệu theo thời gian thực | (snapshot, context)
|
({ snapshot, context }) => { ... }
|
| Cấu hình từ xa | (version, context)
|
({ version, context }) => { ... }
|
| Trình lập lịch biểu | (context)
|
({ context }) => { ... }
|
| Hàng đợi tác vụ | (data, context)
|
({ data, context }) => { ... }
|
Trước đây (thế hệ thứ 1):
export const myPubSubV1 = functions.pubsub.topic("my-topic").onPublish((message, context) => {
const data = message.json;
const eventId = context.eventId;
// ... rest of the logic
});
Giải pháp thay thế mới (thế hệ thứ 2 có tính năng giải mã):
import { onMessagePublished } from "firebase-functions/v2/pubsub";
export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
// No need to change the function body!
const data = message.json; // Uses v1 Message wrapper
const eventId = context.eventId; // Uses v1 EventContext map
// ... rest of the logic
});
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 để xác định các tham số cấu hình một cách khai báo trong cơ sở mã của bạn.
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.
Trước đây: 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 này: 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 của bạn 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.
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 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```Cập nhật mã hàm để liên kết các bí mật
Để sử dụng cấu hình được lưu trữ trong bí mật mới trong Cloud Secret Manager, hãy sử dụng API
defineJsonSecrettrong nguồn hàm của bạn. Ngoài ra, hãy đảm bảo rằng các bí mật được liên kết với tất cả các hàm cần đến chúng.Trước đây
const functions = require("firebase-functions/v1"); exports.myFunction = functions.https.onRequest((req, res) => { const apiKey = functions.config().someapi.key; // ... });Sau này
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; // ... });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 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 khả năng mới để đặt các tuỳ chọn cho tất cả các hàm.
Trước đây: 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 này: 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 các hàm thế hệ thứ 1 sử dụng tài khoản dịch vụ mặc định của Google App Engine để cho phép truy cập vào các API Firebase, thì các 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 của 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 đồng thời
Một ưu điểm đáng kể của các hàm thế hệ thứ 2 là khả năng phục vụ nhiều yêu cầu cùng một lúc của một thực thể hàm duy nhất. Đ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 đồ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 đồ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 đồ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ệ thứ nhất được viết mà không tính đến tí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 đồng thời được bật và một thực thể duy nhất 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 sử 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 các lợi thế về hiệu suất của các 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 các 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 một hàm, bạn sẽ cần đặt tên mới cho hàm thế hệ thứ hai 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ẽ gây ra lỗi:
Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.
Chiến lược di chuyển phụ thuộc vào loại điều kiện kích hoạt mà hàm của bạn sử dụng.
Đối với các điều kiện kích hoạt có thể gọi, hàng đợi tác vụ và HTTP
Các điều kiện kích hoạt này là các lệnh gọi trực tiếp. Vì hàm thế hệ thứ 2 sẽ có tên mới (và URL mới cho các điều kiện kích hoạt HTTP), bạn có thể di chuyển lưu lượng truy cập bằng cách cập nhật các ứng dụng khách.
- Đổi tên hàm trong mã của bạn (ví dụ: đổi tên
myCallablethànhmyCallableV2). - Triển khai hàm. Cả hàm thế hệ thứ 1 và thế hệ thứ 2 hiện đều đang chạy.
- Cập nhật mã ứng dụng khách hoặc trình gọi để trỏ đến tên hoặc URL của hàm thế hệ thứ 2 mới.
- Sau khi tất cả lưu lượng truy cập đã chuyển sang hàm mới, hãy xoá hàm thế hệ thứ 1 bằng lệnh
firebase functions:deletecủa Giao diện dòng lệnh (CLI) của Firebase.
Đối với các điều kiện kích hoạt ở chế độ nền (Pub/Sub, Cloud Firestore, Cloud Storage, v.v.)
Các điều kiện kích hoạt ở chế độ nền phản hồi các sự kiện trong dự án của bạn. Để tránh bỏ lỡ bất kỳ sự kiện nào trong quá trình chuyển đổi, bạn phải tạm thời chạy cả hàm thế hệ thứ 1 và thế hệ thứ 2 song song.
Trong giai đoạn chuyển đổi, cả hai hàm sẽ kích hoạt cùng một sự kiện. Điều này có nghĩa là logic trong kinh doanh của bạn sẽ chạy hai lần cho mỗi sự kiện. Đảm bảo hàm của bạn là hàm luỹ đẳng trước khi tiếp tục.
Bước 1: Thêm hàm thế hệ thứ 2 cùng với hàm thế hệ thứ 1
Giữ hàm thế hệ thứ 1 hiện có trong mã của bạn và thêm hàm thế hệ thứ 2 đang theo dõi cùng một nguồn sự kiện.
Mẹo chuyên nghiệp: Sử dụng tính năng chuyển tiếp để xác minh
Để tránh sao chép logic trong kinh doanh trong cơ sở mã của bạn trong quá trình chuyển đổi,
hoặc để xác minh rằng hàm thế hệ thứ 2 đang nhận sự kiện chính xác trước khi
hoàn toàn tin tưởng hàm đó, hãy tạo hàm thế hệ thứ 2 chuyển tiếp gọi hàm thế hệ thứ 1 bằng phương thức run.
import * as functions from "firebase-functions/v1";
import { onMessagePublished } from "firebase-functions/v2/pubsub";
// --- Existing 1st gen function ---
export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => {
console.log("V1 handler running for event:", context.eventId);
// ... existing v1 function logic ...
});
// --- New v2 passthrough function ---
export const myPubSubV2 = onMessagePublished("my-topic", async ({ message, context }) => {
console.log("v2 handler triggering V1 for event:", context.eventId);
// Call the v1 function's handler
await myPubSub.run(message, context);
});
Bước 2: Triển khai cả hai hàm
Chạy firebase deploy. Cả hai hàm hiện đều đang hoạt động và theo dõi cùng một
sự kiện.
Bước 3: Xác minh rằng hàm thế hệ thứ hai đang nhận lưu lượng truy cập
Theo dõi nhật ký cho cả hai hàm. Đảm bảo rằng hàm thế hệ thứ 2 được gọi cho tất cả các sự kiện và các lệnh gọi đều thành công.
Bước 4: Chuyển logic đầy đủ sang hàm thế hệ thứ 2
Sau khi tự tin, hãy chuyển logic kinh doanh thực tế từ hàm thế hệ thứ 1 vào phần nội dung của hàm thế hệ thứ 2. Nếu bạn đã sử dụng phương thức chuyển tiếp, hãy xoá
lệnh gọi đến myPubSub.run().
import * as functions from "firebase-functions/v1";
import { onMessagePublished } from "firebase-functions/v2/pubsub";
// --- Existing v1 function (to be removed next) ---
export const myPubSub = functions.pubsub.topic("my-topic").onPublish((message, context) => {
console.log("v1 handler running for event:", context.eventId);
// ... existing v1 function logic ...
});
// --- New v2 function with full logic ---
export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
console.log("v2 handler running for event:", context.eventId);
// ... existing v1 function logic WAS MOVED HERE ...
});
Triển khai thay đổi này.
Bước 5: Huỷ triển khai hàm thế hệ thứ 1
Xoá định nghĩa hàm thế hệ thứ 1 khỏi mã của bạn và triển khai lại. CLI sẽ nhắc bạn xoá hàm thế hệ thứ 1 khỏi Google Cloud.
import { onMessagePublished } from "firebase-functions/v2/pubsub";
// --- V1 function definition REMOVED ---
// --- New v2 function with full logic ---
export const myPubSubV2 = onMessagePublished("my-topic", ({ message, context }) => {
console.log("v2 handler running for event:", context.eventId);
// ... existing v1 function logic ...
});