Glacier is a Go language, support dependency injection, modular application development framework, it uses go-ioc The dependency injection container core solves the problems of dependency transfer and modularization for Go application development.

characteristic

  • dependency injection: Manage object dependencies through dependency injection, support creation of singleton and prototype objects
  • Modular: Through the Provider feature, it is easy to realize the modularization of the application
  • Built-in web development support: Glacier has built-in support for web application development and provides a feature-rich API to simplify web development

use

Create a new project and install the Glacier development framework using the following command


go get github.com/mylxsw/glacier

In order to simplify the application creation process, we can generally create applications through starter templates


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)

Example:


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
})

Core idea

dependency injection

The Glacier framework takes full advantage of the go-ioc The provided dependency injection capability provides powerful dependency injection features for applications.

When using the dependency injection feature, you must first understand the role of the following two interfaces

  • infra.Binder This interface is used for the binding of object creation instance method, simply put to go-ioc How to create a container registration object
  • infra.Resolver This interface is used to instantiate objects and get object instances

Whether it is Binder still Resolverthere will be a interface{} Type parameter, its type is a function that conforms to certain rules, followed by Binder and Resolver section will be explained in detail.

Binder

infra.Binder It is an object definition interface, which is used to bind the creation method of the instance to the dependency injection container, and provides the following common methods

  • Prototype(initialize interface{}) error Prototype binding, each access to the bound instance will be based on initialize function to create a new instance
  • Singleton(initialize interface{}) error Singleton binding, the bound instance is the same every time you access it, and only the initial instance will be created when you access it for the first time
  • BindValue(key string, value interface{}) error Bind a concrete value to key

Prototype and Singleton method parameters initialize interface{} The following two forms are supported

  • Form 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"),
      		...		
      	}
      })

  • Form 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 It is an object instantiation interface, which obtains instances through dependency injection, and provides the following common methods

  • Resolve(callback interface{}) error Execute the callback function and automatically provide the required parameters for the callback function
  • Call(callback interface{}) ([]interface{}, error) Execute the callback function, automatically provide the required parameters for the callback function, support the return value, and the return parameter is Call The first array parameter of
  • AutoWire(object interface{}) error Automatically perform dependency injection on the structure object, and object must be a pointer to the structure object.Auto-injected fields (both public and private are supported) need to be added autowire tag, supports the following two

    • autowire: “@” is injected according to the type of the field
    • autowire: “custom key” is injected according to a custom key (look for a binding named key)

  • Get(key interface{}) (interface{}, error) Find the corresponding object instance directly through the 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

In the Glacier application development framework, Provider is the core of application modularization. Each independent functional module completes instance initialization through Provider, and each Provider needs to implement infra.Provider interface.In each functional module, we usually create a file called provider.go, and create a provider implementation in this file


type Provider struct{}

func (Provider) Register(binder infra.Binder) {
	... // 这里可以使用 binder 向 IOC 容器注册当前模块中的实例创建方法
}

Provider An interface has only one method that must be implemented Register(binder infra.Binder)this method is used to register the object of the current module into the IOC container to support dependency injection.

For example, we implement a database-based user management module repothe module contains two methods


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) {...}

In order for this module to work properly, we need to create the UserRepo when, provide db parameter, in Glacier, we can implement it like this


package repo

type Provider struct {}

func (Provider) Register(binder infra.Binder) {
  binder.Singleton(func(db *sql.DB) *UserRepo { return &UserRepo {db: db} })
}

When our application is created, use ins.Provider method to register the module


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

When we use Provider, only one interface method needs to be implemented by default Register(binder infra.Binder) That is, this method is used to register the instance creation method of the module in the IOC container of the Glacier framework.

In Glaicer, there is also a ProviderBoot interface, which contains a Boot(resolver Resolver) method, the module that implements this method can execute some business logic of the module during the startup process of the Glacier framework. This method is executed after all modules are loaded (all modules’ Register methods have been executed), therefore, all objects in the system are available.

Boot(resolver Resolver) The method is suitable for performing some one-time tasks that must be completed during the application startup process, and the tasks should be completed as soon as possible to avoid affecting the application startup.


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

Module Provider’s Boot The method is blocking execution and is usually used to perform some initialization tasks that need to be performed when the application starts. In an application, all Provider’s Boot Methods are executed serially.

and DaemonProvider The interface provides the module with the ability to execute asynchronously, and the module’s Daemon(ctx context.Context, resolver infra.Resolver) The method is executed asynchronously, where we can perform operations such as creating a web server.


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

