github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/chooser/ip_chooser.go (about) 1 package chooser 2 3 import ( 4 "bytes" 5 "container/heap" 6 "context" 7 "fmt" 8 "net" 9 "sync" 10 "time" 11 ) 12 13 type ( 14 blackItem struct { 15 index int 16 domain string 17 ip net.IP 18 expiredAt time.Time 19 } 20 21 blackheap struct { 22 m map[string]*blackItem 23 items []*blackItem 24 } 25 26 ipChooser struct { 27 blackheap blackheap 28 blackheapMutex sync.Mutex 29 freezeDuration time.Duration 30 } 31 32 // IPChooserConfig IP 选择器的选项 33 IPChooserConfig struct { 34 // FreezeDuration IP 冻结时长(默认:600s) 35 FreezeDuration time.Duration 36 } 37 ) 38 39 // NewIPChooser 创建 IP 选择器 40 func NewIPChooser(options *IPChooserConfig) Chooser { 41 if options == nil { 42 options = &IPChooserConfig{} 43 } 44 freezeDuration := options.FreezeDuration 45 if freezeDuration == 0 { 46 freezeDuration = 10 * time.Minute 47 } 48 return &ipChooser{ 49 blackheap: blackheap{ 50 m: make(map[string]*blackItem, 1024), 51 items: make([]*blackItem, 0, 1024), 52 }, 53 freezeDuration: freezeDuration, 54 } 55 } 56 57 func (chooser *ipChooser) Choose(_ context.Context, ips []net.IP, options *ChooseOptions) []net.IP { 58 if len(ips) == 0 { 59 return nil 60 } 61 if options == nil { 62 options = &ChooseOptions{} 63 } 64 65 chooser.blackheapMutex.Lock() 66 defer chooser.blackheapMutex.Unlock() 67 68 chosenIPs := make([]net.IP, 0, chooser.blackheap.Len()) 69 for _, ip := range ips { 70 if item := chooser.blackheap.FindByDomainAndIp(options.Domain, ip); item == nil { 71 chosenIPs = append(chosenIPs, ip) 72 } 73 } 74 if len(chosenIPs) > 0 || options.FailFast { 75 return chosenIPs 76 } 77 78 var chosenExpiredAt time.Time 79 toFind := makeSet(ips, options.Domain, makeMapKey) 80 backups := make([]*blackItem, 0, chooser.blackheap.Len()) 81 for chooser.blackheap.Len() > 0 { 82 firstChosen := heap.Pop(&chooser.blackheap).(*blackItem) 83 backups = append(backups, firstChosen) 84 key := makeMapKey(firstChosen.domain, firstChosen.ip) 85 if _, ok := toFind[key]; ok { 86 chosenExpiredAt = firstChosen.expiredAt 87 chosenIPs = append(chosenIPs, firstChosen.ip) 88 delete(toFind, key) 89 break 90 } 91 } 92 if chosenExpiredAt.IsZero() { 93 panic("chosenExpiredAt should not be empty") 94 } 95 for chooser.blackheap.Len() > 0 { 96 item := heap.Pop(&chooser.blackheap).(*blackItem) 97 backups = append(backups, item) 98 if chosenExpiredAt.Equal(item.expiredAt) { 99 key := makeMapKey(item.domain, item.ip) 100 if _, ok := toFind[key]; ok { 101 chosenIPs = append(chosenIPs, item.ip) 102 delete(toFind, key) 103 } 104 } else { 105 break 106 } 107 } 108 for _, backup := range backups { 109 if backup.expiredAt.After(time.Now()) { 110 heap.Push(&chooser.blackheap, backup) 111 } 112 } 113 return chosenIPs 114 } 115 116 func (chooser *ipChooser) FeedbackGood(_ context.Context, ips []net.IP, options *FeedbackOptions) { 117 if len(ips) == 0 { 118 return 119 } 120 if options == nil { 121 options = &FeedbackOptions{} 122 } 123 124 chooser.blackheapMutex.Lock() 125 defer chooser.blackheapMutex.Unlock() 126 127 for _, ip := range ips { 128 if item := chooser.blackheap.FindByDomainAndIp(options.Domain, ip); item != nil { 129 heap.Remove(&chooser.blackheap, item.index) 130 } 131 } 132 } 133 134 func (chooser *ipChooser) FeedbackBad(_ context.Context, ips []net.IP, options *FeedbackOptions) { 135 if len(ips) == 0 { 136 return 137 } 138 if options == nil { 139 options = &FeedbackOptions{} 140 } 141 142 chooser.blackheapMutex.Lock() 143 defer chooser.blackheapMutex.Unlock() 144 145 newExpiredAt := time.Now().Add(chooser.freezeDuration) 146 for _, ip := range ips { 147 if item := chooser.blackheap.FindByDomainAndIp(options.Domain, ip); item != nil { 148 chooser.blackheap.items[item.index].expiredAt = newExpiredAt 149 heap.Fix(&chooser.blackheap, item.index) 150 } else { 151 heap.Push(&chooser.blackheap, &blackItem{ 152 domain: options.Domain, 153 ip: ip, 154 expiredAt: newExpiredAt, 155 }) 156 } 157 } 158 } 159 160 func (h *blackheap) Len() int { 161 return len(h.items) 162 } 163 164 func (h *blackheap) Less(i, j int) bool { 165 return h.items[i].expiredAt.Before(h.items[j].expiredAt) 166 } 167 168 func (h *blackheap) Swap(i, j int) { 169 if i == j { 170 return 171 } 172 h.items[i].domain, h.items[j].domain = h.items[j].domain, h.items[i].domain 173 h.items[i].ip, h.items[j].ip = h.items[j].ip, h.items[i].ip 174 h.items[i].expiredAt, h.items[j].expiredAt = h.items[j].expiredAt, h.items[i].expiredAt 175 h.m[makeMapKey(h.items[i].domain, h.items[i].ip)] = h.items[i] 176 h.m[makeMapKey(h.items[j].domain, h.items[j].ip)] = h.items[j] 177 } 178 179 func (h *blackheap) Push(x interface{}) { 180 item := x.(*blackItem) 181 item.index = len(h.items) 182 h.items = append(h.items, item) 183 h.m[makeMapKey(item.domain, item.ip)] = item 184 } 185 186 func (h *blackheap) Pop() interface{} { 187 n := len(h.items) 188 last := h.items[n-1] 189 h.items = h.items[0 : n-1] 190 delete(h.m, makeMapKey(last.domain, last.ip)) 191 return last 192 } 193 194 func (h *blackheap) FindByDomainAndIp(domain string, ip net.IP) *blackItem { 195 key := makeMapKey(domain, ip) 196 if item, ok := h.m[key]; ok { 197 if item.expiredAt.Before(time.Now()) { 198 heap.Remove(h, item.index) 199 return nil 200 } 201 return item 202 } 203 return nil 204 } 205 206 func (h *blackheap) String() string { 207 var buf bytes.Buffer 208 209 buf.WriteString("&blackheap{ items: [") 210 for _, item := range h.items { 211 buf.WriteString(item.String()) 212 buf.WriteString(", ") 213 } 214 buf.WriteString("], m: {") 215 for k, v := range h.m { 216 buf.WriteString(k) 217 buf.WriteString(": ") 218 buf.WriteString(v.String()) 219 buf.WriteString(", ") 220 } 221 buf.WriteString("}") 222 223 return buf.String() 224 } 225 226 func (item *blackItem) String() string { 227 var buf bytes.Buffer 228 229 buf.WriteString("&blackItem{ index: ") 230 buf.WriteString(fmt.Sprintf("%d", item.index)) 231 buf.WriteString(", domain: ") 232 buf.WriteString(item.domain) 233 buf.WriteString(", ip: ") 234 buf.WriteString(item.ip.String()) 235 buf.WriteString(", expiredAt: ") 236 buf.WriteString(item.expiredAt.String()) 237 buf.WriteString("}") 238 239 return buf.String() 240 } 241 242 func makeMapKey(domain string, ip net.IP) string { 243 return ip.String() + "|" + domain 244 }