Mẹo và thủ thuật

Tài liệu này mô tả các phương pháp hay nhất để thiết kế, triển khai, kiểm thử, và triển khai Cloud Functions.

Tính chính xác

Phần này mô tả các phương pháp chung hay nhất để thiết kế và triển khai Cloud Functions.

Viết các hàm giá trị giống nhau

Các hàm của bạn phải cho ra cùng một kết quả ngay cả khi chúng được gọi là nhiều lần. Điều này cho phép bạn thử gọi lại nếu lệnh gọi trước đó không thành công một phần trong mã của bạn. Để biết thêm thông tin, hãy xem thử lại các hàm dựa trên sự kiện.

Không bắt đầu hoạt động trong nền

Hoạt động ở chế độ nền là mọi hoạt động xảy ra sau khi hàm của bạn kết thúc. Lệnh gọi hàm kết thúc sau khi hàm trả về hoặc phát tín hiệu khác hoàn thành, chẳng hạn như bằng cách gọi đối số callback trong Node.js dựa trên sự kiện . Mọi mã chạy sau khi kết thúc linh hoạt đều không thể truy cập vào CPU và sẽ không có gì tiến triển.

Ngoài ra, khi lệnh gọi tiếp theo được thực thi trong cùng một môi trường, hoạt động trong nền của bạn sẽ tiếp tục, cản trở lệnh gọi mới. Việc này có thể dẫn đến hành vi không mong muốn và lỗi khó chẩn đoán. Truy cập mạng sau khi một chức năng bị chấm dứt thường dẫn đến việc kết nối bị đặt lại (ECONNRESET mã lỗi).

Hoạt động ở chế độ nền thường có thể được phát hiện trong nhật ký của từng lệnh gọi riêng lẻ, bằng cách tìm bất kỳ nội dung nào được ghi lại sau dòng lệnh gọi đã hoàn tất. Hoạt động trong nền đôi khi có thể được tập trung sâu hơn trong mã, đặc biệt khi có các thao tác không đồng bộ như lệnh gọi lại hoặc bộ tính giờ. Xem lại mã của bạn để đảm bảo tất cả các hoạt động không đồng bộ hoàn tất trước khi bạn thực hiện chấm dứt hàm này.

Luôn xoá các tệp tạm thời

Lưu trữ ổ đĩa trên máy trong thư mục tạm thời là một hệ thống tệp trong bộ nhớ. Tệp mà bạn ghi sẽ sử dụng bộ nhớ có sẵn cho hàm và đôi khi vẫn tồn tại giữa các lần gọi. Nếu bạn không thể xóa các tệp này một cách rõ ràng, thì cuối cùng, dẫn đến lỗi hết bộ nhớ và khởi động nguội tiếp theo.

Bạn có thể xem bộ nhớ mà một chức năng riêng lẻ sử dụng bằng cách chọn bộ nhớ đó trong danh sách hàm trong Google Cloud Console và chọn biểu đồ Mức sử dụng bộ nhớ.

Đừng cố ghi ra bên ngoài thư mục tạm thời và nhớ sử dụng nền tảng/hệ điều hành để tạo đường dẫn tệp.

Bạn có thể giảm yêu cầu về bộ nhớ khi xử lý các tệp lớn hơn bằng cách sử dụng tính năng quy trình (pipeline). Ví dụ: bạn có thể xử lý tệp trên Cloud Storage bằng cách tạo một luồng đọc, truyền nó qua một quy trình dựa trên luồng và ghi luồng đầu ra trực tiếp lên Cloud Storage.

Khung hàm

Khi bạn triển khai một hàm, Khung hàm sẽ tự động được thêm dưới dạng một bằng phiên bản hiện tại của phần phụ thuộc đó. Để đảm bảo rằng các phần phụ thuộc giống nhau được cài đặt nhất quán trên các môi trường khác nhau, do đó, bạn ghim hàm của mình vào một phiên bản cụ thể của Khung hàm.

Để thực hiện việc này, hãy thêm phiên bản ưu tiên của bạn vào tệp khoá liên quan (ví dụ: package-lock.json đối với Node.js hoặc requirements.txt đối với Python).

Công cụ

Phần này đưa ra hướng dẫn về cách sử dụng các công cụ để triển khai, thử nghiệm và tương tác với Cloud Functions.

Phát triển cục bộ

Việc triển khai hàm mất một chút thời gian nên thường sẽ nhanh hơn khi kiểm thử mã của hàm.

Nhà phát triển Firebase có thể sử dụng Trình mô phỏng Cloud Functions CLI của Firebase.

Sử dụng Sendgrid để gửi email

Cloud Functions không cho phép các kết nối đi trên cổng 25, nên bạn không thể tạo kết nối không an toàn đến máy chủ SMTP. Cách gửi được đề xuất gửi email bằng SendGrid. Bạn có thể tìm thấy các lựa chọn khác để gửi email trong Gửi email từ thực thể hướng dẫn dành cho Google Compute Engine.

Hiệu suất

Phần này mô tả các phương pháp hay nhất để tối ưu hoá hiệu suất.

Sử dụng phần phụ thuộc một cách khôn ngoan

Vì các hàm không có trạng thái nên môi trường thực thi thường được khởi tạo từ đầu (trong quá trình khởi động nguội). Khi khởi động nguội xảy ra, ngữ cảnh toàn cục của hàm sẽ được đánh giá.

Nếu các hàm của bạn nhập mô-đun, thời gian tải cho các mô-đun đó có thể thêm vào độ trễ của lệnh gọi trong khi khởi động nguội. Bạn có thể giảm độ trễ này cũng như thời gian cần thiết để triển khai hàm bằng cách tải các phần phụ thuộc một cách chính xác và không tải các phần phụ thuộc mà hàm của bạn không sử dụng.