The ProviderAggregate interface provides an application with the ability to aggregate Providers from other modules. Aggregate() []ProviderIn the method, we can define multiple other modules that our current module depends on. During the startup process of the Glacier framework, the dependent modules defined here will be loaded first, and then our current module will be loaded.

we can pass ProviderAggregate to create our own module, Aggregates() []infra.Provider The dependent submodule is returned in the method, and the framework will initialize the submodule first, and then initialize the current module.


// 创建自定义模块,初始化了 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

In the Glacier framework, Service represents a background module, and Service will continue to run in the framework life cycle.To implement a Service, you need to implement infra.Service interface, which contains only one method

  • Start() error Used to start the Service

Apart from Start method, the following control methods are also supported, but they are all optional

  • Init(resolver Resolver) error Used for Service initialization, injecting dependencies, etc.
  • Stop() Trigger the shutdown of the Service
  • Reload() Trigger a reload of the Service

The following is an example


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() { ... }

When our application is created, use app.Service Method to register Service


ins := app.Create("1.0")
...
ins.Service(&service.DemoService{})
...
app.MustRun(ins)

ModuleLoadPolicy

Provider and service Supports on-demand loading, to use this feature, just let the Provider and service accomplish ShouldLoad(…) bool method.ShouldLoad method for controlling Provider and service Whether to load, support the following forms

  • func (Provider) ShouldLoad(...依赖) bool
  • func (Provider) ShouldLoad(...依赖) (bool, error)

example


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"})
}

Notice:ShouldLoad When the method is executed,Provider not finished Register method execution, therefore, in ShouldLoad In the parameter list of the method, only the object instance that is injected globally when the application is created can be used.



ins := app.Create("1.0")
...
ins.Singleton(func(c infra.FlagContext) *config.Config { return ... })
...
app.MustRun(ins)

priority

accomplish infra.Priority The **Provider ** and **Service ** of the interface will follow the Priority() The return value of the method is loaded sequentially. The larger the value, the lower the loading order. The default priority is 1000.


type Provider struct {}
func (Provider) Register(binder infra.Binder) {...}

func (Provider) Priority() int {
	return 10
}

web framework

Glacier is an application framework. In order to facilitate Web development, it also has a built-in flexible Web application development framework.

Usage

Glaicer Web is a built in Glacier framework DaemonProvider, no different from other modules.we pass web.Provider(builder infra.ListenerBuilder, options ...Option) infra.DaemonProvider method to create a web module.

parameter builder Used to create a listener for web services (used to tell the web framework how to listen to the port). In Glaicer, there are several ways to create a listener:

  • listener.Default(listenAddr string) infra.ListenerBuilder The builder uses a fixed listenAddr to create the listener
  • listener.FlagContext(flagName string) infra.ListenerBuilder The builder obtains the address to listen to according to the command line option flagName to create a listener
  • listener.Exist(listener net.Listener) infra.ListenerBuilder This builder uses the listener that should exist to create

parameter options Used to configure the behavior of web services, including the following common configurations

  • web.SetRouteHandlerOption(h RouteHandler) Option Set the route registration function, register the API routing rules in this function
  • web.SetExceptionHandlerOption(h ExceptionHandler) Option Set request exception handler
  • web.SetIgnoreLastSlashOption(ignore bool) Option Set routing rules to ignore the last /is not ignored by default
  • web.SetMuxRouteHandlerOption(h MuxRouteHandler) Option Sets the underlying gorilla Mux object for direct control of the underlying Gorilla framework
  • web.SetHttpWriteTimeoutOption(t time.Duration) Option Set HTTP write timeout
  • web.SetHttpReadTimeoutOption(t time.Duration) Option Set HTTP read timeout
  • web.SetHttpIdleTimeoutOption(t time.Duration) Option Set HTTP idle timeout
  • web.SetMultipartFormMaxMemoryOption(max int64) Sets the maximum memory that form parsing can use
  • web.SetTempFileOption(tempDir, tempFilePattern string) Option Set temporary file storage rules
  • web.SetInitHandlerOption(h InitHandler) Option In the initialization phase, the web application object has not been created, and the web configuration can be updated here
  • web.SetListenerHandlerOption(h ListenerHandler) Option In the service initialization phase, the web service object has been created, and the web configuration cannot be updated at this time

The easiest way to use the Web module is to create a Provider directly,


// 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)

A better way is to use modularity and write an independent 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)
}

controller

The controller must implement web.Controller interface, which has only one method

  • Register(router Router) Routing rules for registering the current controller

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,
		},
	})
}

use web.Router example of Controllers method to register the controller.


// 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(),
	)
}

