作为go语言的工程师,相信我们都或多或少的使用过go语言的后端框架,今天我来分享一下自己对go语言比较轻量级的echo以及martini框架的见解。那直接开门见山了吧。
Martini框架
martini框架是go语言轻量级的后端框架,使用简单易上手,极佳的路由匹配与转发,同时扩展性极强,模块化的中间件设计,这些都是它的特点,特别是利用依赖注入的思想,下面我们结合源码来讲讲吧。
martini.go这个源码文件主要包含Martini这个结构体,里面包含Injector, logger以及中间件Handler。
type Martini struct {
inject.Injector// 依赖注入的调用者
handlers []Handler // 中间件函数
actionHandler // 路由匹配,路由转发,在所有中间件函数处理后再执行
logger*log.Logger // 日志
}
inject.go主要是实现依赖注入,用于存储中间件函数以及用户自定义函数回调时的参数。依赖注入不同于传统编程,传统编程是调用者自己来决定使用那些被调用者实现的,而依赖注入则是由注入器(injector)来决定,注入器创建被调用者,注入调用者。例如:在inject.go中,被调用者为func,注入属性就是对func注入实参。
router.go主要是实现路由匹配,路由转发,分组路由等功能。
通过阅读源码之后,发现martini框架核心的源码,其实就是martini.go, inject.go以及router.go,下面就来仔细分析一下每个类的功能与作用。
inject.go源码
依赖注入核心是inject.go代码,辅助实现martini机具扩展性的中间件。
先来讲讲inject.go:
type Injector struct {
Application // 结构体各个字段赋值
Invoker // 通过反射实现函数调用的接口
TypeMapper //Maps以val的反射Type为key,反射Value为值
SetParent(Injector) // 设置父Injector
}
type injector struct {
values map[reflect.Type]reflect.Value // 存放注入参数的类型和值
parent Injector // 父节点
}
type TypeMapper interface {
Map(interface{}) TypeMapper // 具体类型的映射,根据值的具体类型直接建立映射
MapTo(interface{}, interface{}) TypeMapper //转化类型映射
….
}
这是几个关键的结构体,具体实现依赖注入就是,将Func以type Handler interface{} 的形式注入,其中参数保存在values的map中,调用方法时,遍历这个方法有的参数的Type,去values中获取对应的值,再使用Call方法实现调用。
直观点说,Injector通过TypeMapper注入方法,与参数值到values中,通过Invoker.invoke去调用注入的方法,调用方法时,获取方法入参的Type,在通过values获取值,实现调用。
martini.go源码
type Martini struct {
inject.Injector// 注入器,匿名类
handlers []Handler // 中间件函数
actionHandler // 请求中间件函数处理后,进行路由分发
logger*log.Logger //日志
}
type context struct {//每个请求上下文
inject.Injector// 注入器
handlers []Handler // 中间件函数
actionHandler // 最后的路由分发处理
rwResponseWriter// 每个返回值
indexint // 索引
}
我们在运行martini框架时,生成一个单例,包含所有注入器,中间件函数,路由;每个请求过来时,会调用createContext创建一个上下文,此时把各种中间件,注入器赋值进去。这样就可以实现每个请求过同样的中间件。中间件是一个type Handler interface{} 的函数,也就是中间件的Type一定要是函数,且返回的是一个参数,可以是基础类型或者结构体。
router.go源码
主要实现路由的存储与转发。
type router struct {
routes[]*route // 存入各种方式的路由(Post,Get等)
notFounds[]Handler
groups[]group // 实现路由分组
routesLock sync.RWMutex
}
type route struct {
methodstring // 请求方式(Get or Post)
regex*regexp.Regexp // 正则匹配
handlers []Handler // 路由方法
patternstring // 路由地址
namestring
}
router结构体主要是存储所有post,get等请求的路由方式;route结构体就是存储的具体路由方法,路由地址。
martini总结
整个Martini框架运行的模式,我们生成一个Martini的单例。
1.通过martini.Use()来添加Handler中间件,Use添加中间件之后,每个请求都会过这个中间件。
2.通过martini.Map()以及martini.MapTo()来注入每个Handler的参数与值。
3.router.go路由器中,Post和Get等方法用来添加路由地址和路由方法。
当请求来到,每个请求会createContext的上下文,context.run()用户运行每个中间件Handler,最后去调用martini.Action开始路由分发,匹配路由地址,执行Post或者Get方法,之后ResponseWriter写回返回值,本次请求结束。
Echo框架
echo框架是go语言轻量级后端框架,它主要特点就是在所有web框架中,路由性能最好,各种灵活的中间件,两个框架使用上都是异曲同工,下面我们深入理解echo框架的原理吧。
在echo框架源码中,主要文件echo.go,router.go文件。直接看看echo.go源码吧。
Echo struct {
premiddleware[]MiddlewareFunc // 中间件函数,执行路由分发前
middleware []MiddlewareFunc // 中间件函数,执行路由分发后
router*Router // 路由分发router
Server*http.Server // 服务端参数
TLSServer *http.Server// 安全加密协议服务端
Listener net.Listener //服务端监听器
TLSListener net.Listener // 安全加密服务端监听器
BinderBinder // 数据处理器
ValidatorValidator // 初始化校检
Renderer Renderer // 请求模板
LoggerLogger // 自定义日志
…..
}
echo框架对中间件函数分为路由前和路由后,路由前指当请求到达服务端后,还没有通过router匹配到指定的路由函数;路由后就是指此时已经执行由router匹配后的路由函数。两者有什么使用场景呢?路由前,我们可以拿来做限流,鉴权,降级等支持服务端稳定性的功能;路由后,可以做数据处理,发送或者熔断等功能。从这里来讲,echo框架的功能比martini支持的更全面。
router路由器,与martini框架相似,主要实现路由的存储与转发,组路由等功能,web框架必不可少的一环。
Server是服务端的一些重要配置参数,读写超时时间配置;Listener则是请求监听器,web框架跑起来之后,监听器会不停地监听i/o是否有请求写入,这里一般使用i/o多路复用。
Binder是数据处理器,会把请求上下文context中的数据,根据Bind()的结构体格式进行数据解析。Validator是初始化验证,在context.Bind后调用,比如验证传入的结构体是否和Bind()结构体相同。Renderer是用户注册一个渲染模板,请求写入格式以及返回格式是怎么样的,我们都可以通过Renderer来设置。Logger就是自定日志啦。
echo框架整体运行流程,调用e.StartServer启动整个服务端,创建监听器。请求过来之后,调用监听器Accept()方法处理请求,调用ServeHTTP处理请求,获取请求上下文,如果premiddleware不为空,则调用路由分发前的中间件函数,之后调用路由分发,找到对应路由分发后的函数,再调用路由分发后的中间件函数,返回请求值。
框架对比
中间件
marini中间件只支持路由前过滤中间件,echo支持路由前与路由后中间件,同时echo还有很多例如鉴权,访问控制等已写好的中间件。扩展性来讲,echo框架更全面。同时,martini框架使用依赖注入,这会是降低框架执行效率。
路由效率
echo框架的路由基于radix tree基数树,这也是linux内核内存管理中建立索引的重要数据结构,后面有时间会具体讲一下radis tree,同时路由使用了sync pool临时对象池来重复利用内存并且几乎达到了零内存占用。所以echo框架的路由效率这么高。具体radis tree的实现可以看下router.go源代码。而martini的框架,router路由中直接用的[]*route来存储路由,所以效率当然不及echo框架。
总结
echo对比于martini扩展性更强,路由效率更高。其他方面的话,echo框架的功能也更全面,支持TLS安全协议,渲染引擎Renderer配置等。
当然因为martini框架出现得更早,稳定性方面可能会比echo更好一些,坑相比较少。项目使用哪一种框架,就看自己的业务来衡量了。这应当是对一个架构师基本的要求了。这大概就是我对这两个框架的了解了,有什么问题或疑问,欢迎提出。
领取专属 10元无门槛券
私享最新 技术干货