Catégories
Start-up et applications

Qu'est-ce qu'une API gRPC et comment fonctionne-t-elle?

gRPC est devenu une technologie importante pour la mise en œuvre de systèmes logiciels distribués qui doivent fonctionner rapidement à grande échelle. En bref, gRPC est un cadre d'API qui permet à un programme situé à un emplacement sur Internet de transmettre des données à une fonction distincte dans un autre programme à un autre emplacement sur Internet pour traitement. Alors que d'autres frameworks d'API tels que REST transmettent généralement les données du client au serveur et inversement à l'aide de formats textuels tels que JSON ou XML, sous gRPC, les données sont transmises entre le client et la fonction cible côté serveur au format binaire. La nature binaire des charges utiles de gRPC est l'un des contributeurs à sa réputation d'être plus rapide que les approches alternatives. Les programmes utilisant gRPC peuvent s'exécuter en nanosecondes, par opposition aux millisecondes qui sont typiques lors de l'utilisation de données textuelles.

gRPC a suscité un intérêt considérable de la part de la communauté des développeurs depuis que la spécification a été publiée en open-source par Google en février 2015. (Voir la figure 1.)

Figure 1: Selon Google Trends, le gRPC suscite un intérêt croissant depuis sa sortie en 2015

Figure 1: Selon Google Trends, le gRPC suscite un intérêt croissant depuis sa sortie en 2015

Les entreprises, grandes et petites, utilisent gRPC, y compris des marques notables telles que Slack, Microsoft, Condé Nast, Netflix et Cisco, pour n'en nommer que quelques-unes.

Nous à ProgrammableWeb estiment que le gRPC est un élément permanent du paysage informatique. En fait, compte tenu des tendances présentées ci-dessus, il y a tout lieu de penser que l'adoption de la technologie continuera de croître, en particulier parmi les entreprises qui ont besoin de systèmes ultra-rapides pour répondre à leurs besoins critiques.

Avoir une large compréhension de gRPC dans le contexte d'alternatives populaires telles que REST et GraphQL est essentiel pour les entreprises qui réalisent ou envisagent de développer au niveau de l'entreprise des systèmes distribués à l'échelle du Web. Présenter une telle compréhension de gRPC est le but de cet article ainsi que des autres qui suivront dans cette série.

Dans cet article, nous allons discuter de l'émergence du gRPC dans le paysage de l'informatique distribuée. Nous allons également présenter un aperçu de la spécification gRPC et montrer comment la spécification est implémentée à l'aide d'une API de démonstration gRPC créée spécialement pour cette série d'articles. Les articles suivants de cette série examineront en détail des aspects spécifiques de gRPC, allant de la présentation détaillée d'une application gRPC de démonstration complexe contenant à la fois les composants client et serveur à un ensemble d'entretiens approfondis avec un certain nombre de entreprises qui ont implémenté gRPC dans le cadre de leur pile technologique. Nous examinerons également les problèmes et les solutions liés au travail avec gRPC à grande échelle. Nous allons en couvrir beaucoup. Mais, comme pour tout voyage, nous devons commencer par le début et ce début commence par comprendre le besoin et l'histoire dont le gRPC est né. Essentiellement, l'héritage de gRPC consiste à distribuer des fonctions discrètes sur un réseau d'ordinateurs.

La nécessité d'une communication inter-processus

Le besoin de partager les ressources informatiques existe depuis le début de la technologie numérique. Le partage des données était le point de départ. Les entreprises devaient déplacer des données d'un ordinateur à un autre afin de traiter les informations d'une manière propre à chaque système. Par exemple, il n'était pas inhabituel pour une banque de partager les informations sur l'historique des prêts d'un client avec une autre banque souhaitant déterminer sa solvabilité.

Cependant, le simple partage de données avait une utilité limitée en termes de maximisation de l'efficacité informatique. Il est vrai que le partage des données a permis de gagner du temps, mais chaque ordinateur devait tout de même traiter ces informations en utilisant son propre ensemble d'algorithmes. Plusieurs fois, un seul algorithme a été dupliqué sur de nombreuses machines. Une telle redondance était inefficace. Si un algorithme était mis à jour, cette mise à jour devait être propagée sur toutes les machines l'utilisant. C'est une entreprise risquée. Ainsi, la notion de permettre à un seul ordinateur de partager ses algorithmes ou procédures avec d'autres ordinateurs a évolué. Le partage un-à-plusieurs d'une procédure est devenu connu sous le nom d'appels de procédure distante (RPC).

Appels de procédure à distance, le précurseur de gRPC

L'idée de base derrière RPC est qu'une procédure (également appelée fonction) qui s'exécute sur une machine peut être partagée par un certain nombre d'autres machines à différents emplacements sur le réseau. (Voir la figure 2 ci-dessous)

Figure 2: RPC permet à un ordinateur de partager une procédure (fonction) avec d'autres ordinateurs

Figure 2: RPC permet à un ordinateur de partager une procédure (fonction) avec d'autres ordinateurs

L'avantage de RPC est qu'il réduit la redondance du système. Quand vient le temps de mettre à niveau la procédure, toutes les modifications ont lieu dans un seul endroit. Il n'est pas nécessaire de copier la procédure sur d'autres machines. Par conséquent, l'activité de mise à niveau est confinée et contrôlable. Tant que l'interface publique de la procédure (souvent appelée «contrat technique») – les structures des données entrant et sortant de la procédure – reste inchangée, la mise à niveau est opaque pour les machines utilisant la procédure. Toutefois, si le contrat technique change, ce que l'on appelle «rupture du contrat», des problèmes peuvent survenir. Ainsi, si l'utilisation de RPC augmente l'efficacité globale des systèmes, elle n'est pas sans risque. C'est quelque chose à garder à l'esprit lorsque nous examinerons les détails de la construction et de l'utilisation des API gRPC plus tard dans cette série.

Implémentations de RPC

Comme mentionné ci-dessus, RPC existe depuis un certain temps. On en trouve des traces dans la programmation Unix dès la fin des années 1970 et jusque dans les années 1980. Aussi, au niveau conceptuel, RPC apparaît indirectement dans de nombreuses technologies. Par exemple, la technologie des procédures stockées, qui est un moyen courant d'incorporer des fonctions dans une base de données, trouve ses racines dans la pensée RPC.

Procédures stockées

