组件
组件解决的是项目中依赖开源社区第三方包的问题,yago 组件的中心思想是在完整保留第三方包的所有功能基础上,也能扩展,并对访问者提供简便统一的操作接口。 应该说组件是 yago 最核心的思想之一。
1. 组件原理
为了方便用户使用组件,我们定义了一个全局的变量:yago.Component
type components struct {
m sync.Map
}
func (c *components) Ins(key string, f func() interface{}) interface{} {
v, ok := c.m.Load(key)
if !ok {
val := f()
v, _ = c.m.LoadOrStore(key, val)
}
return v
}
// 全局变量 Component 供外部调用
var Component = new(components)
可以看到, components 的本质是一个安全 Map,我们把所有的组件都放到 map 里面,每个组件都会有一个对应的 key, 这个 key,通常采用配置文件中的块名称,如下面日志组件 logger 的配置:
# logger 会做为组件的 Key,logger 也是日志组件的默认名称,修改 logger 名字的话,调用时需要指定修改后的名称
[logger]
# json | text, default text
formatter = "json"
# 日志最低等级 Panic = 0, Fatal = 1, Error = 2, Warn = 3, Info = 4, Debug = 5, Trace = 6
level = 5
# 文件路径
file_path = "./logs/app.log"
# 最大保留的备份数
max_backups = 20
# 日志最大保留天数
max_age = 30
# 文件最大大小(mb)
max_size = 500
# 是否开启压缩
compress = true
2. 实现组件
每个组件需要实现 Ins() 方法,在自己的 Ins 方法中调用 yago.Component.Ins(),注册到全局组件中。 以 logger 组件为例:
// Logger 是对 logrus.Logger 的扩展
type Logger struct {
*logrus.Logger
}
func Ins(id ...string) *Logger {
var name string
if len(id) == 0 {
name = "logger"
} else if len(id) > 0 {
name = id[0]
}
// 调用 yago.Component.Ins() 注册到全局组件容器中
v := yago.Component.Ins(name, func() interface{} {
conf := yago.Config.GetStringMap(name)
formatter := conf["formatter"].(string)
filePath := conf["file_path"].(string)
maxSize := int(conf["max_size"].(int64))
maxBackups := int(conf["max_backups"].(int64))
maxAge := int(conf["max_age"].(int64))
level := logrus.Level(conf["level"].(int64))
compress := conf["compress"].(bool)
val := &Logger{logrus.New()}
// 设置最低log level
val.SetLevel(level)
// 日志中显示记录的文件和函数名, 注意:textField 中需要避开 file 和 func 字段
val.SetReportCaller(true)
if formatter == "json" {
val.Formatter = &logrus.JSONFormatter{CallerPrettyfier: CallerPretty}
} else {
val.Formatter = &logrus.TextFormatter{CallerPrettyfier: CallerPretty}
}
val.Out = &lumberjack.Logger{
Filename: filePath,
MaxSize: maxSize,
MaxBackups: maxBackups,
MaxAge: maxAge,
Compress: compress,
}
return val
})
return v.(*Logger)
}
3. 使用组件
组件只有第一次调用 Ins() 时才会初始化组件,所以可以理解为每个组件都是一个单例。 还是以 logger 组件为例:
logger.Ins().Info("this is a test msg")
logger.Ins() 返回的是 yago 定义的 Logger,同时它又可以完整的使用 logrus.Logger 的方法。从上面定义 logger 的 Ins() 方法可以看出,Ins() 是可以传参的,参数就是全局组件容器里面的 key, 这在项目需要多个同类型组件时是十分有用的,比如使用多个数据库连接,我们可以通过调用不同的 key 获取不同的数据库连接。
4. 组件关闭
如果组件实现了Closer接口,那么com会在程序收到关闭信号的最后,去close所有已经注册进来的并且已经实现close接口的这些组件。
5. 组件组成
yago 的组件分两部分,一部分十分常用的组件我们放在 yago/coms
下,
还有一部分我们认为并不是每个项目都需要,单独开源了一个项目,https://github.com/hulklab/yago-coms
,里面的每个组件可以单独下载。