Go語(yǔ)言實(shí)現(xiàn)權(quán)重抽獎(jiǎng)系統(tǒng)的項(xiàng)目實(shí)踐
需求描述
- 支持配置多個(gè)獎(jiǎng)品及對(duì)應(yīng)權(quán)重
- 保證抽獎(jiǎng)結(jié)果符合權(quán)重概率分布
- 防止重復(fù)中獎(jiǎng)
- 提供抽獎(jiǎng)結(jié)果驗(yàn)證接口
完整實(shí)現(xiàn)代碼
package main import ( "crypto/rand" "encoding/json" "fmt" "math/big" "net/http" "sync" ) // 獎(jiǎng)品配置 type Prize struct { ID int `json:"id"` Name string `json:"name"` Weight int `json:"weight"` // 權(quán)重值(非百分比) } // 抽獎(jiǎng)系統(tǒng) type LotterySystem struct { prizes []Prize totalWeight int issuedPrizes map[int]bool mu sync.Mutex } // 初始化抽獎(jiǎng)系統(tǒng) func NewLotterySystem(prizes []Prize) *LotterySystem { total := 0 for _, p := range prizes { total += p.Weight } return &LotterySystem{ prizes: prizes, totalWeight: total, issuedPrizes: make(map[int]bool), } } // 安全隨機(jī)數(shù)生成 func secureRandom(max int) (int, error) { n, err := rand.Int(rand.Reader, big.NewInt(int64(max))) if err != nil { return 0, err } return int(n.Int64()), nil } // 執(zhí)行抽獎(jiǎng) func (ls *LotterySystem) Draw() (*Prize, error) { ls.mu.Lock() defer ls.mu.Unlock() if ls.totalWeight == 0 { return nil, fmt.Errorf("no available prizes") } // 生成隨機(jī)數(shù) randomNum, err := secureRandom(ls.totalWeight) if err != nil { return nil, err } // 權(quán)重選擇 current := 0 for _, p := range ls.prizes { current += p.Weight if randomNum < current { if ls.issuedPrizes[p.ID] { continue // 已發(fā)放的獎(jiǎng)品跳過(guò) } ls.issuedPrizes[p.ID] = true return &p, nil } } return nil, fmt.Errorf("draw failed") } // HTTP服務(wù) func main() { // 初始化獎(jiǎng)品池 prizes := []Prize{ {ID: 1, Name: "一等獎(jiǎng)", Weight: 1}, {ID: 2, Name: "二等獎(jiǎng)", Weight: 5}, {ID: 3, Name: "三等獎(jiǎng)", Weight: 20}, {ID: 4, Name: "參與獎(jiǎng)", Weight: 74}, } lottery := NewLotterySystem(prizes) http.HandleFunc("/draw", func(w http.ResponseWriter, r *http.Request) { prize, err := lottery.Draw() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(prize) }) fmt.Println("抽獎(jiǎng)服務(wù)已啟動(dòng),監(jiān)聽(tīng)端口 8080") http.ListenAndServe(":8080", nil) }
核心功能說(shuō)明
權(quán)重算法:
// 權(quán)重選擇邏輯 current := 0 for _, p := range ls.prizes { current += p.Weight if randomNum < current { return &p } }
- 使用累計(jì)權(quán)重區(qū)間算法
- 保證概率分布準(zhǔn)確性
安全隨機(jī)數(shù):
// 使用crypto/rand生成安全隨機(jī)數(shù) func secureRandom(max int) (int, error) { n, err := rand.Int(rand.Reader, big.NewInt(int64(max))) // ... }
- 避免使用math/rand的可預(yù)測(cè)性
- 滿足安全抽獎(jiǎng)需求
并發(fā)控制:
var mu sync.Mutex func (ls *LotterySystem) Draw() { ls.mu.Lock() defer ls.mu.Unlock() // ... }
- 使用互斥鎖保證線程安全
- 防止并發(fā)抽獎(jiǎng)導(dǎo)致的數(shù)據(jù)競(jìng)爭(zhēng)
防重復(fù)機(jī)制:
issuedPrizes map[int]bool
- 使用內(nèi)存映射記錄已發(fā)放獎(jiǎng)品
- 生產(chǎn)環(huán)境可替換為Redis等持久化存儲(chǔ)
擴(kuò)展功能建議
概率可視化驗(yàn)證:
// 添加測(cè)試端點(diǎn)驗(yàn)證概率分布 http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) { results := make(map[int]int) for i := 0; i < 10000; i++ { tempLottery := NewLotterySystem(prizes) prize, _ := tempLottery.Draw() results[prize.ID]++ } json.NewEncoder(w).Encode(results) })
分布式鎖擴(kuò)展:
// 使用Redis分布式鎖 func (ls *LotterySystem) DistributedDraw() { lock := redis.NewLock("lottery_lock") err := lock.Lock() // ...抽獎(jiǎng)邏輯... lock.Unlock() }
獎(jiǎng)品庫(kù)存管理:
type Prize struct { // ... Stock int // 新增庫(kù)存字段 } func (ls *LotterySystem) Draw() { // 檢查庫(kù)存 if p.Stock <= 0 { continue } // 扣減庫(kù)存 p.Stock-- }
運(yùn)行測(cè)試
啟動(dòng)服務(wù):
go run main.go
測(cè)試抽獎(jiǎng):
curl http://localhost:8080/draw # 示例返回:{"id":3,"name":"三等獎(jiǎng)","weight":20}
概率驗(yàn)證測(cè)試:
curl http://localhost:8080/test # 返回萬(wàn)次抽獎(jiǎng)結(jié)果分布
關(guān)鍵優(yōu)化點(diǎn)
性能優(yōu)化:
- 使用預(yù)計(jì)算總權(quán)重值
- 內(nèi)存級(jí)鎖粒度控制
- 對(duì)象池復(fù)用
安全增強(qiáng):
- JWT用戶身份驗(yàn)證
- 抽獎(jiǎng)?lì)l率限制
- 敏感操作日志
業(yè)務(wù)擴(kuò)展:
- 支持不同抽獎(jiǎng)活動(dòng)
- 獎(jiǎng)品有效期管理
- 中獎(jiǎng)名單公示
到此這篇關(guān)于Go語(yǔ)言實(shí)現(xiàn)權(quán)重抽獎(jiǎng)系統(tǒng)的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)Go語(yǔ)言 權(quán)重抽獎(jiǎng)系統(tǒng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go1.20最新資訊go?arena手動(dòng)管理內(nèi)存鴿了
由于過(guò)于繁雜,Go?核心團(tuán)隊(duì)成員@Ian?Lance?Taylor,也表態(tài):目前尚未做出任何決定,也不可能在短期內(nèi)做出任何決定,可以認(rèn)為這個(gè)提案基本鴿了,今天這篇文章就是給大家同步目前的情況2023-11-11go語(yǔ)言實(shí)現(xiàn)聊天服務(wù)器的示例代碼
這篇文章主要介紹了go語(yǔ)言實(shí)現(xiàn)聊天服務(wù)器的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08詳解Golang中文件系統(tǒng)事件監(jiān)聽(tīng)
文件系統(tǒng)事件是指文件系統(tǒng)相關(guān)的各種操作和狀態(tài)變化,當(dāng)一個(gè)應(yīng)用層的進(jìn)程操作文件或目錄時(shí),會(huì)觸發(fā)system call,內(nèi)核的notification子系統(tǒng)可以守在那里,把該進(jìn)程對(duì)文件的操作上報(bào)給應(yīng)用層的監(jiān)聽(tīng)進(jìn)程,這篇文章主要介紹了Golang之文件系統(tǒng)事件監(jiān)聽(tīng),需要的朋友可以參考下2024-01-01Golang使用gin模板渲染base64圖片出現(xiàn)#ZgotmplZ的解決辦法
這篇文章主要介紹了Golang使用gin模板渲染base64圖片出現(xiàn)#ZgotmplZ的的場(chǎng)景復(fù)現(xiàn)和解決辦法,文中通過(guò)代碼示例講解的非常詳細(xì),對(duì)大家解決問(wèn)題有一定的幫助,需要的朋友可以參考下2024-05-05go語(yǔ)言go?func(){select{}}()的用法
本文主要介紹了go語(yǔ)言go?func(){select{}}()的用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02Go1.18新特性工作區(qū)模糊測(cè)試及泛型的使用詳解
這篇文章主要為大家介紹了Go?1.18新特性中的工作區(qū)?模糊測(cè)試?泛型使用進(jìn)行詳細(xì)講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07Go 中 slice 的 In 功能實(shí)現(xiàn)探索
這篇文章主要介紹了Go 中 slice 的 In 功能實(shí)現(xiàn)探索,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09