利用 Azure Functions 实现无服务器体系结构

来源:互联网 发布:js构造函数与原型对象 编辑:程序博客网 时间:2024/05/29 06:55

从工具到机器再到计算机,我们一直在寻找能够自动执行重复工作并让我们所处理的上下文规范化的方法,以便我们可以将重心放在做出高价值的专业化贡献上,从而完成任务并解决问题。


与此同时,很显然,随着 IT 产业的不断发展,我们一直都努力在每个系统层级(从 CPU 到服务器场)实现更高密度,以期最大限度地提高我们的系统输出效率。无服务器体系结构就可以同时满足这两种需求。借助这种体系结构,不仅可以最大限度地细化个人工作量以重点完成特定任务,还能最大限度地减少系统浪费。


在无服务器环境中,开发者创建的是解决方案(而不是基础结构),监视的是执行情况(而不是环境运行状况)。开销对象为时间片,而不是大多数情况下都处于空闲状态的虚拟机 (VM) 场。


在 Microsoft Azure 中,有许多可使用的服务能够关联在一起,形成一个完整的解决方案。此解决方案的一个新关键组件就是 Azure Functions,可以在完整的解决方案生态系统中提供无服务器计算功能。在本文中,我们将探索无服务器体系结构的意义和 Azure Functions 工具。



利用 Azure Functions 实现无服务器体系结构

无服务器体系结构的定义有许多。虽然是这样命名的,但无服务器体系结构并不是指不使用服务器运行的代码。


事实上,服务器仍是非常有必要的,这一点毋庸置疑。你可能会认为它是平台即服务 (PaaS) 的下一迭代,虽然这已经很接近了,但其实还不是。


那么,它究竟什么是呢? 从根本上讲,无服务器体系结构是云服务的下个革命,建立在 PaaS 基础之上,通过绑定移除了 VM、应用程序框架和外部依赖关系,让开发者能够将重心仅放在实现业务逻辑的代码上。


谨慎的做法是介绍 Azure Functions 和无服务器体系结构的典型属性。Azure Functions 提供无服务器体系结构的无服务器计算组件。如图 1 所示,Azure Functions 建立在 Azure App Service 和 WebJobs SDK 的基础之上,在托管和运行 Azure Function 代码方面,以及在一些细节方面(如运行时赋值)施展了一些奇思妙想。


图 1:Azure Functions 体系结构


因此,使用 Azure App Service 的所有好处一样不少(见设置中服务的正下方)。此外,这还意味着,可以在本地使用 WebJobs SDK,创建本地运行时环境。


Azure Functions 是一个多语通平台,支持 Node.js、C#、PhP、Python 和 Windows PowerShell 等多种语言,尽管这不是体系结构方面的优势。运行时和依赖关系由平台进行处理。


这对于使用混合环境的人员来说绝对是一大优势,因为偏好设置和技能不同的团队可以利用同一平台来构建和传递功能。使用 Azure Functions 作为无服务器体系结构的无服务器计算组件有以下多项重要优势:


  • 缩短上市时间: 由于基础结构是由平台进行管理,因此开发者可以将重心放在实现业务逻辑的应用程序代码上。Azure Functions 可视为将微服务进一步分解成纳米服务。这两种范例都是开发流程的一大福音,因为开发、测试和部署活动可以只侧重于一小部分与其他相关(但离散)服务分开管理的功能。


  • 降低总拥有成本: 由于基础结构或操作系统负责维护,因此开发者可以将重心放在提高业务价值上。此外,DevOps 和维护所需的投资还会大大精简。


  • 按执行次数付费: 仅按使用周期付费,再加上增加所用计算资源的函数密度,通常可以大大节约成本。


至于你在使用 Azure Functions 生成解决方案时采用的方法,实现全部价值的关键在于,只编写最小逻辑单元来完成一个范畴内的工作,并最大限度地减少依赖关系。使用 Azure Functions 时,最好遵循以下最佳做法:


  • 在本质上,Azure 函数的用途应该单一。将它们看作是精简的语句,而不是并列复合句。


  • 操作为幂等操作。也就是说,如果后续使用相同的参数调用 API 终结点,生成的系统状态不变。


  • 快速执行。目标应为收到输入内容,执行所需操作,然后向下游使用者传递结果。对于长时间运行的进程,不妨考虑使用 Azure WebJobs 或在 Azure Service Fabric 中托管服务。


  • 在试图保持较低的整体复杂性和快速执行的同时,最好最大限度地减少内部依赖关系。添加过多的运行时权重不仅会延长初始加载时间,还会增加系统的复杂性。


  • 通过输入和输出绑定进行外部集成。有关高性能网站的一些常见指南是编写无状态服务。这样一来,既可以简化或加快用于保持、序列化和反序列化运行时状态的服务,也可以简化调试工作量,因为你无需发现状态,也无需尝试重现状态,即可了解发生了什么;若为无状态,只需返回参数值即可。


