关于go:Glacier-Framework-支持依赖注入的-Go-应用开发框架

8次阅读

共计 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{} 类型的参数,它的类型为合乎肯定规定的函数,前面在 BinderResolver 局部将会具体阐明。

Binder

infra.Binder 是一个对象定义接口,用于将实例的创立办法绑定到依赖注入容器,提供了以下罕用办法

  • Prototype(initialize interface{}) error 原型绑定,每次拜访绑定的实例都会基于 initialize 函数从新创立新的实例
  • Singleton(initialize interface{}) error 单例绑定,每次拜访绑定的实例都是同一个,只会在第一次拜访的时候创立初始实例
  • BindValue(key string, value interface{}) error 将一个具体的值绑定到 key

PrototypeSingleton 办法参数 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

ProviderService 反对按需加载,要应用此性能,只须要让 ProviderService 实现 ShouldLoad(…) bool 办法。ShouldLoad 办法用于管制 ProviderService 是否加载,反对以下几种模式

  • 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 来创立 listener
  • listener.FlagContext(flagName string) infra.ListenerBuilder 该构建器依据命令行选项 flagName 来获取要监听的地址,以此来创立 listener
  • listener.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 服务器
正文完
 0