「有问必答」秒杀系统 Go并发编程实践!
有问必答

摘要
本文将介绍如何使用Go语言的并发原语来构建一个简单的高并发秒杀系统。
我们将使用Go语言的原生库和一些常见的技术手段,包括互斥锁、通道、计数器等,来解决并发访问和数据一致性的问题。
本文只是一个简单的示例,重点是Go语言并发原语在业务场景中的应用。
在实际应用中,还需要考虑数据库事务、分布式锁、限流等问题。我之前也写过一篇文章,附在了文末。
1. 引言
秒杀系统是一种高并发场景下的特殊应用,需要处理大量的并发请求和保证数据的一致性。本文将介绍如何使用Go语言的并发原语来构建一个高并发的秒杀系统,以满足用户的需求并保证系统的稳定性。
2. 架构设计
我们的秒杀系统将采用经典的客户端-服务器架构。客户端发送秒杀请求,服务器处理请求并更新库存。为了保证系统的高并发性能,我们将使用以下技术和原语:
- 互斥锁(
sync.Mutex):用于保护共享资源的并发访问。 - 通道(
channel):用于协程间的通讯。 - 计数器(
sync.WaitGroup):用于等待所有请求完成。
3. 实现步骤
下面是我们实现秒杀系统的关键步骤:
3.1 初始化库存
在系统启动时,我们需要初始化商品的库存。
var stock = 100 // 商品库存
var mu sync.Mutex
3.2 处理秒杀请求
当客户端发送秒杀请求时,服务器需要处理请求并更新库存。
func handleRequest(user int) {
defer wg.Done()
if tryAcquireLock() {
if stock > 0 {
// 执行秒杀逻辑
stock--
fmt.Printf("用户%d秒杀成功,剩余库存:%d\n", user, stock)
} else {
fmt.Printf("用户%d秒杀失败,库存不足\n", user)
}
releaseLock()
} else {
fmt.Printf("用户%d未获取到锁,秒杀失败\n", user)
}
}
3.3 并发控制和等待
为了控制并发请求的数量,我们使用计数器和通道来限制并发度。
var wg sync.WaitGroup
func main() {
for i := 1; i <= 1000; i++ {
wg.Add(1)
go handleRequest(i)
}
wg.Wait()
}
3.4 互斥锁和并发安全
为了保证并发访问的安全性,我们使用互斥锁来保护共享资源的访问。
注意:TryLock()是go1.18才引入的
func tryAcquireLock() bool {
return mu.TryLock()
}
func releaseLock() {
mu.Unlock()
}
4. 完整代码
package main
import (
"fmt"
"sync"
)
//后面开启了1000个goroutine,所以这里channel的缓冲区设置成了1000
var ch = make(chan bool, 1000)
type Product struct {
sync.Mutex
stock int64 // 商品库存
}
func main() {
p := Product{stock: 1000}
for i := 1; i <= 1000; i++ {
go p.handleRequest(i)
}
<-ch
}
func (p *Product) handleRequest(user int) {
if p.tryAcquireLock() {
if p.stock > 0 {
// 执行秒杀逻辑
p.stock--
fmt.Printf("用户%d秒杀成功,剩余库存:%d\n", user, p.stock)
} else {
fmt.Printf("用户%d秒杀失败,库存不足\n", user)
}
//这里是不可以使用defer的,因为可能会加锁失败,unlock一个不存在的锁
p.releaseLock()
} else {
fmt.Printf("用户%d未获取到锁,秒杀失败\n", user)
}
}
func (p *Product) tryAcquireLock() bool {
//p.TryLock() 方法用于尝试获取锁,如果成功获取到锁,则相当于执行了 Lock() 操作,即加锁成功。
return p.TryLock()
}
func (p *Product) releaseLock() {
p.Unlock()
ch <- true
}
解析代码
var ch = make(chan bool, 1000):后面开启了1000个goroutine,所以这里channel的缓冲区设置成了1000
p.releaseLock():这里是不可以使用defer的,因为可能会加锁失败,unlock一个不存在的锁
p.TryLock():方法用于尝试获取锁,如果成功获取到锁,则相当于执行了 Lock() 操作,即加锁成功。
5. 运行结果

