2015-01-17 27 views
8

In Node.js konnte ich mit dem EventEmitter einen WordPress-Klon relativ einfach replizieren und ein Hooks-System in den CMS-Kern einbauen, an den sich dann Plugins anhängen konnten.Goleg-Ereignisse: EventEmitter/Dispatcher für Plugin-Architektur

Ich brauche jetzt die gleiche Erweiterbarkeit und Core-Isolation für mein CMS geschrieben und portiert nach Go. Im Grunde habe ich den Kern jetzt fertig, aber um es wirklich flexibel zu machen, muss ich in der Lage sein, Ereignisse (Hooks) einzufügen und Plugins mit zusätzlichen Funktionen an diese Hooks anzuhängen.

I kümmern sich nicht um (dynamisch/statische Linken) neu zu kompilieren, solange man den Kern laden Plugins nicht ändern müssen - das CMS Kern sollte nie geändert werden. (Wie WP, Drupal etc.)

Ich habe bemerkt, es gibt ein paar eher unbekannte Projekte, versuchen Ereignisse in Go zu implementieren suchen etwas ähnlich EventEmitter in Node.js:

https://github.com/CHH/eventemitter

https://github.com/chuckpreslar/emission

Da diese 2 Projekte oben nicht viel Popularität und Aufmerksamkeit gewonnen haben, denke ich, dass diese Art des Nachdenkens über Ereignisse nun so sein könnte, wie wir es in Go machen sollten? Heißt das Go ist vielleicht nicht auf diese Aufgabe ausgerichtet? Um wirklich erweiterbare Anwendungen durch Plugins zu machen?

Scheint nicht, dass Go Ereignisse in seinen Kern eingebaut hat, und RPC scheint keine gültige Lösung für die Integration von Plugins in Ihre Kernanwendung zu sein, als wären sie nativ integriert und als wären sie Teil des Hauptprogramms Anwendung selbst.

Was ist der beste Weg für die nahtlose Plugin Integration in Ihr Kern-App, für unbegrenzte Erweiterungspunkte (in Kern) ohne Kern Manipulation jedes Mal, müssen Sie ein neues Plugin anschließen?

+1

Ein Beispiel für eine Plugin-Architektur finden Sie in den Paketen database/sql und database/sql/driver. In dieser Architektur funktioniert plugins [register] (http://godoc.org/database/sql#Register) von init() und implementiert Schnittstellen wie vom Treiberpaket definiert. –

Antwort

25

Im Allgemeinen, in Go, wenn Sie Ereignisse benötigen, müssen Sie wahrscheinlich Kanäle verwenden, aber wenn Sie Plugins benötigen, ist der Weg zu gehen Schnittstellen. Hier ist etwas langatmig Beispiel einer einfachen Plugin-Architektur, die den Code minimiert, die in der Hauptdatei der App geschrieben werden muss, um Plug-in hinzuzufügen (dies kann automatisiert werden, aber nicht dnyamic, siehe unten).

Ich hoffe, es ist in der Richtung, die Sie suchen.


1. Das Plugin-Schnittstellen

Also gut, lassen Sie uns sagen, dass wir zwei Plugins, Fooer und Handelnde. Wir definieren zunächst ihre Schnittstellen:

// 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. Das Plugin Registry

nun unsere Kern App ein Plugin-Registry hat. Ich mache etwas Quicky und schmutzig hier, nur über die Idee zu bekommen:

package plugin_registry 

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

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

wir nun Methoden aussetzen Plugins in der Registrierung hinzuzufügen. Der einfache Weg ist, einen pro Typ hinzuzufügen, aber Sie könnten mit komplexeren Reflektionsmaterial gehen und eine Funktion haben.Aber in der Regel in Go, versuchen, die Dinge einfach :)

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. Implementierung und Registrierung ein Plugin halten

Angenommen nun, das ist dein Plugin-Modul. Wir erstellen ein Plugin, das ein Macher ist, und in unserem Paket init() Methode registrieren wir es. init() passiert einmalig beim Programmstart für jedes importierte Paket.

package myplugin 

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

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

Auch hier ist die "init Magie", die das Paket automatisch registriert sie,

Und jetzt automatisch

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

4. Importieren Sie die Plugins registriert die einzige Was wir ändern müssen, ist, was wir in unser Hauptpaket importieren. Seit Go hat keine dynamischen Importe oder Verknüpfungen, das ist das einzige, was Sie schreiben müssen. Es ist ziemlich trivial, ein go generate Skript zu erstellen, das eine Hauptdatei generiert, indem Sie in den Dateibaum oder eine Konfigurationsdatei schauen und alle Plugins finden, die Sie importieren müssen. Es ist nicht dynamisch, aber es kann automatisiert werden. Weil main das Plugin für den Nebeneffekt der Registrierung importiert, muss der Import uses the blank identifier to avoid unused import error sein.

package main 

import (
    "github.com/myframework/plugin_registry" 

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

5. Im Kern App

Und nun unser Kern App braucht keinen Code ändern kann mit Plugins interagieren:

func main() { 


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

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

} 

Und das ist es auch schon. Beachten Sie, dass die Plugin-Registrierung ein separates Paket sein sollte, das sowohl der Kern der App als auch die Plugins importieren können, sodass Sie keine zirkulären Importe haben.

Natürlich können Sie Event-Handler zu diesem Mix hinzufügen, aber wie ich gezeigt habe, ist es nicht erforderlich.