Go语言 在Uber FX中实现后台进程优雅关闭的正确方法是什么?

44u64gxh  于 2024-01-04  发布在  Go
关注(0)|答案(1)|浏览(160)

假设我的Uber FX应用程序中有一个服务,它应该执行一些后台活动,比如轮询外部API。我可以通过触发goroutine来运行后台任务,但是如何正确地停止它们呢?
作为一种可能的实现,让我们考虑以下示例:

package main

import (
    "context"
    "log"
    "sync"
    "time"

    "go.uber.org/fx"
)

type AwesomeService struct {
    // context to use for background processes
    bg context.Context
    // to trigger background processes stopping
    cancel context.CancelFunc
    // to wait for background processes to gracefully finish
    wg *sync.WaitGroup
}

func New(lc fx.Lifecycle) *AwesomeService {
    bg, cancel := context.WithCancel(context.Background())
    service := &AwesomeService{
        bg:     bg,
        cancel: cancel,
        wg:     new(sync.WaitGroup),
    }

    lc.Append(fx.Hook{
        OnStart: service.start,
        OnStop:  service.stop,
    })
    return service
}

func (s *AwesomeService) start(_ context.Context) error {
    s.runBackgroundProcess()
    log.Println("Start done")
    return nil
}

func (s *AwesomeService) stop(_ context.Context) error {
    s.cancel()
    s.wg.Wait()
    log.Println("Stop done")
    return nil
}

// runBackgroundProcess does some work till context is done.
func (s *AwesomeService) runBackgroundProcess() {
    s.wg.Add(1)
    go func() {
        defer s.wg.Done()
        for {
            select {
            case <-s.bg.Done():
                return
            case <-time.After(1 * time.Second):
                log.Println("Working...")
            }
        }
    }()
}

func main() {
    fx.New(
        fx.Provide(New),
        fx.Invoke(func(*AwesomeService) {}),
    ).Run()
}

字符串
注意事项:

  • 通过使用fx.Lifecycle挂钩,服务与应用程序生命周期相关联。
  • 我不能依赖和使用OnStart/OnStop方法中的上下文,因为它们是不同的上下文,并且对应于启动/停止活动,而不是应用生命周期上下文。

关注和问题:

  • 在跟踪bg任务方面,给出的例子是相当繁重的。而且,在结构中存储上下文是一种反模式。有没有什么方法可以简化它?
  • 我是否应该等待goroutine完成,以防没有资源可以释放?
a9wyjsp7

a9wyjsp71#

在我看来,使用上下文是很好的,但是你也可以通过一个通道向任何你想要的Go例程传递一个关闭信号。
是的,你也应该等待等待组计数返回到零之前完全关闭应用程序.所以你会先关闭通道,然后等待等待组.

package main

import (
    "context"
    "log"
    "sync"
    "time"

    "go.uber.org/fx"
)

type AwesomeService struct {
    // channel to shutdown background processes
    shutdown chan struct{}
    // to wait for background processes to gracefully finish
    wg *sync.WaitGroup
}

func New(lc fx.Lifecycle) *AwesomeService {
    service := &AwesomeService{
        shutdown: make(chan struct{}),
        wg:     new(sync.WaitGroup),
    }

    lc.Append(fx.Hook{
        OnStart: service.start,
        OnStop:  service.stop,
    })
    return service
}

func (s *AwesomeService) start(_ context.Context) error {
    s.runBackgroundProcess()
    log.Println("Start done")
    return nil
}

func (s *AwesomeService) stop(_ context.Context) error {
    close(s.shutdown)
    s.wg.Wait()
    log.Println("Stop done")
    return nil
}

// runBackgroundProcess does some work till context is done.
func (s *AwesomeService) runBackgroundProcess() {
    s.wg.Add(1)
    go func() {
        defer s.wg.Done()
        for {
            select {
            case <-s.shutdown:
                return
            case <-time.After(1 * time.Second):
                log.Println("Working...")
            }
        }
    }()
}

func main() {
    fx.New(
        fx.Provide(New),
        fx.Invoke(func(*AwesomeService) {}),
    ).Run()
}

字符串

相关问题