转载 codeceo 原文连接:http://www.codeceo.com/article/3-reason-aws-lambda-not-ready.html

本文由码农网 – Wendy原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划

Chad Lung 最近写了个关于如何使用AWS Lambda编写一个Python微服务的教程,通过HTTP可以访问。写的不错,很有说服力,而且很好的诠释了Lambda有多酷。

或许你并不熟悉Lambda,它是AWS的一个新特性,利用这个特性你可以快速地编写出一个service,而让Amazon去关心那些样板垃圾,通常随着你的service启动一起运行,以一种人们可以实际谈论的方式。你不用配置子网、实例或者加载Lambda的平衡器:你只是写一些代码,然后告诉Amazon连上你的服务。这是一个无法让人抗拒的承诺。

我们在Datawire的时候,试图真地使用Lambda写一个真实世界存在的、基于HTTP的微服务,那时Lung的教程还没有写出来,不过,随后我们发现一些不那么酷的事情,Lambda还没有准备好迎接我们所真实存在的这个世界:

  • Lambda是一个构件,而不是一个工具
  • Lambda缺乏文档支持
  • Lambda在错误处理上很糟糕

Lung跳过了这些不酷的事情,不过这也可以理解,因为他们想要使入门教程简化易读,但是如果你想要在真实世界中使用它工作的话就不得不考虑这些问题。(注意,如果你使用Lambda做AWS领域的事件处理,你的生活会更加轻松。但是在微服务领域真正有趣的案例是Lambda和HTTP。)

Lambda只是一个构件

不止是Lambda,AWS的模块都只是提供一些构件,期望别人开发一些真正的工具来使用它们。如果你想直接跟AWS对话,是很荒谬的。

换句话说,Lung写的教程向我们展示了,手动建立一个Python Lambda需要20步-那实际上是一个只有一个endpoint的服务,并且使用GET方法,附带一个查询参数。请注意,这其中有大约一半的步骤(8-10,所依赖的)是你每次创建endpoint时必须重复做的。如果你只有5个这样的服务,那么你不是只需要注意20步,而是50-60步。想象100个服务。想象一下如果你的endpoint进行了版本管理,这样的重复操作你需要做多少次。你每一次升级版本,针对每一个endpoint手动配置8-10步,听起来很爽吧?

问题的根本是我们想要一个工具(我们的微服务),但是AWS却给我们一些构件,让我们负责连接它们。所说的那些构件就是Lambda和API Gateway,Lung不是通过创建一个Lambda来开始教程,而是搞了一通API Gateway – gateway有点烦人。前边我提到的那8-10步都是关于API Gateway的,更糟糕的是,那是用于仅支持HTTP的最低限度了。如果你想要支持HTTPS(你也应该这么做,现在是2016了),你还需要把它加进去。

当然了,这些事情迫切需要自动化,许多人正在努力填充这个空白。我们已经使用了Terraform来装备EC2,我想很快他们就会搞定Lambda和API Gateway。Serverless声称Python将在最近的0.2.0版本中提供支持;两天前发布了Zappa的初始版本。看起来像这种尝试还有很多,尽管如此,还是没有一个工具值得我信任,所以我们还是手动配置吧。(当然,如果你是开发这些工具的其中一员,欢迎你来告诉我,我是错的!)

Lambda缺乏文档支持

“但是,等一下,”我听到你(所有的AWS同胞们)在喊,“你撒谎!网上有各种各样的关于Lambda和AWS的文章!”

好的,让我们想象一下,你是一名Alice’s House of Grues的开发人员。你的任务是创建Grue的定位服务,这个服务接收一个grue的名称,返回它的位置。让我们再想象一下你是一个很优秀的开发人员:

  • 你是团队中的一员,所以在你写代码前,你和团队其它人员要在API上达成一致。
  • 你做事很谨慎,所以你在发布代码前会先进行本地测试(你会自动化这些测试)。
  • 你是一名Python开发人员,所以你要使用Python来编写。

我们会使API简单些:GET /v1/grue/grue_name 会返回你一些JSON数据。完成。(我们会假设服务魔法般地知道grue在哪。)

得到API,你就可以坐下来编写你的测试了,然后在本地编写代码。首要问题:你的代码如何得到grue_name,输出又如何返回呢?记住:你要编写代码,就需要考虑那些通常的操作、异常条件、错误情况、边界值情况,这些都会是那些错误的(或者恶意的)客户端请求所带来的。

继续。去Google一下。我等着。

回来了?好的。你发现了很多结果,对吗?然后你不得不花一些时间来区分是关于AWS Lambda的信息还是关于Python的编程术语lambda。即使是那样,还是有很多没有价值的信息,但是你,我确定,你最终会点开AWS Programming Model for Authoring Lambda Functions in Python这一页,这一页列举了一些主题和示例,你可以从中推断出许多事情,但请不要真的准备整理出一个规范出来。

这儿有段话,是关于Python编程模块是怎样描述输入输出的:

“event - AWS Lambda 使用这个参数将事件相关数据传递给处理函数。这个参数的数据类型通常是Python的dict类型。它也可以是list,str,int,float或者NoneType类型。”

