我在第一个Azure Functions项目中学到的东西

作为欧盟通用数据保护法规(GDPR)要求的一部分,我们在Visual Studio 2017中构建了一个系统,以使用消费计划中托管的时间和队列触发器Azure功能进行数据最小化。 以下列出了一些值得分享的技巧和提示,希望它们对Azure Functions开发的新手有所帮助。

确保它适合您

Azure Functions功能强大,但这并不是针对所有业务需求的解决方案。 要运行Azure功能应用程序,有两种不同的托管计划。 以下是它们之间的区别的简要比较。 该页面使您对每个人的利弊及其工作方式有很好的了解。

进入Kudu后,打开“进程资源管理器”,您将看到IIS辅助进程w3wp.exe托管了Azure Functions版本1.0

然后,您的函数程序集将在w3wp进程中加载​​,该进程就像一个容器。 以下是演示如何初始化的示例:

  D:\ Windows \ SysWOW64 \ inetsrv \ w3wp.exe -ap“ YourFunctionName” -v“ v4.0” -a“ \\。\ pipe \ iisipmbfc62ec4-5183-4ebd-9999-ae18ec4527e5” -h“ C:\ DWASFiles \ Sites \ YourFunctionName \ Config \ applicationhost.config“ -w” C:\ DWASFiles \ Sites \ YourFunctionName \ Config \ rootweb.config“ -m 0 -t 20 -ta 0 

了解Azure Functions应用程序设置

与其他任何.NET应用程序一样,Azure Functions也可以使用System.Environment.GetEnvironmentVariable方法读取应用程序设置。 在应用程序设置与功能之间的关联方面,可能并非您想象的那样。

对于本地,应用程序设置在local.settings.json文件中定义,该文件仅用于本地开发,即使它的名称已经很明显。 但是,我发现许多开发人员(包括我自己)仍然错误地认为此处定义的设置将在Azure中生效-答案是绝对没有。 要将自定义设置添加到您的local.settings.json中,只需在Values添加一个新条目。

  //   local.settings.json 
{
“已加密”:false,
“值”:{
“ AzureWebJobsStorage”:“ UseDevelopmentStorage = true”,
“ AzureWebJobsDashboard”:“ UseDevelopmentStorage = true”,
“ MyCustomSetting”:“自定义设置”
}
}

若要定义要在Azure中使用的应用程序设置,可以通过Azure资源管理(ARM)模板中的应用程序服务部署来完成。 这是一个快速入门模板供参考。 如您在azuredeploy.json模板中azuredeploy.json ,应用程序设置被定义为Microsoft.web / sites资源的一部分,此资源是事后部署功能所必需的。 因此,将单独部署Azure功能和功能消耗的应用程序设置。

限制队列触发功能缩放

Azure Functions具有的重​​要功能之一是,Azure Functions运行时在负载下会自动扩展函数实例。 在我们的情况下,它会根据正在侦听的队列中的消息量进行扩展。 但是,此功能可能会导致其依赖的下游API出现问题。 相反,我们要限制它的自动缩放功能,以免在有大量消息到达时自己避免DDOS。 幸运的是,队列触发器Azure函数允许您通过使用host.json文件中的以下两个设置来限制其功能:

  “队列”:{ 
//将功能运行时同时检索的队列消息数限制为1。
“ batchSize”:1
//这指示运行时在不处理任何消息时检索另一个批处理。
“ newBatchThreshold”:0
}

这基本上意味着每个函数在任何时间可以处理的并发消息的最大数量为1( batchSize加上newBatchThreshold )。 虽然,使用这种方法,您可能会认为,如果每个函数的执行都非常快地一个接一个地完成,那仍然可能会导致问题,这是绝对可能的。 因此,我们进行了性能测试,以使我们更加放心,其报告显示每个函数执行的平均持续时间大约需要1.2秒-我知道,太慢了! 但是,好消息是,它离我们的下游API速率限制还差得远。 快乐的时光!

请注意Newtonsoft.Json上的程序集重定向绑定问题

从1.0.0-alpha6版本开始,Azure Functions SDK现在严格要求使用Newtonsoft.Json 9.0.1版,以防止运行时失败。 有时会令人沮丧,因为它限制了您使用最新的Newtonsoft.Json软件包。 另外,由于Newtonsoft.Json软件包的普及,许多其他软件包都在引用它,因此遇到此问题的机会非常高。

