Utilizzo di gRPC

Questa pagina mostra i dettagli specifici di Cloud Run per gli sviluppatori che vogliono utilizzare gRPC per connettere un servizio Cloud Run ad altri servizi, ad esempio per fornire comunicazioni semplici e ad alte prestazioni tra microservizi interni. Con Cloud Run puoi utilizzare tutti i tipi di gRPC, in modalità flusso o unario.

I possibili casi d'uso includono:

  • Comunicazione tra microservizi interni.
  • Carichi di dati elevati (gRPC utilizza i buffer di protocollo, che sono fino a sette volte più veloci delle chiamate REST).
  • È necessaria solo una semplice definizione di servizio, quindi non vuoi scrivere una libreria client completa.
  • Utilizza gRPC in modalità flusso nel server gRPC per creare applicazioni e API più reattive.

Per integrare il servizio con gRPC:

  • Configura il servizio per l'utilizzo di HTTP/2 se usi gRPC streaming. HTTP/2 è il metodo di trasporto per il flusso di dati gRPC.
  • Definisci i messaggi e le risposte di richiesta in un file proto e compilali.
  • Crea un server gRPC per gestire le richieste e restituire le risposte: deve ascoltare la variabile di ambiente PORT.
  • Crea un client che invia le richieste e gestisce le risposte dal server gRPC.
  • Se vuoi, aggiungi l'autenticazione.
  • Crea ed esegui il deployment del tuo servizio.

Configurazione del servizio per l'utilizzo di HTTP/2

Google consiglia di configurare il servizio per l'utilizzo di HTTP/2 se utilizzi gRPC con Cloud Run. Sebbene alcune semplici funzionalità gRPC funzionino senza utilizzare HTTP/2, molte funzionalità gRPC, come i flussi di dati e i metadati, richiedono HTTP/2.

Definizione e compilazione dei messaggi in un file di protocollo

Non ci sono elementi aggiuntivi o specifici di Cloud Run da aggiungere alle definizioni dei proto. Come per qualsiasi altro utilizzo di gRPC, utilizzi i buffer di protocollo gRPC per le definizioni dei servizi e la serializzazione dei dati.

Creazione di un client gRPC

Non ci sono elementi aggiuntivi o specifici di Cloud Run da aggiungere a un client che utilizza gRPC: segui la documentazione gRPC sull'utilizzo delle definizioni dei servizi nel codice client e i client di esempio forniti nei tutorial per gRPC specifici per il linguaggio.

Scalabilità automatica e bilanciamento del carico

Cloud Run utilizza bilanciatori del carico gestiti da Google che mantengono connessioni separate tra i client e le tue istanze Cloud Run. Con gRPC, la scalabilità automatica si comporta come segue:

  • Le connessioni gRPC dai client terminano al bilanciatore del carico perimetrale. La regolazione delle impostazioni di KeepAlive influisce solo sulla connessione al bilanciatore del carico, non sulle istanze di Cloud Run. Il client non riconosce l'abbandono di un'istanza.
  • Durante lo scale in, il bilanciatore del carico chiude le connessioni inviando messaggi GOAWAY alle istanze di backend quando vengono arrestate.
  • Durante lo scale out, il bilanciatore del carico crea nuove connessioni alle istanze di backend. Tutte queste operazioni sono trasparenti per i clienti.
  • Durante la scalabilità automatica, molte istanze possono avviarsi e multiplexare in un'unica connessione tra il client e il bilanciatore del carico proxy.
  • La contemporaneità è determinata dal numero massimo di richieste in parallelo per istanza per i messaggi. Nel flusso di dati, ogni flusso viene conteggiato una volta in base al numero massimo di richieste in parallelo.

Ascolto delle richieste gRPC in un servizio Cloud Run

L'unico requisito speciale per un server gRPC in esecuzione in Cloud Run è l'ascolto in corrispondenza della porta specificata dalla variabile di ambiente PORT, come mostrato nel codice seguente:

Go

