用 Node.js 和 AWS Lambda 创建无服务器的微服务

ManLavallee 8年前
   <p>在本文中,我们将使用 Lambda—Amazon Web Services(AWS)套件中的一个新工具—来启动并运行一个微服务。 我们将使用 Lambda 创建一个 HTTP GET 终端,该终端使用 GitHub 的API 发起请求,从 GitHub 中提取存储库信息并返回一个 JSON 响应。</p>    <p>为方便你可以按本文中的步骤进行操作,你将需要一个自己的 AWS 账户。 如果没有,您可以在  https://aws.amazon.com/ 上创建免费的AWS账户。</p>    <h2>什么是 AWS Lambda?</h2>    <p>Lambda 的口号是:“运行代码不用考虑服务器”。乍一看,这可能让人觉得比较困惑。 代码究竟是在哪里或如何运行的呢?</p>    <h3>无服务器与函数即服务</h3>    <p>“无服务器”是一个新的软件基础设施术语,你可能有所耳闻。 它用于描述按需执行代码的解决方案。“无服务器”这个术语可能会误导大家,因为事实上,在程序中仍然有服务器。 更好的描述符是 FaaS 或“函数即服务”。</p>    <p>这两个定义用于描述新的开发和部署经验。 这种新体验被认为是“无服务器”的,因为作为开发人员,不再需要管理,监视或扩展正在运行代码的任何服务器。 您只需将代码上传到 FaaS 提供服务的程序(在本例中为 AWS Lambda),FaaS 提供程序将在后台帮你执行代码并管理所有的基础架构。</p>    <h3>无服务器架构的利弊</h3>    <p>鉴于这一“无服务器”架构扩展的定义,让我们来看看用 Lambda 工作的一些利弊。</p>    <h3><strong>优点</strong></h3>    <ul>     <li> <p><strong>按需使用定价</strong></p> </li>    </ul>    <p>传统的服务器托管使用重复计费周期。 你的服务器总是启动和运行使用资源和等待输入。  为了保持服务器正常运行,你按月或按年作为账单周期来支付费用。用按需定价,Lambda 按每个函数的使用进行计费。这意味着你的项目利用 Lambda 的功能是不频繁的,相比传统的托管解决方案,你可以节省大量的金钱。</p>    <p>Lambda 定价如下:</p>    <ul>     <li>每 100 万个请求 0.20 美元</li>     <li> <p>每 GB 秒的计算时间 0.00001667 美元,每次执行时间接近 100ms</p> </li>     <li> <p><strong>内置的自动伸缩功能 </strong></p> <p>在一个传统的被托管的基础设施中, 你会进入到这样一个时期,你可能需要担心性能和扩展性。随着应用程序使用量和通信量的增加,您可能需要添加更多的托管服务器基础设施来跟上需求。对于你的用户,这可能导致失败,成为瓶颈。 当需要增加或减少额外的开销时,Lambda 会自动完成扩展性。</p> </li>    </ul>    <h3><strong>缺点</strong></h3>    <ul>     <li> <p>与本地开发工作流程不一致。</p> <p>你可以在本地写 Lambda 功能代码,隔离测试,但是在没有创建你的拼装版的 Lambda,你不能在本地模拟生产环境。</p> </li>    </ul>    <h2><strong>Lambda 关键概念</strong></h2>    <h3><strong>函数代码和触发器</strong></h3>    <p>Lambda 有两个主要概念:代码和触发器。 代码是不言自明的。 在我们的示例中,就是那些由您编写并上传到 Lambda 以产生你所需行为的 JavaScript 代码。</p>    <p>一旦你上传后,代码不会自行执行。 Lambda 有一个称为“触发器”的附加概念。触发器是由其他 AWS 服务触发的事件,它们将数据传递到 Lambda 函数以供执行。</p>    <p>一些示例触发器:</p>    <ul>     <li> <p>用指向 AWS API 网关的 HTTP 请求来触发 Lambda 代码。</p> </li>     <li> <p>用轮循的事件触发,例如来源于 CloudWatch 事件的 corn 任务。</p> </li>     <li> <p>用 DynamoDB 表的更新来触发 Lambda 代码。</p> </li>    </ul>    <h3><strong>Lambda 代码函数签名</strong></h3>    <p>你可以从与预期的 Lambda 签名相匹配的 JavaScript 中,通过导出一个常规函数来定义一个 Lambda 函数。</p>    <pre>  <code class="language-javascript">exports.myLambdaFunction = (event, context, callback) => {     // Use callback() and return   }</code></pre>    <p>该函数接收3个参数:</p>    <ol>     <li>event— Lambda 传递给函数的'触发数据'的键值对字典。</li>     <li>context— AWS 内部信息,例如 AWS 请求ID, Lambda 超时时间, 和日志信息.</li>     <li>callback— 一个标准的 JavaScript 异步回调句柄。</li>    </ol>    <h2>建设Lambda函数</h2>    <p>开始创建新的 Lambda 函数。访问 Lambda 仪表盘:</p>    <p>然后会看到像这样的界面:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/5f25ced7f915b7e6995e8d710abe2f23.png"></p>    <p>点击蓝色的按钮 <em>创建 Lambda 函数(Create a Lambda function)</em> 来开始。</p>    <h3>选择设计图</h3>    <p>下一个界面会提示你 <em>选择一个设计图(Select a blueprint)</em> 并提供一个可过滤的设计图列表。点击 <em>空函数(Blank Function)</em> 选项,它应该是设计图列表中的第一个选项。这个页面可用于将来参考着使用其它工具。</p>    <h3><strong>配置触发器</strong></h3>    <p>接下来的界面是 <em>配置触发器(Configure Triggers)</em> ,显示成这样:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d8c5f4ba375f04d337f8c69722f0d63d.png"></p>    <p>点击 <em>下一步(Next)</em> 离开这个界面。我们会在后面,建立起我们的函数之后,再来定义触发器。</p>    <h3><strong>配置函数</strong></h3>    <p><img src="https://simg.open-open.com/show/a7f54d5f8c155b0abdfada7765014f67.png"></p>    <p>在这一部分,需要给 Lambda 函数一个名称。我会使用 GithubGet 这个名称。你也可以填写对这个函数的描述,这是可选的。</p>    <h3><strong>指定 Lambda 函数代码</strong></h3>    <p>默认情况下,Lambda UI 设置为内联编辑代码。 您应该看到一个内联编辑器,编辑器中有一个样本函数,如下所示:</p>    <p><img src="https://simg.open-open.com/show/97eed1bb49bc75f0e122b4652f586838.png"></p>    <p>内联编辑器需要很少的开销来获取和运行 lambda 代码,但对于本教程,我们将做一些更进阶的事情。</p>    <h3><strong>创建复杂的函数依赖关系</strong></h3>    <p>在大多数真实环境中,你如果要创建更多的复杂函数,常常需要通过npm来安装第三方的库。</p>    <p>让我们创建一个使用 npm 依赖项的自定义函数,并将其上传到Lambda。 您可以按照下面的步骤,或者可随意使用 <a href="/misc/goto?guid=4959729161329409706" rel="nofollow,noindex">示例代码库</a> 中的代码</p>    <p>创建一个新的函数</p>    <p>让我们为我们的新函数设置一个文件夹,并在文件夹中用默认的 package.json 文件初始化 npm:</p>    <pre>  <code class="language-javascript">npm init -f</code></pre>    <p>接下来,我们将安装 GitHub 客户端:</p>    <pre>  <code class="language-javascript">npm install github</code></pre>    <p>创建 index.js 文件,使用以下代码:</p>    <pre>  <code class="language-javascript">var GitHubApi = require('github');  var github = new GitHubApi();    exports.handler = (event, context, callback) => {      github.search.repos({      q: 'sitepoint',      sort: 'stars'    }, function(err, res){      if(err){        callback(err);      }        var results = res.items.map((repo) => {        return {          url: repo.html_url,          stars: repo.stargazers_count        };      });        callback(null, {        statusCode: 200,        headers: { "Content-Type": "application/json" },        body: JSON.stringify(results)      });    });    };</code></pre>    <p>下面是这段代码的功能细节:</p>    <ol>     <li> <p>引入并初始化了 GitHubAPI</p> </li>     <li> <p>定义了一个与 Lambda 签名匹配的 handler 函数。</p> </li>     <li> <p>当 handler 函数被调用时,它会发出一个查询请求到 GitHub,对所有符合‘sitepoint’的数据仓库进行查询。</p> </li>     <li> <p>在 Github 的应答格式中,创建了一个 map 数据结构,其中包含了每个数据仓库的网址和星标数。</p> </li>     <li> <p>最后,用 http 响应(如对象)调用 Lambda 的回调函数,好与 API 网关预计集成 相匹配。</p> </li>    </ol>    <p>将代码上传到AWS Lambda</p>    <p>任意使用一个你熟悉的 zip 工具,创建一个包含函数文件的 zip 压缩包。我在 OS X 系统上使用 zip 命令行是这样的:</p>    <pre>  <code class="language-javascript">zip -r lambdaupload.zip ./index.js ./node_modules/</code></pre>    <p>要将代码上传到 Lambda, 在内联编辑器的 “代码条目类型” 选项上选择 “ <em>上传 zip 文件</em> ”,然后使用弹出的窗口上传您的代码。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ef74361e30f8d50802dff4ffdeb4424a.gif"></p>    <p><strong>配置函数的 handler 和任务</strong></p>    <p>在这一部分下,我们要设置几个值。 Handler 是在上传的 JavaScript 文件中 Lambda 函数的引用。 默认情况下,它设置为 index.handler。hander 函数通过查找 index 文件与我们上传的文件相映射,并为我们 hander 函数检索出与我们 export.handler 文件相对应的的导出语句。</p>    <p>使用以下信息填写“Role”字段,为创建 Lambda 函数的基本角色:</p>    <ul>     <li> <p>Role: 从模板中创建新的 role</p> </li>     <li> <p>Role name: Lambda 获取 Role</p> </li>     <li> <p>Policy templates: 简单的微服务权限</p> </li>    </ul>    <p>这部分完成后, 单击“Next” 按钮继续。比可以看到  ‘Review’ 界面的概述配置:</p>    <p><img src="https://simg.open-open.com/show/9715d80cf2c654e18b3bd64a99fbcd74.png"></p>    <p>如果一切正常,单击“创建函数”按钮继续。</p>    <h3>为新函数分配触发器</h3>    <p>既然我们的函数已经创建并初始化了,我们需要一种方法来调用它。现在是时候为该函数分配触发器了。对于触发器的实现,我们将使用 API 网关。</p>    <p>API 网关是另一种 AWS 服务,它能自动创建可配置响应源的 HTTP 终端。我们将把我们的 Lambda 函数作为 API 网关的响应。</p>    <ol>     <li> <p>点击‘Triggers’(触发器)</p> </li>     <li> <p>点击‘Add trigger’</p> </li>     <li> <p>点击lambda旁边的空白区域</p> </li>     <li> <p>选择“API Gateway”</p> </li>     <li> <p>在安全性中选择“Open”</p> </li>     <li> <p>点击提交</p> </li>    </ol>    <p>这些步骤可以在下面动画中看到:</p>    <p><img src="https://simg.open-open.com/show/b37b3f0651cd5c1ffbedd82e1766359a.gif"></p>    <p>当成功添加触发器之后,你应该可以在 Triggers 标签中看到触发器和你的函数被关联起来。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/510e92f6e1f0d6a80d402265e6e0e255.png"></p>    <p>在 API 网关 ID 中会列出一个 URL。你可以访问在浏览器中这个 URL,然后你赢高可以看到一个 JSON 响应,类似下面的日志:</p>    <pre>  <code class="language-javascript">[{"url":"https://github.com/bodrovis/Sitepoint-source","stars":106},  {"url":"https://github.com/Azzurrio/moviestore","stars":80},  {"url":"https://github.com/bodrovis/SitepointMiniChat","stars":54},  {"url":"https://github.com/upchuk/d8-demo-modules","stars":34},</code></pre>    <p>恭喜!你已经成功在 Lambda 上部署和触发代码了。</p>    <h2>后续及 Lambda 的未来</h2>    <p>希望这个项目能给你在 AWS Lambda 上工作建立一个好的基础。虽然我们在函数代码中整合了第三方客户端(GitHub),但它可以替换为其它客户端 API 或者数据库的客户端链接。</p>    <h3>无服务器框架</h3>    <p>这篇文章中演示了建立起 Lambda 的这个过程。这过程看起来手工操作比较多,时间也不长。还有另外一个配置和初始化 Lambda 的方法,就是通过 AWS API 来完成。</p>    <p>现在,已经有一些框架基于 AWS API 建立起来了,它们有助于简化这个过程。</p>    <ul>     <li> <p>https://serverless.com/</p> <p>Serverless 框架是当前最强大的无服务器框架。在写文本的时候,这个框架进行了重大版本更新,从0.5 更新到官方 1.0 发行版。很不幸,这个更新并不向后兼容。大多数流行的插件当前还是 0.5,未能更新到 1.0,还需要等等待一段时间才能使用。</p> <p>Serverless 以及它的插件,承诺提供一个非常全面的 Lambda 体验。它的本地开发环境提供了快速迭代、自动化的 Lambda 代码开发、多个开发平台环境,以及其它功能。</p> </li>     <li> <p>https://open-lambda.org/</p> <p>OpenLambda 试图通过提供一个本地开发体验来模拟 Lambda 环境。它提供工具来让部署 Lambda 代码变得容易,也提供快速迭代。这弥补了上面列出的 Lambda 的一些缺点。</p> </li>    </ul>    <p> </p>    <p>来自:https://www.oschina.net/translate/getting-started-node-js-aws-lambda</p>    <p> </p>