欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

GoLang抽獎系統(tǒng)簡易實現(xiàn)流程

 更新時間:2022年12月15日 09:31:24   作者:上后左愛  
這篇文章主要介紹了GoLang抽獎系統(tǒng)實現(xiàn)流程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧

業(yè)務難點

設(shè)計一個抽獎系統(tǒng),這個系統(tǒng)并不是具體化,是抽象化,具有以下的幾個難點:

1、抽獎業(yè)務需要 復雜多變

2、獎品類型和概率設(shè)置

3、公平的抽獎和安全的發(fā)獎

4、并發(fā)安全性問題 一個人不能槍多次

5、高效的抽獎和發(fā)獎,提供高并發(fā)和性能

6、 如何使用redies進行優(yōu)化

技術(shù)選項

  • 高并發(fā) Go 協(xié)程優(yōu)先于 PHP多進程,Java的 多線程模型
  • 高性能編譯后的二進制優(yōu)先于PHP解釋性和Java虛擬機
  • 高效的網(wǎng)絡(luò)模型 epoll 模型優(yōu)先于PHPBIO模型和Java NIO模型

抽獎活動

  • 年會抽獎,彩票刮獎,微信搖一搖,抽獎大轉(zhuǎn)盤,集福卡等活動,本項目以 抽獎大轉(zhuǎn)盤作為一種活動進行設(shè)計。
  • 項目實戰(zhàn)內(nèi)容: 框架/核心代碼后臺功能 ,合理設(shè)置獎品和發(fā)送獎品, mysql+優(yōu)化-使用redies 發(fā)獎計劃與獎品池, 壓力測試和更多的運營策略(系統(tǒng)的性能有更好的了解,運營產(chǎn)品的策略有更多), 引入 thirft 框架(RPC 框架), 設(shè)計接口生成代碼, 服務端接口和客戶端程序

需求分析

1. go mod 配置

2. 配置國內(nèi)代理: go env -w GOPROXY=https://goproxy.cn,https://goproxy.io,direct

3. go get -u -v github.com/kataras/iris 下載包在 GOPATH的PKG目錄下

4. iris:功能: 安全認證,緩存 cookies 文件 MVC, 模板 豐富的示例代碼

5. https://iris-go.com/v10/recipe

*年會抽獎程序

使用的是iris 這個web 框架 進行處理

/**
*  curl http://localhost:8080/
*  curl --data "users=123,567" http://localhost:8080/import
*  curl http://localhost:8080/lucky
 */
package main
import (
	"fmt"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/mvc"
	"math/rand"
	"strings"
	"sync"
	"time"
)
var userList []string // 共享變量讀寫前后 需要增加 鎖的設(shè)定 簡單方式添加互斥鎖
var mu sync.Mutex
type lotteryController struct {
	Ctx iris.Context
}
// 啟動一個 iris 應用
func newApp() *iris.Application {
	app := iris.New()
	mvc.New(app.Party("/")).Handle(&lotteryController{})
	return app
}
func main() {
	app := newApp()
	userList = []string{}
	mu = sync.Mutex{}
	err := app.Listen(":8080")
	if err != nil {
		panic(fmt.Sprintf("web server start error: %s\n", err))
		return
	}
}
func (c *lotteryController) Get() string {
	count := len(userList)
	return fmt.Sprintf("當前總共參與抽獎的用戶數(shù):%d\n", count)
}
// PostImport POST http://localhost:8090/import
// params : users
func (c *lotteryController) PostImport() string {
	strUsers := c.Ctx.FormValue("users")
	users := strings.Split(strUsers, ",")
	// 批量線程導入時候 發(fā)現(xiàn)有多線程的問題 數(shù)據(jù)統(tǒng)計不正確
	mu.Lock()
	defer mu.Unlock()
	count1 := len(userList)
	for _, u := range users {
		u = strings.TrimSpace(u)
		if len(u) > 0 {
			userList = append(userList, u)
		}
	}
	count2 := len(userList)
	return fmt.Sprintf("當前總共參與抽獎的用戶數(shù):%d, 成功導入的用戶數(shù):%d\n", count2, count2-count1)
}
// GetLucky GET http://localhost:8090/lucky
func (c *lotteryController) GetLucky() string {
	// 抽獎地方進行鎖的判斷
	mu.Lock()
	defer mu.Unlock()
	count := len(userList)
	if count > 1 {
		index := rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(int32(count))
		user := userList[index]
		// 需要 刪除被挑選過的人 直接可以刪除 當前index 下的數(shù)據(jù) 更好
		userList = append(userList[0:index], userList[index+1:]...)
		return fmt.Sprintf("當前中獎用戶:%s, 剩余用戶數(shù):%d\n", user, count-1)
	} else if count == 1 {
		user := userList[0]
		return fmt.Sprintf("當前中獎用戶:%s, 剩余用戶數(shù):%d\n", user, count-1)
	} else {
		return fmt.Sprintf("當前中獎完畢,沒有用戶參與中獎\n")
	}
}