Les procédures stockées sont invariablement mentionnées dans le même souffle que les systèmes de gestion de bases de données relationnelles (SGBDR). L'idée générale était que la logique métier réutilisable pour la mise à jour d'une base de données était maintenue à proximité de la base de données elle-même (souvent dans le même SGBDR). La façon dont fonctionne une procédure stockée est qu'au lieu de demander à un programme client de préparer les données pour le stockage, puis d'exécuter les requêtes SQL INSERT ou UPDATE nécessaires pour stocker ces données dans la base de données, la logique de l'insertion ou de la mise à jour est stockée en tant que fonction nommée dans le système de base de données lui-même. Le listing 1 ci-dessous montre un exemple de deux procédures stockées.

USE MyDatabase

GO
-- the stored procedure that validates data
CREATE PROCEDURE (dbo).(usp_customer_validate)
	@first_name varchar(75),@last_name varchar(75), _
      @gender varchar(6),@email varchar(75), _
	@postal_code varchar(12)
AS
BEGIN
      -- the validation code goes here
END

GO

-- the stored procedure that inserts data
CREATE PROCEDURE (dbo).(usp_customer_insert)
	@first_name varchar(75),@last_name varchar(75), _
      @gender varchar(6),@email varchar(75), _
	@postal_code varchar(12)
AS
BEGIN 
      -- use the stored procedure usp_customer_validate
      EXEC usp_customer_validate @first_name, @last_name, @gender, @postal_code
	
      -- insert the data
      INSERT INTO customers (first_name,last_name,gender,email,postal_code)
	VALUES  (@first_name,@last_name,@gender,@email,@postal_code)
END

GO

Liste 1: Un exemple de procédures stockées qui s'exécutent sur des serveurs de base de données SQL comme ceux d'Oracle ou de Microsoft

Une procédure stockée est nommée usp_customer_validate. L'autre est nommé usp_customer_update. La procédure stockée usp_customer_validate valide les informations client transmises en tant que paramètres à la procédure. La procédure stockée usp_customer_insert utilise le usp_customer_validate procédure stockée pour valider les données client soumises. Puis, usp_customer_insert insère un enregistrement dans la table nommée clients. La table clients fait partie d'une base de données fictive nommée Ma base de données.

Lorsqu'une procédure stockée est en cours de lecture, les clients n'ont qu'à établir une connexion réseau à la base de données et à transmettre les données brutes à la procédure stockée donnée dans la base de données. Le listing 2 ci-dessous est un exemple qui montre comment appeler une procédure stockée à l'aide de .NET / C #.

var connectionString = "Server=(local);DataBase=MyDatabase;Integrated Security=SSPI";
using (SqlConnection sqlConnection1 = new SqlConnection(connectionString)) {
using (SqlCommand cmd = new SqlCommand()) {
  Int32 rowsAffected;

  cmd.CommandText = "usp_customer_insert";
  cmd.CommandType = CommandType.StoredProcedure;
  
  cmd.Parameters.Add (new SqlParameter ("@first_name", "Dick"));
  cmd.Parameters.Add (new SqlParameter ("@last_name", "Tracy"));
  cmd.Parameters.Add (new SqlParameter ("@gender", "male"));
  cmd.Parameters.Add (new SqlParameter ("@email", "dick.tracy@example.com"));
  cmd.Parameters.Add (new SqlParameter ("@postal_code", "02122"));
  cmd.Connection = sqlConnection1;

  sqlConnection1.Open();

  rowsAffected = cmd.ExecuteNonQuery();
}}

Liste 2: Code client C # qui utilise une procédure stockée pour insérer des données dans une base de données SQL

La procédure stockée effectue ensuite le travail de validation et de stockage des données. La validation et le stockage des données sont opaques pour le client. Tout ce travail se fait en interne dans la base de données. Si le schéma de la base de données doit être modifié, dans ce cas, les requêtes SQL internes à la procédure stockée sont mises à jour. Le comportement du client ne change pas du tout. C'est essentiellement la dynamique du RPC. La fonction existe à un endroit sur le réseau et est appelée par des clients situés ailleurs.

RPC et ERP

RPC existe également dans les systèmes ERP (Enterprise Resource Planning). SAP, une technologie ERP majeure, prend en charge une fonctionnalité nommée Remote Function Call (RFC). RFC est essentiellement RPC pour les systèmes d'entreprise SAP. (Voir la figure 3 ci-dessous.)

Figure 3: Les appels de fonction à distance (RFC) de SAP sont une variante du style d'architecture RPC

Figure 3: Les appels de fonction à distance (RFC) de SAP sont une variante du style d'architecture RPC

RFC permet aux systèmes SAP d'utiliser des fonctions qui résident sur des machines SAP externes et non SAP à condition que la machine distante dispose des bibliothèques requises pour prendre en charge la RFC. Les appels de fonction à distance sous SAP sont un bon exemple d'utilisation des techniques RPC pour étendre la puissance de calcul d'un système d'entreprise au-delà des capacités traditionnelles d'un ERP classique.

RPC sous Java

Aujourd'hui, l'une des implémentations les plus courantes de RPC utilise une technologie appelée Java Remote Method Invocation (Java RMI). Java RMI permet à tout ordinateur compatible Java Virtual Machine (JVM) d'exposer des fonctions Java à d'autres machines exécutant une machine virtuelle Java avec les bibliothèques Java RMI.

Java RMI est essentiellement une architecture client-serveur mais avec une petite torsion. Au lieu d'appeler directement un point de terminaison comme vous le feriez dans une interaction requête / réponse API RESTful typique sur Internet, les clients doivent obtenir une référence au serveur RMI qui a une méthode distante particulière d'intérêt. (Voir la figure 4 ci-dessous). Il y a beaucoup de va-et-vient qui doivent se produire avant même qu'une conversation puisse avoir lieu.

Figure 4: L'appel de méthode à distance Java permet d'accéder à des fonctions sur un serveur distant

Figure 4: L'appel de méthode à distance Java permet d'accéder à des fonctions sur un serveur distant

Le listing 3 ci-dessous montre le code de la classe Java MyClient dont une partie a été illustrée ci-dessus dans la figure 4. La classe MyClient effectue le travail d'interaction avec le serveur Java RMI.

Listing 3: Le code client Java RMI qui appelle une méthode distante

Liste 3: Le code client Java RMI qui appelle une méthode distante

