IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Go rpc通讯 示例/分析 -> 正文阅读

[网络协议]Go rpc通讯 示例/分析

RPC - Remote Procedure Call Protocol 就是想实现函数调用模式的网络化,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。

客户端就像调用本地函数一样,然后客户端把这些参数打包之后通过网络传给服务端,服务端解包到处理过程中执行,然后执行结果返回给客户端

运行时一次客户机对服务器的RPC调用步骤有:

  • 调用客户端句柄,执行传送参数
  • 调用本地系统内核发送网络信息
  • 消息传送到远程主机
  • 服务器句柄得到消息并取得参数
  • 执行远程过程
  • 执行的过程将结果返回服务器句柄
  • 服务器句柄返回结果,调用远程系统内核
  • 消息传回本地主机
  • 客户句柄由内核接收消息
  • 客户接收句柄返回的数据

标准库提供了 net/rpc 包用来实现基础的rpc调用。

net/rpc库使用encoding/gob进行编解码,支持tcphttp数据传输方式,由于其他语言不支持gob编解码方式,所以使用net/rpc库实现的RPC方法没办法进行跨语言调用。

主要有服务端和客户端。

首先是提供方法暴露的一方–服务器。

服务定义及暴露

在编程实现过程中,服务器端需要注册结构体对象,然后通过对象所属的方法暴露给调用者,从而提供服务,该方法称之为输出方法,此输出方法可以被远程调用。当然,在定义输出方法时,能够被远程调用的方法需要遵循一定的规则。我们通过代码进行讲解:

func (t *T) MethodName(request T1,response *T2) error

T、T1和T2类型都必须能被encoding/gob包编解码

任何RPC都需要通过网络来传递数据,Go RPC可以利用HTTP和TCP来传递数据

上述代码是go语言官方给出的对外暴露的服务方法的定义标准,其中包含了主要的几条规则,分别是:

  • 对外暴露的方法有且只能有两个参数,这个两个参数只能是输出类型或内建类型,两种类型中的一种。
  • 方法的第二个参数必须是指针类型。
  • 方法的返回类型为error。
  • 方法的类型是可输出的。
  • 方法本身也是可输出的。

HTTP服务端代码

package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"net/rpc"
)

type Arithmetic int

type Args struct {
	A, B int
}

func (a *Arithmetic) Add(args *Args, reply *int) error {
	fmt.Println("接收到请求信息 :", args)
	*reply = args.A + args.B
	return nil
}
// 正常情况下,方法的返回值为是error,为nil。如果遇到异常或特殊情况,则error将作为一个字符串返回给调用者,此时,resp参数就不会再返回给调用者。
// 至此为止,已经实现了服务端的功能定义,接下来就是让服务代码生效,需要将服务进行注册,并启动请求处理。

func main() {
	l, e := net.Listen("tcp", ":1234")
	if e != nil {
		log.Fatal("listen error:", e)
	}
	defer l.Close()
	_ = rpc.Register(new(Arithmetic))
	rpc.HandleHTTP()
	e = http.Serve(l, nil)
	if e != nil {
		log.Fatal("server error:", e)
	}
}
// 经过服务注册和监听处理,RPC调用过程中的服务端实现就已经完成了。

HTTP客户端代码

package main

import (
	"fmt"
	"log"
	"net/rpc"
)

type Args struct {
	A, B int
}

func main() {
	client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")
	if err != nil {
		log.Fatal("client error:", err)
	}
	args := Args{4, 2}
	var reply int
	err = client.Call("Arithmetic.Add", args, &reply)
	if err != nil {
		log.Fatal("arithmetic error:", err)
	}
	fmt.Println(reply)
}

net/rpc 还提供了一个简易的统计页供查看:
http://127.0.0.1:1234/debug/rpc
在这里插入图片描述

TPC服务端

