İpuçları ve püf noktaları

Bu belgede, Cloud Functions tasarlama, uygulama, test etme ve dağıtmayla ilgili en iyi uygulamalar açıklanmaktadır.

Doğruluk

Bu bölümde, Cloud Functions tasarlama ve uygulama ile ilgili genel en iyi uygulamalar açıklanmaktadır.

Eşgüçlü işlevler yazma

İşlevleriniz birden çok kez çağrılsa bile aynı sonucu üretmelidir. Bu sayede, önceki çağrı kodunuzun ortasında başarısız olursa çağrıyı yeniden deneyebilirsiniz. Daha fazla bilgi için olaya dayalı işlevleri yeniden deneme başlıklı makaleyi inceleyin.

Arka plan etkinliklerini başlatma

Arka plan etkinliği, işleviniz sonlandırıldıktan sonra gerçekleşen her şeydir. İşlev çağrısı, işlev döndürüldüğünde veya tamamlandığını bildirdiğinde (ör. Node.js etkinliğe dayalı işlevlerde callback bağımsız değişkenini çağırarak) tamamlanır. Düzgün sonlandırmadan sonra çalıştırılan kodlar CPU'ya erişemez ve ilerleme kaydedemez.

Ayrıca, aynı ortamda sonraki bir çağrı yürütüldüğünde arka plan etkinliğiniz devam eder ve yeni çağrıyı engeller. Bu durum, beklenmeyen davranışlara ve teşhis edilmesi zor hatalara neden olabilir. Bir işlev sonlandırıldıktan sonra ağa erişmek genellikle bağlantıların sıfırlanmasına (ECONNRESET hata kodu) neden olur.

Arka plan etkinliği genellikle tek tek çağrılardan alınan günlüklerde, çağrının bittiğini belirten satırdan sonra günlüğe kaydedilen her şey bulunarak tespit edilebilir. Arka plan etkinliği bazen kodun daha derinlerinde olabilir. Bu durum özellikle geri çağırmalar veya zamanlayıcılar gibi eşzamansız işlemler olduğunda geçerlidir. İşlevi sonlandırmadan önce tüm eşzamansız işlemlerin tamamlandığından emin olmak için kodunuzu inceleyin.

Geçici dosyaları her zaman sil

Geçici dizindeki yerel disk depolama alanı, bellek içi bir dosya sistemidir. Yazdığınız dosyalar, işleviniz için kullanılabilir olan belleği tüketir ve bazen çağrılar arasında kalır. Bu dosyaların açıkça silinmemesi, zaman içinde bellek yetersizliği hatasına ve ardından soğuk başlatmaya neden olabilir.

Google Cloud konsolundaki işlev listesinden bir işlevi seçip Bellek kullanımı grafiğini görüntüleyerek ilgili işlevin kullandığı bellek miktarını görebilirsiniz.

Uzun süreli depolama alanına erişmeniz gerekiyorsa Cloud Storage ile Cloud Run birim bağlamaları veya NFS birimleri kullanabilirsiniz.

Ardışık düzen kullanarak daha büyük dosyaları işlerken bellek gereksinimlerini azaltabilirsiniz. Örneğin, Cloud Storage'daki bir dosyayı okuma akışı oluşturarak, akış tabanlı bir işlemden geçirerek ve çıkış akışını doğrudan Cloud Storage'a yazarak işleyebilirsiniz.

Functions Framework

Aynı bağımlılıkların tüm ortamlarda tutarlı bir şekilde yüklendiğinden emin olmak için Functions Framework kitaplığını paket yöneticinize eklemenizi ve bağımlılığı Functions Framework'ün belirli bir sürümüne sabitlemenizi öneririz.

Bunu yapmak için tercih ettiğiniz sürümü ilgili kilit dosyasına (ör. Node.js için package-lock.json veya Python için requirements.txt) ekleyin.

Functions Framework, bağımlılık olarak açıkça listelenmemişse derleme işlemi sırasında mevcut en son sürüm kullanılarak otomatik olarak eklenir.

Araçlar

Bu bölümde, Cloud Functions uygulamasını uygulamak, test etmek ve etkileşimde bulunmak için araçların nasıl kullanılacağına dair yönergeler sağlanmaktadır.

Yerel geliştirme

İşlev dağıtımı biraz zaman alır. Bu nedenle, işlevinizin kodunu yerel olarak test etmek genellikle daha hızlıdır.

Firebase geliştiricileri, Firebase CLI Cloud Functions Emulator'ı kullanabilir.

Başlatma sırasında dağıtım zaman aşımını önleme

İşlev dağıtımınız zaman aşımı hatasıyla başarısız olursa bu durum, işlevinizin global kapsam kodunun dağıtım işlemi sırasında yürütülmesinin çok uzun sürdüğü anlamına gelebilir.

Firebase CLI, dağıtım sırasında işlevlerinizi keşfetmek için varsayılan bir zaman aşımı süresine sahiptir. İşlevlerinizin kaynak kodundaki başlatma mantığı (modülleri yükleme, ağ çağrıları yapma vb.) bu zaman aşımı süresini aşarsa dağıtım başarısız olabilir.

