diff --git a/go.mod b/go.mod index 96fc7cc..b9d4ec5 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.18 require ( github.com/gin-gonic/gin v1.9.1 github.com/go-redis/redis v6.15.9+incompatible + github.com/gorilla/websocket v1.5.0 + github.com/satori/go.uuid v1.2.0 gorm.io/driver/mysql v1.5.1 gorm.io/gorm v1.25.3 ) diff --git a/go.sum b/go.sum index 4b47a4b..799fdd9 100644 --- a/go.sum +++ b/go.sum @@ -45,6 +45,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= @@ -79,6 +81,8 @@ github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZ github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/pkg/controller/front/product.go b/pkg/controller/front/product.go index b67b0c0..02a0568 100644 --- a/pkg/controller/front/product.go +++ b/pkg/controller/front/product.go @@ -6,8 +6,11 @@ import ( "fmt" "github.com/gin-gonic/gin" "github.com/go-redis/redis" + "github.com/satori/go.uuid" "gorm.io/gorm" + "strconv" "sync" + "time" "worktest/pkg/controller" "worktest/pkg/model" "worktest/pkg/redistab" @@ -25,6 +28,8 @@ type DetailReq struct { var synMysqlToRedisLock sync.RWMutex +var buych = make(chan int,1) + func MysqlList(c *gin.Context) { var req ListReq if err := c.ShouldBind(&req); err != nil { @@ -42,15 +47,13 @@ func MysqlList(c *gin.Context) { } -func RedisBuy(c *gin.Context) { +func NormalMysqlBuy(c *gin.Context) { var req DetailReq if err := c.ShouldBind(&req); err != nil { controller.Error(c, err.Error()) return } - var list []*model.Product - model.DB.Model(&model.Product{}).Find(&list) err := model.DB.Transaction(func(tx *gorm.DB) error { var obj *model.Product @@ -72,8 +75,57 @@ func RedisBuy(c *gin.Context) { } //扣除库存 - model.DB.Model(&model.Product{}).Where(&model.Product{ID: req.ID}).UpdateColumn("stock_size", gorm.Expr("stock_size - 1")) + if err := model.DB.Model(&model.Product{}).Where(&model.Product{ID: req.ID}). + UpdateColumn("stock_size", gorm.Expr("stock_size - 1")).Error;err != nil{ + return err + } + return nil + }) + + if err != nil { + controller.Error(c, err.Error()) + return + + } + + controller.Success(c, nil) + + return + +} +func MysqlBuy(c *gin.Context) { + var req DetailReq + if err := c.ShouldBind(&req); err != nil { + controller.Error(c, err.Error()) + return + } + + + err := model.DB.Transaction(func(tx *gorm.DB) error { + var obj *model.Product + if err := tx.Model(&model.Product{}).First(&obj, req.ID).Error; err != nil { + return err + } + + if obj.StockSize <= 0 { + return errors.New("库存不足") + } + + //生成订单 + productOrder := &model.ProductOrder{ + ProductID: req.ID, + UserId: req.UserId, + } + if err := tx.Model(&model.ProductOrder{}).Create(&productOrder).Error; err != nil { + return err + } + + //扣除库存 + if err := tx.Model(&model.Product{}).Where(&model.Product{ID: req.ID}). + UpdateColumn("stock_size", gorm.Expr("stock_size - 1")).Error;err != nil{ + return err + } return nil }) @@ -83,23 +135,256 @@ func RedisBuy(c *gin.Context) { } - fmt.Println("2--------", redistab.RedisClient) + controller.Success(c, nil) - controller.Success(c, list) + return + +} + +//GoroutineBuy redis事务购买 +func GoroutineBuy(c *gin.Context) { + var req DetailReq + if err := c.ShouldBind(&req); err != nil { + controller.Error(c, err.Error()) + return + } + + stackKey:=redistab.GetProductStockSize(req.ID) + orderKey:=redistab.GetProductOrderList(req.ID) + saleKey:=redistab.GetProductSaleNum(req.ID) + + select { + case buych <-1: + // 从ch中读取到数据 + default: + controller.Error(c, "请稍后购买") + return + } + + defer func() { + <-buych + }() + + n, err := redistab.RedisClient.Get(stackKey).Int64() + if err != nil && err != redis.Nil { + controller.Error(c, err.Error()) + return + } + + if n<=0 { + controller.Error(c, "库存不足") + return + } + + //扣除库存 + redistab.RedisClient.Decr(stackKey) + + //增加订单 + redistab.RedisClient.LPush(orderKey,fmt.Sprintf(`{"productId":%d,"userId":%d}`,req.ID,req.UserId)) + + //记录已售出 + redistab.RedisClient.Incr(saleKey) + + if err != nil { + controller.Error(c, err.Error()) + return + } + + + controller.Success(c, nil) return +} + + +//RedisLockBuy redis事务购买 +func RedisLockBuy(c *gin.Context) { + var req DetailReq + if err := c.ShouldBind(&req); err != nil { + controller.Error(c, err.Error()) + return + } + + stackKey:=redistab.GetProductStockSize(req.ID) + orderKey:=redistab.GetProductOrderList(req.ID) + saleKey:=redistab.GetProductSaleNum(req.ID) + lockKey:=redistab.GetProductLockRedisKey(req.ID) + uuidStr := getUuid() + + //两个锁 + isSet, err :=redistab.RedisClient.SetNX(lockKey,uuidStr,30*time.Second).Result() + if err != nil{ + controller.Error(c, err.Error()) + return + } + + if !isSet { + controller.Error(c, "请稍后重试") + return + } + + /* + defer func(lockKey,uuidStr string) { + temp,err:= redistab.RedisClient.Get(lockKey).Result() + if err == nil && temp == uuidStr{//存在可能释放了别人的锁 + redistab.RedisClient.Del(lockKey) + } + }(lockKey,uuidStr) + */ + + //lua脚本释放 + defer func(lockKey,uuidStr string) { + + luaUnLockStr := "if redis.call('get',KEYS[1]) == ARGV[1] then " + + "return redis.call('del',KEYS[1]) else return 0 end" + //return jedis.eval(luaScript, Collections.singletonList(key), Collections.singletonList(value)).equals(1L); + script := redis.NewScript(luaUnLockStr) + + // Call the Lua script and pass the key as an argument + result, err := script.Run(redistab.RedisClient, []string{lockKey},uuidStr).Result() + fmt.Println(result,err) + + }(lockKey,uuidStr) + + + n, err := redistab.RedisClient.Get(stackKey).Int64() + if err != nil && err != redis.Nil { + controller.Error(c, err.Error()) + return + } + + if n<=0 { + controller.Error(c, "库存不足") + return + } + + //扣除库存 + redistab.RedisClient.Decr(stackKey) + + //增加订单 + redistab.RedisClient.LPush(orderKey,fmt.Sprintf(`{"productId":%d,"userId":%d}`,req.ID,req.UserId)) + + //记录已售出 + redistab.RedisClient.Incr(saleKey) + if err != nil { + controller.Error(c, err.Error()) + return + } + + + controller.Success(c, nil) + + return } -func MysqlBuy(c *gin.Context) { + +//RealRedisTxBuy redis事务购买 +func RealRedisTxBuy(c *gin.Context) { + +} + +//RedisTxBuy redis事务购买 +func RedisTxBuy(c *gin.Context) { var req DetailReq if err := c.ShouldBind(&req); err != nil { controller.Error(c, err.Error()) return } - var list []*model.Product - model.DB.Model(&model.Product{}).Find(&list) + stackKey:=redistab.GetProductStockSize(req.ID) + orderKey:=redistab.GetProductOrderList(req.ID) + saleKey:=redistab.GetProductSaleNum(req.ID) + + err := redistab.RedisClient.Watch(func(tx *redis.Tx) error { + n, err := tx.Get(stackKey).Int64() + + fmt.Println("2--------",n,err) + if err != nil { + return err + } + + if n <= 0 { + return errors.New("库存不足") + } + + _, err = tx.TxPipelined( func(pipeliner redis.Pipeliner) error { + + v,err:=pipeliner.Decr(stackKey).Result() + fmt.Println() + fmt.Println("1----------",v,err) + + //增加订单 + pipeliner.LPush(orderKey,fmt.Sprintf(`{"productId":%d,"userId":%d}`,req.ID,req.UserId)) + + //记录已售出 + pipeliner.Incr(saleKey) + + return nil + }) + + + return err + }, stackKey) + fmt.Println() + fmt.Println() + fmt.Println() + fmt.Println() + + if err != nil { + fmt.Println(err.Error()) + controller.Error(c, err.Error()) + return + } + + fmt.Println("2--------", redistab.RedisClient) + + controller.Success(c, nil) + + return +} + +//RedisTxBuy redis事务购买 +func ChainBuy(c *gin.Context) { + var req DetailReq + if err := c.ShouldBind(&req); err != nil { + controller.Error(c, err.Error()) + return + } + + stackKey:=redistab.GetProductStockSize(req.ID) + orderKey:=redistab.GetProductOrderList(req.ID) + saleKey:=redistab.GetProductSaleNum(req.ID) + + + redistab.RedisClient.Watch(func(tx *redis.Tx) error { + n, err := tx.Get(stackKey).Int64() + if err != nil && err != redis.Nil { + return err + } + + if n<=0 { + return errors.New("库存不足") + } + + //扣除库存 + tx.Decr(stackKey) + + //增加订单 + tx.LPush(orderKey,fmt.Sprintf(`{"productId":%d,"userId":%d}`,req.ID,req.UserId)) + + //记录已售出 + tx.Incr(saleKey) + + /* + _, err = tx.Pipelined(func(pipe redis.Pipeliner) error { + pipe.Set(key, strconv.FormatInt(n+1, 10), 0) + return nil + }) + */ + + return err + }, stackKey) err := model.DB.Transaction(func(tx *gorm.DB) error { var obj *model.Product @@ -134,24 +419,156 @@ func MysqlBuy(c *gin.Context) { fmt.Println("2--------", redistab.RedisClient) - controller.Success(c, list) + controller.Success(c, "购买成功") return - } func RedisList(c *gin.Context) { var req ListReq + var productKeys []string + var productList []*model.Product if err := c.ShouldBind(&req); err != nil { controller.Error(c, err.Error()) return } - var list []*model.Product - model.DB.Model(&model.Product{}).Find(&list) - fmt.Println("2--------", redistab.RedisClient) + //vals, err :=redistab.RedisClient.ZRange(redistab.GetProductIdZet(),0,-1).Result() + //fmt.Println("1--------",vals,err) + + //vals, err =redistab.RedisClient.ZRange(redistab.GetProductIdZet(),0,1).Result() + //fmt.Println("1--------",vals,err) + + //vals, err =redistab.RedisClient.ZRange(redistab.GetProductIdZet(),3,5).Result() + //fmt.Println("1--------",vals,err) + /* + //hash单个 + result, err := client.HGetAll("user").Result() + if err != nil { + panic(err) + } + user := User{ + Name: result["name"], + Email: result["email"], + Age: strconv.Atoi(result["age"]), + } + + + //hash单个 + keys := []string{"user:0", "user:1"} + results, err := client.MGet(keys...).Result() + if err != nil { + panic(err) + } + + var users []User + for _, result := range results { + data, ok := result.(map[string]interface{}) + if ok { + user := User{ + Name: data["name"].(string), + Email: data["email"].(string), + Age: int(data["age"].(int64)), + } + users = append(users, user) + } + } + */ + + vals, err :=redistab.RedisClient.ZRevRange(redistab.GetProductIdZet(),0,-1).Result() + fmt.Println("1--------",vals,err) + + for _,t:= range vals{ + tempId, err := strconv.Atoi(t) + if err == nil{ + productKeys = append(productKeys,redistab.GetProductDetail(uint(tempId))) + } + } + + // 输出结果 + values,err:=redistab.RedisClient.MGet(productKeys...).Result() + if err != nil { + controller.Error(c, err.Error()) + return + } + + if len(values) <= 0 { + controller.Error(c, "") + return + } + + for _,t := range values{ + var temp *model.Product + if t != nil{ + err = json.Unmarshal([]byte(t.(string)),&temp) + if err != nil{ + continue + } + productList = append(productList,temp) + } + + } + + controller.Success(c, productList) + + //统计人获取的次数 + return +} + +func RecList(c *gin.Context) { + var req ListReq + var productKeys []string + var productList []*model.Product + if err := c.ShouldBind(&req); err != nil { + controller.Error(c, err.Error()) + return + } + + //vals, err :=redistab.RedisClient.ZRange(redistab.GetProductIdZet(),0,-1).Result() + //fmt.Println("1--------",vals,err) + + //vals, err =redistab.RedisClient.ZRange(redistab.GetProductIdZet(),0,1).Result() + //fmt.Println("1--------",vals,err) + + //vals, err =redistab.RedisClient.ZRange(redistab.GetProductIdZet(),3,5).Result() + //fmt.Println("1--------",vals,err) + + vals, err :=redistab.RedisClient.ZRevRange(redistab.GetProductIdZet(),0,-1).Result() + fmt.Println("1--------",vals,err) + + for _,t:= range vals{ + tempId, err := strconv.Atoi(t) + if err == nil{ + productKeys = append(productKeys,redistab.GetProductDetail(uint(tempId))) + } + } + + // 输出结果 + values,err:=redistab.RedisClient.MGet(productKeys...).Result() + if err != nil { + controller.Error(c, err.Error()) + return + } + + if len(values) <= 0 { + controller.Error(c, "") + return + } + + for _,t := range values{ + var temp *model.Product + if t != nil{ + err = json.Unmarshal([]byte(t.(string)),&temp) + if err != nil{ + continue + } + productList = append(productList,temp) + } + + } + + controller.Success(c, productList) - controller.Success(c, list) //统计人获取的次数 return } @@ -349,6 +766,67 @@ func RedisCountProduct(c *gin.Context) { return } +func RedisDetailBitMapCount1(c *gin.Context) { + var productObj *model.Product + var req DetailReq + + if err := c.ShouldBind(&req); err != nil { + controller.Error(c, err.Error()) + return + } + + v, err := redistab.RedisClient.Get(redistab.GetProductDetail(req.ID)).Result() + + //bitmap + bit, err := redistab.RedisClient.GetBit(redistab.GetProductBitMap(), int64(req.ID)).Result() + + fmt.Println("2----------", bit, err) + if bit == 0 { //不存在 + controller.Error(c, "非法数据") + return + } + + //无缓存 则去mysql同步数据 + if err != nil { + synMysqlToRedisLock.Lock() + defer synMysqlToRedisLock.Lock() + + var tempObj *model.Product + if err := model.DB.Model(&model.Product{}).First(&tempObj, req.ID).Error; err != nil { + controller.Error(c, err.Error()) + return + } + + b, err := json.Marshal(tempObj) + if err != nil { + controller.Error(c, err.Error()) + return + } + + v, err = redistab.RedisClient.Set(redistab.GetProductDetail(req.ID), string(b), 0).Result() + fmt.Println("1---------", v, err) + + if err != nil { + controller.Error(c, err.Error()) + return + } + + v, _ = redistab.RedisClient.Get(redistab.GetProductDetail(req.ID)).Result() + } + + err = json.Unmarshal([]byte(v), &productObj) + if err != nil { + controller.Error(c, err.Error()) + return + } + + //增加统计,增加去重统计 该商品的DAU + _, _ = redistab.RedisClient.PFAdd(redistab.GetProductDetailLogCount(req.ID)).Result() + + controller.Success(c, productObj) + return +} + func RedisDetailBitMapCount(c *gin.Context) { var productObj *model.Product var req DetailReq @@ -409,3 +887,9 @@ func RedisDetailBitMapCount(c *gin.Context) { controller.Success(c, productObj) return } + +func getUuid() string{ + id := uuid.NewV4() + return id.String() + +} diff --git a/pkg/controller/product_backend.go b/pkg/controller/product_backend.go index b2eb96a..75013b0 100644 --- a/pkg/controller/product_backend.go +++ b/pkg/controller/product_backend.go @@ -4,7 +4,9 @@ import ( "encoding/json" "fmt" "github.com/gin-gonic/gin" + "github.com/go-redis/redis" "strconv" + "time" "worktest/pkg/model" "worktest/pkg/redistab" ) @@ -152,6 +154,11 @@ func SynCache(c *gin.Context) { model.DB.Model(&model.Product{}).Find(&list) fmt.Println("2--------", redistab.RedisClient) + + redistab.RedisClient.Del(redistab.GetProductBitMap()) + redistab.RedisClient.Del(redistab.GetProductIdSet()) + redistab.RedisClient.Del(redistab.GetProductIdZet()) + for _, t := range list { v, err := redistab.RedisClient.Set(redistab.GetProductStockSize(t.ID), t.StockSize, 0).Result() fmt.Println("1---------", v, err) @@ -161,13 +168,49 @@ func SynCache(c *gin.Context) { fmt.Println("1---------", v, err) //同步bitmap - redistab.RedisClient.Del(redistab.GetProductBitMap(), string(temp)) b, err := redistab.RedisClient.SetBit(redistab.GetProductBitMap(), int64(t.ID), 1).Result() fmt.Println("1---------", b, err) + //id同步到set + redistab.RedisClient.SAdd(redistab.GetProductIdSet(),t.ID) + + //id同步到zset + redistab.RedisClient.ZAdd(redistab.GetProductIdZet(),redis.Z{Score: float64(t.ID), Member: t.ID}) + } + //同步列表 + Success(c, list) return } + +func PublishMsg(c *gin.Context) { + + for i := 0 ; i<=10 ; i++ { + msg := fmt.Sprintf("消息标号:%d号",i) + _= redistab.RedisClient.Publish(redistab.GetChannelKey(),msg) // 订阅 mychannel + } + + + Success(c, nil) + + return +} + +func PublishMsgTime(c *gin.Context) { + + for i := 0 ; i<=10 ; i++ { + time.Sleep(100*time.Microsecond) + msg := fmt.Sprintf("消息标号:%d号",i) + _= redistab.RedisClient.Publish(redistab.GetChannelKey(),msg) // 订阅 mychannel + } + + + + + Success(c, nil) + + return +} diff --git a/pkg/model/product.go b/pkg/model/product.go index ed64773..2ab1977 100644 --- a/pkg/model/product.go +++ b/pkg/model/product.go @@ -15,6 +15,6 @@ type Product struct { // ProductOrder type ProductOrder struct { ID uint `gorm:"primarykey"` - ProductID uint `gorm:"column:ProductID" json:"ProductID"` - UserId uint `gorm:"column:UserID" json:"UserID"` + ProductID uint `gorm:"column:product_id" json:"productID"` + UserId uint `gorm:"column:user_id" json:"userID"` } diff --git a/pkg/redistab/redis.go b/pkg/redistab/redis.go index 2f5bc2c..483a363 100644 --- a/pkg/redistab/redis.go +++ b/pkg/redistab/redis.go @@ -7,7 +7,7 @@ var RedisClient *redis.Client func init() { RedisClient = redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", - Password: "", + Password: "123456", PoolSize: 20, DB: 1, }) diff --git a/pkg/redistab/redis_key.go b/pkg/redistab/redis_key.go index 4644cfb..9f332fb 100644 --- a/pkg/redistab/redis_key.go +++ b/pkg/redistab/redis_key.go @@ -25,6 +25,26 @@ func GetProductBitMap() string { return fmt.Sprintf("product:bitmap") } +func GetProductIdSet() string { + return fmt.Sprintf("product:id:set") +} + +func GetProductIdZet() string { + return fmt.Sprintf("product:id:zset") +} + func GetProductDetailLogCount(productId uint) string { return fmt.Sprintf("product:log_count:%d", productId) } + +//某个商品的锁 +func GetProductLockRedisKey(productId uint) string { + return fmt.Sprintf("product:lock:%d", productId) +} + +//某个商品的锁 +func GetChannelKey() string { + return fmt.Sprintf("product:channel" ) +} + + diff --git a/pkg/router/router.go b/pkg/router/router.go index b50c4ab..afbcd33 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -18,14 +18,19 @@ func RegisterRoute(router *gin.Engine) { backend.POST("all/cache/remove", controller.RemoveAllCache) backend.POST("cache/remove", controller.RemoveCache) backend.POST("syn/cache", controller.SynCache) + + backend.POST("publish/msg", controller.PublishMsg) + backend.POST("publish/time/msg", controller.PublishMsgTime) } { + //首页列表部分 backend := v1.Group("/front") backend.POST("mysql/list", front.MysqlList) //数据库连接 backend.POST("redis/list", front.RedisList) //缓存连接 string/hash - backend.POST("redis/", controller.List) //缓存 - backend.POST("getlist", controller.List) //mysql + backend.POST("rec/list", front.RecList) //推荐列表 list + //#backend.POST("change/rec/list", front.List) //推荐列表 list + //#backend.POST("getlist", front.List) //推荐去除已买连接 set/zset // jemter 对比 backend.POST("mysql/detail", front.MysqlDetail) //数据库连接 @@ -35,25 +40,20 @@ func RegisterRoute(router *gin.Engine) { backend.POST("redis/bitmapcount/detail", front.RedisDetailBitMapCount) //redis单商品详情 锁保护 bitmap缓存击穿保护 增加访问统计 //增加bitmap的统计等 - backend.POST("redis/bitmapcount/productcount", front.RedisCountProduct) //redis单商品详情 锁保护 bitmap缓存击穿保护 增加访问统计 - - backend.POST("mysql/buy", front.MysqlBuy) //抢购 mysql事务 - backend.POST("redis/tx/buy", front.RedisBuy) //redistab 事务抢购 watch unwatch mutile exec - backend.POST("redis/lock/buy", controller.List) //redistab 分布式锁抢购 setNx lua - backend.POST("gortout/buy", controller.List) //go语言锁抢购,协程模拟锁 - backend.POST("getlist", controller.List) //go语言锁抢购 - - /* + backend.POST("redis/bitmapcount/productcount", front.RedisCountProduct) //redis 增加bitmap的统计等 - backend.POST("getlist", controller.List) //推荐列表 list - backend.POST("getlist", controller.List) //推荐去除已买连接 set/zset - //jemter对比 - backend.POST("getlist", controller.List) //websocket redistab 广播 receive - backend.POST("getlist", controller.List) //websocket redistab 广播 chain - backend.POST("getlist", controller.List) //websocket go通信 协程通信 + //抢购 watch事务 锁 list(未演示) + backend.POST("mysql/buy", front.NormalMysqlBuy) //抢购 mysql事务 + backend.POST("mysql/tx/buy", front.MysqlBuy) //抢购 mysql事务 + backend.POST("redis/tx/buy", front.RedisTxBuy) //redistab 事务抢购 watch unwatch mutile exec + backend.POST("redis/lock/buy", front.RedisLockBuy) //redistab 分布式锁抢购 setNx lua + backend.POST("channelLock/buy", front.GoroutineBuy ) //go语言锁抢购,协程模拟锁 分布部署则失效 + //backend.POST("getlist", controller.List) //go语言锁抢购 略 - */ + //广播 + backend.GET("channel/receive", front.RedisRecWebsocket) //websocket redistab 广播 receive + backend.GET("redis/receive", front.RedisWebsocket) //websocket redistab 广播 chain }