「あなたの質問にすべて答えます」フラッシュセールシステムでコンカレントプログラミングを実践しよう!

すべての質問に答えてください

まとめ

この記事では、Go 言語の同時実行プリミティブを使用して、単純な高同時実行フラッシュ セール システムを構築する方法を紹介します。

Go 言語のネイティブ ライブラリと、ミューテックス、チャネル、カウンターなどの一般的な技術的手段を使用して、同時アクセスとデータの一貫性の問題を解決します。

この記事は単なる単純な例であり、ビジネス シナリオにおける Go 言語の同時実行プリミティブの適用に焦点を当てています。

実際のアプリケーションでは、データベース トランザクション、分散ロック、電流制限などの問題も考慮する必要があります。以前にも記事を書きましたので、記事の最後に貼っておきます。

1 はじめに

フラッシュ セール システムは、多数の同時リクエストを処理し、データの一貫性を確保する必要がある、同時実行性の高いシナリオにおける特別なアプリケーションです。この記事では、Go 言語の同時実行プリミティブを使用して、ユーザーのニーズを満たし、システムの安定性を確保する高同時実行フラッシュ セール システムを構築する方法を紹介します。

2. アーキテクチャ設計

当社のフラッシュセールシステムは、古典的なクライアントサーバーアーキテクチャを使用します。クライアントはフラッシュセールリクエストを送信し、サーバーはリクエストを処理して在庫を更新します。システムの高い同時実行パフォーマンスを確保するために、次のテクノロジとプリミティブを使用します。

  • ミューテックス ロック ( sync.Mutex): 共有リソースへの同時アクセスを保護するために使用されます。
  • チャネル ( channel): コルーチン間の通信に使用されます。
  • カウンタ ( sync.WaitGroup): すべてのリクエストが完了するのを待つために使用されます。

3. 導入手順

フラッシュ セール システムを実装するための重要な手順は次のとおりです。

3.1 インベントリの初期化

システムが起動したら、商品の在庫を初期化する必要があります。

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

3.2 フラッシュセールリクエストの処理

クライアントがフラッシュ セール リクエストを送信すると、サーバーはリクエストを処理して在庫を更新する必要があります。

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 同時実行制御と待機

同時リクエストの数を制御するには、カウンターとチャネルを使用して同時実行を制限します。

var wg sync.WaitGroup

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

3.4 ミューテックスロックと同時実行の安全性

同時アクセスの安全性を確保するために、ミューテックス ロックを使用して共有リソースへのアクセスを保護します。

注: TryLock() は go1.18 で導入されました

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

func releaseLock() {
    
    
    mu.Unlock()
}

4. 完全なコード

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
}

コードを解析する

var ch = make(chan bool, 1000): 1000 個のゴルーチンが後で開かれたため、ここのチャネルのバッファーは 1000 に設定されます

p.releaseLock(): ロックが失敗して存在しないロックのロックが解除される可能性があるため、ここでは遅延は使用できません。

p.TryLock(): メソッドを使用してロックの取得を試みます。ロックの取得に成功した場合は、Lock() オペレーションを実行したことと同じ、つまりロックが成功したことになります。

5. 走行結果

6. まとめ

Go 言語の同時実行プリミティブを使用することで、同時実行性の高いフラッシュ セール システムを構築することに成功しました。

ミューテックスやカウンターなどのプリミティブを使用して、同時実行制御、データの一貫性、および同時実行の安全性を実現します。これらのプリミティブは、同時実行性が高いシナリオでの同時アクセスの問題を解決し、システムの安定性とパフォーマンスを確保するのに役立ちます。

この記事は単なる例であり、実際のフラッシュ セール システムには、さらに多くのビジネス ロジックと同時実行制御が含まれる場合があります。

実際のアプリケーションでは、データベース トランザクション、分散ロック、電流制限などの問題も考慮する必要があります。したがって、実際のニーズとシナリオに基づいて、より詳細な設計と実装を行うことをお勧めします。

以前に10,000文字の長い記事を書いてまとめましたので、興味のある方はぜひご覧ください:10,000文字の詳しい解説:フラッシュセールシステムの設計

一緒に勉強する

皆さん、私のアカウントをフォローしてください。あなたのサポートが記事を更新する最大の動機です。

また、私の公式アカウント「Programmer's Journey to Promotion and Salary Increase」をフォローして、Go 学習やインタビューの資料をさらに受け取ることもできます。

WeChat ID: wangzhongyang1993

おすすめ

転載: blog.csdn.net/w425772719/article/details/134669834