github.com/cloudwego/kitex@v0.9.0/pkg/warmup/pool_helper.go (about) 1 /* 2 * Copyright 2021 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package warmup 18 19 import ( 20 "context" 21 "net" 22 "sync" 23 24 "github.com/cloudwego/kitex/pkg/gofunc" 25 "github.com/cloudwego/kitex/pkg/klog" 26 "github.com/cloudwego/kitex/pkg/remote" 27 ) 28 29 // PoolHelper is trivial implementation to do warm-ups for connection pools. 30 type PoolHelper struct { 31 ErrorHandling 32 } 33 34 // WarmUp warms up the given connection pool. 35 func (p *PoolHelper) WarmUp(po *PoolOption, pool remote.ConnPool, co remote.ConnOption) (err error) { 36 num := 1 37 if po.Parallel > num { 38 num = po.Parallel 39 } 40 41 ctx, cancel := context.WithCancel(context.Background()) 42 43 mgr := newManager(ctx, po, p.ErrorHandling) 44 go mgr.watch() 45 var wg sync.WaitGroup 46 for i := 0; i < num; i++ { 47 wg.Add(1) 48 go func() { 49 defer wg.Done() 50 defer func() { 51 if x := recover(); x != nil { 52 klog.Errorf("KITEX: warmup: unexpected error: %+v", x) 53 } 54 }() 55 o := &worker{ 56 jobs: mgr.jobs, 57 errs: mgr.errs, 58 pool: pool, 59 co: co, 60 po: po, 61 } 62 o.fire(mgr.control) 63 }() 64 } 65 wg.Wait() 66 cancel() 67 return mgr.report() 68 } 69 70 type manager struct { 71 ErrorHandling 72 work context.Context 73 control context.Context 74 cancel context.CancelFunc 75 errs chan *job 76 jobs chan *job 77 bads []*job 78 errLock sync.RWMutex // for bads 79 } 80 81 func newManager(ctx context.Context, po *PoolOption, eh ErrorHandling) *manager { 82 control, cancel := context.WithCancel(ctx) 83 return &manager{ 84 ErrorHandling: eh, 85 work: ctx, 86 control: control, 87 cancel: cancel, 88 errs: make(chan *job), 89 jobs: split(po.Targets, po.ConnNum), 90 } 91 } 92 93 func (m *manager) report() error { 94 <-m.work.Done() 95 close(m.errs) 96 m.errLock.RLock() 97 defer m.errLock.RUnlock() 98 switch { 99 case len(m.bads) == 0 || m.ErrorHandling == IgnoreError: 100 return nil 101 case m.ErrorHandling == FailFast: 102 return m.bads[0].err 103 default: 104 return m.bads[0].err // TODO: assemble all errors 105 } 106 } 107 108 func (m *manager) watch() { 109 for { 110 select { 111 case <-m.work.Done(): 112 return 113 case tmp := <-m.errs: 114 if tmp == nil { 115 return // closed 116 } 117 m.errLock.Lock() 118 m.bads = append(m.bads, tmp) 119 m.errLock.Unlock() 120 121 if m.ErrorHandling == FailFast { 122 m.cancel() // stop workers 123 gofunc.GoFunc(context.Background(), func() { 124 // clean up 125 discard(m.errs) 126 discard(m.jobs) 127 }) 128 return 129 } 130 default: 131 continue 132 } 133 } 134 } 135 136 func discard(ch chan *job) { 137 for range ch { 138 } 139 } 140 141 func split(targets map[string][]string, dup int) (js chan *job) { 142 js = make(chan *job) 143 gofunc.GoFunc(context.Background(), func() { 144 for network, addresses := range targets { 145 for _, address := range addresses { 146 for i := 0; i < dup; i++ { 147 js <- &job{network: network, address: address} 148 } 149 } 150 } 151 close(js) 152 }) 153 return 154 } 155 156 type job struct { 157 network string 158 address string 159 err error 160 } 161 162 type worker struct { 163 jobs chan *job 164 errs chan *job 165 pool remote.ConnPool 166 co remote.ConnOption 167 po *PoolOption 168 } 169 170 func (w *worker) fire(ctx context.Context) { 171 var conns []net.Conn 172 defer func() { 173 for _, conn := range conns { 174 w.pool.Put(conn) 175 } 176 }() 177 for { 178 select { 179 case <-ctx.Done(): 180 return 181 case wood := <-w.jobs: 182 if wood == nil { 183 return 184 } 185 var conn net.Conn 186 conn, wood.err = w.pool.Get( 187 ctx, wood.network, wood.address, w.co) 188 if wood.err == nil { 189 conns = append(conns, conn) 190 } else { 191 w.errs <- wood 192 } 193 } 194 } 195 }