Golang Goroutines 并发处理多个请求

huangapple go评论82阅读模式
英文:

Golang Goroutines Handle Concurrently Multiple Request

问题

用户可以通过我的项目购买产品。但是,购买请求中的产品库存数量必须等于或大于请求的数量。如果满足条件,购买记录将保存在表中。然后,在另一个表中更新该产品的库存数量。因此,有3个操作,如果我从另一个 Golang 应用程序发送 20 个并发请求来购买具有 100 个库存数量的 A 产品,它会出现错误。它在数据库中打开了 20 个购买记录,并将库存数量更新为 60。通常,在处理 5 个请求后,它应该因为产品库存不足而抛出错误。我无法调整这个方法来实现这个目的。我该怎么办?

这是该方法,它调用了存储库方法 3 次,但在那里没有特殊的操作,只有 gorm 的保存和更新操作:

var wg sync.WaitGroup
wg.Add(1)

errs := make(chan error, 1)
go func(ctx context.Context, request *dto.CreateTicketPurchaseRequest) {
	defer wg.Done()
	productStockCount, err := t.stockRepo.GetTicketStockCount(productId) // 首先检查可用库存数量
	if err != nil {
		errs <- err
		return
	}

	if productStockCount.StockCount < payload.Quantity {
		err = fmt.Errorf("没有足够的产品")
		errs <- err
		return
	}

	u := new(entity.TicketPurchases)
	u.Quantity = payload.Quantity
	u.UserId = payload.UserId
	u.StockId = productId
	err = t.purchaseRepo.PurchaseTicket(u) // 如果库存数量正常,则购买产品
	if err != nil {
		errs <- err
		return
	}

	productStockCount.StockCount = productStockCount.StockCount - u.Quantity
	err = t.stockRepo.RemoveStockCount(productStockCount) // 更新产品的库存数量

	if err != nil {
		errs <- err
		return
	}
	close(errs)

}(ctx, payload)
wg.Wait()
if err = <-errs; err != nil {
	return c.JSON(http.StatusBadRequest, nil)
}
英文:

User can purchase products with my project. However, product stock count must be equal or greater then requested quantity in purchase request. If It is ok, then a purchase record is saved on the table. After then, stock count is updated for this product in the another table. So there are 3 operations and if I send 20 concurrently requests to purchase 20 of product A which has 100 stock count in the db from another golang app, it works wrongly. It opens 20 purchase records in db and stock count updated as 60. Normally, after 5 requests operated, it must throws error because of stock count of product. I could not tweak method for this purpose. How Can I do?

This is the method, It calls repository methods 3 times but there is no special operations there only gorm save and update operations:

var wg sync.WaitGroup
wg.Add(1)

errs := make(chan error, 1)
go func(ctx context.Context, request *dto.CreateTicketPurchaseRequest) {
	defer wg.Done()
	productStockCount, err := t.stockRepo.GetTicketStockCount(productId) // first check available stock count
	if err != nil {
		errs &lt;- err
		return
	}

	if productStockCount.StockCount &lt; payload.Quantity {
		err = fmt.Errorf(&quot;no enough products&quot;)
		errs &lt;- err
		return
	}

	u := new(entity.TicketPurchases)
	u.Quantity = payload.Quantity
	u.UserId = payload.UserId
	u.StockId = productId
	err = t.purchaseRepo.PurchaseTicket(u) // if stock count is ok, then purchase product
	if err != nil {
		errs &lt;- err
		return
	}

	productStockCount.StockCount = productStockCount.StockCount - u.Quantity
	err = t.stockRepo.RemoveStockCount(productStockCount) // update stock count for product

	if err != nil {
		errs &lt;- err
		return
	}
	close(errs)

}(ctx, payload)
wg.Wait()
if err = &lt;-errs; err != nil {
	return c.JSON(http.StatusBadRequest, nil)
}

答案1

得分: 1

我假设多线程性能不是关键问题,而且你只在同一个数据库上运行一个应用程序实例。

你可以使用 sync.Mutex 来锁定整个 stockrepo。在进行任何操作之前锁定互斥锁,并在事务完成之前不要解锁。建议使用 deferUnlock

示例代码:

var mu sync.Mutex

func doStuff() {
    mu.Lock()
    defer mu.Unlock()
    
    // 获取票数、检查、更新等操作
}

一个更高效但更复杂的解决方案是按 productID 进行锁定,但这可能超出了你的问题范围。

英文:

I am assuming multi-threaded performance is not critical and you are only running one instance of the app on the same database.

You could use a sync.Mutex to Lock the entire stockrepo. Lock the mutex before doing anything, and don't unlock until the transaction is done. It is recommended to use defer with Unlock.

example:

var mu sync.Mutex

func doStuff() {
    mu.Lock()
    defer mu.Unlock()
    
    // get ticket count, check, update, etc.
}

a more efficient but more complex solution is to lock per productID, but that's a bit more complex and probably out of scope for your questions.

huangapple
  • 本文由 发表于 2022年8月28日 23:02:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/73519783.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定