Help us shape the future of Go! Take the 2026 Go Developer Survey

triggered

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2026 License: MIT Imports: 6 Imported by: 0

README

Event Executor 事件执行器

被动触发式 执行器: 不使用任何定时器, 每次 Notify 数据到达时立即执行, 把当时缓冲区中的数据合并成一个批次。天然具备"突发流量批量放大"的能力。

import "github.com/474420502/execute/triggered"

快速开始

exec := triggered.RegisterExecute[Event](func(items *triggered.Items[Event]) error {
    // items.Value 是本批次数据, 顺序与投递顺序一致
    return push(items.Value)
}).WithShared(newCounter())
defer exec.Wait(nil)

exec.Notify(event)  // 数据到达即执行(队列满时按背压策略阻塞)

执行语义

  • 到达即执行: 每次 Notify 被处理时, 把当时缓冲区中的数据一次性交付。
  • 串行执行: 同一执行器内任意时刻只有一个批次在执行, 因此下游无需额外串行化。
  • 突发合并: 处理器忙碌期间投递的数据会排队, 并在处理器空出来后合并执行。 这使上游可以放心地"来一条发一条", 由执行器承担合并职责。
  • 顺序保持: 批次内与批次间都保持投递顺序。
  • 主动合并: 连续投递后调用 Flush(), 可把缓冲区数据合并为单个批次提交。

批次上下文

func(items *triggered.Items[Event]) error {
    items.Value  // []Event, 仅本次调用内有效(需要保留请自行复制)
    items.Shared // *triggered.Share, 从创建起存在、跨批次共享
    items.Len()  // 本批次条数
    items.Seq()  // 批次序号, 从 1 开始严格递增(便于幂等与排障)
    return nil
}

跨批次共享状态 Share

s := triggered.NewShare()

s.Store(v)                  // 原子写
s.Load()                    // 原子读(未设置过返回 nil)
s.CompareAndSwap(old, new)  // 原子 CAS
s.Swap(v)                   // 原子交换并返回旧值

s.SetValue(v)               // 回调风格写
s.Value(func(v any) { ... })// 回调风格读(回调在锁内执行, 不得嵌套调用 Value/SetValue)

一次性构造:

exec := triggered.RegisterExecuteEx(&triggered.Config[Event]{
    ItemsChanSize: 4096,                      // Notify 队列容量
    ExecuteDo:     handler,                   // 必填
    Shared:        triggered.NewShare(),
    Recover:       func(ierr any) { log.Println(ierr) },
    Backpressure:  engine.BackpressureDrop,   // 队列满时丢弃而不是阻塞
    MaxPending:    1 << 20,
    Name:          "event-push",
})

生命周期

exec.Notify(e)     // 阻塞投递(队列满时按背压策略)
exec.TryNotify(e)  // 非阻塞投递, 队列满返回 basic.ErrQueueFull 并计数
exec.Flush()       // 立即把缓冲区数据合并为一个批次执行
exec.Close()       // 幂等
exec.Wait(ctx)     // 等待在途批次结束
exec.Stats()       // 指标: 接收/处理/失败/丢弃/在途

Close() 之后 Notify/TryNotify 返回 basic.ErrClosed, 不会 panic。

适用与不适用

  • 适合: 事件驱动流水线、通知推送、需要"来一条处理一条且自动合并"的聚合场景。
  • 不适合: 需要严格按固定节拍执行 —— 请使用 batch/periodic; 需要"攒够 N 条再执行" —— 请使用 batch/threshold。

Documentation

Overview

Package triggered 提供被动触发式执行器.

与 batch/periodic、batch/threshold 不同, 它不使用任何定时器: 每次 Notify 数据到达时, 立即把"当前缓冲区中已有的全部数据"合并成一个批次执行, 因此天然具备合并突发流量(批量放大)的能力, 适合事件驱动、日志聚合、通知推送等场景.

执行函数是串行的: 同一时刻只有一个批次在执行, 上游可以放心地用 Notify 做背压。

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config added in v0.3.0

