"यदि कोई कर्मचारी अपना काम अच्छी तरह से करना चाहता है, तो उसे पहले अपने औजारों को तेज करना होगा।" - कन्फ्यूशियस, "द एनालेक्ट्स ऑफ कन्फ्यूशियस। लू लिंगगोंग"
मुखपृष्ठ > प्रोग्रामिंग > गो के साथ एडब्ल्यूएस लैम्ब्डा, प्रारंभिक बॉयलरप्लेट

गो के साथ एडब्ल्यूएस लैम्ब्डा, प्रारंभिक बॉयलरप्लेट

2024-11-06 को प्रकाशित
ब्राउज़ करें:229

अनस्प्लैश पर लुकास वानट्को द्वारा फोटो

परिचय

गो अपनी सरलता के कारण हमेशा मेरी पसंदीदा भाषाओं में से एक रही है। हाल ही में, मैंने यह पता लगाने का निर्णय लिया कि गो में लिखे लैम्ब्डा फ़ंक्शंस के साथ एक सरल बॉयलरप्लेट सर्वर रहित प्रोजेक्ट बनाने में क्या लगता है। मैं टूलींग और डेवलपर अनुभव के बारे में उत्सुक था।

लक्ष्य

मैं एक REST API बनाना चाहता हूं, जो डेटा लेयर के रूप में पोस्टग्रेज डीबी का उपयोग करता है। मेरी प्रारंभिक आवश्यकताएं निम्नलिखित हैं

  • OpenApi के साथ परिभाषित की जाने वाली विशिष्टता, और उससे उत्पन्न मॉडल
  • AWS SAM का उपयोग करें
  • प्रत्येक समापन बिंदु को एक अलग लैम्ब्डा फ़ंक्शन द्वारा नियंत्रित किया जाना है
  • स्थानीय विकास यथासंभव आसान
  • परिनियोजन के लिए समान

आवश्यक शर्तें

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

परियोजना का निर्माण

कोड इस रेपो में उपलब्ध है

आइए सैम इनिट चलाकर शुरुआत करें। मैंने हेलो वर्ल्ड टेम्प्लेट चुना, दिए गए al.2023 env के साथ जाएं। पहले गो के लिए एक प्रबंधित रनटाइम था, लेकिन आजकल इसे बंद कर दिया गया है।

ओपनएपीआई स्कीमा

एपीआई स्कीमा को ओपनएपीआई स्पेक के रूप में परिभाषित करने के कुछ स्पष्ट फायदे हैं। हम इसका उपयोग दस्तावेज़ तैयार करने, क्लाइंट, सर्वर आदि बनाने के लिए कर सकते हैं। मैं इसका उपयोग AWS HttpApi गेटवे के आकार को परिभाषित करने के लिए भी करता हूं।

मेरा स्कीमा सीधा है। एकमात्र दिलचस्प हिस्सा x-amazon-apigateway-integration संपत्ति है, जो लैम्ब्डा एकीकरण के साथ कनेक्शन की अनुमति देता है। सेटअप भाषा-अज्ञेयवादी है.

आप रेपो में स्कीमा फ़ाइल पा सकते हैं

सैम टेम्पलेट

HttpApi और रहस्य

# template.yaml
# ....
ItemsAPI:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: Prod
      DefinitionBody:
        'Fn::Transform':
          Name: 'AWS::Include'
          Parameters:
            Location: './api.yaml'
      FailOnWarnings: false
DBSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: my-db-secret
      Description: Postgres config string
      SecretString: 'host=172.17.0.1 port=5431 user=admin password=root dbname=lambdas sslmode=disable'
# ....

जैसा कि ऊपर बताया गया है, यहां जाने के लिए कुछ खास नहीं है। HttpApi गेटवे OpenApi के आधार पर बनाया गया है।

कनेक्शन स्ट्रिंग को संग्रहीत करने का भी एक रहस्य है। मैं तैनाती के बाद इसका मूल्य अपडेट करूंगा

