2015-01-17 9 views
8

Node.js에서 EventEmitter를 사용하여 쉽게 복제하고 플러그인 할 수있는 CMS 코어에 후크 시스템을 구축하는 WordPress 클론을 쉽게 만들 수있었습니다.Golang events : 플러그인 아키텍처를위한 EventEmitter/dispatcher

Go로 작성되고 이식 된 CMS에 대해 동일한 수준의 확장 성과 핵심 격리가 필요합니다. 기본적으로 코어는 끝났지 만 실제로 유연하게 만들려면 이벤트 (후크)를 삽입하고 추가 기능을 사용하여 플러그인을이 후크에 부착 할 수 있어야합니다.

플러그인을로드하기 위해 코어를 수정할 필요가없는 한 다시 컴파일 (동적/정적 링크)을 신경 쓰지 않습니다. CMS 코어는 절대로 수정하면 안됩니다. 내가 Node.js를에 EventEmitter 다소 유사하게 보이는 이동 이벤트를 구현하기 위해 노력하고, 몇 오히려 알 수없는 프로젝트 거기에 눈치

(WP, 드루팔 등 같은) :

https://github.com/CHH/eventemitter

https://github.com/chuckpreslar/emission

위의 2 개 프로젝트가 많은 인기와 관심을 얻지 못했기 때문에 어떻게 든 이벤트에 대한 사고 방식이 지금 우리가 Go에서해야하는 방식 일 수 있다고 생각하십니까? Go가이 작업에 맞지 않는다는 뜻입니까? 플러그 인을 통해 진정으로 확장 가능한 응용 프로그램을 만들려면 어떻게해야합니까?

Go에는 코어에 이벤트가 내장되어 있지 않으므로 RPC는 기본적으로 플러그인이 핵심 애플리케이션에 통합되어있는 것처럼 보이지 않으며 주요 플러그인의 일부인 것처럼 보입니다. 응용 프로그램 자체.

새로운 플러그인을 연결해야 할 때마다 코어를 조작하지 않고 무제한 확장 점 (코어)을 위해 코어 앱에 원활하게 플러그인하는 가장 좋은 방법은 무엇입니까?

+1

플러그인 아키텍처의 예는 database/sql 및 database/sql/driver 패키지를 참조하십시오. 이 아키텍처에서는 init() 함수에서 plugins [register] (http://godoc.org/database/sql#Register)를 호출하고 드라이버 패키지에서 정의한대로 인터페이스를 구현합니다. –

답변

25

일반적으로 Go에서는 이벤트가 필요한 경우 아마 채널을 사용해야하지만 플러그인이 필요한 경우 이동 방법은 인터페이스입니다. 다음은 플러그인을 추가하기 위해 앱의 기본 파일에 작성된 코드 인 을 최소화하는 단순한 플러그인 아키텍처의 약간 긴 예제입니다 (자동으로 수행 할 수 있지만 아래에는 dnyamic 참조).

나는 그것이 당신이 찾고있는 방향에 있기를 바랍니다. 플러그인 인터페이스

그래서 괜찮


1. 이제 우리는 두 개의 플러그인, Fooer 및 행위자가 있다고 가정 해 보자. 우리는 먼저 자신의 인터페이스를 정의 :

// 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. 플러그인 레지스트리

이제, 우리의 핵심 응용 프로그램은 플러그인 레지스트리가 있습니다. 이제 우리는 레지스트리에 플러그인을 추가하는 방법을 노출

package plugin_registry 

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

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

: 난 그냥 걸쳐 아이디어를 얻을, quicky 더러운 여기서 뭔가를하고 있어요. 간단한 방법은 유형 당 하나를 추가하는 것이지만, 더 복잡한 반사 재료로 이동하고 하나의 기능을 가질 수 있습니다.그러나 일반적으로 이동에서, 이제 간단한

:

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. 구현 및 등록 플러그인 물건을 유지하려고이 플러그인 모듈입니다 가정합니다. 우리는 doer 인 플러그인을 만들고, 패키지에 init() 메소드를 등록합니다. 가져온 패키지마다 시작된 프로그램에서 init()이 한 번 발생합니다.

다시
package myplugin 

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

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

, 여기에 유일한 자동으로 플러그인을 가져 오기

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

4. 자동

을 등록 패키지를 등록하고 "초기화 마법"입니다 우리가 바꿀 필요가있는 것은 우리가 주 패키지로 가져 오는 것입니다. Go에는 동적 가져 오기 또는 링크가 없으므로 작성해야하는 유일한 것입니다. 파일 트리 또는 설정 파일을 살펴보고 가져올 모든 플러그인을 찾아 주 파일 을 생성하는 go generate 스크립트를 만드는 것은 매우 간단합니다. 동적은 아니지만 자동화가 가능합니다. main은 등록의 부작용을위한 플러그인을 수입하기 때문에 import uses the blank identifier to avoid unused import error. 응용 프로그램의 핵심에


5.

package main 

import (
    "github.com/myframework/plugin_registry" 

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

그리고 지금 우리의 핵심 응용 프로그램은 플러그인과 상호 작용 할 수 있도록 변경하는 코드를 필요로하지 않는다 :

func main() { 


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

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

} 

그리고 그것에 관한 것입니다. 플러그인 레지스트리는 앱의 핵심 패키지와 플러그인을 모두 가져올 수있는 별도의 패키지 이어야하므로 순환 가져 오기 기능을 사용할 수 없습니다.

물론이 믹스에 이벤트 처리기를 추가 할 수는 있지만, 필자가 시연했듯이 필요하지는 않습니다.