Interfacer Python et
Erlang
by Christophe Romain <cro@tuxfamily.org>
1 Interfacer, pouquoi ?
Erlang est réellement une plateforme de développement aux
qualités exceptionnelles mais souffre d'un manque certain de
composants applicatifs. En effet, bien que complet fonctionnellement,
bon nombre d'applications ne sont pas implémentées sour
Erlang et réaliser une interface pour chacune s'avère une
tâche titanesque.
Python est quant à lui largement répandu, reste dans la
phylosophie d'Erlang une plateforme de développement
multi-plateforme avec une abstraction de la machine physique et un cycle
de développement accéléré. Python est
à ”l'orienté objet” ce qu'est Erlang à
”l'orienté processus”. Une multitute d'applications
sont implémentées en Python, de la
génération de documents PDF à l'interface homme
machine (wxWindow, Qt, etc...) en passant par le calcul
mathématique ou bien encore les services réseau. Bref
beaucoup de choses existent sous Python et sont très bien
implémentées.
Pourquoi tout adapter, interfacer ou réecrire sous Erlang ?
n'est-il pas plus avantageux de faire dialoguer ensemble des composants
logiciels Erlang et Python ? Vous l'aurrez vite compris, la
réalité du développement logiciel modulaire que
nous connaissons aujourd'hui nous permet d'envisager sérieusement
ce genre d'approche. En effet, avec le succes grandissant des logiciels
libre et la sensibilisation aux procédés de génie
logiciel, les développeurs de qualité respectant un
minimum les contraintes de réutilisation de leurs code sont en
nombre grandissant. Ils représentent bien la comunauté des
développeurs utilisant Python et/ou Erlang.
Le but: mettre facilement en place une architecture N-tiers en
interfacant une base Erlang solide avec un vaste panel de composants
Python.
1.1 La puissance d'Erlang
Erlang est un langage informatique disposant de nombreuses
caractéristiques plus souvent associées à un
système d'exploitation qu'à un langage de
développement: concurrence des processus, arbitrage entre les
processus et planification, gestion de la mémoire, distribution,
gestion transparente du réseau, etc. Le langage propose les
caractéristiques suivantes:
-
Concurrence - Erlang gère des processus extrêmement
légers, dont l'occupation mémoire évolue
dynamiquement. Les processus ne partagent pas de mémoire et
communique de manière asynchrone par passage de messages.
Erlang permet de développer des applications utilisant un
très grand nombre de processus concurrents. La concurrence du
système et la gestion des processus n'est pas liée aux
caractéristiques du système d'exploitation.
-
Distribution - Erlang est conçu pour fonctionner en
environnement distribué. Une machine virtuelle Erlang constitue
en fait ce que l'on nomme un noeud Erlang. Un système
distribué Erlang est un réseau de noeuds. Un noeud
Erlang peut créer des processus sur d'autres noeuds, dont
certains peuvent même tourner sur des systèmes
d'exploitation différents. Les processus
s'éxécutant sur différents noeuds communiquent de
la même manière qu'avec des processus qui
s'éxécutent sur un même noeud.
-
Robustesse - Erlang dispose de fonctions standard de détection
d'erreurs qui peuvent être utilisées pour bâtir un
système tolérant aux pannes. Par exemple, des processus
peuvent surveiller le statut et l'activité d'autres processus,
même si ces derniers s'exécutent sur d'autres noeuds.
Dans un système distribué, les processus peuvent
être configurée pour s'éxécuter sur
d'autres noeuds en cas de problème and automatiquement revenir
sur les noeuds une fois le problème résolu.
-
Temps réel logiciel - Erlang permet de développer des
systèmes temps-réel logiciel. Ces systèmes
nécessite des temps de réponse de quelques
millisecondes. Il n'est pas possible, dans ces systèmes de
tolérer de longues phases de récupération de la
mémoire (garbage collection). Erlang utilise donc des
techniques de récupération mémoire
incrémentales.
-
Mise à jour du code "à chaud" (Sans
interruption) - Certains systèmes ne peuvent pas être
arrêté, même pour mettre à jour les
programmes. Erlang permet de mettre à jour ces programmes sans
arrêter le système. L'ancien code peut-être
neutralisé et renplacer par le nouveau code. Pendant la
transition, l'ancien et le nouveau code peuvent cohabiter. Il est
ainsi possible de corriger des bugs et d'installer de nouvelles
versions sur un système sans perturber son fonctionnement.
-
Chargement incrémental du code - Les utilisateurs peuvent
contrôler très précisément la
manière dont le code est chargé. Pour un système
embarqué, le code peut être chargé au
démarrage du système. Pour un système de
développement, le code peut être chargé au fur et
à mesure des besoins, même lorsque le système
fonctionne. Si l'on découvre des bugs durant les tests, seul le
code buggué à besoin d'être remplacé.
-
Ouverture vers l'extérieur - Les processus Erlang peuvent
communiquer avec d'autres programmes grâce au même
mécanisme de passage de messages qui est utilisé entre
les processus eux-mêmes. Ce mécanisme est utilisé
pour les communications avec le système d'exploitation et pour
les intéractions avec des programmes écrits dans
d'autres langages. Si les performances pures sont recherchées
une extension de ce concept permet par exemple à des programmes
C d'être directement liés dans le système
d'exécution Erlang.
1.2 Les avantages de Python
Python est un langage de programmation objet interprété et
multi-plateforme. Le langage peut être étendu par l'ajout
de nouveaux modules écris en Python ou même dans un langage
compilé comme C ou C++. Ces modules peuvent définir de
nouvelles fonctions et variables comme de nouveaux
objets.
Python possède une gestion complète des
chaines de caractères avec gestion des expressions rationnelles.
Cela dégage l'utilisateur de la gestion mémoire
abituellement associée à ce genre de tâche. Ces
fonctionnalités comme tant d'autres font de Python un langage
idéal pour le prototypage par exemple.
Python permet
également d'écrire des programmes importants et n'est donc
pas limité au prototypage. Un programme peut utiliser plusieurs
modules, chaque module ayant son propre espace mémoire, et chaque
objets pouvant en encapsuler d'autres. De plus, une gestion des
exceptions permet des récupérer les erreurs lorsque et
où cela est nécessaire, sans parsemmer le code de
vérifications d'erreurs.
Un très large nombre de
modules sont disponibles en Python. Certains font partie de la
bibliothèque standard de Python, utilisables dans tous programmes
Python. D'autres sont plus spécialisés ou
spécifiques et doivent alors être importés dans
l'interpreteur Python au préalable.
Python permet
également la sérialisation des objets, ce qui permet un
partage de ceux-ci dans le câdre d'un environnement
distribué.
2 Méthodes d'interactions
Erlang et Python fonctionnent à la base grâce à une
machine virtuelle. Celle-ci offrant les diverses fonctionnalités
propre à chaque langage, elle est le passage obligatoire d'une
interraction entre une application Python ou Erlang avec la machine
physique. Ainsi, pour interfacer un programme écris en Python ou
en Erlang sans écrire toute une API ou sans mettre en place un
système client serveur au niveau socket, il est possible
d'utiliser les différentes interfaces de ces machines virtuelles,
offrant bien souvent tous les mécanismes nécessaire
à l'intégration d'extension au langage.
2.1 Interfaces d'Erlang
La machine Erlang peut s'interfacer de différentes
manières. Chacune d'entre elle pose des avantages et des
inconvénients. Le choix d'une d'entre elles dépend
essentiellement des fonctionnalitées désirées.
-
Port - Il s'ajit de l'interface de base d'Erlang. Toute communication
se fait via socket par envoi/reception de séquence de bits.
Fastidieuse à mettre en place, cette interface est
essentiellement utilisée par Erlang et pour Erlang.
L'utilisation de cette interface nécessite
l'implémentation de tous les encodages et décodages
nécessaires à la communication. Cette solution n'est pas
envisageables pour l'implémentation d'un mécanisme
simple et évolutif et ne sera pas traitée dans ce
document.
-
PortDriver - Rapide et direct, cette interface permet d'embarquer
directement dans Erlang des fonctions via le chargement d'une
bibliothèque dynamique usuellement écrite en C. Comme
l'interface Port, celle-ci n'est pas simple à mettre en oeuvre
mais est envisageable dans notre cas en utilisant un PortDriver qui
embarque une machine Python pour executer du code Python.
-
Erl_interface - Cette interface est une surcouche des Ports qui
contient tous les mécanismes d'encodage et de décodage
de données qu'il manque à l'interface Port. Celle-ci est
donc directement utilisable sans avoir à ce soucier du codage
binaire de l'information qui transite sur les sockets.
-
Noeud - L'implémentation d'un noeud Erlang s'appuie sur
l'utilisation d'Erl_interface. Elle permet de s'abstraire de tout
détail technique concernant la communication et permet
également une abstraction totale du fonctionnement du Noeud
ainsi créé puisqu'il se comporte comme un noeud Erlang.
2.2 Interfaces de Python
Les possibilités d'interface et d'extension de Python sont
très variée:
-
Module d'extention - Une API simple offre la possibilité de
créer des fonctions et des callbacks écris en C. Les
modules ainsi créés sont chargée depuis un
programme Python comme des bibliothèques partagées
(fichier .so sous UNIX, .dll sous Windows). Le module est vu alors
comme une classe Python avec ses fonctions.
-
Python embarqué - Cette approche permet de lancer une machine
Python au sein d'un programme écris en C. Plus destinée
à un pilotage d'application écris en Python qu'à
une réelle interactions entre Python et C, cette solution peut
néanmoins être utile dans notre cas.
-
Sockets - Les possibilités réseau de Python étant
très vastes, un mécanisme client serveur peut être
mis en place, nottament avec les bibliothèques standard ou des
extensions comme Twisted.
2.3 Intéraction Erlang/Python
L'idée est d'utiliser des programmes Python depuis depuis Erlang
et réciproquement. Il serai ainsi possible d'utiliser au sein
d'une application Erlang, les nombreux modules Python existant et
permettant d'étendre à un champs très vaste
l'ensemble des fonctionnalités disponibles. Il serai
également possible d'utiliser des processus Erlang (ou des
tâches spécifiques pour lesquelles Erlang permet une
implémentation simple et rapide) au sein d'applications Python.
Enfin, l'interraction Erlang/Python permettrai par exemple
d'intégrer des applications Erlang au sein d'un système
basé sur Zope.
Nous constatons vite qu'il y à
quatre solutions au problème:
-
Client/Serveur full duplex. Un serveur Erlang est mis à la
disposition de Python et un serveur Python est mis à la
disposition d'Erlang. Cette solution à l'avantage d'être
générique et valable pour Python ou tout autre langage.
La machine virtuelle Python peut tourner sur la même plateforme
faisant tournant Erlang ou sur une plateforme distante. Attention
toutefois au passage de paramètres volumineux pouvant
très vite faire tomber les performances globales du
système. Tenir compte également que dans ce cas toute la
gestion des API client/serveur est à écrire et à
adapter à l'application réalisée.
-
Communication socket directe Erlang/Python via Erl_interface. Dans ce
cas une classe Python implémente et se comporte comme un noeud
Erlang. Cette solution est relativement simple, évolutive,
souple d'utilisation et bidirectionnelle. De plus, elle ne
nécessire aucune compilation de code C et est ainsi directement
utilisable sur toute machine Python. La communication se fait par
passage de messages ou par appel de fonctions, et couvre ainsi un
grand nombre de domaines d'application. La machine virtuelle Python
peut tourner sur la même plateforme faisant tournant Erlang ou
sur une plateforme distante. Attention toutefois au passage de
paramètres volumineux pouvant très vite faire tomber les
performances globales du système.
-
Module Python associé à un PortDriver Erlang. Deux
bibliothèques sont chacune chargées par leur machine
virtuelle respective et dialogues entre elles via un tube ou des IPC
par exemple dans le cas d'UNIX, offrent un registre partagé,
permettent l'acces commun à des bibliothèques
système existantes, etc... Le mécanisme mis en oeuvre
est simple mais la communication Erlang/Python est indirecte et
nécessite la mise en place d'une API générique.
Ici les machines Erlang et Python tournent sur la même
plateforme et peuvent échanger avec de bonnes performances des
volumes de données importants. Attention, dans ce cas,
l'implémentation UNIX ne sera pas compatible avec
l'implementation sous Windows.
-
PortDriver Erlang embarquant une machine virtuelle Python. Dans cette
solution, la machine Erlang initialise une machine Python. Ainsi
Erlang, via une interface codée en C, peut utiliser des
fonctionnalités Python. Celle-ci comporte quelques
inconvénients et laisse à Python un rôle d'esclave
envers Erlang: l'appel aux fonctions est unidirectionnelle (Erlang
appel des fonctions Python). Le principal inconvénient de cette
solution est qu'une API applicative générique doit
être mise en oeuvre côté Python pour assurer une
modularité de la solution sans obliger une modification
régulière et fastidieuse du code C. Par contre, ici la
machine Erlang et la machine Python tournent sur la même
plateforme; De ce fait et car les appels Erlang se font directement
dans la machine virtuelle, des échanges de volumes de
données plus important que dans la première solution
peuvent être envisagés. A notter toutefois que dans ce
cas, un arret innopiné de la machine Python entrainerai
également l'arret de la machine Erlang.
La première solution est générique mais pas
très performante et demande une maintenance double pour deux
serveurs. Il est claire que la seconde solution reste la plus simple, la
plus souple et la plus pratique à mettre en
oeuvre. Celle-ci peut être
adoptée dans un grand nombre de situations lorsque les dialogues
entre Python et Erlang ne sont pas trop encombrés de
données volumineuses. La troisième solution est plus
compliquée à mettre en oeuvre mais permet un partage de
données importantes entre Python et Erlang ainsi qu'une
mutualisation des services offerts par des bibliothèques
système existantes. La quatrième solution n'apporte pas de
réelle plus value par rapport aux deux précédentes.
3 Conclusion
Différents logiciels sont disponibles. Beaucoup n'ont jamais
aboutis, et ne sont pas utilisables directement. Parmis eux, nottons
idx-spyerl et python_erlang qui peuvent éventuellement servir de
base de travail mais ne sont pas réellement utilisables. La plus
aboutie des solutions existante est incontestablement py_interface qui
met en oeuvre la solution la plus simple, la plus souple et la plus
pratique qui consiste en un noeud Erlang implémenté en
Python.
Cette solution, entièrement écrite en Python, est
utilisable sur toute machine et toute architecture sans compilation
particulière. Les messages Erlang peuvent être
envoyés de Python vers Erlang et réciproquement. Python
peut également utiliser les rpc pour lancer des fonctions Erlang.
Cette solution est donc très complète et permet
déjà un vaste domaine d'utilisation.
Dans des cas très particuliers (passage importants de
données volumineuses, adhérance forte aux couches basses
du système, utilisation partagée d'une bibliothèque
système existante, interface avec un logiciel existant
implémenté dans un langage type C ou C++, etc...), on lui
préfèrera une solution réalisée en C dont
l'implémentation propose une extension Python ainsi qu'un driver
Erlang partageant des ressources système partagées
(gestion d'une mémoire partagée, interaction diverses avec
des bibliothèques, passages de données plus
optimisés via les couches basses du système
d'exploitation, etc...). Dans ce cas, tout reste à faire.
4 Remerciements
Je tiends à remercier tout particulièrement Mickael
Rémond et Thierry Mallard pour leurs actions autour d'Erlang en
France. Je souhaite remercier également la société
Ericsson pour avoir pensé et investit dans un projet comme on
aimerais en voir d'aventage de la part de ceux qui ont les moyen de
réaliser de tels choses. Bravo!