लैम्ब्डा फ़ंक्शन

गो के लिए AWS SAM समर्थन बहुत बढ़िया है। मैं CodeUri को लैम्ब्डा हैंडलर वाले फ़ोल्डर में इंगित कर सकता हूं और बिल्ड विधि को go1.x

के रूप में परिभाषित कर सकता हूं।

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

फ़ंक्शन की परिभाषा इस तरह दिखती है:

# template.yaml
# ....
  GetItemFunction:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: go1.x
    Properties:
      Tracing: Active
      CodeUri: lambda_handlers/cmd/get_item/
      Handler: bootstrap
      Runtime: provided.al2023
      Architectures:
        - x86_64
      Environment:
        Variables:
          DB_SECRET_NAME: !Ref DBSecret
          API_STAGE: Prod
      Events:
        HttpApiEvents:
          Type: HttpApi
          Properties:
            Path: /item/{id}
            Method: GET
            ApiId: !Ref ItemsAPI
      Policies: 
        - AWSLambdaBasicExecutionRole
        - AWSSecretsManagerGetSecretValuePolicy:
            SecretArn: !Ref DBSecret
# ....

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

फ़ंक्शन कोड

परियोजना संरचना

ईमानदारी से कहें तो, फ़ोल्डर संरचना संभवतः मुहावरेदार नहीं है। लेकिन मैंने सामान्य गो पैटर्न का पालन करने की कोशिश की

lambda_handlers
|--/api
|--/cmd
|--/internal
|--/tools
|--go.mod
|--go.sum

cmd वास्तविक लैम्ब्डा हैंडलर वाला मुख्य फ़ोल्डर है
आंतरिक हैंडलर के बीच साझा किया गया कोड रखता है
टूल्स परियोजनाओं में उपयोग किए जाने वाले अतिरिक्त टूल को परिभाषित करते हैं
ओपनएपीआई जेनरेटर कॉन्फ़िगरेशन और जेनरेट किए गए मॉडल के लिए एपीआई

फ़ंक्शन हैंडलर

लैम्ब्डा हैंडलर के लिए प्रारंभिक बॉयलरप्लेट इस तरह दिखती है:

// ...
func handleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    // handler logic
}

func main() {
    lambda.Start(handleRequest)
}

आम तौर पर, पहला सवाल यह पूछा जाता है कि AWS SDK क्लाइंट, डेटाबेस कनेक्शन और अन्य चीजों का इनिशियलाइज़ेशन कहां रखा जाए, जिन्हें हम कोल्ड स्टार्ट के दौरान निपटाना चाहते हैं।

हमारे पास यहां विकल्प हैं। सबसे पहले AWS दस्तावेज़ीकरण उदाहरण से पैटर्न का पालन करना और init() फ़ंक्शन के अंदर सेवाओं को प्रारंभ करना है। मुझे यह दृष्टिकोण पसंद नहीं है, क्योंकि इससे यूनिट परीक्षणों में हैंडलर का उपयोग करना कठिन हो जाता है।

इस तथ्य के लिए धन्यवाद कि Lambda.Start() विधि एक फ़ंक्शन को इनपुट के रूप में लेती है, मैं इसे कस्टम संरचना में लपेट सकता हूं, और इसे उन सेवाओं के साथ प्रारंभ कर सकता हूं जिनकी मुझे आवश्यकता है। मेरे मामले में, कोड इस तरह दिखता है:

package main

// imports ...

type GetItemsService interface {
    GetItem(id int) (*api.Item, error)
}

type LambdaHandler struct {
    svc GetItemsService
}

func InitializeLambdaHandler(svc GetItemsService) *LambdaHandler {
    return &LambdaHandler{
        svc: svc,
    }
}

