Transcodifica di HTTP/JSON in gRPC

Cloud Endpoints supporta la transcodifica del protocollo in modo che i client possano accedere alla tua API gRPC utilizzando HTTP/JSON. Il proxy di servizio Extensible (ESP) transcodisce HTTP/JSON in gRPC.

Questa guida descrive:

  • Come utilizzare le annotazioni nel file .proto per specificare la conversione dei dati da HTTP/JSON a gRPC
  • Come eseguire il deployment del servizio in Endpoints per utilizzare questa funzionalità
  • Dove trovare ulteriori informazioni di riferimento sulla progettazione e sull'implementazione del transcoding per i servizi gRPC

Si presume che tu abbia già completato i nostri tutorial su gRPC e che tu abbia familiarità con i concetti di base degli endpoint per le API gRPC.

Progettare un'API adatta alla transcodifica

La transcodifica prevede la mappatura delle richieste HTTP/JSON e dei relativi parametri ai metodi gRPC e ai loro parametri e tipi di ritorno. Per questo motivo, anche se è possibile mappare una richiesta HTTP/JSON a qualsiasi metodo API arbitrario, è consigliabile farlo se l'API gRPC è strutturata in modo orientato alle risorse, proprio come un'API HTTP REST tradizionale. In altre parole, progetti il servizio API in modo che utilizzi un numero ridotto di metodi standard, corrispondenti a verbi HTTP come GET e PUT, che operano sulle risorse e sulle raccolte di risorse del servizio, che sono esse stesse un tipo di risorsa. Questi metodi standard sono List, Get, Create, Update e Delete.

Se necessario, l'API può avere anche alcuni metodi personalizzati non standard, anche se la loro mappatura non è così semplice.

Puoi scoprire di più sul design orientato alle risorse e sulle mappature di transcodifica standard nella guida alla progettazione delle API. Questa guida alla progettazione è lo standard di progettazione seguito in Google per la progettazione di API pubbliche come le API Cloud. Sebbene non sia necessario seguire questa guida per utilizzare la transcodifica gRPC, ti consigliamo vivamente di farlo. In particolare, le seguenti pagine possono aiutarti a comprendere questi principi di progettazione e ad aggiungere mappature di transcodifica utili ai tuoi metodi:

Potrebbe essere utile anche la seguente pagina di riferimento:

Nel resto di questo documento, utilizzerai l'esempio della libreria che hai utilizzato nei nostri tutorial, che già utilizza questi principi. La Libreria contiene raccolte di risorse "libri", che gli utenti possono List, Get, Create o Delete.

Dove configurare la transcodifica

La transcodifica gRPC è abilitata per impostazione predefinita e puoi utilizzarla senza alcuna configurazione. Segui le istruzioni per implementare un servizio che utilizza la transcodifica. Successivamente, quando invii una richiesta POST HTTP al percorso dell'URL GRPC_SERVICE_FULL_NAME/METHOD_NAME> con i valori dei campi del messaggio di richiesta del metodo (se presenti) come JSON nel corpo della richiesta HTTP, l'ESP invia il messaggio di richiesta al metodo gRPC appropriato. Nell'esempio precedente, GRPC_SERVICE_FULL_NAME è il nome completo del servizio gRPC e METHOD_NAME è il nome del metodo.

Ad esempio, se invii un POST all'URL ListShelves della libreria come segue:

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

Riceverai un elenco aggiornato delle sezioni in formato JSON.

Tuttavia, in termini di progettazione dell'interfaccia HTTP, è fortemente preferibile configurare esplicitamente le mappature, come descritto nel resto di questo documento.

Lo standard di configurazione dell'API gRPC per la configurazione del servizio ti consente di specificare esattamente come devono essere tradotti i dati da HTTP/JSON a gRPC. Per farlo sono supportati due meccanismi: annotazioni dirette nel file .proto e in YAML nell'ambito del file di configurazione dell'API gRPC. Ti consigliamo di utilizzare le annotazioni proto per facilitare la lettura e la manutenzione. Per ulteriori informazioni sulla configurazione YAML e su quando potrebbe essere necessario utilizzarla, consulta Configurare la transcodifica in YAML.

