Published by

Il y a 1 semaine -

Temps de lecture 12 minutes

Real-Time Data Processing : Google Cloud Functions & Firebase

Aujourd’hui, la transformation numérique est plus présente, voire même indispensable afin de pouvoir se créer une place dans le futur qui nous attend. Nous y faisons déjà face et la nécessité de passer à des architectures Event-Driven
est grandissante. Certains outils de cloud providers tel que GCP, AWS ou Azure peuvent alors nous aider, afin de relever ces défis.

Dans cet article, nous présentons ici une manière d’effectuer le traitement de données textuelles en temps réel afin d’exposer les mots-clés de la page data du blog de PS Engineering suite à un événement via les Cloud Functions et Firebase Realtime Database.

Cloud Functions: Cas d’utilisation et Fonctionnement

Cloud Functions est un service “serverless” de Google Cloud Plateform (GCP). Il permet de gérer divers évènements (events) par l’intermédiaire de déclencheurs (triggers):

  • Cloud Storage : un fichier/objet a été uploadé, supprimé ou bien archivé
  • Cloud Pub/Sub : un message est publié dans un topic Pub/Sub
  • HTTP : un appel direct via une requête HTTP (exemple: GET ou POST)
  • Firebase : un changement s’est produit par rapport à l’état de la base de données Firebase (exemple : ingestion de données ou suppression de données)

Le fonctionnement est assez simple, les Cloud Functions réagissent à l’un des événements ci-dessus en déclenchant le code qui leur aura été associé préalablement. Ainsi, elles offrent l’ensemble des ingrédients pour construire une application (ou micro-service) suivant une approche dite Event-Driven.

Cependant, certaines contraintes sont à connaître telles que :

  • Les langages utilisables : concernant le code source à associer aux Cloud Functions, seuls les langages Python, Node.js, Go et Java sont supportés
  • Le temps d’exécution : il ne peut pas dépasser les 60s par défaut (jusqu’à 540)
  • La mémoire allouée à une Cloud Function : 256 Mio par défaut (128 Mio à 2 Gio)
  • La taille du code source : elle est soumise à certaines limites (voir le lien ci-dessous)

Pour plus d’information, vous pouvez consulter la page ressources de la documentation du service Cloud Function.

À noter, le coût prend en compte des variables comme le temps d’utilisation (toutes les 100 ms) ou bien la quantité de ressources fournies par GCP en autres. Vous trouverez des informations plus détaillées ici

Firebase Realtime Database: Pourquoi ?

Nous n’allons pas nous épancher sur toutes les possibilités qu’offre Firebase car ce n’est pas l’objectif de cet article mais sachez que Firebase est un produit de Google offrant de nombreuses fonctionnalités telles que des bases de données ou de la messagerie pour ne citer que ceux-là.

Ici, nous parlerons de Firebase Realtime Database.

Firebase Realtime Database est une base de données Cloud NoSQL donnant la possibilité de stocker des données tout en les gardant fraîches grâce à sa capacité de synchronisation en temps réel. En effet, cette spécialisation lui permet de faire partie des outils (base de données ou stockage) capables de servir par exemple une web application ou bien une application mobile en quasi temps réel, procurant alors constamment un contenu identique à la database sans avoir à fournir aucun effort afin d’obtenir cette “fraîcheur de contenus”.

L’accès aux données se fait au travers d’une “reference” (localisation ou bien nœud), semblable à une clé d’un fichier JSON et qui peut avoir une ou plusieurs valeurs associées dites “child”. Lorsque l’on souhaite accéder à une donnée via Python par exemple, on se localise d’abord sur une “reference”, ce qui permet ensuite de récupérer les données contenues sous forme d’un dictionnaire.

C’est pourquoi, elle est souvent désignée par la mention “realtime JSON database”.

Pour plus d’information concernant le coût de Firebase Realtime Database, consulter le lien ici.

 

Real-Time Event-Driven Application

Présentation de notre application

À présent, nous allons voir comment créer un micro-service interagissant en temps réel avec une base de données ainsi qu’un algorithme procédant à du traitement de texte. À l’aide de méthodes utilisées en machine learning, l’objectif est d’obtenir puis exposer les mots-clés d’une page regroupés par balises HTML. Afin d’effectuer cela, nous avons choisi de coupler Firebase Realtime Database et les Cloud Functions.

La démarche est la suivante :

  • Création d’une 1ère Cloud Function déclenchée par appel HTTP afin d’ingérer la donnée dans Firebase
  • Création d’une 2ème Cloud Function déclenchée suite à l’événement ci-dessus afin d’appliquer la tâche de traitement de texte (text processing) puis d’ingérer le résultat obtenu

Comment déployer une Cloud Function

