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. L'Extensible Service Proxy (ESP) transcodifica HTTP/JSON in gRPC.

Questa guida descrive:

  • Come utilizzare le annotazioni nel tuo file .proto per specificare la conversione di 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 della transcodifica per i servizi gRPC

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

Progettazione di un'API compatibile con la transcodifica

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

Se necessario, l'API può anche avere alcuni metodi personalizzati non standard, sebbene non siano altrettanto semplici da mappare.

Puoi trovare ulteriori informazioni sulla progettazione orientata 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 durante la progettazione di API pubbliche come le API Cloud. Anche se non devi seguire questa guida, ti consigliamo vivamente di utilizzare la transcodifica gRPC. In particolare, le seguenti pagine possono aiutarti a comprendere questi principi di progettazione e aggiungere utili mappature di transcodifica ai tuoi metodi:

Potrebbe essere utile anche la seguente pagina di riferimento:

Nella parte restante di questo documento usi l'esempio della Libreria che hai utilizzato nei nostri tutorial, che già utilizzano questi principi. La Libreria ha raccolte "scaffali" di "prenotazioni" di 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 il deployment di un servizio tramite transcodifica. Successivamente, quando invii una richiesta POST HTTP al percorso URL GRPC_SERVICE_FULL_NAME/METHOD_NAME> con il valore del campo del messaggio di richiesta methods (se disponibile) come JSON nel corpo della richiesta HTTP, ESP invia il messaggio di richiesta al metodo gRPC appropriato. Nell'esempio precedente, GRPC_SERVICE_FULL_NAME è il nome completo del tuo servizio gRPC e METHOD_NAME è il nome del metodo.

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

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

Riceverai un elenco attuale di scaffali in formato JSON.

Tuttavia, è molto preferibile in termini di progettazione dell'interfaccia HTTP per configurare in modo esplicito 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 tradurre i dati da HTTP/JSON a gRPC. Per farlo, sono supportati due meccanismi: le annotazioni dirette nel file .proto e in YAML come file di configurazione dell'API gRPC. Ti consigliamo di utilizzare le annotazioni proto per semplificare la lettura e la manutenzione. Per ulteriori informazioni sulla configurazione YAML e su quando potresti doverla utilizzare, consulta l'articolo sulla configurazione della transcodifica in YAML.

Ecco un esempio che utilizza l'approccio consigliato della 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 a ESP che effettuando una richiesta GET HTTP con l'URL http://mydomain/v1/shelves/1 viene chiamato il metodo GetShelf() del server gRPC, con un GetShelfRequest contenente l'ID scaffale richiesto shelf (in questo caso, 1).

Aggiunta di mappature di transcodifica

In questa sezione vengono descritte alcune annotazioni di mappatura aggiuntive del campione della Libreria. Nell'esempio Libreria sono presenti due file proto di esempio, quindi puoi eseguirne il deployment con o senza le mappature di transcodifica e confrontare le differenze nei file proto:

Per una guida più completa a specificare le mappature di transcodifica, consulta le sezioni Metodi standard, Metodi personalizzati e Riferimento regola HTTP.

Mappa un metodo List

Il metodo List è definito nel file .proto con il suo 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 HTTP GET.
  • "/v1/shelves" è il modello di percorso dell'URL (aggiunto al dominio del servizio) che la richiesta GET utilizza per chiamare questo metodo. Il percorso dell'URL è noto anche come percorso della risorsa perché in genere specifica la risorsa "cosa" o la risorsa che vuoi utilizzare. In questo caso, tutte le risorse della nostra libreria.

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 gli scaffali, che vengono convertiti in formato ESP e restituiti al client.

Mappa un metodo Get

Il metodo GetShelf della Libreria è definito nel file .proto con i suoi 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 in precedenza, ma specifica /v1/shelves/ e poi {shelf}. Questa notazione della parentesi graffa indica a ESP che quello che si trova in {shelf} è il valore che dovrebbe 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 di 4 e quindi chiama il metodo gRPC GetShelf() con tale metodo. Il backend gRPC restituisce quindi il valore Shelf richiesto con l'ID 4, che converte ESP in formato JSON e restituisce il client.

Questo metodo richiede che un solo valore del campo di richiesta sia fornito dal client, shelf, specificato nel modello di percorso dell'URL con la notazione"ri mediante parentesi graffa". Se necessario, puoi anche acquisire più parti dell'URL. Ad esempio, il metodo GetBook richiede che il client specifichi sia l'ID scaffale 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 a valori letterali e parentesi graffe per i valori dei campi, i modelli di percorso dell'URL possono utilizzare caratteri jolly per indicare che tutti gli elementi in questa parte dell'URL devono essere acquisiti. La notazione {shelf} utilizzata nell'esempio precedente è in realtà una scorciatoia per {shelf=*}. Per saperne di più sulle regole per i modelli di percorso, consulta la guida di riferimento alle regole HTTP.

