Google Cloud Platform Kullanarak Sunucusuz Ön Uç Uygulamaları Oluşturma
Yayınlanan: 2022-03-10Son zamanlarda, uygulamaların geliştirme paradigması, bir uygulamada kullanılan kaynakları manuel olarak dağıtmak, ölçeklendirmek ve güncellemek zorunda olmaktan, bu kaynakların yönetiminin çoğunu yapmak için üçüncü taraf bulut hizmeti sağlayıcılarına güvenmeye doğru kaymaya başladı.
Mümkün olan en kısa sürede pazara uygun bir uygulama oluşturmak isteyen bir geliştirici veya kuruluş olarak, ana odak noktanız, yapılandırma, dağıtım ve stres testi için daha az zaman harcarken temel uygulama hizmetinizi kullanıcılarınıza sunmak olabilir. başvurunuz. Kullanım durumunuz buysa, uygulamanızın iş mantığını sunucusuz bir şekilde ele almak en iyi seçeneğiniz olabilir. Ama nasıl?
Bu makale, uygulamalarında belirli işlevler oluşturmak isteyen ön uç mühendisleri veya Google Cloud Platform'a dağıtılan sunucusuz bir uygulama kullanarak mevcut bir arka uç hizmetinden belirli bir işlevi çıkarmak ve işlemek isteyen arka uç mühendisleri için faydalıdır.
Not : Burada anlatılanlardan faydalanmak için React ile çalışma deneyimine sahip olmanız gerekmektedir. Sunucusuz uygulamalarda önceden deneyim gerekmez.
Başlamadan önce, sunucusuz uygulamaların gerçekte ne olduğunu ve bir ön uç mühendis bağlamında bir uygulama oluştururken sunucusuz mimarinin nasıl kullanılabileceğini anlayalım.
Sunucusuz Uygulamalar
Sunucusuz uygulamalar, uygulama yazarı adına genel bulut içinde üçüncü taraf bulut hizmeti sağlayıcıları tarafından barındırılan ve yönetilen, yeniden kullanılabilir küçük olay güdümlü işlevlere bölünmüş uygulamalardır. Bunlar belirli olaylar tarafından tetiklenir ve talep üzerine yürütülür. Serverless kelimesine eklenen “ less ” eki sunucunun olmadığını gösterse de bu %100 değildir. Bu uygulamalar sunucularda ve diğer donanım kaynaklarında çalışmaya devam eder, ancak bu durumda bu kaynaklar geliştirici tarafından değil, üçüncü taraf bulut hizmeti sağlayıcısı tarafından sağlanır. Bu nedenle, uygulama yazarı için sunucusuzdurlar, ancak yine de sunucularda çalışırlar ve genel internet üzerinden erişilebilirler.
Sunucusuz bir uygulamanın kullanım örneği, açılış sayfanızı ziyaret eden ve ürün lansmanı e-postaları almaya abone olan potansiyel kullanıcılara e-posta göndermek olabilir. Bu aşamada, muhtemelen çalışan bir arka uç hizmetiniz yoktur ve bir tane oluşturmak, dağıtmak ve yönetmek için gereken zaman ve kaynakları feda etmek istemezsiniz, çünkü hepsi e-posta göndermeniz gerekir. Burada, bir e-posta istemcisi kullanan tek bir dosya yazabilir ve sunucusuz uygulamayı destekleyen herhangi bir bulut sağlayıcısına konuşlandırabilir ve bu sunucusuz uygulamayı açılış sayfanıza bağlarken sizin adınıza bu uygulamayı yönetmelerine izin verebilirsiniz.
Ön uç uygulamanız için sunucusuz uygulamalardan veya Hizmet Olarak İşlevler'den (FAAS) yararlanmayı düşünmeniz için pek çok neden olsa da, göz önünde bulundurmanız gereken bazı çok önemli nedenler şunlardır:
- Uygulama otomatik ölçeklendirme
Sunucusuz uygulamalar yatay olarak ölçeklenir ve bu “ ölçeklendirme ”, çağrıların miktarına bağlı olarak Bulut sağlayıcısı tarafından otomatik olarak yapılır, bu nedenle uygulama ağır yük altındayken geliştiricinin kaynakları manuel olarak eklemesi veya kaldırması gerekmez. - Maliyet etkinliği
Olaya dayalı, sunucusuz uygulamalar yalnızca gerektiğinde çalışır ve bu, çağrılan süreye göre faturalandırıldıkları için ücretlere de yansır. - Esneklik
Sunucusuz uygulamalar, yüksek oranda yeniden kullanılabilir olacak şekilde oluşturulmuştur ve bu, tek bir projeye veya uygulamaya bağlı olmadıkları anlamına gelir. Belirli bir işlevsellik, sunucusuz bir uygulamaya çıkarılabilir, birden çok proje veya uygulamada dağıtılabilir ve kullanılabilir. Sunucusuz uygulamalar, uygulama yazarının tercih ettiği dilde de yazılabilir, ancak bazı bulut sağlayıcıları yalnızca daha az sayıda dili destekler.
Sunucusuz uygulamalardan yararlanırken, her geliştiricinin genel bulut içinde yararlanabileceği çok çeşitli bulut sağlayıcıları vardır. Bu makale bağlamında, Google Cloud Platform'daki sunucusuz uygulamalara — nasıl oluşturulduklarına, yönetildiklerine, dağıtıldıklarına ve ayrıca Google Cloud'daki diğer ürünlerle nasıl entegre olduklarına odaklanacağız. Bunu yapmak için, süreç boyunca çalışırken bu mevcut React uygulamasına yeni işlevler ekleyeceğiz:
- Kullanıcı verilerini bulutta depolamak ve almak;
- Google Cloud'da cron işleri oluşturma ve yönetme;
- Cloud Functions'ı Google Cloud'a Dağıtma.
Not : Sunucusuz uygulamalar yalnızca React'e bağlı değildir, tercih ettiğiniz ön uç çerçeve veya kitaplık bir HTTP
isteği yapabildiği sürece sunucusuz bir uygulama kullanabilir.
Google Bulut İşlevleri
Google Cloud, geliştiricilerin Bulut İşlevlerini kullanarak sunucusuz uygulamalar oluşturmasına olanak tanır ve bunları İşlevler Çerçevesini kullanarak çalıştırır. Bulut işlevleri, mevcut altı olay tetikleyicisinden belirli tetikleyiciyi dinlemek ve ardından yürütmek için yazıldığı işlemi gerçekleştirmek için Google Cloud'a dağıtılan yeniden kullanılabilir olay güdümlü işlevlerdir.
Kısa ömürlü ( varsayılan yürütme zaman aşımı 60 saniye ve maksimum 9 dakika olan) bulut işlevleri JavaScript, Python, Golang ve Java kullanılarak yazılabilir ve çalışma zamanları kullanılarak yürütülebilir. JavaScript'te, yalnızca Node çalışma zamanının bazı kullanılabilir sürümleri kullanılarak yürütülebilirler ve Google Cloud'da çalıştırılacak birincil işlev olarak dışa aktarıldıkları için düz JavaScript kullanılarak CommonJS modülleri biçiminde yazılırlar.
Bulut işlevine bir örnek, işlevin bir kullanıcının verilerini işlemesi için boş bir standart olan aşağıdaki gibidir.
// index.js exports.firestoreFunction = function (req, res) { return res.status(200).send({ data: `Hello ${req.query.name}` }); }
Yukarıda bir fonksiyonu dışa aktaran bir modülümüz var. Yürütüldüğünde, HTTP
yoluna benzer istek ve yanıt argümanlarını alır.
Not : Bir istek yapıldığında, bir bulut işlevi her HTTP
protokolüyle eşleşir. Bir bulut işlevini yürütmek için istekte bulunurken eklenen veriler, GET
istekleri için sorgu gövdesindeyken POST
istekleri için istek gövdesinde bulunacağından, istek bağımsız değişkeninde veri beklerken bu dikkate değerdir.
Bulut işlevleri, geliştirme sırasında @google-cloud/functions-framework
paketini yazılı işlevin yerleştirildiği klasöre kurarak veya npm i -g @google-cloud/functions-framework
çalıştırarak birden çok işlev için kullanmak üzere global bir yükleme yaparak yerel olarak yürütülebilir. komut satırınızdan npm i -g @google-cloud/functions-framework
. Kurulduktan sonra, aşağıdakine benzer dışa aktarılan modülün adıyla package.json
betiğine eklenmelidir:
"scripts": { "start": "functions-framework --target=firestoreFunction --port=8000", }
Yukarıda, package.json
dosyasında, function-framework'ü çalıştıran ve ayrıca 8000
numaralı bağlantı noktasında yerel olarak çalıştırılacak hedef işlev olarak firestoreFunction
belirten betiklerimizde tek bir komut var.
Bu işlevin uç noktasını, curl kullanarak localhost'ta 8000
numaralı bağlantı noktasına GET
isteğinde bulunarak test edebiliriz. Aşağıdaki komutu bir terminale yapıştırmak bunu yapacak ve bir yanıt döndürecektir.
curl https://localhost:8000?name="Smashing Magazine Author"
Yukarıdaki komut, GET HTTP
yöntemiyle bir istek yapar ve 200
durum kodu ve sorguya eklenen adı içeren bir nesne verisi ile yanıt verir.
Bir Bulut İşlevi Dağıtma
Mevcut dağıtım yöntemleri arasında, yerel bir makineden bir bulut işlevini dağıtmanın hızlı bir yolu, kurduktan sonra bulut Sdk'sini kullanmaktır. Google Cloud'daki projenizle gcloud sdk'nin kimliğini doğruladıktan sonra aşağıdaki komutu terminalden çalıştırmak, yerel olarak oluşturulan bir işlevi Cloud Function hizmetine dağıtır.
gcloud functions deploy "demo-function" --runtime nodejs10 --trigger-http --entry-point=demo --timeout=60 --set-env-vars=[name="Developer"] --allow-unauthenticated
Aşağıda açıklanan işaretleri kullanarak, yukarıdaki komut, " demo-function " adıyla google bulutuna HTTP ile tetiklenen bir işlevi dağıtır.
- İSİM
Bu, bir bulut işlevine dağıtılırken verilen addır ve gereklidir. -
region
Bu, bulut işlevinin dağıtılacağı bölgedir. Varsayılan olarakus-central1
. -
trigger-http
Bu, işlevin tetikleyici türü olarak HTTP'yi seçer. -
allow-unauthenticated
Bu, işlevin, arayanın kimliğinin doğrulanıp doğrulanmadığını kontrol etmeden oluşturulan uç noktası kullanılarak İnternet üzerinden Google Cloud dışında çağrılmasına olanak tanır. -
source
Dağıtılacak işlevi içeren dosyaya uçbirimden yerel yol. -
entry-point
Bu, işlevlerin yazıldığı dosyadan dağıtılacak belirli dışa aktarılan modül. -
runtime
Bu, kabul edilen çalışma zamanı listesindeki işlev için kullanılacak dil çalışma zamanıdır. -
timeout
Bu, bir işlevin zaman aşımına uğramadan önce çalışabileceği maksimum süredir. Varsayılan olarak 60 saniyedir ve maksimum 9 dakikaya ayarlanabilir.
Not : Bir işlevin kimliği doğrulanmamış isteklere izin vermesini sağlamak, işlevinizin uç noktasına sahip olan herkesin, siz izin vermeden de istekte bulunabileceği anlamına gelir. Bunu azaltmak için, ortam değişkenleri aracılığıyla veya her istekte yetkilendirme başlıkları isteyerek uç noktanın özel kalmasını sağlayabiliriz.
Artık demo işlevimiz dağıtıldığına ve uç noktaya sahip olduğumuza göre, bu işlevi küresel bir otomatik top yüklemesi kullanan gerçek dünyadaki bir uygulamada kullanılıyormuş gibi test edebiliriz. Açılan terminalden autocannon -d=5 -c=300 CLOUD_FUNCTION_URL
çalıştırıldığında, bulut işlevine 5 saniyelik bir süre içinde 300 eşzamanlı istek oluşturulur. Bu, bulut işlevini başlatmak ve ayrıca işlevin kontrol panelinde keşfedebileceğimiz bazı ölçümler oluşturmak için fazlasıyla yeterlidir.
Not : Bir işlevin uç noktası, dağıtımdan sonra terminalde yazdırılacaktır. Aksi takdirde, uç nokta da dahil olmak üzere dağıtılan işlevle ilgili ayrıntıları almak için uçbirimden gcloud function describe FUNCTION_NAME
çalıştırın.
Gösterge tablosundaki metrikler sekmesini kullanarak, kaç çağrı yapıldığını, ne kadar sürdüğünü, işlevin bellek ayak izini ve yapılan istekleri işlemek için kaç örneğin döndürüldüğünü içeren son istekten görsel bir temsil görebiliriz.
Yukarıdaki resimdeki Etkin Örnekler grafiğine daha yakından bakıldığında, otomatik top kullanılarak yapılan istekleri işlemek için 209 örneğin birkaç saniye içinde döndürüldüğünü görebildiğimiz için Bulut İşlevlerinin yatay ölçeklendirme kapasitesi görülmektedir.
Bulut İşlev Günlükleri
Google bulutuna dağıtılan her işlevin bir günlüğü vardır ve bu işlev her yürütüldüğünde, o günlüğe yeni bir giriş yapılır. İşlev panosundaki Günlük sekmesinden, bir bulut işlevindeki tüm günlük girişlerinin bir listesini görebiliriz.
Aşağıda, autocannon
kullanarak yaptığımız istekler sonucunda oluşturulan, konuşlandırılmış demo-function
günlük girişleri bulunmaktadır.
Yukarıdaki günlük girişlerinin her biri, bir işlevin tam olarak ne zaman yürütüldüğünü, yürütmenin ne kadar sürdüğünü ve hangi durum koduyla sona erdiğini gösterir. Bir fonksiyondan kaynaklanan herhangi bir hata varsa, oluştuğu satır da dahil olmak üzere hatanın detayları buradaki günlüklerde gösterilecektir.
Google Cloud'daki Günlük Gezgini, bir bulut işlevindeki günlükler hakkında daha kapsamlı ayrıntıları görmek için kullanılabilir.
Ön Uç Uygulamaları ile Bulut İşlevleri
Bulut işlevleri, ön uç mühendisler için çok kullanışlı ve güçlüdür. Arka uç uygulamalarını yönetme bilgisi olmayan bir ön uç mühendisi, bir işlevi bir bulut işlevine çıkarabilir, Google Cloud'a dağıtabilir ve uç noktası aracılığıyla bulut işlevine HTTP
istekleri göndererek bir ön uç uygulamada kullanabilir.
Bir ön uç uygulamada bulut işlevlerinin nasıl kullanılabileceğini göstermek için bu React uygulamasına daha fazla özellik ekleyeceğiz. Uygulama, kimlik doğrulama ve ana sayfa kurulumu arasında zaten temel bir yönlendirmeye sahiptir. Oluşturulan bulut işlevlerinin kullanımı uygulama azaltıcılar içinde yapılacağından, uygulama durumumuzu yönetmek için React Context API'yi kullanacak şekilde genişleteceğiz.
Başlamak için, createContext
API'sini kullanarak uygulamamızın bağlamını oluşturuyoruz ve ayrıca uygulamamızdaki eylemleri işlemek için bir indirgeyici oluşturuyoruz.
// state/index.js import { createContext } from “react”;
dışa aktarma const UserReducer = (eylem, durum) => { geçiş (action.type) { case “CREATE-USER”: break; durum “KULLANICI-RESMİ YÜKLE”: break; durum “FETCH-DATA” : ara durum “LOGOUT” : ara; varsayılan: console.log(
${action.type} is not recognized
) } };dışa aktarma const userState = { user: null, isLoggedIn : false };
dışa aktarma const UserContext = createContext(userState);
Yukarıda, kendisine gönderilen eylemin türüne göre bir işlem gerçekleştirmesine izin veren bir switch ifadesi içeren bir UserReducer
işlevi oluşturmaya başladık. Switch ifadesinin dört durumu vardır ve bunlar ele alacağımız eylemlerdir. Şimdilik henüz bir şey yapmıyorlar ama bulut fonksiyonlarımızla entegrasyona başladığımızda, onlarda yapılacak işlemleri adım adım uygulayacağız.
Ayrıca, React createContext API'sini kullanarak uygulamamızın bağlamını oluşturduk ve dışa aktardık ve ona, kimlik doğrulamadan sonra şu anda null değerinden kullanıcının verilerine güncellenecek olan bir kullanıcı değeri ve ayrıca olup olmadığını bilmek için bir isLoggedIn
boole değeri içeren userState
nesnesinin varsayılan değerini verdik. kullanıcı oturum açtı veya oturum açmadı.
Şimdi bağlamımızı tüketmeye devam edebiliriz, ancak bunu yapmadan önce, alt bileşenlerin bağlamımızın değer değişikliğine abone olabilmesi için tüm uygulama ağacımızı UserContext
eklenen Provider ile sarmamız gerekir.
// index.js import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; import App from "./app"; import { UserContext, userState } from "./state/"; ReactDOM.render( <React.StrictMode> <UserContext.Provider value={userState}> <App /> </UserContext.Provider> </React.StrictMode>, document.getElementById("root") ); serviceWorker.unregister();
Enter uygulamamızı, root bileşenindeki UserContext
sağlayıcısı ile sardık ve prop değerinde önceden oluşturulmuş userState
varsayılan değerimizi ilettik.
Artık uygulama durumumuz tamamen kurulduğuna göre, bir bulut işlevi aracılığıyla Google Cloud Firestore'u kullanarak kullanıcının veri modelini oluşturmaya geçebiliriz.
Uygulama Verilerini İşleme
Bu uygulamadaki bir kullanıcının verileri benzersiz bir kimlik, bir e-posta, bir şifre ve bir görüntünün URL'sinden oluşur. Bir bulut işlevi kullanılarak bu veriler, Google Cloud Platform'da sunulan Cloud Firestore Hizmeti kullanılarak bulutta depolanacaktır.
Esnek bir NoSQL veritabanı olan Google Cloud Firestore , çevrimdışı veri desteğinin yanı sıra daha zengin ve daha hızlı sorgulara izin veren yeni geliştirilmiş özelliklerle Firebase Gerçek Zamanlı Veritabanından oluşturulmuştur. Firestore hizmeti içindeki veriler, MongoDB gibi diğer NoSQL veritabanlarına benzer şekilde koleksiyonlar ve belgeler halinde düzenlenir.
Firestore'a Google Cloud Console aracılığıyla görsel olarak erişilebilir. Başlatmak için sol gezinme bölmesini açın ve Veritabanı bölümüne gidin ve Firestore'a tıklayın. Bu, mevcut verilere sahip kullanıcılar için koleksiyonların listesini gösterir veya mevcut bir koleksiyon olmadığında kullanıcıdan yeni bir koleksiyon oluşturmasını ister. Uygulamamız tarafından kullanılacak bir kullanıcı koleksiyonu oluşturacağız.
Google Cloud Platform'daki diğer hizmetlere benzer şekilde Cloud Firestore, bir düğüm ortamında kullanılmak üzere oluşturulmuş bir JavaScript istemci kitaplığına sahiptir ( tarayıcıda kullanılırsa bir hata verilir). Doğaçlama yapmak için, @google-cloud/firestore
paketini kullanarak Cloud Firestore'u bir bulut işlevinde kullanıyoruz.
Cloud Firestore'u Bulut İşleviyle Kullanma
Başlamak için, demo-function
işlevinden oluşturduğumuz ilk işlevi firestoreFunction
olarak yeniden adlandıracağız ve ardından Firestore ile bağlantı kuracak ve verileri kullanıcılarımızın koleksiyonuna kaydedecek şekilde genişleteceğiz.
require("dotenv").config(); const { Firestore } = require("@google-cloud/firestore"); const { SecretManagerServiceClient } = require("@google-cloud/secret-manager"); const client = new SecretManagerServiceClient(); exports.firestoreFunction = function (req, res) { return { const { email, password, type } = req.body; const firestore = new Firestore(); const document = firestore.collection("users"); console.log(document) // prints details of the collection to the function logs if (!type) { res.status(422).send("An action type was not specified"); } switch (type) { case "CREATE-USER": break case "LOGIN-USER": break; default: res.status(422).send(`${type} is not a valid function action`) } };
Yangın deposunu içeren daha fazla işlemi ele almak için, uygulamamızın kimlik doğrulama ihtiyaçlarını ele almak için iki durumlu bir switch ifadesi ekledik. Switch deyimimiz, uygulamamızdan bu fonksiyona istek yaparken istek gövdesine eklediğimiz bir type
ifadesini değerlendirir ve bu type
veriler istek gövdemizde bulunmadığında, istek Kötü İstek ve 400
durum kodu olarak tanımlanır. eksik type
belirtmek için bir iletinin yanında yanıt olarak gönderilir.
Cloud Firestore istemci kitaplığı içindeki Application Default Credentials(ADC) kitaplığını kullanarak Firestore ile bağlantı kurarız. Sonraki satırda başka bir değişkende toplama yöntemini çağırıyoruz ve koleksiyonumuzun adını geçiyoruz. Bunu, içerilen belgelerin toplanmasıyla ilgili diğer işlemleri daha ileri düzeyde gerçekleştirmek için kullanacağız.
Not : Google Cloud'daki hizmetler için istemci kitaplıkları, oluşturucu başlatılırken iletilen oluşturulmuş bir hizmet hesabı anahtarını kullanarak ilgili hizmetlerine bağlanır. Hizmet hesabı anahtarı bulunmadığında, varsayılan olarak Uygulama Varsayılan Kimlik Bilgilerini kullanır ve bu da bulut işlevine atanan IAM
rollerini kullanarak bağlanır.
Gcloud SDK kullanılarak yerel olarak dağıtılan bir işlevin kaynak kodunu düzenledikten sonra, bulut işlevini güncellemek ve yeniden dağıtmak için önceki komutu bir terminalden yeniden çalıştırabiliriz.
Artık bir bağlantı kurulduğuna göre, istek gövdesindeki verileri kullanarak yeni bir kullanıcı oluşturmak için CREATE-USER
vakasını uygulayabiliriz.
require("dotenv").config(); const { Firestore } = require("@google-cloud/firestore"); const path = require("path"); const { v4 : uuid } = require("uuid") const cors = require("cors")({ origin: true }); const client = new SecretManagerServiceClient(); exports.firestoreFunction = function (req, res) { return cors(req, res, () => { const { email, password, type } = req.body; const firestore = new Firestore(); const document = firestore.collection("users"); if (!type) { res.status(422).send("An action type was not specified"); } switch (type) { case "CREATE-USER": if (!email || !password) { res.status(422).send("email and password fields missing"); } const id = uuid() return bcrypt.genSalt(10, (err, salt) => { bcrypt.hash(password, salt, (err, hash) => { document.doc(id) .set({ id : id email: email, password: hash, img_uri : null }) .then((response) => res.status(200).send(response)) .catch((e) => res.status(501).send({ error : e }) ); }); }); case "LOGIN": break; default: res.status(400).send(`${type} is not a valid function action`) } }); };
Kaydedilmek üzere olan belgenin ID'si olarak kullanılacak uuid paketini kullanarak belge üzerindeki set
metoduna ve ayrıca kullanıcının ID'sine geçirerek bir UUID oluşturduk. Varsayılan olarak, eklenen her belgede rastgele bir kimlik oluşturulur, ancak bu durumda, resim yüklemesini işlerken belgeyi güncelleyeceğiz ve belirli bir belgenin güncellenmesini sağlamak için UUID kullanılacaktır. Kullanıcının parolasını düz metin olarak saklamak yerine, önce bcryptjs kullanarak onu tuzlarız, ardından sonuç karmasını kullanıcının parolası olarak saklarız.
firestoreFunction
bulut işlevini uygulamaya entegre ederek, bunu kullanıcı azaltıcı içindeki CREATE_USER
durumundan kullanıyoruz.
Hesap Oluştur düğmesine tıkladıktan sonra, firestoreFunction
işlevinin uç noktasına yazılan e-posta ve parolayı içeren bir POST
isteği yapmak için CREATE_USER
türünde redüktörlere bir eylem gönderilir.
import { createContext } from "react"; import { navigate } from "@reach/router"; import Axios from "axios"; export const userState = { user : null, isLoggedIn: false, }; export const UserReducer = (state, action) => { switch (action.type) { case "CREATE_USER": const FIRESTORE_FUNCTION = process.env.REACT_APP_FIRESTORE_FUNCTION; const { userEmail, userPassword } = action; const data = { type: "CREATE-USER", email: userEmail, password: userPassword, }; Axios.post(`${FIRESTORE_FUNCTION}`, data) .then((res) => { navigate("/home"); return { ...state, isLoggedIn: true }; }) .catch((e) => console.log(`couldnt create user. error : ${e}`)); break; case "LOGIN-USER": break; case "UPLOAD-USER-IMAGE": break; case "FETCH-DATA" : break case "LOGOUT": navigate("/login"); return { ...state, isLoggedIn: false }; default: break; } }; export const UserContext = createContext(userState);
Yukarıda, firestoreFunction
istekte bulunmak için Axios'u kullandık ve bu istek çözüldükten sonra, istekten döndürülen verilere kullanıcının başlangıç durumunu null
olarak ayarladık ve son olarak kullanıcıyı kimliği doğrulanmış bir kullanıcı olarak ana sayfaya yönlendirdik. .
Bu noktada, yeni bir kullanıcı başarıyla bir hesap oluşturabilir ve ana sayfaya yönlendirilebilir. Bu süreç, bir bulut işlevinden temel veri oluşturma işlemini gerçekleştirmek için Firestore'u nasıl kullandığımızı gösterir.
Dosya Depolamayı Kullanma
Bir uygulamada bir kullanıcının dosyalarının saklanması ve alınması, çoğu zaman bir uygulamada çok ihtiyaç duyulan bir özelliktir. Bir node.js arka ucuna bağlı bir uygulamada Multer, yüklenen bir dosyanın geldiği çok parçalı/form verilerini işlemek için genellikle bir ara yazılım olarak kullanılır. Ancak node.js arka ucunun yokluğunda, çevrimiçi bir dosya kullanabiliriz. Statik uygulama varlıklarını depolamak için Google Cloud Storage gibi depolama hizmeti.
Google Cloud Storage, uygulamalar için nesneler olarak herhangi bir miktarda veriyi paketlerde depolamak için kullanılan, küresel olarak kullanılabilen bir dosya depolama hizmetidir. Hem küçük hem de büyük boyutlu uygulamalar için statik varlıkların depolanmasını idare edecek kadar esnektir.
Bulut Depolama hizmetini bir uygulama içinde kullanmak için mevcut Depolama API uç noktalarından veya resmi düğüm Depolama istemci kitaplığını kullanarak kullanabiliriz. Ancak, Node Storage istemci kitaplığı bir Tarayıcı penceresinde çalışmaz, bu nedenle kitaplığı kullanacağımız Bulut İşlevinden yararlanabiliriz.
Bunun bir örneği, oluşturulan bir Bulut Kovasına bir dosya bağlayan ve yükleyen aşağıdaki Bulut İşlevidir.
const cors = require("cors")({ origin: true }); const { Storage } = require("@google-cloud/storage"); const StorageClient = new Storage(); exports.Uploader = (req, res) => { const { file } = req.body; StorageClient.bucket("TEST_BUCKET") .file(file.name) .then((response) => { console.log(response); res.status(200).send(response) }) .catch((e) => res.status(422).send({error : e})); }); };
Yukarıdaki bulut işlevinden aşağıdaki iki ana işlemi gerçekleştiriyoruz:
İlk olarak,
Storage constructor
içinde Cloud Storage ile bir bağlantı oluşturuyoruz ve bu, Cloud Storage ile kimlik doğrulaması yapmak için Google Cloud'daki Application Default Credentials (ADC) özelliğini kullanıyor.İkinci olarak, istek gövdesinde bulunan dosyayı
TEST_BUCKET
yöntemini çağırarak ve dosyanın adını.file
. Bu eşzamansız bir işlem olduğundan, bu eylemin ne zaman çözüldüğünü bilmek için bir söz kullanırız ve200
yanıtı geri göndeririz, böylece çağrının yaşam döngüsünü sona erdiririz.
Şimdi, bir kullanıcının profil görüntüsünün yüklenmesini işlemek için yukarıdaki Uploader
Bulut İşlevini genişletebiliriz. Bulut işlevi, bir kullanıcının profil resmini alır, bunu uygulamamızın bulut paketinde saklar ve ardından kullanıcının img_uri
verilerini Firestore hizmetinde kullanıcılarımızın koleksiyonunda günceller.
require("dotenv").config(); const { Firestore } = require("@google-cloud/firestore"); const cors = require("cors")({ origin: true }); const { Storage } = require("@google-cloud/storage"); const StorageClient = new Storage(); const BucketName = process.env.STORAGE_BUCKET exports.Uploader = (req, res) => { return Cors(req, res, () => { const { file , userId } = req.body; const firestore = new Firestore(); const document = firestore.collection("users"); StorageClient.bucket(BucketName) .file(file.name) .on("finish", () => { StorageClient.bucket(BucketName) .file(file.name) .makePublic() .then(() => { const img_uri = `https://storage.googleapis.com/${Bucket}/${file.path}`; document .doc(userId) .update({ img_uri, }) .then((updateResult) => res.status(200).send(updateResult)) .catch((e) => res.status(500).send(e)); }) .catch((e) => console.log(e)); }); }); };
Şimdi, aşağıdaki ekstra işlemleri gerçekleştirmek için yukarıdaki Yükleme işlevini genişlettik:
- İlk olarak, Firestore yapıcısını başlatarak
users
koleksiyonunu almak için Firestore hizmetiyle yeni bir bağlantı kurar ve Cloud Storage ile kimlik doğrulaması yapmak için Uygulama Varsayılan Kimlik Bilgilerini (ADC) kullanır. - İstek gövdesine eklenen dosyayı yükledikten sonra, yüklenen dosya üzerinde
makePublic
yöntemini çağırarak genel bir URL üzerinden erişilebilir olması için herkese açık hale getiriyoruz. Cloud Storage'ın varsayılan Erişim Kontrolüne göre, bir dosyayı herkese açık hale getirmeden internet üzerinden bir dosyaya erişilemez ve uygulama yüklendiğinde bunu yapabilmek için.
Not : Bir dosyayı herkese açık hale getirmek, uygulamanızı kullanan herkesin dosya bağlantısını kopyalayabileceği ve dosyaya sınırsız erişime sahip olabileceği anlamına gelir. Bunu önlemenin bir yolu, paketinizdeki bir dosyaya tamamen herkese açık hale getirmek yerine geçici erişim vermek için İmzalı bir URL kullanmaktır.
- Ardından, kullanıcının mevcut verilerini, yüklenen dosyanın URL'sini içerecek şekilde güncelleriz. Firestore'un
WHERE
sorgusunu kullanarak belirli kullanıcının verilerini buluruz ve istek gövdesinde bulunanuserId
kullanırız, ardındanimg_uri
alanını yeni güncellenen görüntünün URL'sini içerecek şekilde ayarlarız.
Yukarıdaki bulut Upload
işlevi, Firestore hizmetinde kayıtlı kullanıcıları olan herhangi bir uygulamada kullanılabilir. Kullanıcının IS'sini ve bir görüntüyü istek gövdesine koyarak, uç noktaya bir POST
isteği yapmak için gereken her şey.
Uygulama içinde buna bir örnek, işleve POST
isteği yapan ve istekten döndürülen görüntü bağlantısını uygulama durumuna getiren UPLOAD-FILE
durumudur.
# index.js import Axios from 'axios' const UPLOAD_FUNCTION = process.env.REACT_APP_UPLOAD_FUNCTION export const UserReducer = (state, action) => { switch (action.type) { case "CREATE-USER" : # .....CREATE-USER-LOGIC .... case "UPLOAD-FILE": const { file, id } = action return Axios.post(UPLOAD_FUNCTION, { file, id }, { headers: { "Content-Type": "image/png", }, }) .then((response) => {}) .catch((e) => console.log(e)); default : return console.log(`${action.type} case not recognized`) } }
Yukarıdaki geçiş durumundan, eklenen dosyanın istek gövdesine dahil edilmesi için UPLOAD_FUNCTION
Axios kullanarak bir POST
isteği yapıyoruz ve ayrıca istek başlığına bir görüntü Content-Type
ekledik.
Başarılı bir yüklemeden sonra, bulut işlevinden döndürülen yanıt, google bulut deposuna yüklenen görüntünün geçerli bir URL'sini içerecek şekilde güncellenen kullanıcının veri belgesini içerir. Daha sonra kullanıcının durumunu yeni verileri içerecek şekilde güncelleyebiliriz ve bu aynı zamanda profil bileşenindeki kullanıcının profil resmi src
öğesini de günceller.
Cron İşlerini İşleme
Kullanıcılara e-posta göndermek veya belirli bir zamanda dahili bir eylem gerçekleştirmek gibi tekrarlayan otomatik görevler, çoğu zaman uygulamalara dahil edilen bir özelliktir. Normal bir node.js uygulamasında, bu tür görevler, node-cron veya node-schedule kullanılarak cron işleri olarak ele alınabilir. Google Cloud Platform'u kullanarak sunucusuz uygulamalar oluştururken, Bulut Zamanlayıcı ayrıca bir cron işlemi gerçekleştirmek üzere tasarlanmıştır.
Not : Bulut Zamanlayıcı gelecekte yürütülecek işler oluşturmada Unix cron yardımcı programına benzer şekilde çalışsa da, Bulut Zamanlayıcı'nın cron yardımcı programı gibi bir komut çalıştırmadığını belirtmek önemlidir. Bunun yerine, belirtilen bir hedefi kullanarak bir işlem gerçekleştirir.
Adından da anlaşılacağı gibi, Bulut Zamanlayıcı, kullanıcıların gelecekte gerçekleştirilecek bir işlemi planlamalarına olanak tanır. Her işleme bir iş adı verilir ve işler görsel olarak oluşturulabilir, güncellenebilir ve hatta Bulut Konsolu'nun Zamanlayıcı bölümünden yok edilebilir. Ad ve açıklama alanının yanı sıra, Bulut Zamanlayıcı'daki işler aşağıdakilerden oluşur:
- Sıklık
Bu, Cron işinin yürütülmesini programlamak için kullanılır. Programlar, bir Linux ortamında cron tablosunda arka plan işleri oluşturulurken orijinal olarak kullanılan unix-cron formatı kullanılarak belirlenir. Unix-cron formatı, her biri bir zaman noktasını temsil eden beş değer içeren bir dizeden oluşur. Aşağıda beş dizenin her birini ve temsil ettikleri değerleri görebiliriz.
- - - - - - - - - - - - - - - - minute ( - 59 ) | - - - - - - - - - - - - - hour ( 0 - 23 ) | | - - - - - - - - - - - - day of month ( 1 - 31 ) | | | - - - - - - - - - month ( 1 - 12 ) | | | | - - - - - -- day of week ( 0 - 6 ) | | | | | | | | | | | | | | | | | | | | | | | | | * * * * *
Bir iş için bir frekans-zaman değeri oluşturmaya çalışırken Crontab oluşturucu aracı kullanışlı olur. Zaman değerlerini bir araya getirmeyi zor buluyorsanız, Crontab oluşturucu bir çizelge oluşturan değerleri seçebileceğiniz ve oluşturulan değeri kopyalayıp frekans olarak kullanabileceğiniz görsel bir açılır menüye sahiptir.
- Saat dilimi
cron işinin yürütüldüğü saat dilimi. Zaman dilimleri arasındaki zaman farkı nedeniyle, belirtilen farklı zaman dilimleriyle yürütülen cron işlerinin yürütme süreleri farklı olacaktır. - Hedef
Belirtilen İşin yürütülmesinde kullanılan budur. Hedef, işin belirtilen zamanda URL'ye istekte bulunduğu birHTTP
türü veya işin mesaj yayınlayabileceği veya buradan mesaj çekebileceği bir Pub/Sub konusu ve son olarak bir App Engine Uygulaması olabilir.
Bulut Zamanlayıcı, HTTP ile tetiklenen Bulut İşlevleri ile mükemmel bir şekilde birleşir. Bulut Zamanlayıcı içinde hedefi HTTP olarak ayarlanmış bir iş oluşturulduğunda, bu iş bir bulut işlevini yürütmek için kullanılabilir. Yapılması gereken tek şey, bulut işlevinin uç noktasını belirlemek, isteğin HTTP fiilini belirlemek ve ardından görüntülenen gövde alanına işlev için iletilmesi gereken verileri eklemek. Aşağıdaki örnekte gösterildiği gibi:
Yukarıdaki resimdeki cron işi, bir bulut işlevinin örnek uç noktasına bir POST
isteğinde bulunarak her gün sabah 9'da çalışır.
Bir cron işinin daha gerçekçi bir kullanım durumu, Mailgun gibi harici bir posta hizmeti kullanarak kullanıcılara belirli aralıklarla planlanmış e-postalar göndermektir. Bunu çalışırken görmek için, Mailgun'a bağlanmak için nodemailer JavaScript paketini kullanarak belirli bir e-posta adresine bir HTML e-postası gönderen yeni bir bulut işlevi oluşturacağız:
# index.js require("dotenv").config(); const nodemailer = require("nodemailer"); exports.Emailer = (req, res) => { let sender = process.env.SENDER; const { reciever, type } = req.body var transport = nodemailer.createTransport({ host: process.env.HOST, port: process.env.PORT, secure: false, auth: { user: process.env.SMTP_USERNAME, pass: process.env.SMTP_PASSWORD, }, }); if (!reciever) { res.status(400).send({ error: `Empty email address` }); } transport.verify(function (error, success) { if (error) { res .status(401) .send({ error: `failed to connect with stmp. check credentials` }); } }); switch (type) { case "statistics": return transport.sendMail( { from: sender, to: reciever, subject: "Your usage satistics of demo app", html: { path: "./welcome.html" }, }, (error, info) => { if (error) { res.status(401).send({ error : error }); } transport.close(); res.status(200).send({data : info}); } ); default: res.status(500).send({ error: "An available email template type has not been matched.", }); } };
Using the cloud function above we can send an email to any user's email address specified as the receiver value in the request body. It performs the sending of emails through the following steps:
- It creates an SMTP transport for sending messages by passing the
host
,user
andpass
which stands for password, all displayed on the user's Mailgun dashboard when a new account is created. - Next, it verifies if the SMTP transport has the credentials needed in order to establish a connection. If there's an error in establishing the connection, it ends the function's invocation and sends back a
401 unauthenticated
status code. - Next, it calls the
sendMail
method to send the email containing the HTML file as the email's body to the receiver's email address specified in theto
field.
Note : We use a switch statement in the cloud function above to make it more reusable for sending several emails for different recipients. This way we can send different emails based on the type
field included in the request body when calling this cloud function.
Now that there is a function that can send an email to a user; we are left with creating the cron job to invoke this cloud function. This time, the cron jobs are created dynamically each time a new user is created using the official Google cloud client library for the Cloud Scheduler from the initial firestoreFunction
.
We expand the CREATE-USER
case to create the job which sends the email to the created user at a one-day interval.
require("dotenv").config();cloc const { Firestore } = require("@google-cloud/firestore"); const scheduler = require("@google-cloud/scheduler") const cors = require("cors")({ origin: true }); const EMAILER = proccess.env.EMAILER_ENDPOINT const parent = ScheduleClient.locationPath( process.env.PROJECT_ID, process.env.LOCATION_ID ); exports.firestoreFunction = function (req, res) { return cors(req, res, () => { const { email, password, type } = req.body; const firestore = new Firestore(); const document = firestore.collection("users"); const client = new Scheduler.CloudSchedulerClient() if (!type) { res.status(422).send({ error : "An action type was not specified"}); } switch (type) { case "CREATE-USER":
const job = { httpTarget: { uri: process.env.EMAIL_FUNCTION_ENDPOINT, httpMethod: "POST", body: { email: email, }, }, schedule: "*/30 */6 */5 10 4", timezone: "Africa/Lagos", }
if (!email || !password) { res.status(422).send("email and password fields missing"); } return bcrypt.genSalt(10, (err, salt) => { bcrypt.hash(password, salt, (err, hash) => { document .add({ email: email, password: hash, }) .then((response) => {
client.createJob({ parent : parent, job : job }).then(() => res.status(200).send(response)) .catch(e => console.log(`unable to create job : ${e}`) )
}) .catch((e) => res.status(501).send(`error inserting data : ${e}`) ); }); }); default: res.status(422).send(`${type} is not a valid function action`) } }); };
From the snippet above, we can see the following:
- A connection to the Cloud Scheduler from the Scheduler constructor using the Application Default Credentials (ADC) is made.
- We create an object consisting of the following details which make up the cron job to be created:
-
uri
The endpoint of our email cloud function in which a request would be made to. -
body
This is the data containing the email address of the user to be included when the request is made. -
schedule
The unix cron format representing the time when this cron job is to be performed.
-
- After the promise from inserting the user's data document is resolved, we create the cron job by calling the
createJob
method and passing in the job object and the parent. - The function's execution is ended with a
200
status code after the promise from thecreateJob
operation has been resolved.
After the job is created, we'll see it listed on the scheduler page.
From the image above we can see the time scheduled for this job to be executed. We can decide to manually run this job or wait for it to be executed at the scheduled time.
Çözüm
Within this article, we have had a good look into serverless applications and the benefits of using them. We also had an extensive look at how developers can manage their serverless applications on the Google Cloud using Cloud Functions so you now know how the Google Cloud is supporting the use of serverless applications.
Within the next years to come, we will certainly see a large number of developers adapt to the use of serverless applications when building applications. If you are using cloud functions in a production environment, it is recommended that you read this article from a Google Cloud advocate on “6 Strategies For Scaling Your Serverless Applications”.
The source code of the created cloud functions are available within this Github repository and also the used front-end application within this Github repository. The front-end application has been deployed using Netlify and can be tested live here.
Referanslar
- Google Bulut
- Cloud Functions
- Cloud Source Repositories
- Cloud Scheduler overview
- Bulut Firestore
- “6 Strategies For Scaling Your Serverless Applications,” Preston Holmes