func main() {
	log.Printf("grpc-ping: starting server...")

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("Defaulting to port %s", port)
	}

	listener, err := net.Listen("tcp", ":"+port)
	if err != nil {
		log.Fatalf("net.Listen: %v", err)
	}

	grpcServer := grpc.NewServer()
	pb.RegisterPingServiceServer(grpcServer, &pingService{})
	if err = grpcServer.Serve(listener); err != nil {
		log.Fatal(err)
	}
}

Apertura di una connessione gRPC a un servizio

Per aprire una connessione gRPC a un servizio in modo da poter inviare messaggi gRPC, devi specificare il dominio host, ovvero l'URL del servizio Cloud Run o il dominio personalizzato mappato a quel servizio, insieme alla porta 443, che è la porta che si prevede dovrà essere utilizzata da gRPC.

Go


import (
	"crypto/tls"
	"crypto/x509"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

// NewConn creates a new gRPC connection.
// host should be of the form domain:port, e.g., example.com:443
func NewConn(host string, insecure bool) (*grpc.ClientConn, error) {
	var opts []grpc.DialOption
	if host != "" {
		opts = append(opts, grpc.WithAuthority(host))
	}

	if insecure {
		opts = append(opts, grpc.WithInsecure())
	} else {
		// Note: On the Windows platform, use of x509.SystemCertPool() requires
		// go version 1.18 or higher.
		systemRoots, err := x509.SystemCertPool()
		if err != nil {
			return nil, err
		}
		cred := credentials.NewTLS(&tls.Config{
			RootCAs: systemRoots,
		})
		opts = append(opts, grpc.WithTransportCredentials(cred))
	}

	return grpc.Dial(host, opts...)
}

Invio di richieste gRPC senza autenticazione

Il seguente esempio mostra come inviare una richiesta senza autenticazione, utilizzando una connessione gRPC configurata come menzionato in precedenza.

Go


import (
	"context"
	"time"

	pb "github.com/GoogleCloudPlatform/golang-samples/run/grpc-ping/pkg/api/v1"
	"google.golang.org/grpc"
)

// pingRequest sends a new gRPC ping request to the server configured in the connection.
func pingRequest(conn *grpc.ClientConn, p *pb.Request) (*pb.Response, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	client := pb.NewPingServiceClient(conn)
	return client.Send(ctx, p)
}

Invio di richieste gRPC con autenticazione

Il seguente esempio mostra come utilizzare l'autenticazione tra i servizi, se il servizio di chiamata ha l'autorizzazione callout per il servizio ricevente. Tieni presente che questo codice crea un'intestazione di autorizzazione che dispone del token di identità corretto: questo è obbligatorio. Le autorizzazioni richieste e l'intestazione dell'autorizzazione sono descritte dettagliatamente in Autenticazione da servizi a servizio.

Go


import (
	"context"
	"fmt"
	"time"

	"google.golang.org/api/idtoken"
	"google.golang.org/grpc"
	grpcMetadata "google.golang.org/grpc/metadata"

	pb "github.com/GoogleCloudPlatform/golang-samples/run/grpc-ping/pkg/api/v1"
)

// pingRequestWithAuth mints a new Identity Token for each request.
// This token has a 1 hour expiry and should be reused.
// audience must be the auto-assigned URL of a Cloud Run service or HTTP Cloud Function without port number.
func pingRequestWithAuth(conn *grpc.ClientConn, p *pb.Request, audience string) (*pb.Response, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Create an identity token.
	// With a global TokenSource tokens would be reused and auto-refreshed at need.
	// A given TokenSource is specific to the audience.
	tokenSource, err := idtoken.NewTokenSource(ctx, audience)
	if err != nil {
		return nil, fmt.Errorf("idtoken.NewTokenSource: %w", err)
	}
	token, err := tokenSource.Token()
	if err != nil {
		return nil, fmt.Errorf("TokenSource.Token: %w", err)
	}

	// Add token to gRPC Request.
	ctx = grpcMetadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token.AccessToken)

	// Send the request.
	client := pb.NewPingServiceClient(conn)
	return client.Send(ctx, p)
}

Codice di esempio per il flusso gRPC

Per codice campione, consulta l'implementazione RouteGuide nel tutorial Nozioni di base su gRPC per la lingua che preferisci. Quando utilizzi Go, ad esempio, consulta Implementazione di RouteGuide.