Écrire des fonctions Cloud Functions

Les fonctions Cloud peuvent être écrites en Node.js, Python, Go et Java. Elles sont exécutées dans des environnements d'exécution spécifiques à chaque langage. L'environnement d'exécution de Cloud Functions varie selon l'environnement d'exécution choisi. Les pages de présentation de l'environnement d'exécution fournissent des informations supplémentaires sur chaque environnement d'exécution :

Types de fonctions cloud

Il existe deux types distincts de fonctions Cloud : les fonctions HTTP et les fonctions d'arrière-plan.

Fonctions HTTP

Les fonctions HTTP sont appelées à partir de requêtes HTTP standards. Ces requêtes HTTP attendent la réponse et sont compatibles avec les méthodes de requêtes HTTP courantes telles que GET, PUT, POST, DELETE et OPTIONS. Lors de l'utilisation des fonctions Cloud, un certificat TLS permettant d'appeler toutes les fonctions HTTP via une connexion sécurisée est automatiquement fourni.

Pour en savoir plus, consultez la section Écrire des fonctions HTTP.

Exemple :

Node.js

const escapeHtml = require('escape-html');

/**
 * HTTP Cloud Function.
 *
 * @param {Object} req Cloud Function request context.
 *                     More info: https://expressjs.com/en/api.html#req
 * @param {Object} res Cloud Function response context.
 *                     More info: https://expressjs.com/en/api.html#res
 */
exports.helloHttp = (req, res) => {
  res.send(`Hello ${escapeHtml(req.query.name || req.body.name || 'World')}!`);
};

Python

from flask import escape

def hello_http(request):
    """HTTP Cloud Function.
    Args:
        request (flask.Request): The request object.
        <https://flask.palletsprojects.com/en/1.1.x/api/#incoming-request-data>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response>.
    """
    request_json = request.get_json(silent=True)
    request_args = request.args

    if request_json and 'name' in request_json:
        name = request_json['name']
    elif request_args and 'name' in request_args:
        name = request_args['name']
    else:
        name = 'World'
    return 'Hello {}!'.format(escape(name))

Go


// Package helloworld provides a set of Cloud Functions samples.
package helloworld

import (
	"encoding/json"
	"fmt"
	"html"
	"net/http"
)

// HelloHTTP is an HTTP Cloud Function with a request parameter.
func HelloHTTP(w http.ResponseWriter, r *http.Request) {
	var d struct {
		Name string `json:"name"`
	}
	if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
		fmt.Fprint(w, "Hello, World!")
		return
	}
	if d.Name == "" {
		fmt.Fprint(w, "Hello, World!")
		return
	}
	fmt.Fprintf(w, "Hello, %s!", html.EscapeString(d.Name))
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Logger;

public class HelloHttp implements HttpFunction {
  private static final Logger logger = Logger.getLogger(HelloHttp.class.getName());

  private static final Gson gson = new Gson();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    // Check URL parameters for "name" field
    // "world" is the default value
    String name = request.getFirstQueryParameter("name").orElse("world");

    // Parse JSON request and check for "name" field
    try {
      JsonElement requestParsed = gson.fromJson(request.getReader(), JsonElement.class);
      JsonObject requestJson = null;

      if (requestParsed != null && requestParsed.isJsonObject()) {
        requestJson = requestParsed.getAsJsonObject();
      }

      if (requestJson != null && requestJson.has("name")) {
        name = requestJson.get("name").getAsString();
      }
    } catch (JsonParseException e) {
      logger.severe("Error parsing JSON: " + e.getMessage());
    }

    var writer = new PrintWriter(response.getWriter());
    writer.printf("Hello %s!", name);
  }
}

Fonctions d'arrière-plan

Les fonctions d'arrière-plan permettent de gérer des événements de votre infrastructure Cloud, tels que des messages sur un sujet Pub/Sub ou des modifications dans un bucket Cloud Storage.

Pour en savoir plus, consultez la section Écrire des fonctions d'arrière-plan.

Exemple :

Node.js

/**
 * Background Cloud Function to be triggered by Pub/Sub.
 * This function is exported by index.js, and executed when
 * the trigger topic receives a message.
 *
 * @param {object} message The Pub/Sub message.
 * @param {object} context The event metadata.
 */
exports.helloPubSub = (message, context) => {
  const name = message.data
    ? Buffer.from(message.data, 'base64').toString()
    : 'World';

  console.log(`Hello, ${name}!`);
};

Python

def hello_pubsub(event, context):
    """Background Cloud Function to be triggered by Pub/Sub.
    Args:
         event (dict):  The dictionary with data specific to this type of
         event. The `data` field contains the PubsubMessage message. The
         `attributes` field will contain custom attributes if there are any.
         context (google.cloud.functions.Context): The Cloud Functions event
         metadata. The `event_id` field contains the Pub/Sub message ID. The
         `timestamp` field contains the publish time.
    """
    import base64

    print("""This Function was triggered by messageId {} published at {}
    """.format(context.event_id, context.timestamp))

    if 'data' in event:
        name = base64.b64decode(event['data']).decode('utf-8')
    else:
        name = 'World'
    print('Hello {}!'.format(name))

Go


// Package helloworld provides a set of Cloud Functions samples.
package helloworld

import (
	"context"
	"log"
)

// PubSubMessage is the payload of a Pub/Sub event.
type PubSubMessage struct {
	Data []byte `json:"data"`
}

// HelloPubSub consumes a Pub/Sub message.
func HelloPubSub(ctx context.Context, m PubSubMessage) error {
	name := string(m.Data) // Automatically decoded from base64.
	if name == "" {
		name = "World"
	}
	log.Printf("Hello, %s!", name)
	return nil
}

