前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WebSocket降级策略(三)

WebSocket降级策略(三)

原创
作者头像
4cos90
发布2022-08-10 01:02:52
5410
发布2022-08-10 01:02:52
举报
文章被收录于专栏:随想随想

CacheServer优化

这时候我发现了SynCache这个接口并不需要被外部调用,在Get Set方法时,已经在内部处理了缓存。

因此我们可以直接把这个接口干掉。

针对问题二,我们可以抽象出一个Clear的接口,入参是key,清理掉cache中所有key值的消息。

代码语言:javascript
复制
type CacheServer interface {
	Get(key string) ([]cacheValue, error)
	Set(key string, kvalue interface{}) error
	Clear(key string) error
	GetAll() ([]cacheValue, error)
}
代码语言:javascript
复制
func (s *cache) Clear(key string) error {
	s.GetLastWriterFromRedis()
	s.clearOutTimeCache()
	newCache := make([]cacheValue, 0)
	for i := 0; i < len(s.Cache); i++ {
		if s.Cache[i].Key != key {
			newCache = append(newCache, s.Cache[i])
		}
	}
	s.Cache = newCache
	s.SetLastWriterToRedis()
	return nil
}

那我们在业务层当中的调用也可以修改为Get,然后Clear。这样做有没有问题呢?

当然抽象出这个方法是没有问题的。但是在业务层中连续调用就有问题了。因为业务层可能存在并发的读写,cache在读写时都是需要加锁的(当然我现在还没加上),在业务层调用时,在Get之后,Clear之前锁是会放开的。此时如果有消息写入,并且key正好是clear的key。那么消息就会在没有被get到的情况下clear掉。

好那么说到锁我们就把锁加上。一样加入cache,New的时候加上。对外提供的Get Set Getall,Clear四个方法里加上,defer解锁。需要注意一下不要加进GetLastWriterFromRedis这些内部互相调用的方法里去,不然就直接死锁了。

代码语言:javascript
复制
type cache struct {
	Name        string
	Cache       []cacheValue
	LocalIp     string
	Timeout     time.Duration
	RedisClient RedisServer
	Lock        sync.Mutex
}
代码语言:javascript
复制
func NewCache(name string, timeout time.Duration, localIp string, RedisClient RedisServer) CacheServer {
	return &cache{
		Name:        name,
		Cache:       make([]cacheValue, 0),
		Timeout:     timeout,
		RedisClient: RedisClient,
		LocalIp:     localIp,
		Lock:        sync.Mutex{},
	}
}
代码语言:javascript
复制
	s.Lock.Lock()
	defer s.Lock.Unlock()

Config实现

既然上面说在业务层实现会有漏洞。那么我们可以想到在cacheServer内部引入config,在New的时候传入config,这样就能满足不同业务逻辑的需求。

为了满足可选Config传入的需求,我们定义了一种配置Config的函数。

代码语言:javascript
复制
type option func(*cache)

在我们的NewCache时,必选参数还是用原来的方式传入,可选参数先给上一个缺省值,并由最后传入的options方法来修改。

代码语言:javascript
复制
func NewCache(name string, timeout time.Duration, localIp string, RedisClient RedisServer, options ...option) CacheServer {
	newCache := cache{
		Name:               name,
		Cache:              make([]cacheValue, 0),
		Timeout:            timeout,
		RedisClient:        RedisClient,
		LocalIp:            localIp,
		Lock:               sync.Mutex{},
		ClearAfterGet:      false,
		SyncFromOtherCache: false,
	}
	for _, option := range options {
		option(&newCache)
	}
	return &newCache
}

这边我们有几个可选参数就定义几个方法供外部调用。

代码语言:javascript
复制
func DialClearAfterGet(sign bool) option {
	return func(c *cache) {
		c.ClearAfterGet = sign
	}
}

func DialSyncFromOtherCache(sign bool) option {
	return func(c *cache) {
		c.SyncFromOtherCache = sign
	}
}

在main函数中修改

代码语言:javascript
复制
MessageCache := cacheServer.NewCache("MessageCache", time.Second*5, "http://localhost:8080", RedisClient, cacheServer.DialClearAfterGet(true), cacheServer.DialSyncFromOtherCache(true))

这样我们的config配置就基本上完成了。在包内加上对config的判断。

代码语言:javascript
复制
func (s *cache) Get(key string) ([]cacheValue, error) {
	s.Lock.Lock()
	defer s.Lock.Unlock()
	s.getLastWriterFromRedis()
	s.clearOutTimeCache()
	rlt := make([]cacheValue, 0)
	if s.ClearAfterGet {
		newCache := make([]cacheValue, 0)
		for i := 0; i < len(s.Cache); i++ {
			if s.Cache[i].Key == key {
				rlt = append(rlt, s.Cache[i])
			} else {
				newCache = append(newCache, s.Cache[i])
			}
		}
		s.Cache = newCache
	} else {
		for i := 0; i < len(s.Cache); i++ {
			if s.Cache[i].Key == key {
				rlt = append(rlt, s.Cache[i])
			}
		}
	}
	s.setLastWriterToRedis()
	return rlt, nil
}

这里直接把对配置项的处理写到最后调用的方法里了。避免配置项的判断都堆叠在一起。

代码语言:javascript
复制
func (s *cache) getLastWriterFromRedis() {
	if s.SyncFromOtherCache {
		IP, err := s.RedisClient.Get(s.Name)
		if err == nil {
			if IP != s.LocalIp {
				resp, err := http.Get(IP + "/syncCache")
				if err != nil {
					return
				}
				defer resp.Body.Close()
				body, _ := ioutil.ReadAll(resp.Body)
				var res []cacheValue
				json.Unmarshal([]byte(body), &res)
				s.Cache = res
			}
		}
	}
}

func (s *cache) setLastWriterToRedis() error {
	if s.SyncFromOtherCache {
		return s.RedisClient.Set(s.Name, s.LocalIp)
	} else {
		return nil
	}
}

好了跑一下代码试了一下没有问题。

这样我们一个简单的Config配置的功能也实现了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • CacheServer优化
  • Config实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档