Transcoder du contenu HTTP/JSON en gRPC

Cloud Endpoints est compatible avec le transcodage de protocole, ce qui permet aux clients d'accéder à votre API gRPC en HTTP/JSON. Extensible Service Proxy (ESP) transcode les contenus HTTP/JSON en gRPC.

Ce guide vous explique :

  • comment utiliser des annotations dans votre fichier .proto pour spécifier la conversion de données HTTP/JSON en gRPC ;
  • comment déployer votre service dans Endpoints pour utiliser cette fonctionnalité ;
  • où trouver plus d'informations de référence sur la conception et la mise en œuvre du transcodage pour les services gRPC.

Nous partons ici du principe que vous avez déjà terminé nos tutoriels gRPC et que vous maîtrisez les concepts de base de Endpoints pour les API gRPC.

Concevoir une API permettant le transcodage

Le transcodage implique de mapper des requêtes HTTP/JSON et leurs paramètres sur les méthodes gRPC et leurs propres paramètres et types de renvois. De ce fait, même s'il est possible de mapper une requête HTTP/JSON sur n'importe quelle méthode API arbitraire, le mappage est facilité si l'API gRPC est elle-même structurée de manière à être orientée ressources, tout comme une API REST HTTP traditionnelle. En d'autres termes, vous devez concevoir le service API de manière à ce qu'il utilise un petit nombre de méthodes standards correspondant à des verbes HTTP tels que GET et PUT, qui opèrent sur les ressources du service et sur des collections de ressources, qui constituent elles-mêmes un type de ressource. Ces méthodes standards sont List, Get, Create, Update et Delete.

Si nécessaire, l'API peut également comporter des méthodes personnalisées non standards, bien que celles-ci ne soient pas aussi simples à mapper.

Pour plus d'informations sur la conception orientée ressources et les mappages de transcodage standards, consultez notre guide de conception d'API. Il s'agit de la norme suivie par Google lors de la conception d'API publiques telles que les API Cloud. Nous vous recommandons vivement ce guide, même s'il est possible d'utiliser le transcodage gRPC sans s'y conformer. En particulier, les pages suivantes vous aideront à comprendre ces principes de conception et à ajouter des mappages de transcodage utiles à vos méthodes :

La page de référence suivante peut également être utile :

Dans la suite de ce document, vous vous servirez de l'exemple Bookstore que vous avez utilisé dans les tutoriels et qui repose déjà sur ces principes. La librairie dispose de collections "shelf" (étagère) de ressources "book" (livre), que les utilisateurs peuvent répertorier (List), récupérer (Get), créer (Create) ou supprimer (Delete).

Où configurer le transcodage

Le transcodage gRPC est activé par défaut et vous pouvez l'utiliser sans configuration de votre part. Suivez les instructions pour déployer un service utilisant le transcodage. Ensuite, lorsque vous envoyez une requête HTTP POST au chemin d'URL GRPC_SERVICE_FULL_NAME/METHOD_NAME> avec, pour le champ de message de requête correspondant à la méthode, des valeurs (le cas échéant) sous forme de contenus JSON dans le corps de la requête HTTP, ESP envoie le message de requête à la méthode gRPC appropriée. Dans l'exemple précédent, GRPC_SERVICE_FULL_NAME est le nom complet de votre service gRPC tandis que METHOD_NAME est le nom de la méthode.

Par exemple, si vous envoyez une requête POST à l'URL ListShelves de Bookstore comme suit :

curl -XPOST http://mydomain/endpoints.examples.bookstore.Bookstore/ListShelves
    

… vous devriez obtenir une liste à jour des étagères au format JSON.

Cependant, en termes de conception d'interface HTTP, il est nettement préférable de configurer explicitement les mappages, comme décrit dans la suite de ce document.

