Heim >Backend-Entwicklung >Golang >Detaillierte Erläuterung, wie der Bestandsabzug in Go implementiert wird (mehrere Methoden)
Dieser Artikel wurde in der Kolumne „Golang-Tutorial“ verfasst, um Ihnen verschiedene Implementierungsmethoden für den Go-Inventarabzug vorzustellen. Ich hoffe, dass er Freunden in Not hilfreich sein wird!
Grpc, Proto, Gorm, Zap, Go-Redis, Go-Redsync und andere Pakete werden hier verwendetvar m sync.Mutexfunc (*InventoryServer) LockSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) { tx := global.DB.Begin() m.Lock() for _, good := range req.GoodsInfo { var i model.Inventory if result := global.DB.Where(&model.Inventory{Goods: good.GoodsId}).First(&i); result.RowsAffected == 0 { tx.Rollback() // 回滚 return nil, status.Errorf(codes.InvalidArgument, "未找到此商品的库存信息。") } if i.Stocks < good.Num { tx.Rollback() return nil, status.Errorf(codes.ResourceExhausted, "此商品的库存不足") } i.Stocks -= good.Num tx.Save(&i) } tx.Commit() m.Unlock() return &emptypb.Empty{}, nil}
func (*InventoryServer) ForUpdateSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) { tx := global.DB.Begin() for _, good := range req.GoodsInfo { var i model.Inventory if result := tx.Clauses(clause.Locking{ Strength: "UPDATE", }).Where(&model.Inventory{Goods: good.GoodsId}).First(&i); result.RowsAffected == 0 { tx.Rollback() return nil, status.Errorf(codes.InvalidArgument, "未找到此商品的库存信息。") } if i.Stocks < good.Num { tx.Rollback() return nil, status.Errorf(codes.ResourceExhausted, "此商品的库存不足") } i.Stocks -= good.Num tx.Save(&i) } tx.Commit() return &emptypb.Empty{}, nil}
func (*InventoryServer) VersionSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) { tx := global.DB.Begin() for _, good := range req.GoodsInfo { var i model.Inventory for { // 并发请求相同条件比较多,防止放弃掉一些请求 if result := global.DB.Where(&model.Inventory{Goods: good.GoodsId}).First(&i); result.RowsAffected == 0 { tx.Rollback() return nil, status.Errorf(codes.InvalidArgument, "未找到此商品的库存信息.") } if i.Stocks < good.Num { tx.Rollback() // 回滚 return nil, status.Errorf(codes.ResourceExhausted, "此商品的库存不足") } i.Stocks -= good.Num version := i.Version + 1 if result := tx.Model(&model.Inventory{}). Select("Stocks", "Version"). Where("goods = ? and version= ?", good.GoodsId, i.Version). Updates(model.Inventory{Stocks: i.Stocks, Version: version}); result.RowsAffected == 0 { zap.S().Info("库存扣减失败!") } else { break } } } tx.Commit() // 提交 return &emptypb.Empty{}, nil}
func (*InventoryServer) RedisSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) { // redis 分布式锁 pool := goredis.NewPool(global.Redis) rs := redsync.New(pool) tx := global.DB.Begin() for _, good := range req.GoodsInfo { mutex := rs.NewMutex(fmt.Sprintf("goods_%d", good.GoodsId)) if err := mutex.Lock(); err != nil { return nil, status.Errorf(codes.Internal, "redis:分布式锁获取异常") } var i model.Inventory if result := global.DB.Where(&model.Inventory{Goods: good.GoodsId}).First(&i); result.RowsAffected == 0 { tx.Rollback() return nil, status.Errorf(codes.InvalidArgument, "未找到此商品的库存信息") } if i.Stocks < good.Num { tx.Rollback() return nil, status.Errorf(codes.ResourceExhausted, "此商品的库存不足") } i.Stocks -= good.Num tx.Save(&i) if ok, err := mutex.Unlock(); !ok || err != nil { return nil, status.Errorf(codes.Internal, "redis:分布式锁释放异常") } } tx.Commit() return &emptypb.Empty{}, nil}
func main() { var w sync.WaitGroup w.Add(20) for i := 0; i < 20; i++ { go TestForUpdateSell(&w) // 模拟并发请求 } w.Wait()}func TestForUpdateSell(wg *sync.WaitGroup) { defer wg.Done() _, err := invClient.Sell(context.Background(), &proto.SellInfo{ GoodsInfo: []*proto.GoodsInvInfo{ {GoodsId: 16, Num: 1}, //{GoodsId: 16, Num: 10}, }, }) if err != nil { panic(err) } fmt.Println("库存扣减成功")}
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung, wie der Bestandsabzug in Go implementiert wird (mehrere Methoden). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!