event management

The Glacier framework provides a simple event management module, which can be used to publish and monitor the events in the running of the application, and perform corresponding business processing.

pass event.Provider(handler func(resolver infra.Resolver, listener Listener), options ...Option) infra.Provider to initialize the event manager.


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)
  }),
))

When publishing events, use the dependency injection capability of the Glacier framework to obtain event.Publisher interface implementation


ins.Async(func(publisher event.Publisher) {
  for i := 0; i < 10; i++ {
    publisher.Publish(CronEvent{GoroutineID: uint64(i)})
  }
})

local memory as event storage backend

Glacier has a built-in memory-based event storage backend, which means that all event listeners are executed synchronously.


// 设置事件管理器选项
event.SetStoreOption(func(cc infra.Resolver) event.Store {
	// 设置使用默认的内存事件存储
	return event.NewMemoryEventStore(true, 100)
})

Redis as an event storage backend

When using memory as the event storage backend, when the application exits abnormally, events may be lost. You can use this Redis-based event storage backend redis-event-store To get persistence support for events.

timed task

Glacier provides built-in support for scheduled tasks, use scheduler.Provider to fulfill.


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 Support distributed locks, through SetLockManagerOption The option can specify the implementation of distributed locks to satisfy the logic that the task will only be triggered once in a group of servers.


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)
		}
	}),
)

Note: The Glacier framework does not have a built-in implementation of distributed locks, in mylxsw/distribute-locks Implemented a simple Redis-based distributed lock implementation, which can be used for reference.

log

In Glacier, the default is to use asteria As a log framework, asteria is a powerful and flexible structured log framework that supports multiple log output formats and output methods, and supports adding context information to log information.

The easiest way is through log.SetDefaultLogger(logger infra.Logger) method to set the default log handler for the Glacier framework,


// import "github.com/mylxsw/glacier/log"

// 默认设置,使用 asteria 日志框架
// import asteria "github.com/mylxsw/asteria/log"
log.SetDefaultLogger(asteria.Module("glacier"))
// 使用标准库中的日志包,Glacier 对标准库日志包进行了简单封装
log.SetDefaultLogger(log.StdLogger())

Of course, if you use the application created by the starter template project, you can also use WithLogger(logger infra.Logger) method to set the log handler.


ins := app.Default("1.0")
...
// 设置使用标准库日志包,不输出 DEBUG 日志
ins.WithLogger(log.StdLogger(log.DEBUG))
...

In addition to the default asteria The logging library and Glacier’s own StdLogger In addition, you can also use other third-party log packages, only need simple encapsulation, to achieve infra.Logger interface.


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 is a database ORM framework developed for Go. Its design is inspired by the famous PHP development framework Laravel and supports MySQL and other databases.

The project address is mylxsw/eloquent can be used with Glacier framework.

smooth exit

Glacier supports smooth exit, when we press the keyboard’s Ctrl+C When (receiving signals such as SIGINT, SIGTERM, Interrupt, etc.), Glacier will receive a shutdown signal, and then trigger the shutdown behavior of the application.By default, our application will exit immediately, we can enable the smooth support option on the application created by the starter template WithShutdownTimeoutFlagSupport(timeout time.Duration) to set the default smooth exit time


ins := app.Create("1.0")
ins.WithShutdownTimeoutFlagSupport(5 * time.Second)
...

// Provider 中获取 `gf.Graceful` 实例,注册关闭时的处理函数
resolver.MustResolve(func(gf graceful.Graceful) {
	gf.AddShutdownHandler(func() {
		...
	})
})

Third-party framework integration

sample project

  • Example Example of use
  • WebDAV Server A WebDAV server that supports LDAP as a user database
  • Adanos Alert A powerful open source alarm platform, through the event aggregation mechanism, provides support for alarm methods such as DingTalk, email, HTTP, JIRA, and voice calls for the monitoring system
  • Health Check Provide health check alarm support for application services
  • Sync Cross-server file synchronization service
  • Tech Share A web application for internal technology sharing management of small and medium teams
  • Universal Exporter A general-purpose Prometheus dimension tool that currently supports querying and generating Metric data from the database
  • Graphviz Server A web service that encapsulates the interface call to Graphviz and realizes the generation of Graphviz graphics through Web API
  • MySQL Guard Used for MySQL long transaction detection kill and deadlock alarm
  • Password Server A simple web server that generates random passwords

#Glacier #Framework #Homepage #Documentation #Downloads #Development #Framework #Supporting #Dependency #Injection #News Fast Delivery

Leave a Comment

Your email address will not be published. Required fields are marked *