La norme de configuration de l'API gRPC pour la configuration de service vous permet de spécifier exactement comment les données HTTP/JSON doivent être converties en gRPC. Deux mécanismes sont disponibles pour spécifier cette conversion : soit des annotations directes dans le fichier .proto, soit des instructions en YAML insérées dans le fichier de configuration de l'API gRPC. Nous vous recommandons d'utiliser les annotations du fichier proto, ce qui facilite la lecture et la maintenance. Pour plus d'informations sur la configuration en YAML et sur les cas où vous devrez peut-être l'utiliser, consultez la section Configurer le transcodage en YAML.

Voici un exemple issu de Bookstore et basé sur l'approche que nous recommandons :

// Returns a specific bookstore shelf.
    rpc GetShelf(GetShelfRequest) returns (Shelf) {
      // Client example - returns the first shelf:
      //   curl http://DOMAIN_NAME/v1/shelves/1
      option (google.api.http) = { get: "/v1/shelves/{shelf}" };
    }

    ...
    // Request message for GetShelf method.
    message GetShelfRequest {
      // The ID of the shelf resource to retrieve.
      int64 shelf = 1;
    }
    

L'annotation indique à ESP qu'une requête HTTP GET avec l'URL http://mydomain/v1/shelves/1 appelle la méthode GetShelf() du serveur gRPC, avec un GetShelfRequest contenant dans le champ shelf l'ID d'étagère demandé (dans ce cas, 1).

Ajouter des mappages de transcodage

Cette section décrit quelques annotations de mappage supplémentaires, toujours en se basant sur l'exemple Bookstore. L'exemple Bookstore contient deux exemples de fichiers proto afin que vous puissiez le déployer avec ou sans les mappages de transcodage et comparer les différences entre les deux fichiers proto :

Pour des informations plus complètes sur la spécification de mappages de transcodage, consultez les rubriques Méthodes standards et Méthodes personnalisées, ainsi que les informations de référence relatives aux règles HTTP.

Mapper une méthode List

La méthode List est définie dans le fichier .proto avec son type de réponse :

      // Returns a list of all shelves in the bookstore.
      rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {
        // Define HTTP mapping.
        // Client example (Assuming your service is hosted at the given 'DOMAIN_NAME'):
        //   curl http://DOMAIN_NAME/v1/shelves
        option (google.api.http) = { get: "/v1/shelves" };
      }
    ...
    message ListShelvesResponse {
      // Shelves in the bookstore.
      repeated Shelf shelves = 1;
    }
    

L'annotation en gras spécifie le mappage HTTP pour cette méthode.

  • option (google.api.http) indique qu'il s'agit d'une annotation de mappage HTTP/gRPC.
  • get indique que cette méthode est mappée sur une requête GET HTTP.
  • "/v1/shelves" est le modèle de chemin d'URL (ajouté au domaine de votre service) que la requête GET utilise pour appeler cette méthode. Il est également appelé chemin de ressource, car il spécifie généralement l'objet ou la ressource que vous souhaitez utiliser : dans ce cas, toutes les ressources "shelf" de notre Bookstore.

Ainsi, par exemple, si un client appelle cette méthode en envoyant une requête GET à l'URL http://mydomain/v1/shelves, ESP appelle la méthode gRPC ListShelves(). Le backend gRPC renvoie ensuite les étagères (shelves). ESP les convertit au format JSON avant de les renvoyer au client.

Mapper une méthode Get

La méthode GetShelf de Bookstore est définie dans le fichier .proto ci-dessous avec ses types de requête et de réponse :

    // Returns a specific bookstore shelf.
    rpc GetShelf(GetShelfRequest) returns (Shelf) {
      // Client example - returns the first shelf:
      //   curl http://DOMAIN_NAME/v1/shelves/1
      option (google.api.http) = { get: "/v1/shelves/{shelf}" };
    }

    ...
    // Request message for GetShelf method.
    message GetShelfRequest {
      // The ID of the shelf resource to retrieve.
      int64 shelf = 1;
    }
    ...
    // A shelf resource.
    message Shelf {
      // A unique shelf id.
      int64 id = 1;
      // A theme of the shelf (fiction, poetry, etc).
      string theme = 2;
    }
    

