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

DeployBot
• 阅读 1013

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

缘起

最近阅读 [Go微服务实战] (刘金亮, 2021.1)
本系列笔记拟采用golang练习之
gitee: https://gitee.com/ioly/learning.gooop

net/rpc

微服务中的进程间通信概述
对于进程间通信的技术,开发者有多种选择。
可以选择基于同步通信的通信机制,比如HTTP RESTful;
也可以选择基于异步通信的方式,Go语言提供了标准的net/rpc包以支持异步。

远程过程调用协议(Remote Procedure Call Protocol, RPC),
是一种通过网络从远程计算机程序上请求服务,
而不需要了解底层网络技术的协议。

目标(Day 2)

  • Day 1的rpc测试是短连接,频繁dial和close非常影响吞吐,改为长连接版本看看

设计

  • TimeConnection:封装rpc.dial以提供长连接的rpc.Client句柄。 rpc.Client.Call方法是并发安全的, 因此允许我们使用共享的长连接。
  • ITimeClientV2: 时间客户端接口
  • tTimeClientV2:时间客户端的实现, 持有长连接的rpc.Client句柄,进行远程GetTime调用

单元测试

  • common.go,封装单元测试的通用代码
package net_rpc

import "testing"

func fnAssertTrue(t *testing.T, b bool, msg string) {
    if !b {
        t.Fatal(msg)
    }
}

type CallLog struct {
    done bool
    cost int64
}
  • net_rpc_v2_test.go
  • 使用TimeConnection提供的共享的长连接
  • 并发100/300/500/1000/10000/50000次rpc调用
  • 统计失败次数和平均耗时
package net_rpc

import (
    "learning/gooop/net_rpc"
    "sync"
    "testing"
    "time"
)


func Test_NetRPC_V2(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(client net_rpc.ITimeClientV2, log *CallLog) {
        t0 := time.Now().UnixNano()
        err, ret := client.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)
        for i, _ := range logs {
            logs[i] = new(CallLog)
        }

        var g sync.WaitGroup

        conn := new(net_rpc.TimeConnection)
        _ = conn.Connect("localhost:3333", func(client net_rpc.ITimeClientV2) error {
            for i, _ := range logs {
                n := i
                g.Add(1)
                go func() {
                    fnTestRpcCall(client, logs[n])
                    g.Done()
                }()
            }

            g.Wait()
            return nil
        })


        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)
    fnTestConcurrency(10000)
    fnTestConcurrency(50000)
}

测试输出

比Day 1的短连接模式快一个数量级

$ go test -v *.go -test.run Test_NetRPC_V2
=== RUN   Test_NetRPC_V2
2021/03/25 13:51:55 rpc.Register: method "Serve" has 2 input parameters; needs exactly three
    net_rpc_v2_test.go:71: threads=100, failed=0, max=1.715795ms, avg=1.179659ms
    net_rpc_v2_test.go:71: threads=300, failed=0, max=6.225059ms, avg=4.792163ms
    net_rpc_v2_test.go:71: threads=500, failed=0, max=10.110459ms, avg=5.502403ms
    net_rpc_v2_test.go:71: threads=1000, failed=0, max=17.945680ms, avg=8.392062ms
    net_rpc_v2_test.go:71: threads=10000, failed=0, max=153.765575ms, avg=85.053883ms
    net_rpc_v2_test.go:71: threads=50000, failed=0, max=709.802299ms, avg=299.928400ms
--- PASS: Test_NetRPC_V2 (1.02s)
PASS
ok      command-line-arguments  1.031s

TimeConnection.go

  • 封装rpc.dial以提供长连接的rpc.Client句柄。
  • rpc.Client.Call方法是并发安全的, 因此允许我们使用共享的长连接。
package net_rpc

import "net/rpc"

type TimeConnection int

func (me *TimeConnection) Connect(serverAddress string, action func(client ITimeClientV2) error) error {
    conn, err := rpc.Dial("tcp", serverAddress)
    if err != nil {
        return err
    }
    defer conn.Close()

    return action(newTimeClientV2(conn))
}

ITimeClientV2

时间客户端接口

package net_rpc

type ITimeClientV2 interface {
    GetTime() (error, int64)
}

tTimeClientV2.go

持有长连接的rpc.Client指针,进行远程GetTime调用

package net_rpc

import "net/rpc"

type tTimeClientV2 struct {
    client *rpc.Client
}

func newTimeClientV2(client *rpc.Client) ITimeClientV2 {
    return &tTimeClientV2{ client, }
}

func (me *tTimeClientV2) GetTime() (error, int64) {
    var t int64 = 0
    err := me.client.Call("TimeServer.GetTime", 1, &t)
    if err != nil {
        return err, 0
    }

    return nil, t
}

(end)

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
秃头王路飞 秃头王路飞
2年前
webpack5手撸vue2脚手架
webpack5手撸vue相信工作个12年的小伙伴们在面试的时候多多少少怕被问到关于webpack方面的知识,本菜鸟最近闲来无事,就尝试了手撸了下vue2的脚手架,第一次发帖实在是没有经验,望海涵。languageJavaScript"name":"vuecliversion2","version":"1.0.0","desc
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
Java爬虫之JSoup使用教程
title:Java爬虫之JSoup使用教程date:201812248:00:000800update:201812248:00:000800author:mecover:https://imgblog.csdnimg.cn/20181224144920712(https://www.oschin
Wesley13 Wesley13
3年前
JDK 竟然是这样实现栈的?
!(https://oscimg.oschina.net/oscnet/b2e60191f9d143e3a4899c717aa4cd2f.jpg)作者|王磊来源|Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG\_Stone)前面的文章《动图演示:手撸堆栈的两种实现方法!》(https://w
Stella981 Stella981
3年前
Golang注册Eureka的工具包goeureka发布
1.简介提供Go微服务客户端注册到Eureka中心。点击:github地址(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2FSimonWang00%2Fgoeureka),欢迎各位多多star!(已通过测试验证,用于正式生产部署)2.原理
Wesley13 Wesley13
3年前
1. 容器化部署一套云服务 第一讲 Jenkins(Docker + Jenkins + Yii2 + 云服务器))
容器化部署一套云服务系列1\.容器化部署一套云服务之Jenkins(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fjackson0714%2Fp%2Fdeploy1.html)一、购买服务器服务器!caeef00
Stella981 Stella981
3年前
Golang与C#之switch区别
Golang与C之switch区别!(http://www.todosomeone.com/wpcontent/uploads/2016/10/101c0007a2dae691e1621.jpg)Go是Google开发的一种编译型,可并行化,并具有垃圾回收功能的编程语言。C是微软公司发布的一种面向对象的、运行于.N
Wesley13 Wesley13
3年前
ES6 新增的数组的方法
给定一个数组letlist\//wu:武力zhi:智力{id:1,name:'张飞',wu:97,zhi:10},{id:2,name:'诸葛亮',wu:55,zhi:99},{id:3,name:'赵云',wu:97,zhi:66},{id:4,na
Stella981 Stella981
3年前
GoLang构造函数
\TOC\GoLang构造函数具体代码位置构造函数(https://gitee.com/chinaliuhan/lhshortvideo/blob/master/scheduler/taskrunner/runner.go)我之前的的另一个学习项目另一篇笔记Golang学习笔记(https://my.o