github.com/songzhibin97/gkit@v1.2.13/container/pool/list.go (about) 1 package pool 2 3 import ( 4 "container/list" 5 "context" 6 "sync" 7 "sync/atomic" 8 "time" 9 10 "github.com/songzhibin97/gkit/options" 11 "github.com/songzhibin97/gkit/timeout" 12 ) 13 14 var _ Pool = &List{} 15 16 // List 双向链表 17 type List struct { 18 // f: item 19 f func(ctx context.Context) (IShutdown, error) 20 21 // mu: 互斥锁, 保护以下字段 22 mu sync.Mutex 23 24 // cond: 发送信号,通知有回收动作,在等待的可以再次尝试获取资源 25 cond chan struct{} 26 27 // cleanerCh: 清空 ch 28 cleanerCh chan struct{} 29 30 // active: 最大连接数 31 active uint64 32 33 // conf: 配置信息 34 conf *config 35 36 // closed: 37 closed uint32 38 39 // idles: 链表 40 idles list.List 41 } 42 43 // Reload 重新设置配置文件 44 func (l *List) Reload(options ...options.Option) { 45 l.mu.Lock() 46 defer l.mu.Unlock() 47 for _, option := range options { 48 option(l.conf) 49 } 50 } 51 52 // Init 初始化 53 func (l *List) Init(d time.Duration) { 54 // 如果 <= 0 放弃设置 55 if d <= 0 { 56 return 57 } 58 // 如果时间间隔d小于等待超时,并且 cleanerCh 不为nil 监听信号 59 if d < l.conf.idleTimeout && l.cleanerCh != nil { 60 select { 61 // 发送立即清除旧配置的信号,如果阻塞说明在时间周期内进行清洁,跳过 62 case l.cleanerCh <- struct{}{}: 63 default: 64 } 65 } 66 // 懒加载 67 if l.cleanerCh == nil { 68 l.cleanerCh = make(chan struct{}, 1) 69 // 开启定时任务 70 go l.Timer(l.conf.idleTimeout) 71 } 72 } 73 74 // Timer 定时任务 75 func (l *List) Timer(d time.Duration) { 76 if d < minDuration { 77 d = minDuration 78 } 79 // ticker: 定时任务 80 ticker := time.NewTicker(d) 81 for { 82 select { 83 // 触发条件: 84 // 1. 定时周期 85 // 2. l.cleanerCh 接收到信号 86 case <-ticker.C: 87 case <-l.cleanerCh: 88 } 89 l.mu.Lock() 90 // 是否关闭 或者 没有设置超时时间 91 if atomic.LoadUint32(&l.closed) == 1 || l.conf.idleTimeout <= 0 { 92 l.mu.Unlock() 93 return 94 } 95 // 循环链表 96 for i, n := 0, l.idles.Len(); i < n; i++ { 97 // idles.Back() 返回链表中最后一个元素, 如果当前链表已经是空了 则返回nil 98 e := l.idles.Back() 99 if e == nil { 100 break 101 } 102 // 断言为 item 103 ic := e.Value.(item) 104 // 判断时间是否超时 105 if !ic.expire(l.conf.idleTimeout) { 106 break 107 } 108 // 如果已经超时,则删除此元素 109 l.idles.Remove(e) 110 // release 计数 111 l.release() 112 l.mu.Unlock() 113 _ = ic.s.Shutdown() 114 l.mu.Lock() 115 } 116 l.mu.Unlock() 117 } 118 } 119 120 // release 当前活跃线程数-1 并发送信号通知 121 // hold p.mu during the call. 122 func (l *List) release() { 123 // l.active -= 1 124 atomic.AddUint64(&l.active, ^uint64(0)) 125 l.signal() 126 } 127 128 // signal 发送信号通知 129 func (l *List) signal() { 130 select { 131 case l.cond <- struct{}{}: 132 default: 133 } 134 } 135 136 // Get 获取 137 func (l *List) Get(ctx context.Context) (IShutdown, error) { 138 l.mu.Lock() 139 // 判断是否关闭 140 if atomic.LoadUint32(&l.closed) == 1 { 141 l.mu.Unlock() 142 return nil, ErrPoolClosed 143 } 144 for { 145 for i, n := 0, l.idles.Len(); i < n; i++ { 146 e := l.idles.Front() 147 if e == nil { 148 break 149 } 150 ic := e.Value.(item) 151 l.idles.Remove(e) 152 l.mu.Unlock() 153 // 没有过期的可以直接返回了 154 if !ic.expire(l.conf.idleTimeout) { 155 return ic.s, nil 156 } 157 // 清理 重新获取锁 158 _ = ic.s.Shutdown() 159 l.mu.Lock() 160 l.release() 161 } 162 163 // 检查是否关闭 164 if atomic.LoadUint32(&l.closed) == 1 { 165 l.mu.Unlock() 166 return nil, ErrPoolClosed 167 } 168 // 判断是否需要新增 169 if l.conf.active == 0 || l.active < l.conf.active { 170 if l.f == nil { 171 return nil, ErrPoolNewFuncIsNull 172 } 173 newItem := l.f 174 l.mu.Unlock() 175 atomic.AddUint64(&l.active, 1) 176 // 新增: 177 c, err := newItem(ctx) 178 if err != nil { 179 l.release() 180 c = nil 181 } 182 return c, err 183 } 184 // 如果满了判断是否需要等待 185 if l.conf.waitTimeout == 0 && !l.conf.wait { 186 l.mu.Unlock() 187 return nil, ErrPoolExhausted 188 } 189 // 获取超时时间,解锁进入等待状态 190 wt := l.conf.waitTimeout 191 l.mu.Unlock() 192 193 // 控制链路超时时间 194 _, nCtx, cancel := timeout.Shrink(ctx, wt) 195 196 // 超时/收到了某应用回收的信号 197 select { 198 case <-nCtx.Done(): 199 cancel() 200 return nil, nCtx.Err() 201 case <-l.cond: 202 } 203 // 自旋,再次尝试获得句柄 204 cancel() 205 l.mu.Lock() 206 } 207 } 208 209 // Put 回收 210 func (l *List) Put(ctx context.Context, s IShutdown, forceClose bool) error { 211 l.mu.Lock() 212 if atomic.LoadUint32(&l.closed) == 0 && !forceClose { 213 // 插入到链表头 214 l.idles.PushFront(item{createdAt: nowFunc(), s: s}) 215 // 判断闲置数量是否达到阈值 216 if uint64(l.idles.Len()) > l.conf.idle { 217 // 拿到尾部淘汰的 shutdown 218 s = l.idles.Remove(l.idles.Back()).(item).s 219 } else { 220 s = nil 221 } 222 } 223 // 如果 s == nil 进入回收 224 if s == nil { 225 l.signal() 226 l.mu.Unlock() 227 return nil 228 } 229 l.mu.Unlock() 230 l.release() 231 return s.Shutdown() 232 } 233 234 // Shutdown 关闭 235 func (l *List) Shutdown() error { 236 l.mu.Lock() 237 if atomic.SwapUint32(&l.closed, 1) == 1 { 238 return ErrPoolClosed 239 } 240 idles := l.idles 241 // .Init 重新初始化链表 快速清空 242 l.idles.Init() 243 if idles.Len() > 0 { 244 atomic.AddUint64(&l.active, ^uint64(idles.Len()-1)) 245 } 246 l.mu.Unlock() 247 // 在循环旧链表进行资源回收 248 for e := idles.Front(); e != nil; e = e.Next() { 249 _ = e.Value.(item).s.Shutdown() 250 } 251 return nil 252 } 253 254 // New 设置创建资源函数 255 func (l *List) New(f func(ctx context.Context) (IShutdown, error)) { 256 l.mu.Lock() 257 defer l.mu.Unlock() 258 l.f = f 259 } 260 261 // NewList 实例化 262 func NewList(options ...options.Option) Pool { 263 l := &List{conf: defaultConfig()} 264 l.cond = make(chan struct{}) 265 for _, option := range options { 266 option(l.conf) 267 } 268 l.Init(l.conf.idleTimeout) 269 return l 270 }