我想让你花点时间想想 Twitter,并从规模的角度思考它。Twitter 有 3.26 亿用户。总的来说,我们每秒创建 ~6,000 条推文。每分钟,就会创建 360,000 条推文。这总计每年近 2000 亿条推文。现在,如果 Twitter 的创建者被如何扩展的问题所困扰,并且他们甚至没有开始呢?
这就像我曾经有过的每一个创业想法,这就是为什么我如此热爱无服务器的原因:它处理了扩展问题,让我去构建下一个 Twitter!

如上所示,随着更多用户请求的到来,我们在几秒钟内将规模从一台服务器扩展到七台服务器。您也可以轻松地扩展它。
因此,让我们构建一个 API,它将随着越来越多的用户涌入和工作负载的增加而即时扩展。我们将通过回答以下问题来做到这一点
如何创建一个新的无服务器项目?
对于每项新技术,我们都需要弄清楚有哪些工具可供我们使用,以及如何将它们集成到我们现有的工具集中。在开始使用无服务器时,我们有一些选项需要考虑。
首先,我们可以使用我们常用的浏览器来创建、编写和测试函数。它功能强大,并且使我们能够在任何地方进行编码;我们只需要一台电脑和一个正在运行的浏览器。浏览器是编写我们第一个无服务器函数的良好起点。

接下来,随着您对新概念越来越熟悉并变得更有成效,您可能希望使用本地环境来继续开发。通常,您需要一些功能的支持
- 在您选择的编辑器中编写代码
- 可以为您完成繁重工作并生成样板代码的工具
- 在本地运行和调试代码
- 支持快速部署您的代码
微软是我的雇主,我主要使用 Azure Functions 构建无服务器应用程序,因此在本文的其余部分,我将继续使用它们作为示例。使用 Azure Functions,在使用 Azure Functions Core Tools 时,您将获得所有这些功能的支持,您可以 从 npm 安装。
npm install -g azure-functions-core-tools
接下来,我们可以使用交互式 CLI 初始化一个新项目并创建新的函数

如果您的首选编辑器恰好是 VS Code,那么您也可以使用它来编写无服务器代码。实际上有一个 很棒的扩展 用于它。
安装后,左侧边栏中会添加一个新图标,在这里我们可以访问所有与 Azure 相关的扩展!所有相关的函数都可以分组到同一个项目中(也称为函数应用)。这就像一个文件夹,用于对应该一起扩展的函数进行分组,以及我们希望同时管理和监控的函数。要使用 VS Code 初始化一个新项目,请点击 Azure 图标,然后点击**文件夹**图标。

这将生成一些帮助我们进行全局设置的文件。让我们现在回顾一下。
host.json
我们可以直接在 host.json
文件中为项目中的所有函数配置全局选项。
在其中,我们的函数应用配置为使用最新版本的无服务器运行时(目前为 2.0)。我们还通过将functionTimeout
属性设置为00:10:00
来配置函数在十分钟后超时,该属性的默认值为五分钟(00:05:00
)。
在某些情况下,我们可能希望控制 URL 的路由前缀,甚至调整设置,例如并发请求的数量。Azure Functions 甚至允许我们自定义其他功能,例如日志记录、healthMonitor
和不同类型的扩展。
以下是我如何配置该文件的示例
// host.json
{
"version": "2.0",
"functionTimeout": "00:10:00",
"extensions": {
"http": {
"routePrefix": "tacos",
"maxOutstandingRequests": 200,
"maxConcurrentRequests": 100,
"dynamicThrottlesEnabled": true
}
}
}
应用程序设置
应用程序设置 是用于管理运行时、语言和版本、连接字符串、读写访问权限和 ZIP 部署等的全局设置。有些是平台所需的设置,例如FUNCTIONS_WORKER_RUNTIME
,但我们也可以定义将在应用程序代码中使用的自定义设置,例如DB_CONN
,我们可以使用它来连接到数据库实例。
在本地开发时,我们在名为local.settings.json
的文件中定义这些设置,并像访问任何其他环境变量一样访问它们。
同样,以下是一个连接这些点的示例代码片段
// local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "your_key_here",
"FUNCTIONS_WORKER_RUNTIME": "node",
"WEBSITE_NODE_DEFAULT_VERSION": "8.11.1",
"FUNCTIONS_EXTENSION_VERSION": "~2",
"APPINSIGHTS_INSTRUMENTATIONKEY": "your_key_here",
"DB_CONN": "your_key_here",
}
}
Azure Functions 代理
Azure Functions 代理 在proxies.json
文件中实现,它们使我们能够在同一个 API 下公开多个函数应用,以及修改请求和响应。在下面的代码中,我们在同一个 URL 下发布了两个不同的端点。
// proxies.json
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {
"read-recipes": {
"matchCondition": {
"methods": ["POST"],
"route": "/api/recipes"
},
"backendUri": "https://tacofancy.azurewebsites.net/api/recipes"
},
"subscribe": {
"matchCondition": {
"methods": ["POST"],
"route": "/api/subscribe"
},
"backendUri": "https://tacofancy-users.azurewebsites.net/api/subscriptions"
}
}
}
通过点击扩展中的**闪电**图标来创建一个新的函数。