type Config[ITEM any] struct {
	// ItemsChanSize 是 Notify 队列容量; <=0 时取默认值 1024.
	ItemsChanSize int
	// QueueSize 是执行器采集队列容量; <=0 时与 ItemsChanSize 一致.
	QueueSize int
	// ExecuteDo 是业务回调, 必填.
	ExecuteDo Handler[ITEM]
	// Shared 是初始共享状态; 为 nil 时自动创建.
	Shared *Share
	// Recover 是 panic 恢复回调(可选).
	Recover basic.RecoverFunc
	// Backpressure 决定队列满时 Notify 的行为, 默认 BackpressureBlock.
	Backpressure engine.Backpressure
	// MaxPending 限制缓冲区积压; <=0 不限制.
	MaxPending int
	// Name 用于日志与指标标识.
	Name string
}

Config 是 RegisterExecuteEx 的完整配置.

type EventExecute added in v0.0.3

type EventExecute[ITEM any] struct {
	*engine.Engine[ITEM]
	// contains filtered or unexported fields
}

EventExecute 是被动触发式执行器.

生命周期: 创建即开始接收 Notify, 首次数据到达即执行; Close 幂等, Close 之后 Notify 返回 basic.ErrClosed(err 不再 panic).

func RegisterExecute added in v0.0.3

func RegisterExecute[ITEM any](execDo Handler[ITEM]) *EventExecute[ITEM]

RegisterExecute 注册一个执行单元.

等价于 RegisterExecuteEx(&Config{ExecuteDo: execDo})。

Example

ExampleRegisterExecute 演示事件触发执行器: 数据到达即执行(把当时缓冲区内的数据合成一个批次), 并可在批次之间共享状态。

package main

import (
	"fmt"
	"sync"

	"github.com/474420502/execute/triggered"
)

func main() {
	var (
		mu       sync.Mutex
		batches  int
		received []int
	)
	done := make(chan struct{})

	exec := triggered.RegisterExecute[int](func(items *triggered.Items[int]) error {
		mu.Lock()
		batches++
		received = append(received, items.Value...)
		total, _ := items.Shared.Load().(int)
		items.Shared.Store(total + items.Len()) // 跨批次累计
		mu.Unlock()
		if items.Len() == 2 {
			select {
			case <-done:
			default:
				close(done)
			}
		}
		return nil
	}).WithShared(0)
	defer func() { _ = exec.Wait(nil) }()

	// Notify 把数据放入有界队列; 队列未满时不会阻塞.
	for i := 1; i <= 4; i++ {
		if err := exec.Notify(i); err != nil {
			fmt.Println("notify:", err)
			return
		}
	}
	if err := exec.Flush(); err != nil { // 等待所有已投递数据执行完成
		fmt.Println("flush:", err)
		return
	}

	mu.Lock()
	fmt.Println("batches:", batches)
	fmt.Println("received:", received)
	fmt.Println("shared:", exec.Shared().Load())
	mu.Unlock()

}
Output:
batches: 4
received: [1 2 3 4]
shared: 4

func RegisterExecuteEx added in v0.3.0

func RegisterExecuteEx[ITEM any](cfg *Config[ITEM]) *EventExecute[ITEM]

RegisterExecuteEx 使用完整配置注册执行单元.

Example

ExampleRegisterExecuteEx 演示带配置的注册方式与优雅关闭.

package main

import (
	"fmt"

	"github.com/474420502/execute/triggered"
)

func main() {
	var count int

	exec := triggered.RegisterExecuteEx(&triggered.Config[int]{
		ItemsChanSize: 128, // Notify 队列容量
		Name:          "example",
		ExecuteDo: func(items *triggered.Items[int]) error {
			count += items.Len()
			return nil
		},
	})
	defer func() { _ = exec.Wait(nil) }()

	if err := exec.Notify(1); err != nil {
		fmt.Println("notify:", err)
	}
	if err := exec.Flush(); err != nil {
		fmt.Println("flush:", err)
	}

	exec.Close()
	exec.Close() // 幂等

	fmt.Println("count:", count)
	// 关闭之后投递会返回 ErrClosed, 而不是 panic.
	fmt.Println("after close:", exec.Notify(2))

}
Output:
count: 1
after close: execute: executor is closed

