共计 3084 个字符,预计需要花费 8 分钟才能阅读完成。
手撸 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_rpc
import (
"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_NetRPC
2021/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)
PASS
ok command-line-arguments 0.666s
TimeServer.go
工夫服务端, 将本身注册到 rpc, 并提供 GetTime 服务
package net_rpc
import (
"fmt"
"net"
"net/rpc"
"time"
)
type TimeServer int
func (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_rpc
import "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)
正文完