共计 17355 个字符,预计需要花费 44 分钟才能阅读完成。
其实这个我的项目在 4 年前就曾经开始了,因为所有的性能都是基于日常工作中的需要来的,断断续续的补充和欠缺性能,之前都是在本人公司这边的各种 Go 我的项目和我开源的一些我的项目中应用。很早之前就想把它开源进去,然而始终懒得写文档(感觉写文档是最难得事儿了),所以始终让它静静地躺 Github。明天终于补充了个简版的文档,是时候把它拿进去了😀。
感兴趣的敌人们欢送来看看啊,有砖拍砖,有需要提需要,肯定虚心向大家学习!
Glacier 是一款反对依赖注入的模块化的利用开发框架,它以 go-ioc 依赖注入容器外围,为 Go 利用开发解决了依赖传递和模块化的问题。
- 个性
- 应用
- 执行流程
-
外围概念
-
依赖注入
- Binder
- Resolver
-
Provider
- ProviderBoot
- DaemonProvider
- ProviderAggregate
- Service
- ModuleLoadPolicy
- Priority
-
-
Web 框架
- Usage
- 控制器
-
事件治理
- 本地内存作为事件存储后端
- Redis 作为事件存储后端
- 定时工作
- 日志
- Eloquent ORM
- 平滑退出
- 第三方框架集成
- 示例我的项目
个性
- 依赖注入 :通过依赖注入的形式来治理对象的依赖,反对单例、原型对象创立
- 模块化 :通过 Provider 个性,轻松实现利用的模块化
- 内置 Web 开发反对 :Glacier 内置了对 Web 利用开发的反对,提供了功能丰富的 API 简化 web 开发
应用
创立一个新的我的项目,应用上面的命令装置 Glacier 开发框架
go get github.com/mylxsw/glacier
为了简化利用的创立过程,咱们个别能够通过 starter 模板来创立利用
import "github.com/mylxsw/glacier/starter/app"
...
// 办法一:快捷启动利用
app.MustStart("1.0", 3, func(app *app.App) error {
// 这里实现利用的初始化
// ...
return nil
})
// 办法二:分步骤启动利用
ins := app.Create("1.0", 3)
// 利用初始化
// ...
app.MustRun(ins)
示例:
app.MustStart("1.0", 3, func(ins *app.App) error {ins.AddStringFlag("listen", ":8080", "http listen address")
ins.Provider(web.Provider(listener.FlagContext("listen"),
web.SetRouteHandlerOption(func(cc infra.Resolver, router web.Router, mw web.RequestMiddleware) {router.Get("/", func(ctx web.Context) web.Response {return ctx.JSON(web.M{})
})
}),
))
return nil
})
代码示例能够参考以后我的项目的 example 目录。
执行流程
外围概念
依赖注入
Glacier 框架充分利用了 go-ioc 提供的依赖注入能力,为利用提供了功能强大的依赖注入个性。
在应用依赖注入个性时,首先要了解以下两个接口的作用
infra.Binder
该接口用于对象创立实例办法的绑定,简略说就是向go-ioc
容器注册对象的创立办法infra.Resolver
该接口用于对象的实例化,获取对象实例
无论是 Binder
还是 Resolver
,都会有一个 interface{}
类型的参数,它的类型为合乎肯定规定的函数,前面在 Binder
和 Resolver
局部将会具体阐明。
Binder
infra.Binder
是一个对象定义接口,用于将实例的创立办法绑定到依赖注入容器,提供了以下罕用办法
Prototype(initialize interface{}) error
原型绑定,每次拜访绑定的实例都会基于initialize
函数从新创立新的实例Singleton(initialize interface{}) error
单例绑定,每次拜访绑定的实例都是同一个,只会在第一次拜访的时候创立初始实例BindValue(key string, value interface{}) error
将一个具体的值绑定到key
Prototype
和 Singleton
办法参数 initialize interface{}
反对以下两种模式
-
模式 1:
func(依赖参数列表...) (绑定类型定义, error)
// 这里应用单例办法定义了数据库连贯对象的创立办法 binder.Singleton(func(conf *Config) (*sql.DB, error) {return sql.Open("mysql", conf.MySQLURI) }) binder.Singleton(func(c infra.FlagContext) *Config { ... return &Config{Listen: c.String("listen"), MySQLURI: c.String("mysql_uri"), APIToken: c.String("api_token"), ... } })
-
模式 2:
func(注入参数列表...) 绑定类型定义
binder.Singleton(func() UserRepo {return &userRepoImpl{} }) binder.Singleton(func(db *sql.DB) UserRepo { // 这里咱们创立的 userRepoImpl 对象,依赖 sql.DB 对象,只须要在函数 // 参数中,将依赖列举进去,容器会主动实现这些对象的创立 return &userRepoImpl{db: db} })
Resolver
infra.Resolver
是对象实例化接口,通过依赖注入的形式获取实例,提供了以下罕用办法
Resolve(callback interface{}) error
执行 callback 函数,主动为 callback 函数提供所需参数Call(callback interface{}) ([]interface{}, error)
执行 callback 函数,主动为 callback 函数提供所需参数,反对返回值,返回参数为Call
的第一个数组参数-
AutoWire(object interface{}) error
主动对构造体对象进行依赖注入,object 必须是构造体对象的指针。主动注入字段(公开和公有均反对)须要增加autowire
tag,反对以下两种- autowire:”@” 依据字段的类型来注入
- autowire:” 自定义 key” 依据自定义的 key 来注入(查找名为 key 的绑定)
Get(key interface{}) (interface{}, error)
间接通过 key 来查找对应的对象实例
// Resolve
resolver.Resolve(func(db *sql.DB) {...})
err := resolver.Resolve(func(db *sql.DB) error {...})
// Call
resolver.Call(func(userRepo UserRepo) {...})
// Call 带有返回值
// 这里的 err 是依赖注入过程中的谬误,比方依赖对象创立失败
// results 是一个类型为 []interface{} 的数组,数组中按秩序蕴含了 callback 函数的返回值,以上面的代码为例,其中
// results[0] - string
// results[1] - error
results, err := resolver.Call(func(userRepo UserRepo) (string, error) {...})
// 因为每个返回值都是 interface{} 类型,因而在应用时须要执行类型断言,将其转换为具体的类型再应用
returnValue := results[0].(string)
returnErr := results[1].(error)
// AutoWire
// 假如咱们有一个 UserRepo,创立该构造体时须要数据库的连贯实例
type UserRepo struct {db *sql.DB `autowire:"@"`}
userRepo := UserRepo{}
resolver.AutoWire(&userRepo)
// 当初 userRepo 中的 db 参数曾经主动被设置为了数据库连贯对象,能够继续执行后续的操作了
Provider
在 Glacier 利用开发框架中,Provider 是利用模块化的外围,每个独立的功能模块通过 Provider 实现实例初始化,每个 Provider 都须要实现 infra.Provider
接口。在每个功能模块中,咱们通常会创立一个名为 provider.go 的文件,在该文件中创立一个 provider 实现
type Provider struct{}
func (Provider) Register(binder infra.Binder) {... // 这里能够应用 binder 向 IOC 容器注册以后模块中的实例创立办法}
Provider 接口只有一个必须实现的办法 Register(binder infra.Binder)
,该办法用于注册以后模块的对象到 IOC 容器中,实现依赖注入的反对。
例如,咱们实现一个基于数据库的用户治理模块 repo
,该模块蕴含两个办法
package repo
type UserRepo struct {db *sql.DB}
func (repo *UserRepo) Login(username, password string) (*User, error) {...}
func (repo *UserRepo) GetUser(username string) (*User, error) {...}
为了使该模块可能失常工作,咱们须要在创立 UserRepo
时,提供 db
参数,在 Glacier 中,咱们能够这样实现
package repo
type Provider struct {}
func (Provider) Register(binder infra.Binder) {binder.Singleton(func(db *sql.DB) *UserRepo {return &UserRepo {db: db} })
}
在咱们的利用创立时,应用 ins.Provider
办法注册该模块
ins := app.Default("1.0")
...
ins.MustSingleton(func() (*sql.DB, error) {return sql.Open("mysql", "user:pwd@tcp(ip:3306)/dbname")
})
// 在这里加载模块的 Provider
ins.Provider(repo.Provider{})
...
app.MustRun(ins)
ProviderBoot
在咱们应用 Provider 时,默认只须要实现一个接口办法 Register(binder infra.Binder)
即可,该办法用于将模块的实例创立办法注册到 Glacier 框架的 IOC 容器中。
在 Glaicer 中,还提供了一个 ProviderBoot
接口,该接口蕴含一个 Boot(resolver Resolver)
办法,实现该办法的模块,能够在 Glacier 框架启动过程中执行一些模块自有的业务逻辑,该办法在所有的模块全副加载结束后执行(所有的模块的 Register
办法都曾经执行结束),因而,零碎中所有的对象都是可用的。
Boot(resolver Resolver)
办法中适宜执行一些在利用启动过程中所必须实现的一次性工作,工作应该尽快实现,以防止影响利用的启动。
type Provider struct{}
func (Provider) Register(binder infra.Binder) {binder.MustSingleton(func(conf *configs.Config) *grpc.Server {return ...})
}
func (Provider) Boot(resolver infra.Resolver) {resolver.MustResolve(func(serv *grpc.Server) {protocol.RegisterMessageServer(serv, NewEventService())
protocol.RegisterHeartbeatServer(serv, NewHeartbeatService())
})
}
DaemonProvider
模块 Provider 的 Boot
办法是阻塞执行的,通常用于执行一些在利用启动时须要执行的一些初始化工作,在一个利用中,所有的 Provider 的 Boot
办法是串行执行的。
而 DaemonProvider
接口则为模块提供了异步执行的能力,模块的 Daemon(ctx context.Context, resolver infra.Resolver)
办法是异步执行的,咱们能够在这里执行创立 web 服务器等操作。
func (Provider) Daemon(_ context.Context, app infra.Resolver) {
app.MustResolve(func(serv *grpc.Server, conf *configs.Config, gf graceful.Graceful,) {listener, err := net.Listen("tcp", conf.GRPCListen)
...
gf.AddShutdownHandler(serv.GracefulStop)
...
if err := serv.Serve(listener); err != nil {log.Errorf("GRPC Server has been stopped: %v", err)
}
})
}
ProviderAggregate
ProviderAggregate 接口为利用提供了一种可能聚合其它模块 Provider 的能力,在 Aggregate() []Provider
办法中,咱们能够定义多个咱们以后模块所依赖的其它模块,在 Glacier 框架启动过程中,会优先加载这里定义的依赖模块,而后再加载咱们的以后模块。
咱们能够通过 ProviderAggregate
来创立咱们本人的模块,Aggregates() []infra.Provider
办法中返回依赖的子模块,框架会先初始化子模块,而后再初始化以后模块。
// 创立自定义模块,初始化了 Glacier 框架内置的 Web 框架
type Provider struct{}
func (Provider) Aggregates() []infra.Provider {return []infra.Provider{
// 加载了 web 模块,为利用提供 web 开发反对
web.Provider(listener.FlagContext("listen"), // 从命令行参数 listen 获取监听端口
web.SetRouteHandlerOption(s.routes), // 设置路由规定
web.SetExceptionHandlerOption(func(ctx web.Context, err interface{}) web.Response {log.Errorf("error: %v, call stack: %s", err, debug.Stack())
return nil
}), // Web 异样解决
),
}
}
func (Provider) routes(cc infra.Resolver, router web.Router, mw web.RequestMiddleware) {
router.Controllers(
"/api",
// 这里增加控制器
controller.NewWelcomeController(cc),
controller.NewUserController(cc),
)
}
func (Provider) Register(app infra.Binder) {}
Service
在 Glacier 框架中,Service 代表了一个后盾模块,Service 会在框架生命周期中继续运行。要实现一个 Service,须要实现 infra.Service
接口,该接口只蕴含一个办法
Start() error
用于启动 Service
除了 Start
办法之外,还反对以下管制办法,不过它们都是可选的
Init(resolver Resolver) error
用于 Service 的初始化,注入依赖等Stop()
触发 Service 的进行运行Reload()
触发 Service 的从新加载
以下为一个示例
type DemoService struct {
resolver infra.Resolver
stopped chan interface{}}
// Init 可选办法,用于在 Service 启动之前初始化一些参数
func (s *DemoService) Init(resolver infra.Resolver) error {
s.resolver = resolver
s.stopped = make(chan interface{})
return nil
}
// Start 用于 Service 的启动
func (s *DemoService) Start() error {
for {
select {
case <-s.stopped:
return nil
default:
... // 业务代码
}
}
}
// Stop 和 Reload 都是可选办法
func (s *DemoService) Stop() { s.stopped <- struct{}{}}
func (s *DemoService) Reload() { ...}
在咱们的利用创立时,应用 app.Service
办法注册 Service
ins := app.Create("1.0")
...
ins.Service(&service.DemoService{})
...
app.MustRun(ins)
ModuleLoadPolicy
Provider 和 Service 反对按需加载,要应用此性能,只须要让 Provider 和 Service 实现 ShouldLoad(…) bool 办法。ShouldLoad
办法用于管制 Provider 和 Service 是否加载,反对以下几种模式
func (Provider) ShouldLoad(... 依赖) bool
func (Provider) ShouldLoad(... 依赖) (bool, error)
示例
type Provider struct{}
func (Provider) Register(binder infra.Binder) {...}
// 只有当 config.AuthType == ldap 的时候才会加载以后 Provider
func (Provider) ShouldLoad(config *config.Config) bool {return str.InIgnoreCase(config.AuthType, []string{"ldap"})
}
留神:
ShouldLoad
办法在执行时,Provider
并没有实现Register
办法的执行,因而,在ShouldLoad
办法的参数列表中,只能应用在利用创立时全局注入的对象实例。ins := app.Create("1.0") ... ins.Singleton(func(c infra.FlagContext) *config.Config {return ...}) ... app.MustRun(ins)
Priority
实现 infra.Priority
接口的 Provider 、Service ,会依照 Priority()
办法的返回值大小顺次加载,值越大,加载程序越靠后,默认的优先级为 1000
。
type Provider struct {}
func (Provider) Register(binder infra.Binder) {...}
func (Provider) Priority() int {return 10}
Web 框架
Glacier 是一个利用框架,为了不便 Web 开发,也内置了一个灵便的 Web 利用开发框架。
Usage
Glaicer Web 在 Glacier 框架中是一个内置的 DaemonProvider,与其它的模块并无不同。咱们通过 web.Provider(builder infra.ListenerBuilder, options ...Option) infra.DaemonProvider
办法创立 Web 模块。
参数 builder
用于创立 Web 服务的 listener(用于告知 Web 框架如何监听端口),在 Glaicer 中,有以下几种形式来创立 listener:
listener.Default(listenAddr string) infra.ListenerBuilder
该构建器应用固定的 listenAddr 来创立 listenerlistener.FlagContext(flagName string) infra.ListenerBuilder
该构建器依据命令行选项 flagName 来获取要监听的地址,以此来创立 listenerlistener.Exist(listener net.Listener) infra.ListenerBuilder
该构建器应用应存在的 listener 来创立
参数 options
用于配置 web 服务的行为,蕴含以下几种罕用的配置
web.SetRouteHandlerOption(h RouteHandler) Option
设置路由注册函数,在该函数中注册 API 路由规定web.SetExceptionHandlerOption(h ExceptionHandler) Option
设置申请异样处理器web.SetIgnoreLastSlashOption(ignore bool) Option
设置路由规定疏忽最初的/
,默认是不疏忽的web.SetMuxRouteHandlerOption(h MuxRouteHandler) Option
设置底层的 gorilla Mux 对象,用于对底层的 Gorilla 框架进行间接管制web.SetHttpWriteTimeoutOption(t time.Duration) Option
设置 HTTP 写超时工夫web.SetHttpReadTimeoutOption(t time.Duration) Option
设置 HTTP 读超时工夫web.SetHttpIdleTimeoutOption(t time.Duration) Option
设置 HTTP 闲暇超时工夫web.SetMultipartFormMaxMemoryOption(max int64)
设置表单解析可能应用的最大内存web.SetTempFileOption(tempDir, tempFilePattern string) Option
设置临时文件存储规定web.SetInitHandlerOption(h InitHandler) Option
初始化阶段,web 利用对象还没有创立,在这里能够更新 web 配置web.SetListenerHandlerOption(h ListenerHandler) Option
服务初始化阶段,web 服务对象曾经创立,此时不能再更新 web 配置了
最简略的应用 Web 模块的形式是间接创立 Provider,
// Password 该构造体时 /complex 接口的返回值定义
type Password struct {Password string `json:"password"`}
// Glacier 框架初始化
ins := app.Default("1.0")
...
// 增加命令行参数 listen,指定默认监听端口 :8080
ins.AddStringFlag("listen", ":8080", "http listen address")
...
ins.Provider(web.Provider(
// 应用命令行 flag 的 listener builder
listener.FlagContext("listen"),
// 设置路由规定
web.SetRouteHandlerOption(func(resolver infra.Resolver, r web.Router, mw web.RequestMiddleware) {
...
r.Get("/simple", func(ctx web.Context, gen *password.Generator) web.Response {
...
return ctx.JSON(web.M{"password": pass})
})
r.Get("/complex", func(ctx web.Context, gen *password.Generator) Password {...})
}),
))
app.MustRun(ins)
更好的形式是应用模块化,编写一个独立的 Provider
type Provider struct{}
// Aggregates 实现 infra.ProviderAggregate 接口
func (Provider) Aggregates() []infra.Provider {return []infra.Provider{
web.Provider(confListenerBuilder{},
web.SetRouteHandlerOption(routes),
web.SetMuxRouteHandlerOption(muxRoutes),
web.SetExceptionHandlerOption(exceptionHandler),
),
}
}
// Register 实现 infra.Provider 接口
func (Provider) Register(binder infra.Binder) {}
// exceptionHandler 异样处理器
func exceptionHandler(ctx web.Context, err interface{}) web.Response {return ctx.JSONWithCode(web.M{"error": fmt.Sprintf("%v", err)}, http.StatusInternalServerError)
}
// routes 注册路由规定
func routes(resolver infra.Resolver, router web.Router, mw web.RequestMiddleware) {mws := make([]web.HandlerDecorator, 0)
// 增加 web 中间件
mws = append(mws,
mw.AccessLog(log.Module("api")),
mw.CORS("*"),
)
// 注册控制器,所有的控制器 API 都以 `/api` 作为接口前缀
router.WithMiddleware(mws...).Controllers(
"/api",
controller.NewServerController(resolver),
controller.NewClientController(resolver),
)
}
func muxRoutes(resolver infra.Resolver, router *mux.Router) {resolver.MustResolve(func() {
// 增加 prometheus metrics 反对
router.PathPrefix("/metrics").Handler(promhttp.Handler())
// 增加健康检查接口反对
router.PathPrefix("/health").Handler(HealthCheck{})
})
}
// 创立自定义的 listener 构建器,从配置对象中读取 listen 地址
type confListenerBuilder struct{}
func (l confListenerBuilder) Build(resolver infra.Resolver) (net.Listener, error) {return listener.Default(resolver.MustGet((*config.Server)(nil)).(*config.Server).HTTPListen).Build(resolver)
}
控制器
控制器必须实现 web.Controller
接口,该接口只有一个办法
Register(router Router)
用于注册以后控制器的路由规定
type UserController struct {...}
// NewUserController 控制器创立办法,返回 web.Controller 接口
func NewUserController() web.Controller { return &UserController{...} }
// Register 注册以后控制器关联的路由规定
func (ctl UserController) Register(router web.Router) {router.Group("/users/", func(router web.Router) {router.Get("/", u.Users).Name("users:all")
router.Post("/", u.Add)
router.Post("/{id}/", u.Update)
router.Get("/{id}/", u.User).Name("users:one")
router.Delete("/{id}/", u.Delete).Name("users:delete")
})
router.Group("/users-helper/", func(router web.Router) {router.Get("/names/", u.UserNames)
})
}
// 读取 JSON 申请参数,间接返回实例,会以 json 的模式返回给客户端
func (ctl UserController) Add(ctx web.Context, userRepo repository.UserRepo) (*repository.User, error) {
var userForm *UserForm
if err := ctx.Unmarshal(&userForm); err != nil {return nil, web.WrapJSONError(fmt.Errorf("invalid request: %v", err), http.StatusUnprocessableEntity)
}
ctx.Validate(userForm, true)
...
return ...
}
// 间接返回谬误,如果 error 不为空,则返回谬误给客户端
func (ctl UserController) Delete(ctx web.Context, userRepo repository.UserRepo) error {userID := ctx.PathVar("id")
...
return userRepo.DeleteID(userID)
}
// 返回 web.Response,能够应用多种格局返回,如 ctx.Nil, ctx.API, ctx.JSON, ctx.JSONWithCode, ctx.JSONError, ctx.YAML, ctx.Raw, ctx.HTML, ctx.HTMLWithCode, ctx.Error 等
func (u UserController) Users(ctx web.Context, userRepo repository.UserRepo, roleRepo repository.RoleRepo) web.Response {page := ctx.IntInput("page", 1)
perPage := ctx.IntInput("per_page", 10)
...
return ctx.JSON(web.M{
"users": users,
"next": next,
"search": web.M{
"name": name,
"phone": phone,
"email": email,
},
})
}
应用 web.Router
实例的 Controllers
办法注册控制器。
// routes 注册路由规定
func routes(resolver infra.Resolver, router web.Router, mw web.RequestMiddleware) {mws := make([]web.HandlerDecorator, 0)
// 增加 web 中间件
mws = append(mws,
mw.AccessLog(log.Module("api")),
mw.CORS("*"),
)
// 注册控制器,所有的控制器 API 都以 `/api` 作为接口前缀
router.WithMiddleware(mws...).Controllers(
"/api",
controller.NewUserController(),)
}
事件治理
Glacier 框架提供了一个简略的事件治理模块,能够用于公布和监听利用运行中的事件,进行响应的业务解决。
通过 event.Provider(handler func(resolver infra.Resolver, listener Listener), options ...Option) infra.Provider
来初始化事件管理器。
ins.Provider(event.Provider(func(cc infra.Resolver, listener event.Listener) {listener.Listen(func(event CronEvent) {log.Debug("a new cron task executed")
// 执行监听到定时工作执行事件后要触发的操作
})
},
// 设置事件管理器选项
event.SetStoreOption(func(cc infra.Resolver) event.Store {
// 设置应用默认的内存事件存储
return event.NewMemoryEventStore(true, 100)
}),
))
公布事件时,应用 Glacier 框架的依赖注入能力,获取 event.Publisher
接口实现
ins.Async(func(publisher event.Publisher) {
for i := 0; i < 10; i++ {publisher.Publish(CronEvent{GoroutineID: uint64(i)})
}
})
本地内存作为事件存储后端
Glacier 内置了基于内存的事件存储后端,说有事件的监听器都是同步执行的。
// 设置事件管理器选项
event.SetStoreOption(func(cc infra.Resolver) event.Store {
// 设置应用默认的内存事件存储
return event.NewMemoryEventStore(true, 100)
})
Redis 作为事件存储后端
应用内存作为事件存储后端时,当利用异样退出的时候,可能会存在事件的失落,你能够应用这个基于 Redis 的事件存储后端 redis-event-store 来取得事件的长久化反对。
定时工作
Glacier 提供了内置的定时工作反对,应用 scheduler.Provider
来实现。
type Provider struct{}
func (Provider) Register(binder infra.Binder) {...}
func (Provider) Aggregates() []infra.Provider {return []infra.Provider{
// 加载 scheduler 定时工作模块
scheduler.Provider(func(resolver infra.Resolver, creator scheduler.JobCreator) {
// 增加一个名为 test-job 的工作,每隔 10s 执行一次
_ = cr.Add("test-job", "@every 10s", TestJob)
// 增加一个名称为 test-timeout-job 的工作,每隔 5s 执行一次
// 通过 AddAndRunOnServerReady 增加的工作会在服务启动时先执行一次
_ = creator.AddAndRunOnServerReady(
"test-timeout-job",
"@every 5s",
// 应用 scheduler.WithoutOverlap 包装的函数,以后一次调度还没有执行结束,本次调度的工夫已到,本次调度将会被勾销
scheduler.WithoutOverlap(TestTimeoutJob).SkipCallback(func() {... // 以后一个工作还没有执行结束时,当前任务会被跳过,跳过期会触发该函数的执行}),
)
},
),
}
}
scheduler.Provider
反对分布式锁,通过 SetLockManagerOption
选项能够指定分布式锁的实现,以满足工作在一组服务器中只会被触发一次的逻辑。
scheduler.Provider(func(resolver infra.Resolver, creator scheduler.JobCreator) {...},
// 设置分布式锁
scheduler.SetLockManagerOption(func(resolver infra.Resolver) scheduler.LockManagerBuilder {
// get redis instance
redisClient := resolver.MustGet(&redis.Client{}).(*redis.Client)
return func(name string) scheduler.LockManager {
// create redis lock
return redisLock.New(redisClient, name, 10*time.Minute)
}
}),
)
留神:Glacier 框架没有内置分布式锁的实现,在 mylxsw/distribute-locks 实现了一个简略的基于 Redis 的分布式锁实现,能够参考应用。
日志
在 Glacier 中,默认应用 asteria 作为日志框架,asteria 是一款功能强大、灵便的结构化日志框架,反对多种日志输入格局以及输入形式,反对为日志信息增加上下文信息。
最简略的形式是通过 log.SetDefaultLogger(logger infra.Logger)
办法为 Glacier 框架设置默认的日志处理器,
// import "github.com/mylxsw/glacier/log"
// 默认设置,应用 asteria 日志框架
// import asteria "github.com/mylxsw/asteria/log"
log.SetDefaultLogger(asteria.Module("glacier"))
// 应用规范库中的日志包,Glacier 对规范库日志包进行了简略封装
log.SetDefaultLogger(log.StdLogger())
当然,如果应用了 starter 模板我的项目创立的利用,也能够应用 WithLogger(logger infra.Logger)
办法来设置日志处理器。
ins := app.Default("1.0")
...
// 设置应用规范库日志包,不输入 DEBUG 日志
ins.WithLogger(log.StdLogger(log.DEBUG))
...
除了默认的 asteria
日志库以及 Glacier 自带的 StdLogger
之外,还能够应用其它第三方的日志包,只须要简略的封装,实现 infra.Logger
接口即可。
type Logger interface {Debug(v ...interface{})
Debugf(format string, v ...interface{})
Info(v ...interface{})
Infof(format string, v ...interface{})
Error(v ...interface{})
Errorf(format string, v ...interface{})
Warning(v ...interface{})
Warningf(format string, v ...interface{})
// Critical 关键性谬误,遇到该日志输入时,利用间接退出
Critical(v ...interface{})
// Criticalf 关键性谬误,遇到该日志输入时,利用间接退出
Criticalf(format string, v ...interface{})
}
Eloquent ORM
Eloquent ORM 是为 Go 开发的一款数据库 ORM 框架,它的设计灵感来源于驰名的 PHP 开发框架 Laravel,反对 MySQL 等数据库。
我的项目地址为 mylxsw/eloquent,能够配合 Glacier 框架应用。
平滑退出
Glacier 反对平滑退出,当咱们按下键盘的 Ctrl+C
时(接管到 SIGINT,SIGTERM, Interrupt 等信号),Glacier 将会接管到敞开的信号,而后触发利用的敞开行为。默认状况下,咱们的利用会立刻退出,咱们能够通过 starter 模板创立的利用上启用平滑反对选项 WithShutdownTimeoutFlagSupport(timeout time.Duration)
来设置默认的平滑退出工夫
ins := app.Create("1.0")
ins.WithShutdownTimeoutFlagSupport(5 * time.Second)
...
// Provider 中获取 `gf.Graceful` 实例,注册敞开时的处理函数
resolver.MustResolve(func(gf graceful.Graceful) {gf.AddShutdownHandler(func() {...})
})
第三方框架集成
- giris: Iris Web Framework 适配
示例我的项目
- Example 应用示例
- WebDAV Server 一款反对 LDAP 作为用户数据库的 WebDAV 服务器
- Adanos Alert 一个功能强大的开源告警平台,通过事件聚合机制,为监控零碎提供钉钉、邮件、HTTP、JIRA、语音电话等告警形式的反对
- Healthcheck 为应用服务提供健康检查告警反对
- Sync 跨服务器文件同步服务
- Tech Share 一个用于中小型团队外部技术分享治理的 Web 利用
- Universal Exporter 一个通用的 Prometheus 维度工具,目前反对从数据库中查问生成 Metric 数据
- Graphviz Server 一个 Web 服务,封装了对 Graphviz 的接口调用,实现通过 Web API 的形式生成 Graphviz 图形
- MySQL Guard 用于 MySQL 长事务检测杀死和死锁告警
- Password Server 一个生成随机明码的简略 web 服务器