防止缓存击穿-单飞模式 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
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
mingw-w64是一个开源的GNU编译器集合,它提供了在Windows平台上编译和运行C和C++程序所需的工具。而Golang(也称为Go)是一种开源的编程语言,最初由Google开发,现在由Go的开发者社区继续维护。 在mingw-w64环境下进行Golang的交叉编译,主要是为了在Windows平台上生成能够在其他操作系统运行的可执行文件。这可以帮助开发者更方便地在Windows上进行跨平台的开发和测试。 为了使用mingw-w64进行Golang交叉编译,我们可以按照以下步骤进行操作: 1. 首先,我们需要在Windows上安装mingw-w64工具链。可以去mingw-w64项目的官方网站下载,选择适合自己操作系统的版本,并按照指示进行安装。 2. 安装完成后,需要配置一些环境变量。将mingw-w64的bin目录添加到系统的PATH变量中,以便在命令行中可以直接访问编译器。 3. 在命令行中,进入到需要进行交叉编译的Golang项目所在的目录。 4. 执行以下命令进行交叉编译: ``` GOOS=target GOARCH=arch CGO_ENABLED=1 CC=mingw-w64-gcc go build -o output.exe ``` 其中,target是目标操作系统(例如windows、linux、darwin等),arch是目标处理器架构(例如amd64、386等)。 5. 执行完以上命令后,将生成一个名为output.exe的可执行文件,该文件即为交叉编译生成的可在目标操作系统上运行的文件。 通过以上步骤,我们可以在mingw-w64环境下使用Golang进行交叉编译,生成适用于其他操作系统的可执行文件。这样,开发者可以更方便地进行跨平台的开发和部署。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值