[Golang] AWS Lambda thông qua Serverless Framework – Phần 2

Mục Tiêu: Nhận request data từ client thông qua api-gateway Response data về cho client thông qua api-gateway Cấu hình đơn giản như: timeout, memory, environment,… Chuẩn Bị: Để tiếp tục đọc và hiểu những phần sau, cần phải có một lambda function được tạo trước đó. Nếu bạn chưa tạo một lambda function thì

Mục Tiêu:

  • Nhận request data từ client thông qua api-gateway
  • Response data về cho client thông qua api-gateway
  • Cấu hình đơn giản như: timeout, memory, environment,…

Chuẩn Bị:

  • Để tiếp tục đọc và hiểu những phần sau, cần phải có một lambda function được tạo trước đó.
  • Nếu bạn chưa tạo một lambda function thì tham khảo here để hiểu các phần sau.

Giải Quyết Vấn Đề

1. Nhận request data:

  • http body có dạng.
{
    "request_id":"123123213",
    "data": {
        "name":"open dev"
    }
}
  • Trong lambda muốn get data từ client, thì sử dụng api-gateway events APIGatewayProxyRequest. Struct này là của aws-sdk trong struct chỉ cần quan tâm field Body và dùng json.Unmarshal
// APIGatewayProxyRequest contains data coming from the API Gateway proxy
type APIGatewayProxyRequest struct {
	Resource                        string                        `json:"resource"` // The resource path defined in API Gateway
	Path                            string                        `json:"path"`     // The url path for the caller
	HTTPMethod                      string                        `json:"httpMethod"`
	Headers                         map[string]string             `json:"headers"`
	MultiValueHeaders               map[string][]string           `json:"multiValueHeaders"`
	QueryStringParameters           map[string]string             `json:"queryStringParameters"`
	MultiValueQueryStringParameters map[string][]string           `json:"multiValueQueryStringParameters"`
	PathParameters                  map[string]string             `json:"pathParameters"`
	StageVariables                  map[string]string             `json:"stageVariables"`
	RequestContext                  APIGatewayProxyRequestContext `json:"requestContext"`
	Body                            string                        `json:"body"`
	IsBase64Encoded                 bool                          `json:"isBase64Encoded,omitempty"`
}
  • Khai báo một struct để unmarshal data
type RequestBodyAPIGW struct{
	RequestID string`json:"request_id"`
	Data      interface{}`json:"data"`}
  • Unmarshal data:
json.Unmarshal([]byte(eventReq.Body),&req)

2. Response data:

  • client muốn response có format. Trong đó field data thì trả về dữ liệu giống với request truyền vào.
{
    "data": {
        "name": "open dev"
    },
    "responseId": "123123213",
    "responseMessage": "successfully",
    "responseTime": "2022-12-17T03:53:42.325+00:00"
}
  • Để lambda return data, thì cũng sử dụng api-gateway events APIGatewayProxyResponse.
// APIGatewayProxyResponse configures the response to be returned by API Gateway for the request
type APIGatewayProxyResponse struct {
	StatusCode        int                 `json:"statusCode"`
	Headers           map[string]string   `json:"headers"`
	MultiValueHeaders map[string][]string `json:"multiValueHeaders"`
	Body              string              `json:"body"`
	IsBase64Encoded   bool                `json:"isBase64Encoded,omitempty"`
}
  • Thông thường response sẽ có 2 dạng, api xảy ra error hoặc không. Mình viết 2 hàm response và gắn vào field Body.
type HttpResponse struct {
	Uuid string // uuid, indicator per api
	Err  error 
	Time string // time tracing
	Data interface{}
}

func responseOk(respBody HttpResponse) string {
	var buf bytes.Buffer
	mapRes := map[string]interface{}{
		"responseId":      respBody.Uuid,
		"responseMessage": "successfully",
		"responseTime":    respBody.Time,
	}
	if respBody.Data != nil {
		mapRes["data"] = respBody.Data
	}
	body, errMarshal := json.Marshal(mapRes)
	if errMarshal != nil {
		log.Default().Println("marshal response err", errMarshal)
	}
	json.HTMLEscape(&buf, body)
	return buf.String()
}

func responseErr(respBody HttpResponse) string {
	var buf bytes.Buffer
	mapRes := map[string]interface{}{
		"responseId":      respBody.Uuid,
		"responseMessage": respBody.Err.Error(),
		"responseTime":    respBody.Time,
	}

	body, errMarshal := json.Marshal(mapRes)
	if errMarshal != nil {
		log.Default().Println("marshal response err", errMarshal)
	}
	json.HTMLEscape(&buf, body)
	return buf.String()
}

