Ticker 本质就是一个通道,可以用 for select
接收,也可以直接 for range
接收。
go func() {
ticker := time.Tick(1 * time.Second)
for {
select {
case <-ticker:
// do something
}
}
}()
go func() {
ticker := time.Tick(1 * time.Second)
for range ticker {
// do something
}
}()
附:Ticker
src\time\tick.go
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
r runtimeTimer
}
func NewTicker(d Duration) *Ticker {
if d <= 0 {
panic(errors.New("non-positive interval for NewTicker"))
}
// Give the channel a 1-element time buffer.
// If the client falls behind while reading, we drop ticks
// on the floor until the client catches up.
c := make(chan Time, 1)
t := &Ticker{
C: c,
r: runtimeTimer{
when: when(d),
period: int64(d),
f: sendTime,
arg: c,
},
}
startTimer(&t.r)
return t
}
src\time\sleep.go
type runtimeTimer struct {
pp uintptr
when int64
period int64
f func(any, uintptr) // NOTE: must not be closure
arg any
seq uintptr
nextwhen int64
status uint32
}
func sendTime(c any, seq uintptr) {
select {
case c.(chan Time) <- Now():
default:
}
}
src\runtime\time.go
// startTimer adds t to the timer heap.
//
//go:linkname startTimer time.startTimer
func startTimer(t *timer) {
if raceenabled {
racerelease(unsafe.Pointer(t))
}
addtimer(t)
}
func addtimer(t *timer) {
// when must be positive. A negative value will cause runtimer to
// overflow during its delta calculation and never expire other runtime
// timers. Zero will cause checkTimers to fail to notice the timer.
if t.when <= 0 {
throw("timer when must be positive")
}
if t.period < 0 {
throw("timer period must be non-negative")
}
if t.status.Load() != timerNoStatus {
throw("addtimer called with initialized timer")
}
t.status.Store(timerWaiting)
when := t.when
// Disable preemption while using pp to avoid changing another P's heap.
mp := acquirem()
pp := getg().m.p.ptr()
lock(&pp.timersLock)
cleantimers(pp)
doaddtimer(pp, t)
unlock(&pp.timersLock)
wakeNetPoller(when)
releasem(mp)
}
func doaddtimer(pp *p, t *timer) {
// Timers rely on the network poller, so make sure the poller
// has started.
if netpollInited.Load() == 0 {
netpollGenericInit()
}
if t.pp != 0 {
throw("doaddtimer: P already set in timer")
}
t.pp.set(pp)
i := len(pp.timers)
pp.timers = append(pp.timers, t)
siftupTimer(pp.timers, i)
if t == pp.timers[0] {
pp.timer0When.Store(t.when)
}
pp.numTimers.Add(1)
}
在 p 的 timers 中插入一个 timer 定时器。
开发一个 Golang 编程框架
- 参照 golang-standards/project-layout 的项目结构
- 使用 go mod 管理依赖(尽量少用第三方依赖,使用原生库)
- 支持 HTTP API 和 gRPC API(也需要对应的客户端实现)
- 在 HTTP API 的基础上增加 RESTful 层
- 使用 Viper 做配置管理(配置文件、环境变量、命令行参数、etcd)
- 使用 slog 做日志输出
- 使用 wire 做依赖注入
- 内置雪花算法、JWT、RBAC、常见加密算法等实现(工具库)
- 支持 MySQL / Redis / RabbitMQ
- 支持 S3 存储
- 内置定时任务框架
- 支持配置中心(内置 etcd 实现)
- 支持服务注册(内置 etcd 实现)