// utils.go
package main

import (
"fmt"
"math/rand"
"sync"
"time"
)

// 抢红包任务结构体
type task struct {
id uint32 // 表示红包id
callback chan uint // 表示返回的金额
}

const TaskNums = 5 // 并发任务数

func main() {
id, money, num := 88, 100, 10

  // 发红包  红包ID 金额  数量  
SetRedPack(id, money, num)

// 构造抢红包任务通道
for i := 0; i < TaskNums; i++ {
TasksChan[i] = make(chan task)
}

// 开启goroutine监听任务通道
go GetPackageMoney(TasksChan)

var wg sync.WaitGroup
// 抢红包
for i := 0; i < num+5; i++ {
wg.Add(1)
go GetRedPack(id, &wg)
}
wg.Wait()
}

var (
// 记录红包已抢总金额
sum uint = 0
TasksChan = make([]chan task, TaskNums) // 任务通道
random_seed = rand.New(rand.NewSource(time.Now().UnixNano())) // 分配的随机数
// 使用并发安全字典表示红包集
// key: uint32 为红包id
// value: []uint 为红包内随机金额的列表
packageList = new(sync.Map)
mu sync.Mutex
)

// 发红包方法
func SetRedPack(id, money, num int) {
left_money, left_num := money, num
money_list := make([]uint, num)
for left_money > 0 {

if left_num == 1 {
money_list[num-1] = uint(left_money)
break
}

if left_num == left_money {
for i := 0; i < left_num; i++ {
money_list[i] = 1
}
break
}

rMoney := int(2 * float64(left_money) / float64(left_num))
// 保证分配金额>=1
rand_m := random_seed.Intn(rMoney) + 1
money_list[num-left_num] = uint(rand_m)
left_money -= rand_m
left_num--
}
packageList.Store(uint32(id), money_list)
}

// 监听任务通道方法
func GetPackageMoney(taskschan []chan task) {
for {
select {
case t := <-taskschan[0]:
GetMoney(t)
case t := <-taskschan[1]:
GetMoney(t)
case t := <-taskschan[2]:
GetMoney(t)
case t := <-taskschan[3]:
GetMoney(t)
case t := <-taskschan[4]:
GetMoney(t)
default:
continue
}
}
}

func GetMoney(t task) {
id := t.id
l_money_list, ok := packageList.Load(id)

if ok && l_money_list != nil {
list := l_money_list.([]uint)
r_index := random_seed.Intn(len(list))
money := list[r_index]

// 更新红包金额列表信息
if len(list) > 1 {

if r_index == 0 {
packageList.Store(id, list[1:])
} else if r_index == len(list)-1 {
packageList.Store(id, list[:1])
} else {
packageList.Store(id,
append(list[:r_index], list[r_index+1:]...))
}

} else {
packageList.Delete(id)
}

t.callback <- money
} else {
t.callback <- 0
}
}

func GetRedPack(id int, wg *sync.WaitGroup) {
defer wg.Done()

// 并发安全字典取 value
list1, ok := packageList.Load(uint32(id))
list := list1.([]uint)
// 没取到就代表红包不存在
if !ok || len(list) < 1 {
fmt.Printf("红包不存在,id=%d\n", id)
}
// 构造抢红包任务
callback := make(chan uint)
t := task{
id: uint32(id),
callback: callback,
}
TasksChan[sum%TaskNums] <- t
money := <-t.callback
if money <= 0 {
fmt.Printf("很遗憾,你没有抢到红包\n")
} else {
fmt.Printf("恭喜你抢到一个红包,金额为:%d\n", money)
mu.Lock()
defer mu.Unlock()
sum += money
}
}