Ecco un esempio che utilizza l'approccio consigliato dalla libreria:

// 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'annotazione indica all'ESP che l'invio di una richiesta HTTP GET con l'URL http://mydomain/v1/shelves/1 chiama il metodo GetShelf() del server gRPC, con un GetShelfRequest contenente l'ID sezione richiesto shelf (in questo caso 1).

Aggiunta di mappature di transcodifica

Questa sezione descrive alcune annotazioni di mappatura aggiuntive dell'esempio Libreria. Nell'esempio di Bookstore sono presenti due file proto di esempio, in modo da poterli implementare sia con che senza le mappature di transcodifica e confrontare le differenze nei file proto:

Per una guida più completa su come specificare le mappature di transcodifica, consulta Metodi standard, Metodi personalizzati e il riferimento alle regole HTTP.

Mappare un metodo List

Il metodo List è definito nel file .proto con il relativo tipo di risposta:

  // 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'annotazione in grassetto specifica la mappatura HTTP per questo metodo.

  • option (google.api.http) specifica che questo metodo è un'annotazione di mappatura HTTP gRPC.
  • get specifica che questo metodo è mappato a una richiesta GET HTTP.
  • "/v1/shelves" è il modello di percorso dell'URL (aggiunto al dominio del servizio) utilizzato dalla richiesta GET per chiamare questo metodo. Il percorso dell'URL è noto anche come percorso della risorsa perché in genere specifica la "cosa" o la risorsa che vuoi utilizzare. In questo caso, tutte le risorse della sezione del nostro Negozio di libri.

Ad esempio, se un client chiama questo metodo inviando un GET all'URL http://mydomain/v1/shelves, ESP chiama il metodo gRPC ListShelves(). Il backend gRPC restituisce quindi le sezioni, che ESP converte in formato JSON e restituisce al client.

Mappare un metodo Get

Il metodo GetShelf della libreria è definito nel file .proto con i relativi tipi di richiesta e risposta:

// 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'annotazione in grassetto specifica la mappatura HTTP per questo metodo.

  • option (google.api.http) specifica che questo metodo è un'annotazione di mappatura HTTP gRPC.
  • get specifica che questo metodo è mappato a una richiesta HTTP GET.
  • "/v1/shelves/{shelf}"è il percorso dell'URL per la richiesta, come prima, ma specifica /v1/shelves/ e poi {shelf}. Questa notazione con parentesi graffe indica a ESP che qualsiasi valore in {shelf} è il valore da fornire per shelf nel parametro GetShelfRequest del metodo.

Se un client chiama questo metodo inviando un GET all'URL http://mydomain/v1/shelves/4, ESP crea un GetShelfRequest con un valore shelf pari a 4 e poi chiama il metodo gRPC GetShelf() con questo valore. Il backend gRPC restituisce quindi il Shelf richiesto con l'ID 4, che ESP converte in formato JSON e restituisce al client.

Questo metodo richiede solo che il client fornisca un singolo valore del campo della richiesta, shelf, che specifichi nel modello di percorso dell'URL con la notazione "acquisizione" tra parentesi graffe. Se necessario, puoi acquisire più parti dell'URL per identificare la risorsa richiesta. Ad esempio, il metodo GetBook richiede al client di specificare sia l'ID sezione sia l'ID libro nell'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;
}

Oltre ai valori letterali e alle parentesi graffe di acquisizione per i valori di campo, i modelli di percorso dell'URL possono utilizzare caratteri jolly per indicare che qualsiasi elemento in questa parte dell'URL deve essere acquisito. La notazione {shelf} utilizzata nell'esempio precedente è in realtà una scorciatoia per {shelf=*}. Puoi scoprire di più sulle regole per i modelli di percorso nel riferimento alle regole HTTP.

Per questo tipo di metodo, non è specificato il corpo della richiesta HTTP. Puoi trovare altre linee guida per la mappatura dei metodi Get, incluso l'utilizzo parametri di ricerca, in Metodi standard.

