Azure Functions:用于昂贵对象的Singleton

Mat*_*ves 16 .net azure azure-functions

我创建了一些非常简单的Azure功能.他们从Couchbase(在VM上运行Azure)中读取和写入数据.

我担心我在Azure功能中与Couchbase建立的连接.我Cluster每次都创建一个对象.这是一项昂贵的操作,我通常只会在普通的网络应用程序中执行一次.但是在Azure功能中,我new每次都在努力.

除了Couchbase之外,实例化这样的对象还有很多代价.有没有办法创建一个单例,或Azure函数可以在调用之间重用的某种共享对象?

Ste*_*ary 19

在Azure Functions上处理单例时,有几个注意事项.一个是全球状态 AF调用共享.因此,如果一个函数被调用一次然后再调用(很快主机没有卸载你的代码),那么初始化只发生一次.另一个考虑因素是AF可以完全自由地同时启动多个AF调用 - 因此任何单例都需要线程安全(包括初始化).

这意味着你将要使用Lazy<T>/ AsyncLazy<T>.但是,请记住,AF(使用这些类型)将保留下次调用的单例状态(初始化后),即使它失败也是如此.这可能是云计算的一个问题,因为如果AF启动时出现网络(或配置)错误,您希望在下一次AF调用时重试初始化.

总之,您希望以一种线程安全的方式使用Lazy<T>/ 并且不会保留故障.AsyncLazy<T>

有了Lazy<T>,这意味着你必须使用LazyThreadSafetyMode.PublicationOnly标志 并将一个函数传递给构造函数(不只是隐式使用默认构造函数T).请注意,这意味着您需要确保初始化函数本身是线程安全的,因为它可以由多个线程同时执行.

有了AsyncLazy<T>,你必须使用AsyncLazyFlags.RetryOnFailure旗帜.由于AsyncLazy<T>本质上是a Lazy<Task<T>>,异步初始化任务在所有同时调用者之间"共享",然后Lazy<Task<T>>如果失败则以原子方式替换为新实例.因此异步初始化函数不需要是线程安全的.

由于这一切都很好(特别是对于多个单身人士)是相当复制和粘贴的,所以我为我正在研究的AF项目抽象出来:

这需要一段时间才能达到这一点,但我对它的结果非常满意.关于这个博客的意思也是......

  • 如果有人好奇,我们确实记录了该播客:https://crosscuttingconcerns.com/Podcast-072-Stephen-Cleary-on-Azure-Functions (4认同)
  • 斯蒂芬感谢你的回答.如果有时候来我的播客谈论它,并促进你的工作:) (2认同)

Jes*_*ter 10

昂贵的连接对象的静态属性可以正常工作,但我建议将它们包装好,Lazy<>以便开箱即可保证线程安全.

基于示例博客文章,您链接到一个示例,使用可保证的线程安全方式使所有函数调用中的存储桶可重用,可能如下所示:

public class FunctionClass
{
    private static Lazy<IBucket> LazyBucket = new Lazy<IBucket>(() =>
    {
        var uri = ConfigurationManager.AppSettings["couchbaseUri"];
        var cluster = new Cluster(new ClientConfiguration
        {
            Servers = new List<Uri> { new Uri(uri) }
        });

        var bucketName = ConfigurationManager.AppSettings["couchbaseBucketName"];
        var bucketPassword = ConfigurationManager.AppSettings["couchbaseBucketPassword"];

        return cluster.OpenBucket(bucketName, bucketPassword);
    });

    // Your actual function implementation
    public static async Task Run()
    {
        // Here you are guaranteed to get back a shared connection object to your bucket that has been
        // initalized only once in a thread safe way
        var initalizedOnceBucket = LazyBucket.Value;

        // do something with the bucket
    }
}
Run Code Online (Sandbox Code Playgroud)

如果应该共享的昂贵对象的构造依赖于某些异步调用(我怀疑Couchbase C#客户端可能具有异步版本的方法).您可以使用Stephen Cleary编写AsyncLazy<>的令人敬畏的Nito.AsyncEx Nuget包.Regular Lazy<>内置于.NET中,因此不需要任何外部依赖项.


Mik*_*kov 6

您可以使用普通的单例,即返回某物单个实例的静态属性。与往常一样,请谨慎使用线程安全性,例如Lazy<T>按@Jesse的建议使用。

在执行对函数的第一次调用之前,您还可以使用静态构造函数进行初始化。根据定义,静态构造函数是线程安全的。

在这两种情况下,您都可以在同一实例(服务器)上运行的所有调用之间重用昂贵的东西。

  • 将Lazy &lt;&gt;或AsyncLazy &lt;&gt;与静态属性一起使用有助于缓解任何潜在的线程问题 (3认同)