In questo caso, non è stato specificato un corpo della richiesta HTTP. Puoi trovare ulteriori linee guida per mappare i metodi Get, compreso l'utilizzo dei parametri di ricerca, in Metodi standard.

Mappa un metodo Create

Il metodo CreateShelf della Libreria è mappato a 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) 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 in precedenza.
  • body: "shelf": viene utilizzato nel corpo della richiesta HTTP per specificare la risorsa che vuoi aggiungere in formato JSON.

Quindi, ad esempio, se un cliente ha chiamato 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, quindi chiama il metodo CreateShelf() gRPC. Tieni presente che il client non fornisce il valore id per Shelf. Gli ID scaffale di Bookstore sono forniti dal servizio quando si crea un nuovo scaffale. Fornisci questo tipo di informazioni agli utenti del tuo servizio nella documentazione dell'API.

Utilizza il carattere jolly nel corpo

Il nome speciale * può essere utilizzato nel corpo della mappatura per indicare che ogni campo non associato al modello di percorso deve essere mappato al corpo della richiesta. Questa operazione attiva 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 HTTP POST.
  • "/v1/shelves/{shelf_id}" è il percorso dell'URL per la richiesta. Qualunque 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 richiesta rimanenti, ad eccezione di shelf_id in questo esempio, e sono shelf_theme e shelf_size. I valori del campo JSON con questi due nomi verranno utilizzati nei campi CreateShelfRequest corrispondenti.

Ad esempio, se un cliente ha chiamato questo metodo:

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 CreateShelf() gRPC. Per informazioni dettagliate, vedi HttpRule.

Configurazione della transcodifica in YAML

Un approccio alternativo consiste nel fatto che devi specificare le mappature HTTP a gRPC nel file YAML configurazione gRPC API anziché nel file .proto. Potrebbe essere necessario configurare la transcodifica in un file YAML se hai una singola definizione dell'API proto utilizzata in più servizi, con mappature diverse specificate per ogni servizio.

La sezione rules nella sezione http del file YAML indica 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 esempio Libreria è disponibile in api_config_http.yaml.

Deployment di un servizio che utilizza la transcodifica

Il deployment di un servizio gRPC che utilizza la transcodifica è molto simile al deployment di qualsiasi altro servizio gRPC, con una differenza importante. Nei tutorial, l'esempio necessario per accettare richieste gRPC dal client di esempio. Tuttavia, se vuoi che la Libreria accetti anche le richieste HTTP, devi eseguire una configurazione aggiuntiva per ESP. I client utilizzano il protocollo HTTP1.1 per inviare richieste JSON/HTTP a ESP, pertanto ESP deve essere configurata in modo da utilizzare SSL (la porta SSL può supportare entrambi i tipi di richieste) oppure deve avere una porta speciale abilitata per accettare queste chiamate. Il deployment è altrimenti molto simile a quello del tutorial per l'ambiente scelto.

Assicurati che sia stato eseguito il deployment delle regole HTTP

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. Devi anche clonare il repository googleapis da GitHub prima di eseguire protoc, poiché devi trovare annotations.proto nel percorso di inclusione.

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

    GOOGLEAPIS_DIR=<your-local-googleapis-folder>

In seguito, crei un nuovo descrittore .pb da http_bookstore.proto durante il deployment della configurazione in endpoint:

    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 gRPC API, devi anche assicurarti che venga eseguito il deployment delle regole pertinenti quando esegui il deployment della configurazione in Endpoints. Per provare a utilizzare questo servizio con la libreria, le regole di base si trovano nel file api_config.yaml, mentre le regole HTTP si trovano 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 tuoi client e ESP, i client possono utilizzare la stessa porta per effettuare chiamate gRPC o HTTP1.1. Per informazioni su come configurare SSL per un servizio Endpoints, vedi Abilitare SSL.

Specifica una porta in modo che ESP accetti 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 di GKE oppure il comando docker run per specificare una porta per accettare le chiamate HTTP1.1. Se vuoi che ESP accetti anche le chiamate gRPC, devi utilizzare il flag --http2_port anche 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",
    ]

Chiamata a un servizio tramite transcodifica

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

Configurazione del servizio

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

  1. Prima di tutto, assicurati di aver eseguito il deployment della configurazione del servizio Bookstore per HTTP in Endpoints, come descritto nella sezione Eseguire il deployment delle regole HTTP.
  2. Esegui il deployment del backend e di ESP come descritto nel tutorial per la tua piattaforma scelta, utilizzando il flag --http_port per abilitare una porta per le richieste HTTP1.1:

Effettuare chiamate HTTP al servizio

  1. Ottieni l'indirizzo IP esterno di 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 stai utilizzando 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 raggiungere la tua porta HTTP2 anziché la porta HTTP.

  3. Prova con un metodo Create. CreateShelf richiede una chiave API, quindi devi creare una chiave 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 tuo nuovo scaffale.