65.9K
CodeProject 正在变化。 阅读更多。
Home

Google Cloud Platform 的 Serverless 实验:第一部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2017 年 6 月 18 日

CPOL

7分钟阅读

viewsIcon

13246

桌面程序员编写的简单云函数

引言

我开始试验 Cloud Functions,并成功地解决了我认为初学者可能会感兴趣的几个问题。请相信我,我不是专家。

背景

当我决定编写第一个小型自定义服务时,标准很明确:简单且我能不时通过连接互联网的设备访问的功能。
事实上,在许多通用场景中,存在类似 IFTTT 的在线解决方案,您无需编写自己的代码,只需进行组合和配置。但有时,别无选择,只能编写一些特定的内容。

1. 选择平台

我研究了现有的各种云平台(如 Amazon Web Service、Microsoft Azure、IBM Cloud、Heroku 等;还有很多,即使您只需要文件存储功能),并选择了 Google Cloud Platform (GCP)。以下是我考虑的因素。

1.1. 定价

GCP 允许在不租用对我而言不必要且昂贵的、全天候运行的服务器的情况下运行功能,您只需为真正需要的资源(如计算时间或磁盘空间)付费。一些云服务是完全免费的,一些则仅在使用量大大增加时才收费,而我永远不会达到该使用量(实际上,“除了 200 万次调用外,免费套餐还提供 400,000 GB-秒,200,000 GHz-秒的计算时间https://cloud.google.com/functions/pricing)。

在一年的试用期内,我获得了 300 美元的预算,可以自由使用(有一些限制,例如不能用于挖比特币 https://cloud.google.com/terms/free-trial/),但在我的特定情况下,我没有花费其中的任何一部分。然而,试用期结束后,GCP 的计费逻辑可能会发生变化,请参阅 2.2 中的存储桶注意事项。

查看其他云提供商的价格列表,根据您的需求选择平台。
命名可能不同,Google 或 Microsoft 称之为 Functions,而 Amazon 称之为 Lambda。
如果您需要持久化存储,一个提供商通常会提供更多解决方案,从文件到数据库,价格也各不相同。 
尝试切换到另一个提供商,您可能会错过一些您习惯的功能。

1.2. Web 访问

GCP 允许访问其他网站。在云平台上,此类功能可能仅限于付费版本(顺便说一句,“即使是免费套餐的使用,我们也需要一个有效的账单账号https://cloud.google.com/functions/pricing),但在我的 GCP 试用期间,我从未遇到过收费(实际上,“每月 5GB 的互联网传出流量”以下均免费 https://cloud.google.com/terms/free-trial/)。

1.3. 编程

各种平台通常支持多种语言。
在 Google 的产品中,我选择了 Node.JS (JavaScript),因为它很简单。对于简单的场景(我将展示一些示例),GCP 提供了一个名为 Inline editor 的在线 Node.JS 编辑器。
GCP 初学者不需要从命令行开始,云配置可以在 GUI(所谓的控制台)中完成。

2. Hello World 示例

像我这样的 Node.JS 新手打开 Google 的教程 https://cloud.google.com/functions/docs/quickstart,当进行到第四步时,会觉得开始变得太复杂了。
幸运的是,有更简单的方法。

2.1. 环境

首先注册一个免费试用 https://cloud.google.com/free/。注册时,会要求您提供银行卡和手机号码。
之后,只需进入您的 GCP 控制台 http://console.cloud.google.com/
在那里,创建一个新的 **项目**。它会获得一个内部 ID,类似于 ProjectName-123456,最终必须连接到连接到您银行卡的 **账单**。**账单** 和此处提到的其他控制台部分通常在控制台的 **主页** 左侧可见。

进入您的项目后,在 **Cloud Functions**(不一定是 **API Manager**)中,启用项目上的 **Cloud Functions API**。
每一步都需要几秒钟。

2.2. Hello World 函数

转到 **Cloud Functions**(如果您还没有),然后选择 **Create Function**。
您必须选择一个项目唯一的函数名称。名称以后无法更改,如果不喜欢,必须复制然后删除原始函数。您将在项目函数列表中看到该名称。

选择 **HTTP Trigger** 选项。这意味着您可以通过类似 https://us-central1-ProjectName-123456.cloudfunctions.net/FunctionName 的方式调用函数(您的确切 URL 显示在 **Trigger** 下方),在 Web 浏览器或其他软件中访问它。
这意味着任何人都可以通过做同样的事情来调用它。在最初的两次调用后,它会影响您的 **账单**。这就是为什么在 **账单**(**Cloud Functions** 之外)中,您可以设置 **Budget & Alerts**。在某些服务中,可以设置自己的额外限制。

对于简单的功能,您可以选择最少可能的 **Memory allocated**。设置较短的 **Timeout**,这样如果您的函数卡住,您就不必等待一分钟。

然后,有必要处理 **Source code**。设置 **Inline editor** 选项,选择 index.js(两者都应预先选中),您就能看到这段预定义的默认脚本。

/**
 * Responds to any HTTP request that can provide a "message" field in the body.
 *
 * @param {!Object} req Cloud Function request context.
 * @param {!Object} res Cloud Function response context.
 */
exports.helloWorld = function helloWorld(req, res) {
  // Example input: {"message": "Hello!"}
  if (req.body.message === undefined) {
    // This is an error case, as "message" is required.
    res.status(400).send('No message defined!');
  } else {
    // Everything is okay.
    console.log(req.body.message);
    res.status(200).send('Success: ' + req.body.message);
  }
};

如果您不知道为什么是 200 或 400,请在此处查看 HTTP 状态码列表 here

如您所见,脚本包含一个导出的函数 helloWorld。您还必须在 **Function to execute** 项目中具有相同的 helloWorld

选择 **Stage bucket**,这是您的代码将存储的地方。
您可以在 **Storage** 中预先 **Create Bucket**。但在 **Create Function** 中,您也可以在 **Browse** 中创建 **New bucket**。
下次,只需 **Browse** 并 **Select** 现有项。

根据 Cloud Storage Always Free Usage Limits https://cloud.google.com/storage/pricing#cloud-storage-always-free,每月 5 GB-月的 **Regional storage** 应该免费。“Cloud Storage Always Free quotas apply to usage in us-west1, us-central1, and us-east1 regions. If you go over these usage limits and are no longer in the free trial period, you are charged according to the price sheet below.

您可以稍后将函数移至不同的存储桶。**Edit**(编辑)现有函数,然后 **Select**(选择)新存储桶。但请记住,您函数的前一个版本(版本)仍保留在原始存储桶中。实际上,每次 **Edit** 都会创建一个新的存储桶文件,保留旧的、已不再使用的文件。您可以小心地在 **Cloud Functions** 之外,在 **Storage**, **Browse** 下删除未使用的文件。

最后,按下 **Create**(创建)按钮,一段时间后函数就会出现在列表中。绿色图标表示成功,红色图标表示失败,例如因为脚本与 **Function to execute** 不匹配。

成功后,在新的浏览器标签页中打开您的 https://us-central1-ProjectName-123456.cloudfunctions.net/FunctionName (您的实际触发链接会不同),您应该会看到预期的输出。

No message defined!

从控制台的函数列表中打开您的函数,然后进入其 **Testing**(测试)选项卡。在 **Triggering event**(触发事件)中,粘贴我从脚本注释中复制的 JSON 格式(JavaScript Object Notation)的输入

{"message": "Hello!"}

或者相同的格式,但分成了更多行

{
  "message": "Hello!"
}

然后按下 **Test the function**(测试函数)按钮。**Output**(输出)应该是

Success: Hello!

现在尝试使用 https://us-central1-Projectname-123456.cloudfunctions.net/FunctionName?message=Hello! ,尽管有 ?message=Hello!,结果仍然是...

No message defined!

...一样。这是因为脚本评估了 req.body.message,它对应于 http 方法 post。当您想响应 URL 中指定的参数(http 方法 get)时,您会在 req.query.message 中找到它。

3. HTML 表单

创建第二个函数。步骤与创建第一个函数类似,只不过现在您可以从现有函数的上下文菜单(函数列表中的三个垂直点)开始,然后选择 **Copy**(复制)来创建它。
选择一个新的项目唯一函数名称,然后输入如下所示的 **Source code**。

/**
 * callHelloWorld
 *
 * @param {!Object} req Cloud Function request context.
 * @param {!Object} res Cloud Function response context.
 */
exports.callHelloWorld = function callHelloWorld(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'}); 
    res.write(
      '<html><form action="helloWorld" method="post">' +
      'Message: <input type="text" name="message"><br>' +
      '<input type="submit" value="Send"></form></html>');
  res.end();
};

在浏览器中打开函数的触发器,会显示一个带有表单的 HTML 页面,允许您键入消息内容,在按下 **Send**(发送)按钮后,它将使用 **method post** 调用 **helloWorld** 函数。
此版本要求两个函数都在同一个存储桶和存储桶文件夹中,否则您必须修改 **action** 以指向被调用的函数(**action** 也可以是完整的 URL)。

4. HTTP 请求

这是第三个函数示例

/**
 * requestFunction
 *
 * @param {!Object} req Cloud Function request context.
 * @param {!Object} res Cloud Function response context.
 */
exports.requestFunction = function requestFunction(req, res) {  
//based on https://www.sitepoint.com/making-http-requests-in-node-js/
var request = require("request");
request({
  uri: "http://www.random.org/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new",
  headers: {'User-Agent': 'request'},
  method: "GET",
  }, function(error, response, body) {
            res.status(200).send('Success: ' + error + "&" + response.statusCode + "&" + body + ' end');
  });  
};

脚本使用 request 对象访问 random.org 的 URL(好的,URI),并在回调函数中显示返回的文本正文。修改此示例以调用您的云函数很容易。
有关 request 的更多信息,请参见 here

要使用 request 对象,必须将 request 模块包含到我们的函数的包中。在编辑器中,从 index.js 切换到 package.json,然后在此处添加依赖项。

{
  "name": "sample-http",
  "version": "0.0.1",
  "dependencies": {
    "request": "^2.81.0"
  }
}

问题是我如何获得 request 模块的版本。

5. Google Cloud Shell

在控制台的顶部工具栏中按下 **Activate Google Cloud Shell**(激活 Google Cloud Shell)按钮。之后,当它显示 Linux 命令 shell 时(为了让我相信您,请尝试 cat /etc/*{release,version} 命令)。
运行 dirls。输出应为

node_modules  README-cloudshell.txt

现在调用 cd node_modules,然后在其中执行 ls。您会看到一个预装模块列表,其中也包含 requestcd requestls,然后 cat package.json - 实际的 request 版本就在那里。
随心所欲地使用 cd .. 或简单地使用 exit

以上是我关于 Cloud Shell 的全部内容。


第一部分结束。在第二部分中,我计划发送和接收电子邮件。

© . All rights reserved.