Le déploiement d’une Cloud Function se déroule en deux temps :

  1. Définition de la configuration
  2. Chargement du code à exécuter

Étape 1 :

La partie configuration se définit ainsi :

  • Nom de la Cloud Function
  • Région : la localisation des utilisateurs joue sur le temps de réponse
  • Déclencheur (ou “trigger”) : choisir entre différents types de déclenchement
  • Type d’authentification :
    • Public : il permet à n’importe quel utilisateur d’appeler la Cloud Function
    • Private : il permet à travers Cloud IAM de définir les utilisateurs qui pourront appeler la Cloud Function
  • Mémoire allouée
  • Inactif : ce paramètre définit le temps limite d’exécution avant que la Cloud Function s’interrompe
  • Nombre d’instances de fonctions :
    • Ce paramètre définit le nombre d’instances maximale à créer lorsque la charge des requêtes devient trop importante à encaisser pour une Cloud Function (par défaut la case est vide, ce qui signifie que ce nombre est défini automatiquement en fonction de la situation)
    • À noter, que laisser ce paramètre en automatique peut devenir assez couteux si un nombre très important d’instances est créé sur une longue durée
  • Variables d’environnement : variables d’environnement disponibles pendant l’exécution de la fonction.
  • Connexion : choisir entre la valeur “Allow all traffic” ou bien “Allow internal traffic only” pour le paramètre d’entrée (par défaut, la valeur est fixée à “Allow all traffic”)

Étape 2 :

Pour la partie code (Python en exemple) :

  • Environnement d’exécution : choisir parmi les langages disponibles (ici Python)
  • Code source :
    • Éditeur intégré : renseigner directement deux fichiers (un fichier python main.py et un fichier requirements.txt)
    • Importer un fichier ZIP : importer un fichier local zip contenant les deux fichiers cités ci-dessus
    • Fichier ZIP depuis Cloud Storage : importer un fichier zip depuis le Cloud Storage contenant les deux fichiers cités ci-dessus
    • Dépôt source Cloud : choisir lorsque vous utilisez Cloud Source Repositories afin de « versionner » votre code
  • Point d’entrée : le nom de la fonction à exécuter dans le fichier main.py (en Python)

Il est également possible de déployer une Cloud Function via le Cloud Shell ou Cloud SDK à travers des commandes gcloud. Cette commande doit être exécutée dans le répertoire où se situe votre fichier main.py, dans le cas où vous n’utiliseriez pas le paramètre --stage-bucket afin d’indiquer l’emplacement de votre fichier .zip. En autres, ces commandes vont différer en termes de paramètres selon le type de déclenchement (trigger) choisi.

Pour plus d’information dessus, consulter le lien ici.

 

Déploiement de nos deux Cloud Functions

Cloud Function n°1:

La configuration pour cette Cloud Function est la suivante :

 

Le code source de cette Cloud Function est le suivant :

Sachant que le déclencheur de cette Cloud Function est en HTTP, nous gérons cela avec des “requests” (ici, une requête GET).

from firebase_admin import db
import firebase_admin

firebase_admin.initialize_app(options={
    'databaseURL': os.environ.get(
        'FIREBASE_DATABASE_URL'
    ),
})

DATA_SEARCHES = db.reference('data-searches')

Cette partie du code nous permet de nous connecter à notre base de données Firebase à partir de la variable d’environnement nommée FIREBASE_DATABASE_URL (cela est à renseigner dans la partie “VARIABLES, NETWORKING AND ADVANCED SETTINGS” lors de la création de la Cloud Function).

Une fois la connexion établie, nous pointons (fonction : “reference”) sur la localisation (“clé”) de notre choix de la base de données par l’intermédiaire de la variable “db” (si cette localisation n’existe pas déjà, elle est créée automatiquement).

Enfin, pour notre cas pratique, nous ingérons la donnée directement dans Firebase. Ceci s’effectue à partir de la fonction “push”, qui s’applique sur une localisation (“clé”) de la base de données et prend en entrée la donnée à ingérer sous forme de dictionnaire.

def create_session(url_html):
    start = datetime.datetime.now()
    data = {'data-to-process': url_html, 'startDate': start.strftime('%Y-%m-%d %H:%M:%S')}
    data_search = DATA_SEARCHES.push(data)

    return data_search.key

À noter, la fonction “push” insère une donnée sous une localisation (session_id) contrairement à la fonction “set” (nous l’utiliserons plus tard) qui insère la donnée directement sous la localisation (“clé”) pointée. Pour plus d’information, consulter le guide d’utilisation de Firebase Realtime Database.

En outre, il est également possible de déployer cette fonction à l’aide d’une commande gcloud :

gcloud functions deploy ingest_part --entry-point process_data --region=europe-west1 --runtime python37 --trigger-http --allow-unauthenticated