單元測試問題,對于 userList 的 多線程下發(fā)生數(shù)據(jù)競爭問題 :

package main
import (
	"fmt"
	"github.com/kataras/iris/v12/httptest"
	"sync"
	"testing"
)
func TestMVC(t *testing.T) {
	app := newApp()
	e := httptest.New(t, app)
	// 使用同步等待鎖
	var wg sync.WaitGroup
	e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("當前總共參與抽獎的用戶數(shù):0\n")
	for i := 0; i < 100; i++ {
		wg.Add(1)
		// 不會出現(xiàn)協(xié)程并發(fā)性問題
		go func(i int) {
			defer wg.Done()
			e.POST("/import").WithFormField("users", fmt.Sprintf("test_u%d", i)).Expect().Status(httptest.StatusOK)
		}(i)
	}
	wg.Wait()
	e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("當前總共參與抽獎的用戶數(shù):100\n")
	e.GET("/lucky").Expect().Status(httptest.StatusOK)
	e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("當前總共參與抽獎的用戶數(shù):99\n")
}

微信搖一搖得抽獎活動

/**
* 微信搖一搖得功能
* wrk -t10 -c10 -d5 http://localhost:8080/lucky 進行壓力測試 查看代碼是否有競爭異常問題和 接口請求量速度
 */
