业务层
注入给Worker通过抽象在service定义,在service初始化的时候注入实际的实现方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type AudioHooks interface { Enqueue(ctx context.Context, tenantID, interviewID, questionID int64, text string) }
interview.NewService(repo, redisStore, gen, graph,interview.WithAudioHooks(audioHooks))
func NewService(repo *Repository, store *RedisStore, gen *QuestionGenerator, graph *Graph, options ...ServiceOption) *Service { service := &Service{repo: repo, store: store, gen: gen, graph: graph} for _, option := range options { if option != nil { option(service) } } return service }
|
在业务方通过接口丢给worker,业务方不管实际业务
1 2 3 4 5 6 7
| func (s *Service) CreateInterview(ctx context.Context, tenantID, targetJobID, resumeID int64, resumeText string, jd *JDContext, cfg *InterviewConfig) (*Interview, error) { for _, q := range mains { s.audio.Enqueue(ctx, tenantID, iv.ID, q.ID, q.Question) } } return iv, nil }
|
Worker
把worker包装为一个结构体,类似于一个员工,他知道所有生产的工序,设计非常解耦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| type Generator struct { synth Synthesizer store *AudioStore repo AudioStateStore
jobs chan Job backlog []Job }
func (g *Generator) Start(ctx context.Context) { g.run() }
func (g *Generator) run(ctx context.Context) {
backlogStreak := 0 for {
if backlogStreak >= generatorBacklogFairnessLimit { backlogStreak = 0 select { case job := <-g.jobs: g.safeProcess(ctx, job) continue default: } }
if job, ok := g.takeBacklog(); ok { backlogStreak++ g.safeProcess(ctx, job) continue }
select { case job := <-g.jobs: g.safeProcess(ctx, job) } } }
|
为什么不用有缓冲的队列?
缓冲 channel = 固定容量 + FIFO + 满了只有”阻塞或丢弃”两个选项;
而这个场景需要”满了不阻塞、不丢、还能插队回收”,所以必须是 非缓冲语义的交接 channel + 显式溢出队列
在程序入口层启动,并注入到service