func (*EventExecute[ITEM]) Notify added in v0.0.3

func (exec *EventExecute[ITEM]) Notify(item ITEM) error

Notify 投递一条事件. 队列满时按背压策略阻塞, 执行器已关闭时返回 basic.ErrClosed.

与历史行为一致: 突发投递的多条事件会被合并为一个批次执行。

func (*EventExecute[ITEM]) Shared added in v0.3.0

func (exec *EventExecute[ITEM]) Shared() *Share

Shared 返回该执行器的共享状态载体.

func (*EventExecute[ITEM]) TryNotify added in v0.3.0

func (exec *EventExecute[ITEM]) TryNotify(item ITEM) error

TryNotify 非阻塞投递; 队列满时返回 basic.ErrQueueFull 并计入丢弃指标.

func (*EventExecute[ITEM]) WithOnExecute added in v0.3.0

func (exec *EventExecute[ITEM]) WithOnExecute(fn func(basic.ExecuteEvent)) *EventExecute[ITEM]

WithOnExecute 设置批次执行完成观测回调(同步调用, 需保持轻量).

func (*EventExecute[ITEM]) WithRecover added in v0.1.0

func (exec *EventExecute[ITEM]) WithRecover(fn basic.RecoverFunc) *EventExecute[ITEM]

WithRecover 设置 panic 恢复回调.

func (*EventExecute[ITEM]) WithShared added in v0.1.0

func (exec *EventExecute[ITEM]) WithShared(v any) *EventExecute[ITEM]

WithShared 设置共享状态, 并返回自身以便链式调用.

type Handler added in v0.3.0

type Handler[ITEM any] func(items *Items[ITEM]) error

Handler 是触发式执行器的业务回调.

items.Value 仅在本批次的执行期间有效: 需要跨批次保留数据时请复制或保存 Shared 中的引用。items.Shared 是从执行器创建起就存在、跨批次共享的状态载体。

type Items added in v0.3.0

type Items[T any] struct {
	// Value 是本批次的数据, 仅在本次执行期间有效.
	Value []T
	// Shared 是跨批次共享状态, 永不为 nil.
	Shared *Share
	// contains filtered or unexported fields
}

Items 是一次执行的上下文: 本批次数据 + 跨批次共享状态.

func (*Items[T]) Len added in v0.3.0

func (it *Items[T]) Len() int

Len 返回本批次数据条数.

func (*Items[T]) Seq added in v0.3.0

func (it *Items[T]) Seq() uint64

Seq 返回本批次的序号(从 1 开始), 便于幂等与排障.

type Share added in v0.3.0

type Share struct {
	// contains filtered or unexported fields
}

Share 是跨批次共享的状态载体, 可安全地被多个 goroutine 并发访问.

存取值有两种风格, 按需选择:

原子风格: Load/Store/CompareAndSwap/Swap —— 适合标量配置、开关、版本号;
回调风格: Value/SetValue —— 与历史 API 兼容, 在锁内访问复合结构.

func NewShare added in v0.3.0

func NewShare() *Share

NewShare 创建一个共享状态载体.

func (*Share) CompareAndSwap added in v0.3.0

func (s *Share) CompareAndSwap(old, new any) bool

CompareAndSwap 原子比较并交换; 仅当当前值 == old 时写入 new 并返回 true.

func (*Share) Load added in v0.3.0

func (s *Share) Load() any

Load 原子地读取共享值; 从未设置过时返回 nil.

func (*Share) SetValue added in v0.3.0

func (s *Share) SetValue(v any)

SetValue 设置共享值(回调风格, 与历史 API 兼容).

func (*Share) Store added in v0.3.0

func (s *Share) Store(value any)

Store 原子地设置共享值.

func (*Share) Swap added in v0.3.0

func (s *Share) Swap(value any) any

Swap 原子地写入新值并返回旧值.

func (*Share) Value added in v0.3.0

func (s *Share) Value(do func(v any))

Value 读取共享值并交给回调 do 处理(回调风格).

语义: do 在锁内被调用, 因此可以安全地读取复合结构; 但 do 中不得再次调用 Value/SetValue(会自锁), 需要嵌套访问请改用 Load/Store。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL