Micro-Kernel
微内核架构有时也被成为插件架构模式(plug-in architecture pattern),通常用于实现基于产品的应用,如Eclipse和Firefox。然而许多公司也将内部的业务软件做成软件产品,提供版本、发版说明和插件特性。微内核架构模式通过插件向核心应用添加额外的功能,提供了可扩展性和功能的独立和分离。
微内核架构包含两部分组件:核心系统(core system)和插件模块(plug-in modules)。应用逻辑被分割为独立的插件模块和核心系统,提供了可扩展性、灵活性、功能隔离和自定义处理逻辑的特性。
微内核架构的核心系统通常提供系统运行所需的最小功能集。许多操作系统使用的就是微内核架构,这也是它名字的由来。从商业应用程序的角度来看,核心系统一般是通用业务逻辑,没有特殊情况、特殊规则或复杂情形下的自定义代码。
插件模块是独立的模块,包含特定的处理、额外的功能和自定义代码,来向核心系统增强或扩展额外的业务能力。通常插件模块之间也是独立的,也有一些插件是依赖于若干其它插件的。重要的是,尽量减少插件之间的通信以避免依赖的问题。
核心系统需要知道哪些插件是可用的且如何使用。一种实现的方式是使用插件注册表。注册表中包含插件的一些信息,如名称、数据契约(输入数据和输出数据)、远程访问协议(决定插件如何与核心系统连接,XML或WSDL等)。
插件模块和核心系统的连接方式有多种,包括OSGi (open service gateway initiative)、messaging、web service、甚至点对点绑定(对象实例化)。选择哪种连接方式取决于构件的应用类型和是否分布式部署等特殊需求。
插件和核心系统之间的契约也是各种各样的,既可以是标准的也可以是自定义的。通常在使用第三方插件时需要自定义契约。这种情况下,通常创建一个该插件契约到你的标准契约的适配器,这样核心系统就不需要针对每个插件的定制编码了。创建标准契约时(通常为XML),要记得从一开始就设计好版本策略。
案例
collect.go 通用的插件接口
package microkernel
import "context"
// 收集器接口
type Collector interface {
Init(evtReceiver EventReceiver) error // 初始化收集器
Start(agtCtx context.Context) error // 开启收集器,使用Context控制收集器退出
Stop() error // 停止收集器
Destroy() error // 销毁收集器
}
event.go 通用事件处理接口
package microkernel
// 事件处理接口
type EventReceiver interface {
OnEvent(evt Event)
}
demo_collector.go 实现处理接口
package microkernel
import (
"context"
"errors"
"fmt"
"time"
)
type DemoCollector struct {
evtReceiver EventReceiver
agtCtx context.Context
stopChan chan struct{}
name string
content string
}
// 创建Collect 收集器
func NewCollect(name string, content string) *DemoCollector {
return &DemoCollector{
stopChan: make(chan struct{}),
name: name,
content: content,
}
}
func (c *DemoCollector) Init(evtReceiver EventReceiver) error {
fmt.Println("initialize collector", c.name)
c.evtReceiver = evtReceiver
return nil
}
func (c *DemoCollector) Start(agtCtx context.Context) error {
fmt.Println("start collector", c.name)
for {
select {
// 监听取消信号,收到立刻退出
case <-agtCtx.Done():
c.stopChan <- struct{}{}
return nil
default:
time.Sleep(time.Millisecond * 50)
// 这里应该运行收集器的处理逻辑
// do something
// 将处理结果给到agent
c.evtReceiver.OnEvent(Event{c.name, c.content})
}
}
}
func (c *DemoCollector) Stop() error {
fmt.Println("stop collector", c.name)
select {
case <-c.stopChan:
return nil
case <-time.After(time.Second * 1):
return errors.New("failed to stop for timeout")
}
}
func (c *DemoCollector) Destroy() error {
fmt.Println(c.name, "released resources.")
return nil
}
agent.go Agent服务 加载Collector,并负责调度Collector
package microkernel
import (
"context"
"errors"
"fmt"
"strings"
"sync"
)
const (
Waiting = iota // 等待中
Running // 运行中
)
var WrongStateError = errors.New("can not take the operation in the current state")
type CollectorsError struct {
CollectorErrors []error
}
func (ce CollectorsError) Error() string {
var strs []string
for _, err := range ce.CollectorErrors {
strs = append(strs, err.Error())
}
return strings.Join(strs, ";")
}
type Event struct {
Source string
Content string
}
type Agent struct {
collectors map[string]Collector // 收集器map,方便查询
evtBuf chan Event // 事件处理结果队列
cancel context.CancelFunc // 取消函数
ctx context.Context // 上下文
state int // 收集器状态
}
func (agt *Agent) EventProcessGoroutine() {
var evtSeg [10]Event
// 10个结果一组打印 收集器的事件结果
for {
for i := 0; i < 10; i++ {
select {
case evtSeg[i] = <-agt.evtBuf:
case <-agt.ctx.Done():
return
}
}
fmt.Println(evtSeg)
}
}
// 创建Agent
func NewAgent(sizeEvtBuf int) *Agent {
agt := Agent{
collectors: map[string]Collector{},
evtBuf: make(chan Event, sizeEvtBuf),
state: Waiting,
}
return &agt
}
// 注册收集器
func (agt *Agent) RegisterCollector(name string, collector Collector) error {
// 非等待状态 不允许注册收集器
if agt.state != Waiting {
return WrongStateError
}
// 注册收集器
agt.collectors[name] = collector
// 初始化收集器
return collector.Init(agt)
}
func (agt *Agent) startCollectors() error {
var err error
var errs CollectorsError
var mutex sync.Mutex
// 异步开启所有的收集器,若发送错误则收集错误,一个收集器出错并不影响其他收集器
for name, collector := range agt.collectors {
go func(name string, collector Collector, ctx context.Context) {
defer func() {
mutex.Unlock()
}()
err = collector.Start(ctx)
mutex.Lock()
if err != nil {
errs.CollectorErrors = append(errs.CollectorErrors, errors.New(name+":"+err.Error()))
}
}(name, collector, agt.ctx)
}
if len(errs.CollectorErrors) == 0 {
return nil
}
return errs
}
func (agt *Agent) stopCollectors() error {
var err error
var errs CollectorsError
// 停止所有的收集器,若发送错误则收集错误,一个收集器出错并不影响其他收集器
for name, collector := range agt.collectors {
if err = collector.Stop(); err != nil {
errs.CollectorErrors = append(errs.CollectorErrors,
errors.New(name+":"+err.Error()))
}
}
if len(errs.CollectorErrors) == 0 {
return nil
}
return errs
}
func (agt *Agent) destoryCollectors() error {
var err error
var errs CollectorsError
// 销毁所有的收集器,若发送错误则收集错误,一个收集器出错并不影响其他收集器
for name, collector := range agt.collectors {
if err = collector.Destroy(); err != nil {
errs.CollectorErrors = append(errs.CollectorErrors,
errors.New(name+":"+err.Error()))
}
}
if len(errs.CollectorErrors) == 0 {
return nil
}
return errs
}
// 开启Agent服务
func (agt *Agent) Start() error {
// 非等待状态 不开启Agent服务
if agt.state != Waiting {
return WrongStateError
}
// 更新状态为运行中
agt.state = Running
// 设置 context和取消函数
agt.ctx, agt.cancel = context.WithCancel(context.Background())
// 异步开启事件处理
go agt.EventProcessGoroutine()
// 开启收集器
return agt.startCollectors()
}
func (agt *Agent) Stop() error {
// 非运行状态 不可停止
if agt.state != Running {
return WrongStateError
}
agt.state = Waiting
// 执行取消函数,停止所有的收集器任务
agt.cancel()
// 这里执行的,收集器的停止方法,和上面的取消不一样
// agt.cancel() 停止收集器运行中的任务
// agt.stopCollectors() 停止方法停止整个收集器
return agt.stopCollectors()
}
func (agt *Agent) Destory() error {
// 非运行状态 不可销毁
if agt.state != Waiting {
return WrongStateError
}
return agt.destoryCollectors()
}
func (agt *Agent) OnEvent(evt Event) {
// 事件结果放入事件处理结果队列
agt.evtBuf <- evt
}
agent_test.go 测试类
package microkernel
import (
"fmt"
"testing"
"time"
)
func TestAgent(t *testing.T) {
agt := NewAgent(100)
c1 := NewCollect("c1", "1")
c2 := NewCollect("c2", "2")
agt.RegisterCollector("c1", c1)
agt.RegisterCollector("c2", c2)
if err := agt.Start(); err != nil {
fmt.Printf("start error %v\n", err)
}
//fmt.Println(agt.Start())
time.Sleep(time.Second * 5)
agt.Stop()
agt.Destory()
}
总结:
特点
- 易于扩展
- 错误隔离
- 保持架构一致性
要点
- 内核包含公共流程或通用逻辑
- 将可变或可扩展部分规划为扩展点
- 抽象扩展点行为,定义接口
- 利用插件进行扩展
参考: