Bu belgede, Cloud Functions tasarlama, uygulama, test etme ve dağıtmaya yönelik 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.
İdempotent fonksiyonlar yazma
İşlevleriniz birden fazla kez çağrılsa bile aynı sonucu vermelidir. Bu sayede, önceki çağırma işlemi kodunuzun ortasında başarısız olursa çağırma işlemini yeniden deneyebilirsiniz. Daha fazla bilgi için etkinliğe 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.
Bir işlev çağırma işlemi, işlev döndüğünde veya başka bir şekilde tamamlanma sinyali verdiğinde (ör. Node.js'deki olaya dayalı işlevlerde callback
bağımsız değişkenini çağırarak) sona erer. 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ğırma işlemi yürütüldüğünde arka plan etkinliğiniz devam eder ve yeni çağırma işlemine müdahale eder. Bu durum, beklenmedik 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ğırmalardan gelen günlüklerde, çağırma işleminin bittiğini belirten satırdan sonra günlüğe kaydedilen her şeyi bularak tespit edilebilir. Arka plan etkinliği bazen kodun daha derinlerinde yer alabilir. Özellikle geri çağırma veya zamanlayıcı gibi eşzamansız işlemlerin bulunduğu durumlarda bu durum daha sık görülür. İşlevi sonlandırmadan önce tüm eşzamansız işlemlerin tamamlandığından emin olmak için kodunuzu inceleyin.
Geçici dosyaları her zaman silme
Geçici dizindeki yerel disk depolama alanı, bellek içi bir dosya sistemidir. Yazdığınız dosyalar, işlevinizin kullanabileceği belleği tüketir ve bazen çağrılar arasında kalıcı olur. Bu dosyaların açıkça silinmemesi, sonunda bellek yetersiz hatasına ve ardından soğuk başlatmaya yol açabilir.
Google Cloud Console'daki işlev listesinde ilgili işlevi seçip Bellek kullanımı grafiğini belirleyerek tek bir işlevin kullandığı belleği görebilirsiniz.
Uzun süreli depolamaya erişmeniz gerekiyorsa Cloud Run birim bağlamalarını Cloud Storage veya NFS birimleriyle kullanabilirsiniz.
Ardışık düzenleme kullanarak daha büyük dosyaları işlerken bellek gereksinimlerini azaltabilirsiniz. Örneğin, Cloud Storage'da bir dosyayı okuma akışı oluşturarak, akış tabanlı bir süreçten geçirerek ve çıkış akışını doğrudan Cloud Storage'a yazarak işleyebilirsiniz.
Functions Framework
Aynı bağımlılıkların tüm ortamlara 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 ekleyin (örneğin, Node.js için package-lock.json
veya Python için requirements.txt
).
Functions Framework, bağımlılık olarak açıkça listelenmiyorsa derleme işlemi sırasında otomatik olarak eklenir ve mevcut en son sürüm kullanılır.
Araçlar
Bu bölümde, Cloud Functions'yı uygulamak, test etmek ve Cloud Functions ile etkileşim kurmak için araçların nasıl kullanılacağıyla ilgili yönergeler verilmektedir.
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.İlk kullanıma hazırlama sırasında dağıtım zaman aşımlarından kaçınma
İşlev dağıtımınız zaman aşımı hatasıyla başarısız olursa bunun nedeni büyük olasılıkla işlevinizin genel kapsam kodunun dağıtım işlemi sırasında çok uzun sürede yürütülmesidir.
Firebase CLI, dağıtım sırasında işlevlerinizi keşfetmek için varsayılan bir zaman aşımına sahiptir. İşlevlerinizin kaynak kodundaki başlatma mantığı (modül yükleme, ağ çağrıları yapma vb.) bu zaman aşımını aşarsa dağıtım başarısız olabilir.
Zaman aşımını önlemek için aşağıdaki stratejilerden birini kullanın:
(Önerilir) Başlatmayı ertelemek için onInit()
kullanın.
Dağıtım sırasında ilk kullanıma hazırlama 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ırın
Kodunuzu onInit()
kullanacak şekilde yeniden düzenleyemiyorsanız FUNCTIONS_DISCOVERY_TIMEOUT
ortam değişkenini kullanarak CLI'nın dağıtım zaman aşımını 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 bir SMTP sunucusuna güvenli olmayan bağlantılar oluşturamazsı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 etmeye yönelik en iyi uygulamalar açıklanmaktadır.
Düşük eşzamanlılık durumundan kaçının
Sıfırdan başlatmalar maliyetli olduğundan, ani artış sırasında yakın zamanda başlatılan örnekleri yeniden kullanabilmek yükü yönetmek için harika bir optimizasyondur. Eşzamanlılık sınırlandırması, mevcut örneklerin nasıl kullanılabileceğini sınırlar ve bu nedenle 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ükteki ani artışların daha kolay yönetilmesini sağlar.Bağımlılıkları akıllıca kullanma
İşlevler durum bilgisi içermediğinden, yürütme ortamı genellikle sıfırdan başlatılır (soğuk başlatma olarak bilinir). Sıfırdan başlatma gerçekleştiğinde işlevin genel bağlamı değerlendirilir.
İşlevleriniz modül içe aktarıyorsa bu modüllerin yükleme süresi, sıfırdan başlatma sırasında çağırma gecikmesine eklenebilir. 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.
Gelecekteki çağırmalarda nesneleri yeniden kullanmak için genel değişkenleri kullanma
Bir işlevin durumunun gelecekteki çağırmalar için korunacağı garanti edilmez. Ancak Cloud Functions genellikle önceki bir çağırmanın yürütme ortamını yeniden kullanır. Bir değişkeni genel kapsamda tanımlarsanız değeri, yeniden hesaplanmasına gerek kalmadan sonraki çağırmalarda yeniden kullanılabilir.
Bu şekilde, her işlev çağrısında yeniden oluşturulması maliyetli olabilecek nesneleri önbelleğe alabilirsiniz. Bu tür nesneleri işlev gövdesinden genel kapsama taşımak, performansta önemli iyileşmeler sağlayabilir. Aşağıdaki örnek, işlev örneği başına yalnızca bir kez ağır bir nesne oluşturur ve bu nesneyi, söz konusu örneğe ulaşan tüm işlev çağrılarıyla paylaşı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 genel kapsamda önbelleğe almak özellikle önemlidir. Örnekler için Ağı optimize etme başlıklı makaleyi inceleyin.
Minimum örnek sayısı belirleyerek baştan başlatma işlemlerini azaltma
Cloud Functions, varsayılan olarak gelen istek sayısına göre örnek sayısını ö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ı ayarlamak, uygulamanızın baştan başlatma işlemlerini azaltır. Uygulamanız gecikmeye duyarlıysa minimum örnek sayısı belirlemenizi ve yükleme sırasında başlatma işlemini 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 olmadan, ilk isteğin başlatmayı tamamlaması ve modülleri yüklemesi gerekir. Bu da daha yüksek gecikmeye neden olur.
Ancak, genel başlatma da sıfırdan başlatmaları etkiler. Bu etkiyi en aza indirmek için yalnızca ilk istekte gerekenleri başlatarak ilk isteğin gecikmesini mümkün olduğunca düşük tutun.
Bu, özellikle gecikmeye duyarlı bir işlev için yukarıda açıklandığı gibi minimum örnek sayısını yapılandırdıysanız önemlidir. Bu senaryoda, yükleme sırasında başlatmanın tamamlanması ve faydalı verilerin önbelleğe alınması, ilk isteğin bunu yapmasına gerek kalmamasını ve düşük gecikmeyle sunulmasını sağlar.
Değişkenleri genel kapsamda başlatırsanız dile bağlı olarak uzun başlatma süreleri iki davranışa neden olabilir: - Bazı dil ve asenkron kitaplık kombinasyonlarında işlev çerçevesi asenkron olarak çalışabilir ve hemen geri dönebilir. Bu durum, kodun arka planda çalışmaya devam etmesine neden olarak 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, ilk kullanıma hazırlama işlemi tamamlanana kadar isteklerin yayınlanmamasını da sağlar. - Öte yandan, başlatma işlemi senkron ise uzun başlatma süresi daha uzun sıfırdan başlatma işlemlerine neden olur. Bu durum, özellikle yük artışları sırasında eşzamanlılık oranı düşük olan işlevlerde 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 özelliğinden yararlanmak için aşağıdaki kod, yükleme sırasında yükleme ve başlatma işlemlerini tamamlayarak modül yüklemesini engeller.
TLA kullanılır. Bu nedenle, node.js kodu için .mjs
uzantısı kullanılarak veya package.json dosyasına type: module
eklenerek ES6'nın kullanılması gerekir.
{ "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. });
Global 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 genel 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ülebilen herhangi bir değer kümesini döndürür.
Bu, özellikle tek bir dosyada birden fazla işlev tanımlarsanız ve farklı işlevler farklı değişkenler kullanırsa önemlidir. Temel başlatma kullanmadığınız sürece, başlatılan ancak hiç kullanılmayan değişkenler için kaynakları boşa harcayabilirsiniz.
Ek kaynaklar
"Google Cloud Performance Atlas" videosundaki Cloud Functions Cold Boot Time (Soğuk Başlatma Süresi) bölümünden performans optimizasyonu hakkında daha fazla bilgi edinin.