栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

防止缓存击穿-单飞模式 golang

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

防止缓存击穿-单飞模式 golang

缓存击穿是指在缓存失效的时刻,大量的请求过来,回被同时打到下游,给下游造成瞬时压力,解决的办法就是只允许少量的请求打到下游,然后回填到缓存,其他请求还是从缓存拿数据。

解决办法一,使用分布式锁,确保只有一个请求获取到这个锁,其他请求去轮训缓存。特点是,为了保护下游,伤害了自己和redis,因为大量的请求停留在这里造成内存和CPU升高,但是大家都知道,接口服务器的性能不是问题,所以这个方案可行。

此处主要介绍方案二,那就是单飞模式 SingleFlight,在 golang 中有对应的实现 golang/groupcache ,它跟方案一的区别是讲分布式的锁改成了内存锁,没有了轮训redis这个操作;其次就是打到下游的请求要多一点,但是不构成压力。

go get -u github.com/golang/groupcache

先来模拟缓存击穿的情况

package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/golang/groupcache/singleflight"
)

var cache sync.Map
var wg sync.WaitGroup
var cachekey = "key"
var cacheval = "test"
var g singleflight.Group

func main() {
	for i := 0; i < 20; i++ {
		go action()
	}
	wg.Wait()
}

func action() {
	wg.Add(1)
	c, ok := cache.Load(cachekey)
	if !ok {
		SetCache(cachekey, cacheval)
		c = cacheval
	}
	fmt.Println(c)
	wg.Done()
}

func SetCache(key string, val string) bool {
	defer fmt.Println("SetCache...")
	time.Sleep(10 * time.Millisecond)
	cache.Store(key, val)
	return true
}

go run main.go

从打印信息可以看出SetCache被调用了多次。

下面使用单飞模式

func action2() {
	wg.Add(1)
	c, ok := cache.Load(cachekey)
	if !ok {
		fn := func() (interface{}, error) {
			SetCache(cachekey, cacheval)
			return cacheval, nil
		}
		cc, _ := g.Do(cachekey, fn)
		c = cc.(string)
	}
	fmt.Println(c)
	wg.Done()
}

从打印信息可以看出SetCache被调用了一次。

源码部分

// Package singleflight provides a duplicate function call suppression
// mechanism.
package singleflight

import "sync"

// call is an in-flight or completed Do call
type call struct {
	wg  sync.WaitGroup
	val interface{}
	err error
}

// Group represents a class of work and forms a namespace in which
// units of work can be executed with duplicate suppression.
type Group struct {
	mu sync.Mutex       // protects m
	m  map[string]*call // lazily initialized
}

// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
	g.mu.Lock()
	if g.m == nil {
		g.m = make(map[string]*call)
	}
	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()

	return c.val, c.err
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/906984.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号