Contactez-nous

Création de bibliothèques partagées

Découvrez comment créer des bibliothèques partagées en Go (shared libraries - .so) : buildmode=plugin, CGo, avantages, limitations et cas d'usage pour l'extensibilité et la modularité.

Introduction à la création de bibliothèques partagées en Go : Modularité et extensibilité avancées

Les bibliothèques partagées (shared libraries), également appelées plugins dynamiques ou modules partagés, sont un mécanisme puissant des systèmes d'exploitation modernes qui permettent de partager du code et des ressources entre plusieurs programmes ou processus. En Go, vous pouvez créer des bibliothèques partagées Go (.so files) en utilisant le mode de build buildmode=plugin de la commande go build, en combinaison avec CGo (chapitre 28). Les bibliothèques partagées Go peuvent être chargées dynamiquement au runtime par d'autres applications Go (ou potentiellement par des applications écrites dans d'autres langages via FFI - Foreign Function Interface, chapitre 28), offrant un mécanisme d'extensibilité dynamique, de modularité, et de réutilisation de code très avancé.

Imaginez une bibliothèque partagée comme une boîte à outils contenant un ensemble de fonctions, de types, et de ressources réutilisables. Plusieurs applications (ou plugins) peuvent partager et utiliser les outils contenus dans cette boîte à outils (la bibliothèque partagée), au lieu de dupliquer le même code dans chaque application. Les bibliothèques partagées permettent de factoriser le code commun, de réduire la taille des exécutables, de faciliter la maintenance et la mise à jour du code partagé, et d'améliorer la modularité et l'extensibilité des systèmes logiciels.

Ce chapitre vous propose un guide expert sur la création de bibliothèques partagées en Go. Nous allons explorer en détail comment compiler du code Go en tant que bibliothèque partagée (.so file) en utilisant go build -buildmode=plugin et CGo, les avantages et les cas d'utilisation des bibliothèques partagées en Go, les limitations et les compromis de l'utilisation des bibliothèques partagées (complexité, portabilité, sécurité), et les bonnes pratiques pour concevoir, construire, et utiliser des bibliothèques partagées robustes et performantes en Go. Que vous souhaitiez créer des plugins dynamiques pour étendre vos applications Go, factoriser du code commun entre plusieurs applications, ou optimiser l'utilisation des ressources système, ce guide complet vous fournira les clés nécessaires pour maîtriser la création de bibliothèques partagées en Go et exploiter leur potentiel pour la modularité et l'extensibilité avancées de vos projets.

Build de bibliothèques partagées Go : buildmode=plugin et CGo

La création de bibliothèques partagées Go (.so files) nécessite l'utilisation du mode de build buildmode=plugin de la commande go build, en combinaison avec CGo (chapitre 28). Le mode buildmode=plugin indique au compilateur Go de compiler le code source Go en tant que plugin Go (fichier .so - shared object file), et CGo est nécessaire pour exporter les symboles Go (fonctions, variables, types) du plugin et les rendre accessibles au code extérieur (application hôte ou code C).

Commande go build -buildmode=plugin pour compiler un plugin Go :

Pour compiler un fichier source Go (ou un package Go) en tant que bibliothèque partagée Go (fichier .so), utilisez la commande go build avec l'option -buildmode=plugin et l'option -o pour spécifier le nom du fichier de sortie (qui doit se terminer par l'extension .so) :

go build -buildmode=plugin -o nom_bibliotheque_partagee.so fichier_source_plugin.go

  • go build : La commande de build Go.
  • -buildmode=plugin : Option essentielle pour spécifier que vous souhaitez compiler une bibliothèque partagée Go (plugin Go).
  • -o nom_bibliotheque_partagee.so : Option -o pour spécifier le nom du fichier de sortie de la bibliothèque partagée. Le nom du fichier de sortie doit se terminer par l'extension .so (shared object file) sur les systèmes Linux et macOS, ou .dll (dynamic link library) sur Windows (bien que les plugins Go ne soient officiellement supportés que sur Linux et macOS, et non sur Windows). Choisissez un nom de fichier descriptif pour votre bibliothèque partagée (par exemple, monplugin.so, libcrypto.so, libtraitement_image.so).
  • fichier_source_plugin.go : Le fichier source Go principal de votre bibliothèque partagée (ou le package Go contenant le code source de la bibliothèque partagée). Le compilateur Go va compiler ce fichier source (et tous les autres fichiers du package bibliothèque partagée) et générer le fichier .so correspondant.

CGo requis pour l'exportation de symboles Go depuis un plugin :

La création de bibliothèques partagées Go (plugins .so) nécessite l'utilisation de CGo pour exporter les symboles Go (fonctions, variables, types) de la bibliothèque partagée et les rendre accessibles au code extérieur (application hôte ou code C). Sans CGo, il n'est pas possible d'exporter des symboles Go depuis un plugin Go et de les utiliser dynamiquement depuis l'application hôte.

Directives CGo //export pour exporter les symboles Go du plugin :

Pour exporter des symboles Go depuis votre code Go de bibliothèque partagée, vous devez utiliser les directives CGo //export (comme nous l'avons vu au chapitre 28, section sur la syntaxe CGo). Ajoutez un commentaire //export NomFonctionGo (ou //export NomVariableGo, //export NomTypeGo) juste avant la déclaration de chaque fonction, variable, ou type Go que vous souhaitez exporter du plugin. Incluez également une déclaration C compatible pour chaque symbole Go exporté dans le bloc commentaire CGo (via des déclarations C de fonctions, de variables externes, ou de types externes). Les déclarations C dans le bloc commentaire CGo servent d'interface pour le code extérieur (application hôte ou code C) pour appeler ou accéder aux symboles Go exportés.

Exemple de commande go build -buildmode=plugin pour compiler une bibliothèque partagée Go (plugin .so) avec CGo :

Reprenons l'exemple du plugin monplugin.go du chapitre précédent (section sur le chargement dynamique de plugins), qui contient une fonction Go exportée HelloWorld (avec le commentaire //export HelloWorld et la déclaration C dans le bloc commentaire CGo). Pour compiler ce fichier en tant que bibliothèque partagée Go (plugin .so), naviguez jusqu'au répertoire plugins/monplugin dans le terminal et exécutez la commande :

cd plugins/monplugin
go build -buildmode=plugin -o monplugin.so monplugin.go

Cette commande va compiler le fichier monplugin.go (qui contient du code Go et du code CGo) et générer un fichier de bibliothèque partagée monplugin.so dans le répertoire plugins/monplugin. Ce fichier .so pourra ensuite être chargé dynamiquement par l'application hôte au runtime, et l'application hôte pourra appeler la fonction Go exportée HelloWorld du plugin en utilisant le package plugin de Go.

Utilisation des bibliothèques partagées Go : Chargement dynamique et appel de symboles exportés

L'utilisation des bibliothèques partagées Go (plugins .so) implique le chargement dynamique des fichiers plugins .so par l'application hôte au runtime, et l'appel des symboles exportés (fonctions, variables, types) de ces plugins depuis l'application hôte, via le package plugin de Go (comme nous l'avons vu au chapitre 26, section sur le chargement dynamique de plugins).

Workflow d'utilisation des bibliothèques partagées Go : (rappel)

  • Chargement dynamique des plugins : plugin.Open(nomFichier string) (*plugin.Plugin, error)
  • Recherche des symboles exportés du plugin : plugin.Lookup(nomSymbole string) (Symbol, error)
  • Conversion du plugin.Symbol vers le type Go concret attendu (assertion de type)
  • Utilisation des symboles du plugin (appels de fonctions, accès aux variables, utilisation des types)

Exemple d'utilisation d'une bibliothèque partagée Go (plugin .so) : (rappel)

L'exemple de code du chapitre 26 (section sur le chargement dynamique de plugins) illustre déjà l'utilisation d'une bibliothèque partagée Go (plugin .so) :

package main

import (
    "fmt"
    "log"
    "plugin"
)

func main() {
    // ... (Chargement du plugin avec plugin.Open) ...

    // ... (Recherche du symbole exporté 'HelloWorld' avec plugin.Lookup) ...

    // ... (Assertion de type du symbole vers le type de fonction attendu) ...

    // ... (Appel de la fonction du plugin via le symbole converti) ...
    message := helloWorldFunc()
    fmt.Println("Message du plugin :", message)
}

Cas d'utilisation typiques des bibliothèques partagées Go (plugins .so) :

  • Extensibilité dynamique des applications : Les bibliothèques partagées Go sont principalement utilisées pour ajouter de l'extensibilité dynamique aux applications Go, en permettant de charger des fonctionnalités additionnelles ou des modules optionnels (les plugins) au runtime, sans recompiler l'application principale. Les systèmes de plugins permettent de construire des applications Go plus flexibles, plus personnalisables, et plus adaptables aux besoins spécifiques de chaque déploiement ou de chaque utilisateur. Exemples d'applications typiques : systèmes de plugins pour les éditeurs de code, les IDE, les outils de ligne de commande, les serveurs d'applications, les plateformes de microservices, les systèmes d'automatisation, etc.
  • Modularité et isolation du code : Les bibliothèques partagées Go permettent de modulariser le code Go en composants indépendants et dynamiquement chargeables. La séparation du code en bibliothèques partagées (plugins) et en application hôte (noyau) favorise une meilleure organisation du code, une isolation des composants, et une réduction des dépendances entre les parties du système. La modularité et l'isolation facilitent la maintenance, l'évolution, et le testabilité du code.
  • Réutilisation de code et écosystème de plugins : Les bibliothèques partagées Go permettent de réutiliser et de partager du code entre différentes applications Go (ou potentiellement avec des applications écrites dans d'autres langages via FFI, chapitre 28). Vous pouvez construire un écosystème de plugins autour de votre application hôte, en permettant à des développeurs tiers de créer et de distribuer leurs propres plugins (bibliothèques partagées) qui étendent les fonctionnalités de votre application hôte. La réutilisation de code via les bibliothèques partagées et les écosystèmes de plugins favorise la collaboration, l'innovation, et l'enrichissement de l'écosystème logiciel Go.
  • Chargement conditionnel de fonctionnalités optionnelles : Les bibliothèques partagées Go permettent de charger conditionnellement des fonctionnalités optionnelles au runtime, en fonction de la configuration, des besoins spécifiques du déploiement, ou des fonctionnalités activées par l'utilisateur. Vous pouvez déployer une application Go avec un noyau minimal, et charger uniquement les plugins (bibliothèques partagées) qui sont réellement nécessaires dans chaque environnement de déploiement, réduisant ainsi la taille des binaires exécutables, la consommation de ressources, et la complexité de l'application déployée.

Les bibliothèques partagées Go (plugins .so) offrent un mécanisme puissant pour l'extensibilité dynamique, la modularité, et la réutilisation de code dans vos applications Go, en particulier pour les applications complexes et évolutives qui nécessitent une architecture basée sur les plugins et les extensions.

Limitations et considérations des plugins Go : Complexité, performance et sécurité (rappel)

Bien que les plugins Go offrent des avantages significatifs en termes d'extensibilité dynamique et de modularité, il est important de connaître leurs limitations et de prendre en compte certaines considérations avant de les adopter dans vos projets Go, en particulier en termes de complexité, de performance, de sécurité, et de portabilité (rappel du chapitre 26, section "Avantages et considérations des fonctions anonymes et closures") :

Limitations et considérations des plugins Go : (rappel)

  • Complexité accrue de l'architecture et du code
  • Overhead de performance du chargement dynamique
  • Sécurité : Risques liés aux plugins non fiables ou malveillants
  • Complexité de la gestion des versions et de la compatibilité des plugins
  • Débogage plus complexe

Bonnes pratiques pour l'utilisation des plugins Go :

Pour utiliser les plugins Go de manière judicieuse et responsable dans vos projets Go, et éviter les pièges potentiels, voici quelques bonnes pratiques à suivre :

  • Utiliser les plugins uniquement lorsque l'extensibilité dynamique est réellement nécessaire : N'introduisez pas la complexité des plugins dans votre application si vous n'avez pas de besoins spécifiques d'extensibilité dynamique ou de personnalisation au runtime. Pour la plupart des applications Go courantes, les mécanismes de modularité et d'abstraction standards de Go (packages, interfaces, composition de structs) sont généralement suffisants et plus simples à gérer que les plugins. Utilisez les plugins uniquement lorsque les avantages de l'extensibilité dynamique justifient réellement la complexité supplémentaire introduite.
  • Définir des interfaces de plugins claires, stables et bien documentées : Concevez des interfaces de plugins claires, précises, stables, et bien documentées, qui définissent un contrat de comportement robuste et évolutif entre l'application hôte et les plugins. Des interfaces de plugins bien conçues facilitent le développement, l'intégration, la compatibilité, et la maintenance des plugins par les développeurs de plugins (internes ou tiers).
  • Limiter et contrôler l'API publique des plugins (symboles exportés) : Limitez et contrôlez l'API publique de vos plugins (les symboles exportés) au strict minimum nécessaire, en exposant uniquement les fonctions, les variables, et les types qui sont réellement destinés à être utilisés par l'application hôte. Masquez les détails d'implémentation interne des plugins et évitez d'exposer des symboles inutiles ou non documentés, pour simplifier l'API des plugins et réduire le risque d'erreurs d'utilisation ou de dépendances involontaires.
  • Valider et sécuriser les plugins chargés dynamiquement : Validez et sécurisez soigneusement les plugins que vous chargez dynamiquement dans votre application, en particulier si les plugins proviennent de sources externes ou non fiables. Vérifiez l'origine et l'authenticité des fichiers plugins .so, utilisez des signatures numériques pour vérifier l'intégrité des plugins, appliquez des tests de sécurité et des analyses statiques de sécurité sur le code des plugins, et mettez en place des mécanismes d'isolation et de sandboxing des plugins pour limiter les risques de sécurité liés aux plugins malveillants ou vulnérables.
  • Documenter clairement l'architecture des plugins et le workflow de développement des plugins : Documentez clairement l'architecture des plugins de votre application, en expliquant comment les plugins sont chargés, comment ils interagissent avec l'application hôte (via les interfaces de plugins), comment les développeurs peuvent créer et implémenter de nouveaux plugins, et quelles sont les conventions et les bonnes pratiques pour le développement des plugins. Une bonne documentation est essentielle pour faciliter l'adoption et l'extension de votre système de plugins par les développeurs de plugins (internes ou tiers).

En appliquant ces bonnes pratiques, vous utiliserez les plugins Go de manière judicieuse et responsable, en tirant parti de leur potentiel pour l'extensibilité dynamique et la modularité de vos applications Go, tout en minimisant les risques et les compromis potentiels en termes de complexité, de performance, de sécurité, et de maintenance.