이 글은 golang tutorial 칼럼에서 Go 재고 공제의 여러 구현 방법을 소개하기 위해 작성되었습니다. 도움이 필요한 친구들에게 도움이 되길 바랍니다!
Grpc, proto, gorm, zap, go-redis, go-redsync 및 기타 패키지가 여기에 사용됩니다.
var 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("库存扣减成功")}
위 내용은 Go에서 재고 공제를 구현하는 방법에 대한 자세한 설명(다중 방법)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!