registration_db.go

package nsqlookupd

import (
    "fmt"
    "sync"
    "sync/atomic"
    "time"
)
//db(注册中心--内存数据库map)结构体
type RegistrationDB struct {
    sync.RWMutex  //读写锁
    registrationMap map[Registration]Producers //
}
//代表一个生产者  主题 通道    
type Registration struct {
    Category string  //主题
    Key      string  //通道
    SubKey   string  //
}
type Registrations []Registration
//代表客户端nsqd 的配置信息
type PeerInfo struct {
    lastUpdate       int64
    id               string
    RemoteAddress    string `json:"remote_address"`
    Hostname         string `json:"hostname"`
    BroadcastAddress string `json:"broadcast_address"`
    TCPPort          int    `json:"tcp_port"`
    HTTPPort         int    `json:"http_port"`
    Version          string `json:"version"`
}
//生产者
type Producer struct {
    peerInfo     *PeerInfo
    tombstoned   bool
    tombstonedAt time.Time
}

type Producers []*Producer

func (p *Producer) String() string {
    return fmt.Sprintf("%s [%d, %d]", p.peerInfo.BroadcastAddress, p.peerInfo.TCPPort, p.peerInfo.HTTPPort)
}

func (p *Producer) Tombstone() {
    p.tombstoned = true
    p.tombstonedAt = time.Now()
}

func (p *Producer) IsTombstoned(lifetime time.Duration) bool {
    return p.tombstoned && time.Now().Sub(p.tombstonedAt) < lifetime
}

func NewRegistrationDB() *RegistrationDB {
    return &RegistrationDB{
        registrationMap: make(map[Registration]Producers),
    }
}

// add a registration key
func (r *RegistrationDB) AddRegistration(k Registration) {
    r.Lock()
    defer r.Unlock()
    _, ok := r.registrationMap[k]
    if !ok {
        r.registrationMap[k] = Producers{}
    }
}

// add a producer to a registration
func (r *RegistrationDB) AddProducer(k Registration, p *Producer) bool {
    r.Lock()
    defer r.Unlock()
    producers := r.registrationMap[k]
    found := false
    for _, producer := range producers {
        if producer.peerInfo.id == p.peerInfo.id {
            found = true
        }
    }
    if found == false {
        r.registrationMap[k] = append(producers, p)
    }
    return !found
}

// remove a producer from a registration
func (r *RegistrationDB) RemoveProducer(k Registration, id string) (bool, int) {
    r.Lock()
    defer r.Unlock()
    producers, ok := r.registrationMap[k]
    if !ok {
        return false, 0
    }
    removed := false
    cleaned := Producers{}
    for _, producer := range producers {
        if producer.peerInfo.id != id {
            cleaned = append(cleaned, producer)
        } else {
            removed = true
        }
    }
    // Note: this leaves keys in the DB even if they have empty lists
    r.registrationMap[k] = cleaned
    return removed, len(cleaned)
}

// remove a Registration and all it's producers
func (r *RegistrationDB) RemoveRegistration(k Registration) {
    r.Lock()
    defer r.Unlock()
    delete(r.registrationMap, k)
}

func (r *RegistrationDB) needFilter(key string, subkey string) bool {
    return key == "*" || subkey == "*"
}

func (r *RegistrationDB) FindRegistrations(category string, key string, subkey string) Registrations {
    r.RLock()
    defer r.RUnlock()
    if !r.needFilter(key, subkey) {
        k := Registration{category, key, subkey}
        if _, ok := r.registrationMap[k]; ok {
            return Registrations{k}
        }
        return Registrations{}
    }
    results := Registrations{}
    for k := range r.registrationMap {
        if !k.IsMatch(category, key, subkey) {
            continue
        }
        results = append(results, k)
    }
    return results
}

func (r *RegistrationDB) FindProducers(category string, key string, subkey string) Producers {
    r.RLock()
    defer r.RUnlock()
    if !r.needFilter(key, subkey) {
        k := Registration{category, key, subkey}
        return r.registrationMap[k]
    }

    results := Producers{}
    for k, producers := range r.registrationMap {
        if !k.IsMatch(category, key, subkey) {
            continue
        }
        for _, producer := range producers {
            found := false
            for _, p := range results {
                if producer.peerInfo.id == p.peerInfo.id {
                    found = true
                }
            }
            if found == false {
                results = append(results, producer)
            }
        }
    }
    return results
}

func (r *RegistrationDB) LookupRegistrations(id string) Registrations {
    r.RLock()
    defer r.RUnlock()
    results := Registrations{}
    for k, producers := range r.registrationMap {
        for _, p := range producers {
            if p.peerInfo.id == id {
                results = append(results, k)
                break
            }
        }
    }
    return results
}

func (k Registration) IsMatch(category string, key string, subkey string) bool {
    if category != k.Category {
        return false
    }
    if key != "*" && k.Key != key {
        return false
    }
    if subkey != "*" && k.SubKey != subkey {
        return false
    }
    return true
}

func (rr Registrations) Filter(category string, key string, subkey string) Registrations {
    output := Registrations{}
    for _, k := range rr {
        if k.IsMatch(category, key, subkey) {
            output = append(output, k)
        }
    }
    return output
}

func (rr Registrations) Keys() []string {
    keys := make([]string, len(rr))
    for i, k := range rr {
        keys[i] = k.Key
    }
    return keys
}

func (rr Registrations) SubKeys() []string {
    subkeys := make([]string, len(rr))
    for i, k := range rr {
        subkeys[i] = k.SubKey
    }
    return subkeys
}

func (pp Producers) FilterByActive(inactivityTimeout time.Duration, tombstoneLifetime time.Duration) Producers {
    now := time.Now()
    results := Producers{}
    for _, p := range pp {
        cur := time.Unix(0, atomic.LoadInt64(&p.peerInfo.lastUpdate))
        if now.Sub(cur) > inactivityTimeout || p.IsTombstoned(tombstoneLifetime) {
            continue
        }
        results = append(results, p)
    }
    return results
}

func (pp Producers) PeerInfo() []*PeerInfo {
    results := []*PeerInfo{}
    for _, p := range pp {
        results = append(results, p.peerInfo)
    }
    return results
}