美文网首页
iris 抽奖实例6

iris 抽奖实例6

作者: 码农工号9527 | 来源:发表于2021-08-13 15:44 被阅读0次

大转盘

  • 固定几个奖品,不同的中奖概率或者总数量限制
  • 每一次转动抽奖,后端计算出这次抽奖的中奖情况,并返回对应的奖品信息

在测试项目目录下创建main.go文件,内容如下:

/**
 * 大转盘程序
 * curl http://localhost:8080/
 * curl http://localhost:8080/debug
 * curl http://localhost:8080/prize
 * 固定几个奖品,不同的中奖概率或者总数量限制
 * 每一次转动抽奖,后端计算出这次抽奖的中奖情况,并返回对应的奖品信息
 *
 * 线程不安全,因为获奖概率低,并发更新库存的冲突很少能出现,不容易发现线程安全性问题
 * 压力测试:
 * wrk -t10 -c100 -d5 "http://localhost:8080/prize"
 */
package main

import (
    "fmt"
    "github.com/kataras/iris/v12"
    "github.com/kataras/iris/v12/mvc"
    "strings"
    "time"
    "math/rand"
)

// 奖品中奖概率
type Prate struct {
    Rate int        // 万分之N的中奖概率
    Total int       // 总数量限制,0 表示无限数量
    CodeA int       // 中奖概率起始编码(包含)
    CodeB int       // 中奖概率终止编码(包含)
    Left int        // 剩余数
}
// 奖品列表
var prizeList []string = []string{
    "一等奖,火星单程船票",
    "二等奖,凉飕飕南极之旅",
    "三等奖,iPhone一部",
    "",                         // 没有中奖
}
// 奖品的中奖概率设置,与上面的 prizeList 对应的设置
var rateList []Prate = []Prate{
    Prate{1, 1, 0, 0, 1},
    Prate{2, 2, 1, 2, 2},
    Prate{5, 10, 3, 5, 10},
    Prate{100,0, 0, 9999, 0},
}

func newApp() *iris.Application {
    app := iris.New()
    mvc.New(app.Party("/")).Handle(&lotteryController{})
    return app
}

func main() {
    app := newApp()
    // http://localhost:8080
    app.Run(iris.Addr(":8080"))
}

// 抽奖的控制器
type lotteryController struct {
    Ctx iris.Context
}

// GET http://localhost:8080/
func (c *lotteryController) Get() string {
    c.Ctx.Header("Content-Type", "text/html")
    return fmt.Sprintf("大转盘奖品列表:<br/> %s", strings.Join(prizeList, "<br/>\n"))
}

// GET http://localhost:8080/prize
func (c *lotteryController) GetPrize() string {
    c.Ctx.Header("Content-Type", "text/html")

    // 第一步,抽奖,根据随机数匹配奖品
    seed := time.Now().UnixNano()
    r := rand.New(rand.NewSource(seed))
    // 得到个人的抽奖编码
    code := r.Intn(10000)
    //fmt.Println("GetPrize code=", code)
    var myprize string
    var prizeRate *Prate
    // 从奖品列表中匹配,是否中奖
    for i, prize := range prizeList {
        rate := &rateList[i]
        if code >= rate.CodeA && code <= rate.CodeB {
            // 满足中奖条件
            myprize = prize
            prizeRate = rate
            break
        }
    }
    if myprize == "" {
        // 没有中奖
        myprize = "很遗憾,再来一次"
        return myprize
    }
    // 第二步,发奖,是否可以发奖
    if prizeRate.Total == 0 {
        // 无限奖品
        fmt.Println("中奖:", myprize)
        return myprize
    } else if prizeRate.Left > 0 {
        // 还有剩余奖品
        prizeRate.Left -= 1
        fmt.Println("中奖:", myprize)
        return myprize
    } else {
        // 有限且没有剩余奖品,无法发奖
        myprize = "很遗憾,再来一次"
        return myprize
    }
}

// GET http://localhost:8080/debug
func (c *lotteryController) GetDebug() string {
    c.Ctx.Header("Content-Type", "text/html")
    return fmt.Sprintf("获奖概率: %v \n", rateList)
}

把运行起来:

[root@localhost lottery]# go run _deamon/6wheel/main.go 
Now listening on: http://localhost:8080
Application started. Press CTRL+C to shut down.

