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.mzph.cn/bicheng/13115.shtml

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

相关文章

蓝桥杯-外卖店优先级(简单写法)

“饱了么”外卖系统中维护着 N 家外卖店&#xff0c;编号 1∼N。 每家外卖店都有一个优先级&#xff0c;初始时 (0 时刻) 优先级都为 0。 每经过 1 个时间单位&#xff0c;如果外卖店没有订单&#xff0c;则优先级会减少 1&#xff0c;最低减到 0&#xff1b;而如果外卖店有订…

【数据结构】双向循环链表专题解析

实现自己既定的目标&#xff0c;必须能耐得住寂寞单干。&#x1f493;&#x1f493;&#x1f493; 目录 •✨说在前面 &#x1f34b;知识点一&#xff1a;双向链表的结构 • &#x1f330;1."哨兵位"节点 • &#x1f330;2.双向带头循环链表的结构 &#x1f34b;…

基于java的超级玛丽游戏的设计与实现(论文 + 源码)

Java的超级玛丽游戏.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89313347 基于java的超级玛丽游戏的设计与实现 摘要 近年来&#xff0c;Java作为一种新的编程语言&#xff0c;以其简单性、可移植性和平台无关性等优点&#xff0c;得到了广泛地应用。J2SE称…

(程序设计语言)传值、传引用

1、传值&#xff08;传递值&#xff09;&#xff1a; 在传值的情况下&#xff0c;函数接收到的是参数的一个副本&#xff0c;而不是参数本身。这意味着函数内部对参数的修改不会影响到原始值。传值通常用于基本数据类型&#xff08;如整数、浮点数、布尔值等&#xff09;的传递…

微信小程序原生全局状态管理——wxminishareddata

一个基于原生小程序的 mini 轻量全局状态管理库&#xff0c;跨页面/组件数据共享渲染。 全局状态 data 支持所有 Page 和 Component。类似 Vuex 的使用适合原生小程序&#xff0c;即使后期引入&#xff0c;也只需增加几行代码。目前为第一版&#xff0c;很粗糙&#xff0c;但也…

前端 CSS 经典:CSS 包含块

前言&#xff1a;CSS 总的来说就两块&#xff0c;样式计算和视觉格式化模型&#xff0c;而包含块就是视觉格式化模型的重要内容&#xff0c;理解包含块的定义&#xff0c;对于 CSS 布局有更好的帮助。 1. 概念 什么叫包含块&#xff0c;指的是一个区域&#xff0c;某一个元素在…

华焰天下晋升质量管理三大体系和产品3C认证实力级

华焰天下&#xff0c;作为业界领先的新能源灶具企业&#xff0c;一直以来都致力于追求卓越的质量管理和产品创新。近日&#xff0c;华焰天下成功晋升为质量管理三大体系先进管理&#xff0c;并成功获得了产品3C认证&#xff0c;这标志着我们在质量管理和产品安全方面迈出了坚实…

ThreadLocal,一次到位

一、定义 ThreadLocal是线程私有变量&#xff0c;用于保存每个线程的私有数据。 那么什么情况下需要进行线程隔离 二、源码分析 public class ThreadLocalTest01 {ThreadLocal<Integer> t new ThreadLocal<>();public void test() {t.set(1);Integer integer…

传输层协议——TCP协议

TCP协议又叫传输控制协议&#xff0c;TCP/IP协议是计算机通信网络中目前使用最多的协议&#xff0c;同时也融入了生活的方方面面&#xff0c;不管是浏览网页使用的http/https协议、物联网设备使用的MQTT/MQTTS协议与下载文件使用的ftp协议、工业以太网中使用的Modbus TCP协议等…

JVM学习-虚拟机栈

虚拟机栈 每个线程创建时都会创建一个虚拟机栈&#xff0c;其内部保存一个个栈帧&#xff0c;对应一次次Java方法调用&#xff0c;栈是线程私有的。 生命周期: 与线程相同 作用 主管Java程序的运行&#xff0c;它保存方法的局部变量、部分结果、并参与方法的调用和返回。 …

PyTorch之list、ndarray、tensor数据类型相互转换

