Golang RPC实现-day01

导航

  • Golang RPC实现
    • 一、主体逻辑设计
    • 二、服务设计
      • 1、监听和接收请求
      • 2、处理请求
        • (1)服务结构体定义
        • (2)确认请求方和服务方编解码格式
        • (3)循环读取请求
        • (4)解析请求的内容
        • (5)响应请求
    • 三、读取和发送数据到连接中代码

Golang RPC实现

  • 先来一个最简单的版本,后续更新。
  • RPC也可以说是一种自定义的应用层协议
  • 所以我们需要自定义消息格式,消息包括 请求头和请求体,所以我们定义一个消息结构体
type request struct {h            *codec.Header // header of requestargv, replyv reflect.Value // argv and replyv of request
}
  • 请求头结构体
type Header struct {ServiceMethod string // format "Service.Method" 调用的服务和方法Seq           uint64 // sequence number chosen by client 连接IdError         string
}
  • 请求的第一条消息用来确定后续消息的格式和编码,这里规定第一条消息是以Json格式编码,对应的结构体如下
type Option struct {MagicNumber int        // MagicNumber marks this's a geerpc requestCodecType   codec.Type // client may choose different Codec to encode body
}
  • 消息格式和编码结构体
var DefaultOption = &Option{MagicNumber: MagicNumber,CodecType:   codec.GobType,
}

一、主体逻辑设计

func main() {log.SetFlags(0)addr := make(chan string)go startServer(addr)//这是RPC的服务端逻辑// in fact, following code is like a simple geerpc clientconn, _ := net.Dial("tcp", <-addr) //客户端拨号建立链接,每次服务逻辑,都通过conn来确定当前客户端/服务端是在哪一个连接上。defer func() { _ = conn.Close() }()time.Sleep(time.Second)// send options,先发了一个,定义后续数据的编码方式_ = json.NewEncoder(conn).Encode(geerpc.DefaultOption)//Encode方法就是发送一个Option结构体内容到conn连接中cc := codec.NewGobCodec(conn)// send request & receive responsefor i := 0; i < 5; i++ {//发五次请求h := &codec.Header{//定义每次请求的请求头ServiceMethod: "Foo.Sum",Seq:           uint64(i),}_ = cc.Write(h, fmt.Sprintf("geerpc req %d", h.Seq))//发送请求体和body内容_ = cc.ReadHeader(h)//接收服务端的响应的请求头内容,处理相应得按顺序,不能并发接收响应数据log.Println("clinet receive response:", h.ServiceMethod)var reply string_ = cc.ReadBody(&reply)//接收服务端的响应的请求体内容log.Println("reply:", reply)//打印}
}

二、服务设计

逻辑就是

  1. 监听请求
  2. 循环获取请求,异步处理请求
  3. 获取当前连接的第一条消息,确认后续消息的格式和编码
  4. 获取请求内容
  5. 响应请求

1、监听和接收请求

func startServer(addr chan string) {// pick a free portl, err := net.Listen("tcp", ":0")if err != nil {log.Fatal("network error:", err)}log.Println("start rpc server on", l.Addr())addr <- l.Addr().String()geerpc.Accept(l)//服务端接收请求
}

2、处理请求

