2017-09-11 1 views
5

J'ai créé des fonctions Azure très simples. Ils lisent et écrivent des données à partir de Couchbase (qui s'exécute dans Azure sur une machine virtuelle).Azure Fonctions: Singleton pour objet cher

Je suis préoccupé par les connexions que je fais à Couchbase dans une fonction Azure. Je crée un objet Cluster chaque fois. C'est une opération coûteuse, et je ne le ferais typiquement qu'une fois dans une application web normale. Mais dans la fonction Azure, je suis new à chaque fois.

Il y a beaucoup de coûteux à instancier des objets comme celui-ci au-delà de Couchbase. Est-il possible de créer un singleton ou un type d'objet partagé que les fonctions Azure peuvent réutiliser entre les appels?

Répondre

3

Vous pouvez utiliser le singleton normale, soit une propriété statique qui retourne la instance unique de quelque chose. Comme toujours, faites attention à la sécurité des threads, par exemple utilisez Lazy<T> comme suggéré par @Jesse.

Vous pouvez également utiliser un constructeur statique pour effectuer l'initialisation avant l'exécution du premier appel de votre fonction. Le constructeur statique est thread-safe par définition. Dans les deux cas, vous serez en mesure de réutiliser le matériel coûteux entre tous les appels qui s'exécutent sur la même instance (serveur).

+2

L'utilisation de Lazy <> ou AsyncLazy <> avec des propriétés statiques permet d'atténuer les problèmes de threads potentiels –

4

Les propriétés statiques de vos objets de connexion coûteux fonctionneront correctement, mais je recommande de les entourer de Lazy<> afin d'obtenir une sécurité de thread garantie dès le départ.

Basé sur le blog de l'échantillon que vous lié à un exemple de rendre le réutilisable seau à travers tous vos appels de fonctions d'une manière sûre de fil garanti pourrait ressembler à ceci:

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 
    } 
} 

Si la construction de votre L'objet cher qui doit être partagé dépend de certains appels asynchrones (je suppose que le client Couchbase C# peut avoir des versions asynchrones de ses méthodes). Vous pouvez utiliser le de l'impressionnant Nito.AsyncEx paquet Nuget écrit par Stephen Cleary. La norme Lazy<> est intégrée dans .NET, donc aucune dépendance externe n'est requise.

+0

J'aimerais pouvoir vous donner à la fois une coche. Merci pour votre réponse! –

+1

Pas de problème, Mikhail était le premier ici et sa réponse est tout à fait correcte. Je voulais juste entrer et montrer les motifs que j'utilise dans mes propres fonctions –

5

Lorsque vous traitez des singletons dans Azure Functions, il y a plusieurs considérations. L'un est que l'état global est partagé entre les invocations AF. Ainsi, si une fonction est invoquée une fois puis de nouveau plus tard (assez tôt que l'hôte n'a pas déchargé votre code), l'initialisation ne se produit qu'une seule fois. Une autre considération est que AF est parfaitement libre de lancer plusieurs appels AF simultanément - donc tous les singletons doivent être threadsafe (y compris leur initialisation). Cela signifie que vous devez utiliser Lazy<T>/AsyncLazy<T>. Cependant, gardez à l'esprit que AF (avec ces types) conservera l'état singleton (post-initialisation) pour votre prochaine invocation même s'il échoue. Cela peut poser un problème en particulier avec le cloud computing car, en cas d'erreur de réseau (ou de configuration) au démarrage de votre FA, vous souhaitez que l'initialisation soit réitérée lors de l'appel AF suivant.

En conclusion, vous souhaitez utiliser Lazy<T>/AsyncLazy<T> de telle manière est threadsafe et ne conserve pas les échecs.

Avec Lazy<T>, cela signifie you have to use the LazyThreadSafetyMode.PublicationOnly flaget passer une fonction au constructeur (et pas seulement utiliser implicitement le constructeur par défaut pour T). Notez que cela signifie que vous devez vous assurer que votre fonction d'initialisation elle-même est threadsafe car elle peut être exécutée par plusieurs threads simultanément.

Avec AsyncLazy<T>, you have to use the AsyncLazyFlags.RetryOnFailure flag. Puisque AsyncLazy<T> est essentiellement Lazy<Task<T>>, la tâche d'initialisation asynchrone est "partagée" entre tous les appelants simultanés, puis remplacée atomiquement par une nouvelle instance Lazy<Task<T>> en cas d'échec. La fonction d'initialisation asynchrone n'a donc pas besoin d'être threadsafe.

Depuis l'obtention de ce bien (surtout pour plusieurs singletons) est de copier-pasteish plutôt, j'abstraire ceci pour the AF project I'm working on:

Il a fallu un certain temps pour arriver à ce point, mais je suis plutôt satisfait de la façon dont cela s'est déroulé. Je voulais aussi bloguer à ce sujet ...

+2

Stephen merci pour la bonne réponse. Que diriez-vous de venir sur mon podcast un jour pour en parler et promouvoir votre travail :) –

+0

@MatthewGroves: J'aimerais, merci! J'ai reçu une erreur du formulaire de courrier électronique sur votre site Web, mais n'hésitez pas à m'envoyer un email - [mon adresse est sur mon site] (https://stephencleary.com/contact/). –

+0

Les URL dans votre réponse sont 404 maintenant - une chance que vous pourriez les corriger ou afficher le code dans votre réponse? – Cocowalla