扩展将使用预定义的模板根据我们所做的选择(语言、函数类型和授权级别)生成代码。
我们使用function.json
来配置我们的函数监听哪种类型的事件,以及可选地绑定到特定的数据源。我们的代码响应特定的触发器运行,当我们响应 HTTP 请求时,这些触发器可以是**HTTP 类型**,当我们响应文件上传到存储帐户时运行代码。其他常用的触发器可以是**队列类型**,用于处理上传到队列的消息,或者时间触发器,用于在指定的时间间隔运行代码。函数绑定用于读取和写入数据到数据源或服务(如数据库)或发送电子邮件。
在这里,我们可以看到我们的函数正在监听 HTTP 请求,并且我们通过名为req
的对象访问实际请求。
// function.json
{
"disabled": false,
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["get"],
"route": "recipes"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
index.js
是我们实现函数代码的地方。我们可以访问上下文对象,使用它与无服务器运行时通信。我们可以执行诸如记录信息、设置函数的响应以及从bindings
对象读取和写入数据等操作。有时,我们的函数应用将有多个依赖于相同代码(例如数据库连接)的函数,并且将该代码提取到单独的文件中以减少代码重复是一个好习惯。
//Index.js
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
if (req.query.name || (req.body && req.body.name)) {
context.res = {
// status: 200, /* Defaults to 200 */
body: "Hello " + (req.query.name || req.body.name)
};
}
else {
context.res = {
status: 400,
body: "Please pass a name on the query string or in the request body"
};
}
};
谁对运行它感到兴奋?
如何本地运行和调试无服务器函数?
当使用 VS Code 时,Azure Functions 扩展为我们提供了许多在本地运行和调试无服务器函数所需的设置。当我们使用它创建一个新项目时,一个.vscode
文件夹会自动为我们创建,这就是所有调试配置包含的地方。要调试我们的新函数,我们可以使用命令面板(Ctrl+Shift+P),通过过滤**调试:选择并启动调试**或键入debug
来进行过滤。

这成为可能的原因之一是 Azure Functions 运行时是开源的,并且在安装azure-core-tools
包时会安装在我们的机器上。
如何安装依赖项?
如果您使用过 Node.js,那么您可能已经知道答案了。与任何其他 Node.js 项目一样,我们首先需要在项目的根文件夹中创建一个package.json
文件。这可以通过运行npm init -y
来完成,-y
将使用默认配置初始化文件。
然后,我们像在任何其他项目中一样使用 npm 安装依赖项。对于此项目,让我们继续从 npm 安装 MongoDB 包,方法是运行
npm i mongodb
该软件包现在可以在函数应用中的所有函数中导入。
如何连接到第三方服务?
无服务器函数非常强大,使我们能够编写对事件做出反应的自定义代码。但是,仅靠代码在构建复杂应用程序时帮助不大。真正的强大之处在于与第三方服务和工具的轻松集成。
那么,我们如何连接并从数据库中读取数据呢?使用 MongoDB 客户端,我们将从我在 Azure 中创建的 Azure Cosmos DB 实例读取数据,但您可以使用任何其他 MongoDB 数据库来执行此操作。
//Index.js
const MongoClient = require('mongodb').MongoClient;
// Initialize authentication details required for database connection
const auth = {
user: process.env.user,
password: process.env.password
};
// Initialize global variable to store database connection for reuse in future calls
let db = null;
const loadDB = async () => {
// If database client exists, reuse it
if (db) {
return db;
}
// Otherwise, create new connection
const client = await MongoClient.connect(
process.env.url,
{
auth: auth
}
);
// Select tacos database
db = client.db('tacos');
return db;
};
module.exports = async function(context, req) {
try {
// Get database connection
const database = await loadDB();
// Retrieve all items in the Recipes collection
let recipes = await database
.collection('Recipes')
.find()
.toArray();
// Return a JSON object with the array of recipes
context.res = {
body: { items: recipes }
};
} catch (error) {
context.log(`Error code: ${error.code} message: ${error.message}`);
// Return an error message and Internal Server Error status code
context.res = {
status: 500,
body: { message: 'An error has occurred, please try again later.' }
};
}
};
这里需要注意的一点是,我们正在重用数据库连接,而不是为每次后续对我们函数的调用创建一个新的连接。这减少了每次后续函数调用的约 300 毫秒。我认为这是一个胜利!
在哪里可以保存连接字符串?
在本地开发时,我们可以将环境变量、连接字符串以及任何秘密内容存储到 local.settings.json
文件中,然后使用 process.env.yourVariableName
以通常的方式访问它们。
local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "node",
"user": "your-db-user",
"password": "your-db-password",
"url": "mongodb://your-db-user.documents.azure.com:10255/?ssl=true"
}
}
在生产环境中,我们可以在 Azure 门户中函数页面上配置应用程序设置。