Mappare un metodo Create

Il metodo CreateShelf della libreria corrisponde a POST HTTP.

  // 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) specifica che questo metodo è un'annotazione di mappatura HTTP gRPC.
  • post specifica che questo metodo è mappato a una richiesta HTTP POST.
  • "/v1/shelves" è il percorso dell'URL per la richiesta, come prima.
  • body: "shelf"viene utilizzato nel corpo della richiesta HTTP per specificare la risorsa da aggiungere in formato JSON.

Ad esempio, se un client chiama questo metodo come segue:

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

ESP utilizza il corpo JSON per creare un valore Shelf con il tema "Music" per CreateShelfRequest e poi chiama il metodo gRPC CreateShelf(). Tieni presente che il client non fornisce il valore id per Shelf. Gli ID sezione della libreria vengono forniti dal servizio quando viene creata una nuova sezione. Fornisci questo tipo di informazioni agli utenti del tuo servizio nella documentazione dell'API.

Utilizzare un carattere jolly nel corpo

Il nome speciale * può essere utilizzato nella mappatura del corpo per indicare che ogni campo non vincolato dal modello di percorso deve essere mappato al corpo della richiesta. In questo modo viene attivata la seguente definizione alternativa del metodo CreateShelf.

  // Creates a new shelf in the bookstore.
  rpc CreateShelf(CreateShelfRequest) returns (Shelf) {
    // Client example:
    //   curl -d '{"shelf_theme":"Music", "shelf_size": 20}' http://DOMAIN_NAME/v1/shelves/123
    option (google.api.http) = {
      post: "/v1/shelves/{shelf_id}"
      body: "*"
    };
  }
...
// Request message for CreateShelf method.
message CreateShelfRequest {
  // A unique shelf id.
  int64 shelf_id = 1;
  // A theme of the shelf (fiction, poetry, etc).
  string shelf_theme = 2;
  // The size of the shelf
  int64 shelf_size = 3;
}
  • option (google.api.http) specifica che questo metodo è un'annotazione di mappatura HTTP gRPC.
  • post specifica che questo metodo è mappato a una richiesta POST HTTP.
  • "/v1/shelves/{shelf_id}" è il percorso dell'URL per la richiesta. Qualsiasi valore in {shelf_id} è il valore del campo shelf_id in CreateShelfRequest.
  • body: "*" viene utilizzato nel corpo della richiesta HTTP per specificare tutti i campi della richiesta rimanenti, ad eccezione di shelf_id in questo esempio, ovvero shelf_theme e shelf_size. Per tutti i campi del corpo JSON con questi due nomi, i relativi valori verranno utilizzati nei campi corrispondenti di CreateShelfRequest.

Ad esempio, se un client chiama questo metodo nel seguente modo:

curl -d '{"shelf_theme":"Music", "shelf_size": 20}' http://DOMAIN_NAME/v1/shelves/123

ESP utilizza il corpo JSON e il modello di percorso per creare un CreateShelfRequest{shelf_id: 123 shelf_theme: "Music" shelf_size: 20}, quindi lo utilizza per chiamare il metodo gRPC CreateShelf(). Per maggiori dettagli, consulta HttpRule.

Configurazione della transcodifica in YAML

Un approccio alternativo consiste nello specificare le mappature da HTTP a gRPC nel file YAML di configurazione dell'API gRPC anziché nel file .proto. Potresti dover configurare la transcodifica in un file YAML se hai una singola definizione dell'API proto utilizzata in più servizi, con mappature diverse specificate per ciascun servizio.

rules nella sezione http del file YAML specifica come mappare le richieste HTTP/JSON ai metodi 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}
  ...

Un esempio più completo dell'utilizzo di questo approccio per il servizio di esempio Libreria si trova in api_config_http.yaml.

Eseguire il deployment di un servizio che utilizza la transcodifica

