当前位置: 首页 > 科技观察

Serverless的框架——图文玩转AWSLambda

时间:2023-03-20 12:17:48 科技观察

前言微服务架构不同于传统的单体应用方案。我们可以将单体应用拆分成多个核心功能。每个功能称为一个服务,可以独立构建和部署,这意味着各种服务在工作时不会相互影响。进一步应用这种设计理念,成为Serverless。“没有服务器”看似荒谬,实际上服务器还是存在的,只是我们不需要去关注或预先配置服务器。这让开发者可以集中更多的精力——只专注于实现Serverless的功能,通常是AWSLambdaAWSLambda如果你是Java开发者,你应该听说过或使用过JDK1.8中的Lambda,但AWS中的Lambda和JDK中的Lambda没有任何关系在这里做AWSLambda是一种无需配置或管理服务器即可运行代码的计算服务,使用Lambda我们可以为几乎任何类型的应用程序或后端服务运行代码而无需任何管理,我们所要做的就是上传相应的代码,并且Lambda将处理运行和扩展HA代码所需的所有工作。说白了,Lambda就像一个实现某个功能的方法(现实中,Lambda的功能通常是越简单越好),我们把这个方法做成一个服务来调用。你可能会在这里感到困惑。既然Lambda是一个“方法”,那么谁来调用呢?或者怎么称呼?如何调用Lambda为了回答上面的问题,我们需要登录AWS,打开Lambda服务,然后创建一个LambdaFunction(hello-lambda)由于Lambda是一个方法,所以我们需要选择对应的Runtime环境,如下图,总有适合你的(我用的是Node.js,这里就用这个)点击右下角的创建函数按钮,进入配置页面。您可以在上图中红色框线的位置配置启动Lambda的触发器。单击添加触发器。从上图可以看出,AWS内置了很多服务都可以触发Lambda。我在工作中经常用到的有:APIGateway(后面会在demo中用到,也是最常用的调用方式)ALB-ApplicationLoacBalancerCloudFrontDynamoDBS3SNS-SimpleNotificationServiceSQS-SimpleQueueService以上只是AWS内置的一些服务。往下翻,你会发现这里还可以配置很多非AWS的事件源。你应该已经回答了上面的问题。这里暂时不需要任何触发器。首先,点击右上角的测试来测试Lambda。将实施一个简单的Lambda函数。红框的响应只是告诉你每个请求都会有一个对应的RequestID,并且会有一个START/END标志快速定位到日志内容(可以通过CloudWatch查看,这里暂不说明).你可能已经开始发散你的想法了。AWSLambda的使用方法,其实在AWS官网上有很多例子:经典案例如适配多平台图片展示一张原始图片上传到S3后,会适配不同平台大小的图片通过拉姆达调整大小。例如,使用AWSLambda和AmazonAPIGateway构建后端来验证和处理API请求。当用户发布一条消息,订阅者会收到相应的通知后,我们将使用Lambda实现经典的分布式订单服务案例订单服务Demo。为了增强用户体验,或者提高程序吞吐量,或者架构设计程序解耦,考虑到以上情况,我们通常使用消息中间件来完成。假设有一个共同的场景。如果用户在下单时选择开具发票,则需要调用开具发票服务。显然,调用发票服务并不是程序运行的关键路径。这种场景下,我们可以通过Message中间件来解耦。这里有两个服务:OrderServiceInvoiceService如果用Lambda来实现这两个服务,整体的设计思路是这样的:在现实中,我们不可能通过在AWS控制台点击一个按钮来创建每一个服务。在AWS的实际开发中,我们通过编写CloudFormationTemplate(以下简称CFT,实际上是YAML或者JSON格式的定义)来创建相关的AWS服务。看上面的demo,从图中可以看出,我们还有很多服务要创建:Lambda*2APIGatewaySQS如果写AWS原生的CFT,还有很多东西要实现,但是。..懒惰的程序员总能带来很多惊喜。ServerlessFramework编写JDBC。麻烦,各种麻烦随着持久层框架的出现,写AWS原生CFT也很麻烦。ServerlessFramework(以下简称SF)的出现,帮助我们定义了相关的serverless组件(顺便说一句,你在用GraphQL吗?)SF不仅简化了AWS原生CFT的编写,也简化了跨云服务的定义,只是像设计模式中的Facade,就是在顶层建立一个门面,将不同服务的细节隐藏在底层,降低跨云和云端使用的门槛。当前支持的云服务包括以下内容。这里我们不对SF做深入的解释。在我们的演示中,我们只需要使用SF来定义和安装ServerlessFramework。如果你已经安装了Node,全局安装只需要一条npm命令:npmupdate-gserverless安装完成后,查看安装版本是否成功。sls-version配置无服务器框架。因为要使用AWSLambda,所以需要对SF做基础配置。至少让SF有创建AWS服务的权限。当你创建AWS用户时,你可以得到AK“access_key_id”和SK“secret_access_key”(不是SKII),它们实际上是用户名和密码的一种形式,然后通过以下命令添加配置:serverlessconfigcredentials--provideraws--key1234--secret5678--pprofilecustom-profile--providercloudserviceprovider--keyyourAK--secretyourSK--profile如果你有多个账户,可以添加这个配置文件,以便快速区分运行上面的命令后,会在~/创建一个在.aws/目录下创建一个名为credentials的文件来存放上面的配置,像这样:这里的准备工作都完成了,就开始写我们的定义创建一个serverless应用程序创建一个serverless应用程序,命令如下slscreate--templateaws-nodejs--path./demo--namelambda-sqs-lambda--template指定创建的模板--path指定创建的目录--name指定创建的服务名运行上面的命令后,进入demo目录,结构和内容如下?demotree.├──handler.js└──serverless.yml0directories,2files因为我们使用Node.js编写Serverless应用,所以还要在demo目录下执行如下命令初始化目录,因为我们会用到稍后npminit-y的两个npm包的当前结构是这样的(其实多了一个package.json):?demotree.├──handler.js├──package.json└──serverless.yml0directories,3个文件到此为止,准备工作都准备好了,然后在serverless.yml中写对应的定义即可(门槛很低:根据对应的key写YAML就可以了,是不是很简单?),打开serverless.yml文件一看,瞬间一头雾水?#Welcometo无服务器!##This文件是您服务的主要配置文件。#It'sveryminimalatthispointandusesdefaultvalues。#Youcanalwaysaddmoreconfigoptionsformorecontrol。#We'veincludedsomecommentedout在此处配置示例。#Justuncommentanyofthemtogetthatconfigoption。##Forfullconfigoptions,checkthedocs:#docs.serverless.com##HappyCoding!service:lambda-sqs-lambda#appandorgforusewithdashboard.serverless.com#app:your-app-name#org:your-org-name#YoucanpinyourservicetoonlydeploywithaspecificServerlessversion#Checkoutourdocsformoredetails#frameworkVersion:"=X.X.X"provider:name:awsruntime:nodejs12.x#youcanoverwritedefaultshere#stage:dev#region:us-east-1#youcanaddstatementstotheLambdafunction'sIAMRolehere#iamRoleStatements:#-Effect:"Allow"#Action复制代码:#-"s3:ListBucket"#Resource:{"Fn::Join":["",["arn:aws:s3:::",{"Ref":"ServerlessDeploymentBucket"}]]}#-效果:"允许"#Action:#-"s3:PutObject"#Resource:#Fn::Join:#-""#--"arn:aws:s3:::"#-"Ref":"ServerlessDeploymentBucket"#-"/*"#youcandefineservicewideenvironmentvariables这里#environment:#variable1:value1#youcanaddpackaginginformationhere#package:#include:#-include-me.js#-include-me-dir/**#exclude:#-exclude-me.js#-exclude-me-dir/**函数:你好:处理程序:处理程序。你好#Thefollowingareafeweexampleeventsyoucanconfigure#NOTE:请确保更改你的处理程序代码以与这些事件一起使用#Checktheeventdocumentationfordetails#events:#-http:#path:users/create#method:get#-websocket:$connect#-s3:${env:BUCKET}#-schedule:rate(10minutes)#-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:#-"EC2InstanceState-changeNotification"#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#Definefunctionenvironmentvariableshere#environment:#variable2:value2#youcanaddCloudFormationresourcetemplateshere#resources:#Resources:#NewResource:#Type:AWS::S3::Bucket#Properties:#BucketName:my-new-bucket#Outputs:#NewOutput:#Description:"Descriptionfortheoutput"#Value:"Someoutputvalue"首先看一眼,你可能会觉得眼花缭乱。其实这是一套比较完整的Lambda配置。我们不需要这么详细的内容,但是这个文件作为我们的参考。接下来定义demo需要的一切(关键注释已经写在代码中)nodejs12.x#运行时节点版本region:ap-northeast-1#发布到东北地区,其实就是东京地区stage:dev#发布环境是deviamRoleStatements:#CreateIAMrole,允许lambdafunction发送消息到队列:app/order.checkout#第一个lambdafunction程序入口是app目录下order.js中的checkout方法events:#triggertrigger是APIGateway的方法lambdafunction-http:method:postpath:orderinvoice:handler:app/invoice.generate在接收到/order的POST请求时被触发:30events:#trigger是一个SQS服务。当消息队列中有消息时,会触发lambda函数消费该消息。服务类型:AWS::SQS::QueueProperties:QueueName:${self:custom.conf.queueName}#package:#exclude:#-node_modules/**custom:conf:${file(conf/config.json)}#引入外部定义的配置变量config.json内容只定义了队列的名称,只是为了说明配置的灵活性{"queueName":"receiverQueue"}因为我们要模拟一个订单的生成,这里我们使用UUID模拟订单号,因为我们要调用AWS服务API,需要用到aws-sdk,所以需要安装这两个包(这两个理由够吗?){"name":"lambda-sqs-lambda","version":"1.0.0","description":"demoforlambda","scripts":{"test":"echo\"Error:notestspecified\"&&exit1"},"license":"MIT","依赖项":{"uuid":"^8.1.0"},"devDependencies":{"aws-sdk":"^2.6.15"}}接下来我们可以编写两个Lambda函数的代码逻辑OrderLambdaFunctionorderservice很简单,收到订单请求,下单成功后快速返回给用户,同时将下单成功的消息发送给SQS,供下游发票服务开具发票使用'usestrict';constconfig=require('../conf/config.json')constAWS=require('aws-sdk');constsqs=newAWS.SQS();const{v4:uuidv4}=require('uuid');module.exports.checkout=异步(事件,上下文,回调)=>{console.log(事件)letstatusCode=200letmessageif(!event.body){return{statusCode:400,body:JSON.stringify({message:'Noorderbodywasfound',}),};}constregion=context.invokedFunctionArn.split(':')[3]constaccountId=context.invokedFunctionArn.split(':')[4]constqueueName=config['queueName']//组装SQS的URL服务constqueueUrl=`https://sqs.${region}.amazonaws.com/${accountId}/${queueName}`constorderId=uuidv4()try{//调用SQS服务awaitsqs.sendMessage({QueueUrl:queueUrl,MessageBody:event.body,MessageAttributes:{orderId:{StringValue:orderId,DataType:'String',},},}).promise();message='OrdermessageisplacedintheQueue!';}catch(error){console.log(error);message=error;statusCode=500;}//快速返回订单IDreturn{statusCode,body:JSON.stringify({message,orderId,}),};};InvoiceLambdaFunction发票服务逻辑也很简单,消费SQS指定队列中的消息,并将开具的发票发送至客户订单信息邮箱module.exports.generate=(event,context,callback)=>{console.log(event)try{for(constrecordofevent.Records){constmessageAttributes=record.messageAttributes;console.log('OrderIdis-->',messageAttributes。orderId.stringValue);console.log('MessageBody-->',record.body);constreqBody=JSON.parse(record.body)//休眠20秒,模拟生成发票的耗时过程setTimeout(()=>{console.log("Receiptisgeneratedandsentto:"+reqBody.email)},20000)}}catch(error){console.log(error);}}这个demo的代码全部实现了,从中可以看到:我们没有注意lambda的底层服务细节,没有关注sqs服务,只是简单的代码逻辑实现和服务之间的系列定义最后,我们来看一下整体的目录结构:.├──app│├──invoice.js│└──order.js├──conf│└──config.json├──package.json└──serverless.yml2directories,5filesreleaseLambdaapplication发布前,编译应用程序并安装必要的包ge"uuidandaws-sdk"npminstallreleaseapplication非常简单,只需要一个命令:slsdeploy-v运行上面的命令后,大约需要几十秒,在构建结束时,我们的构建服务信息会打印出来:图中的endpoints就是我们后面访问的触发lambda入口的API网关。在调用之前,我们先去AWS控制台查看我们定义的服务lambda函数。SQS-receverQueueAPIGatewayS3从上图中的构建信息中,应该还可以看到一个S3bucket的名称。我们没有创建S3。这是SF自动创建的,用于存放lambdazip包。测试调用API网关端点测试lambda开启SQS服务。你会发现收到一条消息:Next我们来看看InvoiceLambda函数的消耗情况,打开CloudWatch查看日志:从日志中我们可以看到程序“花费”了20秒,然后打印了登录到客户的邮箱(邮件也可以使用AWSSES邮件服务实现)。至此,一个完整的demo就完成了,实际写的代码也不多,就这样搞定了。这么紧的系列删除服务Lambda是按照调用次数收费的。为了防止额外的开销,服务通常在演示结束后销毁。使用SF销毁新建的服务也很简单。你只需要在serverless.yml文件目录下执行这条命令:slsremove总结和体验AWSLambda是一个典型的serverless。借助Lambda,无需服务构建即可实现更细粒度的“服务”,同时也加快了开发速度。Lambda还可以组合许多AWS服务,接收请求,并将计算结果传递给下游服务。此外,众多第三方合作伙伴也正在加入Lambda的触发力量,为Lambda赋予更多的触发可能。同时,借助CI/CD,可以快速实现功能闭环,关注下方二维码。转载本文请联系日工一兵公众号。