网站哪类业务建设投入会带来间接收益,免费做视频的软件app,开源镜像网站开发,wordpress仿 模板前言
下面是我根据业务需求画了一个架构图#xff0c;没有特别之处#xff0c;很普通#xff0c;都是我们常见的中间件#xff0c;都是一些幂等性GET 请求。有一个地方很有意思#xff0c;从service 分别有10000 qps 请求到Redis#xff0c;并且它们的key 是一样的。这样…前言
下面是我根据业务需求画了一个架构图没有特别之处很普通都是我们常见的中间件都是一些幂等性GET 请求。有一个地方很有意思从service 分别有10000 qps 请求到Redis并且它们的key 是一样的。这样一个简单的业务Redis 就需要承担20000qps此时监控 redis 我们会发现有一个峰值如果Redis 没有值这些流量会穿透到PostgreSQL监控PostgreSQL也有一个峰值。于是我在想如果在Service 层我们我们让流量排队阻塞只需一个流量去请求Redis 获取数据那么所有的key 不都获取数据了吗对于Redis 和 PostgreSQL 来说流量就是1那么流量的收缩比就是20000:2也就是10000:1这是很高效的。 描述了那么多这个方案可以用来解决缓存击穿实际上它超越了这个问题。这个方案这么好我们该怎么实现了在这里我们用 Singleflight 去实现。接下来我将着重讲解 Singleflight 。
一、什么是Singleflight
字面解释就是单飞模式我通常叫它归并回源策略。它的主要目的是确保在任何给定时间内对昂贵或重复操作的调用只有一次。当多个 goroutine 请求相同资源时singleflight 可确保函数只执行一次并且结果由所有调用者共享。这种模式在不适合缓存或结果会经常变化的情况下特别有用。
二、Singleflight是如何工作的
单飞机制非常简单它提供了一个Group每个Group 代表一类工作我们需要做的就是这类工作中需要防止重复操作。 首次启动第一次请求资源时会初始化Group并且对计算资源函数进行调用 并发请求处理如果在初始请求仍在处理过程中又有其他对同一资源的请求提出单飞会保留这些调用。 结构共享一旦第一个请求完成结果将返回给原始调用者并同时与所有其他等待的调用者共享。 防止重复整个过程中要保证一类工作函数只会调用一次防止重复工作。
三、Singleflight 的优点 效率通过确保只有一个请求完成工作可避免对服务和数据库造成不必要的负载。 简单singleflight 抽象了处理对同一资源的并发请求的复杂性使您的代码更简洁、更易懂。 资源优化 它有助于优化内存和 CPU 的使用因为相同的计算不会重复多次。
四、Singleflight 的redis 请求 wg : sync.WaitGroup{}wg.Add(10)for i : 0; i 10; i {go func() {defer wg.Done()v, _, _ : group.Do(key1, func() (interface{}, error) {client : redis.NewClient(redis.Options{Addr: 127.0.0.1:6379},)a : client.Get(context.Background(), a)return a, nil})fmt.Println(v)}()
}wg.Wait()
output: 用wireshark 抓包可以看到 只有一个客户端发起了请求所有的客户端都返回了数据
五、Singleflight 方法讲解 Do这个方法执行一个函数并返回函数执行的结果。你需要提供一个 key对于同一个 key在同一时间只有一个在执行同一个 key 并发的请求会等待。第一个执行的请求返回的结果就是它的返回结果。函数 fn 是一个无参的函数返回一个结果或者 error而 Do 方法会返回函数执行的结果或者是 errorshared 会指示 v 是否返回给多个请求。 DoChan类似 Do 方法只不过是返回一个 chan等 fn 函数执行完产生了结果以后就能从这个 chan 中接收这个结果 Forget告诉 Group 忘记这个 key。这样一来之后这个 key 请求会执行 f而不是等待前一个未完成的 fn 函数的结果。
Do 和 DoChan 最终都会调用 doCall只不过 Do 是同步如果一个key 处理的很慢就可能造成阻塞DoChan 是异步调用的这个地方一定要注意不同的key 的数据每个不同的key 都要开一个goroutine 数量多的话就会造成系统的内存OM掉了。这个地方大家一定要注意了建议用协程池。