访问首页,获奖概率那些:

[root@localhost Work]# curl "http://localhost:8080/"
大转盘奖品列表:<br/> 一等奖,火星单程船票<br/>
二等奖,凉飕飕南极之旅<br/>
三等奖,iPhone一部<br/>
[root@localhost Work]# curl "http://localhost:8080/debug"
获奖概率: [{1 1 0 0 1} {2 2 1 2 2} {5 10 3 5 10} {100 0 0 9999 0}]

试试抽奖:

[root@localhost Work]# curl "http://localhost:8080/prize"
很遗憾,再来一次
[root@localhost Work]# curl "http://localhost:8080/prize"
很遗憾,再来一次
[root@localhost Work]# curl "http://localhost:8080/prize"
很遗憾,再来一次
[root@localhost Work]# curl "http://localhost:8080/prize"
很遗憾,再来一次
[root@localhost Work]# curl "http://localhost:8080/prize"
很遗憾,再来一次
[root@localhost Work]# curl "http://localhost:8080/prize"
很遗憾,再来一次

当前抽奖存在并发问题,先进行压测看看效果,在进行压测前做一些修改
奖品列表只保留一个奖品,并设置中奖概率:

var rateList []Prate = []Prate{
    Prate{100, 1000, 0, 9999, 1000},
    //Prate{1, 1, 0, 0, 1},
    //Prate{2, 2, 1, 2, 2},
    //Prate{5, 10, 3, 5, 10},
    //Prate{100,0, 0, 9999, 0},
}

加入日志打印:

...
var logger *log.Logger

//初始化日志
func initLog() {
    f, _ := os.Create("/var/log/lottery_demo.log")
    logger = log.New(f, "", log.Ldate|log.Lmicroseconds)
}
......
func newApp() *iris.Application {
    app := iris.New()
    mvc.New(app.Party("/")).Handle(&lotteryController{})

    initLog()

    return app
}
......
if prizeRate.Total == 0 {
        // 无限奖品
        fmt.Println("中奖:", myprize)

        saveLuckData(myprize)

        return myprize
    } else if prizeRate.Left > 0 {
        // 还有剩余奖品
        prizeRate.Left -= 1
        fmt.Println("中奖:", myprize)

        saveLuckData(myprize)

        return myprize
    } else {
        // 有限且没有剩余奖品,无法发奖
        myprize = "很遗憾,再来一次"
        return myprize
    }
......
func saveLuckData(prize string) {
    logger.Printf("lucky, prize=%s", prize)
}

启动程序:

[root@localhost lottery]# 
[root@localhost lottery]# go run _deamon/6wheel/main.go 
Now listening on: http://localhost:8080
Application started. Press CTRL+C to shut down.

清空日志并压测抽奖:

[root@localhost vagrant]# > /var/log/lottery_demo.log 
[root@localhost vagrant]# wc -l /var/log/lottery_demo.log 
0 /var/log/lottery_demo.log
[root@localhost vagrant]# /usr/local/bin/wrk -t10 -c100 -d5 "http://localhost:8080/prize"
Running 5s test @ http://localhost:8080/prize
  10 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.55ms    4.62ms  38.60ms   76.30%
    Req/Sec     2.06k   491.62     5.17k    75.80%
  103334 requests in 5.07s, 13.80MB read
Requests/sec:  20362.61
Transfer/sec:      2.72MB
[root@localhost vagrant]# wc -l /var/log/lottery_demo.log 
1001 /var/log/lottery_demo.log

加上互斥锁:

......
var mu sync.Mutex = sync.Mutex{}
......
        mu.Lock()
        prizeRate.Left -= 1
        defer mu.Unlock()
        fmt.Println("中奖: ", myprize)
......

再次测试就看不到多出来的中奖数量了,或者可以采取第二种方法,使用go的原子操作。

原子操作即是进行过程中不能被中断的操作。也就是说,针对某个值的原子操作在被进行的过程当中,CPU绝不会再去进行其它的针对该值的操作。无论这些其它的操作是否为原子操作都会是这样。为了实现这样的严谨性,原子操作仅会由一个独立的CPU指令代表和完成。只有这样才能够在并发环境下保证原子操作的绝对安全。

修改代码如下:

