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 Chức năng đám mây.

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 Chức năng đám mây.

Viết các hàm không thay đổi

Các hàm của bạn phải tạo ra cùng một kết quả ngay cả khi được gọi nhiều lần. Điều này cho phép bạn thử lại lệnh gọi nếu lệnh gọi trước đó không thành công thông qua mã của bạn. Để biết thêm thông tin, vui lòng xem phần 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 trong nền là bất cứ điều gì xảy ra sau khi chức năng của bạn bị chấm dứt. Lệnh gọi hàm kết thúc sau khi hàm trả về hoặc báo hiệu việc hoàn tất, chẳng hạn như bằng cách gọi đối số callback trong các hàm dựa trên sự kiện Node.js. Bất kỳ mã nào chạy sau khi chấm dứt hợp lệ đều không thể truy cập vào CPU và sẽ không thực hiện bất cứ tiến trình nào.

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 ở chế độ nền sẽ tiếp tục, gây cản trở cho lệnh gọi mới. Điều này có thể dẫn đến các hành vi và lỗi không mong muốn rất khó chẩn đoán. Việc truy cập mạng sau khi một hàm chấm dứt thường dẫn đến việc các kết nối bị đặt lại (mã lỗi ECONNRESET).

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 cho biết lệnh gọi đã kết thúc. Đôi khi, hoạt động ở chế độ nền có thể bị chôn sâu hơn trong mã, đặc biệt là khi có các thao tác không đồng bộ như lệnh gọi lại hoặc bộ tính giờ. Hãy xem lại mã của bạn để đảm bảo tất cả các hoạt động không đồng bộ đã kết thúc trước khi bạn chấm dứt hàm.

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

Bộ nhớ ổ đĩa cục bộ trong thư mục tạm thời là một hệ thống tệp trong bộ nhớ. Các 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ệnh gọi. Nếu không xoá các tệp này một cách rõ ràng, thì có thể dẫn đến lỗi hết bộ nhớ và khởi động nguội sau đó.

Bạn có thể xem bộ nhớ mà một hàm riêng lẻ sử dụng bằng cách chọn hàm đó trong danh sách hàm trong Bảng điều khiển GCP rồi chọn biểu đồ Mức sử dụng bộ nhớ.

Đừng tìm cách ghi bên ngoài thư mục tạm thời và hãy nhớ sử dụng các phương thức độc lập với nền tảng/hệ điều hành để tạo đường dẫn tệp.

Bạn có thể giảm các yêu cầu về bộ nhớ khi xử lý các tệp lớn hơn bằng tính năng pipeline. Ví dụ: bạn có thể xử lý một tệp trên Cloud Storage bằng cách tạo một luồng đọc, truyền tệp đó qua một quy trình dựa trên luồng và ghi luồng đầu ra trực tiếp vào 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 phần phụ thuộc bằng cách sử dụng phiên bản hiện tại của hàm đó. Để đảm bảo cài đặt các phần phụ thuộc giống nhau một cách nhất quán trên nhiều môi trường, bạn nê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 bạn muốn vào tệp khoá có 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 cung cấp hướng dẫn về cách sử dụng các công cụ để triển khai, kiểm thử và tương tác với Chức năng đám mây.

Phát triển cục bộ

Việc triển khai hàm mất một chút thời gian, vì vậy, việc kiểm thử mã hàm cục bộ trên máy sẽ nhanh hơn.

Nhà phát triển Firebase có thể sử dụng Trình mô phỏng chức năng đám mây CLI của Firebase.

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

Chức năng đám mây không cho phép kết nối đi trên cổng 25, vì vậy, bạn không thể thực hiện các kết nối không an toàn tới máy chủ SMTP. Bạn nên sử dụng SendGrid (Gửi email) để gửi email. Bạn có thể tìm thấy các lựa chọn khác để gửi email trong hướng dẫn Gửi email từ một thực thể 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 hợp lý

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 này được gọi là khởi động nguội). Khi khởi động nguội, ngữ cảnh chung của hàm sẽ được đánh giá.

Nếu các hàm của bạn nhập mô-đun, thì thời gian tải của các mô-đun đó có thể làm tăng độ trễ của lệnh gọi trong quá trình 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 đúng cách và không tải các phần phụ thuộc mà hàm của bạn không sử dụng.

Sử dụng biến toàn cục để sử dụng lại đối tượng trong các lệnh gọi sau này

Không có gì đảm bảo rằng trạng thái của Hàm đám mây sẽ được giữ nguyên cho các lệnh gọi trong tương lai. Tuy nhiên, Hàm đám mây 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 một biến trong phạm vi toàn cầu, thì giá trị của biến đó có thể được sử dụng lại trong các lệnh gọi tiếp theo mà không cần phải 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ể gây tốn kém vào bộ nhớ đệm khi tạo lại trên mỗi lệnh gọi hàm. Việc di chuyển các đối tượng như vậy từ nội dung hàm sang phạm vi toàn cục có thể dẫn đến những cải thiện đáng kể về 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ả các lệnh gọi hàm đến thực thể nhất định:

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 đố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 hợp giá trị nào có thể chuyển thành đối tượng Response bằng cách sử dụng make_response.

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

Thực hiện khởi động 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, thì mã khởi tạo 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 sẽ dẫn đến thời gian chờ gián đoạn đối với các dịch vụ được gọi nếu các dịch vụ đó không được xử lý đúng cách trong khối try/catch. Nếu một số đối tượng không được dùng trong tất cả các đường dẫn mã, hãy cân nhắc khởi chạy các đối tượng đó từng phần 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 sẽ 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 hợp giá trị nào 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 và các hàm khác nhau sẽ sử dụng các biến khác nhau. Trừ phi bạn sử dụng tính năng khởi chạy từng phần, bạn có thể lãng phí tài nguyên vào các biến được khởi tạo nhưng không bao giờ được sử dụng.

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

Theo mặc định, Chức năng đám mây đ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 thiết lập số lượng thực thể tối thiểu mà Chức năng đám mây phải luôn sẵn sàng phân phát các yêu cầu. Việc đặt số lượng thực thể tối thiểu sẽ giảm số lần khởi động nguội ứng dụng. Bạn nên đặt số lượng thực thể tối thiểu nếu ứng dụng của bạn nhạy cảm về độ trễ.

Hãy xem phần Kiểm soát hành vi điều chỉnh theo 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 video "Google Cloud Performance Atlas" có tên Thời gian khởi động nguội của các chức năng Cloud.