golang 依赖控制反转(IoC)
主流开发语言,为了达到项目间的低耦合,都会借助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次测试代码执行。这个性能,我觉得在业务系统开发中,不需要考虑性能损耗的问题了。
---------------------------------分割线-------------------------------------------------------------
我的ioc项目,已经挂在github上,有兴趣的可以去了解下。https://github.com/Berkaroad/ioc
通过go来安装ioc包: go get github.com/berkaroad/ioc
使用中有何问题,欢迎在github上给我提issue,谢谢!
golang 依赖控制反转(IoC)的更多相关文章
- 浅析“依赖注入(DI)/控制反转(IOC)”的实现思路
开始学习Spring的时候,对依赖注入(DI)——也叫控制反转(IOC)—— 的理解不是很深刻.随着学习的深入,也逐渐有了自己的认识,在此记录,也希望能帮助其他入门同学更深入地理解Spring.本文不 ...
- 控制反转IOC的依赖注入方式
引言: 项目中遇到关于IOC的一些内容,因为和正常的逻辑代码比较起来,IOC有点反常.因此本文记录IOC的一些基础知识,并附有相应的简单实例,而在实际项目中再复杂的应用也只是在基本应用的基础上扩展而来 ...
- 控制反转IOC与依赖注入DI
理解 IOC http://www.cnblogs.com/zhangchenliang/archive/2013/01/08/2850970.html IOC 相关实例 的http:// ...
- 控制反转(Ioc)和依赖注入(DI)
控制反转IOC, 全称 “Inversion of Control”.依赖注入DI, 全称 “Dependency Injection”. 面向的问题:软件开发中,为了降低模块间.类间的耦合度,提倡基 ...
- 控制反转IOC与依赖注入DI【转】
转自:http://my.oschina.net/1pei/blog/492601 一直对控制反转.依赖注入不太明白,看到这篇文章感觉有点懂了,介绍的很详细. 1. IoC理论的背景我们都知道,在采用 ...
- 依赖注入(DI)和控制反转(IOC)
依赖注入(DI)和控制反转(IOC) 0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只 ...
- iOS控制反转(IoC)与依赖注入(DI)的实现
背景 最近接触了一段时间的SpringMVC,对其控制反转(IoC)和依赖注入(DI)印象深刻,此后便一直在思考如何使用OC语言较好的实现这两个功能.Java语言自带的注解特性为IoC和DI带来了极大 ...
- 个人对【依赖倒置(DIP)】、【控制反转(IOC)】、【依赖注入(DI)】浅显理解
一.依赖倒置(Dependency Inversion Principle) 依赖倒置是面向对象设计领域的一种软件设计原则.(其他的设计原则还有:单一职责原则.开放封闭原则.里式替换原则.接口分离原则 ...
- 【转载】浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)
原文地址 http://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...
随机推荐
- SQL Server: Difference Between Locking, Blocking and Dead Locking
Like ever, today’s article of Pinal Dave was interesting and informative. After, our mutual discussi ...
- bzoj 1833 [ZJOI2010]count 数字计数(数位DP)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1833 [题意] 统计[a,b]区间内各数位出现的次数. [思路] 设f[i][j][k ...
- C# 结构体保存自定义文件 实例部分代码(转)
http://www.cnblogs.com/jxsoft/archive/2011/08/26/2154705.html
- BNUOJ-26579 Bread Sorting YY
题目链接:http://www.bnuoj.com/bnuoj/problem_show.php?pid=26579 考虑两个性质:蚂蚁的相对位置不变,蚂蚁碰撞时相当于对穿而过,然后排两次序就可以了. ...
- LaTex 高中数学公式
排版数学公式是TeX系统设计的初衷,它在LaTeX中占有特殊地位,也是LaTeX最为人所称道的功能之一.基于对MathType排版效果的不满意,以及对公式进行检索的需求,我们使用LaTeX输入数学公式 ...
- 远程控制篇:在DELPHI程序中拨号上网
用MODEM拨号上网,仍是大多数个人网民选择上网的方式.如果能在我们的应用程序中启动拨号连接(如IE浏览器程序中的自动拨号功能),无疑将会方便我们的软件用户(不用再切换应用程序,运行拨号网络),提高我 ...
- MYSQL- 分页存储过程
工作需要,用到MYSQL的分页功能,在网上找到一个不错的分页存储过程,代码整理了一下! 存储过程代码 CREATE PROCEDURE `sp_hj_splitpage`( in _pagecurre ...
- win32多线程学习总结:同步机制critical sections
Critical sections是win32中最容易使用的同步机制,用来处理一份共享资源,共享资源指的是每次只能够被一个线程处理的资源,包括内存.数据结构.文件等. 优点: 1.使用便捷,即声明即使 ...
- hbase多用户入库,regionserver下线问题
近期对hbase多用户插入数据时,regionserver会莫名奇妙的关闭,regionserver的日志有很多异常: 如下: org.apache.hadoop.hbase.DroppedSnaps ...
- 修改 suse 上的postfix 的日志路径
刚刚搭建的邮件服务器,没过多久硬盘就爆满了,不得已移动存储,包括日志. 移动存储灰常简单,查找 /etc/postfix/main.cf 中的 data_directory / base_direct ...