Zaman aşımını önlemek için aşağıdaki stratejilerden birini kullanın:

Dağıtım sırasında başlatma kodunun çalıştırılmasını önlemek için onInit() kancasını kullanın. onInit() kancasının içindeki kod, yalnızca işlev Cloud Run işlevlerine dağıtıldığında çalışır, dağıtım sürecinde çalışmaz.

Node.js

const { onInit } = require('firebase-functions/v2/core');
const { onRequest } = require('firebase-functions/v2/https');

// Example of a slow initialization task
function slowInitialization() {
  // Simulate a long-running operation (e.g., loading a large model, network request).
  return new Promise(resolve => {
      setTimeout(() => {
          console.log("Slow initialization complete");
          resolve("Initialized Value");
      }, 20000); // Simulate a 20-second delay
  });
}
let initializedValue;

onInit(async () => {
  initializedValue = await slowInitialization();
});

exports.myFunction = onRequest((req, res) => {
  // Access the initialized value. It will be ready after the first invocation.
  res.send(`Value: ${initializedValue}`);
});

Python

from firebase_functions.core import init
from firebase_functions import https_fn
import time

# Example of a slow initialization task
def _slow_initialization():
  time.sleep(20)  # Simulate a 20-second delay
  print("Slow initialization complete")
  return "Initialized Value"

_initialized_value = None

@init
def initialize():
  global _initialized_value
  _initialized_value = _slow_initialization()

@https_fn.on_request()
def my_function(req: https_fn.Request) -> https_fn.Response:
  # Access the initialized value. It will be ready after the first invocation.
  return https_fn.Response(f"Value: {_initialized_value}")

(Alternatif) Keşif zaman aşımını artırma

Kodunuzu onInit() kullanacak şekilde yeniden düzenleyemiyorsanız FUNCTIONS_DISCOVERY_TIMEOUT ortam değişkenini kullanarak CLI'nin dağıtım zaman aşımı süresini artırabilirsiniz:

$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions

E-posta göndermek için Sendgrid'i kullanma

Cloud Functions, 25 numaralı bağlantı noktasında giden bağlantılara izin vermediğinden SMTP sunucusuna güvenli olmayan bağlantılar kuramazsınız. E-posta göndermek için SendGrid gibi bir üçüncü taraf hizmeti kullanmanız önerilir. E-posta göndermeyle ilgili diğer seçenekleri Google Compute Engine için Örnekten E-posta Gönderme eğitiminde bulabilirsiniz.

Performans

Bu bölümde, performansı optimize etmeyle ilgili en iyi uygulamalar açıklanmaktadır.

Düşük eşzamanlılıktan kaçınma

Baştan başlatma işlemi maliyetli olduğundan, yükü yönetmek için ani artışlar sırasında yakın zamanda başlatılan örnekleri yeniden kullanabilmek harika bir optimizasyondur. Eşzamanlılığı sınırlamak, mevcut örneklerin nasıl kullanılabileceğini sınırlayarak daha fazla soğuk başlatmaya neden olur.

Eşzamanlılığı artırmak, örnek başına birden fazla isteğin ertelenmesine yardımcı olarak yük artışlarının daha kolay yönetilmesini sağlar.

Bağımlılıkları akıllıca kullanın

İşlevler durum bilgisi içermediğinden yürütme ortamı genellikle sıfırdan başlatılır (soğuk başlatma olarak bilinir). Soğuk başlatma gerçekleştiğinde işlevin global bağlamı değerlendirilir.

İşlevleriniz modülleri içe aktarıyorsa bu modüllerin yükleme süresi, soğuk başlatma sırasında çağrı gecikmesini artırabilir. Bağımlılıkları doğru şekilde yükleyerek ve işlevinizin kullanmadığı bağımlılıkları yüklemeyerek bu gecikmeyi ve işlevinizi dağıtmak için gereken süreyi azaltabilirsiniz.

Nesneleri gelecekteki çağrılarda yeniden kullanmak için global değişkenleri kullanma

Bir işlevin durumunun gelecekteki çağrılar için korunacağının garantisi yoktur. Ancak Cloud Functions, genellikle önceki bir çağrının yürütme ortamını geri dönüştürür. Global kapsamda bir değişken bildirirseniz değişkenin değeri, yeniden hesaplanmasına gerek kalmadan sonraki çağrılarda yeniden kullanılabilir.

Bu sayede, her işlev çağrısında yeniden oluşturulması maliyetli olabilecek nesneleri önbelleğe alabilirsiniz. Bu tür nesneleri işlev gövdesinden global kapsama taşımak, performansta önemli iyileştirmeler sağlayabilir. Aşağıdaki örnekte, işlev örneği başına yalnızca bir kez ağır bir nesne oluşturulur ve bu nesne, belirli örneğe ulaşan tüm işlev çağrıları arasında paylaşılır:

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}")
  

Bu HTTP işlevi bir istek nesnesi (flask.Request) alır ve yanıt metnini ya da make_response kullanılarak Response nesnesine dönüştürülebilecek herhangi bir değer kümesini döndürür.