Dùng biến toàn cục để sử dụng lại đối tượng trong các lệnh gọi trong tương lai

Không có gì đảm bảo rằng trạng thái của một hàm sẽ là được bảo tồn cho các lệnh gọi trong tương lai. Tuy nhiên, Cloud Functions thường tái chế môi trường thực thi của lệnh gọi trước đó. Nếu bạn khai báo biến trong toàn cầu, giá trị của nó có thể được sử dụng lại trong các lệnh gọi tiếp theo mà không phải để được tính toán lại.

Bằng cách này, bạn có thể lưu các đối tượng có thể tốn kém vào bộ nhớ đệm trên mỗi lệnh gọi hàm. Di chuyển các đối tượng như vậy từ phần nội dung hàm sang phạm vi toàn cục có thể giúp cải thiện đáng kể hiệu suất. Ví dụ sau đây chỉ tạo một đối tượng nặng một lần cho mỗi thực thể hàm và chia sẻ đối tượng đó trên tất cả lời gọi hàm tiếp cận thực thể đã cho:

Node.js

console.log('Global scope');
const perInstance = heavyComputation();
const functions = require('firebase-functions');

exports.function = functions.https.onRequest((req, res) => {
  console.log('Function invocation');
  const perFunction = lightweightComputation();

  res.send(`Per instance: ${perInstance}, per function: ${perFunction}`);
});

Python

import time

from firebase_functions import https_fn

# Placeholder
def heavy_computation():
  return time.time()

# Placeholder
def light_computation():
  return time.time()

# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()

@https_fn.on_request()
def scope_demo(request):

  # Per-function scope
  # This computation runs every time this function is called
  function_var = light_computation()
  return https_fn.Response(f"Instance: {instance_var}; function: {function_var}")
  

Hàm HTTP này nhận một đối tượng yêu cầu (flask.Request) và trả về nội dung phản hồi hoặc bất kỳ tập hợp giá trị nào có thể chuyển thành Đối tượng Response đang sử dụng make_response.

Điều đặc biệt quan trọng là lưu các kết nối mạng, tài liệu tham khảo thư viện vào bộ nhớ đệm, và đối tượng ứng dụng API trong phạm vi toàn cầu. Hãy xem bài viết Tối ưu hoá mạng để biết thêm ví dụ.

Thực hiện khởi chạy từng phần các biến toàn cục

Nếu bạn khởi tạo các biến trong phạm vi toàn cục, mã khởi chạy sẽ luôn được thực thi thông qua lệnh gọi khởi động nguội, làm tăng độ trễ của hàm. Trong một số trường hợp nhất định, điều này gây ra thời gian chờ không liên tục đối với các dịch vụ được gọi nếu chúng không được xử lý thích hợp trong khối try/catch. Nếu một số đối tượng không được dùng trong tất cả đường dẫn mã, hãy cân nhắc khởi tạo từng đối tượng theo yêu cầu:

Node.js

const functions = require('firebase-functions');
let myCostlyVariable;

exports.function = functions.https.onRequest((req, res) => {
  doUsualWork();
  if(unlikelyCondition()){
      myCostlyVariable = myCostlyVariable || buildCostlyVariable();
  }
  res.status(200).send('OK');
});

Python

from firebase_functions import https_fn

# Always initialized (at cold-start)
non_lazy_global = file_wide_computation()

# Declared at cold-start, but only initialized if/when the function executes
lazy_global = None

@https_fn.on_request()
def lazy_globals(request):

  global lazy_global, non_lazy_global

  # This value is initialized only if (and when) the function is called
  if not lazy_global:
      lazy_global = function_specific_computation()

  return https_fn.Response(f"Lazy: {lazy_global}, non-lazy: {non_lazy_global}.")
  

Hàm HTTP này sử dụng các tập lệnh toàn cục được khởi tạo từng phần. Phương thức này nhận một đối tượng yêu cầu (flask.Request) và trả về văn bản phản hồi hoặc bất kỳ tập giá trị nào mà có thể chuyển thành đối tượng Response bằng cách sử dụng make_response.

Điều này đặc biệt quan trọng nếu bạn xác định nhiều hàm trong một tệp duy nhất, và các hàm khác nhau sử dụng các biến khác nhau. Trừ phi bạn dùng lazy bạn có thể lãng phí tài nguyên vào các biến được khởi tạo nhưng chưa bao giờ được sử dụng.

Giảm số lượt khởi động nguội bằng cách thiết lập số lượng thực thể tối thiểu

Theo mặc định, Cloud Functions sẽ điều chỉnh số lượng thực thể dựa trên số lượng yêu cầu đến. Bạn có thể thay đổi hành vi mặc định này bằng cách đặt một số lượng thực thể tối thiểu mà Cloud Functions phải luôn sẵn sàng để phân phát. Việc đặt số lượng thực thể tối thiểu sẽ làm giảm số lượng thực thể khởi động nguội ứng dụng của bạn. Bạn nên đặt số lượng thực thể tối thiểu nếu ứng dụng nhạy cảm về độ trễ.

Xem Kiểm soát hành vi điều chỉnh tỷ lệ để biết thêm thông tin về các tuỳ chọn thời gian chạy này.

Tài nguyên khác

Tìm hiểu thêm về cách tối ưu hoá hiệu suất trong bài viết "Hiệu suất của Google Cloud Atlas" video Cloud Functions Thời gian khởi động nguội.