La façon dont le code client fonctionne est qu'il obtient une référence à la fonction distante en effectuant une recherche sur le registre d'objets distants en cours d'exécution sur la machine serveur, comme indiqué ci-dessus dans le listing 3, ligne 8. Une fois qu'une connexion au serveur distant est établie et une référence est obtenue à partir du registre d'objets distants, cette référence est utilisée pour appeler la méthode distante (liste 3, ligne 18) sur le serveur en utilisant les données d'entrée fournies par l'utilisateur à la ligne 14. Le résultat de l'appel est ensuite renvoyé. à l'utilisateur à la ligne 21.

Comme vous pouvez le voir même à partir du simple exemple montré ci-dessus, Java RMI est une technologie puissante qui peut rendre l'informatique distribuée beaucoup plus efficace. Mais cela présente un inconvénient fondamental. Lors de l'utilisation de Java RMI, le client et le serveur doivent parler Java. Par exemple, vous ne pouvez pas facilement demander à un code .NET d'appeler directement le code RMI Java. Dans le cas de .NET, il existe des solutions de contournement telles que JNBridge. Mais, globalement, Java RMI est destiné à être une solution Java. Le support de développement Polyglot ne faisait pas partie de la conception du produit.

Pourtant, malgré le manque de prise en charge du développement polyglotte prêt à l'emploi, Java RMI a intégré le RPC au courant dominant de l'informatique distribuée pour les environnements matériels de base et a jeté les bases des futures générations de RPC, y compris gRPC.

Cependant, alors que Java RMI était un net positif pour le développement d'API basé sur RPC, la prise en charge du développement polyglotte restait un besoin majeur, d'autant plus que PHP, Python et .NET gagnaient en popularité dans le paysage du développement. Ce besoin a été satisfait par XML-RPC.

XML-RPC

XML-RPC, comme son nom l'indique, est un protocole d'appel de procédure à distance basé sur un langage de balisage extensible (XML). XML-RPC est une spécification indépendante du langage. Tout langage de programmation capable de traiter XML peut prendre en charge XML-RPC.

XML-RPC existe depuis un certain temps. La spécification a été publiée pour la première fois en 1998. Bien que l'intérêt pour XML-RPC ait diminué ces dernières années, le protocole est toujours utilisé. WordPress utilise XML-RPC pour permettre la publication de clients externes sur les téléphones portables et les tablettes. De plus, Atlassian fournit une interface XML-RPC dans son produit Confluence. (Pour une liste complète des API prenant en charge XML-RPC, effectuez une recherche sur le terme, xml-rpc dans le ProgrammableWeb Répertoire API trouvé ici.)

En termes simples, les clients XML-RPC utilisent HTTP standard pour envoyer des données à des fonctions prédéfinies qui résident sur un serveur XML-RPC. La lingua franca de l'échange de données est XML qui est formaté selon la norme définie par la spécification XML-RPC. Comme mentionné ci-dessus, XML-RPC est indépendant du langage, il est donc tout à fait concevable qu'un programme client puisse être écrit en C alors que le serveur est programmé en Java. Une telle flexibilité fait de XML-RPC une alternative viable aux frameworks RPC spécifiques au langage tels que Java RMI et pour beaucoup, c'était un grand pas vers la séparation des préoccupations entre le client et le serveur; un principe extrêmement important de la révolution des API.

Mais il y a un prix à payer pour la flexibilité. XML est un format basé sur du texte. Ainsi, par rapport à une approche binaire, les données transmises en échange de données sont très volumineuses. Alors que certains serveurs API XML-RPC peuvent accepter des données dans un format compressé tel que .zip ou .rar, la spécification XML-RPC décrit le type de contenu d'une demande et d'une réponse au format texte / xml. Cela peut être un obstacle pour les applications qui doivent échanger des données à des vitesses de l'ordre de la nanoseconde. Mais, pour les applications qui tolèrent les échanges au niveau des millisecondes, XML-RPC est toujours un framework d'API exploitable, c'est pourquoi certaines API continuent de s'appuyer dessus.

L'utilisation de XML-RPC suit le même modèle de demande / réponse que dans tout échange de données HTTP. Les détails d'un tel échange sont décrits dans les sections qui suivent.

Anatomie de la requête XML-RPC

Le listing 4 ci-dessous montre un exemple du XML pour une requête qui appelle une procédure que nous avons nommée "ping" qui est hébergée sur le même serveur qui héberge une API XML-RPC.

Listing 4: Un simple appel à la méthode ping sur le serveur API XML-RPC de démonstration

Liste 4: Un simple appel à la méthode ping sur le serveur API XML-RPC de démonstration

Contrairement à REST, dans lequel les seules fonctions exposées par une API sont les verbes HTTP implicites tels que GET, POST, PUT et DELETE, le nom de la fonction / procédure ciblée par l'appel à une API XML-RPC est encodé directement dans le XML structure envoyée au serveur API. Les paramètres associés à la fonction sont également codés.

Dans le listing 4 ci-dessus, la balise comme indiqué à la ligne 2 est l'élément XML qui définit la méthode qui est appelée sur l'API XML-RPC. Cette La balise est requise par la spécification XML-RPC. le La balise à la ligne 4 du listing 4 est l'élément qui contiendra les paramètres qui sont envoyés à l'API avec le nom de la procédure. Chaque élément contiendra un ou plusieurs éléments. Chaque aura un élément enfant comme indiqué dans le listing 4 à la ligne 6. Le contenu du l'élément variera en fonction du type scalaire de la valeur. Dans le cas du listing 4 ci-dessus, le type scalaire de la valeur du paramètre à la ligne 7 est une chaîne. Ainsi, la chaîne, J'aime la glace, est enfermé dans un ensemble de éléments.

Le tableau 1 ci-dessous montre les six types de scalers définis dans la spécification XML-RPC.

Marque Type Exemple
ou entier signé de quatre octets -22
0 (faux) ou 1 (vrai) 1
chaîne J'aime la glace
nombre à virgule flottante signé double précision -22,214
date / heure 19980717T14: 08: 55
binaire encodé en base64 eW91IGNhbid0IHJlYWQgdGhpcyE =

Tableau 1: Les types scalaires XML-RPC