温故而知新&#xff0c;可以为师矣&#xff01; 一、参考资料 python中list、numpy、torch.tensor之间的相互转换 二、常用操作 list 转 numpy ndarray np.array(list) import numpy as npa_list [[j for j in range(5)] for i in range(3)] a_ndarray np.array(a_lis…

云服务器配置mysql允许被远程连接从而使用图形化界面

介绍 在云服务器上搭建和配置数据库是进行网站和应用开发的关键步骤之一。本文将介绍如何在云服务器上设置 MySQL 8 和 MySQL 5&#xff0c;以允许远程连接&#xff0c;从而让你的数据库能够被远程用户访问。这样你的本机就可以访问linux服务器上的mysql能&#xff0c;就可以使…

254 基于matlab的钢筋混凝土非线性分析

基于matlab的钢筋混凝土非线性分析&#xff0c;根据梁本构关系&#xff0c;然后进行非线性分析&#xff0c;绘制弯矩-曲率曲线。可设置梁的截面尺寸、混凝土本构&#xff0c;钢筋截面面积等相关参数&#xff0c;程序已调通&#xff0c;可直接运行。 254 钢筋混凝土非线性分析 弯…

利用管道通信(pipe)测量进程间的上下文切换(context switch)开销

利用管道通信(pipe)测量进程间的上下文切换(context switch)开销 《https://pages.cs.wisc.edu/~remzi/OSTEP/cpu-mechanisms.pdf》 Measuring the cost of a context switch is a little trickier. The lmbench benchmark does so by running two processes on a single CPU…

qmake、CMake、make和Makefile

为了跟踪C工程的全部部分&#xff0c;要求有一种机制来精确地指定&#xff1a; 涉及的输入文件&#xff0c;如源代码文件&#xff1a;.cpp&#xff0c;头文件&#xff1a;.h建立程序时所需的工具&#xff0c;如编译器&#xff1a; g.exe&#xff0c;链接器&#xff1a;ld.exe&a…

哈夫曼编码的应用

数据结构与算法课的一个简单实验&#xff0c;记录一下&#xff0c;以供参考。 文章目录 要求测试样例统计字母出现次数建立哈夫曼树对字符编码对原文进行编码译码 要求 输入一段100—200字的英文短文&#xff0c;存入一文件a中。统计短文出现的字母个数n及每个字母的出现次数…

终于搞懂Linux 设备树中的#address-cells,#size-cells 和reg 属性

目录 一、前置知识 1. 处理器平台2. reg 属性的基本格式3. reg 属性的作用 reg 用法 二、#address-cells 和 #size-cells 属性 1. 示例1 2. 示例23. 示例3 一、前置知识 要理解#address-cells和#size-cell 这两个属性&#xff0c;就要先了解 reg属性。 1. 处理器平台 下…

VS2022如何添加现有项

以 想在队列里&#xff0c;使用堆栈的.c&#xff0c;.h文件 为例 目录 1.复制堆栈的.c&#xff0c;.h文件 ​编辑 2.打开队列所在项目的文件夹 3.粘贴堆栈的.c&#xff0c;.h文件 4.在头文件和源文件添加相应的堆栈的.c&#xff0c;.h文件 1.复制堆栈的.c&#xff0c;.h文件…

QML进阶(十七) ECMAScript 语法介绍

文章目录 基本语法变量基本类型类型转换对象函数和循环打印输出内置对象来自QML的基本类型ECMAScript语言的标准是由Netscape、Sun、微软、Borland等公司基于JavaScript和JScript定义出来的脚本语言标准。可以为不同种类的浏览器环境提供核心的脚本编程能力。在QML中我们通过EC…

HCIP【VLAN综合实验】

目录 一、实验拓扑图&#xff1a; 二、实验要求&#xff1a; 三、实验思路&#xff1a; 四、实验步骤&#xff1a; 1、在交换机SW1,SW2,SW3配置VLAN和各个接口对应类型的配置 2、在路由器上面配置DHCP服务 一、实验拓扑图&#xff1a; 二、实验要求&#xff1a; 1、PC1 …