對于大部分應用,盡可能快地響應請求是首要任務。例如,應用程序可能正在服務用戶的HTTP請求,或者檢索復制的數據塊。 在這些情況下,你需要做出權衡:是將請求復制到多個處理程序(無論是goutoutine,進程還是服務器),并且其中一個將比其他處理程序返回更快呢,還是立即返回結果——缺點是必須考慮如何高效利用資源來保持處理程序的多個副本同時運行。
如果這種復制是在內存中完成的,它耗費的資源可能并不是那么昂貴,但如果處理程序需要復制進程,服務器甚至數據中心,這就完全不一樣了。你必須考慮這樣做成本是否值得。
我們來看看如何在單個進程中復制請求。我們將使用多個goroutines作為請求處理程序,并且goroutine將在1到6納秒之間隨機休眠一段時間以模擬負載。 這將使處理程序在不同時間返回結果,并讓我們看到這樣做能否更高效。
下面這個例子通過10個處理程序復制模擬請求:
```
doWork := func(done <-chan interface{}, id int, wg *sync.WaitGroup, result chan<- int) {
started := time.Now()
defer wg.Done()
// 模擬隨機加載
simulatedLoadTime := time.Duration(1+rand.Intn(5)) * time.Second
select {
case <-done:
case <-time.After(simulatedLoadTime):
}
select {
case <-done:
case result <- id:
}
took := time.Since(started)
// 顯示處理程序將花費多長時間
if took < simulatedLoadTime {
took = simulatedLoadTime
}
fmt.Printf("%v took %v\n", id, took)
}
done := make(chan interface{})
result := make(chan int)
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ { //1
go doWork(done, i, &wg, result)
}
firstReturned := <-result //2
close(done) //3
wg.Wait()
fmt.Printf("Received an answer from #%v\n", firstReturned)
```
1. 我們開啟10個處理程序以處理請求。
2. 抓取處理程序第一個返回的值。
3. 取消所有剩余的處理程序。這確保他們不會繼續做不必要的工作。
這會輸出:
```
4 took 1s
3 took 1s
6 took 2s
8 took 1s
2 took 2s
0 took 2s
7 took 4s
5 took 5s
1 took 3s
9 took 3s
Received an answer from #3
```
在輸出中,我們顯示每個處理程序花費了多久,以便你了解此技術可以節省多少時間。
唯一需要注意的是,所有的處理程序都需要有相同且平等的請求。換句話說,不會出現從無法處理請求的處理程序接收響應時間。正如我所提到的那樣,所有處理者用來完成工作的資源都需要復制。
如果你的處理程序太相似了,那么任何一個出現異常的幾率都會更小。你只應該將這樣的請求復制到具有不同運行時條件的處理程序:不同的進程,計算機,數據存儲路徑或完全訪問不同的數據存儲區。
這樣做的代價可能是昂貴且難以維護。如果速度是你的目標,那這個技術就很有價值了。當然你還需要考慮容錯和可擴展性。
* * * * *
學識淺薄,錯誤在所難免。我是長風,歡迎來Golang中國的群(211938256)就本書提出修改意見。
- 前序
- 誰適合讀這本書
- 章節導讀
- 在線資源
- 第一章 并發編程介紹
- 摩爾定律,可伸縮網絡和我們所處的困境
- 為什么并發編程如此困難
- 數據競爭
- 原子性
- 內存訪問同步
- 死鎖,活鎖和鎖的饑餓問題
- 死鎖
- 活鎖
- 饑餓
- 并發安全性
- 優雅的面對復雜性
- 第二章 代碼建模:序列化交互處理
- 并發與并行
- 什么是CSP
- CSP在Go中的衍生物
- Go的并發哲學
- 第三章 Go的并發構建模塊
- Goroutines
- sync包
- WaitGroup
- Mutex和RWMutex
- Cond
- Once
- Pool
- Channels
- select語句
- GOMAXPROCS
- 結論
- 第四章 Go的并發編程范式
- 訪問范圍約束
- fo-select循環
- 防止Goroutine泄漏
- or-channel
- 錯誤處理
- 管道
- 構建管道的最佳實踐
- 便利的生成器
- 扇入扇出
- or-done-channel
- tee-channel
- bridge-channel
- 隊列
- context包
- 小結
- 第五章 可伸縮并發設計
- 錯誤傳遞
- 超時和取消
- 心跳
- 請求并發復制處理
- 速率限制
- Goroutines異常行為修復
- 本章小結
- 第六章 Goroutines和Go運行時
- 任務調度