L'annotation en gras spécifie le mappage HTTP pour cette méthode.

  • option (google.api.http) indique qu'il s'agit d'une annotation de mappage HTTP/gRPC.
  • get indique que cette méthode est mappée sur une requête GET HTTP.
  • "/v1/shelves/{shelf}" est, comme précédemment, le chemin d'URL de la requête, mais le chemin indiqué ici est /v1/shelves/, puis {shelf}. Cette notation entre accolades indique à ESP que ce qui se trouve dans {shelf} est la valeur qu'il doit fournir pour shelf dans le paramètre GetShelfRequest de la méthode.

Si un client appelle cette méthode en envoyant une requête GET à l'URL http://mydomain/v1/shelves/4, ESP crée un GetShelfRequest avec une valeur shelf de 4, puis l'utilise pour appeler la méthode gRPC GetShelf(). Le backend gRPC renvoie ensuite le Shelf demandé ayant l'ID 4, qu'ESP convertit au format JSON et renvoie au client.

Cette méthode ne nécessite qu'une seule valeur de champ de requête devant être fournie par le client (shelf). Nous la spécifions dans le modèle de chemin d'URL à l'aide de la notation de "capture" entre accolades. Si nécessaire, vous pouvez également capturer plusieurs parties de l'URL pour identifier la ressource demandée. Par exemple, la méthode GetBook nécessite que le client spécifie à la fois l'ID d'étagère (shelf) et l'ID de livre (book) dans l'URL :

// Returns a specific book.
    rpc GetBook(GetBookRequest) returns (Book) {
      // Client example - get the first book from the second shelf:
      //   curl http://DOMAIN_NAME/v1/shelves/2/books/1
      option (google.api.http) = { get: "/v1/shelves/{shelf}/books/{book}" };
    }
    ...
    // Request message for GetBook method.
    message GetBookRequest {
      // The ID of the shelf from which to retrieve a book.
      int64 shelf = 1;
      // The ID of the book to retrieve.
      int64 book = 2;
    }
    

Outre les littéraux et les accolades de capture de valeurs de champ, les modèles de chemin d'URL peuvent faire appel à des caractères génériques pour indiquer que tout élément figurant dans un segment donné de l'URL doit être capturé. La notation {shelf} utilisée dans l'exemple précédent est en réalité une version abrégée de {shelf=*}. Pour en savoir plus sur les règles applicables aux modèles de chemin d'accès, consultez les informations de référence relatives aux règles HTTP.

Dans le cas de ce type de méthode, il n'y a pas de corps de requête HTTP. Vous trouverez davantage de consignes relatives au mappage des méthodes Get (y compris avec des paramètres de requête) dans la rubrique Méthodes standards.

Mapper une méthode Create

La méthode CreateShelf de Bookstore est mappée sur la méthode HTTP POST.

      // Creates a new shelf in the bookstore.
      rpc CreateShelf(CreateShelfRequest) returns (Shelf) {
        // Client example:
        //   curl -d '{"theme":"Music"}' http://DOMAIN_NAME/v1/shelves
        option (google.api.http) = {
          post: "/v1/shelves"
          body: "shelf"
        };
      }
    ...
    // Request message for CreateShelf method.
    message CreateShelfRequest {
      // The shelf resource to create.
      Shelf shelf = 1;
    }
    ...
    // A shelf resource.
    message Shelf {
      // A unique shelf id.
      int64 id = 1;
      // A theme of the shelf (fiction, poetry, etc).
      string theme = 2;
    }
    
  • option (google.api.http) indique qu'il s'agit d'une annotation de mappage HTTP/gRPC.
  • post indique que cette méthode est mappée sur une requête POST HTTP.
  • "/v1/shelves" est le chemin d'URL de la requête, comme précédemment.
  • body: "shelf" est utilisé dans le corps de la requête HTTP pour spécifier, au format JSON, la ressource que vous souhaitez ajouter.

