golang一个例子引出的几个问题

简介:

这个例子是从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
func benchmarkEndToEndAsync(dial func() (*Client, error), b *testing.B) {
      const  MaxConcurrentCalls = 100
      b.StopTimer()
      once.Do(startServer)
      client, err := dial()
      if  err != nil {
           b.Fatal( "error dialing:" , err)
      }
 
      // Asynchronous calls
      args := &Args{7, 8}
      procs := 4 * runtime.GOMAXPROCS(-1)
      send := int32(b.N)
      recv := int32(b.N)
      var  wg sync.WaitGroup
      wg.Add(procs)
      gate := make(chan bool, MaxConcurrentCalls)
      res := make(chan *Call, MaxConcurrentCalls)
      b.StartTimer()
 
      for  p := 0; p < procs; p++ {
           go func() {
                for  atomic.AddInt32(&send, -1) >= 0 {
                     gate <- true
                     reply := new (Reply)
                     client.Go( "Arith.Add" , args, reply, res)
                }
           }()
           go func() {
                for  call := range res {
                     A := call.Args.(*Args).A
                     B := call.Args.(*Args).B
                     C := call.Reply.(*Reply).C
                     if  A+B != C {
                          b.Fatalf( "incorrect reply: Add: expected %d got %d" , A+B, C)
                     }
                     <-gate
                     if  atomic.AddInt32(&recv, -1) == 0 {
                          close(res)
                     }
                }
                wg.Done()
           }()
      }
      wg.Wait()
}

这个代码用来对rpc的客户端Go函数进行压力测试。

这里有几个地方值得揣摩下:

1 如何测试客户端服务端?

先使用startServer(这个函数里面具体是开启了一个routine)进行服务器服务。然后在每个测试用例中启动server,如果是benchTest的话记得这里的Timer要在启动服务器行为之后再开启。

2 这里的wg变量有什么用?

wg变量是sync.WaitGroup类型,Add增加计数,Done减少计数,Wait进行阻塞等待,等计数减为0的时候再停止阻塞。

这里如果不使用WaitGroup进行wait阻塞的话,主routine会先于次routine先结束。会导致程序提早退出。

因此这里也给出了一个测试用例中测试异步函数的方法。就是使用WaitGroup

3 为什么要有gate这个channel buffer?

看起来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却是不会阻塞的。

具体看源码:

Image(7)

这里select加了个default分支,说明了done是非阻塞的。看注释,作者认为这个buffer的大小容量应该由调用者来保证。rpc包并不保证容量大小。

在并发情况下,我们使用Client.Go的时候就要自己保证channel buffer大小。

方法有个两个:

1 使用一个同样大小的channel buffer来进行阻塞保证。

这个方法就是gate的使用原因了。只有gate容量有剩余的时候才会容许调用client.Go

2 调大channel buffer大小

在这个例子中,bench的channel最大只会是b.N,所以,如果我们分配的res的channel buffer大小为b.N也能解决这个问题。

这个方法导致的效果就是bench的时间变快了,但是mem分配增加了。

4 这里的atomic什么作用?

因为这里会有多个routine会对send和recive进行操作,这里就需要保证原子性。

多个并发routine对一个共享变量进行操作有两种方法,channel和锁。

这里当然使用channel也能起到原子操作的效果。sync包的atomic和sync的mutex都是锁的方式。

所以说这里其实可以使用channel,mutex,atomic三种方法。

5 procs的作用?

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数是足够的。

目录
相关文章
|
9天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
29 2
|
13天前
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
107 53
|
7天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
18 2
|
7天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
20 2
|
12天前
|
Ubuntu 编译器 Linux
go语言中SQLite3驱动安装
【11月更文挑战第2天】
36 7
|
12天前
|
关系型数据库 Go 网络安全
go语言中PostgreSQL驱动安装
【11月更文挑战第2天】
45 5
|
12天前
|
安全 Go
用 Zap 轻松搞定 Go 语言中的结构化日志
在现代应用程序开发中,日志记录至关重要。Go 语言中有许多日志库,而 Zap 因其高性能和灵活性脱颖而出。本文详细介绍如何在 Go 项目中使用 Zap 进行结构化日志记录,并展示如何定制日志输出,满足生产环境需求。通过基础示例、SugaredLogger 的便捷使用以及自定义日志配置,帮助你在实际开发中高效管理日志。
31 1
|
11天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
87 58
|
10天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
11天前
|
存储 编译器 Go
go语言中的变量、常量、数据类型
【11月更文挑战第3天】
28 9