Golang RPC实现-day02

导航

  • Golang RPC实现
    • 一、客户端异步并发多个请求
      • 1、 客户端结构体
      • 2、 一个客户端,异步发送多个请求,使用`call`结构体代表客户端的每次请求
      • 3、客户端并发多个请求
      • 4、客户端接收请求

Golang RPC实现

  • day01 我们实现了简单的服务端客户端
  • 我们简单总结一下day01的模式。
  • 服务端按顺序处理客户端过来的请求,按顺序响应客户端的请求。
  • 客户端同步的方式发送请求,不能并发发出请求。
  • 那么我们day02干的事情就是,让客户端异步并发的发出请求(请求顺序变得随机),服务端依然是按请求顺序进行处理,处理完某一个请求就返回,可以不按请求的顺序响应数据,但是响应数据是要上锁的,否则会发生响应数据并发安全问题。
  • 主要逻辑是修改了客户端的代码,服务端和day01没有变化

一、客户端异步并发多个请求

1、 客户端结构体

type Client struct {cc       codec.Codec//编码方式opt      *Option//发出请求的第一个包,用来协商后续包的格式和编码方式sending  sync.Mutex // 当一个请求正在发送时,不可以转头去执行别的请求header   codec.Header // 请求头内容mu       sync.Mutex // protect followingseq      uint64 //记录该客户端一次请求连接的序号,pending  map[uint64]*Call//通过seq快速找到客户端的某个请求closing  bool // user has called Closeshutdown bool // server has told us to stop
}

2、 一个客户端,异步发送多个请求,使用call结构体代表客户端的每次请求

type Call struct {Seq           uint64	//当前请求的序号,唯一标识一个请求ServiceMethod string      // format "<service>.<method>" 此次请求的服务和方法Args          interface{} // arguments to the function 请求函数的参数Reply         interface{} // reply from the function 服务端函数的响应数据Error         error       // if error occurs, it will be set //发生错误时的信息Done          chan *Call  // Strobes when call is complete.完成一次请求通过chan来通知
}

3、客户端并发多个请求

  • 主函数逻辑
func main() {log.SetFlags(0)addr := make(chan string)go startServer(addr)client, _ := geerpc.Dial("tcp", <-addr)defer func() { _ = client.Close() }()time.Sleep(time.Second)// send request & receive responsevar wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1)go func(i int) {//go 实现异步非阻塞发送多个请求defer wg.Done()args := fmt.Sprintf("geerpc req %d", i)//一次请求携带的数据var reply stringif err := client.Call("Foo.Sum", args, &reply); err != nil {//call发出一次请求,&reply,传的是引用,如果有响应,就能接收到log.Fatal("call Foo.Sum error:", err)}log.Println("reply:", reply)}(i)}wg.Wait()
}
  • Call 准备发出一次请求
// Call invokes the named function, waits for it to complete,
// and returns its error status.
func (client *Client) Call(serviceMethod string, args, reply interface{}) error {call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done//阻塞等待此次请求的channel,直到服务端处理并响应才返回return call.Error
}
  • 绑定数据到请求中
