5.1. Eudore
Eudore
github: github.com/eudore/eudore
Eudore是一个自由扩展web框架,框架本身提供基础的框架操作和各种机制实现功能,对于简单的应用框架本身就可以轻易的处理,对于构建复杂的程序基于扩展思路可以轻松添加任意方法和修改内置逻辑,而Eudore本身就提供函数扩展机制、控制器执行机制、中间件运行机制、路由匹配算法、内置日志输出、集成配置及操作库等功能,向设计有序、逻辑简化、组件解耦的发展方向不停前进。
eudore解决一下痛点:
- 自定义扩展能力,不修改源码的前提下修改任意逻辑。
- 内置Logger输出全部提示,只要有错误就会自动输出。
- 高性能功能齐全路由器,不会出现路由器优先级问题。
- Context自动闭包封装,复用常用逻辑。
- 低代码复杂度减少和中文注释阅读阅读难度
基于完全解耦实现六主要五辅助的结构,六主:App、Config、Logger、Server、Router、Context,五辅:Handler、Controller、Bind、Render、Validater。
各部分定义:
名称 | 作用 | 定义 |
---|---|---|
Application | 运行对象主体 | app.go |
Config | 配置数据管理 | config.go |
Logger | App和Ctx日志输出 | logger.go loggerstd.go |
Server | http Server启动 | server.go |
Router | 请求路由选择 | router.go routerstd.go |
Context | 请求处理上下文 | context.go |
Handler | 请求处理函数 | handler.go |
Controller | 解析执行控制器 | controller.go |
Bind | 请求数据反序列化 | bind.go |
Render | 响应数据序列化 | render.go |
Validater | 数据校验 | validater.go |
特性
扩展
自由扩展能力是eudore最大的特点。
- eudore App是一个简单的App实现,对于复杂功能App可以通过组合定制一个需要的App。
- Server、Router、Logger、Config、Context、Controller、Binder、Renderer、Validater等部分都是接口,可以使用接口重写逻辑。
- Context和Controller执行函数具有扩展机制,可以对其扩展额外的执行逻辑和方法。
- 超过95%的代码可以重新实现或移除。
简单
eudore由于具有强大的扩展机制和高度解耦,非必要的配置选项和逻辑;接口即为逻辑,几乎方法名称就表示其实现的逻辑,没有一些额外的逻辑。
eudore框架核心仅一个包,包含多种内置实现共6200+核心代码,同时不具有其他依赖,代码覆盖率测试100.00%。
易用
App、Context、Controller可以不修改源码的前提下,额外添加任意方法。
可以使用任意形式的请求处理参数,例如func(Context) error类型函数就会自动处理error返回500.
内置Logger库,框架内返回错误全部将自动输出,不会忽略掉出现error。
内置Config库,支持环境变量、命令行参数设置配置,支持差异化配置。
功能丰富默认具有100+exmaple,同时具有不少扩展功能,熔断器、Session、代码更新自动重启、后台启动、重写实现的Server、RAM鉴权、httptest等。
性能
eudore各个部分均没用明显的性能瓶颈,最差情况下与其他库性能相识。
在关键实现中使用对象复用、静态化、benchmark排查等基本方法,避免了各类性能浪费,同时基于易扩展默认并没有多的额外逻辑。
- Server 部分具有两种实现,nethttp适配和独立开发的eserver,eserver重新实现的http协议,细节不一定完善,但是有默认的nethttp兜底,性能最差的情况与nethttp略低。
- Router 独立重新实现的路由器,性能具有httprouter性能的70%-90%,内存占用少数倍,并扩展多项功能。
- Config 用于初始化使用,可以忽略性能对运行影响,可以把config当作一个map或struct使用。
- Logger 需要输出的多项Field,默认实现使用的json输出,使用json字符拼接,性能不低于默认logrus。
- Binder & Renderer 通常实现,可以不使用或自行重写。
- Controller 默认使用反射获取并执行函数,性能与其他反射mvc相似。
- HandlerFunc 处理函数扩展在不计算网络和业务的情况下,对框架性能损耗不足1%。
- Validate 实现数据校验器
源码文件关系
app.go
|
------------------------------------------------------------------------------------
| | | | | | | |
config.go logger.go router.go server.go context.go bind.go render.go validater.go
| |
converter.go |
-------------------------------
| | |
handler.go routerstd.go controller.go
上述文件相关间的关系:
- app.go 组合Config、Logger、Router、Server、Context、Bind、Render、Validate、GetWarp等对象定义App,对应源码文件定义每部分对象。
- config.go 定义Config接口相关对象,同时实现内置两种Config对象ConfigMap和ConfigEudore;converter.go为configEudore提供一些基础函数(Set & Get)支持。
- logger.go 定义Logger接口相关对象,实现LoggerStd、LooggerInit对象
- server.go 定义Server接口相关对象,实现默认Server版本ServerStd。
- router.go 定义Router接口相关对象,实现RouterStd对象和中间件存储前缀树;
- routerstd.go 定义RouterCoreStd实现,使用radix实现高性能路由器。
- context.go 定义Context接口,同时实现默认Context对象和ContextBase;
- handler.go 定义http Handler对象以及实现处理函数扩展机制。
- controller.go定义控制器相关接口,同时给与几种默认控制器实现。
- bind.go 定义Binder对象并实现。
- render.go 定义Renderer对象并实现。
- validater.go 定义Validate接口和实现。
下列文件未出现在上述关系图,提供了基本对象的支持:
- doc.go 定义godoc。
- const.go 定义全部常量。
- http.go 定义http对象。
- util.go 实现简单类型转换函数封装。
功能
主包:eudore
扩展包:component、middleware
命名规则
app对象变量名称统一为app,Context及衍生对象变量名称统一为ctx。基础控制器命名为ControllerXxxx,应用控制器为XxxxController,这些控制器变量命名为ctl。
对象构造函数名称统一为NewTypeName,例如NewLoggerStd.
设计思考
App对象的定位
eudore.App对象只是一个对象复合载体,用于保存各个对象,如果重新实现自定义App对象,eudore.App的方法全部可以忽略,同时仅使用结构体属性,用于传递给其他构造函数。
为什么Config、Logger、Router的默认实现中没有App对象?
主要组件嵌套App就循环嵌套,防止App逻辑混乱,所以使用set print的方法来提供日志输出,给予所有组件最小App权限。
为什么Context里面没有返回App对象的方法?
App对象是组件载体,不是默认app对象,Context可能需要一个专用的App对象;正确的方法应该是闭包返回一个HandlerFunc。
func NewHandleOfApp(app *eudore.Core) eudore.HandlerFunc {
return func(ctx eudore.Context) {
app.xxx
ctx.xxx
...
}
}
Context为什么不实现io.StringWriter接口?
一方面WriteString(string) error
是默认的render一种,与WriteJSON(interface{}) error
和WriteFile(string) error
类似,均只返回err不返回写入长度;另一方面依赖ResponseWrite实现io.StringWriter接口,如果再开启gizp后io.StringWriter就无效了,所以过于麻烦也没必要,实际使用WriteString并没有那么多。
Context为什么不实现context.Context接口?
Context作为请求上下文不主动实现context.Context接口,为了推荐使用Context() context.Context
方法获取环境上下文来使用,而不是将Context作为环境上下文实现,原因见context包源码实现。同时context.Context接口中Value(key interface{}) interface{}
方法没有实现的必要性,可以在扩展中实现,一般数据使用Params保存。而实现的Err() error
方法是为了返回请求上下文做日志本身输出的err。
Context为什么不实现GetQueryint这样的方法?
不具有必要性同时对象太多。在ContextData中就实现了这些参数的类型转换的方法,而Context本身获取的param、query、header、cookie、formvalue就五类获取字符串参数,然后每个最少转换5种,就是至少20个额外方法了。
为什么独立定义Cookie对象?
请求的Cookie本身就是键值形式,而http.Cookie是被Cookie读写复用的对象,是Cookie也是SetCookie,但是一些SetCookie的属性Cookie不需要,会占用更多的内存分配,同时标准库的http.Cookie想改也无法改了。
RequestReader对象设计
RequestReader对象是http.Request对象的别名,使用http.Request对象作为请求数据载体,eudore框架会忽略http.Request对象的方法仅使用结构体属性。
反馈和交流请加群组:QQ群373278915。