1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        手把手帶你玩轉 AWS Lambda

        共 21555字,需瀏覽 44分鐘

         ·

        2020-10-16 15:00

        前言

        微服務架構有別于傳統(tǒng)的單體式應用方案,我們可將單體應用拆分成多個核心功能。每個功能都被稱為一項服務,可以單獨構建和部署,這意味著各項服務在工作時不會互相影響

        這種設計理念被進一步應用,就變成了無服務(Serverless)?!笩o服務」看似挺荒唐的,其實服務器依舊存在,只是我們不需要關注或預置服務器。這讓開發(fā)人員的精力更集中——只關注功能實現(xiàn)

        Serverless 的典型便是 AWS Lambda

        AWS Lambda

        如果你是 Java 開發(fā)人員,你應該聽說過或使用過 JDK 1.8 里面的 Lambda,但是 AWS 中的 Lambda 和 JDK 中的 Lambda 沒有任何關系

        這里的 AWS Lambda 就是一種計算服務,無需預置或管理服務器即可運行代碼,借助 Lambda,我們幾乎可以為任何類型的應用程序或后端服務運行代碼,而且完全無需管理,我們要做的只是上傳相應的代碼,Lambda 會處理運行和擴展 HA 代碼所需的一切工作

        說的直白一點

        Lambda 就好比實現(xiàn)某一個功能的方法 (現(xiàn)實中,通常會讓 Lambda 功能盡可能單一),我們將這個方法做成了一個服務供調用

        到這里你可能會有個困惑,Lambda 既然就是一個「方法」,那誰來調用?或怎么來調用呢?

        如何調用 Lambda

        為了回答上面這個問題,我們需要登陸到 AWS,打開 Lambda 服務,然后創(chuàng)建一個 Lambda Function (hello-lambda)

        Lambda 既然是個方法,就要選擇相應的 Runtime 環(huán)境,如下圖所示,總有一款適合你的(最近在用 Node.js, 這里就用這個吧)

        點擊右下角的 Create function 按鈕進入配置頁面

        在上圖紅色框線的位置就可以配置出發(fā) Lambda 的觸發(fā)器了,點擊 Add trigger

        從上圖可以看出,AWS 內置的很多服務都可以觸發(fā) Lambda,我在工作中常用的有:

        • API Gateway (一會的 demo 會用到,也是最常見的調用方式)
        • ALB - Application Loac Balancer
        • CloudFront
        • DynamoDB
        • S3
        • SNS - Simple Notification Service
        • SQS - Simple Queue Service

        上面只是 AWS 內置的一些服務,向下滑動,你會發(fā)現(xiàn),你也可以配置很多非 AWS 的事件源

        到這里,上面的問題你應該已經有了答案了。這里暫時先無需任何 trigger,先點擊右上角的 Test 測試一下 Lambda

        一個簡單的 Lambda Function 就實現(xiàn)了,紅色框線的 response 只是告訴大家,每個請求都會有相應的 Request ID,更有 START/END 標識快速定位 Log 內容 (可以通過 CloudWatch 查看,這里暫不展開說明)

        你也可能已經開始發(fā)散你的思維了,如何運用 AWS Lambda,其實在 AWS 官網有很多樣例:

        經典案例

        比如為了適應多平臺圖片展示,一張原始圖片上傳到 S3 后,會通過 Lambda resize 適應不同平臺大小的圖片

        比如使用 AWS Lambda 和  Amazon API Gateway 構建后端,以驗證和處理 API 請求,當某一個用戶發(fā)布一條動態(tài),訂閱用戶將收到相應的通知

        接下來我們就用 Lambda 實現(xiàn)經典的分布式訂單服務案例

        訂單服務 Demo

        為了增強用戶使用體驗,或者為了提升程序吞吐量,亦或是為了架構設計程序解耦,考慮到以上這些情況,我們通常都會借助消息中間件來完成

        假設有一常見場景,用戶下訂單時如果選擇開具發(fā)票,則需要調用發(fā)票服務,很顯然調用發(fā)票服務不是程序運行的關鍵路徑,這種場景,我們就可以通過消息中間件來解耦。這里有兩個服務:

        1. 訂單服務
        2. 發(fā)票服務

        如果用 Lambda 來實現(xiàn)兩個服務,整體設計思想就是這樣滴:

        現(xiàn)實中,我們不可能在 AWS console 通過點擊按鈕來創(chuàng)建各個服務的,在 AWS 實際開發(fā)中, 我們通過寫 CloudFormation Template (以下會簡稱 CFT,其實就是一種 YAML 或者 JSON 格式的定義)來創(chuàng)建相關 AWS 服務,如果上述這個 Demo,從圖中可以看出,我們要創(chuàng)建的服務還是非常多的:

        • Lambda * 2
        • API Gateway
        • SQS

        如果寫 AWS 原生的 CFT,要實現(xiàn)的內容還是挺多的

        但是...... 懶惰的程序員總是能帶來很多驚喜

        Serverless Framework

        寫 JDBC 麻煩,就有了各種持久層框架的出現(xiàn),同樣寫 AWS 原生 CFT 麻煩,就有了 Serverless Framework (以下會簡稱 SF)的出現(xiàn)幫助我們定義相關 Serverless 組件 (順便問一下,GraphQL 你們有在用嗎?)

        SF 不但簡化了 AWS 原生 CFT 的編寫,還簡化了跨云服務的定義,就好比設計模式當中的 Facade,在上面建立了一層門面,隱藏了底部不同服務的細節(jié),降低了跨云并用云的門檻,目前支持的云服務有下面這些

        這里暫時不會對 SF 展開深入的說明,在我們的 demo 中只不過是要應用 SF 來定義

        安裝 Serverless Framework

        如果你有安裝 Node,那只需要一條 npm 命令全局安裝即可:

        npm update -g serverless

        安裝過后檢查一下安裝版本是否成功

        sls -version

        配置 Serverless Framework

        由于要使用 AWS 的 Lambda,所以要對 SF 做基本的配置,至少要讓 SF 有權限創(chuàng)建 AWS 服務,當你創(chuàng)建一個 AWS 用戶時,你可以獲取 AK 「access_key_id」和 SK 「secret_access_key」(不是 SKII 哦),其實就是一種用戶名和密碼形式

        然后通過下面一條命令添加配置就可以了:

        serverless config credentials --provider aws --key 1234 --secret 5678 --profile custom-profile
        • --provider 云服務商
        • --key 你的AK
        • --secret 你的SK
        • --profile 如果你有多個賬戶時,你可以添加這個 profile 做快速區(qū)分

        運行上述命令后,就會在 ~/.aws/目錄創(chuàng)建一個名為 credentials 的文件存儲上述配置,就像這樣:

        到這里準備工作就都完成了,開始寫我們的定義就好了

        創(chuàng)建 Serverless 應用

        通過下面一條命令創(chuàng)建 serverless 應用

        sls create --template aws-nodejs --path ./demo --name lambda-sqs-lambda
        • --template 指定創(chuàng)建的模版
        • --path 指定創(chuàng)建的目錄
        • --name 指定創(chuàng)建的服務名稱

        運行上述命令后,進入 demo 目錄就是下面這個結構和內容了

        ?  demo tree
        .
        ├── handler.js
        └── serverless.yml

        0 directories, 2 files

        因為我們是用 Node.js 來編寫 Serverless 應用,同樣在 demo 目錄下執(zhí)行下面命令來初始化該目錄,因為我們后面要用到兩個 npm package

        npm init -y

        現(xiàn)在的結構是這樣的(其實就多了一個 package.json):

        ?  demo tree
        .
        ├── handler.js
        ├── package.json
        └── serverless.yml

        0 directories, 3 files

        至此,準備工作都已就緒,接下來就在 serverless.yml 中寫相應的定義就可以了 (門檻很低:按照相應的 key 寫 YAML 即可,是不是很簡單?),打開 serverless.yml 文件來看一下,瞬間懵逼?

        # Welcome to Serverless!
        #
        # This file is the main config file for your service.
        # It's very minimal at this point and uses default values.
        # You can always add more config options for more control.
        # We've included some commented out config examples here.
        # Just uncomment any of them to get that config option.
        #
        # For full config options, check the docs:
        #    docs.serverless.com
        #
        # Happy Coding!

        service: lambda-sqs-lambda
        # app and org for use with dashboard.serverless.com
        #app: your-app-name
        #org: your-org-name

        # You can pin your service to only deploy with a specific Serverless version
        # Check out our docs for more details
        # frameworkVersion: "=X.X.X"

        provider:
          name: aws
          runtime: nodejs12.x

        # you can overwrite defaults here
        #  stage: dev
        #  region: us-east-1

        # you can add statements to the Lambda function's IAM Role here
        #  iamRoleStatements:
        #    - Effect: "Allow"
        #      Action:
        #        - "s3:ListBucket"
        #      Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
        #    - Effect: "Allow"
        #      Action:
        #        - "s3:PutObject"
        #      Resource:
        #        Fn::Join:
        #          - ""
        #          - - "arn:aws:s3:::"
        #            - "Ref" : "ServerlessDeploymentBucket"
        #            - "/*"

        # you can define service wide environment variables here
        #  environment:
        #    variable1: value1

        # you can add packaging information here
        #package:
        #  include:
        #    - include-me.js
        #    - include-me-dir/**
        #  exclude:
        #    - exclude-me.js
        #    - exclude-me-dir/**

        functions:
          hello:
            handler: handler.hello
        #    The following are a few example events you can configure
        #    NOTE: Please make sure to change your handler code to work with those events
        #    Check the event documentation for details
        #    events:
        #      - http:
        #          path: users/create
        #          method: get
        #      - websocket: $connect
        #      - s3: ${env:BUCKET}
        #      - schedule: rate(10 minutes)
        #      - sns: greeter-topic
        #      - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
        #      - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx
        #      - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
        #      - iot:
        #          sql: "SELECT * FROM 'some_topic'"
        #      - cloudwatchEvent:
        #          event:
        #            source:
        #              - "aws.ec2"
        #            detail-type:
        #              - "EC2 Instance State-change Notification"
        #            detail:
        #              state:
        #                - pending
        #      - cloudwatchLog: '/aws/lambda/hello'
        #      - cognitoUserPool:
        #          pool: MyUserPool
        #          trigger: PreSignUp
        #      - alb:
        #          listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/
        #          priority: 1
        #          conditions:
        #            host: example.com
        #            path: /hello

        #    Define function environment variables here
        #    environment:
        #      variable2: value2

        # you can add CloudFormation resource templates here
        #resources:
        #  Resources:
        #    NewResource:
        #      Type: AWS::S3::Bucket
        #      Properties:
        #        BucketName: my-new-bucket
        #  Outputs:
        #     NewOutput:
        #       Description: "Description for the output"
        #       Value: "Some output value"

        乍一看,你可能覺得眼花繚亂,其實這是一個相對完整的 Lambda 配置全集,我們不需要這么詳細的內容,不過這個文件作為我們的參考

        接下來我們就定義 demo 所需要的一切 (關鍵注釋已經寫在代碼中)

        service:
          name: lambda-sqs-lambda # 定義服務的名稱

        provider:
          name: aws # 云服務商為 aws
          runtime: nodejs12.x # 運行時 node 的版本
          region: ap-northeast-1 # 發(fā)布到 northeast region,其實就是東京 region
          stage: dev # 發(fā)布環(huán)境為 dev
          iamRoleStatements: # 創(chuàng)建 IAM role,允許 lambda function 向隊列發(fā)送消息
            - Effect: Allow
              Action:
                - sqs:SendMessage
              Resource:
                - Fn::GetAtt: [ receiverQueue, Arn ]
              
        functions: # 定義兩個 lambda functions
          order:
            handler: app/order.checkout # 第一個 lambda function 程序入口是 app 目錄下的 order.js 里面的 checkout 方法
            events: # trigger 觸發(fā)器是 API Gateway 的方式,當接收到 /order 的 POST 請求時觸發(fā)該 lambda function
              - http:
                  method: post
                  path: order

          invoice:
            handler: app/invoice.generate # 第二個 lambda function 程序入口是 app 目錄下的 invoice.js 里面的 generate 方法
            timeout: 30
            events: # trigger 觸發(fā)器是 SQS 服務,消息隊列有消息時觸發(fā)該 lambda function 消費消息
              - sqs:
                  arn:
                    Fn::GetAtt:
                      - receiverQueue
                      - Arn
        resources:
          Resources:
            receiverQueue: # 定義 SQS 服務,也是 Lambda 需要依賴的服務
              Type: AWS::SQS::Queue
              Properties:
                QueueName: ${self:custom.conf.queueName}

        # package:
        #   exclude:
        #     - node_modules/**

        custom: 
          conf: ${file(conf/config.json)} # 引入外部定義的配置變量

        config.json 內容僅僅定義了 queue 的名稱,只是為了說明配置的靈活性

        {
          "queueName""receiverQueue"
        }

        因為我們要模擬訂單的生成,這里用 UUID 來模擬訂單號,

        因為我們要調用 AWS 服務API,所以要使用 aws-sdk,

        所以要安裝這兩個 package (這兩個理由夠充分嗎?)

        {
          "name""lambda-sqs-lambda",
          "version""1.0.0",
          "description""demo for lambda",
          "scripts": {
            "test""echo \"Error: no test specified\" && exit 1"
          },
          "license""MIT",
          "dependencies": {
            "uuid""^8.1.0"
          },
          "devDependencies": {
            "aws-sdk""^2.6.15"
          }
        }

        接下來,我們就可以編寫兩個 Lambda function 的代碼邏輯了

        Order Lambda Function

        訂單服務很簡單,接收一個下單請求,下單成功后快速返回給用戶,同時將訂單下單成功的消息發(fā)送到 SQS 中,供下游發(fā)票服務開具發(fā)票使用

        'use strict';

        const config = require('../conf/config.json')
        const AWS = require('aws-sdk');
        const sqs = new AWS.SQS();
        const { v4: uuidv4 } = require('uuid');

        module.exports.checkout = async (event, context, callback) => {
            console.log(event)
            let statusCode = 200
            let message

            if (!event.body) {
                return {
                statusCode400,
                bodyJSON.stringify({
                    message'No order body was found',
                }),
                };
            }

            const region = context.invokedFunctionArn.split(':')[3]
            const accountId = context.invokedFunctionArn.split(':')[4]
            const queueName = config['queueName']

            // 組裝 SQS 服務的 URL
            const queueUrl = `https://sqs.${region}.amazonaws.com/${accountId}/${queueName}`
            const orderId = uuidv4()

            try {
               // 調用 SQS 服務
                await sqs.sendMessage({
                    QueueUrl: queueUrl,
                    MessageBody: event.body,
                    MessageAttributes: {
                        orderId: {
                            StringValue: orderId,
                            DataType'String',
                        },
                    },
                }).promise();

                message = 'Order message is placed in the Queue!';

          } catch (error) {
            console.log(error);
            message = error;
            statusCode = 500;
          }

          // 快速返回訂單 ID
          return {
            statusCode,
            bodyJSON.stringify({
              message, orderId,
            }),
          };
        };

        Invoice Lambda Function

        發(fā)票服務邏輯同樣很簡單,消費 SQS 指定隊列中的消息,并將開具出的發(fā)票發(fā)送到客戶訂單信息的 email 中

        module.exports.generate = (event, context, callback) => {
            console.log(event)
            try {
                for (const record of event.Records) {
                  const messageAttributes = record.messageAttributes;
                  console.log('OrderId is  -->  ', messageAttributes.orderId.stringValue);
                  console.log('Message Body -->  ', record.body);
                  const reqBody = JSON.parse(record.body)
                  // 睡眠 20 秒,模擬生成發(fā)票的耗時過程
                  setTimeout( () => {
                      console.log("Receipt is generated and sent to :" + reqBody.email)
                  }, 20000)
                }
            } catch (error) {
                console.log(error);
            }
        }

        到此 demo 的代碼就全部實現(xiàn)了,從中你可以看到:

        我們沒有關注 lambda 的底層服務細節(jié),沒有關注 sqs 的服務,只是簡單的代碼邏輯實現(xiàn)以及服務之間的串聯(lián)定義

        最后我們看一下整體的目錄結構吧:

        .
        ├── app
        │   ├── invoice.js
        │   └── order.js
        ├── conf
        │   └── config.json
        ├── package.json
        └── serverless.yml

        2 directories, 5 files

        發(fā)布 Lambda 應用

        在發(fā)布之前,編譯一下應用,安裝必須的 package「uuid 和 aws-sdk」

        npm install

        發(fā)布應用非常簡單,只需要一條命令:

        sls deploy -v

        運行上述命令后大概需要等帶幾十秒鐘, 在構建的最后,會打印出我們的構建服務信息:

        上圖的 endpoints 就是我們一會要訪問的 API gateway 觸發(fā) lambda 的入口,在調用之前,我們先到 AWS console 看一下我們定義的服務

        lambda functions

        SQS-receverQueue

        API Gateway

        S3

        從上圖的構建信息中你應該還看到一個 S3  bucket 的名稱,我們并沒有創(chuàng)建 S3, 這是 SF 自動幫我們創(chuàng)建,用來存儲 lambda zip package 的

        測試

        調用 API gateway 的 endpoint 來測試 lambda

        打開 SQS 服務,你會發(fā)現(xiàn),接收到一條消息:

        接下來我們看看 Invoice Lambda function 的消費情況,打開 CloudWatch 查看 log:

        從 log 中可以看出程序“耗費” 20 秒后打印了向客戶郵件的 log(郵件也可以借助 AWS SES 郵件服務來實現(xiàn))

        至此,一個完整的 demo 就完成了,實際編寫的代碼并沒有多少,就搞定了這么緊密的串聯(lián)

        刪除服務

        Lambda 是按照調用次數(shù)進行收取費用的,為了防止造成額外的開銷,demo 結束后通常都會將服務銷毀,使用 SF 銷毀剛剛創(chuàng)建的服務也非常簡單,只需要在 serverless.yml 文件目錄執(zhí)行這條命令:

        sls remove

        總結與感受

        AWS Lambda 是 Serverless 的典型,借助 Lambda 可以實現(xiàn)更小粒度的“服務”,無需服務搭建也加快了開發(fā)速度。Lambda 同樣可以結合 AWS 很多其服務,接收請求,將計算結果傳遞給下游服務等。另外很多第三方合作伙伴也在加入 Lambda 的 trigger 大部隊,給 Lambda 更多觸發(fā)可能,同時,借助 CI/CD,可以快速實現(xiàn)功能閉環(huán)

        開通 AWS free tier,玩轉 Lambda

        關注下方公眾號

        回復【lambda】獲取代碼地址


        往期推薦

        為什么 Redis 單線程能支撐高并發(fā)?

        華為又一戰(zhàn)略級生態(tài)啟程:華為IdeaHub 使能千行百業(yè)

        必須了解的 MySQL 三大日志

        說了低調...這下百度知道了...

        離職半年了,最近又開始被吐槽輸出不夠...


        深度內容

        推薦加入


        最近熱門內容回顧   #技術人系列


        瀏覽 68
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            av男人天堂网 | 国产在线视频你懂的 | 老牛影视av一区二区 | 婷婷五月天色色 | 五月婷婷欧美激情 | 亚洲性爱视频在线观看 | 免费操比视频 | 伊人日日 | 国产人伦子伦一级A片下载 | 欧美日产久久 |