Comme mentionné ci-dessus, un échange de données XML-RPC suit le modèle de requête / réponse HTTP. Un client envoie le XML en tant que HTTP POST au point de terminaison de l'API. En général, il n'y a qu'un seul point de terminaison sur un serveur XML-RPC. Le projet de démonstration Simple XML-RPC Server qui accompagne cet article écoute les requêtes XML-RPC entrantes à la racine HTTP, /.

Le serveur accepte la demande et la valide pour s'assurer que les en-têtes de demande et le XML dans le corps sont bien formés. La demande est ensuite transmise aux composants internes du serveur pour la désérialisation et le traitement.

Une fois le traitement réussi, le résultat sera sérialisé en XML qui est renvoyé au client dans une réponse HTTP. Jetons un coup d'œil aux détails de la réponse.

Anatomie de la réponse XML-RPC

Le listing 5 ci-dessous montre un exemple du XML reçu dans une réponse HTTP de la procédure API XML-RPC nommée ping comme décrit ci-dessus. Le XML est structuré selon le format décrit dans la spécification XML-RPC.

Listing 5: Une réponse à la méthode ping sur le serveur API XML-RPC de démonstration

Listing 5: Une réponse à la ping méthode sur le serveur d'API XML-RPC de démonstration

La déclaration XML standard est faite à la ligne 1 du listing 5 ci-dessus. Il est suivi à la ligne 2 par l'élément XML-RPC requis cela indique que le XML est une réponse à une demande adressée à l'API. La ligne 3 a le balise qui contiendra diverses réponses éléments. Dans chaque élément il y aura un marque. La balise value contiendra une autre balise nommée en fonction du type scalaire de la valeur. Dans ce cas, à la ligne 6 du listing 5, il n'y a qu'un seul paramètre renvoyé. C'est un type scalaire, avec la valeur ("J'aime la glace").

Remarquez à la ligne 6, la valeur du paramètre renvoyé par la chaîne est formatée sous la forme d'un tableau JavaScript. Le formatage de la chaîne en tant que tableau est complètement arbitraire. Le renvoi de paramètres de chaîne qui représentent un tableau est une décision prise par le concepteur de la bibliothèque d'API que vous avez choisie pour aider à mettre en place votre API XML-RPC. Il est tout aussi possible qu'une autre bibliothèque puisse renvoyer des chaînes en tant qu'objet, ou simplement en tant que chaîne simple. Ainsi, ceux qui utilisent une API XML-RPC particulière feraient bien de connaître le format de la ou des chaînes renvoyées par un appel à une API XML-RPC.

Travailler avec des tableaux

XML-RPC prend en charge les tableaux en tant que chaîne pour les éléments enfants dans l'élément XML puis dans l'élément enfant .

Le listing 6 ci-dessous montre un exemple du XML qui décrit un appel à la procédure XML-RPC nommée ajouter fait sur l'API de démonstration Simple XML-RPC Server. Le XML passe un tableau d'entiers, dans ce cas (0,2,2) à une procédure nommée, ajouter. Le tableau est défini aux lignes 8 à 19.

Listing 6: Un appel à une procédure XML-RPC, add, qui prend un tableau d'entiers à additionner

Liste 6: Un appel à une procédure XML-RPC, add, qui prend un tableau d'entiers à additionner

Notez qu'il existe une collection de éléments dans le séquence. Chaque element contient un élément qui décrit le type de données de la valeur. La ligne 10 du listing 6 ci-dessus décrit la valeur du premier élément du tableau comme ceci: 0. L'élément est défini dans la spécification XML-RPC pour décrire un entier. Un tableau de chaînes définirait chaque valeur comme .

Le listing 7 ci-dessous montre la réponse XML de la procédure XML-RPC nommée add.

Listing 7: La réponse d'une procédure XML-RPC, add, qui résume un tableau d'entiers

Liste 7: La réponse d'une procédure XML-RPC, add, qui résume un tableau d'entiers

En plus de soumettre des tableaux, XML-RPC spécifie également un moyen de soumettre des paramètres nommés à une procédure. Ceci est fait en utilisant le élément.

Utilisation de structures et de paramètres nommés

Le Listing 8 montre un exemple du XML qui décrit un appel à la procédure XML-RPC bavarder; l'une des méthodes de l'API de démonstration Simple XML-RPC. Le XML transmet les paramètres avec les noms message et limite en tant que structure définie par XML-RPC. Le paramètre nommé message a la valeur "Salut!". Le paramètre nommé limite a une valeur entière de 3.

Listing 8: Un appel à une procédure personnalisée, chatter, qui utilise des valeurs nommées dans une structure qui comprend deux paramètres nommés.

Liste 8: Un appel à une procédure personnalisée, bavarder, qui utilise des valeurs nommées dans une structure qui comprend deux paramètres nommés

Le listing 9 ci-dessous montre la réponse XML de l'appel de procédure bavarder. Notez que le XML montre que le résultat de l'appel est un tableau de XML-RPC struct. Chaque struct contient le message envoyé dans la demande ainsi qu'une valeur compter, qui rapporte l'ordre de la struct dans le tableau. Émission des messages dans un tableau et rapport des valeurs message et compter est la logique personnalisée qui est programmée dans le bavarder procédure.

Listing 9: Procédure API XML-RPC, le chatter répond avec un tableau de valeurs, chacune contenant une structure

Liste 9: Procédure d'API XML-RPC, le chatter répond avec un tableau de valeurs, chacune contenant une structure

Pourquoi répandons-nous autant d'encre sur les exemples XML-RPC? Comme nous venons de le démontrer, le format d'un tableau XML-RPC peut avoir pour résultat de créer un contenu XML volumineux qui peut rendre les requêtes et réponses HTTP très volumineuses et donc lentes à transmettre et à recevoir sur un réseau. Les développeurs qui envisagent d'utiliser XML-RPC doivent être conscients de cette lacune. Comme mentionné ci-dessus, la taille d'une requête et d'une réponse HTTP peut être réduite en utilisant la compression .zip ou .rar. En outre, les développeurs qui écrivent la logique qui renvoie des données dans des tableaux peuvent utiliser la pagination pour renvoyer des tableaux sous la forme d'une série de blocs de données livrés sur plusieurs conversations HTTP. L'important est de savoir que les tableaux sous XML-RPC peuvent devenir assez volumineux et qu'en tant que tels, la grande taille devra être prise en compte.