Go - 高并发抢到红包实现的更多相关文章

  1. PHP 高并发、抢票、秒杀 解决方案

    对于抢票.秒杀这种业务,我说说自己对这种高并发的理解吧,这里提出个人认为比较可行的几个方案:方案一:使用队列来实现可以基于例如MemcacheQ等这样的消息队列,具体的实现方案这么表述吧比如有100张 ...

  2. asp.net c# 通过消息队列处理高并发请求(以抢小米手机为例)

    网站面对高并发的情况下,除了增加硬件, 优化程序提高以响应速度外,还可以通过并行改串行的思路来解决.这种思想常见的实践方式就是数据库锁和消息队列的方式.这种方式的缺点是需要排队,响应速度慢,优点是节省 ...

  3. 利用redis + lua解决抢红包高并发的问题

    抢红包的需求分析 抢红包的场景有点像秒杀,但是要比秒杀简单点.因为秒杀通常要和库存相关.而抢红包则可以允许有些红包没有被抢到,因为发红包的人不会有损失,没抢完的钱再退回给发红包的人即可.另外像小米这样 ...

  4. java高级精讲之高并发抢红包~揭开Redis分布式集群与Lua神秘面纱

    java高级精讲之高并发抢红包~揭开Redis分布式集群与Lua神秘面纱 redis数据库 Redis企业集群高级应用精品教程[图灵学院] Redis权威指南 利用redis + lua解决抢红包高并 ...

  5. JAVA构建高并发商城秒杀系统——架构分析

    面试场景 我们打算组织一个并发一万人的秒杀活动,1元秒杀100个二手元牙刷,你给我说说解决方案. 秒杀/抢购业务场景 商品秒杀.商品抢购.群红包.抢优惠劵.抽奖....... 秒杀/抢购业务特点 秒杀 ...

  6. Java Web(1)高并发业务

    互联网无时无刻不面对着高并发问题,例如商品秒杀.微信群抢红包.大麦网抢演唱会门票等. 当一个Web系统,在一秒内收到数以万计甚至更多的请求时,系统的优化和稳定是至关重要的. 互联网的开发包括Java后 ...

  7. Springcloud 微服务 高并发(实战1):第1版秒杀

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列之15 [博客园总入口 ] 前言 前言 疯狂创客圈(笔者尼恩创建的高并发研习社群)Springcloud 高并发系列文章,将为大家介绍三个版 ...

  8. 高并发&高可用系统的常见应对策略

    解耦神器:MQ MQ是分布式架构中的解耦神器,应用非常普遍.有些分布式事务也是利用MQ来做的.由于其高吞吐量,在一些业务比较复杂的情况,可以先做基本的数据验证,然后将数据放入MQ,由消费者异步去处理后 ...

  9. 高并发&高可用系统的常见应对策略 秒杀等-(阿里)

    对于一个需要处理高并发的系统而言,可以从多个层面去解决这个问题. 1.数据库系统:数据库系统可以采取集群策略以保证某台数据库服务器的宕机不会影响整个系统,并且通过负载均衡策略来降低每一台数据库服务器的 ...

  10. JAVA架构师眼中的高并发架构,分布式架构 应用服务器集群

    前言 高并发经常会发生在有大活跃用户量,用户高聚集的业务场景中,如:秒杀活动,定时领取红包等. 为了让业务可以流畅的运行并且给用户一个好的交互体验,我们需要根据业务场景预估达到的并发量等因素,来设计适 ...

随机推荐

  1. 接口自动化(TestNG)

    数据驱动概念: 用户输入输出数据来判断测试用例是否通过从而验证需求的测试. 一.接口自动化框架搭建(TestNG数据驱动) ---parameter 关键代码: <?xml version=&q ...

  2. cin和缓存区问题

    稍微记录一下今天刷题遇到的C++问题 看到使用while(cin >> s);来读取最后一个字符串.百度了一下发现cin以空格,制表符和回车为终止依据.也就是说我输入"abc 1 ...

  3. HDFS、Ceph、GFS、GPFS、Swift、Lustre……容器云选择哪种分布式存储更好?

    HDFS.Ceph.GFS.GPFS.Swift.Lustre--容器云选择哪种分布式存储更好?-51CTO.COM 容器云在使用分布式存储时,HDFS.CEPH.GFS.GPFS.Swift等分布式 ...

  4. 解决CentOS 7.x虚拟机无法上网的问题

    参考地址:https://blog.csdn.net/weixin_43317914/article/details/124770393 1.关闭虚拟机 2.打开cmd,查看本机dns 3.打开虚拟机 ...

  5. Shell脚本结构化-控制流

    脚本结构化命令 上一章给出的那些 shell 脚本里,shell 按照命令在脚本中出现的顺序依次进行处理.对顺序操作来说,这已经足够了,因为在这种操作环境下,你想要的就是所有的命令按照正确的顺序执行. ...

  6. vue 项目页面刷新router-view控制

    vue项目开发过程中,需要在页面提交表单信息之后清空还原,即恢复页面初始状态,第一想法就是对当前页面刷新,重新加载. 想起location.reload()方式和this.$router.go(0)方 ...

  7. Mysql 5.7 内存配置 (未完成)

    innodb_buffer_pool_size = 4024024024 innodb 引擎,用于 缓存数据和索引

  8. (6) JavaScript - Math对象与日期对象

    1 认识对象 概念:对象就是一种类型,一种引用类型,而对象则是引用类型的实例.在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起,通常它也被称作类. 面向过程思想:只考虑过程 ...

  9. ABP vNext微服务架构详细教程——镜像推送

    1. Jenkins搭建 为实现容器化部署,我们需要将代码打包成镜像并推送至容器仓库,我们可以选择自建容器仓库或者使用公有云服务商提供的镜像仓库.这里我们使用阿里云提供的免费镜像仓库. 代码打包和镜像 ...

  10. ASP.NET在Repeater中使用Button控件报错

    普通Button在这里会报错,小编找了一天也没有解决这个问题, 这里可以换做LinkButton或者ImageButton替换普通的Button