Ağ bağlantılarını, kitaplık referanslarını ve API istemci nesnelerini global kapsamda önbelleğe almak özellikle önemlidir. Örnekler için Ağ performansını optimize etme başlıklı makaleyi inceleyin.

Minimum örnek sayısı belirleyerek soğuk başlatmaları azaltma

Varsayılan olarak Cloud Functions, örnek sayısını gelen isteklerin sayısına göre ölçeklendirir. Cloud Functions'ın istekleri karşılamak için hazır tutması gereken minimum örnek sayısını ayarlayarak bu varsayılan davranışı değiştirebilirsiniz. Minimum örnek sayısı belirlemek, uygulamanızın baştan başlatma sayısını azaltır. Uygulamanız gecikmeye duyarlıysa minimum sayıda örnek ayarlamanızı ve başlatma işlemini yükleme sırasında tamamlamanızı öneririz.

Bu çalışma zamanı seçenekleri hakkında daha fazla bilgi için Ölçeklendirme davranışını kontrol etme başlıklı makaleyi inceleyin.

Baştan başlatma ve başlatma ile ilgili notlar

Genel başlatma, yükleme sırasında gerçekleşir. Bu özellik kullanılmadığında ilk isteğin başlatma işlemini tamamlaması ve modülleri yüklemesi gerekir. Bu da daha yüksek gecikmeye neden olur.

Ancak global başlatma, baştan başlatma üzerinde de etkilidir. Bu etkiyi en aza indirmek için yalnızca ilk istek için gerekenleri ilk kullanıma hazırlayarak ilk isteğin gecikmesini mümkün olduğunca düşük tutun.

Bu, özellikle gecikmeye duyarlı bir işlev için minimum örnek sayısını yukarıda açıklandığı şekilde yapılandırdıysanız önemlidir. Bu senaryoda, yükleme sırasında başlatma işlemini tamamlamak ve faydalı verileri önbelleğe almak, ilk isteğin bu işlemi yapmasına gerek kalmamasını ve düşük gecikmeyle sunulmasını sağlar.

Değişkenleri global kapsamda başlatırsanız dile bağlı olarak uzun başlatma süreleri iki davranışa neden olabilir: - Bazı dil ve eşzamansız kitaplık kombinasyonlarında işlev çerçevesi eşzamansız olarak çalışabilir ve hemen geri dönebilir. Bu durum, kodun arka planda çalışmaya devam etmesine neden olur ve CPU'ya erişememe gibi sorunlara yol açabilir. Bunu önlemek için aşağıda açıklandığı gibi modül başlatma sırasında engelleme yapmanız gerekir. Bu sayede, başlatma işlemi tamamlanana kadar isteklerin yayınlanmaması da sağlanır. - Öte yandan, başlatma işlemi eşzamanlıysa uzun başlatma süresi, soğuk başlatma sürelerinin uzamasına neden olur. Bu durum, özellikle yük artışları sırasında düşük eşzamanlılık işlevlerinde sorun yaratabilir.

Eşzamansız bir node.js kitaplığını önceden ısıtma örneği

Firestore ile Node.js, asenkron Node.js kitaplığına bir örnektir. min_instances'tan yararlanmak için aşağıdaki kod, yükleme sırasında yükleme ve başlatma işlemlerini tamamlayarak modül yüklemeyi engeller.

TLA kullanılıyor. Bu, ES6'nın gerekli olduğu anlamına gelir. Node.js kodu için .mjs uzantısı kullanılır veya package.json dosyasına type: module eklenir.

{
  "main": "main.js",
  "type": "module",
  "dependencies": {
    "@google-cloud/firestore": "^7.10.0",
    "@google-cloud/functions-framework": "^3.4.5"
  }
}

Node.js

import Firestore from '@google-cloud/firestore';
import * as functions from '@google-cloud/functions-framework';

const firestore = new Firestore({preferRest: true});

// Pre-warm firestore connection pool, and preload our global config
// document in cache. In order to ensure no other request comes in,
// block the module loading with a synchronous global request:
const config = await firestore.collection('collection').doc('config').get();

functions.http('fetch', (req, res) => {

// Do something with config and firestore client, which are now preloaded
// and will execute at lower latency.
});

Genel başlatma örnekleri

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}.")
  

Bu HTTP işlevi, geç başlatılan global değişkenleri kullanır. Bir istek nesnesi (flask.Request) alır ve yanıt metnini ya da make_response kullanılarak Response nesnesine dönüştürülebilecek herhangi bir değer grubunu döndürür.

Bu, özellikle tek bir dosyada birden fazla işlev tanımlıyorsanız ve farklı işlevler farklı değişkenler kullanıyorsa önemlidir. Yavaş başlatma kullanmazsanız başlatılan ancak hiç kullanılmayan değişkenler için kaynakları boşa harcayabilirsiniz.

Ek kaynaklar

Performansı optimize etme hakkında daha fazla bilgi edinmek için "Google Cloud Performance Atlas" videosunu izleyin: Cloud Functions Soğuk Başlatma Süresi.