package main
import (
	"fmt"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/mvc"
	"log"
	"math/rand"
	"os"
	"sync"
	"time"
)
var mu sync.Mutex
const (
	giftTypeCoin      = iota // 虛擬幣
	giftTypeCoupon           // 不同卷
	giftTypeCouponFix        // 不同卷
	giftTypeRealSmall        // 實物小獎
	giftTypeRealLarge        // 十五大獎
)
type gift struct {
	id       int
	name     string
	pic      string
	link     string
	gType    int
	data     string // 獎品數(shù)據(jù)(特定得配置信息)
	dataList []string
	total    int
	left     int
	inuse    bool
	rate     int // 萬分之N
	rateMin  int
	rateMax  int
}
// 最大中獎號碼
const rateMax = 1000
var logger *log.Logger
// 獎品類表
var giftList []*gift
type lotteryController struct {
	Ctx iris.Context
}
// 啟動一個 iris 應用
func newApp() *iris.Application {
	app := iris.New()
	mvc.New(app.Party("/")).Handle(&lotteryController{})
	return app
}
func initLog() {
	f, _ := os.Create("G:\\goLandProject\\lottery_demo.log")
	logger = log.New(f, "", log.Ldate|log.Lmicroseconds)
}
func initGift() {
	giftList = make([]*gift, 5)
	g1 := gift{
		id:       1,
		name:     "手機大獎",
		pic:      "",
		link:     "",
		gType:    giftTypeRealLarge,
		data:     "",
		dataList: nil,
		total:    20000,
		left:     20000,
		inuse:    true,
		rate:     10000,
		rateMin:  0,
		rateMax:  0,
	}
	g2 := gift{
		id:       2,
		name:     "充電器",
		pic:      "",
		link:     "",
		gType:    giftTypeRealSmall,
		data:     "",
		dataList: nil,
		total:    5,
		left:     5,
		inuse:    false,
		rate:     10,
		rateMin:  0,
		rateMax:  0,
	}
	g3 := gift{
		id:       3,
		name:     "優(yōu)惠卷滿200減50",
		pic:      "",
		link:     "",
		gType:    giftTypeCouponFix,
		data:     "mall-coupon-2018",
		dataList: nil,
		total:    50,
		left:     50,
		inuse:    false,
		rate:     500,
		rateMin:  0,
		rateMax:  0,
	}
	g4 := gift{
		id:       4,
		name:     "直降優(yōu)惠卷",
		pic:      "",
		link:     "",
		gType:    giftTypeCoupon,
		data:     "",
		dataList: []string{"c01", "c02", "c03", "c04", "c05"},
		total:    50,
		left:     50,
		inuse:    false,
		rate:     100,
		rateMin:  0,
		rateMax:  0,
	}
	g5 := gift{
		id:       5,
		name:     "金幣",
		pic:      "",
		link:     "",
		gType:    giftTypeCoin,
		data:     "10金幣",
		dataList: nil,
		total:    100,
		left:     100,
		inuse:    false,
		rate:     5000,
		rateMin:  0,
		rateMax:  0,
	}
	giftList[0] = &g1
	giftList[1] = &g2
	giftList[2] = &g3
	giftList[3] = &g4
	giftList[4] = &g5
	// s數(shù)據(jù)整理 中獎區(qū)間數(shù)據(jù)
	rateStart := 0
	for _, data := range giftList {
		if !data.inuse {
			continue
		}
		data.rateMin = rateStart
		data.rateMax = rateStart + data.rate
		if data.rateMax >= rateMax {
			data.rateMax = rateMax
			rateStart = 0
		} else {
			rateStart += data.rate
		}
	}
}
func main() {
	initLog()
	initGift()
	mu = sync.Mutex{}
	app := newApp()
	err := app.Listen(":8080")
	if err != nil {
		panic(fmt.Sprintf("web server start error : %s\n", err))
	}
}
// Get http://localhost:8080
func (c *lotteryController) Get() string {
	count := 0
	total := 0
	for _, data := range giftList {
		if data.inuse && (data.total == 0 || (data.total > 0 && data.left > 0)) {
			count++
			total += data.left
		}
	}
	return fmt.Sprintf("當前有效獎品種類數(shù)量:%d, 限量獎品總數(shù)量:%d\n", count, total)
}
// GetLucky  http://localhost:8080/lucky
func (c *lotteryController) GetLucky() map[string]interface{} {
	mu.Lock()
	defer mu.Unlock()
	code := luckyCode()
	ok := false
	result := make(map[string]interface{})
	result["success"] = ok
	// 對 code 與 rateMin -rateMax 區(qū)間內(nèi)進行對比 判斷是否獲獎
	for _, data := range giftList {
		if !data.inuse || (data.total > 0 && data.left <= 0) {
			continue
		}
		if data.rateMin <= int(code) && data.rateMax > int(code) {
			sendData := ""
			switch data.gType {
			case giftTypeCoin:
				ok, sendData = sendCoin(data)
			case giftTypeCoupon:
				ok, sendData = sendCoupon(data)
			case giftTypeCouponFix:
				ok, sendData = sendCouponFix(data)
			case giftTypeRealSmall:
				ok, sendData = sendRealSmall(data)
			case giftTypeRealLarge:
				ok, sendData = sendRealLarge(data)
			}
			if ok {
				// 中獎后得到獎品 生成中獎記錄
				saveLuckyData(code, data, sendData)
				result["success"] = true
				result["id"] = data.id
				result["name"] = data.name
				result["data"] = sendData
				break
			}
		}
	}
	return result
}
func luckyCode() int32 {
	seed := time.Now().UnixNano()
	code := rand.New(rand.NewSource(seed)).Int31n(int32(rateMax))
	return code
}
func sendCoin(data *gift) (bool, string) {
	if data.total == 0 {
		// 數(shù)量無數(shù)
		return true, data.data
	} else if data.left > 0 {
		data.left -= 1
		return true, data.data
	} else {
		return false, "獎品已經(jīng)發(fā)完"
	}
}
// 不同優(yōu)惠卷
func sendCoupon(data *gift) (bool, string) {
	if data.left > 0 {
		left := data.left - 1
		data.left = left
		return true, data.dataList[left%5]
	} else {
		return false, "獎品已經(jīng)發(fā)完"
	}
}
func sendCouponFix(data *gift) (bool, string) {
	if data.total == 0 {
		// 數(shù)量無數(shù)
		return true, data.data
	} else if data.left > 0 {
		data.left -= 1
		return true, data.data
	} else {
		return false, "獎品已經(jīng)發(fā)完"
	}
}
func sendRealSmall(data *gift) (bool, string) {
	if data.total == 0 {
		// 數(shù)量無數(shù)
		return true, data.data
	} else if data.left > 0 {
		data.left -= 1
		return true, data.data
	} else {
		return false, "獎品已經(jīng)發(fā)完"
	}
}
func sendRealLarge(data *gift) (bool, string) {
	if data.total == 0 {
		// 數(shù)量無數(shù)
		return true, data.data
	} else if data.left > 0 {
		data.left -= 1
		return true, data.data
	} else {
		return false, "獎品已經(jīng)發(fā)完"
	}
}
func saveLuckyData(code int32, g *gift, data string) {
	logger.Printf("lucky, code =%d ,id =%d, name =%d, data =%s, left=%d \n", code, g.id, g.name, data, g.left)
}

