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;就算修改了也是…

tag-字符串:最长公共前缀

题目 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 “”。 示例 题解一 class Solution:def longestCommonPrefix(self, strs: List[str]) -> str:# 按照字典顺序找到strs中最大的字符串和最小的字符串str0 min(strs)st…

51单片机超声波测距_液位检测_温度检测原理图PCB仿真代码

目录 实物图&#xff1a; PCB ​原理图​ 仿真图 ​编辑 程序 资料下载地址&#xff1a;51单片机超声波测距-液位检测-温度检测原理图PCB仿真代码 主控为stc89c52,通过ds18b20进行温度采集&#xff0c;超声波测距&#xff0c;距离不可以超过1m&#xff0c;通过按键可以设…

Java面试八股之List、Set、Map和Queue之间的区别

Java中List、Set、Map和Queue之间的区别 List List 是一个有序且允许重复元素的集合。它提供了按索引访问元素的能力&#xff0c;也就是说&#xff0c;你可以通过元素的插入位置或指定的索引来精确地访问、添加或删除元素。List 的典型实现包括 ArrayList 和 LinkedList。 特…

喜茶·茶坊黑金首店入驻北京三里屯,率先引入珍稀娟姗奶制茶

发布 | 大力财经 近日&#xff0c;喜茶茶坊 BLACK 在北京三里屯开业&#xff0c;这是喜茶新业态的首家黑金店型。该店在延续喜茶茶坊“鲜、茶、纯”的精品茗茶特色和宋代茶文化审美意趣的基础上&#xff0c;首次升级呈现了铜锅手煮烹茶工艺、娟姗牛乳制茶等创新尝试&#xff0…

【C++ 高阶数据结构 Test】AVL ~ 二叉搜索树

文章目录 1. AVL 树概念2. AVL 树节点的定义3. AVL树的插入4. AVL树的旋转4.1 新节点插入较高左子树的左侧---左左&#xff1a;右单旋4.2 新节点插入较高右子树的右侧---右右&#xff1a;左单旋4.3 新节点插入较高左子树的右侧---左右&#xff1a;先左单旋再右单旋4.4 新节点插…

电工能混到这份上

最近看到某电工师傅发了一篇帖子&#xff0c;大致内容是他在处理一个简单故障的时候居然花了很长的时间。我们一起来看看他遇到的是什么故障吧! plc 控制的一台设备&#xff0c;行走部分靠 2 个脚踏开关控制&#xff08;内部开关量控制方向&#xff0c;电位器控制速度&#xff…

微信小程序快速开发-基础内容(内容真的又多又干货)

目录 实现横向布局效果 实现滚动效果 实现轮播图效果 实现文本长按选中复制效果 渲染 HTML 标签 按钮组件的使用效果 图片组件的使用效果 Mustache 语法 动态绑定内容&#xff08;定义变量&#xff0c;渲染变量&#xff09; 动态绑定属性&#xff08;将属性定义为变量…

用SwitchHosts模拟本地域名解析访问

一.用SwitchHosts模拟本地域名解析访问 1.下载地址 https://download.csdn.net/download/jinhuding/89313168 2.使用截图