// Go invokes the function asynchronously.
// It returns the Call structure representing the invocation.
func (client *Client) Go(serviceMethod string, args, reply interface{}, done chan *Call) *Call {if done == nil {done = make(chan *Call, 10)} else if cap(done) == 0 {log.Panic("rpc client: done channel is unbuffered")}call := &Call{ServiceMethod: serviceMethod,//此次请求的服务和方法Args:          args,//此次请求的参数Reply:         reply,//此处是引用类型,暂时还没有数据,等服务端响应就有数据了Done:          done,//绑定此次请求的响应channel,服务端响应后就往对应的channel发一条数据}client.send(call)return call
}
  • 发送请求到服务端
func (client *Client) send(call *Call) {// make sure that the client will send a complete requestclient.sending.Lock()defer client.sending.Unlock()// register this call.seq, err := client.registerCall(call)//注册这次call,把这次的请求ID注册到客户端中。。。if err != nil {call.Error = errcall.done()return}// prepare request headerclient.header.ServiceMethod = call.ServiceMethodclient.header.Seq = seqclient.header.Error = ""// encode and send the requestif err := client.cc.Write(&client.header, call.Args); err != nil {//发送请求头和请求参数call := client.removeCall(seq)// call may be nil, it usually means that Write partially failed,// client has received the response and handledif call != nil {call.Error = errcall.done()}}
}

4、客户端接收请求

func (client *Client) receive() {var err errorfor err == nil {var h codec.Headerif err = client.cc.ReadHeader(&h); err != nil { 接收请求是一个个来,当连接关闭时,此处会报错,退出整个客户端break}call := client.removeCall(h.Seq)//通过Seq唯一标识符删除一个请求switch {case call == nil:// it usually means that Write partially failed// and call was already removed.err = client.cc.ReadBody(nil)case h.Error != "":call.Error = fmt.Errorf(h.Error)err = client.cc.ReadBody(nil)call.done()default:err = client.cc.ReadBody(call.Reply)if err != nil {call.Error = errors.New("reading body " + err.Error())}call.done()//向通道发送一条消息,客户端等待的这个call可以推出了}}// error occurs, so terminateCalls pending callsclient.terminateCalls(err)//关闭所有请求
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/705199.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

景源畅信电商:做抖音有哪些未开发的蓝海领域?

在互联网信息爆炸的今天&#xff0c;抖音已经成为人们获取信息和娱乐的重要渠道。然而&#xff0c;随着用户数量的增加和内容的丰富&#xff0c;抖音的红海竞争也日益激烈。在这样的背景下&#xff0c;寻找还未被充分开发的蓝海领域&#xff0c;对于内容创作者来说&#xff0c;…

思科模拟器--2.静态路由和默认路由配置24.5.15

首先&#xff0c;创建三个路由器和两个个人电脑。 接着&#xff0c;配置两台电脑的IP&#xff0c;子网掩码和默认网关 对Router 0&#xff0c;进行以下命令&#xff1a; 对Router进行以下命令&#xff1a; 对Router2进行以下命令&#xff1a; 本实验完成。 验证&#xff1a;PC…

MT3036 第一节离数课后

思路&#xff1a; 这道题与之前的表达式求值题目不同的是&#xff0c;有not这个单目运算符。而且如果表达式错误&#xff0c;要输入error。 把true和false成为操作数&#xff0c;把and or not成为运算符。 考虑error的情况&#xff1a; 1.and 和 or是双目运算符&#xff0c…

线性模型之岭回归的用法

实战&#xff1a;使用岭回归模型 完整代码&#xff1a; import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.datasets import make_regression from sklearn.model_selection import train_test_split fro…

pytest教程-46-钩子函数-pytest_sessionstart

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest_report_testitemFinished钩子函数的使用方法&#xff0c;本小节我们讲解一下pytest_sessionstart钩子函数的使用方法。 pytest_sessionstart 是 Pytest 提供的一个钩子函数&#xff0c…

实战+代码!Selenium + Phantom JS爬取天天基金数据

功能&#xff1a; 通过程序实现从基金列表页&#xff0c;获取指定页数内所有基金的近一周收益率以及每支基金的详情页链接。再进入每支基金的详情页获取其余的基金信息&#xff0c;将所有获取到的基金详细信息按近6月收益率倒序排列写入一个Excel表格。 思路&#xff1a; 1.…

fastadmin对登录token的处理

fastadmin对token的操作 最近开发遇到一个场景&#xff0c;需要绕过验证获取登录&#xff0c;所以恶补了一下这个知识点&#xff0c;这个主要就是控制fastadmin的token的问题 代码分步实现 class Xcxuser extends Api {//关于鉴权protected $noNeedLogin [login,getopenid,…

错误: 找不到或无法加载主类问题(已解决)

今天在虚拟机中安装了idea2023.2的版本&#xff0c;运行代码时发现错误找不到主类&#xff01; 直接说结论&#xff1a; 我先clean了一下target&#xff0c;然后重新build&#xff0c;发现maven报错了&#xff0c;idea2023.2默认使用了内置的maven&#xff0c;然后我切换了一下…

Yalmip使用教程(8)-常见报错及调试方法

博客中所有内容均来源于自己学习过程中积累的经验以及对yalmip官方文档的翻译&#xff1a;https://yalmip.github.io/tutorials/ 这篇博客将详细介绍使用yalmip工具箱编程过程中的常见错误和相应的解决办法。 1.optimize的输出参数 众所周知&#xff0c;optimize是yalmip用来求…

中国农业大学:学硕11408复试线上涨40分,今年还会持续涨吗?中国农业大学计算机考研考情分析!

中国农业大学&#xff08;China Agricultural University&#xff09;&#xff0c;简称“中国农大”&#xff0c;坐落于中国首都北京&#xff0c;由中华人民共和国教育部直属&#xff0c;中央直管副部级建制&#xff0c;水利部、农业部和北京市共建&#xff0c;位列国家“双一流…

解决kali Linux2024无法获取动态IPv4地址(DHCP)解决方案

用root用户启动终端 进入根目录&#xff0c;选择配置文件 cd到根目录下/../etc/network找到interfaces文件 编辑interfaces文件 vi interfaces&#xff0c;编辑interfaces文件 输入如下命令 打开虚拟网络编辑器 选择虚拟机选项卡&#xff0c;编辑&#xff0c;打开虚拟网络编…

Jmeter(四十一) - 从入门到精通进阶篇 - Jmeter配置文件的刨根问底 - 下篇(详解教程)

宏哥微信粉丝群&#xff1a;https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 为什么宏哥要对Jmeter的配置文件进行一下讲解了&#xff0c;因为有的童鞋或者小伙伴在测试中遇到一些需要修改配置文件的问题不是很清楚也不是很懂&#xff0c;就算修改了也是…