作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Andrew拥有用多种语言开发应用程序的经验. 他曾在领先的网络公司和初创公司工作.
在构建web应用程序时, 有许多选择可以帮助或阻碍您的应用程序在未来,一旦您承诺. 语言、框架、托管和数据库等选择至关重要.
其中一个选择是使用面向服务的体系结构(Service Oriented Architecture, SOA)还是传统的方法来创建基于服务的应用程序, 单独的应用程序. 这是影响初创公司的常见架构决策, scale-ups, 和企业公司一样.
面向服务的体系结构被大量知名的独角兽和顶级科技公司(如谷歌)所使用, Facebook, Twitter, Instagram, and Uber. 从表面上看,这种体系结构模式适用于大公司,但它是否适用于您呢?
在本文中,我们将介绍面向服务的体系结构的主题, 以及如何利用AWS Lambda与Python结合来轻松构建可扩展的, 有成本效益的服务. 为了证明这些观点, 我们将使用Python构建一个简单的图像上传和大小调整服务, AWS Lambda, Amazon S3和其他一些相关的工具和服务.
面向服务的体系结构(SOA)并不新鲜,几十年前就有了根源. 近年来,它作为一种模式越来越受欢迎,因为它为面向web的应用程序提供了许多好处.
SOA is, in essence, 将一个大型应用程序抽象为许多相互通信的小型应用程序. 这遵循软件工程的几个最佳实践,比如解耦, 关注点分离和单一职责架构.
SOA的实现在粒度方面各不相同:从极少数覆盖大面积功能的服务到数十个或数百个小型应用程序,这些应用程序被称为“microservice” architecture. 无论粒度级别如何, SOA实践者普遍认同的一点是,它绝不是免费的午餐. 就像软件工程中的许多好的实践一样, 这是一项需要额外计划的投资, 开发和测试.
AWS Lambda是AWS提供的一项服务 亚马逊网络服务 platform. AWS Lambda允许您上传将在Amazon管理的按需容器上运行的代码. AWS Lambda将管理服务器的供应和管理,以运行代码, 因此,用户所需要的只是一组打包的运行代码和一些配置选项,以定义服务器运行的上下文. 这些托管的应用程序称为Lambda函数.
AWS Lambda有两种主要的运行模式:
异步事件驱动:
Lambda函数可以以异步模式运行以响应事件. 任何事件源,如S3、SNS等. 不会阻塞和Lambda函数可以在许多方面利用这一点, 例如为一些事件链建立处理管道. 有很多信息来源, 并根据源事件将从事件源推送到Lambda函数, 或由AWS Lambda轮询事件.
Synchronous/Request->Response:
对于需要同步返回响应的应用程序, Lambda可以在同步模式下运行. 通常,它与API Gateway服务一起使用,从AWS Lambda向最终用户返回HTTP响应, 然而,Lambda函数也可以通过直接调用AWS Lambda来同步调用.
AWS Lambda函数作为zip文件上传,其中包含处理程序代码以及处理程序操作所需的任何依赖项. Once uploaded, AWS Lambda将在需要时执行此代码,并在需要时将服务器数量从零扩展到数千台, 不需要消费者的任何额外干预.
基本SOA是一种将代码库构建为小型应用程序的方法,以便以本文前面描述的方式使应用程序受益. 由此,这些应用程序之间的通信方法成为焦点. 事件驱动的SOA(又名SOA 2).0)不仅允许SOA中传统的直接服务到服务通信1.0,还可以在整个体系结构中传播事件,以便沟通更改.
事件驱动的体系结构是一种自然地促进松耦合和可组合性的模式. 通过创造事件和对事件作出反应, 可以特别添加服务,以便向现有事件添加新功能, 并且可以组合多个事件以提供更丰富的功能.
AWS Lambda可以用作轻松构建SOA 2的平台.0 applications. There are many ways to trigger a Lambda function; from the traditional message-queue approach with Amazon SNS, 到上传到Amazon S3的文件所创建的事件, 或者用Amazon SES发送的电子邮件.
我们将构建一个简单的应用程序来利用AWS堆栈上传和检索图像. This example project will contain two lambda functions: one running in request->response mode that will be used to serve our simple web frontend, 另一个会检测上传的图片并调整它们的大小.
第一个lambda函数将异步运行,以响应存储上传图像的S3存储桶上触发的文件上传事件. 它将获取所提供的图像并调整其大小以适合400x400的图像.
另一个lambda函数将为HTML页面提供服务, 为用户提供查看由其他Lambda函数调整大小的图像的功能,以及上传图像的界面.
在开始之前,我们需要配置一些必要的AWS服务,如IAM和S3. 这些将使用基于web的AWS控制台进行配置. However, 大多数配置也可以通过使用AWS命令行实用程序来实现, 我们稍后会用到哪个.
S3(或简单存储服务)是一种Amazon对象存储服务,它为任何数据提供可靠且经济高效的存储. 我们将使用S3来存储将要上传的图像, 以及我们处理过的图像的缩放版本.
S3服务可以在AWS控制台中“存储”下的“服务”下拉列表中找到 & “内容交付”子节. 创建存储桶时,系统会提示您输入存储桶名称并选择一个区域. 选择靠近用户的区域将允许S3针对延迟和成本进行优化, 还有一些监管因素. 对于本例,我们将选择“US Standard”区域. 这个区域稍后将用于托管AWS Lambda函数.
值得注意的是,S3桶名必须是唯一的, 因此,如果选择的名字被占用,你将被要求选择一个新的, unique name.
对于这个示例项目,我们将创建两个名为“test-upload”和“test-resize”的存储桶。. “test-upload”桶将用于上传图像,并在处理和调整大小之前存储上传的图像. Once resized, 图像将保存到“test-resize”桶中, 上传的原始图片被删除了.
By default, S3权限是限制性的,不允许外部用户甚至非管理用户读取, write, update, 或删除桶上的任何权限或对象. 为了改变这一点, 我们需要以具有管理AWS存储桶权限的用户身份登录.
假设我们在AWS控制台上, 我们可以通过按名称选择桶来查看上传桶的权限, 点击屏幕右上角的“属性”按钮, 并打开折叠的“权限”部分.
以便允许匿名用户上传到此bucket, 我们需要编辑桶策略以允许允许上传的特定权限. 这是通过基于json的配置策略完成的. 这些JSON策略在整个AWS中与IAM服务一起广泛使用. 点击“编辑桶策略”按钮, 只需粘贴以下文字,点击“保存”即可上传公共图片:
{
“版本”:“2008-10-17”,
“Id”:“Policy1346097257207”,
"Statement": [
{
"Sid": "允许匿名上传到/",
“效应”:“允许”,
"Principal": {
"AWS": "*"
},
“行动”:“s3: propertynames”,
“资源”:“在攻击:aws: s3::: test-upload / *”
}
]
}
做完这些之后, 我们可以通过尝试将映像上传到桶中来验证桶策略是否正确. 下面的cURL命令可以做到这一点:
curl http://test-upload.s3.amazonaws.-F 'key=test . com.' -F 'file=@test . jpeg.jpeg'
如果返回一个200范围的响应, 我们将知道上传桶的配置已经成功应用. 我们的S3桶现在应该(大部分)配置好了. 稍后我们将在控制台上返回此服务,以便将图像上传事件连接到resize函数的调用.
Lambda角色都在权限上下文中运行,在本例中是由IAM服务定义的“角色”. 此角色定义Lambda函数在其调用期间具有的任何和所有权限. 为本示例项目的目的, 我们将创建一个将在两个Lambda函数之间使用的通用角色. However, 在生产场景中,建议在权限定义中使用更细的粒度,以确保任何安全利用都只与定义的权限上下文隔离.
IAM服务可以在“Security”中找到 & “服务”下拉列表中的“身份”子部分. IAM服务是一个非常强大的工具,用于管理跨AWS服务的访问, 如果您不熟悉类似的工具,所提供的界面一开始可能会有点令人不知所措.
一次在IAM仪表板页面, 在页面的左侧可以找到“角色”子部分. 从这里开始,我们可以使用“Create New Role”按钮来弹出一个多步骤向导来定义角色的权限. 让我们使用“lambda_role”作为泛型权限的名称. 从名称定义页面继续之后, 您将看到选择角色类型的选项. 因为我们只需要S3访问, 点击“AWS服务角色”,在选择框中选择“AWS Lambda”。. 您将看到可以附加到此角色的策略页面. 选择“AmazonS3FullAccess”策略,并继续下一步,以确认要创建的角色.
重要的是要注意所创建角色的名称和ARN (Amazon Resource name). 这将在创建新的Lambda函数时使用,以标识将用于函数调用的角色.
Note: AWS Lambda将自动记录AWS Cloudwatch中函数调用的所有输出, 日志服务. 如果需要此功能, 在生产环境中推荐使用哪种方法, 该角色的策略中必须添加Cloudwatch日志流的写权限.
现在我们准备开始编码. 现在我们假设您已经设置了“awscli”命令. 如果你没有,你可以按照说明 http://aws.amazon.com/cli/ 在您的计算机上设置awscli.
Note: 为了便于屏幕查看,这些示例中使用的代码更短. 要获得更完整的版本,请访问 http://github.com/gxx/aws-lambda-python/.
首先,让我们为项目建立一个骨架结构.
aws-lambda-python /
- image_list/
- handler.py
- list.html
- Makefile
- requirements.txt
- image_resize /
- handler.py
- resize.py
- Makefile
- requirements.txt
- .pydistutils.cfg
在我们的结构中有两个子目录,分别对应一个lambda函数. 在每一种情况下,我们都有一个公共处理程序.py、Makefile和需求.txt files. The handler.Py文件将包含在调用每个lambda函数时要调用的方法, 并且可以看作是函数的入口点. 的需求.TXT文件将包含我们的依赖项列表,因此我们可以很容易地指定和更新需求. 最后,我们将使用Makefile命令来提供与awscli交互的简单机制. 这将使创建和更新lambda函数的过程更加容易.
你会注意到 .pydistutils.CFG文件放到项目目录的根目录下. 如果您在Homebrew下使用Python,则需要此文件. 由于使用部署Lambda函数的方法(将在下一节中介绍),因此需要这个文件. 有关更多详细信息,请参阅存储库.
从resize_image函数开始, 我们将冻结魔杖依赖, 我们的图像处理库, by saving Wand==0.4.2
需求.txt. 这将是image_resize lambda函数的唯一依赖项. resize中的resize_image函数.py将需要处理来自Wand库的图像资源, 并根据指定的宽度和高度参数调整其大小. 以保留正在调整大小的图像, 我们将使用“最佳拟合”调整大小算法,该算法将保持原始图像的图像比例, 同时减少图像大小以适应指定的宽度和高度.
Def resize_image(image, resize_width, resize_height):
...
Original_ratio = image.宽度/浮动(图像.height)
Resize_ratio = resize_width / float(resize_height)
我们坚持原来的比例,不管大小比例是多少
if original_ratio > resize_ratio:
#如果宽度更大,我们将调整高度作为宽度比率的函数
Resize_height = int(round(resize_width / original_ratio))
else:
否则,我们将宽度作为高度比率的函数
Resize_width = int(round(resize_height * original_ratio))
if ((image.宽度- resize_width) +(图像.height - resize_height)) < 0:
Filter_name = 'mitchell'
else:
Filter_name = 'lanczos2'
image.调整大小(width=resize_width, height=resize_height, filter=filter_name, blur=1)
return image
把这个解决了, 需要一个处理程序函数来接受上载S3的图像生成的事件, 把它交给 resize_image
函数,并保存生成的调整大小的图像.
从__future__导入print_function
import boto3
from wand.导入图像
从resize导入resize_image
Def handle_resize(事件,上下文):
#获取事件对应的桶名和密钥
bucket_name事件[‘记录’][0]= [s3的](“桶”)(“名字”)
key_path事件[‘记录’][0]= [s3的]['对象'](“关键”)
响应= boto3.resource('s3').对象(bucket_name key_path).get() #检索S3对象
#执行大小调整操作
图像(blob =响应(的身体).读取())作为图像:
resize_data = resize_image(image, 400,400).make_blob()
#最后,将新图像上传到resize bucket
s3_connection.对象(test-resized, key_path).把(ACL =“公有可读”,身体= resized_data)
#最后移除, 因为bucket是公共的,我们不希望任何人转储我们的文件列表!
s3_object.delete()
代码完成后,需要将其作为一个新的Lambda函数上传到Amazon Lambda. 这就是添加到目录结构中的Makefile发挥作用的地方. 这个Makefile将用于部署我们正在创建的Lambda函数定义.
ROLE_ARN = arn:aws:iam::601885388019:role/lambda_role
FUNCTION_NAME = ResizeImage
区域= us-west-1
TIMEOUT = 15
Memory_size = 512
ZIPFILE_NAME = image_resize.zip
HANDLER =处理器.handle_resize
clean_pyc :
find . | grep .Pyc $ b| xargs rm
install_deps :
PIP安装-r要求.txt -t .
Build: install_deps clean_pyc
zip $(ZIPFILE_NAME) -r * . zip
create : build
aws lambda创建函数——region $(region) \
——function-name $(FUNCTION_NAME) \
——zip-file fileb://$(ZIPFILE_NAME) \
——role $(ROLE_ARN) \
——handler $(handler) \
——运行时python2.7 \
——timeout $(timeout) \
——美元内存大小(MEMORY_SIZE)
update : build
aws lambda update-function-code——region $(region) \
——function-name $(FUNCTION_NAME) \
——zip-file fileb://$(ZIPFILE_NAME) \
--publish
这个Makefile的主要功能是“创建”和“更新”。. 这些函数首先打包当前目录, 它代表运行Lambda函数所需的所有代码. 中指定的任何依赖项 requirements.txt
文件将被安装到当前要打包的子目录中. 打包步骤压缩目录的内容,以便稍后使用“awscli”命令上传. 我们的Makefile可以在其他Lambda函数定义中使用.
在实用程序Makefile中,我们定义了创建/更新映像所需的几个变量. 根据需要配置这些命令,以便Makefile命令能够为您正常工作.
ROLE_ARN
:这是Amazon资源名,它标识我们在其中运行Lambda函数的角色.FUNCTION_NAME
:我们正在创建/更新的Lambda函数的名称.REGION
:将在其中创建/更新Lambda函数的区域.TIMEOUT
:终止Lambda调用之前的超时时间(以秒为单位).MEMORY_SIZE
:调用Lambda函数时可以访问的内存大小(以兆字节为单位).ZIPFILE_NAME
:包含Lambda函数代码和依赖项的压缩包的名称.HANDLER
:处理器函数的绝对导入路径,以点表示.配置完成后,运行 make create
命令将生成类似以下输出的内容:
$ make create
PIP安装-r要求.txt -t .
...
find . | grep .pyc| xargs rm
zip image_resize.zip -r *
...
Aws lambda创建函数——region ap-northeast-1 \
——function-name ResizeImage2 \
——压缩文件fileb: / / image_resize.zip \
——role arn:aws:iam::11111111111:role/lambda_role \
——处理程序处理程序.handle_resize \
——运行时python2.7 \
--timeout 15 \
——内存大小512
{
:“CodeSha256 doB1hsujmZnxZHidnLKP3XG2ifHM3jteLEBvsK1G2nasKSo = ",
:“FunctionName ResizeImage”,
“CodeSize”:155578年,
“MemorySize”:512年,
:“FunctionArn攻击:aws:λ:us-west-1:11111111111:功能:ResizeImage”,
“版本”:“最近美元”,
“角色”:“在攻击:aws:我::11111111111:角色/ lambda_role”,
"Timeout": 15,
“LastModified”:“2016 - 01 - 10 - t11:11:11.000+0000",
:“处理程序处理程序.handle_resize”,
“运行时”:“python2.7",
“描述”:“
}
执行完create命令后, 可以使用图像的大小调整功能, 但是,为了接收事件,它还没有连接到S3桶. 我们仍然可以通过AWS控制台测试该函数,以断言该函数正确处理S3文件上传事件. AWS Lambda仪表板, 可在“服务”下拉菜单的“计算”部分找到, 选择我们创建的函数的名称. 这个Lambda函数细节页面提供了一个标签为“Actions”的下拉菜单,其中包含一个标签为“Configure Test Event”的选项。.
单击它将打开一个模态,允许您指定一个测试事件和一些示例模板. 选择“S3 Put”示例, 并将提到的桶名替换为已设置的桶名. 一旦配置好了, 后续使用Lambda函数页面上的“Test”按钮将调用Lambda函数,就像之前配置的事件真的发生了一样.
以便监视任何错误堆栈跟踪或日志消息, 您可以在Cloudwatch中查看日志流. 在创建Lambda函数的同时,将创建一个新的日志组. 这些日志流非常有用,可以通过管道连接到其他服务中.
回到S3仪表板, 展开“属性”菜单中折叠的“事件”部分,显示“事件通知”表单. 我们必须填写的字段是“Events”和“Send to”输入. 从“事件”中选择“对象创建(全部)”事件. 这将允许拦截在上传桶上创建对象的所有事件. 对于“发送到”输入,选择“Lambda函数”单选按钮. 将出现一个新的部分,其中包含我们配置为选项的“ResizeImage”lambda函数. 点击“保存”后, 任何“Object Created”事件现在都将作为“ResizeImage”Lambda函数调用的输入路由.
现在我们有了应用程序的核心功能. 让我们运行另一个cURL测试,以确保一切都按预期工作. 使用cURL将图像上传到S3桶中, 手动检查图像是否上传到resize桶中.
curl http://test-upload.s3.amazonaws.-F 'key=test . com.' -F 'file=@test . jpeg.jpeg'
执行此命令后, 根据Lambda函数是否已经“预热”,应该在50-1000ms后在“test-resize”桶中创建调整大小的图像.
ListImage Lambda函数将检索调整大小的图像列表,并在HTML页面上为用户显示它们. 这个HTML页面还为用户提供了上传自己的图像的功能. 函数中使用Jinja2从模板定义呈现HTML. 与前面一样,这些需求在 requirements.txt
file.
从__future__导入print_function
import os
import boto3
从jinja2导入环境
从jinja2导入FileSystemLoader
def _render_template (image_urls):
env = Environment(loader=FileSystemLoader(os . exe).path.abspath(os.path.目录名(__file__))))
template = env.get_template('列表.html')
Rendered_template =模板.呈现(image_urls = image_urls)
返回rendered_template
Def handle_list_image(事件,上下文):
bucket = boto3.resource('s3').桶(“test-resized”)
image_summary = sorted((image_summary for image_summary in bucket).objects.All ()), key=lambda 0: 0.last_modified)
Image_urls = []
image_summaries中的摘要:
image_urls.append(
boto3.client('s3').generate_presigned_url (
'get_object',
Params={
“斗”:总结.bucket_name,
'Key': summary.key
}
)
)
返回{'htmlContent': _render_template(image_urls)}
List Images
{image_urls中image_url的% %}
{% endfor %}
Once again, 我们可以修改前面的Makefile并使用create命令来部署lambda函数.
ImageList Lambda函数是完整的,但是它不能提供给任何用户. 这是因为Lambda函数只能在响应来自另一个服务的事件时调用, 或通过编程的方式. 这就是Amazon AWS API Gateway服务发挥作用的地方. API网关可以在“应用程序服务”子部分下找到.
API Gateway是一种将端点建模为一组资源和方法的方法, 本质上是一个REST接口. 除了提供验证和转换请求的工具之外, API网关执行其他功能,如提供节流/速率限制请求.
从API Gateway指示板中,创建一个新的API,用于服务ListImage函数. 名称和描述可以设置为您的偏好. 创建后,单击新API的名称以访问API的详细信息. 为根URL " / "创建一个新资源. 这个URL将用于提供HTML页面.
在查看根资源页面的详细信息时,添加一个GET方法. 将“积分类型”设置为“Lambda函数”, 将“Lambda区域”设置为“us-west-1”或您选择的区域, 并键入ListImage Lambda函数的名称.
在开始将响应映射到HTML输出之前, 除了将响应映射到内容类型之外,我们还需要定义一个“模型”,该模型将为来自服务器的响应定义一个模式. 选择API的“Models”部分,并单击“Create”来创建一个新模型. 给模型命名为“HTML”,内容类型为“text/ HTML”,并定义模式如下:
{
“美元模式”:“http://json-schema.org/draft-04/schema #”,
"title": HTML,
"type": "object"
}
回到API仪表板, 选择我们创建的资源并导航到“Integration Response”部分. 本节定义在接收到Lambda函数的响应之后要处理的任何转换,然后再将响应输送到最后一步.
打开“映射模板”部分,添加新的“内容类型”为“text/html”. 同时删除旧的“内容类型”. 在右侧,将下拉菜单从“Output Passthrough”改为“Mapping template”。. 这将允许我们修改API网关接受的原始JSON, 并使用返回数据的" htmlContent "属性中的HTML内容. 对于映射模板,指定“$input”.htmlContent”作为模板. Finally, 修改“Method Response”部分,从“Response Models for 200”中删除“application/json”,添加“text/html”.
返回到API的指示板, 在页面的左上角有一个标有“部署API”的按钮。. 单击此按钮以使用指定资源更新或创建API, methods, models, and mappings. 一旦完成, 将显示所选部署阶段的URL(默认为部署阶段). 最后,示例完成了! 您可以上传一些文件来测试和查看调整后的图像.
AWS是一项大型服务,短期内不会消失. 尽管供应商锁定总是需要小心的, AWS Lambda提供了一个相对精简的服务,并提供了一组丰富的辅助配置选项. 利用AWS提供的服务来实现易于扩展和维护的应用程序将提供使用AWS平台的最大好处. AWS Lambda提供了一个优雅的, 可伸缩且经济高效的解决方案,由大量消费者使用的企业级平台支持. 我相信“无服务器”应用程序是未来的发展方向. 请在下面的评论中告诉我们你的想法.
Andrew拥有用多种语言开发应用程序的经验. 他曾在领先的网络公司和初创公司工作.
15
世界级的文章,每周发一次.
世界级的文章,每周发一次.