微博搶紅包

  • 紅包得集合,紅包內(nèi)紅包數(shù)量讀寫都存在并發(fā)安全性問題
  • 第一種方式 使用 Sync.Map 互斥鎖得方式
/**
* 微信搶紅包 普通得 map 發(fā)生 競爭情況 所以需要使用 互斥 sync.Map
* 在大量得寫 和 讀得情況下會發(fā)生 競爭
*
 */
package main
import (
	"fmt"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/mvc"
	"math/rand"
	"sync"
	"time"
)
// 紅包列表
var packageList *sync.Map = new(sync.Map)
type lotteryController struct {
	Ctx iris.Context
}
// 啟動一個 iris 應用
func newApp() *iris.Application {
	app := iris.New()
	mvc.New(app.Party("/")).Handle(&lotteryController{})
	return app
}
func main() {
	app := newApp()
	err := app.Listen(":8080")
	if err != nil {
		panic(fmt.Sprintf("web server start error : %s\n", err))
	}
}
// Get http://localhost:8080
func (c *lotteryController) Get() map[uint32][2]int {
	// 返回當前全部得紅包
	rs := make(map[uint32][2]int)
	packageList.Range(func(key, value interface{}) bool {
		id := key.(uint32)
		list := value.([]uint)
		var money int
		for _, v := range list {
			money += int(v)
		}
		rs[id] = [2]int{len(list), money}
		return true
	})
	return rs
}
// GetSet   http://localhost:8080/set?uid=1&money=100&num=100
func (c *lotteryController) GetSet() string {
	uid, errUid := c.Ctx.URLParamInt("uid")
	moeny, errMoney := c.Ctx.URLParamFloat64("money")
	num, errNum := c.Ctx.URLParamInt("num")
	if errUid != nil || errNum != nil || errMoney != nil {
		fmt.Sprintf("errUid=%d, errMoney=%d, errNum=%d \n", errUid, errMoney, errNum)
	}
	moenyTotal := int(moeny * 100)
	if uid < 1 || moenyTotal < num || num < 1 {
		return fmt.Sprintf("參數(shù)數(shù)值異常, uid=%d, money=%d, num=%d \n", uid, moeny, num)
	}
	// 金額分配算法
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	rMax := 0.55 // 隨機分配最大值
	if num > 1000 {
		rMax = 0.01
	} else if num < 10 {
		rMax = 0.80
	}
	list := make([]uint, num)
	leftMoney := moenyTotal
	leftNum := num
	for leftNum > 0 {
		if leftNum == 1 {
			list[num-1] = uint(leftMoney)
			break
		}
		// 剩余錢數(shù)等于剩余紅包數(shù)每個紅包進行均分
		if leftMoney == leftNum {
			for i := num - leftNum; i < num; i++ {
				list[i] = 1
				break
			}
		}
		// 隨機分配最大值
		rMoney := int(float64(leftMoney-leftNum) * rMax)
		m := r.Intn(rMoney)
		if m < 1 {
			m = 1
		}
		list[num-leftNum] = uint(m)
		leftMoney -= m
		leftNum--
	}
	// 紅包得UUID
	id := r.Uint32()
	packageList.Store(id, list)
	return fmt.Sprintf("/get?id=%d&uid=%d&num=%d", id, uid, num)
}
// GetGet http://localhost:8080/get?id=1&uid=1
func (c *lotteryController) GetGet() string {
	id, errid := c.Ctx.URLParamInt("id")
	uid, errUid := c.Ctx.URLParamInt("uid")
	if errUid != nil || errid != nil {
		return fmt.Sprintf("")
	}
	if uid < 1 || id < 1 {
		return fmt.Sprintf("")
	}
	listq, ok := packageList.Load(uint32(id))
	list := listq.([]int)
	if !ok || len(list) < 1 {
		return fmt.Sprintf("紅包不存在, id =%d \n", id)
	}
	// 分配隨機數(shù)獲取紅包
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	i := r.Intn(len(list))
	money := list[i]
	// 更新紅包中列表信息
	if len(list) > 1 {
		if i == len(list)-1 {
			packageList.Store(uint32(id), list[:i])
		} else if i == 0 {
			packageList.Store(uint32(id), list[1:])
		} else {
			packageList.Store(uint32(id), append(list[:i], list[i+1:]...))
		}
	} else {
		packageList.Delete(uint32(id))
	}
	return fmt.Sprintf("恭喜你搶到一個紅包, 紅包金額:%d \n", money)
}