func main() {
	l, e := net.Listen("tcp", ":1234")
	if e != nil {
		log.Fatal("listen error:", e)
	}
	defer l.Close()
	_ = rpc.Register(new(Arithmetic))
	conn, e := l.Accept()
	rpc.ServeConn(conn)
}

TCP客户端

func main() {
	client, err := rpc.Dial("tcp", "127.0.0.1:1234") // 仅这里发生了改变
	if err != nil {
		log.Fatal("client error:", err)
	}
	args := Args{4, 2}
	var reply int
	err = client.Call("Arithmetic.Add", args, &reply)
	if err != nil {
		log.Fatal("arithmetic error:", err)
	}
	fmt.Println(reply)
}

原理解析

首先注册了两个路由与handle,这是 rpc.HandleHTTP() 做的事情。

func (server *Server) HandleHTTP(rpcPath, debugPath string) {
	http.Handle(rpcPath, server)
	http.Handle(debugPath, debugHTTP{server})
}

func HandleHTTP() {
	DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath)
}

其中

DefaultRPCPath   = "/_goRPC_"
DefaultDebugPath = "/debug/rpc"

而且 /debug/rpc 在上面还用到过;
/_goRPC_ 则是rpc服务的路由,然后通过传递的参数去调用指定对象的方法。

再就是注册服务,其实就是通过反射导出对象的可调用的方法存入一个map。这是 rpc.Register(new(repo.Order)) 做的事情。

源码如下:

func (server *Server) register(rcvr interface{}, name string, useName bool) error {
	s := new(service)
	s.typ = reflect.TypeOf(rcvr)
	s.rcvr = reflect.ValueOf(rcvr)
	sname := reflect.Indirect(s.rcvr).Type().Name()
	if useName {
		sname = name
	}
	if sname == "" {
		s := "rpc.Register: no service name for type " + s.typ.String()
		log.Print(s)
		return errors.New(s)
	}
	if !token.IsExported(sname) && !useName {
		s := "rpc.Register: type " + sname + " is not exported"
		log.Print(s)
		return errors.New(s)
	}
	s.name = sname

	// Install the methods
	s.method = suitableMethods(s.typ, true)

	if len(s.method) == 0 {
		str := ""

		// To help the user, see if a pointer receiver would work.
		method := suitableMethods(reflect.PtrTo(s.typ), false)
		if len(method) != 0 {
			str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)"
		} else {
			str = "rpc.Register: type " + sname + " has no exported methods of suitable type"
		}
		log.Print(str)
		return errors.New(str)
	}

	if _, dup := server.serviceMap.LoadOrStore(sname, s); dup {
		return errors.New("rpc: service already defined: " + sname)
	}
	return nil
}

func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType {
	methods := make(map[string]*methodType)
	for m := 0; m < typ.NumMethod(); m++ {
		method := typ.Method(m)
		mtype := method.Type
		mname := method.Name
		...
		...
		methods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType}
	}
	return methods
}

为了更直观的理解,可以看下面的例子。

package main

import (
	"fmt"
	"reflect"
)

type Peaple struct {
}

func (p *Peaple) Eat() {
}
func (p *Peaple) Drink() {
}

func main() {
	a := new(Peaple)
	getType := reflect.TypeOf(a)
	getValue := reflect.ValueOf(a)
	obj := reflect.Indirect(getValue).Type().Name()
	num := getType.NumMethod()
	for i := 0; i < num; i++ {
		m := getType.Method(i)
		fmt.Println(obj + "." + m.Name)
	}
}

输出结果

Peaple.Drink
Peaple.Eat

也就是客户端调用的第一个参数。

参考地址

  • https://www.cnblogs.com/wanghui-garcia/p/10451028.html
  • https://studygolang.com/static/pkgdoc/pkg/net_rpc.htm
  • https://blog.csdn.net/raoxiaoya/article/details/109473904
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-04 11:34:02  更:2021-08-04 11:35:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 13:49:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计