Golang 实现包的初始化控制器与流程控制器

背景

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

原理

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

初始化控制

结构体定义

func DefaultApplicationLifeCycle() *ApplicationLifeCycle {
	return appInit
}

type InitProc func() error
type InitializeDoneProc func(err error)
type ShutdownProc func(ctx context.Context) error

type initProcCtx struct {
	name     string
	priority int
	proc     InitProc
}

type shutdownProcCtx struct {
	name     string
	priority int
	proc     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.MaxInt32
	max := math.MinInt32
	for _, 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 = true
	return nil
}

/*
根据优先级执行各对象的初始化函数
*/
func (alc *ApplicationLifeCycle) Shutdown(ctx context.Context) {
	alc.shutdownRequested = true
	alc.once.Do(func() {
		min := math.MaxInt32
		max := math.MinInt32
		for _, 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.go
func 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.go
func 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.Service1
	app.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():
			return
		default:
			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.Service2
		app.GetOrCreateRootContainer().Invoke(func(service app.Service2) {
			s2 = service
			initDone, _ := 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实现。只要能更好的集成到服务框架,能更少的产生理解偏差,更容易使用,耦合度更低就是好程序。

posted @ 2023-01-03 15:31  zscbest  阅读(108)  评论(0编辑  收藏  举报