第二種方式: chan 隊列方式 解決線程安全

/**
* 微信搶紅包 普通得 map 發(fā)生 競爭情況 所以需要使用 互斥 sync.Map
* 在大量得寫 和 讀得情況下會發(fā)生 競爭
*
* 單核任務 修改成 16 核心 進行搶紅包
 */
package main
import (
	"fmt"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/mvc"
	"math/rand"
	"sync"
	"time"
)
// 紅包列表
var packageList *sync.Map = new(sync.Map)
type task struct {
	id       uint32
	callback chan uint
}
const taskNum = 16 // 初始化隊列數(shù)量
var chTaskList []chan task = make([]chan task, taskNum)
type lotteryController struct {
	Ctx iris.Context
}
// 啟動一個 iris 應用
func newApp() *iris.Application {
	app := iris.New()
	mvc.New(app.Party("/")).Handle(&lotteryController{})
	return app
}
func main() {
	app := newApp()
	err := app.Listen(":8080")
	// 啟動多個子線程進行 紅包搶
	for i := 0; i < taskNum; i++ {
		chTaskList[i] = make(chan task)
		go fetchPackageListMoney(chTaskList[i])
	}
	if err != nil {
		panic(fmt.Sprintf("web server start error : %s\n", err))
	}
}
// Get http://localhost:8080
func (c *lotteryController) Get() map[uint32][2]int {
	// 返回當前全部得紅包
	rs := make(map[uint32][2]int)
	packageList.Range(func(key, value interface{}) bool {
		id := key.(uint32)
		list := value.([]uint)
		var money int
		for _, v := range list {
			money += int(v)
		}
		rs[id] = [2]int{len(list), money}
		return true
	})
	return rs
}
// GetSet   http://localhost:8080/set?uid=1&money=100&num=100
func (c *lotteryController) GetSet() string {
	uid, errUid := c.Ctx.URLParamInt("uid")
	moeny, errMoney := c.Ctx.URLParamFloat64("money")
	num, errNum := c.Ctx.URLParamInt("num")
	if errUid != nil || errNum != nil || errMoney != nil {
		fmt.Sprintf("errUid=%d, errMoney=%d, errNum=%d \n", errUid, errMoney, errNum)
	}
	moenyTotal := int(moeny * 100)
	if uid < 1 || moenyTotal < num || num < 1 {
		return fmt.Sprintf("參數(shù)數(shù)值異常, uid=%d, money=%d, num=%d \n", uid, moeny, num)
	}
	// 金額分配算法
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	rMax := 0.55 // 隨機分配最大值
	if num > 1000 {
		rMax = 0.01
	} else if num < 10 {
		rMax = 0.80
	}
	list := make([]uint, num)
	leftMoney := moenyTotal
	leftNum := num
	for leftNum > 0 {
		if leftNum == 1 {
			list[num-1] = uint(leftMoney)
			break
		}
		// 剩余錢數(shù)等于剩余紅包數(shù)每個紅包進行均分
		if leftMoney == leftNum {
			for i := num - leftNum; i < num; i++ {
				list[i] = 1
				break
			}
		}
		// 隨機分配最大值
		rMoney := int(float64(leftMoney-leftNum) * rMax)
		m := r.Intn(rMoney)
		if m < 1 {
			m = 1
		}
		list[num-leftNum] = uint(m)
		leftMoney -= m
		leftNum--
	}
	// 紅包得UUID
	id := r.Uint32()
	packageList.Store(id, list)
	return fmt.Sprintf("/get?id=%d&uid=%d&num=%d", id, uid, num)
}
// GetGet http://localhost:8080/get?id=1&uid=1
func (c *lotteryController) GetGet() string {
	id, errid := c.Ctx.URLParamInt("id")
	uid, errUid := c.Ctx.URLParamInt("uid")
	if errUid != nil || errid != nil {
		return fmt.Sprintf("")
	}
	if uid < 1 || id < 1 {
		return fmt.Sprintf("")
	}
	listq, ok := packageList.Load(uint32(id))
	list := listq.([]int)
	if !ok || len(list) < 1 {
		return fmt.Sprintf("紅包不存在, id =%d \n", id)
	}
	// 構(gòu)造一個任務
	callback := make(chan uint)
	t := task{id: uint32(id), callback: callback}
	// 發(fā)送任務
	chTasks := chTaskList[id%taskNum]
	chTasks <- t
	// 接受返回結(jié)果值
	money := <-callback
	if money <= 0 {
		return "很遺憾,沒有搶到紅包\n"
	} else {
		return fmt.Sprintf("恭喜你搶到一個紅包, 紅包金額:%d \n", money)
	}
}
// 使用隊列方式, 需要不斷從chan 通道中獲取數(shù)據(jù)
func fetchPackageListMoney(chTasks chan task) {
	for {
		t := <-chTasks
		id := t.id
		l, ok := packageList.Load(id)
		if ok && l != nil {
			// 分配隨機數(shù)獲取紅包
			list := l.([]int)
			r := rand.New(rand.NewSource(time.Now().UnixNano()))
			i := r.Intn(len(list))
			money := list[i]
			// 更新紅包中列表信息
			if len(list) > 1 {
				if i == len(list)-1 {
					packageList.Store(uint32(id), list[:i])
				} else if i == 0 {
					packageList.Store(uint32(id), list[1:])
				} else {
					packageList.Store(uint32(id), append(list[:i], list[i+1:]...))
				}
			} else {
				packageList.Delete(uint32(id))
			}
			t.callback <- uint(money)
		} else {
			t.callback <- 0
		}
	}
}