Java


import com.google.cloud.functions.BackgroundFunction;
import com.google.cloud.functions.Context;
import functions.eventpojos.PubSubMessage;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;

public class HelloPubSub implements BackgroundFunction<PubSubMessage> {
  private static final Logger logger = Logger.getLogger(HelloPubSub.class.getName());

  @Override
  public void accept(PubSubMessage message, Context context) {
    String name = "world";
    if (message != null && message.getData() != null) {
      name = new String(
          Base64.getDecoder().decode(message.getData().getBytes(StandardCharsets.UTF_8)),
          StandardCharsets.UTF_8);
    }
    logger.info(String.format("Hello %s!", name));
    return;
  }
}

Structurer le code source

Pour que Cloud Functions trouve la définition de votre fonction, chaque environnement d'exécution a certaines exigences concernant la structuration de votre code source.

Node.js

Dans l'environnement d'exécution Node.js, le code source de votre fonction doit être exporté depuis un module Node.js, que Cloud Functions charge à l'aide d'un appel require(). Pour déterminer le module à charger, Cloud Functions utilise le champ main de votre fichier package.json. Si le champ main n'est pas spécifié, Cloud Functions charge le code à partir de index.js.

Par exemple, les configurations de code source suivantes sont valides :

  • Un seul fichier index.js situé dans le répertoire racine de votre fonction qui exporte une ou plusieurs fonctions :

    .
    └── index.js
    
  • Un fichier index.js qui importe le code à partir d'un fichier foo.js, puis exporte une ou plusieurs fonctions :

    .
    ├── index.js
    └── foo.js
    
  • Un fichier app.js qui exporte une ou plusieurs fonctions, accompagné d'un fichier package.json contenant "main": "app.js" :

    .
    ├── app.js
    └── package.json
    

Python

Dans l'environnement d'exécution Python, le point d'entrée de votre fonction doit être défini dans un fichier source Python situé à la racine de votre projet et nommé main.py.

Par exemple, les configurations de code source suivantes sont valides :

  • Un seul fichier main.py dans le répertoire racine de votre fonction, qui définit une ou plusieurs fonctions :

    .
    └── main.py
    
  • Un fichier main.py accompagné d'un fichier requirements.txt qui spécifie des dépendances :

    .
    ├── main.py
    └── requirements.txt
    
  • Un fichier main.py qui importe le code à partir d'une dépendance locale :

    .
    ├── main.py
    └── mylocalpackage/
        ├── __init__.py
        └── myscript.py
    

Go

Dans l'environnement d'exécution Go, votre fonction doit se trouver dans un package Go à la racine de votre projet. Votre fonction ne peut pas se trouver dans package main. Les sous-packages ne sont compatibles que lors de l'utilisation de modules Go.

Par exemple, les configurations de code source suivantes sont valides :

  • Un package à la racine du projet qui exporte une ou plusieurs fonctions :

    .
    └── function.go
    
  • Un package à la racine du projet qui importe le code d'un sous-package et exporte une ou plusieurs fonctions :

    .
    ├── function.go
    ├── go.mod
    └── shared/
        └── shared.go
    
  • Un package à la racine de votre projet avec un sous-répertoire qui définit un package main :

    .
    ├── cmd/
    |   └── main.go
    └── function.go
    

Java

Dans l'environnement d'exécution Java, vous devez créer un répertoire de fonction de premier niveau contenant un sous-répertoire src/main/java/ et un fichier pom.xml. Nous vous recommandons de placer les tests dans un sous-répertoire src/test/java/.

.
├── pom.xml
└── src/
    ├── main/
    |   └── java/
    |       └── MyFunction.java
    └── test
        └── java/
            └── MyFunctionTest.java

Si votre fichier .java déclare un package (par exemple, functions), votre hiérarchie de répertoires se présente comme suit :

.
├── pom.xml
└── src/
    ├── main/
    |   └── java/
    |       └── functions/
    |               └── MyFunction.java
    └── test/
        └── java/
                └── functions/
                    └── MyFunctionTest.java

Si votre fonction est définie dans un package spécifique comme la plupart des fonctions Java, elle doit être spécifiée par la valeur --entry-point au moment du déploiement.

Regrouper plusieurs fonctions

Si vous envisagez de regrouper plusieurs fonctions dans un seul projet, sachez qu'il est possible qu'elles partagent le même ensemble de dépendances. Toutefois, certaines fonctions ne nécessitent pas l'ensemble des dépendances partagées.

Comme indiqué ci-dessus, nous vous recommandons de placer chaque fonction dans son propre répertoire de premier niveau, avec un sous-répertoire src/main/java et un fichier pom.xml dédiés. Cette approche minimise le nombre de dépendances requises par une fonction particulière et réduit la quantité de mémoire dont votre fonction a besoin.

En outre, des fonctions distinctes facilitent la spécification d'une fonction lors de l'exécution locale via le framework des fonctions. Cela peut être utile lors du développement et des tests locaux.

Spécifier des dépendances

Vous spécifiez les dépendances de votre fonction de manière idiomatique en fonction de l'environnement d'exécution que vous utilisez. Pour plus d'informations, consultez la page appropriée :

Nommer les fonctions cloud

Les fonctions Cloud ont une propriété "name" définie au moment du déploiement et, une fois définie, celle-ci ne peut plus être modifiée. Le nom d'une fonction est utilisé comme identifiant et doit être unique dans une région. Consultez la documentation sur le déploiement pour en savoir plus.

Étapes suivantes