无服务器体系结构的属性如下:


  • 无服务器体系结构的工作单元采用事件调用的无状态函数的形式。


  • 缩放、容量和基础结构管理以服务的形式提供。


  • 以执行次数为依据的计费模型,只需按代码运行时间付费。


下面介绍了无服务器体系结构带来的一些挑战:


  • 复杂性: 虽然无服务器体系结构为开发者简化了很多操作,但鉴于平台的抽象性,仍需要重新审视应用程序的生成方式。将整个应用程序作为一个单元进行管理,比管理一系列专门构建的函数及其之间的依赖关系更为简单。诸如 Azure API 管理提供的功能便派上用场,可用于创建向外的一致命名空间,从而将所有离散定义和托管的函数关联在一起。


  • 工具: 用于编写、调试和测试函数的工具相对较新,目前仍处于开发阶段。截至本文撰写之时,这些工具暂处于预览阶段,但将内置在 Visual Studio 2017 中。此外,管理和监视工具仍在改进阶段,但已推出适用于 Azure Functions 的基本界面,可以在其中详细了解请求、成功消息、错误消息和请求响应。将应用监视工具绑定到平台还有大量工作需要完成,Azure Functions 团队正在努力提供和改进此类支持。


  • 组织支持: 迁移至无服务器范例对于一些组织来说非常重要。许多组织在迁移至全自动持续集成 (CI)/持续交付 (CD) 管道和微服务体系结构时都面临挑战。迁移至无服务器设计更是难上加难,因为这经常要质疑现行标准,并需要为资源提供有关可用性以及如何关联和管理的指导。


  • 无运行时优化: 在传统设计中,可以根据工作负载优化执行环境,同时更改 RAM、交换、磁盘和网络连接等的类型和容量。使用 Azure Functions,只能作出微小更改,如要使用的存储帐户。


传统体系结构与无服务器体系结构

一套典型的系统设计项目包括逻辑设计、技术设计和软件体系结构。逻辑设计通常定义的是系统的功能和用途,而技术设计则通常定义的是系统概念。


随着你迁移至无服务器体系结构,将更为关注系统的用途,而不是系统的概念。在许多 IT 商店中,看到的更多是像图 2 一样的体系结构图。

图 2:传统技术体系结构


这一类项目对于负责托管、网络和 DBA 组的人员尤为重要,因为他们需要知道预配和配置什么。


不过,在平台即服务 (PaaS) 环境中,你将重心放在函数属性上,而将预配详细信息交由只需定义 PaaS 服务本身配置的平台处理。


输入的配置详细信息将保存在模板中,并且所有对话都将以功能和集成为重心,无需讨论 RAM、CPU 和磁盘的最佳容量。


生成的图可能更接近于图 3 所示的常见逻辑图。此类图一开始的重心将放在函数部分以及如何关联这些部分上,而不是放在配置应用程序主机上。


这样简化沟通和阐明用途不仅有助于就意向和实现展开技术讨论,还必然会让更多的非技术性办公室享受系统带来的价值,因为讨论的重心从系统的概念转变为系统的用途。


图 3:无服务器体系结构



Azure Functions 演示 

Azure IoT 平台提供一系列丰富的服务来支持收集和分析数据。我们的示例包括现有的 IoT 实现,以使用基本分析功能来收集车辆遥测并将其存储在云中。


我们现在要探索如何向解决方案添加新功能,如实时查询数据以找到距离给定位置最近的车辆。这种功能可用于独立的汽车共享程序,甚至可用于在停车场找车。


在此示例实现中,必须注意一些重要事项,因为我们出于工具和平台演示目的要故意采取一些简便方法。


首先,我们直接使用单分区 DocumentDB。在黄金时段实现中,我们至少会根据预期数据量对 DocumentDB 进行分片,但我们也可能选择执行其他操作,如添加 Azure Redis 缓存和 Azure 弹性搜索作为优化一些读取路径的方法。


其次,由于最新的 DocumentDB API 要求客户端进行更多处理才能获取我们要比较的记录,因此我们采取了简便方法,只要求获取前 100 条记录。若要在大型数据集中查找所需记录,更为典型的方法是使用分区和搜索功能。