XML-RPC a apporté beaucoup de flexibilité pour traiter les appels de procédure distante et il a permis aux développeurs de travailler avec RPC d'une manière indépendante du langage. Et la spécification était facile à maîtriser. Mais, XML-RPC présente un inconvénient majeur. La spécification ne permet pas de définir des éléments XML personnalisés. Par exemple, vous ne pouvez pas ajouter un élément au message XML comme moyen d'organiser un certain nombre d'interactions de requête / réponse HTTP sous une seule transaction.

Il s'avère qu'au moment de la création de XML-RPC en 1998, les travaux sur son successeur étaient déjà en cours. Cette spécification plus récente était le protocole SOAP (Simple Object Access Protocol). SOAP était essentiellement destiné à amener XML-RPC au niveau suivant.

SAVON

Le protocole SOAP (Simple Object Access Protocol) est très similaire à XML-RPC en ce qu'il s'agit d'un protocole d'échange d'informations codées en XML contre une procédure ou un service qui réside sur Internet. La spécification a été rendue publique en 1999 et est publiée par le W3C en tant que norme ouverte.

Comme XML-RPC, SOAP utilise XML comme format de message et prend en charge HTTP comme mécanisme de transport. Cependant, SOAP va au-delà des capacités de XML-RPC de plusieurs manières. Premièrement, SOAP peut être utilisé par divers protocoles de transport en plus de HTTP, par exemple SMTP et FTP. (Le modèle typique consiste à utiliser HTTP pour l'échange de données synchrone et SMTP ou FTP pour les interactions asynchrones).

Une autre différence clé est que SOAP utilise un schéma XML standard (XSL) pour encoder XML. En outre, les développeurs peuvent créer leurs propres schémas XML pour ajouter des éléments XML personnalisés aux messages SOAP. Enfin, SOAP est généralement utilisé avec le Web Service Description Language (WSDL). Cela signifie que les développeurs et les machines peuvent inspecter un service Web prenant en charge SOAP pour découvrir les spécificités de l'accès au service sur le réseau ainsi que la manière de structurer les messages de demande et de réponse SOAP pris en charge par le service. La découverte via WSDL rend la programmation de services Web à l'aide de messages SOAP une entreprise moins lourde.

La structure de base d'un message SOAP

Au niveau de base, la structure d'un message SOAP est une hiérarchie dans laquelle l'élément racine est le . Cet élément racine peut contenir trois éléments enfants. (Voir la figure 5 ci-dessous.)

Figure 5: La structure de base d'un message SOAP

Figure 5: La structure de base d'un message SOAP

L'élément , comme indiqué dans la figure ci-dessus est nécessaire. Les éléments et sont facultatifs. Si l'élément, est présent, il doit s'agir du premier élément enfant dans le parent. Si l'élément est présent, il doit être un enfant de l'élément, .

Le tableau 2 ci-dessous décrit le but de chaque élément.

Élément La description
Enveloppe Décrit le document XML en tant que message SOAP.
Entête Contient des informations d'en-tête.
Corps Informations pertinentes pour la demande ou la réponse en cours
Faute Contient des informations sur les erreurs survenues lors du traitement des messages.

Comme mentionné ci-dessus, les messages SOAP sont codés en XML. Le listing 10 ci-dessous montre un message SOAP avec les informations d'en-tête HTTP intégrées dans la requête HTTP. Les lignes 1 à 5 sont les entrées d'en-tête de la requête HTTP.

Listing 10: Une demande de service Web écrite en SOAP

Liste 10: Une demande de service Web écrite en SOAP

Les lignes 1 et 2 sont les habituelles PUBLIER et Hôte en-têtes. La ligne 3 déclare que le Content-Type de la demande contient du XML codé selon la spécification SOAP. La ligne 4 est la longueur du corps de la requête, qui est utilisée d'une manière similaire à l'utilisation sous XML-RPC.

La ligne 5 est l'attribut obligatoire, SOAPAction qui définit l'intention de la demande SOAP. Plusieurs fois, les services Web qui prennent en charge SOAP acheminent la demande vers les composants internes d'un service en fonction des informations contenues dans le SOAPAction entête.

Les lignes 7 à 17 du listing 10 ci-dessus décrivent le message SOAP. Notez que deux schémas XML sont déclarés aux lignes 8 et 9. Le schéma SOAP est lié au nom de l'espace de noms, savon à la ligne 8. L'espace de noms, m est défini à la ligne 9. Les éléments Enveloppe (Ligne 8), Entête (Ligne 10) et Corps (Ligne 12) font partie du savon espace de noms. Les éléments GetStockPriceRequest (Ligne 13) et StockName (Ligne 14) sont des éléments personnalisés associés à l'espace de noms XML m.

Les éléments personnalisés, GetStockPriceRequest et StockName, sont spécifiques au service Web qui provisionne l'API SOAP cible. Comme on pouvait s'y attendre, le service Web utilise les informations de ces éléments personnalisés pour traiter le contenu du message SOAP d'une manière qui est particulière au service. La prise en charge des espaces de noms personnalisés rend SOAP extensible pour un nombre infini de cas d'utilisation de manière contrôlée.

En termes de transport et de consommation, le message présenté ci-dessus dans le Listing 10 peut faire partie d'un échange HTTP synchrone entre un client HTTP et un serveur Web. Cependant, comme mentionné ci-dessus, SOAP est indépendant du transport. Ainsi, le message pourrait tout aussi bien être envoyé qu'un e-mail à un serveur de messagerie fonctionnant sous le protocole de transport de courrier simple (SMTP). L'intelligence dans le serveur de messagerie prendrait alors le message SOAP et le traiterait. Une fois terminé, l'intelligence de traitement envoie la réponse en tant que réponse au courrier électronique d'origine à un moment ultérieur. Une conversation par e-mail est essentiellement un échange asynchrone.

Le listing 11 ci-dessous montre une réponse SOAP à la demande faite dans le listing 10 ci-dessus. Contrairement à la spécification XML-RPC qui distingue et messages, la spécification SOAP ne fournit pas une telle distinction dans le schéma des espaces de noms. Il n'y a pas ou . La distinction est plutôt laissée aux éléments XML définis dans un espace de noms personnalisé.

Le listing 11 ci-dessous montre une réponse fictive à la GetStockPriceRequest fait plus tôt dans le listing 10. Notez l'élément personnalisé à la ligne 13. Cette sémantique de l'élément décrit le corps du message comme une réponse. Le nom de l'élément est GetStockPriceResponse est arbitraire. Il aurait tout aussi bien pu être nommé TickerResponse. La chose importante à comprendre est que la distinction demande / réponse est déléguée aux éléments définis dans l'espace de noms XML personnalisé.

Listing 11: Une réponse de service Web écrite en SOAP

Liste 11: Une réponse de service Web écrite en SOAP

Notez également que les éléments indiqués aux lignes 14 à 16 du listing 11 ci-dessus font partie de l'espace de noms personnalisé m et contiennent des informations sémantiquement pertinentes pour la réponse. Ceci est un autre exemple d'utilisation d'un espace de noms personnalisé pour étendre la signification d'un message SOAP. La combinaison du schéma SOAP standard avec des schémas personnalisés fait de SOAP un format très polyvalent pour l'échange de données.

Avantages et inconvénients

L'utilisation de SOAP présente de nombreux avantages. Le protocole utilise HTTP comme transport, mais il peut également utiliser des protocoles tels que SMTP et FTP. SOAP prend en charge la réflexion via WSDL et il est indépendant du langage, ce qui est un avantage significatif dans les grandes entreprises qui prennent en charge une variété de langages de programmation mais qui ont besoin d'une lingua franca d'échange de messages. Aussi, à un moment donné dans les premiers jours des services Web, SOAP était très populaire. Ainsi, il y a encore beaucoup d'implémentations SOAP héritées qui doivent être maintenues, ce qui en fait un moyen très attrayant de gagner de l'argent.

Si COBOL nous a appris quelque chose, c'est que l'ancien code ne meurt pas. Il s'estompe lentement après des décennies et des décennies d'entretien. Il y aura une demande importante dans un avenir prévisible pour que les développeurs maintiennent et étendent le code SOAP existant. Au fur et à mesure que l'offre de développeurs SOAP diminue, les développeurs restants qui connaissent bien le SOAP pourront obtenir des salaires plus élevés. La loi de l'offre et de la demande favorise les développeurs SOAP.

Comme tout modèle d'architecture d'API, cependant, l'utilisation de SOAP présente des inconvénients. Le principal inconvénient est que, comme SOAP est basé sur XML qui à son tour peut être très détaillé (en particulier avec les espaces de noms personnalisés), vous devez déplacer beaucoup de texte sur le réseau pour faire avancer les choses. De plus, la spécification SOAP prend du temps à maîtriser. Le protocole est très détaillé. Par exemple, alors que la spécification prend en charge la notion d'utilisation de messages SOAP pour effectuer des appels de procédure à distance, la mise en œuvre effective de RPC sous SOAP nécessite de mélanger des éléments SOAP standard avec des éléments personnalisés. Les éléments personnalisés doivent fournir la sémantique RPC requise par la spécification SOAP mais que le schéma SOAP ne fournit pas. En d'autres termes, vous ne pouvez pas implémenter le RPC réel en utilisant uniquement les éléments XML définis dans le schéma SOAP standard. Vous devez créer des éléments personnalisés pour combler le vide.

Le dernier inconvénient est que SOAP est vieux. Il existe depuis 1999. À l'époque où Internet était jeune, SOAP apportait beaucoup de puissance à la programmation basée sur les services Web. But now billions of people and more importantly, billions if not trillions of machines in the IoT universe use the Internet every minute of every day. The bulkiness of the protocol is an impediment to achieving the speeds needed to operate at this degree of web-scale.

SOAP met a real need at one time. But that was then and this now. Today enormous amounts of data need to move over the network at lightning-fast speeds. People want to view their movies now, not in an hour. Stock trading takes place in terms of nanoseconds, not milliseconds. Faster, more efficient protocols beyond the capabilities of SOAP and XML-RPC are required. One protocol that meets the need at hand is gRPC.

The emergence of gRPC

As mentioned above, gRPC was created at Google to meet the company's need for speed for code that runs cost-efficiently at web-scale. While it's true that all companies want code that executes fast, Google's needs redefine the word fast. Critical to Google's operation is its ability to index the entire Internet. In order to perform such a feat, three things are needed: first lightning speed, vast scale, and extraordinary machine efficiency. Google has the computing horsepower and it has the networking capabilities to achieve its goal. Where things get touchy is around the speed by which data that gets passed around between applications.

Meeting the Need for Speed

For better or worse, most of the data that get passed back and forth on the Internet is structured according to text-based formats. HTML is text-based as is XML. These are bulky formats in that they require opening and closing tags. For example, just to transit the ten characters that make up the name, John James, in a structured manner via standard HTML requires 151 characters as shown in Listing 12 below.




   


John
James

Listing 12: HTML is a bulky format for transmitting information

Transmitting the same information in XML requires ninety-four characters which is fewer than the number of characters in HTML. Still, it's bulky. (See Listing 13 below.)



  Jone
  James

Listing 13: XML offers a more concise way to structure data than HTML

JSON, another popular text-based data format is even more concise than XML. Transmitting the name, John Doe in JSON requires only 47 characters as shown in Listing 14 below.

{
  "first_name": "John",
  "last_name": "James"
}

Listing 14: JSON is intended to be a concise text-based data format

While JSON is a more concise format than either HTML or XML, in terms of Google's need for speed and efficiency, it's still too bulky. Table 3 below illustrates the actual bit count of the name John James as expressed in the HTML, XML, and JSON shown in the examples above. (Remember, each character is 1 byte which equals 8 bits.)

Exemple Word Count Bit count
Listing 4: HTML 151 1208
Listing 5: XML 96 768
Listing 6: JSON 47 376

Table 3: The number of bits required to transmit the name, John James according to first name and last name.

When you consider that the actual bit count for the 10 characters that make up the name — John James in only 80 bits of white space characters included — all of the formats illustrated above in Table 1 are overwhelmingly large when you have needs on the order of Google's. Especially when it comes to the packaging and transmission of data across a network. Something better was needed. That something better is Protocol Buffers.

In gRPC, all data is transmitted in binary format. Information is serialized into a compact collection of bits and then sent over the network. Then, when the bits reach the target destination they are deserialized back into text. The binary format used in gRPC is protocol buffers. Using protocol buffers make data fast to transmit, but it does come with a cost and that cost is incurred due to the overhead that comes with describing data.

The Cost and Benefits of Binary Data Formats

When you take a look at the HTML, XML, and JSON shown in the listings above you'll notice that most of the text in the examples is used to describe the data. Take a look again at the JSON example:

{
  "first_name": "John",
  "last_name": "James"
}

The name John James takes ten characters including white space. But, the JSON representation of the name takes 47 characters. Those extra 37 characters are used to describe and structure the data. It's necessary. Without that description, we have no idea what the data is about. We don't know if the content of the JSON is a name, let alone if James is a first name or the last name. Thus, data descriptions are essential in terms of processing the data.

HTML, XML, and JSON are known as self-describing formats. The format tells you what the data is about just by looking at it. In the JSON example above, we know that John is the first name and James is the last name because the JSON properties first_name et last_name describe the fields directly.

Self-describing data formats are very useful but they are also very expensive to transmit over the network due to the added characters required. But, what if we used a data format that was not self-describing? What if both the source and the target of the information being transmitted had a "reference manual" by which one knew how to determine the segmentation and order of fields in the collection of bits being sent over the network? Removing self-description from a data structure dramatically reduces the actual size of the data that needs to move over the network.

The reduced size that a binary data format supports is the essential benefit of Protocol Buffers. But there's a tradeoff. With self-describing data, you do not need a "reference manual" common to both the source and the target. With Protocol Buffers you do.

Using Protocol Buffers is more costly in terms of the added complexity and processing required to serialize/deserialize the data and decipher meaningful information from the binary message. On the other hand, the benefit is that data moves faster once it's on the wire. When designing gRPC, Google chose to accept the costs to get the speed. When you index the Internet as a way of life, nanoseconds count. Hence, foundational to gRPC is the use of the Protocol Buffers binary format for transmitting data over the network.

Today the need for the sort of increased speed offered by binary data formats has gone well beyond Google. As more people want more data at an increasing rate, more companies are willing to accept the technical overhead that goes with supporting gRPC in order to reap its benefit. In short, gRPC is becoming mainstream. Knowing how it works and to what applications its best suited is no longer a luxury. It's essential.

The gRPC Framework in a Nutshell

gRPC is an API framework that supports point-to-point function calls over HTTP/2 (essentially, version 2 of the World Wide Web). The important thing to understand about gRPC is that it's a specification that can be implemented in any programing language capable of supporting the requirements described in the gRPC specification. As of this writing there are implementations in a variety of languages, including but not limited to GoLang, C#, C++, Java, Objective-C, Ruby, Node.js, and Python just to name a few.

Using HTTP/2 makes it so that gRPC supports bi-directional streaming (a feature of HTTP/2). While gRPC allows standard request/response interactions between client and server, gRPC's dependence on HTTP/2 means that a client can transmit data to a server in a continuous stream of bits and vice versa. Also, gRPC makes it possible for bi-directional streaming to occur simultaneously (another feature of HTTP/2). The result is that a client can stream data continuously to a server while that same server concurrently streams data back to the client.

Another key benefit of HTTP/2 has to do with the allocation of system resources, particularly on Linux. As you will see in ProgrammableWeb's case study regarding Ably.io's implementation of gRPC, HTTP/2 provides a workaround to the network connection limitations imposed by the Linux kernel. In HTTP/1.1, each request and response requires a sole connection. HTTP/2 supports multiple request/response exchanges over a single connection.

The other key point about gRPC is that as described previously, all data gets passed between client and server as binary data using Protocol Buffers. The definitions of the data types, known in gRPC parlance as messages, as well as the functions published by a gRPC API, are described in a .proto file which is known to both the client and server. You can think of a .proto file as the aforementioned "reference manual" that is used by both the client and server. Using a common .proto file is similar to the pattern of using an IDL (Interface Description Language) to facilitate inter-process communication between different languages in an RPC interaction. In fact, the .proto format is derived from IDL.

Figure 6 below illustrates the basic concepts behind gRPC. Notice that client and server communicate over HTTP/2 and that information can be exchanged as a single request/response event or as a continuous stream.

Figure 6: The schema that describes the gRPC API is defined in a .proto file that is shared by both client and server

Figure 6: The schema that describes the gRPC API is defined in a .proto file that is shared by both client and server

Also, notice that both the client and server reference the schema in a common .proto file to determine the structure of the messages that are to be serialized and deserialized between client and server. (In upcoming installments in this series we'll discuss the different techniques that are used to allow a gRPC server and its clients to share the schema defined in the .proto file.)

In addition, the gRPC schema in the .proto file contains the function signatures that are published by the server. The client will use this information to pass messages to a particular function, according to the published function declaration. The following is an example of a function declaration that would be defined in a .proto fichier.

rpc Add (Request) returns (Response) {}

WHERE:
rpc is a reserved protocol buffers keyword indicating that the function is a remote procedure call
Ajouter is the name of the function
(Request) indicates that the function has a single parameter of custom message type, Request
returns is a reserved protocol buffers keyword indicating prefacing the return type of the function
(Response) indicates that the function will return a custom message of type, Response

As you can see, working with a .proto file is an important aspect of working with a gRPC API. So let's take a detailed look at the .proto file that defines the Simple Service demonstration project that accompanies this article. Understanding the specifics of the protocol buffer's language specification is important for anyone intending to work with a gRPC API.

Defining the .proto File

Listing 15 below shows the contents of the protocol buffers .proto file that is the foundation of this article's demonstration API, Simple Service. Line 1 is the syntax statement that declares the version of the language specification used. As of this writing, the current version of Protocol Buffers is Version 3. But be advised, there are a lot of gRPC APIs in force that uses Version 2.

Listing 15, line 3 is the package declaration where simplegrpc is an arbitrary name for the package. A package is similar to a namespace in C++, C# or Java. It's a way of organizing messages and functions in the .proto file under a common name.

Listing 15: The .proto file that describes a simple gRPC API

Listing 15: le .proto file that describes a simple gRPC API

Lines 5 – 43 describe the messages that the Simple Service API supports. An interesting thing to note about the message declaration format is the use of a field index number when defining a field in the message. Let's take a look at the reasoning behind the use of index numbers.

Understanding Field Indexing in Protocol Buffers

Take a look at the description for the message type ChatterRequest.

message ChatterRequest {
    string data = 1;
    int32 limit = 2;
}

Notice that ChatterRequest has two fields, Les données et limit. le Les données field is of type chaîne. The field limit is of type int32. These are standard types defined in the Protocol Buffers language specification. Also, notice above that the field Les données is assigned an index number of 1, and limit is assigned an index number of 2. Using index numbers is particularly noteworthy because unlike a self-describing format such as JSON where field naming is explicit to the data structure, binary data in a serialized protocol buffer message lacks direct field naming. All that's known is the field length and hence the value assigned to the field as well as the index number.

Just knowing only a field's value and its index number is not useful. You need a way to determine what the field represents. This is where the .proto file comes into play. Imagine a message definition like so:

message Birthday {
    int32 day = 1;
    int32 month = 2;
    int32 year = 3;
}

Each field contains a number. However, were we to examine an instance of a Birthday message in pure binary format, how will we know what each number in the message means? Does that number represent a month? A year? A day?

In order to decipher the meaning behind a field's value, we use the .proto file as a "reference manual". In the Birthday example above, when it comes time to deserialize the binary message into a text format, all the logic has to do is look-up the field according to the known index number as defined in the message definition within the .proto fichier. Then the logic extracts the field name according to the field index number. All that's left to do to create a textual representation is to combine the field name and the field value. Figure 7 below illustrates the concept of field name determination using index number lookup.

Figure 7: Binary encoding under Protocol Buffers uses index numbers to segment and identify fields

Figure 7: Binary encoding under Protocol Buffers uses index numbers to segment and identify fields

Let's move on to how services are defined within the .proto fichier.

Understanding Service and Method Definitions

Line 45 in Listing 5 above is the beginning of the service declaration section. gRPC organizes all functions under a service name. In this case, the service name is SimpleService. Lines 46 – 57 define the signatures of the functions published by the API. The .proto file defines six functions, Ajouter, Substract, Multiply, Divide, Ping, et Chatter. The implementation of these function signatures is programmed later according to the language framework used. The Simple Service demonstration API is implemented using the Node.js platform so the logic for the functions will be programmed in server-side JavaScript files.

The functions Ajouter, Subtract, Multiply, et Divide do as their names imply. Each function takes a Request message as a parameter, defined at line 5 of Listing 6 shown above. (Naming the message Request is arbitrary.) The Request message has a single property (numbers) which is an array of floating-point values. Each function will perform its implied operation on the array of numbers submitted according to their order in the array. For example the function Add((2,3,4,5)) returns a Request message like so: {"result": 14} as shown in Figure 8 below.

Figure 8: The Simple Service Add function sums up all the numbers in the submitted array using BloomRPC as a gRPC client application

Figure 8: The Simple Service Ajouter function sums up all the numbers in the submitted array using BloomRPC as a gRPC client application

The function Divide((2,3,4,5)) returns the following Request message: {"result": 0.03333333333333333}. (See Figure 9, below.)

Figure 9: The Simple Service Divide function divides the numbers in the submitted array according to each number's position in the array

Figure 9: The Simple Service Divide function divides the numbers in the submitted array according to each number's position in the array

The function, Chatter (ChatterRequest) returns a stream of ChatterResponse messages as shown below in Figure 10.

Figure 10: The Simple Server function, Chatter returns ChatterResponse message in a stream

Figure 10: The Simple Server function, Chatter returns ChatterResponse message in a stream

Streams vs Arrays

As you can see from Figure 9 above, streaming is a powerful feature of gRPC and one that deserves additional attention.

Let's take another look at the function signature for (ChatterRequest) as defined above in Listing 5 because it's a good example of the implications of streaming under gRPC.

rpc Chatter (ChatterRequest) returns (stream ChatterResponse) {}

Notice how the stream is declared after the returns clause, (stream ChatterResponse). This means that an HTTP/2 connection will be established and a series of ChatterRequest messages will be sent or "streamed" over the wire for the duration of that connection. Depending on how the function is implemented, that connection will be closed once the stream transmission is complete. Or, it can be left open. It's up to the programmer.

If I wanted to make it so that Chatter (ChatterRequest) aggregated all the ChatterResponse message on the server-side in an array and then sent the array all as part of the function return, I would declare the function signature as:

rpc Chatter (ChatterRequest) returns (repeated ChatterResponse) {}

WHERE

repeated is a keyword that indicates an array of ChatterResponse messages.

Differentiating between the use of the keywords stream et repeated might seem like an inconsequential decision but it's not. Imagine that for some reason the result of the function Chatter is 500 million ChatterResponse messages, which is over 1 gigabyte of data. When a stream is in play, a remote client can process the results of the function as each ChatterResponse message comes in from the HTTP/2 connection. However, when the keyword, repeated is used and the whole array is returned from the function Chatter, the client needs to wait for over 1 gigabyte of data to download before processing can take place. Waiting for the download to complete can cause a myriad of problems all the way from process blocking to memory overload. However, this is not to say that returning entire arrays of data should be avoided. Choosing between working with a stream of data or an entire array is a decision that is appropriate to the particular use case at hand. Fortunately, gRPC offers the flexibility to support either technique.

Putting it All Together

gRPC is an important framework on the API development landscape. The use of Protocol Buffers, which is foundational to the gRPC specification offers a way to facilitate lightning-fast data transfers over the network. Also, because gRPC is intended to be run over HTTP/2, developers and consumers of gRPC APIs can enjoy the flexibility and power that comes with the streaming capabilities inherent in the HTTP/2 protocol.

There's a lot of benefit to using gRPC. However, there are tradeoffs. Working with gRPC requires a good deal of programming acumen. While other API technologies such as REST make it possible for a developer who is well-versed in the principles of HTTP to be productive, gRPC requires a lot more. You need to know how to serialize and deserialize information into binary data using Protocol Buffers and you need to be familiar with working with streams. While it's true there are many libraries that will do a lot of this work for you, the fact remains that even writing a simple client to consume data from a gRPC API requires a lot of know-how. But, if you're in an enterprise where nanosecond differences in speed could make a difference, absorbing the significant learning curve is a price well worth paying.

gRPC offers a lot and it's not going away. More companies are using it every day. Eventually, it will become an expected skill set among employers, on par with other known technologies such as REST, AJAX, and GraphQL.

Coming Up Next

The next installment of this series will take an in-depth look at how to develop a full-fledged gRPC application that has both client and server components.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *