सर्वर रहित कार्य क्लाउड सेवाओं के विकास और परिनियोजन का एक नया प्रोग्रामिंग प्रतिमान है। सर्वर रहित दुनिया में, हम क्लाउड प्रदाता के लिए अपनी बैकएंड सेवाओं के प्रावधान, रखरखाव और स्केलिंग को सारगर्भित करते हैं। यह डेवलपर्स को एक विशिष्ट समस्या को हल करने पर ध्यान केंद्रित करने की अनुमति देकर डेवलपर उत्पादकता में काफी सुधार करता है। जबकि सर्वर रहित कार्यों के निर्माण के कई फायदे और नुकसान हैं, उन्हें बनाने में एक बात पर विचार करना भाषा का समर्थन है। हाल ही में, Google ने Google क्लाउड फ़ंक्शंस के लिए रूबी 2.7 के समर्थन की घोषणा की, और इस लेख में, मैं Google क्लाउड फ़ंक्शंस पर रूबी में सर्वर रहित फ़ंक्शन के निर्माण, परीक्षण और परिनियोजन और सर्वर रहित फ़ंक्शंस के पेशेवरों और विपक्षों पर ध्यान केंद्रित करूँगा।पी>
सर्वर रहित OTP सिस्टम बनाना
वन-टाइम पासवर्ड (ओटीपी) छोटे संख्यात्मक कोड होते हैं जिनका उपयोग प्रमाणीकरण उद्देश्यों के लिए किया जाता है, जैसे कि जब आपका बैंक आपकी पहचान सत्यापित करने के लिए टेक्स्ट के माध्यम से एक ऑप्ट भेजता है।
इस लेख में, हम एक ओटीपी फ़ंक्शन बनाएंगे जो तीन मुख्य जिम्मेदारियों को संभालता है:
POST /otp
:दिए गए phone_number
. पर एक OTP संदेश जेनरेट और भेजता है ।
# Request
{
"phone_number": "+2347012345678"
}
# Response
{
"status": true,
"message": "OTP sent successfully",
"data": {
"phone_number": "+2347012345678",
"otp": 6872,
"expires_at": "2021-02-09 07:15:25 +0100"
}
}
PUT /otp/verify
:उपयोगकर्ता द्वारा प्रदत्त ओटीपी के विरुद्ध एक ओटीपी सत्यापित करता है।
# Request
{
"phone_number": "+2347012345678",
"otp": 7116
}
# Response
{
"status": true,
"message": "OTP verified",
"data": {}
}
PUT /otp/resend
:दिए गए phone_number
. पर एक ओटीपी जनरेट करने और फिर से भेजने की कोशिश करता है ।
# Request
{
"phone_number": "+2347012345678"
}
# Response
{
"status": true,
"message": "OTP sent successfully",
"data": {
"phone_number": "+2347012345678",
"otp": 8533,
"expires_at": "2021-02-09 08:59:16 +0100"
}
}
सरलता के लिए, हमारे क्लाउड फ़ंक्शन को पूर्ण SQL या NoSQL डेटाबेस के बजाय क्लाउड मेमोरीस्टोर (GCP पर Redis या Memcache) द्वारा समर्थित किया जाएगा। यह हमें स्टेटलेस वातावरण में साझा राज्यों के बारे में जानने में भी सक्षम करेगा।
रूबी के साथ Google क्लाउड फ़ंक्शन लिखना
GCF में फंक्शन लिखने के लिए, हम Functions Framework
. पर भरोसा करेंगे जीसीएफ कार्यों के निर्माण के लिए Google क्लाउड टीम द्वारा प्रदान किया गया (इस पर बाद में)।
सबसे पहले, ऐप निर्देशिका बनाएं और निर्देशिका दर्ज करें।
mkdir otp-cloud-function && cd otp-cloud-function
इसके बाद, अपना जेमफाइल बनाएं और इंस्टॉल करें।
अधिकांश मानक रूबी अनुप्रयोगों की तरह, हम bundler
. का उपयोग करेंगे हमारे फ़ंक्शन की निर्भरता को प्रबंधित करने के लिए
source "https://rubygems.org"
# Core
gem "functions_framework", "~> 0.7"
# Twilio for Sms
gem 'twilio-ruby', '~> 5.43.0'
# Database
gem 'redis'
# Connection Pooling
gem 'connection_pool'
# Time management
gem 'activesupport'
# API Serialization
gem 'active_model_serializers', '~> 0.10.0'
group :development, :test do
gem 'pry'
gem 'rspec'
gem 'rspec_junit_formatter'
gem 'faker', '~> 2.11.0'
end
bundle install
फ़ंक्शन बनाना
आम तौर पर, विभिन्न होस्टिंग वातावरण आपको एक अलग फ़ाइल निर्दिष्ट करने की अनुमति देते हैं जहां आपके कार्य लिखे जाते हैं। हालाँकि, Google क्लाउड फ़ंक्शंस के लिए यह आवश्यक है कि वह app.rb
हो आपकी परियोजना निर्देशिका की जड़ में। अब, हम अपना फंक्शन लिखने के लिए तैयार हैं।
app.rb
खोलें और फ़ंक्शन बनाएं:
# Cloud Functions Entrypoint
require 'functions_framework'
require 'connection_pool'
require 'active_model_serializers'
require './lib/store'
require './lib/send_sms_notification'
require './lib/response'
require './lib/serializers/models/base_model'
require './lib/serializers/models/otp_response'
require './lib/serializers/application_serializer'
require './lib/serializers/base_model_serializer'
require './lib/serializers/otp_response_serializer'
FunctionsFramework.on_startup do |function|
# Setup Shared Redis Client
require 'redis'
set_global :redis_client, ConnectionPool.new(size: 5, timeout: 5) { Redis.new }
end
# Define HTTP Function
FunctionsFramework.http "otp" do |request|
store = Store.new(global(:redis_client))
data = JSON.parse(request.body.read)
if request.post? && request.path == '/otp'
phone_number = data['phone_number']
record = store.get(phone_number)
unless record.nil? || record.expired?
data = Models::OtpResponse.new(phone_number: phone_number,
otp: record['otp'],
expires_at: record['expires_at'])
json = Response.generate_json(status: true,
message: 'OTP previously sent',
data: data)
return json
end
otp = rand(1111..9999)
record = store.set(phone_number, otp)
SendSmsNotification.new(phone_number, otp).call
data = Models::OtpResponse.new(phone_number: phone_number,
otp: record['otp'],
expires_at: record['expires_at'])
Response.generate_json(status: true,
message: 'OTP sent successfully',
data: data)
elsif request.put? && request.path == '/otp/verify'
phone_number = data['phone_number']
record = store.get(phone_number)
if record.nil?
return Response.generate_json(status: false, message: "OTP not sent to number")
elsif record.expired?
return Response.generate_json(status: false, message: 'OTP code expired')
end
is_verified = data['otp'] == record['otp']
if is_verified
return Response.generate_json(status: true, message: 'OTP verified')
else
return Response.generate_json(status: false, message: 'OTP does not match')
end
elsif request.put? && request.path == '/otp/resend'
phone_number = data['phone_number']
store.del(phone_number)
otp = rand(1111..9999)
record = store.set(phone_number, otp)
SendSmsNotification.new(phone_number, otp).call
data = Models::OtpResponse.new(phone_number: phone_number,
otp: record['otp'],
expires_at: record['expires_at'])
json = Response.generate_json(status: true,
message: 'OTP sent successfully',
data: data)
else
Response.generate_json(status: false,
message: 'Request method and path did not match')
end
end
यह बहुत सारा कोड है, इसलिए हम इसे तोड़ देंगे:
-
Functions_Framework.on_startup
कोड का एक ब्लॉक है जो कार्यों के अनुरोधों को संसाधित करने से पहले रूबी इंस्टेंस के अनुसार चलता है। हमारे कार्यों को बुलाए जाने से पहले किसी भी प्रकार के प्रारंभिकरण को चलाने के लिए यह आदर्श है। इस मामले में, मैं इसका उपयोग हमारे Redis सर्वर से कनेक्शन पूल बनाने और साझा करने के लिए कर रहा हूँ:set_global :redis_client, ConnectionPool.new(size: 5, timeout: 5) { Redis.new }
यह हमें बिना किसी डर के कई समवर्ती फ़ंक्शन इनवोकेशन में रेडिस कनेक्शन ऑब्जेक्ट्स का एक पूल साझा करने में सक्षम बनाता है। एकाधिक स्टार्टअप को परिभाषित किया जा सकता है। वे इस क्रम में चलेंगे कि उन्हें परिभाषित किया गया है। यह भी ध्यान रखना महत्वपूर्ण है कि
Functions Framework
कार्य पूरा होने के बाद चलाने के लिए कोई विशेष हुक प्रदान नहीं करता है। -
Functions_Framework.http 'otp' do |request|
हमारे कार्यों के अनुरोध और प्रतिक्रिया प्रसंस्करण को संभालता है। यह फ़ंक्शन तीन अलग-अलग मार्ग पैटर्न का समर्थन करता है। अन्य प्रकार के कार्यों को परिभाषित करना संभव है (जैसे,Functions_Framework.cloud_event 'otp' do |event|
) जो अन्य Google सेवाओं की घटनाओं को संभालता है। एक ही फाइल में कई कार्यों को परिभाषित करना भी संभव है लेकिन स्वतंत्र रूप से तैनात किया गया है। -
store = Store.new(global(:redis_client))
. में ,global
विधि का उपयोग वैश्विक साझा स्थिति में संग्रहीत किसी भी वस्तु को पुनः प्राप्त करने के लिए किया जाता है। जैसा कि ऊपर उपयोग किया गया है, हम अपनेstartup
में वैश्विक सेटअप में परिभाषित कनेक्शन पूल से Redis क्लाइंट को पुनः प्राप्त करते हैं। ब्लॉक करें। -
Response
औरModels::OtpResponse
active_model_serializers
. के साथ प्रतिक्रिया क्रमांकन को संभालता है ठीक से स्वरूपित JSON प्रतिक्रियाएँ देने के लिए।
स्थानीय रूप से हमारे फ़ंक्शन का परीक्षण करना
Functions Framework
लाइब्रेरी हमें अपने कार्यों को क्लाउड पर परिनियोजित करने से पहले आसानी से स्थानीय स्तर पर परीक्षण करने में सक्षम बनाती है। स्थानीय स्तर पर परीक्षण करने के लिए, हम दौड़ते हैं
bundle exec functions-framework-ruby --target=otp --port=3000
--target
परिनियोजित करने के लिए फ़ंक्शन का चयन करने के लिए उपयोग किया जाता है।
जबकि मैन्युअल परीक्षण बहुत अच्छा है, स्वचालित परीक्षण और स्वयं परीक्षण सॉफ़्टवेयर परीक्षण में पवित्र कब्र है। Functions Framework
Minitest
. दोनों के लिए सहायक तरीके प्रदान करता है और RSpec
http
. दोनों के लिए हमारे कार्यों का परीक्षण करने में मदद करने के लिए और cloudevents
संचालक। यहां एक परीक्षण का उदाहरण दिया गया है:
require './spec/spec_helper.rb'
require 'functions_framework/testing'
describe 'OTP Functions' do
include FunctionsFramework::Testing
describe 'Send OTP', redis: true do
let(:phone_number) { "+2347012345678" }
let(:body) { { phone_number: phone_number }.to_json }
let(:headers) { ["Content-Type: application/json"] }
it 'should send OTP successfully' do
load_temporary "app.rb" do
request = make_post_request "/otp", body, headers
response = call_http "otp", request
expect(response.status).to eq 200
expect(response.content_type).to eq("application/json")
parsed_response = JSON.parse(response.body.join)
expect(parsed_response['status']).to eq true
expect(parsed_response['message']).to eq 'OTP sent successfully'
end
end
end
end
हमारे कार्य को परिनियोजित करना
सबसे पहले, हमें Google Cloud Memorystore
. का उपयोग करके एक Redis सर्वर परिनियोजित करने की आवश्यकता है जिस पर हमारा कार्य निर्भर है। जीसीपी में रेडिस सर्वर को कैसे परिनियोजित किया जाए, इस बारे में मैं और विस्तार से नहीं बताऊंगा, क्योंकि यह इस लेख के दायरे से बाहर है।
हमारे फ़ंक्शन को Google क्लाउड फ़ंक्शन के परिवेश में परिनियोजित करने के कई तरीके हैं:अपनी मशीन से परिनियोजित करना, GCP कंसोल से परिनियोजित करना, और हमारे कोड रिपॉजिटरी से परिनियोजित करना। आधुनिक सॉफ्टवेयर इंजीनियरिंग हमारे अधिकांश विकास के लिए CI/CD प्रक्रियाओं को प्रोत्साहित करती है, और इस लेख के उद्देश्य के लिए, मैं अपने क्लाउड फ़ंक्शन को Github से Github क्रियाओं के साथ परिनियोजित-क्लाउड-फ़ंक्शंस का उपयोग करके परिनियोजित करने पर ध्यान केंद्रित करूँगा।
आइए हमारी परिनियोजन फ़ाइल (.github/workflows/deploy.yml) सेट करें।
name: Deployment
on:
push:
branches:
- main
jobs:
deploy:
name: Function Deployment
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- id: deploy
uses: google-github-actions/deploy-cloud-functions@main
with:
name: otp-cloud-function
runtime: ruby26
credentials: ${{ secrets.gcp_credentials }}
env_vars: "TWILIO_ACCOUNT_SID=${{ secrets.TWILIO_ACCOUNT_SID }},TWILIO_AUTH_TOKEN=${{ secrets.TWILIO_AUTH_TOKEN }},TWILIO_PHONE_NUMBER=${{ secrets.TWILIO_PHONE_NUMBER }},REDIS_URL=${{ secrets.REDIS_URL }}"
पर्यावरण चर
उपरोक्त कोड में, अंतिम पंक्ति हमें पर्यावरण चर निर्दिष्ट करने की अनुमति देती है जो Google क्लाउड वातावरण में हमारे कार्य के लिए उपलब्ध होंगे। ध्यान दें कि सुरक्षा कारणों से, हम अपने कोड-बेस में इन चरों को उजागर नहीं कर रहे हैं; इसके बजाय, हम इस जानकारी को निजी रखने के लिए Github क्रिया रहस्यों का उपयोग कर रहे हैं। यह जांचने के लिए कि हमारे टोकन ठीक से तैनात किए गए हैं, Google कंसोल में अपने क्लाउड फ़ंक्शन की जांच करें, जैसा कि नीचे दिखाया गया है:
प्रमाणीकरण
एक Service Account
बनाएं Cloud Functions Admin
. के साथ और Service Account User
भूमिकाएँ।
मशीन-टू-मशीन IAM के लिए सेवा खाते का उपयोग किया जाता है। इस प्रकार, जब कोई सिस्टम, चाहे वह Google क्लाउड पर चल रहा हो या नहीं, Google क्लाउड पर किसी अन्य सिस्टम से बात करता है, तो यह पहचानने में सहायता के लिए एक सेवा खाते की आवश्यकता होती है कि कौन हमारे Google संसाधन तक पहुंच का अनुरोध कर रहा है। भूमिकाएँ Cloud Functions Admin
और Service Account User
हमें यह निर्धारित करने में सक्षम बनाता है कि उपयोगकर्ता संसाधन तक पहुंचने के लिए अधिकृत है या नहीं। इस परिदृश्य में, एक Github एक्शन रनर हमारे फ़ंक्शन को परिनियोजित करने के लिए आवश्यक अनुमतियों के साथ एक सेवा खाते के रूप में प्रमाणित करने वाले Google क्लाउड के साथ संचार करता है।
सेवा खाता कुंजी बनाएं, JSON डाउनलोड करें और इसे GitHub सीक्रेट्स में जोड़ें।
वोइला! 🎉 हमारे क्लाउड फ़ंक्शन को सफलतापूर्वक परिनियोजित किया गया है।
क्लाउड फंक्शन लिमिट्स बनाम AWS लिमिट्स
नीचे दो सबसे बड़े सर्वर रहित फ़ंक्शन प्रदाताओं की विस्तृत तुलना दी गई है:
फ़ंक्शन फ़्रेमवर्क अनुबंध बनाम सर्वर रहित फ़्रेमवर्क
इस लेख में, हमने Google क्लाउड फ़ंक्शंस के लिए क्लाउड फ़ंक्शंस बनाने पर ध्यान केंद्रित किया है। इस सेगमेंट में, मैं फंक्शन फ्रेमवर्क बनाम सर्वरलेस फ्रेमवर्क के साथ बिल्डिंग की तुलना करना चाहता हूं।
Serverless Framework
serverless.yml
. पर आधारित है , जबकि Functions FrameworkFunctions Framework Contract
. पर आधारित है , जिसका उपयोग Google क्लाउड इन्फ्रास्ट्रक्चर में सर्वर रहित कार्यों को परिनियोजित करने के लिए किया जाता है।Serverless Framework
के साथ , केवल कुछ उदाहरण हैं, और यह बिल्कुल स्पष्ट नहीं है कि रूबी के साथ सर्वर रहित फ़ंक्शन को विभिन्न Google सर्वर रहित वातावरण (क्लाउड फ़ंक्शंस, क्लाउड रन और नेटिव वातावरण) में कैसे बनाया और तैनात किया जाए।Functions Framework Contract
. के साथ फ़ंक्शन, इन विभिन्न Google उत्पादों में रूबी के साथ निर्माण करना आसान है।- पिछले बिंदु के बाद,
Functions Framework Contract
आपकी परिनियोजन प्रक्रिया में आवश्यक रूप से अधिक परिवर्तन किए बिना बैकिंग भाषा को आपके फ़ंक्शन के पीछे स्विच करना बहुत आसान बनाता है।
- पिछले बिंदु के बाद,
- इस लेखन के समय,
Functions Framework
केवल Google क्लाउड सर्वर रहित परिवेशों और नेटिव परिवेशों में अंतरसंचालनीयता का समर्थन करता है।Serverless Framework
, हालांकि, कई प्रदाताओं में कई प्लेटफॉर्म का समर्थन करता है।
संदर्भ के लिए, पूरा कोड यहां उपलब्ध है।