就我们而言,此问题使我们WindowsAzure.Storage v9.2.0使用WindowsAzure.Storage v9.2.0程序包,该程序包具有支持通过Azure AD访问Azure存储帐户的功能。

在Azure Functions中访问客户端证书

访问证书是一种非常常见的情况,通常是在获取令牌以访问远程受保护资源时。 有几种方法可以实现此目的。 在Azure Functions团队将其提供之前,我已经看到一些人通过读取文件系统来做到这一点,该文件系统适用于公共证书,但是读取私有证书的安全性不够。

现在,访问客户端证书非常简单,但是值得注意的是:

  • 如何将证书设置到Azure应用服务上 。 在撰写本文时,正确的方法仍然是功能请求。 解决该问题的方法是通过New-AzureRmWebAppSSLBinding Azure Power Shell脚本完成的,该脚本旨在上传SSL证书,但是即使存在域与主题不匹配的错误,它仍然可以上传客户端证书。证书名称。 为了解决这个问题,您可以通过指定ErrorAction参数来抑制错误,该参数具有以下选项并且将忽略所有可能的错误,或者可以通过使用try and catch块检查错误来更具体地说明该错误。
  [:{继续| 忽略| 查询 静默继续 停止| 暂停 }] 
  • 如何使证书易于访问 。 要使证书可用,您需要进行WEBSITE_LOAD_CERTIFICATES应用设置,并将其值设置为要读取的证书的指纹。 另外,您可以将其设置为多个用逗号分隔的指纹,或简单地将其设置为*以读取所有可用的证书。
  • 如何验证证书可用 。 要验证您的证书是否可访问,您可以通过Kudu上的站点扩展库安装“证书阅读检查器”,该向导将向您显示是否可以通过代码读取证书。
  • 如何读取代码中的证书 。 由于带有消费计划的Azure功能在公共规模的单元上托管,因此只能在CurrentUser个人存储下安装证书。 这显示了如何在C#中读取证书的示例。

与应用程序洞察力集成

Azure Functions中有两种内置的日志记录方法,一种是使用表存储的Webjob仪表板,另一种是更为高级和强大的Application Insights。 下面显示了如何配置Azure功能和Application Insights。

与Application Insights集成非常简单。 您所需要做的就是将检测键添加到函数应用程序设置中,其余的将由Azure函数负责。 有关更多信息,请参见此处。

  APPINSIGHTS_INSTRUMENTATIONKEY =“您的乐器钥匙” 

在您的ARM部署模板中,使用reference功能可以在配置功能应用程序时检索工具键,并将其添加到Azure功能应用程序设置列表中。

  { 
“ name”:“ APPINSIGHTS_INSTRUMENTATIONKEY”,
“值”:“ [引用(resourceId('Microsoft.Insights / components',parameters(' YourAppInsightsInstanceName ')),'2014-04-01')。InstrumentationKey]”
},

如果选择使用Application Insight进行日志记录,则出于节省成本的目的,最好禁用Webjob仪表板日志记录。 否则,它仍将日志写入表存储中,这会花费很多钱。 为此,只需从应用程序设置中删除AzureWebJobsDashboard

如何在Azure Functions中实现依赖项注入

不幸的是,不立即支持依赖项注入(DI),这使开发人员的生活变得更加艰难,因为随着添加的服务的增多,项目的规模也随之增加。 鲍里斯·威廉斯(Boris Wilhelms)提出了一个很好的解决方案,那就是使用绑定属性通过函数输入方法注入依赖项,然后在函数绑定扩展中注册所有服务。 如果您希望在功能项目中使用DI,上面的链接就是您所需要的。

大多数情况下,您希望服务在某些情况下能够记录信息或提供警告。 使用上述方法设置DI后,即可创建内置记录器,以作为对服务的依赖关系使用。 下面显示了如何执行此操作的示例:

 公共类RegisterDependencies:IExtensionConfigProvider { 
公共无效的Initialize(ExtensionConfigContext上下文){
var container = new WindsorContainer();
var logger = context.Config.LoggerFactory
.CreateLogger ();
container.Register(
Component.For ()。Instance(logger),
Component.For ()
);
context.AddBindingRule ()
.Bind(new InjectBindingProvider(container));
}
}

上面列出了一些注释,希望对您有所帮助。 如果您有任何疑问,请在这里发表评论,我会尽力回答。