func (h *LambdaHandler) HandleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

    strId, err := helpers.GetPathParameter("id", request.PathParameters)

    if err != nil {
        return helpers.SendResponse("id is required", 400), nil
    }

    id, err := strconv.Atoi(*strId)

    if err != nil {
        return helpers.SendResponse("id must be an integer", 400), nil
    }

    log.Printf("id: %d", id)

    result, err := h.svc.GetItem(id)

    if err != nil {
        if err.Error() == "Item not found" {
            return helpers.SendResponse("Item not found", 404), nil
        }
        return helpers.SendResponse("error", 500), nil
    }

    jsonRes, err := json.Marshal(result)

    if err != nil {
        log.Printf("error marshalling response: %s", err.Error())
        return helpers.SendResponse("internal server error", 500), nil
    }

    return helpers.SendResponse(string(jsonRes), 200), nil
}

func main() {

    dbSecretName := os.Getenv("DB_SECRET_NAME")

    log.Printf("dbSecretName: %s", dbSecretName)

    cfg, err := awssdkconfig.InitializeSdkConfig()

    if err != nil {
        log.Fatal(err)
    }

    secretsClient := awssdkconfig.InitializeSecretsManager(cfg)

    connString, err := secretsClient.GetSecret(dbSecretName)

    if err != nil {
        log.Fatal(err)
    }

    conn, err := db.InitializeDB(connString)

    if err != nil {
        log.Fatal(err)
    }

    defer conn.Close()

    log.Println("successfully connected to db")

    svc := items.InitializeItemsService(conn)

    handler := InitializeLambdaHandler(svc)

    lambda.Start(handler.HandleRequest)
}

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

HandleRequest पथ पैरामीटर से आईडी को पार्स करता है, और डीबी से एक आइटम प्राप्त करने के लिए आइटम्स सर्विस को कॉल करता है।

आंतरिक मॉड्यूल

चूंकि फ़ंक्शन सरल है, इसलिए इसमें बहुत अधिक व्यावसायिक तर्क नहीं है। आइटम्ससर्विस विशिष्ट आइटम के लिए बस डीबी को कॉल करता है

package items

import (
    "database/sql"
    "errors"
    api "lambda_handlers/api"
    "log"
)

type ItemsService struct {
    conn *sql.DB
}

func InitializeItemsService(conn *sql.DB) *ItemsService {
    return &ItemsService{
        conn: conn,
    }
}

func (svc ItemsService) GetItem(id int) (*api.Item, error) {

    log.Printf("Getting item id %v", id)

    query := `SELECT * FROM items WHERE id = $1`

    var item api.Item

    err := svc.conn.QueryRow(query, id).Scan(&item.Id, &item.Name, &item.Price)

    log.Printf("Got item id %v", id)

    if err != nil {
        log.Printf("Error getting item %v: %v", id, err)
        if err == sql.ErrNoRows {
            return nil, errors.New("Item not found")
        }
        return nil, err
    }

    return &item, nil

}

इस बिंदु पर, हमें यहां किसी और चीज़ की आवश्यकता नहीं है।

औजार

मेरा लक्ष्य अतिरिक्त टूल का उपयोग करना है, जिसे प्रोजेक्ट निर्भरता से जोड़ा जा सकता है, इसलिए डेवलपर की मशीन पर इंस्टॉल किए गए टूल पर भरोसा करने की कोई आवश्यकता नहीं है।

गो में ऐसा करने का एक तरीका टूल पैकेज में oapi-codegen को रखना है

//go:build tools
//  build tools

package main

import (
    _ "github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen"
)

और इसे api_gen.go के अंदर से कॉल करें

//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=cfg.yaml ../../api.yaml

package api

इस तरह मैं अलग से oapi-codegen बायनेरिज़ इंस्टॉल किए बिना गो जनरेट चला सकता हूं।

स्थानीय विकास

निर्माण

मेरी निर्माण प्रक्रिया के लिए दो चरणों की आवश्यकता होती है: ओपनएपीआई से मॉडल तैयार करना, और स्वयं प्रोजेक्ट बनाना। मैंने AWS SAM को बाद वाले से निपटने दिया।

यहां मेरी मेकफ़ाइल है

.PHONY: build local deploy