6. 总结
通过使用Go语言的并发原语,我们成功地构建了一个高并发的秒杀系统。
使用互斥锁和计数器等原语,我们实现了并发控制、数据一致性和并发安全。这些原语帮助我们解决了高并发场景下的并发访问问题,并保证了系统的稳定性和性能。
本文只是一个简单的示例,实际的秒杀系统可能涉及更多的业务逻辑和并发控制。
在实际应用中,还需要考虑数据库事务、分布式锁、限流等问题。因此,建议根据实际需求和场景进行更详细的设计和实现。
我之前也有写万字长文总结过,感兴趣的朋友欢迎查看:万字详解:秒杀系统设计
一起学习
欢迎大家关注我的账号,你的支持,是我更文的最大动力!
也欢迎关注我的公众号: 程序员升职加薪之旅,领取更多Go学习和面试资料。
微信号:wangzhongyang1993
「有问必答」秒杀系统 Go并发编程实践!的更多相关文章
- PHP秒杀系统-高并发高性能的极致挑战
慕课网实战教程后端:1.java c++算法与数据结构2.java Spring Boot带前后端 渐进式开发企业级博客系统3.java Spring Boot企业微信点餐系统4.java Sprin ...
- LibreOJ2097 - 「CQOI2015」任务查询系统
Portal Description 给出\(n(n\leq10^5)\)个任务,和总时间范围\(m(m\leq10^5)\).每个任务有开始/结束时间\(s_i,e_i(1\leq s_i \leq ...
- PHP秒杀系统 高并发 高性能的极致挑战 下载
第1章 课程介绍 秒杀系统在各种网站和应用中经常会用到.本课程从基本的系统设计和基础功能开始教导大家用PHP来设计和实现秒杀系统,并且为海量并发提供更高级的技术方案和实现手段. 第2章 系统技术选型分 ...
- 「CQOI2015」任务查询系统
「CQOI2015」任务查询系统 传送门 好像也是板子题??? 区间修改,单点查询,考虑差分. 然后每次查询时就直接在对应的主席树上二分即可. 参考代码: #include <cstdio> ...
- [Java 并发] Java并发编程实践 思维导图 - 第一章 简单介绍
阅读<Java并发编程实践>一书后整理的思维导图.
- [Java 并发] Java并发编程实践 思维导图 - 第二章 线程安全性
依据<Java并发编程实践>一书整理的思维导图.
- 并发编程实践五:ReentrantLock
ReentrantLock是一个可重入的相互排斥锁,实现了接口Lock,和synchronized相比,它们提供了同样的功能.但ReentrantLock使用更灵活.功能更强大,也更复杂.这篇文章将为 ...
- 并发编程实践三:Condition
Condition实例始终被绑定到一个锁(Lock)上.Lock替代了Java的synchronized方法,而Condition则替代了Object的监视器方法,包含wait.notify和noti ...
- 读Java并发编程实践中,向已有线程安全类添加功能--客户端加锁实现示例
在Java并发编程实践中4.4中提到向客户端加锁的方法.此为验证示例,写的不好,但可以看出结果来. package com.blackbread.test; import java.util.Arra ...
- [Java 并发] Java并发编程实践 思维导图 - 第四章 对象的组合
依据<Java并发编程实践>一书整理的思维导图. 第一部分: 第二部分:
随机推荐
- 部署基于etcd的coredns集群
前言 现需要为公司搭建私有DNS,私有服务器都使用私有DNS的地址,便于访问内部自定义的域名.采用CoreDNS + ETCD方案部署,coredns和etcd都以三实例运行,etcd为集群模式,使用 ...
- python实现创建一个银行类,这个类实现了两个方法,第一个方法可以将用户信息写入到文件中,第二个方法可以读取文件中的用户信息出来
class bank: def user_info(self): a=input('请输入用户信息:') # 不写encoding = 'utf-8'中文会乱码 with open('info.txt ...
- Matlab机器人工具箱安装教程
参考以下博客 https://blog.csdn.net/AprilsHell/article/details/90722892
- 形象谈JVM-第一章-认识JVM
对jvm的历史不做过多介绍,感兴趣的同学可以去自行搜索. 我们直接以HotSpot VM(Virtual Machine)举例. why 为什么要有虚拟机? 举一个形象的例子:手机现在几乎是人手一台 ...
- SpringBoot项目统一处理返回值和异常
目录 简介 前期准备 统一封装报文 统一异常处理 自定义异常信息 简介 当使用SpringBoot开发Web项目的API时,为了与前端更好地通信,通常会约定好接口的响应格式.例如,以下是一个JSON格 ...
- 《SQL与数据库基础》01. SQL概述 · 分类
目录 SQL概述 SQL语法特征 SQL分类 本文以 MySQL 为例 SQL概述 SQL(Structured Query Language),结构化查询语言,用于访问和处理数据库的标准的计算机语言 ...
- 一文了解Validator库
1. 引言 github.com/go-playground/validator 是一个 Go 语言的库,用于对结构体字段进行验证.它提供了一种简单而灵活的方式来定义验证规则,并在验证过程中检查结构体 ...
- Elasticsearch之环境搭建
一.安装 elasticsearch -- 拉取镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:8.9.1 -- 创建 doc ...
- LVS DR模式负载均衡群集部署
LVS DR模式负载均衡群集部署 1 LVS-DR 模式的特点 直接路由直接路由 调节器仅作为客户端的访问入口,节点服务器的响应消息是直接返回客户端的,不需要经过调节器(与NAT模式的区别)节点服务器 ...
- 【源码】Vue.js 官方脚手架 create-vue 是怎么实现的?
Vue.js 官方脚手架 create-vue 是怎么实现的? 摘要 本文共分为四个部分,系统解析了vue.js 官方脚手架 create-vue 的实现细节. 第一部分主要是一些准备工作,如源码下载 ...