3. Cấu hình timeout, memory, env:

  • Để cấu hình 3 thuộc tính trên, đơn giản chỉ cần mở file serverless.yml.
  • Trong block provider: ta time 2 field timeoutmemorySize, thì đã set được timeout và resource.
provider:
   timeout: 6
   memorySize: 256
  • Environment của lambda, cũng tương tự trong block provider:, thêm environment: trong lambda sẽ có env env_test có giá trị là value-test
provider:
      environment:
            env_test: "value-test"

Deploy Function:

  • file Makefile:
build:
	env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/hello hello/main.go
clean:
	rm -rf ./bin
deploy: clean build
	sls deploy --verbose
  • run:
make deploy
  • output:

Test:

Full source:

  • file golang:
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"log"
	"time"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

type Response events.APIGatewayProxyResponse

type RequestBodyAPIGW struct {
	RequestID string      `json:"requestId"`
	Data      interface{} `json:"data"`
}

// type ResponseBodyAPIGW struct {
// 	RequestID string `json:"request_id"`
// 	Message   string `json:"message"`
// }

func Handler(ctx context.Context, eventReq events.APIGatewayProxyRequest) (Response, error) {
	var (
		req  = RequestBodyAPIGW{}
		resp = Response{
			StatusCode:      404,
			IsBase64Encoded: false,
			Headers: map[string]string{
				"Content-Type":           "application/json",
				"X-MyCompany-Func-Reply": "hello-handler",
			},
		}
	)
	err := json.Unmarshal([]byte(eventReq.Body), &req)
	if err != nil {
		resp.Body = ParseResponse(HttpResponse{
			Uuid: req.RequestID,
			Err:  err})
		return resp, nil
	}
	resp.StatusCode = 200
	resp.Body = ParseResponse(HttpResponse{Uuid: req.RequestID, Data: req.Data})
	return resp, nil
}

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

type HttpResponse struct {
	Uuid string // uuid, indicator per api
	Err  error
	Time string // time tracing
	Data interface{}
}

func ParseResponse(respBody HttpResponse) string {
	respBody.Time = time.Now().Format("2006-01-02T15:04:05.000-07:00")
	if respBody.Err != nil {
		return responseErr(respBody)
	}
	return responseOk(respBody)
}

func responseOk(respBody HttpResponse) string {
	var buf bytes.Buffer
	mapRes := map[string]interface{}{
		"responseId":      respBody.Uuid,
		"responseMessage": "successfully",
		"responseTime":    respBody.Time,
	}
	if respBody.Data != nil {
		mapRes["data"] = respBody.Data
	}
	body, errMarshal := json.Marshal(mapRes)
	if errMarshal != nil {
		log.Default().Println("marshal response err", errMarshal)
	}
	json.HTMLEscape(&buf, body)
	return buf.String()
}

func responseErr(respBody HttpResponse) string {
	var buf bytes.Buffer
	mapRes := map[string]interface{}{
		"responseId":      respBody.Uuid,
		"responseMessage": respBody.Err.Error(),
		"responseTime":    respBody.Time,
	}

	body, errMarshal := json.Marshal(mapRes)
	if errMarshal != nil {
		log.Default().Println("marshal response err", errMarshal)
	}
	json.HTMLEscape(&buf, body)
	return buf.String()
}
  • file serverless.yml
service: lambda-go
frameworkVersion: '3'

provider:
  name: aws
  runtime: go1.x
  timeout: 6
  memorySize: 256

  environment:
    env_test: "value-test"

package:
  patterns:
    - '!./**'
    - ./bin/**

functions:
  hello:
    handler: bin/hello
    events:
      - httpApi:
          path: /hello
          method: post

Nguồn: viblo.asia

Bài viết liên quan

Thay đổi Package Name của Android Studio dể dàng với plugin APR

Nếu bạn đang gặp khó khăn hoặc bế tắc trong việc thay đổi package name trong And

Lỗi không Update Meta_Value Khi thay thế hình ảnh cũ bằng hình ảnh mới trong WordPress

Mã dưới đây hoạt động tốt có 1 lỗi không update được postmeta ” meta_key=

Bài 1 – React Native DevOps các khái niệm và các cài đặt căn bản

Hướng dẫn setup jenkins agent để bắt đầu build mobile bằng jenkins cho devloper an t

Chuyển đổi từ monolith sang microservices qua ví dụ

1. Why microservices? Microservices là kiến trúc hệ thống phần mềm hướng dịch vụ,