Il deployment di un servizio gRPC che utilizza la transcodifica è molto simile a quello di qualsiasi altro servizio gRPC, con una differenza sostanziale. Nei tutorial, l'esempio doveva accettare le richieste gRPC del client di esempio. Tuttavia, se vuoi che la libreria accetti anche le richieste HTTP, devi eseguire un'ulteriore configurazione per ESP. I client utilizzano il protocollo HTTP1.1 per inviare richieste JSON/HTTP all'ESP, pertanto l'ESP deve essere configurato per utilizzare SSL (la porta SSL può supportare entrambi i tipi di richiesta) o deve avere una porta speciale abilitata per accettare queste chiamate. Per il resto, il deployment è molto simile a quello descritto nel tutorial per l'ambiente scelto.

Assicurati che le regole HTTP siano dipistate

Se hai già scaricato l'esempio della libreria per i tutorial, tieni presente che devi scaricare una versione leggermente diversa del file .proto con le annotazioni, http_bookstore.proto. Inoltre, devi clonare il repository googleapis da GitHub prima di eseguire protoc, poiché hai bisogno di annotations.proto nel percorso di inclusione.

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

    GOOGLEAPIS_DIR=<your-local-googleapis-folder>

Poi, crea un nuovo descrittore .pb da http_bookstore.proto durante il deployment della configurazione in Endpoints:

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

Se utilizzi il metodo alternativo per configurare le mappature HTTP nel file YAML della configurazione dell'API gRPC, devi anche assicurarti che le regole pertinenti vengano implementate durante il deployment della configurazione in Endpoints. Per provare questa operazione con il servizio Libreria, le relative regole di base si trovano nel file api_config.yaml e le regole HTTP nel file api_config_http.yaml:

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

Utilizzo di SSL

Se SSL è abilitato per la comunicazione tra i client e l'ESP, i client possono utilizzare la stessa porta per effettuare chiamate gRPC o HTTP1.1. Puoi scoprire come configurare SSL per un servizio Endpoints in Abilitazione di SSL.

Specifica una porta per consentire a ESP di accettare le chiamate SSL utilizzando il flag --ssl_port nel file di configurazione di Google Kubernetes Engine (GKE) o il comando 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",
    ]

Configurazione di una porta HTTP1.1

Se non utilizzi SSL, devi configurare una porta separata per le richieste HTTP1.1 perché gRPC e HTTP1.1 non possono condividere la stessa porta senza SSL. Utilizza il flag --http_port nel file di configurazione GKE o il comando docker run per specificare una porta per accettare le chiamate HTTP1.1. Se vuoi anche che l'ESP accetti le chiamate gRPC, devi utilizzare anche il flag --http2_port per specificare una porta 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",
    ]

Chiamare un servizio utilizzando la transcodifica

Questa sezione descrive la configurazione del servizio e come effettuare chiamate HTTP al servizio.

Configurazione del servizio

Si presume che tu abbia già completato i tutorial di base sul servizio gRPC per l'ambiente scelto e che tu abbia un cluster GKE o un'istanza Compute Engine per eseguire l'esempio.

  1. Innanzitutto, assicurati di aver eseguito il deployment della configurazione del servizio Bookstore abilitato per HTTP in Endpoints, come descritto in Verificare il deployment delle regole HTTP.
  2. Esegui il deployment del backend e dell'ESP come descritto nel tutorial per la piattaforma scelta, utilizzando il flag --http_port per attivare una porta per le richieste HTTP1.1:

Effettuare chiamate HTTP al servizio

  1. Ottieni l'indirizzo IP esterno dell'ESP e impostalo su $ESP_IP.
  2. Fai la seguente richiesta HTTP con curl

    curl http://$ESP_IP/v1/shelves
    

    (oppure utilizza lo stesso URL con https:// se utilizzi SSL). Il server risponde con:

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

    Se l'output mostra una risposta binaria, controlla la configurazione della porta perché potresti aver raggiunto la porta HTTP2 anziché la porta HTTP.

  3. Prova un metodo Create. CreateShelf richiede una chiave API, quindi devi crearla per il tuo progetto e impostarla come $KEY. Chiama ora:

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

    Se chiami di nuovo GetShelves, dovresti vedere il nuovo scaffale.