无论如何,仍然可以使用函数来查找可能记录,然后将它们与给定位置进行比较,返回数据集中距离最近的车辆。


创建函数

截至本文撰写之时,Visual Studio 工具无法帮助加速开发流程。


为此,我们将先通过门户界面创建 Azure 函数。在 Azure 门户中开始使用 Azure Functions 创建函数后,便会看到一个边栏选项卡,用于为函数设定一些设置。请注意应用服务计划选择选项: “使用量计划”和“应用服务计划”。


在这两个选项之间做出选择看似最简单,但实际上是要在管理资源这一老式做法和无服务器体系结构的理想目标之间做出选择。选择“应用服务计划”时,必须猜测需要多少处理能力。如果选择过多,就要为没有使用的资源付费;而如果选择过少,就可能会遇到实现问题,最终会对客户和经济利益造成影响。


“使用量计划”是首选,因为这样一来,系统可以为你缩放资源,而你作为使用者则可以按应用的使用量来支付相应费用。


最后一步就是创建函数本身。将从 WebHook 预先创建的 C# 函数入手。这会将触发器预先配置为根据是否收到 HTTP 请求来执行函数。


选择左侧菜单上的“整合项”后,有多个触发器、输入和输出配置选项可供选择。触发器选择页上有一些实用信息,包括 WebHook 绑定信息、如何使用 API 密钥,以及一些用于调用 WebHook 的示例 C# 和 Node.js 代码。看到对话框后,便可以配置输入和输出绑定,如图 4 所示。


图 4:配置输入绑定


虽然可以通过添加库和适配器来调用外部系统,但系统是通过绑定运行,并提供对 EventHubs 和 DocumentDB 等众多数据源的一级支持。可通过 Azure Functions 提供的绑定基础结构来提升开发体验。


绑定可让实际目标软件基础结构(DocumentDB、EventHubs、存储等)抽象化,让开发者可以向表示目标的参数添加代码,同时保持灵活性,因为可以通过更改配置来更改目标。


此时,已将其配置为与源 DocumentDB 通信。借助此配置,可以在函数内直接针对 DocumentDB 客户端编写代码,你无需自行连接。请注意文档参数名称 inputDocument。这是传递给函数的变量,将用来调用 DocumentDB。


此时,可以看到输出选项,其中包括许多存储、队列和其他外部系统。所有可以通过 UI 选择和配置的项稍后都可通过 Function App 设置 UI 访问,并能作为模板的一部分或通过程序设计界面进行配置。只需通过 HTTP 将 JSON 结果返回给调用方即可。由于已为输出设置“HTTP(res)”(定义了输出参数),因此将原样接受。


开发代码

选择左侧菜单中的“开发”后,便会看到联机编辑器。这有助于快速执行迭代更改并实时查看日志,提供了用于触发 WebHook 函数的界面。


适用于 Visual Studio 和 Visual Studio Code 的工具正处于开发阶段,将带来更丰富多彩的体验。如果现在使用的是 Azure Functions,不妨使用 IDE,Visual Studio 通过服务器资源管理器可轻松连接 Function App。


编辑文件时需要执行以下几项操作。“代码”窗口正下方有一个“查看文件”链接。在“代码”窗口右侧直接打开文件资源管理器。


首先,需要有 DocumentDB 客户端。有许多库可通过使用顶部的 #r 指令自动引用。


有关这些库的列表以及其他许多开发者信息,请参阅联机开发者参考信息 (bit.ly/2gaWT9x)。


例如,需要访问 DocumetDB 客户端对象,因为有对 DocumentDB 的一级支持。只需在文件顶部为相应的程序集添加 #r 指令即可。如果所需的库不包含在内,请指出一个要维护并发布到 NuGet 的库,它就会被添加到 project.json 文件(使用 Node.js 编写的 package.json)中。


此时,可以编辑 run.csx 文件,其中包含所有代码。为此,直接在适用于 Azure Functions 的联机 IDE 中编辑该文件,如图 5 所示。


图 5:编辑函数代码


从模板代码入手,先将你自己的自定义外部库添加到函数中,因为其中包含半正矢函数代码。若有不是太大的函数专有自定义类或函数,可以将它们直接添加到 run.csx 文件中。


不过,若有可重用部分,则需要采取不同措施,将它们的编译版本添加到 \bin 文件夹,或通过 project.json 文件以 NuGet 包形式引用它们,然后使用 #r 引用库。也可以将代码置于其他 .csx 文件中,然后使用 #load 指令。