抽獎大轉(zhuǎn)盤

后端設(shè)置各個獎品得中獎概率和數(shù)量限制,更新庫存時候發(fā)現(xiàn)并發(fā)安全性質(zhì)問題 和微信搖一搖 類似

使用CAS進行安全代碼進行修改,不在使用同步鎖,CAS樂觀鎖比sync.mutSync 會快一些

/**
 * 大轉(zhuǎn)盤程序
 * curl http://localhost:8080/
 * curl http://localhost:8080/debug
 * curl http://localhost:8080/prize
 * 固定幾個獎品,不同的中獎概率或者總數(shù)量限制
 * 每一次轉(zhuǎn)動抽獎,后端計算出這次抽獎的中獎情況,并返回對應的獎品信息
 *
 * 增加互斥鎖,保證并發(fā)庫存更新的正常
 * 壓力測試:
 * wrk -t10 -c100 -d5 "http://localhost:8080/prize"
 */
package main
import (
	"fmt"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/mvc"
	"log"
	"math/rand"
	"strings"
	"sync/atomic"
	"time"
)
// Prate 獎品中獎概率
type Prate struct {
	Rate  int    // 萬分之N的中獎概率
	Total int    // 總數(shù)量限制,0 表示無限數(shù)量
	CodeA int    // 中獎概率起始編碼(包含)
	CodeB int    // 中獎概率終止編碼(包含)
	Left  *int32 // 剩余數(shù)使用CAS樂觀鎖 進行修改
}
var left = int32(1000)
// 獎品列表
var prizeList []string = []string{
	"一等獎,火星單程船票",
	"二等獎,涼颼颼南極之旅",
	"三等獎,iPhone一部",
	"", // 沒有中獎
}
// 獎品的中獎概率設(shè)置,與上面的 prizeList 對應的設(shè)置
var rateList []Prate = []Prate{
	//Prate{1, 1, 0, 0, 1},
	//Prate{2, 2, 1, 2, 2},
	Prate{5, 1000, 0, 9999, &left},
	//Prate{100,0, 0, 9999, 0},
}
type lotteryController struct {
	Ctx iris.Context
}
// 啟動一個 iris 應用
func newApp() *iris.Application {
	app := iris.New()
	mvc.New(app.Party("/")).Handle(&lotteryController{})
	return app
}
func main() {
	app := newApp()
	err := app.Listen(":8080")
	if err != nil {
		panic(fmt.Sprintf("web server start error : %s\n", err))
	}
}
// Get GET http://localhost:8080/
func (c *lotteryController) Get() string {
	c.Ctx.Header("Content-Type", "text/html")
	return fmt.Sprintf("大轉(zhuǎn)盤獎品列表:<br/> %s", strings.Join(prizeList, "<br/>\n"))
}
// GetPrize GET http://localhost:8080/prize
func (c *lotteryController) GetPrize() string {
	c.Ctx.Header("Content-Type", "text/html")
	// 第一步,抽獎,根據(jù)隨機數(shù)匹配獎品
	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
	}
	// 第二步,發(fā)獎,是否可以發(fā)獎
	if prizeRate.Total == 0 {
		// 無限獎品
		fmt.Println("中獎: ", myPrize)
		return myPrize
	} else if *prizeRate.Left > 0 {
		// 還有剩余獎品
		left := atomic.AddInt32(prizeRate.Left, -1)
		if left >= 0 {
			log.Printf("獎品:%s", myPrize)
			return myPrize
		}
	}
	// 有限且沒有剩余獎品,無法發(fā)獎
	myPrize = "很遺憾,再來一次"
	return myPrize
}
// GetDebug GET http://localhost:8080/debug
func (c *lotteryController) GetDebug() string {
	c.Ctx.Header("Content-Type", "text/html")
	return fmt.Sprintf("獲獎概率: %v", rateList)
}

