使用go進行商品秒殺
package main
import (
"errors"
"fmt"
"log"
"runtime"
"sync"
"time"
)
var GoodsA = SecondObject{&sync.RWMutex{}, 10}
func main() {
// 單例消費
fmt.Println("案例一: 單筆消費")
GoodsA.SecondKill(5*time.Second, 3, func(r chan Result) {
// 支付訂單
time.Sleep(4 * time.Second)
r <- Result{1, nil}
})
fmt.Println("消費成功,剩餘:", GoodsA.Num)
// 逾時消費
fmt.Println("案例2: 消費逾時")
n, e := GoodsA.SecondKill(3*time.Second, 3, func(r chan Result) {
// 支付訂單
time.Sleep(4 * time.Second)
r <- Result{1, nil}
})
fmt.Println(n, e)
// 庫存不足
fmt.Println("案例3: 庫存不足")
n, e = GoodsA.SecondKill(3*time.Second, 11)
fmt.Println(n, e)
fmt.Println("案例4: 1000個請求,搶購剩餘7個")
var wg sync.WaitGroup
runtime.GOMAXPROCS(runtime.NumCPU())
wg.Add(1000)
for i := 0; i < 1000; i++ {
go func(i int) {
defer wg.Done()
n, e := GoodsA.SecondKill(5*time.Second, 1)
fmt.Println(fmt.Sprintf("request %d result:", i), n, e)
}(i)
}
wg.Wait()
}
type SecondObject struct {
M *sync.RWMutex
Num int
}
// 下單到支付成功之間的handler result
// 1,nil 成功
// 2, error 錯誤
type Result struct {
Status int
Err error
}
// 0,nil success
// 500, error 系統錯誤
// 400, error 請求錯誤
// 2, error 庫存不足,賣完了
// 3, error 購買數量大于庫存
// 4, 逾時
func (s *SecondObject) SecondKill(timeout time.Duration, buyNum int, handlers ... func(results chan Result)) (int, error) {
defer func() {
if e := recover(); e != nil {
log.Println(e)
}
}()
s.M.Lock()
defer s.M.Unlock()
if buyNum > s.Num {
return 3, errors.New(fmt.Sprintf("buy number %d more than saved number %d", buyNum, s.Num))
}
if s.Num < 0 {
return 2, errors.New("save out")
}
var jobNum = len(handlers)
result := make(chan Result)
for _, v := range handlers {
go v(result)
}
for {
if jobNum == 0 {
break
}
select {
case <-time.After(timeout):
return 4, errors.New("time out")
case jobResult := <-result:
switch jobResult.Status {
case 1:
jobNum --
case 2:
return jobResult.Status, jobResult.Err
}
}
}
s.Num -= buyNum
return 0, nil
}