手撸golang GO与微服务 net.rpc之1

ioly · · 309 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

手撸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)


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:ioly

查看原文:手撸golang GO与微服务 net.rpc之1

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

309 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传