Utiliser gRPC

Cette page, qui présente des détails propres à Cloud Run, s'adresse aux développeurs qui souhaitent utiliser gRPC afin de connecter un service Cloud Run à d'autres services, par exemple pour fournir une communication simple et hautes performances entre des microservices internes. Avec Cloud Run, vous pouvez utiliser tous les types gRPC en streaming ou unaire.

Exemples d'utilisation possible :

  • Communication entre des microservices internes.
  • Charges de données élevées (gRPC utilise des tampons de protocole, qui sont jusqu'à sept fois plus rapides que les appels REST).
  • Seule une définition de service simple est nécessaire, vous ne souhaitez pas écrire une bibliothèque cliente complète.
  • Utilisez des gRPC en streaming sur votre serveur gRPC pour créer des applications et des API plus réactives.

Pour intégrer votre service à gRPC, procédez comme suit :

  • Configurez votre service pour qu'il utilise HTTP/2 si vous utilisez gRPC en streaming. HTTP/2 est la méthode de transport pour le streaming gRPC.
  • Définissez les messages de requête et les réponses dans un fichier proto, puis compilez-les.
  • Créez un serveur gRPC pour gérer les requêtes et renvoyer des réponses : il doit écouter la variable d'environnement PORT.
  • Créez un client qui envoie des requêtes et gère les réponses du serveur gRPC.
  • Vous pouvez éventuellement ajouter une authentification.
  • Créez et déployez votre service.

Configurer votre service pour utiliser HTTP/2

Google recommande de configurer votre service pour en HTTP/2 si vous utilisez gRPC avec Cloud Run. Bien que certaines fonctionnalités gRPC de base fonctionnent sans HTTP/2, de nombreuses fonctionnalités gRPC, telles que le streaming et les métadonnées, nécessitent HTTP/2.

Définir et compiler des messages dans un fichier proto

Vous n'avez pas besoin d'ajouter d'éléments supplémentaires ou propres à Cloud Run dans vos définitions de proto. Comme pour toute autre utilisation de gRPC, vous utilisez des tampons de protocole gRPC pour les définitions de service et la sérialisation des données.

Créer un client gRPC

Vous n'avez pas besoin d'ajouter d'éléments supplémentaires ou propres à Cloud Run à un client qui utilise gRPC : suivez les documents gRPC sur l'utilisation des définitions de service dans le code client et les exemples de clients fournis dans les tutoriels gRPC propres aux différents langages.

Autoscaling et équilibrage de charge

Cloud Run utilise des équilibreurs de charge gérés par Google qui maintiennent des connexions distinctes entre les clients et vos instances Cloud Run. Avec gRPC, l'autoscaling se comporte comme suit :

  • Les connexions gRPC des clients se terminent au niveau de l'équilibreur de charge périphérique. L'ajustement des paramètres KeepAlive n'affecte que la connexion à l'équilibreur de charge, et pas les instances Cloud Run. Le client ne reconnaît pas le moment où une instance est arrêtée.
  • Lors du scaling vertical, l'équilibreur de charge ferme les connexions en envoyant des messages GOAWAY aux instances de backend lorsqu'elles s'arrêtent.
  • Pendant le scaling horizontal, l'équilibreur de charge crée des connexions aux instances de backend. Toutes ces opérations sont transparentes pour les clients.
  • Lors de l'autoscaling, de nombreuses instances peuvent démarrer et être multiplexées en une seule connexion entre le client et l'équilibreur de charge proxy.
  • La simultanéité est déterminée par le nombre maximal de requêtes simultanées par instance pour les messages. En cas de diffusion en flux continu, chaque flux est comptabilisé une fois dans le nombre maximal de requêtes simultanées.

Écouter des requêtes gRPC dans un service Cloud Run

La seule condition requise pour un serveur gRPC exécuté dans Cloud Run est d'écouter le port spécifié par la variable d'environnement PORT, comme indiqué dans le code suivant :

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)
	}
}

Ouvrir une connexion gRPC à un service

Pour ouvrir une connexion gRPC à un service afin d'envoyer des messages gRPC, vous devez spécifier le domaine hôte, qui est l'URL du service Cloud Run ou le domaine personnalisé mappé avec ce service, ainsi que le port 443, qui est le port censé être utilisé par 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...)
}

Envoyer des requêtes gRPC sans authentification

L'exemple suivant montre comment envoyer une requête sans authentification, à l'aide d'une connexion gRPC configurée comme indiqué précédemment.

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)
}

Envoyer des requêtes gRPC avec authentification

L'exemple suivant montre comment utiliser l'authentification entre les services, si le service appelant dispose d'une autorisation d'appel sur le service destinataire. Notez que ce code crée un en-tête d'autorisation doté du jeton d'identité approprié : cela est obligatoire. Les autorisations requises et l'en-tête d'autorisation sont décrites en détail sur la page Authentification de service à service.

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)
}

Exemple de code pour le streaming gRPC

Pour obtenir un exemple de code, reportez-vous à la mise en œuvre de RouteGuide dans le tutoriel gRPC "Principes de base" dans la langue de votre choix. Lorsque vous utilisez Go, par exemple, reportez-vous à la section Mettre en œuvre RouteGuide.