generate:
    cd lambda_handlers && go generate ./...

build: generate
    rm -rf .aws-sam
    sam build

local: build
    sam local start-api --env-vars parameters.json

deploy: build
    sam deploy

प्रारंभिक तैनाती

मेरे लिए, स्थानीय स्तर पर एपीआई गेटवे का परीक्षण करने का सबसे आसान तरीका सैम लोकल स्टार्ट-एपीआई चलाना है
चूँकि हमारा फ़ंक्शन पर्यावरण चर पर निर्भर करता है, इसलिए मैंने env vars को sam local

में पास करने के लिए पैरामीटर्स.json फ़ाइल बनाई।

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

मैं मेक डिप्लॉय चलाता हूं लेकिन अभी मैं संपूर्ण परिनियोजन की जांच नहीं करता हूं, बस कंसोल से एक गुप्त नाम ले लेता हूं। मुझे कंसोल में रहस्य को अद्यतन करने की भी आवश्यकता है, ताकि इसमें सही कनेक्शन स्ट्रिंग बनी रहे।

परीक्षण के लिए, मैंने सुपाबेस पर एक डीबी बनाया है और इसे कुछ डमी रिकॉर्ड के साथ जोड़ा है

स्थानीय परीक्षण

मेक लोकल चलाने के बाद मैं स्थानीय स्तर पर एपीआई का परीक्षण कर सकता हूं

AWS Lambda with Go, initial boilerplate

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

AWS पर परीक्षण करें

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

मैं समापन बिंदु पर कॉल करता हूं, और यह अपेक्षा के अनुरूप काम करता है:

AWS Lambda with Go, initial boilerplate

कोल्ड स्टार्ट थोड़ा लंबा है, क्योंकि आरंभीकरण में ~300 एमएस लगते हैं, ज्यादातर इसलिए क्योंकि इसमें रहस्य लेना और डीबी से कनेक्शन स्थापित करना शामिल है। लेकिन ईमानदारी से कहूं तो यह एक अच्छे परिणाम से कहीं अधिक है।

सारांश

दिया गया प्रोजेक्ट गो में सर्वर रहित REST API बनाने के लिए एक प्रारंभिक बिंदु है। यह स्कीमा के लिए ओपनएपीआई और तैनाती और स्थानीय परीक्षण के प्रबंधन के लिए एडब्ल्यूएस एसएएम का उपयोग करता है।

मैंने सीक्रेट मैनेजर से कनेक्शन स्ट्रिंग प्राप्त करने के लिए एक बाहरी पोस्टग्रेस डीबी और एडब्ल्यूएस एसडीके का उपयोग किया है।

लैम्ब्डा हैंडलर और आइटम सेवा के लिए यूनिट परीक्षण भी हैं

मैंने अधिकांश समय AWS भाग को कॉन्फ़िगर करने में बिताया है, जो सभी भाषाओं के लिए समान होगा। गो कोड बहुत सीधा है (इस सरल उपयोग के मामले के लिए)।

विज्ञप्ति वक्तव्य यह आलेख यहां पुन: प्रस्तुत किया गया है: https://dev.to/aws-builders/aws-lambda-with-go-initial-boilerplate-2787?1 यदि कोई उल्लंघन है, तो कृपया इसे हटाने के लिए [email protected] से संपर्क करें।
नवीनतम ट्यूटोरियल अधिक>

चीनी भाषा का अध्ययन करें

अस्वीकरण: उपलब्ध कराए गए सभी संसाधन आंशिक रूप से इंटरनेट से हैं। यदि आपके कॉपीराइट या अन्य अधिकारों और हितों का कोई उल्लंघन होता है, तो कृपया विस्तृत कारण बताएं और कॉपीराइट या अधिकारों और हितों का प्रमाण प्रदान करें और फिर इसे ईमेल पर भेजें: [email protected] हम इसे आपके लिए यथाशीघ्र संभालेंगे।

Copyright© 2022 湘ICP备2022001581号-3