上一篇文章用php原生代码实现了秒杀功能,本文用golang来实现秒杀;
运行环境:win10,docker+redis+lua最后AB压力测试;
共同点: 用户id都是10000-1000000之间的随机数,库存stock都是1000,AB参数 -n 10000 -c 200两者相同
不同点: 本项目演示中,PHP没有采用框架,go采用gin实现http服务。
先看看下之前php的压力测试结果,为了严谨一点,我执行三次取平均值。三次测试QPS平均值 314
php的秒杀实现请参考我的历史文章 :
按照入门级的理解,go用了框架,应该更繁重点,但是最后的测试结果让人吃惊:
那就是go的QPS将近是PHP的 3.4 倍,所以go不愧是面向工资编程语言,来看结果:
QPS平均值 1068.7
代码奉上,环境搭建此处就不累赘了。
package main
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis"
"math/rand"
"net/http"
"strconv"
"time"
)
var redisDb *redis.Client
var luaScript = `
local userId = KEYS[1];
local goodKey = KEYS[2];
local stock = KEYS[3];
local userExit = redis.call("sismember",goodKey,userId);
if tonumber(userExit) == 1 then
return 2;
end
local num = redis.call("get",stock);
if tonumber(num) <= 0 then
return 3;
else
redis.call("decr",stock);
redis.call("sadd",goodKey,userId);
end
return 1;`
var evalSha string
func init() {
initRedisClient()
}
func initRedisClient() {
redisDb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
var err error
evalSha, err = redisDb.ScriptLoad(context.Background(), luaScript).Result()
if err != nil {
panic(err)
}
}
func main() {
// 1.创建路由
r := gin.Default()
// 2.绑定路由规则,执行的函数
// gin.Context,封装了request和response
r.GET("/kill", func(c *gin.Context) {
kill()
c.String(http.StatusOK, "秒杀成功")
})
// 3.监听端口,默认在8080
// Run("里面不指定端口号默认为8080")
r.Run(":8000")
}
func kill() {
err := redisDb.SetNX(context.Background(), "go_stock", 1000, 0).Err()
if err != nil {
panic(err)
}
//time.Now().Unix()秒 ab测试产生了很多一样的用户
rand.Seed(time.Now().UnixNano()) //纳秒
id := rand.Intn(990001) + 10000 //10000-1000000的随机数
userId := strconv.Itoa(id) //整数转字符,如果直接int(id)会出现意想不到的结果
res, err2 := redisDb.EvalSha(context.Background(), evalSha, []string{userId, "go_user_ids", "go_stock"}).Result()
if err2 != nil {
panic(err2)
}
if res.(int64) == int64(1) {
fmt.Println("秒杀成功")
} else if res.(int64) == int64(2) {
fmt.Println("请勿重复操作")
} else {
fmt.Println("暂无库存了")
}
}
不妥之处请指正。