但是,另一种巧妙的方法是通过 VS Code 扩展。无需离开 IDE,我们就可以添加新的设置、删除现有的设置或将它们上传/下载到云端。

如何自定义 URL 路径?
对于 REST API,围绕 URL 本身的格式有一些最佳实践。我为我们的食谱 API 选择的格式是
GET /recipes
:检索食谱列表GET /recipes/1
:检索特定食谱POST /recipes
:创建新的食谱PUT /recipes/1
:更新 ID 为 1 的食谱DELETE /recipes/1
:删除 ID 为 1 的食谱
创建新函数时默认提供的 URL 格式为 http://host:port/api/function-name
。要自定义 URL 路径和我们侦听的方法,我们需要在 function.json
文件中配置它们。
// function.json
{
"disabled": false,
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["get"],
"route": "recipes"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
此外,我们可以通过使用花括号将参数添加到函数的路由中:route: recipes/{id}
。然后,我们可以从 req
对象中的代码中读取 ID 参数。
const recipeId = req.params.id;
如何部署到云端?
恭喜,您已经完成了最后一步!🎉是时候将这些好东西推送到云端了。与往常一样,VS Code 扩展可以为您提供帮助。实际上,只需要单击鼠标右键,我们就完成了。

扩展程序将使用 Node 模块压缩代码,并将它们全部推送到云端。
虽然此选项在测试我们自己的代码或在处理小型项目时非常有用,但很容易意外覆盖其他人的更改——或者更糟糕的是,覆盖您自己的更改。
不要让朋友右键单击部署!
——每个 DevOps 工程师
一个更健康的选择是设置 GitHub 部署,这可以通过 Azure 门户中的 **部署中心** 选项卡在几个步骤中完成。

您准备好创建无服务器 API 了吗?
本文全面介绍了无服务器 API 的世界。但是,这里介绍的内容远不止这些。无服务器使我们能够以创造性的方式解决问题,并且只需支付使用传统平台通常所需成本的一小部分。
Chris 在 CSS-Tricks 上的其他帖子中提到过,但他创建了 这个优秀的网站,您可以在其中了解有关无服务器的更多信息,并找到您可以使用它构建的事物的想法和资源。请务必查看,并告诉我您是否有其他关于无服务器扩展的技巧或建议。
关于“我们可以使用旧浏览器来创建、编写和测试函数。”——这是什么屏幕截图?我们究竟该如何做到这一点?
嗨,Derek!感谢您的阅读。我录制了一个视频,其中包含使用 Azure 门户创建新函数的步骤,您可以在此处观看 https://youtu.be/6WMIGlxcL7I。文档页面上也有详细的步骤 https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-azure-function。希望这有帮助。
嗨,Simona,
这非常有帮助。谢谢 :)
我现在正在学习 Azure Functions 和 Cosmo DB(完全新手),我看到您可以为“in”(提供查询结果)或“out”(写入 Cosmo DB)设置 Cosmo DB 绑定。您是否知道使用这些绑定与在 Azure 函数主体中直接建立连接相比如何?