**:
寫在前面
go語言作為新興的語言,最近發展勢頭很是迅猛,其最大的特點就是原生支援併發。它使用的是「協程(goroutine)模型」,和傳統基於 os 執行緒和程序實現不同,go
語言的併發是基於使用者態的併發,這種併發方式就變得非常輕量,能夠輕鬆執行幾萬併發邏輯。
go 的併發屬於 csp 併發模型的一種實現,csp 併發模型的核心概念是:「不要通過共享記憶體來通訊,而應該通
過通訊來共享記憶體」。這在 go 語言中的實現就是 goroutine 和 channel。
場景描述
在一些場景下,有大規模請求(十萬或百萬級qps),我們處理的請求可能不需要立馬知道結果,例如資料的打點,檔案的上傳等等。這時候我們需要非同步化處理。常用的方法有使用resque、mq、rabbitmq等。這裡我們在golang語言裡進行設計實踐。
方案演進
直接使用goroutine
在go語言原生併發的支援下,我們可以直接使用乙個goroutine(如下方式)去並行處理這個請求。但是,這種方法明顯有些不好的地方,我們沒法控制goroutine產生數量,如果處理程式稍微耗時,在單機萬級十萬級qps請求下,goroutine大規模爆發,記憶體暴漲,處理效率會很快下降甚至引發程式崩潰。
...
go handle(request)
...
goroutine協同帶快取的管道
var queue = make(chan job, max_queue_size)
go func()
}}()
job := &job
queue
講真,這種方法使用了緩衝佇列一定程度上了提高了併發,但也是治標不治本,大規模併發只是推遲了問題的發生時間。當請求速度遠大於佇列的處理速度時,緩衝區很快被打滿,後面的請求一樣被堵塞了。
job佇列+工作池
只用緩衝佇列不能解決根本問題,這時候我們可以參考一下執行緒池的概念,定乙個工作池(協程池),來限定最大goroutine數目。每次來新的job時,從工作池裡取出乙個可用的worker來執行job。這樣一來即保障了goroutine的可控性,也盡可能大的提高了併發處理能力。
我們分別維護乙個全域性的job佇列和工作池。
var (
jobqueue jobchan
workerpool workerchan
)
type worker struct
func (w *worker) start()
// recieve quit event, stop worker
case
return}}
}()}
實現乙個分發器(dispatcher)。分發器包含乙個worker的指標陣列,啟動時例項化並啟動最大數目的worker,然後從job佇列中不斷取job選擇可用的worker來執行job。
type dispatcher struct
func (d *dispatcher) run()
for (job)
// stop dispatcher
case
return}}
}
感謝handling 1 million requests per minute with go這篇文章給予的巨大啟發。 Golang百萬級高併發實踐
go語言作為新興的語言,最近發展勢頭很是迅猛,其最大的特點就是原生支援併發。它使用的是 協程 goroutine 模型 和傳統基於 os 執行緒和程序實現不同,go 語言的併發是基於使用者態的併發,這種併發方式就變得非常輕量,能夠輕鬆執行幾萬併發邏輯。go 的併發屬於 csp 併發模型的一種實現,c...
Golang百萬級高併發實踐
go語言作為新興的語言,最近發展勢頭很是迅猛,其最大的特點就是原生支援併發。它使用的是 協程 goroutine 模型 和傳統基於 os 執行緒和程序實現不同,go 語言的併發是基於使用者態的併發,這種併發方式就變得非常輕量,能夠輕鬆執行幾萬併發邏輯。go 的併發屬於 csp 併發模型的一種實現,c...
Golang百萬級高併發實踐
寫在前面 go語言作為新興的語言,最近發展勢頭很是迅猛,其最大的特點就是原生支援併發。它使用的是 協程 goroutine 模型 和傳統基於 os 執行緒和程序實現不同,go 語言的併發是基於使用者態的併發,這種併發方式就變得非常輕量,能夠輕鬆執行幾萬併發邏輯。go 的併發屬於 csp 併發模型的一...