"Answer all your questions" Flash Sale System Go Concurrent Programming Practice!

Answer all questions

Summary

This article will introduce how to use the concurrency primitives of the Go language to build a simple high-concurrency flash sale system.

We will use the Go language's native library and some common technical means, including mutexes, channels, counters, etc., to solve the problems of concurrent access and data consistency.

This article is just a simple example, focusing on the application of Go language concurrency primitives in business scenarios.

In practical applications, issues such as database transactions, distributed locks, and current limiting also need to be considered. I have also written an article before, which is attached at the end of the article.

1 Introduction

The flash sale system is a special application in high-concurrency scenarios that needs to handle a large number of concurrent requests and ensure data consistency . This article will introduce how to use the concurrency primitives of the Go language to build a high-concurrency flash sale system to meet the needs of users and ensure the stability of the system.

2. Architecture design

Our flash sale system will use a classic client-server architecture. The client sends a flash sale request, and the server processes the request and updates the inventory. In order to ensure high concurrency performance of the system, we will use the following technologies and primitives:

  • Mutex lock ( sync.Mutex): used to protect concurrent access to shared resources.
  • Channel ( channel): used for communication between coroutines.
  • Counter ( sync.WaitGroup): used to wait for all requests to complete.

3. Implementation steps

The following are the key steps for us to implement the flash sale system:

3.1 Initialize inventory

When the system starts, we need to initialize the inventory of goods.

var stock = 100 // 商品库存
var mu sync.Mutex

3.2 Handling flash sale requests

When a client sends a flash sale request, the server needs to process the request and update the inventory.

func handleRequest(user int) {
    
    
    defer wg.Done()
    if tryAcquireLock() {
    
    
        if stock > 0 {
    
    
            // 执行秒杀逻辑
            stock--
            fmt.Printf("用户%d秒杀成功,剩余库存:%d\n", user, stock)
        } else {
    
    
            fmt.Printf("用户%d秒杀失败,库存不足\n", user)
        }
        releaseLock()
    } else {
    
    
        fmt.Printf("用户%d未获取到锁,秒杀失败\n", user)
    }
}

3.3 Concurrency control and waiting

To control the number of concurrent requests, we use counters and channels to limit concurrency.

var wg sync.WaitGroup

func main() {
    
    
    for i := 1; i <= 1000; i++ {
    
    
        wg.Add(1)
        go handleRequest(i)
    }
    wg.Wait()
}

3.4 Mutex locks and concurrency safety

In order to ensure the safety of concurrent access, we use mutex locks to protect access to shared resources.

Note: TryLock() was introduced in go1.18

func tryAcquireLock() bool {
    
    
    return mu.TryLock()
}

func releaseLock() {
    
    
    mu.Unlock()
}

4. Complete code

package main

import (
 "fmt"
 "sync"
)

//后面开启了1000个goroutine,所以这里channel的缓冲区设置成了1000
var ch = make(chan bool, 1000)

type Product struct {
    
    
 sync.Mutex
 stock int64 // 商品库存
}

func main() {
    
    
 p := Product{
    
    stock: 1000}
 for i := 1; i <= 1000; i++ {
    
    
  go p.handleRequest(i)
 }
 <-ch
}

func (p *Product) handleRequest(user int) {
    
    
 if p.tryAcquireLock() {
    
    
  if p.stock > 0 {
    
    
   // 执行秒杀逻辑
   p.stock--
   fmt.Printf("用户%d秒杀成功,剩余库存:%d\n", user, p.stock)
  } else {
    
    
   fmt.Printf("用户%d秒杀失败,库存不足\n", user)
  }
  //这里是不可以使用defer的,因为可能会加锁失败,unlock一个不存在的锁
  p.releaseLock()
 } else {
    
    
  fmt.Printf("用户%d未获取到锁,秒杀失败\n", user)
 }
}

func (p *Product) tryAcquireLock() bool {
    
    
//p.TryLock() 方法用于尝试获取锁,如果成功获取到锁,则相当于执行了 Lock() 操作,即加锁成功。 
 return p.TryLock()
}

func (p *Product) releaseLock() {
    
    
 p.Unlock()
 ch <- true
}

parse code

var ch = make(chan bool, 1000): 1000 goroutines were opened later, so the buffer of the channel here is set to 1000

p.releaseLock(): Defer cannot be used here, because the lock may fail and unlock a non-existent lock.

p.TryLock(): method is used to try to acquire the lock. If the lock is successfully acquired, it is equivalent to executing the Lock() operation, that is, the lock is successful.

5. Running results

6. Summary

By using the concurrency primitives of the Go language, we successfully built a high-concurrency flash sale system.

Using primitives such as mutexes and counters, we achieve concurrency control, data consistency, and concurrency safety. These primitives help us solve the problem of concurrent access in high concurrency scenarios and ensure the stability and performance of the system.

This article is just a simple example. The actual flash sale system may involve more business logic and concurrency control.

In practical applications, issues such as database transactions, distributed locks, and current limiting also need to be considered. Therefore, it is recommended to conduct more detailed design and implementation based on actual needs and scenarios.

I have written a long article of 10,000 words before to summarize it. Friends who are interested are welcome to check it out: Detailed explanation of 10,000 words: Design of flash sale system

study together

Welcome everyone to follow my account. Your support is my biggest motivation to update my articles!

You are also welcome to follow my official account: Programmer’s Journey to Promotion and Salary Increase to receive more Go learning and interview materials.

WeChat ID: wangzhongyang1993

Guess you like

Origin blog.csdn.net/w425772719/article/details/134669834