Comparatif des performances d’Azure Webjobs et Azure Functions

Initialement publié sur blog-tech.younited-credit.com le 29/03/2018.

Dernièrement nous avons dû développer une application chargée d’envoyer des emails et SMS à nos clients. Chaque envoi est déclenché par la présence d’un message dans une file d’attente Azure Service Bus. Comme la plupart des composants de notre système d’informations, notre application qui consomme ces messages pour envoyer les mails et SMS sera hébergée dans Azure, et il existe plusieurs façons de l’implémenter : notamment les Azure Webjobs et les Azure Functions.

Jusqu’à présent nous avons surtout implémenté nos batchs et autres jobs sous la forme d’Azure Webjobs, ce nouveau projet était l’occasion de jeter un œil aux Azure Functions et comparer leurs performances à celles des Webjobs. En effet, la logique d’envoi de mails et SMS ne demande pas des ressources énormes en soi, mais nous avons périodiquement de gros volumes de mails à envoyer (plusieurs dizaines de milliers), et nous voulons éviter qu’une congestion ne se forme au niveau de la file de messages et que les mails arrivent à leur destinataire trois jours après leur date d’envoi prévu.

L’objet de notre test est donc de déterminer quel type d’applications permet de consommer le plus rapidement un gros volume de messages.

Nous allons mesurer le temps nécessaire à un Azure Webjob et à une Azure Function pour consommer 100 000 messages présents dans une file.

Les applications du test feront exactement la même chose : elles liront les messages présents dans une file… et c’est tout. Elles seront instrumentées avec Application Insights, ce qui permettra de collecter automatiquement la durée du traitement de chaque message.

Voici les différentes étapes du test :

  • Insérer 100 000 messages dans la file.
  • Une fois que les 100 000 messages sont dans la file, démarrer l’application testée.
  • Mesurer le temps mis par l’application pour consommer l’intégralité des messages.

Un exemple de message :

Le test sera joué dans trois configurations différentes :

  • Avec un Azure Webjob hébergé dans un service plan de type « Standard : 1 Medium (S2) ». Le nombre de messages traités en parallèle est 16, la valeur par défaut.
  • Avec une Azure Function hébergée dans un service plan de type « Standard : 1 Medium (S2) ».
  • Avec une Azure Function hébergée dans un service plan de type « Consumption ». Ce type de plan n’est pas disponible pour les Azure Webjobs, il permet la mise à l’échelle automatique des Azure Functions en cas d’augmentation ou de diminution de la charge de travail à effectuer.

Pour ce qui est de l’infrastructure, l’environnement de test est le suivant :

  • Un Azure service bus de niveau « Standard » contenant une seule file de messages.
  • Les options suivantes sont activées sur la file de message :
  • Enable Batched Operations.
  • Enable Dead Lettering On Message Expiration.
  • Enable Partitioning.

Voici les temps mesurés à l’aide d’Application Insights pour les trois exécutions du test.

Performances de l’Azure Webjob sur un plan S2
Performances de l’Azure Function sur un consumption plan
Performances de l’Azure Function sur un plan S2

Les résultats montrent que l’Azure Function est clairement supérieure à l’Azure Webjob :

  • Sur le même type de service plan (S2), l’Azure Function met en moyenne 476 fois moins de temps à traiter un message que l’Azure Webjob (1,45 ms contre 691ms), et elle est 12 fois plus rapide que lui à consommer la totalité des 100 000 messages (10 minutes contre 2 heures et 8 minutes).
  • Lorsque l’Azure Function est dans un plan de type Consumption, elle est moins performante dans le traitement unitaire des messages que lorsqu’elle s’exécute sur un plan S2 (3,85ms en moyenne par message au lieu de 1,45ms), mais elle reste malgré tout 179 fois plus rapide que l’Azure Webjob. C’est au niveau de la durée totale que le fait d’utiliser un plan de type Consumption se montre plus avantageux : il faut à peine deux minutes à l’Azure Function pour défiler les 100 000 messages lorsqu’elle s’exécute sur un Consumption plan, elle est alors 64 fois plus rapide que l’Azure Webjob.

A l’aide des temps moyens de traitement d’un message, on peut comparer l’efficacité de la parallélisation dans le traitement des messages pour chacune des configurations. On calcule le temps total que prendrait la lecture des 100 000 messages s’ils étaient consommés l’un après l’autre, puis le ratio entre cette durée théorique et la durée réelle :

Dans le cas d’un Webjob, la parallélisation permet de consommer les 100 000 messages 9 fois plus vite que s’il n’y avait qu’un seul consommateur séquentiel.

L’Azure Function sur un Consomption plan est, elle, un peu plus de 3 fois plus rapide.

Etrangement, lorsque l’Azure Function s’exécute sur un plan standard, la durée mesurée de traitement des 100 000 messages est 4 fois plus longue que si elle travaillait séquentiellement, comme si l’Azure Function passait environ 75% du temps à attendre.

Au niveau des performances pures, l’Azure Function est bien plus intéressante que l’Azure Webjob avec une capacité à défiler une centaine de milliers de messages en à peine dix minutes lorsqu’il faut plusieurs heures au Webjob.

L’exécution d’une Azure Function sur un plan de type Consumption est encore plus efficace, car même s’il lui faut plus de temps pour consommer un seul message que sur un plan de type S2, la capacité de mise à l’échelle automatique de cet environnement permet de paralléliser au maximum le traitement des messages et de diviser par cinq le temps total de traitement. C’est sur cette configuration que nous sommes partis pour implémenter notre solution, cela nous permettra d’absorber en très peu de temps de grands volumes de demandes d’envois de mails et SMS.

Notre choix d’implémenter une Azure Function hébergée sur un service plan de type Consumption a été guidé par un besoin de performances, mais dans d’autres contextes un Azure Webjob peut être suffisant dans un premier temps (il n’est de toute façon pas très compliqué de transformer un Webjob en une Azure Function). Lors du développement de l’Azure Function, nous avons été ralentis par les points suivants :

Références :