Ainsi, par exemple, si un client appelle cette méthode comme ceci :

curl -d '{"theme":"Music"}' http://DOMAIN_NAME/v1/shelves
    

ESP utilise le corps JSON pour créer une valeur Shelf associée au thème "Music" pour la requête CreateShelfRequest, puis appelle la méthode gRPC CreateShelf(). Le client ne fournit pas la valeur id pour l'objet Shelf. Les ID d'étagère de Bookstore sont fournis par le service lors de la création d'une étagère. (Ce sont des informations de ce genre que vous devez fournir aux utilisateurs de votre service dans la documentation de l'API.)

Configurer le transcodage en YAML

Une approche alternative consiste à spécifier vos mappages HTTP vers gRPC dans le fichier YAML de configuration de l'API gRPC plutôt que dans le fichier .proto. Configurer le transcodage dans un fichier YAML peut être nécessaire si vous avez une seule définition d'API proto utilisée au sein de plusieurs services, des mappages différents étant spécifiés pour chaque service.

Les règles (rules) de la section http de votre fichier YAML indiquent comment mapper des requêtes HTTP/JSON sur des méthodes gRPC :

http:
      rules:
      ...
      #
      # 'GetShelf' is available via the GET HTTP verb and '/shelves/{shelf}' URL
      # path, where {shelf} is the value of the 'shelf' field of 'GetShelfRequest'
      # protobuf message.
      #
      # Client example - returns the first shelf:
      #   curl http://DOMAIN_NAME/v1/shelves/1
      #
      - selector: endpoints.examples.bookstore.Bookstore.GetShelf
        get: /v1/shelves/{shelf}
      ...
    

Dans le cadre de notre exemple de service Bookstore, vous trouverez un fichier d'exemple plus complet basé sur cette approche ici : api_config_http.yaml.

Déployer un service utilisant le transcodage

Le déploiement d'un service gRPC utilisant le transcodage ressemble beaucoup à celui de tout autre service gRPC, avec toutefois une différence majeure. Dans les exemples des tutoriels, l'application se contentait d'accepter les requêtes gRPC provenant du client. Il faut maintenant compléter la configuration d'ESP afin que l'application Bookstore puisse accepter les requêtes HTTP. Les clients utilisent le protocole HTTP1.1 pour envoyer des requêtes JSON/HTTP à ESP. Pour pouvoir accepter ces requêtes, ESP doit soit être configuré pour utiliser SSL (le port SSL étant compatible avec les deux types de requêtes), soit avoir un autre port activé dédié à ces appels. Le déploiement est par ailleurs sensiblement identique à celui décrit pour chaque environnement dans le tutoriel.

Vérifier que les règles HTTP sont déployées

Si vous avez déjà téléchargé l'exemple Bookstore dans le cadre des tutoriels, vous devez télécharger une version légèrement différente du fichier .proto comportant des annotations, http_bookstore.proto. Vous devez également cloner le dépôt googleapis à partir de GitHub avant d’exécuter protoc, car annotations.proto doit être ajouté dans votre chemin d’inclusion.

    git clone https://github.com/googleapis/googleapis

        GOOGLEAPIS_DIR=<your-local-googleapis-folder>
    

Créez ensuite un fichier descripteur .pb à partir de http_bookstore.proto lors du déploiement de votre configuration sur Endpoints :

    protoc \
            --include_imports \
            --include_source_info \
            --proto_path=${GOOGLEAPIS_DIR} \
            --proto_path=. \
            --descriptor_set_out=api_descriptor.pb \
            http_bookstore.proto
    