这句话的言外之意是,它期望数据通过JSON格式来传输,然后将它反序列化为event参数。这一点在lung的关于讲映射模板的时候已经提到了,那里明确地讲到了设置输入类型为application/json,然后手动在URL查询字符串中构建一个JSON数据字典,但是那仍然是一个暗示,而不是一个明确地规范。

或许没必要这么迂腐深究。或许处理函数要接收一个JSON字典,然后反序列化,这看起来是件很明显的事情,所以你不需要非得讲出来。但是不,这真的很重要:如果你不讲出来什么是有效的输入,他们不会告诉你,当(不是如果)用户提交了些所不期待的东西你应该怎么处理。

假设用户提交了一个简单的字符串而不是一个JSON编码的字典数据?假设他们提交了一个JSON编码的数组呢?假设他们提交了一段乱码?我们会在错误处理那段进一步讨论下这点,但是问题是你真的不知道怎么办。而你只能靠猜测,测试。

同样地,这还有一段是讲输出的:

“处理函数可以随意地返回一个值。返回什么样的值,取决于你调用Lambda函数时使用的调用类型:

  • 如果你使用RequestResponse调用类型(同步执行),AWS Lambda返回Python函数通知客户端调用Lambda函数的结果(对于请求的HTTP相应内容会被序列化为JSON格式)。例如,AWS Lambda的console使用RequestResponse调用类型,所以如果你使用console测试函数调用,那么console会显示出返回值。
  • 如果处理函数没有返回任何东西,AWS Lambda会返回null。
  • 如果你使用Event调用类型(异步执行),返回值就丢弃了,不使用了。

那么,这样,更好。的确有说要将输出数据序列化为JSON格式。很好。主要问题是,我们必须去别的地方找找看如何来控制调用类型(教程里没有提到);至少很明确地提到了测试console使用RequestResponse版本。但是,再想想看,假设你返回了变形的数据呢?如果你返回一个自定义的对象,如何序列化?如果你返回None该怎么弄?如果处理发生异常呢?还有,如果没有一个明确的定义,这将成为一个边猜边测试的游戏。

注意AWS Lambda在这方面并不孤单:在行业中,贫瘠的文档是十分特有的,我会在之后详细地讨论这个问题。但是如果想要开始尝试使用Lambda,这真的是个问题。

Lambda处理错误很糟糕

我们前面提到过一些特例,但是让我们更进一步,因为这是个硬伤。如何,真的吗,你想要使用Lambda管理错误?

请牢牢记住,Lambda中有许多潜在的错误点。用户可以使用坏数据调用你,你自己写的服务可能会挂掉,网络会挂掉,要列举还有很多。。。还有,Amazon从来不会写文档来说明你应该如何处理这些事情。

继续列举一些简单的案例:

用户会提交无法解析的输入。

这真地很简单:如果AWS无法理解输入数据,就不会运行你的Lambda。当然,AWS提供的日志(AWS CloudTrail)常常不会真地捕捉发生这件事情的任何信息,所以要确定到底发生了什么真地很难。

用户提交的输入是可以解析的但是非法。

有时候,用户提交的数据是有效的JSON格式的,但是却没有任何意义、讲不通(或许有个你需要的字典元素他们没有提供)。Lambda似乎没有特定的方式来处理此类问题,所以我们必须把它当做一个运行时错误,那将会导致。。。

运行时出错。

也许另一个我们所需要的服务挂掉了。也许用户的某次请求太荒谬。我们想要记录这些用来调试,给调用者返回一个错误以便让它们知道挂掉了。记录这些应该很简单:每一个AWS请求,每一次我们从Python中所写的日志都应该出现在CloudTrail中,就像应该写入标准输出一样。很不幸,我做实验的时候经常会丢失这些输出,好复杂的生活。

更糟糕的是,似乎没有方法可以让用Python写的Lambda返回除了HTTP状态码200(成功)以外的东西。如果你在用JavaScript编写呢,或许。。。那么为什么不是Python?(关于这一点,我希望我是错的。)

这种组合意味着,最有效的工具总要返回一个包含一个元素的字典,不管那说明请求成功还是失败,因为你也不需要处理HTTP状态码。

最后:

运行时产生异常。

AWS声明,如果Lambda产生异常,就会返回一个特定的JSON字典。这看起来很有效,而我也那么做了,当然那就意味着你会得到一些完全不同于在正常案例中会看到的输出。结合控制HTTP状态码的缺失,使用try/catch块把整个服务包裹起来,这对于控制所有的异常来说似乎很重要。

工具。不仅仅是构件

当我第一次坐下来开始使用Lambda编写我的微服务时,我真的认为这会是有史以来最伟大的事情。有太多的承诺,而我只是喜欢某个想法,那就是我可以一气呵成写完50行Python代码,然后让Amazon来操心部署的事情。

不幸的是,我很确定。我们最近使用Terraform和EC2实例迁移了所写的微服务,因为Lambda还没有准备好应对真实的世界。也许事情很快会变得不同。也许Zappa,Serverless,还有Terraform会统领世界。谁知道呢,也许Amazon会决定首先将Lambda脱离构件的舞台。我会密切关注的:同时,如果你认为我遗漏了什么,务必要联系我@_flynn