最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

最新消息:Go语言实现包的初始化控制器与流程控制器

来源:博客园


(资料图片)

目录
  • 背景
  • 原理
  • 初始化控制
    • 结构体定义
    • 初始化控制
    • 使用
    • 测试
  • 流程控制
    • 自实现的WaitGroup功能
    • 应用
    • 测试
  • 思考

背景

在go的工程中,有时init的顺序是至关重要的,本文写了一个小控制器去管理init的顺序,可以根据自己的要求设置不同的权重来实现加载顺序。本文控制器主要实现两个功能,一是按照优先级加载包的引用,二是流程控制,主流程与异步流程存在顺序要求(实现类似sync.WaitGroup功能)

原理

优先级加载:设计一个优先级队列,将所有初始化函数(包括退出函数)注册到优先级队列中,在所有类注册完成之后统一按照优先级进行初始化。流程控制:通过原子计数器进行异步数据的控制。本片代码结合Ioc一起使用,有不理解可以查看 GO语言的控制反转 (IOC)在工程中应用

初始化控制

结构体定义

func DefaultApplicationLifeCycle() *ApplicationLifeCycle {return appInit}type InitProc func() errortype InitializeDoneProc func(err error)type ShutdownProc func(ctx context.Context) errortype initProcCtx struct {name     stringpriority intproc     InitProc}type shutdownProcCtx struct {name     stringpriority intproc     ShutdownProc}type ApplicationLifeCycle struct {allProcs          []initProcCtx     // 需要初始化执行列表allShutdown       []shutdownProcCtx // 需要销毁执行列表launched          bool              // 已加载标识shutdownRequested bool              // 已退出标识served            bool              // 异步函数加载标识once              sync.Once         waitCounter       int32}

初始化控制

/*根据优先级启动各对象的初始化函数*/func (alc *ApplicationLifeCycle) Launch() error {if alc.launched {return fmt.Errorf("already launched")}min := math.MaxInt32max := math.MinInt32for _, ctx := range alc.allProcs {if ctx.priority > max {max = ctx.priority}if ctx.priority < min {min = ctx.priority}}for i := min; i <= max; i++ {for _, ctx := range alc.allProcs {if ctx.priority == i {fmt.Println("calling initializer for ", ctx.name)err := ctx.proc()if err != nil {return fmt.Errorf("luanch application failed: %v", err)}}}}alc.launched = truereturn nil}/*根据优先级执行各对象的初始化函数*/func (alc *ApplicationLifeCycle) Shutdown(ctx context.Context) {alc.shutdownRequested = truealc.once.Do(func() {min := math.MaxInt32max := math.MinInt32for _, sctx := range alc.allShutdown {if sctx.priority > max {max = sctx.priority}if sctx.priority < min {min = sctx.priority}}for i := min; i <= max; i++ {for _, sctx := range alc.allShutdown {if sctx.priority == i {fmt.Println("calling finalizer for ", sctx.name)err := sctx.proc(ctx)if err != nil {fmt.Println("shutdown application failed: ", err)}}}}fmt.Println("application shutdown")})}/*注册初始化函数,本质是一个优先级队列,可以通过优先级控制函数执行顺序,而不是根据根据包的引用顺序。Launch函数执行*/func (alc *ApplicationLifeCycle) RegisterInitializer(name string, proc InitProc, priority int) error {if alc.launched {return fmt.Errorf("too late to register intializer, application already launched")}alc.allProcs = append(alc.allProcs, initProcCtx{name, priority, proc})return nil}/*注册退出清理函数,Shutdown执行*/func (alc *ApplicationLifeCycle) RegisterFinalizer(name string, proc ShutdownProc, priority int) error {if alc.shutdownRequested {return fmt.Errorf("too late to register finalizer")}alc.allShutdown = append(alc.allShutdown, shutdownProcCtx{name:     name,priority: priority,proc:     proc,})return nil}

使用

注册

// module.gofunc init() {app.DefaultApplicationLifeCycle().RegisterInitializer("module", func() error {mo := &ModuleObj{}// static assert 静态断言类型检测func(t app.Module) {}(mo)app.GetOrCreateRootContainer().RegisterTo(mo, (*app.Module)(nil), ioc.Singleton)app.GetOrCreateRootContainer().Invoke(func(r app.Resource) {rs = r})return nil}, 2)app.DefaultApplicationLifeCycle().RegisterFinalizer("module", func(ctx context.Context) error {fmt.Println("module exit")return nil}, 2)}//resource.gofunc init() {app.DefaultApplicationLifeCycle().RegisterInitializer("resource", func() error {mo := &ResourceObj{name: "mongo"}// static assert 静态断言类型检测func(t app.Resource) {}(mo)//rd := &ResourceObj{name: "redis"}app.GetOrCreateRootContainer().RegisterTo(mo, (*app.Resource)(nil), ioc.Singleton)//app.GetOrCreateRootContainer().RegisterTo(rd, (*app.Resource)(nil), ioc.Singleton)return nil}, 1)app.DefaultApplicationLifeCycle().RegisterFinalizer("resource", func(ctx context.Context) error {fmt.Println("resource exit")return nil}, 1)}//service.init() 略

使用

err := app.DefaultApplicationLifeCycle().Launch()if err != nil {fmt.Println("DefaultApplicationLifeCycle Launch err ", err)return}var s1 app.Service1app.GetOrCreateRootContainer().Invoke(func(service app.Service1) {s1 = service})s1.AddData("IOC Test")app.DefaultApplicationLifeCycle().Shutdown(context.Background())

测试

流程控制

自实现的WaitGroup功能

/*用于流程控制,所有异步的初始化完成之后,这里才会跳出循环,利用原子计数器实现的一个自旋锁*/func (alc *ApplicationLifeCycle) WaitUntilInitialized(ctx context.Context) {alc.served = true// alc.serveWaitGroup.Wait()for {select {case <-ctx.Done():returndefault:if alc.waitCounter == 0 {return}time.Sleep(time.Millisecond * 50) // sleep 50ms}}}/*用于流程控制 原子计数器+1,与-1的处理闭包函数*/func (alc *ApplicationLifeCycle) RegisterInitializeAwait(name string) (InitializeDoneProc, error) {if alc.served {return nil, fmt.Errorf("too late to register intialize await")}once := sync.Once{}atomic.AddInt32(&alc.waitCounter, 1)fmt.Println("register intialize await for ", name)return func(err error) {once.Do(func() {if err != nil {fmt.Println(fmt.Sprintf("initialize await `%s` report a failure: %v", name, err))} else {fmt.Println(fmt.Sprintf("`%s` initialized", name))}atomic.AddInt32(&alc.waitCounter, -1)})}, nil}

应用

Service2 的接口定义与实现

type Service2 interface {AddData(string)DelData(string)SyncData(t int)InitService(done InitializeDoneProc)}type Service2 struct {initDone app.InitializeDoneProc}func (s2 *Service2) InitService(done app.InitializeDoneProc) {s2.initDone = done}func (s2 *Service2) AddData(str string) {fmt.Println("Service2 AddData ", str)module.DataToSave(str)}func (s2 *Service2) DelData(str string) {fmt.Println("Service2 DelData ", str)module.DataToRemove(str)}func (s2 *Service2) SyncData(t int) {time.Sleep(time.Second * 2 * time.Duration(t))fmt.Println("SyncData over : ", t)if s2.initDone != nil {s2.initDone(nil)}}

使用

//模拟不同场景的异步处理。for i := 0; i < 5; i++ {var s2 app.Service2app.GetOrCreateRootContainer().Invoke(func(service app.Service2) {s2 = serviceinitDone, _ := app.DefaultApplicationLifeCycle().RegisterInitializeAwait(fmt.Sprintf("service: %d", i))s2.InitService(initDone)})s2.SyncData(i)}app.DefaultApplicationLifeCycle().WaitUntilInitialized(context.Background())  //会进行阻塞fmt.Println("App Wait Over")

测试

思考

这里提供一个解决这两种问题的思路,每个人可能都有自己不同的处理方式,可以通过显示Init调用,也可以使用对象中添加 WaitGroup实现。只要能更好的集成到服务框架,能更少的产生理解偏差,更容易使用,耦合度更低就是好程序。

关键词: 流程控制 设计一个 处理方式