正文
-
什么是serverless?
官方介绍中与serverless相对应的词是traditional。 traditional对应的是传统的开发模式。构建一套应用程序的过程中不仅需要写好有关商业的逻辑, 还要考虑一些额外的内容,比如服务器的配置,系统的升级等;这样使得开发人员在开发的过程中花费了 大量的精力放在了与商业逻辑无关的工作上。 相对而言serverless就是将开发人员解放出来,更加集中与商业逻辑相关代码的编写,而并不需要 在意运维以及一些其他的操作,AWS提供的相关的服务,开箱即用。 此外需要引入lambda的概念,这是撰写商业逻辑相关代码的工具。 -
创建第一个API
1、在AWS服务中选择API Gateway 2、创建API 3、选择需要创建API的类型,在生产环境上建议创建rest api,原因在于rest api更加强大也更加规范 此外,WebSocket API 更加适合需要实时连接的场景,游戏公司一般会使用此种。当然在付费的角度上看, rest api调用也是价格最高的 4、创建好API之后需要给API创建,方便API对不同资源的访问得到不同的响应 5、对API创建的资源创建方法,在此我们创建一个GET方法,选择模拟,意思是当外部调用我们创建的这个API 时,并不调用AWS的服务,而是我们可以自定义返回的相关内容 6、在集成响应中配置映射模板,添加映射模板,添加数据 { "name":"hello world" } 7、部署API 填写相关内容 8、访问API对外提供的URL。注意,实际上需要访问的时我们配置的资源,所以实际的URL还要再加上资源地址 https://6u8dpai033.execute-api.us-west-1.amazonaws.com/dev/fiirst-api-test2 返回 { "name":"hello world" } 这样,我们就创建好了第一个API -
对于不同的需求需要用到的服务
- API获取数据的流程
当WEB API接收到请求后,可以触发lambda,在这里可以写代码逻辑,当然也可以做权限控制
下面介绍一下创建API方法后,不同流程的作用
上图模仿了API请求的流程
方法请求:控制方法请求的格式,设置权限
集成请求:当收到请求时,可以和请求集成的操作,比如调用lambda
集成响应:可以设置响应的结果
方法响应:设置最终响应的格式,并且返回客户端
-
上面我们建立了一个简单的API,不涉及数据的传输,接下来,设计一个更加强大的API,并且与lambda进行交互
1、首先创建一个lambda函数,可以选择空白或者蓝图,蓝图就是会有标准的功能模板 2、选择需要编写的语言,在这里选择了python 3、创建执行的角色,因为lambda可能需要调用其他的aws service所以先需要设置好权限,也可以选择创建 具有权限的角色,系统会自动创建一个角色供lambda使用 4、这样我们就成功创建了一个lambda函数,具体的界面还有许多其他的设置,可自行探索 5、以下是系统创建的lambda函数,我们来看一下含义 import json def lambda_handler(event, context): # TODO implement return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') } 定义了一个方法lambda_handler,这个方法是系统自己定义的,可以更改名字,但是更改名字的同时需要注意 同时需要更改运行时设置的处理程序,须一致。 其次方法有两个参数,event是一个外界传入的参数,是json类型,此方法中未用到event,另外还有context 笔者猜测,context是配置系统设置的一个参数。应该是系统运行时自动传入的一个参数 此外,方法体内部返回一个json格式的数据包含了相应码和具体的数据 6、当lambda函数创建好了之后,可以进行测试 选择 configure test event ,将event进行实例化 以下就是系统生成的event模板,可以看出来,是一个json格式的数据 { "key1": "value1", "key2": "value2", "key3": "value3" } 7、配置好event之后可以点击测试 Response { "statusCode": 200, "body": "\"Hello from Lambda!\"" } 可以看出来,测试的结果成功返回了lambda方法体定义的结果 -
在此,我们就创建了一个简单的lambda函数,接下来看看如何与API进行交互
1、同样的我们需要创建一个新的API 2、我们需要创建两个资源点 get方法,对应的操作是获取数据 /fetch-data post方法,对应的操作是将数据传到服务器 /store-data 3、需要对API进行流程的设置 对于/fetch-data ,在集成请求中选择模拟,系统不会进行操作,所以我们自己需要配一下返回的响应体 在集成响应中配上方法体 { name:"zhangsan" age:"22" } 对于/store-data,在集成请求中选择lambda函数,选择函数所在的区域以及名字 4、选择操作-启用CORS-启用CORS并替换现有的CORS标头(都需要此操作) 原因在于此操作可以标准化相应头的格式,笔者目前尚未深究 5、部署此API,并选择继承方法 -
以上我们配好了调用lambda的API ,接下来我们使用一个网站来call这个API观察是否有结果
A Pen by yuerushui (codepen.io)
var xhr = new XMLHttpRequest(); xhr.open('GET','https://6ey90rummb.execute-api.us-west-1.amazonaws.com/dev/fetch-data') xhr.onreadystatechange = function(event){ console.log(event.target.response); } xhr.send(); ----- var xhr = new XMLHttpRequest(); xhr.open('POST','https://6ey90rummb.execute-api.us-west-1.amazonaws.com/dev/store-data') xhr.onreadystatechange = function(event){ console.log(event.target.response); } xhr.send();可以看出右侧返回了我们设置的结果
上面讨论了如何创建API和lambda进行联动,下面介绍一些更深的应用方法
- 模型 在引入模型概念之前,提出一个问题,如果通过API传入的数据结构过于复杂,但是在lambda中只需要其中的一部分数据,可是我们需要在lambda中写大量的方法来处理这些与商业逻辑无关的数据,不够优美,所以接下来可以使用将数据处理与逻辑计算分开,在分开的处理步骤中就使用了模型
回顾一下API调用的流程
- 方法请求
方法请求是API调用经过的第一关,在这里,我们可以验证这个API的请求是否符合规范,上图 请求验证程序选择了验证正文
同样的,这里可以选择验证请求头,那我们如何验证呢?验证标准是什么?
请看下面的内容类型,增加一条 内容类型是 "application/json" 表示传入的请求正文是json的格式,并且需要符合内容标准
以下为验证的json,可以直接设置为模板,然后在此处验证时直接使用这个模板即可
{
"$schema":"http://json-schema.org/draft-04/schema#",
"title":"CompareData",
"type":"object",
"properties":{
"age":{"type":"integer"},
"height":{"type":"integer"},
"income":{"type":"integer"}
},
"required":["age","height","income"]
}
上述模板中比较重要的部分是 properties 和 required 表示需要传入三个属性并且一个不能少,否则调用API会报方法体不正确的
错误
- 集成请求
集成请求是API调用的第二步,当API调用来到了集成请求,说明第一关校验已经通过了,也拿到了API的请求数据,
接下来要做就是设置下一步要进行的操作,在此我们选择lambda函数表示调用lambda,那之前我们提过,如果将复杂的数据
处理从lambda中剥离出来,那么这一步就是可以完成这个需求,同样的我们需要选择映射模板-当未定义模板-设置
Content-Type是application/json表示正文是json格式,然后可以应用之前创建的模板,如下
#set($inputRoot = $input.path('$'))
{
"age" : 42,
"height" : 42,
"income" : 42
}
-----
$input.path('$') 表示解析传入的json数据,得到json的请求体
{
"age" : 42,
"height" : 42,
"income" : 42
}
则表示返回的数据类型,在这里如果这样写将给lambda返回固定死的数据,所以我们需要改一下
{
"age2" : $inputRoot.age,
"height2" : $inputRoot.height,
"income2" : $inputRoot.income
}
这样就把数据格式转换了一下,传给lambda
-
lambda调用
import json import logging def lambda_handler(event, context): # logging.info(event) # TODO implement age = event['age2'] return age*2 以上就是lambda的主题代码了,核心就是去接受event里面的age2属性,并且乘以2进行返回 那我们在集成请求中其实定义了传给lambda的age2就是 "age2" : $inputRoot.age 。其实就是从input 正文中取到的age -
集成相应
我们就到了集成相应这一步,我们拿到了lambda返回的数据是 age*2 我们是否可以包装这个结果,答案是可以,就在这一
步进行。同样的我们在映射模板中创建一个新的模板设置 Content-Type 为 "application/json",文本中可以自己定义json
的格式,也可以使用之前创建好的模板,如果使用了模板,那么会自动生成文本,也可以进行修改
-
方法响应
方法响应决定了最终返回内容的形状 -
接下来我们测试一下
可以看到当我们请求正文是
{
"age":22,
"height":123,
"income":2000
}
返回的结果是
{
"your-age": 44,
"height": null,
"income": null
}
和我们预期是一致的
- 加餐:CloudWatch
如果想查看具体日志可以打开CloudWatch,在日志-日志组,找到相应的日志。
-
接下来我们考虑一个需求,如果想要生成两个get资源,分别对应获取一条数据和全部数据,应该如何操作呢?
按照之前的步骤,应该是创建两个资源,在创建两个lambda函数分别对应获取单个数据和全部数据,但是有没有更好的方法,答案是有的。
如上图,我们在创建资源的时候,并未锁定资源是all还是single,我们用type来表示资源。那后续如果可以动态识别API访问的路径,然后传给lambda,然后在lambda中进行逻辑判断返回不同的值即可,具体做法如下。
1、 编写lambda 代码块
import json
def lambda_handler(event, context):
# TODO implement
if event["type"] = 'all':
return "get all"
else if event["type"] = 'single':
return "get single"
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
2、那如何从请求的url中获取type的值且当作请求体传给lambda呢?get方法本来是没有请求体的,所以我们需要
在集成请求中将数据进行转换,很自然,应为集成请求就负责将数据传给lambda,下面是集成请求中的方法
{
"type" : "$input.params('type')"
}
可以看出来,通过$input.params('type')获取url中的资源地址发送给lambda
下面提供了官方文档,可以进行查阅
API Gateway mapping template and access logging variable reference - Amazon API Gateway
- codepen 进行url测试
可以看出来,当对不同的url进行call的时候,返回了不同的结果
这期就讲到这里,谢谢观看