需要使用一些函数来帮助确定要检测邻近区域的车辆与传递到函数的点之间的距离。


距离学生时代已有一段时间,因此不常需要用到半正矢公式。维基百科对此提供了很好的参考资料 (bit.ly/2gCWrgb),我们借用了 bit.ly/2gD26mK 上的 C# 函数,并进行了一些更改。我们创建了作为半正矢类的静态成员的必要函数:


namespace SphericalDistanceLib{  public class Haversine  {    public static double CalculateDistance(      double currentLong, double currentLat,      double vehicleLong, double vehicleLat){…}    public static double ToRadians(double degrees){...}  }}


代码经编译后会上传到 bin 文件夹(相对于函数的根文件夹)。


在我们的示例中,路径为 FindNearVehicle/bin,但我们必须创建 bin 文件夹,因为它默认不存在。完成这部分设置后,将重心转移到所需的代码上。在函数顶部,需要确保引用的是所需的库。


尤其需要 DocumentDB 客户端对象类型、Newtonsoft 以及已上传到 /bin 文件夹的自定义库。这些库通过 #r 指令添加到文件顶部:

#r "Microsoft.Azure.Documents.Client"#r "Newtonsoft.Json"#r "SphericalDistanceLib.dll"


如上所述,将根据 created_at 时间戳字段捕捉最后 100 条记录。DocumentDB 有一个可以使此操作变得非常简单的实用 SQL 语法:

IQueryable<Document> telemDocs = inputDocument.CreateDocumentQuery<Document>(  UriFactory.CreateDocumentCollectionUri(dbName, collectionName),  new SqlQuerySpec("SELECT TOP 100 c.vehicle.vin, c.vehicle.model,  c.location FROM c ORDER BY c.created_at DESC"), queryOptions);


你使用的是文档类型,这样可以略微简化一下操作,因为你将把它转换成动态类型,以便向对象轻松添加属性。SQL 以 SqlQuerySpec 形式传递,你只会将 VIN、模型和位置投影到对象。


此时,需要循环访问文档列表,使用外部库中的半正矢函数计算距离,然后确定最近目标并将其返回。不过,这样做有点棘手。


需要跟踪看到的所有 VIN,因为你只想获取相应 VIN 的最新位置记录。由于将按降序排序,因此第一个文档是收到的最后一个文档。


将检查 VIN 是否为 NULL,因为你要获取的是在开车辆。如果 VIN 为 NULL,可以假定文档无效。如果元素中有非 NULL 值,只需尝试将 VIN 添加到 HashSet 即可。如果列表中没有,便会成功添加,否则将会失败,并且你会知道列表中已经有该车辆的最新记录,如图 6 所示。

