有问必答

摘要

本文将介绍如何使用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并发编程实践!的更多相关文章

  1. PHP秒杀系统-高并发高性能的极致挑战

    慕课网实战教程后端:1.java c++算法与数据结构2.java Spring Boot带前后端 渐进式开发企业级博客系统3.java Spring Boot企业微信点餐系统4.java Sprin ...

  2. LibreOJ2097 - 「CQOI2015」任务查询系统

    Portal Description 给出\(n(n\leq10^5)\)个任务,和总时间范围\(m(m\leq10^5)\).每个任务有开始/结束时间\(s_i,e_i(1\leq s_i \leq ...

  3. PHP秒杀系统 高并发 高性能的极致挑战 下载

    第1章 课程介绍 秒杀系统在各种网站和应用中经常会用到.本课程从基本的系统设计和基础功能开始教导大家用PHP来设计和实现秒杀系统,并且为海量并发提供更高级的技术方案和实现手段. 第2章 系统技术选型分 ...

  4. 「CQOI2015」任务查询系统

    「CQOI2015」任务查询系统 传送门 好像也是板子题??? 区间修改,单点查询,考虑差分. 然后每次查询时就直接在对应的主席树上二分即可. 参考代码: #include <cstdio> ...

  5. [Java 并发] Java并发编程实践 思维导图 - 第一章 简单介绍

    阅读<Java并发编程实践>一书后整理的思维导图.

  6. [Java 并发] Java并发编程实践 思维导图 - 第二章 线程安全性

    依据<Java并发编程实践>一书整理的思维导图.

  7. 并发编程实践五:ReentrantLock

    ReentrantLock是一个可重入的相互排斥锁,实现了接口Lock,和synchronized相比,它们提供了同样的功能.但ReentrantLock使用更灵活.功能更强大,也更复杂.这篇文章将为 ...

  8. 并发编程实践三:Condition

    Condition实例始终被绑定到一个锁(Lock)上.Lock替代了Java的synchronized方法,而Condition则替代了Object的监视器方法,包含wait.notify和noti ...

  9. 读Java并发编程实践中,向已有线程安全类添加功能--客户端加锁实现示例

    在Java并发编程实践中4.4中提到向客户端加锁的方法.此为验证示例,写的不好,但可以看出结果来. package com.blackbread.test; import java.util.Arra ...

  10. [Java 并发] Java并发编程实践 思维导图 - 第四章 对象的组合

    依据<Java并发编程实践>一书整理的思维导图. 第一部分: 第二部分:

随机推荐

  1. [go笔记]websocket入门

    简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议.WebSocket让客户端和服务端之间的数据交换变得非常简单,且允许服务器主动向客户端推送数据,并且之后客户端和服务端所有的通信都 ...

  2. 使用docker-compose搭建redis-cluster集群

    前言 如果你也因为centos的gcc版本落后导致redis编译安装不通过而烦恼,大可以试一试用docker去搭建redis集群.本文以单节点搭建redis-cluster三主三从为例. ip:192 ...

  3. 群晖DS218+部署PostgreSQL(docker)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 起因是懒 最近在开发中要用到PostgreSQL数据库 ...

  4. 【Qt6】工具提示以及调色板设置

    工具提示即 Tool Tip,当用户把鼠标移动到某个UI对象上并悬停片刻,就会出现一个"短小精悍"的窗口,显示一些说明性文本.一般就是功能描述,让用户知道这个XX是干啥用的. 在 ...

  5. Python操作Redis大全

    一.字符串 string Python操作Redis的redis模块对字符串(string)的主要操作函数包括:SET.GET.GETSET.SETEX.SETNX.MSET.MSETNX.INCR( ...

  6. Vue用v-bind给标签属性赋值 src, href...

    给属性渲染数据不能使用 {{name}} 标记, 请使用 v-bind:属性名称="name" name是json数据键值对中的键名, 请配合下面JS代码片食用 HTML < ...

  7. 拓展kmp的应用

    Smiling & Weeping ---- 我与月亮,进行了一次深夜谈话 它与我谈论太阳,而我与它谈论你. 题目链接:P3435 [POI2006] OKR-Periods of Words ...

  8. Salesforce LWC学习(四十五) lwc支持Console App控制Tab了

    本篇参考:https://help.salesforce.com/s/articleView?id=release-notes.rn_lwc_workspaceAPI.htm&release= ...

  9. MFAN论文阅读笔记(待复现)

    论文标题:MFAN: Multi-modal Feature-enhanced Attention Networks for Rumor Detection 论文作者:Jiaqi Zheng, Xi ...

  10. Solution -「POJ 1322」Chocolate

    Description Link. 包里有无穷多个巧克力,巧克力有 \(c\) 种颜色,每次从包里拿出不同颜色巧克力的概率都是相等的,桌面的巧克力不允许颜色相同,若某次拿出的巧克力与桌上的巧克力颜色相 ...