go-channel處理高並發請求
最近看了一篇文章講解怎樣使用go-channel的,周末就花了點時間學習了一下,文章原文地址:
http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/ ,然后自己進行了一個簡單的性能測試。
一、Channel簡介
下面是go by example中的 一個簡單的channel使用的例子:
package main
import "fmt"
func main() {
messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}
通道 是連接多個 Go 協程的管道。你可以從一個 Go 協程將值發送到通道,然后在別的 Go 協程中接收。使用 make(chan val-type) 創建一個新的通道。通道類型就是他們需要傳遞值的類型。使用 channel <- 語法 發送 一個新的值到通道中。這里我們在一個新的 Go 協程中發送 "ping" 到上面創建的messages 通道中。使用 <-channel 語法從通道中 接收 一個值。這里將接收我們在上面發送的 "ping" 消息並打印出來。我們運行程序時,通過通道,消息 "ping" 成功的從一個 Go 協程傳到另一個中。
$ go run channels.go
ping
默認發送和接收操作是阻塞的,直到發送方和接收方都准備完畢。這個特性允許我們,不使用任何其它的同步操作,來在程序結尾等待消息 "ping"。channel也有帶緩沖的可以不阻塞直接寫到緩沖去(在緩沖沒有滿的情況下)。更多例子請參考: https://books.studygolang.com/gobyexample/channels/
二、處理包並發請求
上面那篇作者寫的分鍾處理百萬請求文章,代碼摘抄了一部分進行分析。下面是關鍵的幾個數據結構:
- 任務,用來需要表示一個需要處理的邏輯
// Job代表一個任務,根據自己需求定義
type Job struct {
Id string
Payload string
}
- worker, 用來處理任務的實例
// Worker用來處理job的實例
type Worker struct {
WorkerPool chan chan Job //需要注冊到的worker池
JobChannel chan Job //用來接受任務的通道
quit chan bool
}
- Dispatcher, 用來分發job的實例
type Dispatcher struct {
WorkerPool chan chan Job //用來注冊worker的池
MaxWorkers int //worker最大個數
}
初始化邏輯
func NewDispatcher(maxWorkers int) *Dispatcher {
pool := make(chan chan Job, maxWorkers)
return &Dispatcher{
WorkerPool: pool, //初始化worker池
MaxWorkers: maxWorkers,
}
}
func (d *Dispatcher) Run() {
for i := 0; i < d.MaxWorkers; i++ {
//對每一個worker進行初始化,也就是將worker注冊到池中,更直接點就是將每個worker的jobChannel放入到池中
worker := NewWorker(d.WorkerPool)
worker.Start()
}
go d.dispatch()
}
func NewWorker(workerPool chan chan Job) *Worker {
return &Worker{
WorkerPool: workerPool,
JobChannel: make(chan Job),
quit: make(chan bool),
}
}
//Start函數,啟一個goroutine, 啟動的時候將自己注冊到worker池當中,然后就等待job被放到自己的jobChannel中
//一旦jobChannel中有job放入的時候,就開始處理這個job
//同時加入了一個quit channel可以用來控制銷毀這個worker
func (w *Worker) Start() {
go func() {
for {
w.WorkerPool <- w.JobChannel //注冊當前這個worker到worker池中,
// 也就是將自己的jobChannel放入到池中,用來接收job
select {
case job := <-w.JobChannel:
//channel中放入了一個job
if _, err := job.Done(); err != nil {
//處理這個job
}
case <-w.quit:
// 收到停止的信號,銷毀這個worker
return
}
}
}()
}
任務分發邏輯
func (d *Dispatcher) dispatch() {
for {
select {
case job := <-JobQueue:
// 收到一個job
go func(job Job) {
// 從worker池中,選取一個worker的jobChannel,如果worker池中是空的,則會阻塞在這里
jobChannel := <-d.WorkerPool
// 將job放入到其中一個worker的jobChannel中,等待這個worker進行處理
jobChannel <- job
}(job)
}
}
}
三、測試
1、測試工具
ab, 下載地址: https://www.apachehaus.com/cgi-bin/download.plx
2、測試結果
$ ./abs -n 100000 -c 1000 "http://127.0.0.1:8080/job"
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests
Server Software:
Server Hostname: 127.0.0.1
Server Port: 8080
Document Path: /job
Document Length: 0 bytes
Concurrency Level: 1000
Time taken for tests: 51.913 seconds
Complete requests: 100000
Failed requests: 1
(Connect: 1, Receive: 0, Length: 0, Exceptions: 0)
Total transferred: 7500000 bytes
HTML transferred: 0 bytes
Requests per second: 1926.28 [#/sec] (mean)
Time per request: 519.134 [ms] (mean)
Time per request: 0.519 [ms] (mean, across all concurrent requests)
Transfer rate: 141.09 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 7.2 0 1007
Processing: 58 497 187.4 429 1448
Waiting: 6 348 195.3 315 1113
Total: 58 497 187.5 430 1449
Percentage of the requests served within a certain time (ms)
50% 430
66% 435
75% 441
80% 446
90% 901
95% 970
98% 1014
99% 1076
100% 1449 (longest request)
測試結果感覺有點不太理想,不知道是不是因為電腦性能的原因還是參數設置的問題。在不用go-channel的測試和這個性能差不多,大家可以把這個代碼下載下來自己測試一下,比對一下結果看看有沒有優化的空間。
完整代碼下載地址: https://gitee.com/ncuzhangben/GoStudy/tree/master/go-channel