2015-01-17 17 views
8

In Node.js sono riuscito a creare un clone WordPress piuttosto facilmente utilizzando EventEmitter per replicare e creare un sistema hooks nel core CMS, a cui i plugin potevano quindi collegarsi.Eventi Golang: EventEmitter/dispatcher per architettura plug-in

Ora ho bisogno di questo stesso livello di estensibilità e isolamento del nucleo per il mio CMS scritto e portato su Go. Fondamentalmente ora ho il core finito, ma per renderlo veramente flessibile devo essere in grado di inserire eventi (hook) e di avere dei plugin collegati a questi hook con funzionalità aggiuntive.

Non mi interessa la ricompilazione (collegamento dinamico/statico), a patto che non sia necessario modificare il core per caricare i plugin: il core CMS non dovrebbe mai essere modificato. (Come WP, Drupal ecc)

ho notato che ci sono un paio di progetti, piuttosto sconosciuti, cercando di attuare gli eventi in andare a cercare in qualche modo simili a EventEmitter in Node.js:

https://github.com/CHH/eventemitter

https://github.com/chuckpreslar/emission

Dato che quei 2 progetti sopra non hanno guadagnato molta popolarità e attenzione, in qualche modo penso che questo modo di pensare agli eventi potrebbe essere come dovremmo farlo in Go? Questo significa che Go non è forse adatto a questo compito? Per rendere le applicazioni veramente estendibili tramite plugin?

Non sembra che gli eventi di Go si siano integrati nel suo nucleo e RPC non sembra una soluzione valida per l'integrazione di plug-in nell'applicazione principale come se fossero stati creati in modo nativo e come se fossero parte del principale applicazione stessa.

Qual è il modo migliore per integrare i plug-in senza soluzione di continuità nell'app principale, per i punti di estensione illimitati (nel core) senza manipolare il core ogni volta che è necessario collegare un nuovo plug-in?

+1

Vedere i pacchetti database/sql e database/sql/driver per un esempio di architettura di plugin. In questa architettura, plugins [register] (http://godoc.org/database/sql#Register) dalle funzioni init() e implementa interfacce come definito dal pacchetto driver. –

risposta

25

In generale, in Go, se hai bisogno di eventi probabilmente devi usare i canali, ma se hai bisogno di plug-in, la strada da percorrere sono le interfacce . Ecco un esempio un po 'lungo di una semplice architettura di plugin che minimizza il codice che deve essere scritto nel file principale dell'applicazione per aggiungere plug-in (questo può essere automatizzato ma non dnyamic, vedi sotto).

Spero che sia nella direzione che stai cercando.


1. Interfacce Il plugin

Così va bene, diciamo che abbiamo due plugin, Fooer e agisce. Per prima cosa definiamo le loro interfacce:

// All DoerPlugins can do something when you call that method 
type DoerPlugin interface { 
    DoSomething() 
} 

// All FooerPlugins can Foo() when you want them too 
type FooerPlugin interface { 
    Foo() 
} 

2. Il Plugin Registry

Ora, il nostro core applicazione ha un Registro di plugin. Sto facendo qualcosa di Quicky e sporco qui, solo per ottenere l'idea attraverso:

package plugin_registry 

// These are are registered fooers 
var Fooers = []FooerPlugin{} 

// Thes are our registered doers 
var Doers = []DoerPlugin{} 

Ora esponiamo metodi per aggiungere plugin al Registro di sistema. Il modo più semplice è quello di aggiungerne uno per tipo, ma è possibile fare con materiale di riflessione più complesso e avere una funzione.Ma di solito in Go, cercare di mantenere le cose semplici :)

package plugin_registry 

// Register a FooerPlugin 
func RegisterFooer(f FooerPlugin) { 
    Fooers = append(Fooers, f) 
} 

// Register a DoerPlugin 
func RegisterDoer(d DoerPlugin) { 
    Doers = append(Doers, d) 
} 

3. Implementazione e registra un plugin

Ora, supponiamo che questo è il vostro modulo di plugin. Creiamo un plug-in che fa l'agente e nel nostro pacchetto init() lo registriamo. init() si verifica una volta all'avvio del programma per ogni pacchetto importato.

package myplugin 

import (
    "github.com/myframework/plugin_registry" 
) 
type MyPlugin struct { 
    //whatever 
} 

func (m *MyPlugin)DoSomething() { 
    fmt.Println("Doing something!") 
} 

Ancora una volta, qui è la "magia init" che registra il pacchetto automaticamente

func init() { 
    my := &MyPlugin{} 
    plugin_registry.RegisterDoer(my) 
} 

4. Importare i plugin li registra automaticamente

E ora, l'unica cosa dovremo cambiare è ciò che importiamo nel nostro pacchetto principale. Poiché Go non ha importazioni o collegamenti dinamici, questa è l'unica cosa che devi scrivere. È piuttosto semplice creare uno script go generate che generi un file principale esaminando l'albero dei file o un file di configurazione e trovando tutti i plug-in che è necessario importare. Non è dinamico ma può essere automatizzato. Poiché la principale importa il plugin per l'effetto collaterale della registrazione, l'importazione uses the blank identifier to avoid unused import error.

package main 

import (
    "github.com/myframework/plugin_registry" 

    _ "github.com/d00dzzzzz/myplugin" //importing this will automaticall register the plugin 
) 

5. Nella principali dell'app

E ora il nostro core applicazione non ha bisogno di alcun codice di cambiare per essere in grado di interagire con i plugin:

func main() { 


    for _, d := range plugin_registry.Doers { 
     d.DoSomething() 
    } 

    for _, f := range plugin_registry.Fooers { 
     f.Foo() 
    } 

} 

E questo è tutto. Tieni presente che il registro dei plug-in deve essere un pacchetto separato che sia il core dell'applicazione sia i plug-in possano essere importati, quindi non avrai importazioni circolari.

Ovviamente è possibile aggiungere gestori di eventi a questo mix, ma come ho dimostrato, non è necessario.