支付宝集福卡
- 奖品是虚拟的5个福字,没数量限制
- 先识别图片,确定福字的获得概率
- 不存在线程安全问题
在测试项目目录下创建main.go文件,内容如下:
package main
import (
"fmt"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc"
"log"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
// 奖品对象
type gift struct {
id int //奖品标识
name string //奖品名称
pic string //奖品图片
link string //奖品链接
inuse bool // 是否使用中
rate int //中将概率,万分之N,0~9999
rateMin int //大于等于最小中奖编码
rateMax int //小于等于最大中奖编码
}
// 最大号码
const rateMax = 10
var logger *log.Logger
type lotterController struct {
Ctx iris.Context
}
func main() {
app := newApp()
app.Run(iris.Addr(":8080"))
}
// 初始化奖品列表信息(管理后台来维护)
func newGift() *[5]gift {
/**
* 表达式new(T)将创建一个T类型的匿名变量,所做的是为T类型的新值分配并清零一块内存空间,
* 然后将这块内存空间的地址作为结果返回,而这个结果就是指向这个新的T类型值的指针值,
* 返回的指针类型为*T。
*
* 我们只需使用new()函数,无需担心其内存的生命周期或怎样将其删除,因为Go语言的内存管理系统会帮我们打理一切。
*/
giftlist := new([5]gift)
// 1 实物大奖
g1 := gift{
id: 1,
name: "富强福",
pic: "富强福.jpg",
link: "",
inuse: true,
rate: 4,
rateMin: 0,
rateMax: 0,
}
giftlist[0] = g1
// 2 实物小奖
g2 := gift{
id: 2,
name: "和谐福",
pic: "和谐福.jpg",
link: "",
inuse: true,
rate: 3,
rateMin: 0,
rateMax: 0,
}
giftlist[1] = g2
// 3 虚拟券,相同的编码
g3 := gift{
id: 3,
name: "友善福",
pic: "友善福.jpg",
link: "",
inuse: true,
rate: 2,
rateMin: 0,
rateMax: 0,
}
giftlist[2] = g3
// 4 虚拟券,不相同的编码
g4 := gift{
id: 4,
name: "爱国福",
pic: "爱国福.jpg",
link: "",
inuse: true,
rate: 1,
rateMin: 0,
rateMax: 0,
}
giftlist[3] = g4
// 5 虚拟币
g5 := gift{
id: 5,
name: "敬业福",
pic: "敬业福.jpg",
link: "",
inuse: true,
rate: 0,
rateMin: 0,
rateMax: 0,
}
giftlist[4] = g5
return giftlist
}
// 根据概率,计算好的奖品信息列表
func giftRate(rate string) *[5]gift {
giftlist := newGift()
rates := strings.Split(rate, ",")
ratesLen := len(rates)
// 整理奖品数据,把rateMin,rateMax根据rate进行编排
rateStart := 0
for i, data := range giftlist {
if !data.inuse {
continue
}
grate := 0
if i < ratesLen { // 避免数组越界
grate, _ = strconv.Atoi(rates[i])
}
giftlist[i].rate = grate
giftlist[i].rateMin = rateStart
giftlist[i].rateMax = rateStart + grate
if giftlist[i].rateMax >= rateMax {
// 号码达到最大值,分配的范围重头再来
giftlist[i].rateMax = rateMax
rateStart = 0
} else {
rateStart += grate
}
}
fmt.Printf("giftlist=%v\n", giftlist)
return giftlist
}
// 初始化日志信息
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
}
// 抽奖的控制器
type lotteryController struct {
Ctx iris.Context
}
// GET http://localhost:8080/?rate=4,3,2,1,0
func (c *lotteryController) Get() string {
rate := c.Ctx.URLParamDefault("rate", "4,3,2,1,0")
giftlist := giftRate(rate)
return fmt.Sprintf("%v\n", giftlist)
}
// GET http://localhost:8080/lucky?uid=1&rate=4,3,2,1,0
func (c *lotteryController) GetLucky() map[string]interface{} {
uid, _ := c.Ctx.URLParamInt("uid")
rate := c.Ctx.URLParamDefault("rate", "4,3,2,1,0")
code := luckyCode()
fmt.Printf("code=%v\n", code)
ok := false
result := make(map[string]interface{})
result["success"] = ok
giftlist := giftRate(rate)
for _, data := range giftlist {
if !data.inuse {
continue
}
if data.rateMin <= int(code) && data.rateMax >= int(code) {
// 中奖了,抽奖编码在奖品中奖编码范围内
ok = true
sendData := data.pic
if ok {
// 中奖后,成功得到奖品(发奖成功)
// 生成中奖纪录
saveLuckyData(uid, code, data.id, data.name, data.link, sendData)
result["success"] = ok
result["uid"] = uid
result["id"] = data.id
result["name"] = data.name
result["link"] = data.link
result["data"] = sendData
break
}
}
}
return result
}
// 抽奖编码
func luckyCode() int32 {
seed := time.Now().UnixNano() // rand内部运算的随机数
code := rand.New(rand.NewSource(seed)).Int31n(int32(rateMax)) // rand计算得到的随机数
return code
}
// 记录用户的获奖记录
func saveLuckyData(uid int, code int32, id int, name, link, sendData string) {
logger.Printf("lucky, uid=%d, code=%d, gift=%d, name=%s, link=%s, data=%s ", uid, code, id, name, link, sendData)
}
首页:
抽奖:
压力测试:
[root@localhost wrk]# > /var/log/lottery_demo.log
[root@localhost wrk]# wc -l /var/log/lottery_demo.log
0 /var/log/lottery_demo.log
[root@localhost wrk]# /usr/local/bin/wrk -t15 -c15 -d10 http://localhost:8080/lucky
Running 10s test @ http://localhost:8080/lucky
15 threads and 15 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.73ms 2.38ms 25.83ms 83.99%
Req/Sec 422.48 61.82 700.00 68.93%
63603 requests in 10.10s, 13.83MB read
Requests/sec: 6297.95
Transfer/sec: 1.37MB
[root@localhost wrk]# /usr/local/bin/wrk -t15 -c15 -d10 http://localhost:8080/lucky
Running 10s test @ http://localhost:8080/lucky
15 threads and 15 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.59ms 2.23ms 28.49ms 83.08%
Req/Sec 440.23 58.14 673.00 69.73%
66240 requests in 10.08s, 14.40MB read
Requests/sec: 6569.48
Transfer/sec: 1.43MB
发现qps比上一个抽奖低了一倍多,是因为每一次都要进入到抽奖里面的指针逻辑,开销比较大,而没有线程安全性问题是因为没有共享变量,也没有库存,操作的giftlist每次都是从内存中开辟出来的新的空间,然后返回的。
网友评论