Si vous utilisez la méthode alternative de configuration des mappages HTTP dans votre fichier YAML de configuration de l'API gRPC, vous devez également vous assurer que les règles adéquates sont mises en place lors du déploiement de votre configuration sur Endpoints. Pour expérimenter cela avec le service Bookstore, récupérez ses règles de base dans le fichier api_config.yaml et ses règles HTTP dans le fichier api_config_http.yaml :

    gcloud endpoints services deploy api_descriptor.pb api_config.yaml api_config_http.yaml
    

Utiliser SSL

Si SSL est activé pour la communication entre vos clients et ESP, les clients peuvent alors effectuer des appels gRPC ou HTTP1.1 sur le même port. Pour savoir comment configurer SSL pour un service Endpoints, consultez la rubrique concernant l'activation de SSL.

Spécifiez un port permettant à ESP d'accepter les appels SSL à l'aide de l'option --ssl_port dans le fichier de configuration de Google Kubernetes Engine (GKE) ou de la commande docker run (Compute Engine/Docker).

    args: [
          "--http_port", "8080",
          "--ssl_port", "443",  # enable SSL port at 443 to serve https requests
          "--backend",  "grpc://127.0.0.1:8081",  # gRPC backend.
          "--service", "SERVICE_NAME",
          "--rollout_strategy", "managed",
        ]
    

Configurer un port HTTP1.1

Si vous n'utilisez pas SSL, vous devez configurer un port distinct pour les requêtes HTTP1.1 car, sans SSL, gRPC et HTTP1.1 ne peuvent pas partager le même port. Utilisez l'option --http_port dans le fichier de configuration GKE ou la commande docker run pour spécifier un port destiné à recevoir les appels HTTP1.1. Si vous souhaitez également que ESP accepte les appels gRPC, vous devez aussi utiliser l'option --http2_port pour spécifier un port pour gRPC.

    args: [
          "--http_port", "8080",  # for HTTP 1.1
          "--http2_port", "8090",  # for gRPC
          "--backend", "grpc://127.0.0.1:8081",  # gRPC backend.
          "--service", "SERVICE_NAME",
          "--rollout_strategy", "managed",
        ]
    

Appeler un service utilisant le transcodage

Cette section décrit la configuration du service et explique comment effectuer des appels au service en HTTP.

Configurer le service

Cette section part du principe que vous avez déjà suivi les tutoriels élémentaires du service gRPC dans l'environnement de votre choix, et que vous disposez d'un cluster GKE ou d'une instance Compute Engine pour exécuter l'exemple.

  1. Commencez par vérifier que vous avez déployé la configuration du service Bookstore compatible avec HTTP sur Endpoints, comme décrit dans la section Vérifier que les règles HTTP sont déployées.
  2. Déployez le backend et ESP comme décrit dans le tutoriel correspondant à la plate-forme de votre choix en utilisant l'option --http_port afin d'activer un port pour les requêtes HTTP1.1 :

Envoyer des appels HTTP au service

  1. Obtenez l'adresse IP externe d'ESP et définissez-la en tant que $ESP_IP.
  2. Envoyez la requête HTTP suivante avec curl

    curl http://$ESP_IP/v1/shelves
        

    (ou utilisez la même URL avec https:// si vous vous servez de SSL). Le serveur doit envoyer la réponse suivante :

    {"shelves":[{"id":"1","theme":"Fiction"},{"id":"2","theme":"Fantasy"}]}
        

    Si la sortie affiche une réponse en binaire, vérifiez la configuration du port. Vous accédez peut-être au port HTTP2 au lieu du port HTTP.

  3. Essayez une méthode Create. CreateShelf nécessite une clé API. Vous devez donc créer une clé pour votre projet et l'utiliser pour renseigner le paramètre $KEY. Effectuez maintenant l'appel suivant :

    curl -d '{"theme":"Music"}' http://$ESP_IP/v1/shelves?key=$KEY
        

    Si vous appelez à nouveau GetShelves, vous devriez voir votre nouvelle étagère.