HashSet<string> seen = new HashSet<string>();foreach(dynamic doc in telemDocs){  if(seen.Add(doc.vin)){    // Calculate the distance    if (doc.location != null) {      doc.distance =        Haversine.CalculateDistance(double.Parse(currentLong),        double.Parse(currentLat), double.Parse(        doc.location.lon.ToString()),        double.Parse(doc.location.lat.ToString()));      lastKnownLocations.Add(doc);    }    else{      // Location is null, so we won't take this      // record as last known location      seen.Remove(doc.vin);    }  }}

图 6:不同 VIN 的上一已知位置


将整个文档添加到 lastKnownLocations 列表,从而轻松逆向显示并根据最小距离值排序来查询第一个文档:

var nearestVehicle = lastKnownLocations.OrderBy(x => ((dynamic)x).distance).First();return currentLat == null || currentLong == null  ? req.CreateResponse(HttpStatusCode.BadRequest,  "Please pass a lat and lon on the query string or in the request body")  : req.CreateResponse(HttpStatusCode.OK, nearestVehicle);


可以返回经过排序的列表中的第一个文档,如最后一行所示(代码还为你处理了 NULL 参数检查和文档序列化)。


运行示例

最后一步是观察实际效果。在开发视图的右上角,有一个带烧瓶图标的“测试”链接。单击此链接将打开测试 UI,如图 7 所示。


在此 UI 中,不仅可以更改 HTTP 方法、添加参数和请求头,还能设置要传递给选定 HTTP 方法的正文。如果有大量这样的工作,我们通常会使用 Postman,因为我们有一个测试库。然而,HTTP 函数的内置测试工具非常实用,可立即执行快速迭代工作。

图 7:测试函数

我们捕捉了纬度和经度,并将其格式化成“运行”窗口中的预期 JSON 格式。


单击“运行”后,可以在“日志”窗口中查看调用日志对象后的所有输出和一般日志输出,并能在“输出”窗口的右下角看到输出和状态。


请注意日志输出用时,我们的函数似乎需要约 250 毫秒才能运行。这非常符合我们努力通过 Azure Functions 实现的执行模型:用途单一且执行时间相对较短。


将内容从“输出”窗口中移出并设置格式后,我们可以更清楚地看到车辆、记录位置时的时间戳、位置和距离:

"vin": "wmwrxxxxxxxx54251""model": "Cooper Hardtop""location": {    "lat": 30.4xxxxxx,    "lon": -97.8xxxxxx,    "accuracy_m": 22.505,    "ts": 1473116792970  },  "distance": 13.552438042837085}


计算距离时,我们给出了以公里为单位的地球周长,因此返回的距离大约为 13.6 公里。

 总结 

在此例子中,我们混合使用了各种联机工具来开发和执行 Azure Functions,但对于开发团队而言,更现实的做法是通过 Git 存储库在本地开发和构建持续集成和部署。使用 WebJobs.Script 和 WebJobs SDK,可以在本地开发和运行 Azure Functions。


有关此操作的逐步指导,请访问 bit.ly/2hhUCt2


你还会发现,可以将许多不同的源配置为部署源。


Azure Functions 是 Azure 平台家族的新成员。它是享受云 PaaS 实现优势所必需的无服务器计算的关键组成部分。


Azure Functions 将重心从管理基础结构转移到了代码价值上。虽然平台、工具支持和语言支持会继续发展,但它已支持许多语言,并且可以绑定到 CI/CD 管道。


有关详细信息,请访问 bit.ly/2h7lo4C


如果还没用过 Azure Functions,应该试一试。若要开始使用,请访问 bit.ly/2glBJnC。






向无服务器计算转变


无服务器计算代表我们对云计算以及生成云应用程序的看法发生了根本的范例转变。无服务器计算为开发者提供了一个完全抽象且可无限扩展的环境来执行代码,并提供了一种只根据代码执行次数付费的付款模型。


无服务器计算可以被视为平台即服务 (PaaS) 的下个革命。它兑现了 PaaS 的承诺,即将应用程序基础结构与代码分离,并提供自动缩放功能。


无服务器的主要卖点是根据执行次数付费的定价模型(而不是根据代码托管时间付费)和即时无限缩放功能。无服务器计算提供了完全托管式的计算体验(没有管理任务)、即时无限缩放功能(没有缩放配置)和事件响应功能(实时处理)。


这样一来,开发者可以开发新一类的应用程序。这类应用程序可以根据设计进行缩放,总体来说可复原性更高。


生成无服务器应用程序的开发者不仅使用 Azure Functions 等无服务器计算,还使用越来越多的完全托管式 Azure 或第三方服务,以生成有效的端到端无服务器解决方案。


如今,开发者可以很容易地就建立和运行完整的无服务器体系结构,利用很少的成本就能根据设计轻松进行缩放。同时,开发者将摆脱管理和监视基础结构的包袱,将重心放在业务逻辑以及解决业务相关问题上,而无需处理运行代码的基础结构的维护问题。


无服务器旨在改变我们对生成应用程序的看法,影响将持续很长一段时间。请访问 bit.ly/2hhP41O 和 bit.ly/2gcbPzr 加入讨论。


请将功能请求提交至 bit.ly/2hhY3jq,并通过 Twitter 与我们联系:@Azure 加井号标签 


#AzureFunctions。    —Yochay Kiriaty






Darren Brust 是 Microsoft 的云解决方案架构师,大部分时间他都在协助企业客户迁移到 Microsoft Azure。


空闲时,他可能会去看他三个孩子的体育比赛,也可能会在当地的 Austin 咖啡店喝许多咖啡。可以通过 dbrust@microsoft.com 或 Twitter (@DarrenBrust) 与他联系。


Joseph Fultz  是 Microsoft 的云解决方案架构师。他负责与 Microsoft 客户合作,共同开发体系结构,从而利用 Microsoft Azure 解决业务问题。


此前,Fultz 负责开发和生成 GM 的汽车共享程序 (mavendrive.com)。可以通过 Twitter (@JosephRFultz) 或电子邮件 (jofultz@microsoft.com) 与他联系。


衷心感谢以下 Microsoft 技术专家对本文的审阅: Fabio Calvacante


原文地址:https://msdn.microsoft.com/zh-cn/magazine/mt793269


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

内容转载自公众号

微软中国MSDN
微软中国MSDN
了解更多
原创粉丝点击