go-channel處理高並發請求


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM