手撸golang GO与微服务 net.rpc
缘起
最近浏览 [Go微服务实战] (刘金亮, 2021.1)
本系列笔记拟采纳golang练习之
gitee: https://gitee.com/ioly/learning.gooop
net/rpc
微服务中的过程间通信概述对于过程间通信的技术,开发者有多种抉择。能够抉择基于同步通信的通信机制,比方HTTP RESTful;也能够抉择基于异步通信的形式,Go语言提供了规范的net/rpc包以反对异步。近程过程调用协定(Remote Procedure Call Protocol, RPC),是一种通过网络从近程计算机程序上申请服务,而不须要理解底层网络技术的协定。
指标
- 应用net/rpc包写个工夫申请rpc, 并测试并发性能.
设计
- TimeServer: 工夫服务端, 将本身注册到rpc, 并提供GetTime服务
- TimeClient: 连贯到工夫服务器, 并近程调用GetTime服务。短连贯模式,即总是在rpc调用后立刻断开连接。
单元测试
net_rpc_test.go, 别离并发创立100/300/500/1000个工夫客户端, 向服务器申请工夫, 并统计失败次数和均匀耗时
package net_rpcimport ( "learning/gooop/net_rpc" "sync" "testing" "time")func fnAssertTrue(t *testing.T, b bool, msg string) { if !b { t.Fatal(msg) }}type CallLog struct { done bool cost int64}func Test_NetRPC(t *testing.T) { server := new(net_rpc.TimeServer) err := server.Serve(3333) if err != nil { t.Fatal(err) } time.Sleep(100 * time.Millisecond) fnTestRpcCall := func(log *CallLog) { c := net_rpc.NewTimeClient("localhost:3333") t0 := time.Now().UnixNano() err, ret := c.GetTime() log.cost = time.Now().UnixNano() - t0 log.done = err == nil if log.done { fnAssertTrue(t, ret > 0, "expecting ret>0") } } fnTestConcurrency := func(threads int) { logs := make([]*CallLog, threads) var g sync.WaitGroup for i, _ := range logs { logs[i] = new(CallLog) n := i g.Add(1) go func() { fnTestRpcCall(logs[n]) g.Done() }() } g.Wait() var failed, max, avg int64 = 0, 0, 0 for _, it := range logs { if !it.done { failed++ } if it.cost > max { max = it.cost } avg += it.cost } avg = avg / int64(threads) maxf := float64(max) / float64(time.Millisecond/time.Nanosecond) avgf := float64(avg) / float64(time.Millisecond/time.Nanosecond) t.Logf("threads=%d, failed=%d, max=%fms, avg=%fms", threads, failed, maxf, avgf) } fnTestConcurrency(100) fnTestConcurrency(300) fnTestConcurrency(500) fnTestConcurrency(1000)}
测试输入
脚趾头通知我, 工夫都花在net.dial下面了
$ go test -v net_rpc_test.go === RUN Test_NetRPC2021/03/24 23:55:21 rpc.Register: method "Serve" has 2 input parameters; needs exactly three net_rpc_test.go:75: threads=100, failed=0, max=50.962322ms, avg=42.961170ms net_rpc_test.go:75: threads=300, failed=0, max=45.608988ms, avg=30.233982ms net_rpc_test.go:75: threads=500, failed=0, max=99.810739ms, avg=81.164639ms net_rpc_test.go:75: threads=1000, failed=0, max=359.049068ms, avg=185.030143ms--- PASS: Test_NetRPC (0.66s)PASSok command-line-arguments 0.666s
TimeServer.go
工夫服务端, 将本身注册到rpc, 并提供GetTime服务
package net_rpcimport ( "fmt" "net" "net/rpc" "time")type TimeServer intfunc (me *TimeServer) GetTime(_ int, t *int64) error { *t = time.Now().UnixNano() return nil}func (me *TimeServer) Serve(port int) error { addy, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("0.0.0.0:%d", port)) if err != nil { return err } err = rpc.Register(me) if err != nil { return err } inbound, err := net.ListenTCP("tcp", addy) if err != nil { return err } go rpc.Accept(inbound) return nil}
TimeClient.go
连贯到工夫服务器, 并近程调用GetTime服务。短连贯模式,即总是在rpc调用后立刻断开连接。
package net_rpcimport "net/rpc"type TimeClient struct { serverAddress string}func NewTimeClient(serverAddress string) *TimeClient { it := new(TimeClient) it.init(serverAddress) return it}func (me *TimeClient) init(serverAddress string) { me.serverAddress = serverAddress}func (me *TimeClient) GetTime() (error, int64) { client, err := rpc.Dial("tcp", me.serverAddress) if err != nil { return err, 0 } defer client.Close() var t int64 = 0 err = client.Call("TimeServer.GetTime", 1, &t) if err != nil { return err, 0 } return nil, t}
(end)