......
type Prate struct {
    Rate int        // 万分之N的中奖概率
    Total int       // 总数量限制,0 表示无限数量
    CodeA int       // 中奖概率起始编码(包含)
    CodeB int       // 中奖概率终止编码(包含)
    //Left int      // 剩余数
    Left *int32         // 剩余数
}
......
var giftLeft = int32(1000)
// 奖品的中奖概率设置,与上面的 prizeList 对应的设置
var rateList []Prate = []Prate{
    Prate{100, 1000, 0, 9999, &giftLeft},
    //Prate{1, 1, 0, 0, 1},
    //Prate{2, 2, 1, 2, 2},
    //Prate{5, 10, 3, 5, 10},
    //Prate{100,0, 0, 9999, 0},
}
......
func (c *lotteryController) GetPrize() string {
    c.Ctx.Header("Content-Type", "text/html")

    // 第一步,抽奖,根据随机数匹配奖品
    seed := time.Now().UnixNano()
    r := rand.New(rand.NewSource(seed))
    // 得到个人的抽奖编码
    code := r.Intn(10000)
    //fmt.Println("GetPrize code=", code)
    var myprize string
    var prizeRate *Prate
    // 从奖品列表中匹配,是否中奖
    for i, prize := range prizeList {
        rate := &rateList[i]
        if code >= rate.CodeA && code <= rate.CodeB {
            // 满足中奖条件
            myprize = prize
            prizeRate = rate
            break
        }
    }
    if myprize == "" {
        // 没有中奖
        myprize = "很遗憾,再来一次"
        return myprize
    }
    // 第二步,发奖,是否可以发奖
    if prizeRate.Total == 0 {
        // 无限奖品
        fmt.Println("中奖: ", myprize)

        saveLuckData(myprize)

        return myprize
    } else if *prizeRate.Left > 0 {
        // 还有剩余奖品
        left := atomic.AddInt32(prizeRate.Left, -1)
        if left >= 0 {
            fmt.Println("中奖: ", myprize)

            saveLuckData(myprize)

            return myprize
        }
    }

    // 有限且没有剩余奖品,无法发奖
    myprize = "很遗憾,再来一次"
    return myprize
}
......

相关文章

  • iris 抽奖实例6

    大转盘 固定几个奖品,不同的中奖概率或者总数量限制 每一次转动抽奖,后端计算出这次抽奖的中奖情况,并返回对应的奖品...

  • iris 抽奖实例2

    1. 简单实现抽奖 安装并创建好iris测试项目在测试项目目录下创建main.go文件,内容如下 启动: 刮刮乐类...

  • iris 抽奖实例5

    微博抢红包在测试项目目录下创建main.go文件,内容如下: 启动: curl设置红包并抽红包: 查看剩余红包数量...

  • iris 抽奖实例1

    1. 简单实现抽奖 安装并创建好iris测试项目在测试项目目录下创建main.go文件,内容如下 测试请求当前总共...

  • iris 抽奖实例4

    支付宝集福卡 奖品是虚拟的5个福字,没数量限制 先识别图片,确定福字的获得概率 不存在线程安全问题在测试项目目录下...

  • iris 抽奖实例3

    微信摇一摇特点: 种类多、数量多 随机匹配奖品,针对虚拟物品、实物,不一样的规则 中奖后,减库存,记录并提示用户。...

  • 第六十九章 使用 REST API 监控 IRIS

    第六十九章 使用 REST API 监控 IRIS 每个 IRIS® 数据平台实例都包含一个提供实例统计信息的 R...

  • golang iris mvc框架的服务端加载过程

    整个iris框架共三层结构: 应用的配置和注册信息,如路由、中间件、日志。 中间的服务端实例,从iris实例拿配置...

  • 第六十七章 使用 Web 服务监控 IRIS - 监控 Web

    第六十七章 使用 Web 服务监控 IRIS - 监控 Web 服务的 URL 对于给定的 IRIS 实例,日志...

  • 第七十章 使用 REST API 监控 IRIS - 互操作性指

    第七十章 使用 REST API 监控 IRIS - 互操作性指标 除了上一节中描述的指标外, IRIS 实例还可...

网友评论

      本文标题:iris 抽奖实例6

      本文链接:https://www.haomeiwen.com/subject/xpfrbltx.html