| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- package tos
- import (
- "container/heap"
- "context"
- "net"
- "strings"
- "sync"
- "time"
- )
- const (
- DefaultCacheCap = 100
- VolceHostSuffix = "volces.com"
- HostSplitSep = "."
- HostSplitLength = 4
- )
- type cacheItem struct {
- host string
- ipList []string
- expireAt time.Time
- heapIndex int
- }
- type priorityQueue []*cacheItem
- func (p priorityQueue) Len() int {
- return len(p)
- }
- func (p priorityQueue) Peek() *cacheItem {
- if p.Len() > 0 {
- return p[0]
- }
- return nil
- }
- func (p priorityQueue) Less(i, j int) bool {
- return p[i].expireAt.Before(p[j].expireAt)
- }
- func (p priorityQueue) Swap(i, j int) {
- p[i], p[j] = p[j], p[i]
- p[i].heapIndex = i
- p[j].heapIndex = j
- }
- func (p *priorityQueue) Push(x interface{}) {
- n := len(*p)
- item := x.(*cacheItem)
- item.heapIndex = n
- *p = append(*p, item)
- }
- func (p *priorityQueue) Pop() interface{} {
- old := *p
- n := len(old)
- item := old[n-1]
- old[n-1] = nil
- item.heapIndex = -1
- *p = old[0 : n-1]
- return item
- }
- type cache struct {
- lock sync.RWMutex
- heap *priorityQueue
- cleanTime time.Time
- data map[string]cacheItem
- expiration time.Duration
- }
- func (c *cache) Remove(key string, removeIp string) {
- c.lock.Lock()
- defer c.lock.Unlock()
- data, ok := c.data[key]
- if !ok {
- return
- }
- value := make([]string, 0, len(data.ipList))
- for _, ip := range data.ipList {
- if ip == removeIp {
- continue
- }
- value = append(value, ip)
- }
- // 没有有效的 IP 将缓存删除
- if len(value) == 0 {
- delete(c.data, key)
- return
- }
- data.ipList = value
- c.data[key] = data
- }
- func (c *cache) Get(key string) ([]string, bool) {
- c.lock.RLock()
- data, ok := c.data[key]
- c.lock.RUnlock()
- if !ok {
- return nil, false
- }
- if data.expireAt.Before(time.Now()) {
- return nil, false
- }
- return data.ipList, true
- }
- func (c *cache) cleanCache() {
- c.cleanTime = time.Now().Add(c.expiration)
- maxCleanCount := 5
- for i := 0; i < maxCleanCount; i++ {
- item := c.heap.Peek()
- if item == nil {
- return
- }
- if item.expireAt.Before(time.Now()) {
- heap.Pop(c.heap)
- data, ok := c.data[item.host]
- if ok && data.expireAt == item.expireAt {
- delete(c.data, item.host)
- }
- } else {
- return
- }
- }
- }
- func (c *cache) Put(key string, ipList []string) {
- c.lock.Lock()
- defer c.lock.Unlock()
- item := cacheItem{
- ipList: ipList,
- expireAt: time.Now().Add(c.expiration),
- host: key,
- }
- c.data[key] = item
- heap.Push(c.heap, &item)
- // 大于 Cap
- if c.heap.Len() > DefaultCacheCap {
- item := heap.Pop(c.heap).(*cacheItem)
- if item == nil {
- return
- }
- data, ok := c.data[item.host]
- if ok && data.expireAt == item.expireAt {
- delete(c.data, item.host)
- }
- }
- if time.Now().After(c.cleanTime) {
- c.cleanCache()
- }
- }
- type resolver struct {
- cache *cache
- }
- func newResolver(expiration time.Duration) *resolver {
- pq := make(priorityQueue, 0)
- return &resolver{cache: &cache{
- heap: &pq,
- cleanTime: time.Now().Add(expiration),
- data: make(map[string]cacheItem),
- expiration: expiration,
- }}
- }
- func ipToStringList(ips []net.IP) []string {
- res := make([]string, len(ips))
- for i, ip := range ips {
- res[i] = ip.String()
- }
- return res
- }
- func wrappedHost(host string) string {
- if !strings.HasSuffix(host, VolceHostSuffix) {
- return host
- }
- hostSplit := strings.Split(host, HostSplitSep)
- if len(hostSplit) != HostSplitLength {
- return host
- }
- return strings.Join(hostSplit[1:], HostSplitSep)
- }
- func (r *resolver) GetIpList(ctx context.Context, host string) ([]string, error) {
- ipList, ok := r.cache.Get(wrappedHost(host))
- if ok {
- return ipList, nil
- }
- ips, err := net.LookupIP(host)
- if err != nil {
- return nil, err
- }
- ipsStr := ipToStringList(ips)
- r.cache.Put(wrappedHost(host), ipsStr)
- return ipsStr, nil
- }
- func (r *resolver) Remove(host string, ip string) {
- r.cache.Remove(wrappedHost(host), ip)
- }
|