在rpc框架中,kitc客户端中需要对访问的服务IP进行缓存与更新,本章谈一谈kitc对服务信息的缓存与更新机制。
kitc获取服务信息是通过请求的方式向 consul服务来获取特定服务的IP地址,为了避免kitc在启动时对同一个Key并发地访问,这里引入一个结构来对重复函数调用进行限制:
type call struct {
wg sync.WaitGroup
val interface{}
err error
}
type Group struct {
mu sync.Mutex
m map[string]*call
}
func (g *Group) Do(key string, fn func() (interface{}, error) {
g.mu.lock()
if g.m == nil {
....
}
if c, ok := g.m[key]; ok {
g.mu.Unlock()
c.Wg.Wait()
return c.val, c,.err
}
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
c.val, c.err = fn()
c.wg.Done()
g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()
}
对信息进行缓存的结构如下:
type Asyncache struct {
sfg *Group
opt *Options
data sync.Map
exit chan struct{}
}
type Options struct {
BlockFirest bool
RefreshDuration time.Duration
Fetcher func(key string) ( interface{}, error)
ErrorHandler func(key string, err error)
ChangeHandler func(key string, oldData, newData interface{})
IsSame func(key string, oldDate, newData interface{}) bool
}
当生成一个Asyncache的时候,就会同时生成一个协程执行对应的更新函数
func ( c *Asyncache) refresher() {
ch := time.Tick(c.opt.RefreshDuration)
for {
select {
case <- c.exit:
return
case <- ch:
c.Refresh()
}
}
}
func (c *Asynccache) refresh() {
oldDate := c.Dump()
for key, oldVal := range oldDate {
newVal, err := c.opt.Fetcher(key)
if err != nil {
c.opt.ErrHandler(key, err)
continue
}
if c.opt.IsSame != nil && !c.opt.IsSame(key, oldVal, newVal) {
if c.opt.ChangeHandler != nil {
go c.opt.ChangeHandler(key, oldVal, newVal)
}
}
c.data.Store(key, newVal)
}
}
重要的get函数:
func (c *AsyncCache) Get(key string, defaultVal interface{}) interface{} {
val, ok := c.data.load(key)
if ok {
return val
}
if !c.opt.BlockFirst {
c.data.Stroe(key, defaultVal)
return defaultVal
}
// 避免启动时,对一个key进行大量的并发请求
val, err := c.sfg.Do(key, func()(interface{], error) {
return c.opt.Fetcher(key)
})
if err != nil { ......}
c.data.Store(key, val)
return val
}
kitc 的服务发现使用了一种消息传播结构体,对系统内的消息进行注册与执行:
type Handler func(event *KitEvent)
// 标示kitc内部一些自动化触发的事件
type KitEvent struct {
Name string
time Time
Detail string
Extra map[string]interface{}
}
type EventBus interface{
Watch(event string, handler Handler)
UnWatch(evnet string, handler Handler)
Dispatch(event *KitEvent)
}
type eventBus struct {
callBacks sync.Map
}
func (c *evenBus) Watch(event string, handler Handler) {
var handlers []Handler
if actual, ok := c.callBacks.Load(event); ok {
handlers = actual.([]Handler)
}
handlers = append(handlers, handler)
s.callBacks.Store(event, handlers)
}
func ( c *EventBus) Dispatch(event *KitcEvent) {
if acutal, ok := c.callBacks.Load(event); ok {
for _, h := range actual.([]Handler) {
go h(event)
}
}
}
kitc 的服务发现中对服务信息进行缓存:
type KitcDiscover struct {
eventBus KitEvent.EventBus
discover discovery.ServiceDiscover
cache *cache.Asyncache
policy *discvery.DiscoversyPolicy
}
KitcDiscver 实现了Options中的Fetcher等函数,这些函数设定了缓存Asyncache的行为:
func (c *KitcDiscover) fetch(key string) (interface{}, error) {
tmp := string.Split(key, ":")
if len(tem) != 4 {...}
service, idc, cluster, env := tmp[0],tmp[1],tmp[2],tmp[3]
ins, err := c.discover.Discover(service, idc)
if err != nil {...}
filtered := c.policy.Filter(ins, cluster, env)
......
}