go语言echo与martini框架详解

作为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更好一些,坑相比较少。项目使用哪一种框架,就看自己的业务来衡量了。这应当是对一个架构师基本的要求了。这大概就是我对这两个框架的了解了,有什么问题或疑问,欢迎提出。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181106G1YT2100?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券