Computer >> कंप्यूटर >  >> प्रोग्रामिंग >> Ruby

रूबी के साथ Google क्लाउड फ़ंक्शंस का निर्माण, परीक्षण और परिनियोजन

सर्वर रहित कार्य क्लाउड सेवाओं के विकास और परिनियोजन का एक नया प्रोग्रामिंग प्रतिमान है। सर्वर रहित दुनिया में, हम क्लाउड प्रदाता के लिए अपनी बैकएंड सेवाओं के प्रावधान, रखरखाव और स्केलिंग को सारगर्भित करते हैं। यह डेवलपर्स को एक विशिष्ट समस्या को हल करने पर ध्यान केंद्रित करने की अनुमति देकर डेवलपर उत्पादकता में काफी सुधार करता है। जबकि सर्वर रहित कार्यों के निर्माण के कई फायदे और नुकसान हैं, उन्हें बनाने में एक बात पर विचार करना भाषा का समर्थन है। हाल ही में, 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 कंसोल में अपने क्लाउड फ़ंक्शन की जांच करें, जैसा कि नीचे दिखाया गया है:

रूबी के साथ Google क्लाउड फ़ंक्शंस का निर्माण, परीक्षण और परिनियोजन

प्रमाणीकरण

एक Service Account बनाएं Cloud Functions Admin . के साथ और Service Account User भूमिकाएँ।

मशीन-टू-मशीन IAM के लिए सेवा खाते का उपयोग किया जाता है। इस प्रकार, जब कोई सिस्टम, चाहे वह Google क्लाउड पर चल रहा हो या नहीं, Google क्लाउड पर किसी अन्य सिस्टम से बात करता है, तो यह पहचानने में सहायता के लिए एक सेवा खाते की आवश्यकता होती है कि कौन हमारे Google संसाधन तक पहुंच का अनुरोध कर रहा है। भूमिकाएँ Cloud Functions Admin और Service Account User हमें यह निर्धारित करने में सक्षम बनाता है कि उपयोगकर्ता संसाधन तक पहुंचने के लिए अधिकृत है या नहीं। इस परिदृश्य में, एक Github एक्शन रनर हमारे फ़ंक्शन को परिनियोजित करने के लिए आवश्यक अनुमतियों के साथ एक सेवा खाते के रूप में प्रमाणित करने वाले Google क्लाउड के साथ संचार करता है।

रूबी के साथ Google क्लाउड फ़ंक्शंस का निर्माण, परीक्षण और परिनियोजन

रूबी के साथ Google क्लाउड फ़ंक्शंस का निर्माण, परीक्षण और परिनियोजन

सेवा खाता कुंजी बनाएं, JSON डाउनलोड करें और इसे GitHub सीक्रेट्स में जोड़ें।

रूबी के साथ Google क्लाउड फ़ंक्शंस का निर्माण, परीक्षण और परिनियोजन

रूबी के साथ Google क्लाउड फ़ंक्शंस का निर्माण, परीक्षण और परिनियोजन

वोइला! 🎉 हमारे क्लाउड फ़ंक्शन को सफलतापूर्वक परिनियोजित किया गया है।

क्लाउड फंक्शन लिमिट्स बनाम AWS लिमिट्स

नीचे दो सबसे बड़े सर्वर रहित फ़ंक्शन प्रदाताओं की विस्तृत तुलना दी गई है:

रूबी के साथ Google क्लाउड फ़ंक्शंस का निर्माण, परीक्षण और परिनियोजन

फ़ंक्शन फ़्रेमवर्क अनुबंध बनाम सर्वर रहित फ़्रेमवर्क

इस लेख में, हमने Google क्लाउड फ़ंक्शंस के लिए क्लाउड फ़ंक्शंस बनाने पर ध्यान केंद्रित किया है। इस सेगमेंट में, मैं फंक्शन फ्रेमवर्क बनाम सर्वरलेस फ्रेमवर्क के साथ बिल्डिंग की तुलना करना चाहता हूं।

  • Serverless Framework serverless.yml . पर आधारित है , जबकि Functions Framework Functions Framework Contract . पर आधारित है , जिसका उपयोग Google क्लाउड इन्फ्रास्ट्रक्चर में सर्वर रहित कार्यों को परिनियोजित करने के लिए किया जाता है।
  • Serverless Framework के साथ , केवल कुछ उदाहरण हैं, और यह बिल्कुल स्पष्ट नहीं है कि रूबी के साथ सर्वर रहित फ़ंक्शन को विभिन्न Google सर्वर रहित वातावरण (क्लाउड फ़ंक्शंस, क्लाउड रन और नेटिव वातावरण) में कैसे बनाया और तैनात किया जाए। Functions Framework Contract . के साथ फ़ंक्शन, इन विभिन्न Google उत्पादों में रूबी के साथ निर्माण करना आसान है।
    • पिछले बिंदु के बाद, Functions Framework Contract आपकी परिनियोजन प्रक्रिया में आवश्यक रूप से अधिक परिवर्तन किए बिना बैकिंग भाषा को आपके फ़ंक्शन के पीछे स्विच करना बहुत आसान बनाता है।
  • इस लेखन के समय, Functions Framework केवल Google क्लाउड सर्वर रहित परिवेशों और नेटिव परिवेशों में अंतरसंचालनीयता का समर्थन करता है। Serverless Framework , हालांकि, कई प्रदाताओं में कई प्लेटफॉर्म का समर्थन करता है।

संदर्भ के लिए, पूरा कोड यहां उपलब्ध है।


  1. Vue, Vuex और Rails के साथ एक पूर्ण-स्टैक एप्लिकेशन का निर्माण

    स्केलेबिलिटी को ध्यान में रखते हुए फुल-स्टैक एप्लिकेशन बनाना डराने वाला हो सकता है, खासकर जब Vue और Vuex के नवीनतम संस्करण के साथ निर्माण किया जाता है, जिसमें पूर्ण टाइपस्क्रिप्ट समर्थन होता है। यह लेख अपने पाठकों को वह सब कुछ सिखाएगा जो उन्हें राज्य प्रबंधन से Vuex 4.0 के साथ स्केलेबल फुल-स्टैक एप्

  1. Fluentd और ObjectRocket के साथ हाइब्रिड क्लाउड में लॉग इन करना

    यह पोस्ट हार्ट हूवर और रयान वॉकर द्वारा सह-लिखा गया था हाल ही में, रैकस्पेस देवओप्स ऑटोमेशन टीम ने एक ऐसी सेवा की घोषणा की जो न्यू रेलिक से रैकस्पेस समर्थन के लिए अलर्ट भेजती है। ये अलर्ट हमारे DevOps इंजीनियर्स को जवाब देने के लिए टिकट जेनरेट करेंगे, ताकि सुबह 3 बजे अलर्ट जेनरेट होने पर हमारे ग्र

  1. Google क्लाउड प्रिंट क्या है और यह कैसे काम करता है?

    Google क्लाउड प्रिंट Google द्वारा विकसित एक सेवा है जो आपको कहीं से भी किसी भी डिवाइस से फ़ाइलें प्रिंट करने में मदद करती है। आपकी फ़ाइलें केवल क्लाउड के माध्यम से कनेक्ट करके आपके स्थान से दूर एक प्रिंटर के माध्यम से मुद्रित की जाएंगी। यह आपके कार्यालय के असाइनमेंट, आपके बच्चों के लिए और आपके प्रि