主流开发语言,为了达到项目间的低耦合,都会借助 IoC 框架来实现.即抽象和实现分离,使用抽象层,不用关心这些抽象层的具体实现;抽象层的实现,可以独立实现.现在比较流行的领域驱动设计(ddd),为了达到将领域层作为最核心,也需要依赖于 IOC.
回过头来,我们看看 golang 实现的 ioc 框架,有 golang 风格的框架,也有从其他主流语言搬过来的比较重的框架.我觉得目前实现最轻量级的,当属 martini 框架的 ioc 依赖库 github.com/codegangsta/inject .代码行数很少,提供类型注册,接口注册,类型解析,函数注入,struct 注入的方法,可以说基本的已经比较全了.从文章开头应该可以猜到,我现在一直在学习 ddd,目前在. NET 实际项目中边运用边学习.在实际使用中发现,ioc 除了要有单例模式(Singleton)支持外,应该还有临时实例(Transient)的支持.因此萌生了我写 golang 下的 ioc 框架的原因.
我的目的很简单,希望 ioc 不仅支持 Singleton,还要支持 Transient.最初想法是,编写一个抽象层,里面支持这两种模式的注入.其中 transition 部分自己独立实现,而 singleton,则采用现成的 github.com/codegangsta/inject 框架,并加一层适配.Transient 的实现,其特点就是,每次解析类型(Resolve)时,都需要创建一个新的对象,这个对象和先前创建的是独立的.此处我采用反射机制,根据类型创建新对象.golang 中没有构造函数,为了在创建对象后并在使用前,对其初始化,我引入了构造函数的概念.这个构造函数的接口其实很简单
// Initializer is to init a struct.
type Initializer interface {
InitFunc() interface {}
}
在这里我吐槽下博客园,怎么插入代码,还不支持 golang 啊?
这个接口很简单,就一个返回 interface{} 的函数.其实返回的应该是另一个函数,即为构造函数.例如:
func(container * iocContainer) InitFunc() interface {} {
return func() {
if ! container.isInitialized {
container.locker = &sync.RWMutex {}
container.singleton = &singletonContainer {
valuemapper: make(map[reflect.Type] reflect.Value)
}
container.transient = &transientContainer {
typemapper: make(map[reflect.Type] reflect.Type)
}
container.isInitialized = true
}
}
}
当初始化时,调用一次构造函数,即完成了一次初始化的操作.其实针对 singleton 也是一样,也需要一次初始化,只是这个初始化要求仅在第一次时进行,在这里不会因此只调用一次(因为 ioc 框架不知道你什么时候会被第一次调用,这里需要由构造函数的实现自己进行判断,此处可以用一个字段 isInitialized 进行检查是否已经初始化了).
都说 golang 的反射,性能很差,我觉得部分反射的部分功能会性能很差,但有些应该还算凑合吧.既然 ioc 框架实现完了,那就测试下性能.由于在调整前,性能数据没有保存,就不展示了.总之,在改版前,发现 inject 包,在 Resolve 的性能很差.经过仔细排查,发现有一处的实现很智能,即当 Resolve 的接口类型在已注入的类型中不存在时,会尝试将已存在的类型转为接口,如果可以转换则返回.由于 golang 的理念里,没有类型树.认为接口的方法都实现了,就认为实现了接口,那么判断本身就会变得耗时.也因为这个原因,我重写了 singleton 部分,在 Resolve 的时候,仅仅根据传入的类型来判断.如果这个类型在注册时为 singleton,那就是 singleton,且原先是接口还是类型,都原样拿出,不进行任何转换.果然发现性能有所提升.
这是 ioc 容器的接口,也是最核心的:
// ReadonlyContainer is a readonly container
type ReadonlyContainer interface {
// Resolve is to get instance of type.
Resolve(typ reflect.Type) reflect.Value
// Invoke is to inject to function's params, such as construction.
Invoke(f interface {})([] reflect.Value, error)
}
// Container is a container for ioc.
type Container interface {
Initializer ReadonlyContainer
// Register is to register a type as singleton or transient.
Register(val interface {},
lifecycle Lifecycle)
// RegisterTo is to register a interface as singleton or transient.
RegisterTo(val interface {},
ifacePtr interface {},
lifecycle Lifecycle)
// SetParent is to resolve parent's container if current hasn't registered a type.
SetParent(parent ReadonlyContainer)
}
这是调用的代码:
func main() {
var requestContext = ioc.NewContainer() requestContext.SetParent(iocContainer) requestContext.RegisterTo( & productcategoryApp.ProductCategoryApplicationServiceImpl {},
( * application.ProductCategoryApplicationService)(nil), ioc.Transient) commandMQAdapter: =new(provider.MyCommandMQProvider) processor: =cqrs.NewCommandProcessor(commandMQAdapter) processor.RegisterMiddleware(( * middleware.AuthCommandMiddleware)(nil))
// execute count
var exeCount = 1000000
// concurrent routine
var concurrentCount = 1
for true {
var wg * sync.WaitGroup = &sync.WaitGroup {}
time.Sleep(300 * time.Millisecond) startTime: =time.Now().UnixNano() for i: =0;
i < concurrentCount;
i++{
wg.Add(1) go func(wg1 * sync.WaitGroup) {
for j: =0;
j < exeCount / concurrentCount;
j++{
requestContext.Invoke(func(productCategoryAppSvc application.ProductCategoryApplicationService, roContainer ioc.ReadonlyContainer) {
//processor.RegisterHandler(productCategoryAppSvc)
})
}
wg1.Done()
} (wg)
}
wg.Wait() endTime: =time.Now().UnixNano() consoleLog.Printf("[info] requestContext.Invoke for %d times with %d routines execute in %vms.\n", exeCount, concurrentCount, float64(endTime - startTime) / float64(time.Millisecond))
}
}
这是性能数据:
1 routine, 3 times resolve singleton and 1 times resolve transient per code invoke, invoke 1,000,000 times.
Result:
[commandprocessor] 2016 / 07 / 17 11 : 31 : 29[info] requestContext.Invoke
for 1000000 times with 1 routines execute in 4971.1971ms. [commandprocessor] 2016 / 07 / 17 11 : 31 : 34[info] requestContext.Invoke
for 1000000 times with 1 routines execute in 4951.494214ms. [commandprocessor] 2016 / 07 / 17 11 : 31 : 39[info] requestContext.Invoke
for 1000000 times with 1 routines execute in 4954.376794ms.
2 routine, 3 times resolve singleton and 1 times resolve transient per code invoke, invoke 1,000,000 times.
Result:
[commandprocessor] 2016 / 07 / 17 11 : 23 : 50[info] requestContext.Invoke
for 1000000 times with 2 routines execute in 2779.720723ms. [commandprocessor] 2016 / 07 / 17 11 : 23 : 53[info] requestContext.Invoke
for 1000000 times with 2 routines execute in 2719.810844ms. [commandprocessor] 2016 / 07 / 17 11 : 23 : 56[info] requestContext.Invoke
for 1000000 times with 2 routines execute in 2734.028326ms.
预估下来,差不多是 2 routine, 4 resolve action, 350,000 / sec 的性能数据.我是在笔记本上进行的测试(i5 双核),启用 2 个并发 routine 来测试 Resolve,每次测试代码的一次执行,包含 Resolve4 次调用.测试下来,每秒 35w 次测试代码执行.这个性能,我觉得在业务系统开发中,不需要考虑性能损耗的问题了.
--------------------------------- 分割线 -------------------------------------------------------------
我的,已经挂在 github 上,有兴趣的可以去了解下.
通过 go 来安装 ioc 包: go get github.com/berkaroad/ioc
使用中有何问题,欢迎在 github 上给我提 issue,谢谢!
来源: http://www.cnblogs.com/Berkaroad/p/5769575.html