抽獎活動總結(jié)

  • 并發(fā)安全性質(zhì)問題,互斥鎖,隊列, CAS遞減方式
  • 優(yōu)化,通過散列減小單個集合得大小

到此這篇關(guān)于GoLang抽獎系統(tǒng)簡易實現(xiàn)流程的文章就介紹到這了,更多相關(guān)Go抽獎系統(tǒng)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用gin框架搭建簡易服務的實現(xiàn)方法

    使用gin框架搭建簡易服務的實現(xiàn)方法

    go語言web框架挺多的,本文就介紹了一下如何使用gin框架搭建簡易服務的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • go?字符串修改的操作代碼

    go?字符串修改的操作代碼

    這篇文章主要介紹了go?字符串修改,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • 詳解如何在Golang中執(zhí)行shell命令

    詳解如何在Golang中執(zhí)行shell命令

    這篇文章主要為大家詳細介紹了在 golang 中執(zhí)行 shell 命令的多種方法和場景,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-02-02
  • go?micro微服務proto開發(fā)安裝及使用規(guī)則

    go?micro微服務proto開發(fā)安裝及使用規(guī)則

    這篇文章主要為大家介紹了go?micro微服務proto開發(fā)中安裝Protobuf及基本規(guī)范字段的規(guī)則詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • go語言通過反射獲取和設(shè)置結(jié)構(gòu)體字段值的方法

    go語言通過反射獲取和設(shè)置結(jié)構(gòu)體字段值的方法

    這篇文章主要介紹了go語言通過反射獲取和設(shè)置結(jié)構(gòu)體字段值的方法,實例分析了Go語言反射的使用技巧,需要的朋友可以參考下
    2015-03-03
  • 淺析Golang中float64的精度問題

    淺析Golang中float64的精度問題

    這篇文章主要來和大家一起探討一下Golang中關(guān)于float64的精度問題,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以了解下
    2023-08-08
  • Go語言遞歸函數(shù)的具體實現(xiàn)

    Go語言遞歸函數(shù)的具體實現(xiàn)

    本文主要介紹了Go語言遞歸函數(shù)的具體實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04
  • 使用golang引入外部包的三種方式:go get, go module, vendor目錄

    使用golang引入外部包的三種方式:go get, go module, ve

    這篇文章主要介紹了使用golang引入外部包的三種方式:go get, go module, vendor目錄,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Golang并發(fā)編程之GMP模型詳解

    Golang并發(fā)編程之GMP模型詳解

    傳統(tǒng)的并發(fā)編程模型是基于線程和共享內(nèi)存的同步訪問控制的,共享數(shù)據(jù)受鎖的保護,線程將爭奪這些鎖以訪問數(shù)據(jù)。本文將介紹Go并發(fā)編程中的GMP模型,感興趣的可以了解一下
    2023-03-03
  • GO語言實現(xiàn)文件上傳代碼分享

    GO語言實現(xiàn)文件上傳代碼分享

    本文給大家分享的是一則使用golang實現(xiàn)文件上傳的代碼,主要是使用os.Create創(chuàng)建文件,io.Copy來保存文件,思路非常清晰,這里推薦給大家,有需要的小伙伴參考下吧。
    2015-03-03

最新評論