Passons à présent au déploiement de la 2ème Cloud Function.

Cloud Function n°2 :

La configuration est quelque peu différente de la fonction précédente. En effet, lorsque pour le type de déclencheur l’option « Firebase Realtime Database » est choisie, des champs additionnels sont à compléter :

  • Type d’événement : le type de l’évènement déclencheur de la Cloud Function
  • Base de données : le nom de votre « Firebase Realtime Database »
  • Chemin : la localisation (“clé”) de la base de données initiant le déclenchement de la Cloud Function

Le code source de cette Cloud Function est le suivant :

from firebase_admin import db
import firebase_admin
import os

from preprocessing.main import Preprocessor
from processing.main import Processor 

def process_data(event, context):
resource_string = context.resource 
session_id = resource_string.split('/data-searches/')[1].split('/data-to-process')[0] 
raw_html = event["delta"] 

send_results(session_id, raw_html)

Cette partie du code permet de lier l’évènement déclencheur à cette Cloud Function.

Ceci est possible par l’intermédiaire des paramètres :

  • Context : ce paramètre permet de percevoir le contexte de l’évènement et donc de récupérer dans notre cas l’id de la session

Exemple :

{event_id: ES5N0DZLPi9cLrmoLWNl1PrzMYY=,

timestamp: 2020-09-24T08:52:34.827Z,

event_type: providers/google.firebase.database/eventTypes/ref.create,

resource: projects/_/instances/gcp_project_id/refs/data-searches/-MHzJ6D9EpDSLEkaz-mq/data-to-process}

  • Event : ce paramètre est un dictionnaire permettant de récupérer la donnée à la localisation du chemin désignée (ici “data-to-process”) lors de la configuration de la Cloud Function dans la partie Déclencheur.

Exemple :

{‘data’: la donnée à l’instant d’avant l’événement (“None” si le trigger était un “oncreate”), ‘delta’: la donnée post l’événement}

Pour plus d’information dessus, consulter la page Déclencheurs Firebase Realtime Database de la documentation des Cloud Functions.

def send_results(session_id, raw_html):
prepro = Preprocessor() 
corpus, words_to_remove_sitename = prepro.preprocessing(raw_html) 

processor = Processor() 
corpus_processing_results = processor.corpus_processing(corpus, words_to_remove_sitename) 

formated_result = format_result(corpus_processing_results) 

data_search = db.reference('data-searches/' + session_id + "/generate-output") 
data_search.set({
 'data-processed': formated_result,
 'status': 'done'
 })

Ensuite, nous pointons vers une nouvelle localisation (“clé”) à laquelle nous assignons la valeur retournée par notre algorithme de text processing (dans le code ci-dessus, la fonction corpus_processing de notre classe Processor) ainsi qu’une valeur “status” représentant l’état de la Cloud Function (optionnel).

La commande pour déployer cette fonction est la suivante :

gcloud functions deploy text_processing --entry-point process_data --trigger-event providers/google.firebase.database/eventType/ref.create --trigger-resource projects/_/instances/GCP_PROJECT_ID/refs/data-searches/-MHzJ6D9EpDSLEkaz-mq/data-to-process process_data --region=europe-west1 --runtime python37

Les deux Cloud Functions sont disponibles ainsi que la base de données Firebase, nous pouvons à présent lancer une simulation de notre cas d’usage.

Application

Comme énoncé au début, nous sommes sur une requête GET par simplicité pour l’article. En effet, nous aurions pu récupérer l’URL de la page à traiter puis récupérer son code HTML (notre data-to-process) mais nous avons préféré lire un fichier le contenant déjà.

La syntaxe permettant d’effectuer un appel HTTP d’une Cloud Function est la suivante : https://regiongcp_project_id-cloudfunctions.net/nom_cloud_function

Nous utilisons donc cela afin de lancer la simulation :

Il est possible de visualiser la base de données en temps réel, voici comment elle se présente :

Nous pouvons observer les principaux sujets présents sur la page data de notre blog à travers les “mots-clés” composant les balises H2 de cette page.

Conclusion

L’association de Firebase Realtime Database et de Cloud Functions est une alternative parmi d’autres afin de réaliser des micro-services Event-Driven et en Real-Time.

En effet, en se projetant, nous pourrions par exemple créer une web application qui suite à la recherche (event) d’un utilisateur spécifiant une page web (trigger), irait détecter les “mots-clés” par balises HTML (tel que nous l’avons effectué dans cet article) afin de les afficher. Et tout cela, en temps réel, tout en profitant du statut serverless des Cloud Functions et de leur complicité avec Firebase Realtime Database afin d’afficher sur une page les résultats de chacune des recherches.

Published by

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.