// 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. AVD文件转移到非系统盘

    AVD文件默认是生成在C:\Users\用户名\.android\avd目录下面的,而AVD文件非常大,可以用下面的方法将AVD文件转移到其他盘中. 1. 将每个模拟器对应的***.avd文件夹的内容 ...

  2. 20200922--计算矩阵边缘元素之和(奥赛一本通P91 3二维数组)

    输入一个整数矩阵,计算位于矩阵边缘的元素之和.所谓矩阵边缘的元素,就是就一行和最后一行以及第一列和最后一列的元素. 输入; 第一行分别为矩阵的行数m和列数n(m<100,n<100),两者 ...

  3. Jmeter-接口测试(三)

    一.jmeter接口关联 1.正则表达式实现接口关联 正则表达式可以这样测试 2.jsonpath表达式实现接口关联(只能作用于返回值是token的) 从根目录开始找$.token 从任意目录开始找( ...

  4. python扑克牌

    import random import operator def auto(): pokers=[] poker=[] for i in ['','','','']: for j in ['A',' ...

  5. input设置自定义属性,并获取值。

    HTML代码: JavaScript代码: 因为busCode不是input标签原生的属性,所以不能使用"点"操作. 非标准属性,要用:obj.getAttribute(" ...

  6. EF OwnsOne 主键不自增

    menu public class Menu { /// <summary> /// id /// </summary> [Key, DatabaseGeneratedAttr ...

  7. css cursor: url() 使用火狐浏览器问题,鼠标没有效果

    在火狐浏览器问题使用cursor: url():鼠标没有效果,需要给使用标签添加一个height

  8. vue 组件之间事件触发($emit)与event Bus($on)的用法说明

    组件之间事件触发 新增按钮组件: 操作按钮组合组件: 此时有个需求就是,无论是哪个按钮,如果改变了列表中的数据,列表需要实时更新数据. 此时就需要用到组件间的事件触发. 父子组件之间事件触发可以使用$ ...

  9. centos 发送邮件

    安装mailx yum install -y mailx 配置邮件服务器信息 vi /etc/mail.rc set from=xxxx@yyy.com set smtp=smtp.yyy.com s ...

  10. python基础简记

    pyhon 2 兼容 python 3 : 导入__future__包: 一些命名规定:以单划线开头的表示不能直接访问类属性,需要访问接口:以双划线开头的表示类的私有成员:以双划线开头和结尾的表示py ...