(1)服务结构体定义
type Server struct{}
// NewServer returns a new Server.
func NewServer() *Server {return &Server{}
}
// DefaultServer is the default instance of *Server.
var DefaultServer = NewServer()
(2)确认请求方和服务方编解码格式
// Accept accepts connections on the listener and serves requests
// for each incoming connection.
func Accept(lis net.Listener) { DefaultServer.Accept(lis) }// Accept accepts connections on the listener and serves requests
// for each incoming connection.
func (server *Server) Accept(lis net.Listener) {for {//不断接收请求conn, err := lis.Accept()if err != nil {log.Println("rpc server: accept error:", err)return}go server.ServeConn(conn)//异步处理请求}
}
// ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
func (server *Server) ServeConn(conn io.ReadWriteCloser) {log.Println("服务端处理连接中..... ")defer func() { _ = conn.Close() }()var opt Optionif err := json.NewDecoder(conn).Decode(&opt); err != nil {//解析第一个Option,确定后续协议消息的格式log.Println("rpc server: options error: ", err)return}if opt.MagicNumber != MagicNumber {//服务方编码方式是否与客户端相同log.Printf("rpc server: invalid magic number %x", opt.MagicNumber)return}f := codec.NewCodecFuncMap[opt.CodecType]//服务方编码方式是否与客户端相同if f == nil {//服务端是否存在客户端对应编码方式log.Printf("rpc server: invalid codec type %s", opt.CodecType)return}server.serveCodec(f(conn))//第一个确认包通过后,再发后续消息,通过conn拿到连接信息,保证服务端后续能向conn发送信息
}
(3)循环读取请求
func (server *Server) serveCodec(cc codec.Codec) {sending := new(sync.Mutex) // make sure to send a complete responsewg := new(sync.WaitGroup)  // wait until all request are handledfor {req, err := server.readRequest(cc)//反复从请求方接收请求,这里会把请求头和请求体内容获取到req的结构体中if err != nil {if req == nil {//直到没有请求过来break // it's not possible to recover, so close the connection}req.h.Error = err.Error()server.sendResponse(cc, req.h, invalidRequest, sending)continue}wg.Add(1)go server.handleRequest(cc, req, sending, wg)//异步处理数据}wg.Wait()_ = cc.Close()
}
(4)解析请求的内容
func (server *Server) handleRequest(cc codec.Codec, req *request, sending *sync.Mutex, wg *sync.WaitGroup) {// TODO, should call registered rpc methods to get the right replyv// day 1, just print argv and send a hello messagedefer wg.Done()log.Println("handleRequest :", req.h, req.argv.Elem())req.replyv = reflect.ValueOf(fmt.Sprintf("geerpc resp %d", req.h.Seq))server.sendResponse(cc, req.h, req.replyv.Interface(), sending)
}
(5)响应请求
func (server *Server) sendResponse(cc codec.Codec, h *codec.Header, body interface{}, sending *sync.Mutex) {sending.Lock()defer sending.Unlock()//time.Sleep(time.Second)if err := cc.Write(h, body); err != nil {log.Println("rpc server: write response error:", err)}
}

三、读取和发送数据到连接中代码

package codecimport ("bufio""encoding/gob""io""log"
)type GobCodec struct {conn io.ReadWriteCloserbuf  *bufio.Writerdec  *gob.Decoderenc  *gob.Encoder
}var _ Codec = (*GobCodec)(nil)func NewGobCodec(conn io.ReadWriteCloser) Codec {buf := bufio.NewWriter(conn)return &GobCodec{conn: conn,buf:  buf,dec:  gob.NewDecoder(conn),enc:  gob.NewEncoder(buf),}
}func (c *GobCodec) ReadHeader(h *Header) error {return c.dec.Decode(h)
}func (c *GobCodec) ReadBody(body interface{}) error {return c.dec.Decode(body)
}func (c *GobCodec) Write(h *Header, body interface{}) (err error) {defer func() {_ = c.buf.Flush()if err != nil {_ = c.Close()}}()if err = c.enc.Encode(h); err != nil {log.Println("rpc: gob error encoding header:", err)return}if err = c.enc.Encode(body); err != nil {log.Println("rpc: gob error encoding body:", err)return}return
}func (c *GobCodec) Close() error {return c.conn.Close()
}

欢迎大家关注我的博客在这里插入图片描述

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

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

相关文章

蜜蜂收卡系统 加油卡充值卡礼品卡自定义回收系统源码 前后端开源uniapp可打包app

本文来自&#xff1a;蜜蜂收卡系统 加油卡充值卡礼品卡自定义回收系统源码 前后端开源uniapp可打包app - 源码1688 卡券绿色循环计划—— 一项旨在构建卡券价值再利用生态的社会责任感项目。在当前数字化消费日益普及的背景下&#xff0c;大量礼品卡、优惠券因各种原因未能有效…

基于 LlaMA 3 + LangGraph 在windows本地部署大模型 (三)

基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 大家继续看 https://lilianweng.github.io/posts/2023-06-23-agent/的文档内容 第二部分&#xff1a;内存 记忆的类型 记忆可以定义为用于获取、存储、保留以及随后检索信息的过程。人脑中有多…

PLL-分频器

概念 分频器的性能一般用四个参数来规定:(1)分频比&#xff0c;(2)最大允许输入频率fmax&#xff0c;(3)功耗&#xff0c;(4)最小允许输入电压摆幅(也叫“灵敏度”)。虽然分频器的相位噪声也很重要&#xff0c;但在大多数情况下它可以忽略不计。 把一般分频器的输入灵敏度画成…

HTML常用标签-表单标签

表单标签 1 表单标签2 表单项标签2.1 单行文本框2.2 密码框2.3 单选框2.4 复选框2.5 下拉框2.6 按钮2.7 隐藏域2.8 多行文本框2.9 文件标签 1 表单标签 表单标签,可以实现让用户在界面上输入各种信息并提交的一种标签. 是向服务端发送数据主要的方式之一 form标签,表单标签,其内…

2024年小学生古诗文大会备考:吃透历年真题和知识点(持续)

根据往年的安排&#xff0c;2024年小学生古诗文大会预计这个月就将启动。该如何备考2024年小学生古诗文大会呢&#xff1f;根据往期的经验&#xff0c;只要吃透这些真题和背后的知识点&#xff0c;通过上海小学生古诗文大会的初选&#xff08;初赛&#xff09;一点问题都没有。…

中国196个城市边界

中国196个城市的城市边界形状文件是通过对Li等人&#xff08;2018&#xff09;的输出进行处理和过滤生成的。根据全球人工不可渗透区域 &#xff08;GAIA&#xff09; 数据绘制全球城市边界。 城市建成区边界是城市研究中的一个重要指标&#xff0c;在很多城市研究中都会涉及到…

优先级队列(堆)

目录 leetcode题目 一、数组中两元素的最大乘积 二、最后一块石头的重量 三、数据流中的第 K 大元素 四、前K个高频元素 五、前K个高频单词 六、数据流的中位数 七、有序矩阵中的第K小的元素 八、根据字符出现频率排序 leetcode题目 一、数组中两元素的最大乘积 146…

20【Aseprite 作图】画岩石

1 画一个岩石的框架,不是一个正规的圆,可以把最高点不放在中心,会显得自然 2 用油漆桶填满框架 3 要改变岩石的颜色,可以调整HSV里面的S,降低饱和度 4 描边,和地面连接处可以不描边 5 颜色调得更浅一点,(通过HSV里面的S可以做到),作为亮处的轮廓; 通过把透明度调…

探索智慧生活:百度Comate引领人工智能助手新潮流

文章目录 百度Comate介绍1. 什么是百度Comate&#xff1f;主要特点 2. Comate的核心功能智能问答功能语音识别功能语音助手功能个性化服务 3. Comate 支持哪些语言&#xff1f; 使用教程(以vscode为例)1. 下载和安装Comate3. 常用操作快捷键(windows) 使用体验自然语言生成代码…

【联通支付注册/登录安全分析报告】

联通支付注册/登录安全分析报告 前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨…

未授权访问:ZooKeeper 未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好的文章&#xff1a; 这里附上大…

多格式兼容的在线原型查看:Axure RP的便捷解决方案

Axure rp不仅可以绘制详细的产品构思&#xff0c;还可以在浏览器中生成html页面&#xff0c;但需要安装插件才能打开。安装Axure后 rpchrome插件后&#xff0c;还需要在扩展程序中选择“允许访问文件网站”&#xff0c;否则无法在Axure中成功选择 rp在线查看原型。听起来很麻烦…

讲解SSM的xml文件

概述&#xff1a;这些配置文件很烦&#xff0c;建议直接复制粘贴 springMVC.xml文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XM…

51 单片机[2-2]:LED闪烁

摘要&#xff1a; 本文使用STC89C52RC单片机实现单个LED闪烁 新建一个项目&#xff0c;具体步骤见[2-1] 分析&#xff1a; 要使 LED 闪烁&#xff08;以D1为例&#xff09;&#xff0c;就要先让 P2 0xfe; 再让 P2 0xff; 先在keil5中把程序写成这样&#xff1a; #include &…

Spring Boot:异常处理

Spring Boot 前言使用自定义错误页面处理异常使用 ExceptionHandler 注解处理异常使用 ControllerAdvice 注解处理异常使用配置类处理异常使用自定义类处理异常 前言 在 Spring Boot 中&#xff0c;异常处理是一个重要的部分&#xff0c;可以允许开发者优雅地处理应用程序中可…

elasticsearch使用Ngram实现任意位数手机号搜索

文章目录 Ngram自定义分词案例实战问题拆解 Ngram分词器定义Ngram分词定义Ngram分词示例Ngram分词应用场景 Ngram分词实战 Ngram自定义分词案例 当对keyword类型的字段进行高亮查询时&#xff0c;若值为123asd456&#xff0c;查询sd4&#xff0c;则高亮结果是&#xff1c;em&a…

2022-1990年 各省碳排放Co2数据集(含数据及参考文献)

碳排放是指人类活动产生的二氧化碳&#xff08;CO2&#xff09;等温室气体释放到大气中的过程。通过划分排放源的范围以避免重复计算的思想&#xff0c;由世界资源研究所在关于企业温室气体排放清单编制的指南中首次提出。城市碳排放核算边界界定借鉴该思想&#xff0c;可分为3…

SQL语句

约束具体表现在表的层面&#xff0c;属性具体表现在字段的层面。 1.SQL语句的类型 根据作用进行分类&#xff1a; DDL 数据定义语言 create&#xff0c;drop&#xff0c;alter DML 数据操作语言&#xff08;对数据本身做操作&#xff0c;增删改查&#xff09; insert&am…

风电功率预测 | 基于PSO-BP神经网络实现风电功率预测(附matlab完整源码)

风电功率预测 风电功率预测完整代码风电功率预测 基于粒子群优化算法(Particle Swarm Optimization, PSO)的BP神经网络是一种常见的方法,用于实现风电功率预测。下面是一个基于PSO-BP神经网络实现风电功率预测的一般步骤: 数据准备:收集与风电场发电功率相关的数据,包括…