天天看點

golang一個例子引出的幾個問題1 如何測試用戶端服務端?2 這裡的wg變量有什麼用?3 為什麼要有gate這個channel buffer?4 這裡的atomic什麼作用?5 procs的作用?

這個例子是從go源碼src/pkg/net/rpc/server_test.go截取出來的

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

<code>func benchmarkEndToEndAsync(dial func() (*Client, error), b *testing.B) {</code>

<code>     </code><code>const</code> <code>MaxConcurrentCalls = 100</code>

<code>     </code><code>b.StopTimer()</code>

<code>     </code><code>once.Do(startServer)</code>

<code>     </code><code>client, err := dial()</code>

<code>     </code><code>if</code> <code>err != nil {</code>

<code>          </code><code>b.Fatal(</code><code>"error dialing:"</code><code>, err)</code>

<code>     </code><code>}</code>

<code>     </code><code>// Asynchronous calls</code>

<code>     </code><code>args := &amp;Args{7, 8}</code>

<code>     </code><code>procs := 4 * runtime.GOMAXPROCS(-1)</code>

<code>     </code><code>send := int32(b.N)</code>

<code>     </code><code>recv := int32(b.N)</code>

<code>     </code><code>var</code> <code>wg sync.WaitGroup</code>

<code>     </code><code>wg.Add(procs)</code>

<code>     </code><code>gate := make(chan bool, MaxConcurrentCalls)</code>

<code>     </code><code>res := make(chan *Call, MaxConcurrentCalls)</code>

<code>     </code><code>b.StartTimer()</code>

<code>     </code><code>for</code> <code>p := 0; p &lt; procs; p++ {</code>

<code>          </code><code>go func() {</code>

<code>               </code><code>for</code> <code>atomic.AddInt32(&amp;send, -1) &gt;= 0 {</code>

<code>                    </code><code>gate &lt;- true</code>

<code>                    </code><code>reply :=</code><code>new</code><code>(Reply)</code>

<code>                    </code><code>client.Go(</code><code>"Arith.Add"</code><code>, args, reply, res)</code>

<code>               </code><code>}</code>

<code>          </code><code>}()</code>

<code>               </code><code>for</code> <code>call := range res {</code>

<code>                    </code><code>A := call.Args.(*Args).A</code>

<code>                    </code><code>B := call.Args.(*Args).B</code>

<code>                    </code><code>C := call.Reply.(*Reply).C</code>

<code>                    </code><code>if</code> <code>A+B != C {</code>

<code>                         </code><code>b.Fatalf(</code><code>"incorrect reply: Add: expected %d got %d"</code><code>, A+B, C)</code>

<code>                    </code><code>}</code>

<code>                    </code><code>&lt;-gate</code>

<code>                    </code><code>if</code> <code>atomic.AddInt32(&amp;recv, -1) == 0 {</code>

<code>                         </code><code>close(res)</code>

<code>               </code><code>wg.Done()</code>

<code>     </code><code>wg.Wait()</code>

<code>}</code>

這個代碼用來對rpc的用戶端Go函數進行壓力測試。

這裡有幾個地方值得揣摩下:

先使用startServer(這個函數裡面具體是開啟了一個routine)進行伺服器服務。然後在每個測試用例中啟動server,如果是benchTest的話記得這裡的Timer要在啟動伺服器行為之後再開啟。

wg變量是sync.WaitGroup類型,Add增加計數,Done減少計數,Wait進行阻塞等待,等計數減為0的時候再停止阻塞。

這裡如果不使用WaitGroup進行wait阻塞的話,主routine會先于次routine先結束。會導緻程式提早退出。

是以這裡也給出了一個測試用例中測試異步函數的方法。就是使用WaitGroup

看起來gate好像是沒什麼用啊,如果去掉gate呢?有可能會出現“rpc: discarding Call reply due to insufficient Done chan capacity”

這個gate完全是因為client.Go這個函數,rpc包的client.Go是異步的調用,雖然是異步調用,這個異步調用的最後一個done參數是一個channel buffer。

當client.Go進行完rpc調用後,将信号傳入這個channel buffer。但是這個channel buffer卻是不會阻塞的。

具體看源碼:

golang一個例子引出的幾個問題1 如何測試用戶端服務端?2 這裡的wg變量有什麼用?3 為什麼要有gate這個channel buffer?4 這裡的atomic什麼作用?5 procs的作用?

這裡select加了個default分支,說明了done是非阻塞的。看注釋,作者認為這個buffer的大小容量應該由調用者來保證。rpc包并不保證容量大小。

方法有個兩個:

這個方法就是gate的使用原因了。隻有gate容量有剩餘的時候才會容許調用client.Go

在這個例子中,bench的channel最大隻會是b.N,是以,如果我們配置設定的res的channel buffer大小為b.N也能解決這個問題。

這個方法導緻的效果就是bench的時間變快了,但是mem配置設定增加了。

因為這裡會有多個routine會對send和recive進行操作,這裡就需要保證原子性。

多個并發routine對一個共享變量進行操作有兩種方法,channel和鎖。

這裡當然使用channel也能起到原子操作的效果。sync包的atomic和sync的mutex都是鎖的方式。

是以說這裡其實可以使用channel,mutex,atomic三種方法。

bench test在運作前自身會調用runtime.GOMAXPROCS進行多核的設定,然後再每個處理器中并行運作測試。

這裡的runtime.GOMAXPROCS(-1)是擷取你要跑的cpu核數,這個核數是根據bench test的 -test.cpu設定的。具體可以看下src/testing/testing.go parseCpuList。在沒有設定過GOMAXPROCS和test.cpu的情況下,這裡的runtime.GoMAXPROCS就預設是1。

你可以使用-test.cpu 1,2,4來設定你的壓力測試用例是有幾個cpu,每個cpu是幾核的。

這裡的procs設定為處理器核數的4倍就是為了測試routine能配置設定遠大于核數的個數,這樣每個核承擔的goroutine能大于1。

上面的for循環就是保證起的routine數是足夠的。

本文轉自軒脈刃部落格園部落格,原文連結:http://www.cnblogs.com/yjf512/archive/2013/01/30/2882570.html,如需轉載請自行聯系原作者