不背八股!!!面试官:为什么有了HTTP还需要RPC?

前言

在前文为什么说TCP是基于字节流的?中我们介绍了为什么TCP是基于字节流的,其中最本质的原因就是因为TCP是流式传输数据的,并且TCP的数据格式没有消息边界。

在网络实践中,为了解决TCP粘包的问题,我们通常使用的是自定义消息头 的方法,因为这样比较高效且灵活,通常消息头内有一些控制信息,比如这条消息的包长度具体是多少,这条消息的格式是什么样子的,有没有进行压缩,来源方IP,来源方是哪里等等。后来随着这种方式慢慢在互联网中被认同,就形成了各种各样的规范,我们将这些规范称之为协议 ,而这些协议通常是在传输层之上的应用层实现的,HTTPRPC就是其中之二。

按照四层网络来看,每一层对应的常用协议大概是下面这样子的。

HTTP与RPC

HTTP协议 (Hyper Text Transfer Protocol),又叫做超文本传输协议,常见于我们的web服务中,它就是用来解析、传输由html标签组成的超文本的。

RPC协议 (Remote Procedure Call),直译过来为远程过程调用,它的定义是允许程序执行在远程计算机上的过程或函数,而不需要显示的处理底层的通信细节RPC并不是一种协议,而是一类协议,只要某个协议能够做到执行远程计算机上的程序/应用,那么就可以称之为RPC协议,现在常用的RPC协议有gRPC(底层实现也是使用的http 2.0)、thifht等等。

从这个角度来说,HTTP其实也是RPC的一种实现,同时也是使用最多的一种实现。

历史发展

其中TCP是在70年代被Vinton Cert和Bob Kahn等人开发的,RPC的概念最早由Bruce Jay Nelson于81年提出,最早的RPC协议是在84年由NFS实现的,而HTTP1.0版本在1996年发布,我们最常用的HTTP1.1 版本是在99年才产生的。

计算机科学的发展是很快的,中间的十几年涌现了各种各样的协议,但是因为各种各样的原因又淹没在历史中,之后因为HTTP的简便性、灵活性、可扩展性等优势,才被广泛适用于浏览器、服务器之间的交互,甚至于服务器、服务器之间的交互,这又更能体现HTTPRPC的实现之一了。

所以上面的问题应该换一换,改为:为什么有了RPC还需要HTTP?

但是我们为什么会觉得 HTTP RPC是两类协议,或者为什么会觉得RPC比HTTP更加高级,更晚出现呢?

我想这和当前开发者的学习路径是分不开的,大部分开发者首先接触到的是HTTP协议,也就是web开发相关的内容;后续技术精进之后,接触到分布式相关内容,这才接触到RPC这类协议。所以我们很容易先入为主的觉得RPCHTTP更加高级、更晚出现。

比较

服务发现

服务发现就是找到具体请求需要发送给哪一台服务器,在HTTP中这个过程是由DNS自动来实现的,无需人为配置;但是在RPC中通常都是通过服务注册与发现中心进行服务发现,例如consul或者etcd等等,需要引入额外的第三方组件。

底层协议

二者都是用的TCP连接,并且可以开启长链接模式进行数据交互。

消息格式

HTTP通常使用的是JSON这种易读的键值对消息格式进行消息的传输,而RPC为了追求效率,通常使用的是不易读但是对机器友好的二进制消息格式,比如protobuf这种。

使用场景

目前HTTP在公司中主要负责向外提供服务,而RPC主要负责内部之间进行通信,但是现在随着架构的融合发展以及HTTP效率的提升,不少内部服务之间进行通信也是使用的HTTP,因为HTTP足够简单方便且高效。

HTTP与RPC实现

HTTP实现

HTTP服务端代码

go 复制代码
// 定义参数和响应
type addParam struct {
	X int `json:"x"`
	Y int `json:"y"`
}
type addResult struct {
	Code int `json:"code"`
	Data int `json:"data"`
}

func add(x, y int) int {
	return x + y
}

// addHandler 解析参数+调用add+响应写回
func addHandler(w http.ResponseWriter, r *http.Request) {
	// parse parameters
	b, _ := io.ReadAll(r.Body)
	var param addParam
	json.Unmarshal(b, &param)
	// use the add func
	ret := add(param.X, param.Y)
	// return the response
	respBytes, _ := json.Marshal(addResult{Code: 0, Data: ret})
	w.Write(respBytes)
}

func main() {
	http.HandleFunc("/add", addHandler)
	log.Fatal(http.ListenAndServe(":9090", nil))
}

HTTP客户端代码

go 复制代码
type addParam struct {
	X int `json:"x"`
	Y int `json:"y"`
}
type addResult struct {
	Code int `json:"code"`
	Data int `json:"data"`
}

func main() {
	url := "http://127.0.0.1:9090/add"
	param := addParam{
		X: 10,
		Y: 20,
	}
	// marshal to json

	paramBytes, _ := json.Marshal(param)
	// call
	resp, _ := http.Post(url, "application/json", bytes.NewReader(paramBytes))
	defer resp.Body.Close()
	respBytes, _ := ioutil.ReadAll(resp.Body)
	var respData addResult
	json.Unmarshal(respBytes, &respData)
	fmt.Println(respData.Data)
}

RPC实现

RPC Service实现

go 复制代码
type Args struct {
	X, Y int
}

type ServiceA struct{}

// Add is an out method
// has two args and a return 
// two params must be out 
// and the return value must be error type 
func (s *ServiceA) Add(args *Args, reply *int) error {
	*reply = args.X + args.Y
	return nil
}

RPC Server实现

go 复制代码
func main() {
  //new service instance   
	service := new(yunyuansheng.ServiceA)
  //register rpc service   
	rpc.Register(service) 
  //botton on http  
	//rpc.HandleHTTP()      
  //botton on tcp   
	l, e := net.Listen("tcp", ":9091")
	if e != nil {
		log.Fatal("listen error:", e)
	}
	//http.Serve(l, nil)
	for {
	  // accpet the request and serve   
		conn, _ := l.Accept()
		rpc.ServeConn(conn)
	}
}

RPC Client实现

go 复制代码
func main() {
	//因为服务端是HTTP请求 所以要建立HTTP连接
	client, err := rpc.Dial("tcp", "127.0.0.1:9091")
	if err != nil {
		fmt.Println(err)
	}
	// 同步调用 Call
	args := &yunyuansheng.Args{10, 20}
	reply := new(int)
	err = client.Call("ServiceA.Add", args, reply)
	if err != nil {
		log.Fatal("ServiceA.Add error:", err)
	}
	fmt.Printf("ServiceA.Add %d+%d=%d\n", args.X, args.Y, *reply)

	//异步调用 Go
	var reply2 int
	divCall := client.Go("ServiceA.Add", args, &reply2, nil)
	replyCall := <-divCall.Done //Done是一个调用结果的通知 有值了就说明调用完成了
	fmt.Println(replyCall.Error)
	fmt.Println(reply2)
}

结语

本文主要向大家介绍了TCP与HTTP及RPC协议之间的关系,通过历史发展的角度,将有了HTTP为什么还要有RPC这个问题拨乱反正为:有了RPC为什么还要有HTTP,并介绍了二者的优缺点及相关代码示例,相信在面试官面前吹水是绝对够用了。创作不易,如果有收获欢迎点赞、评论、收藏,您的支持就是我最大的动力。

相关推荐
mit6.8242 小时前
[实现Rpc] 通信类抽象层 | function | using | 解耦合设计思想
c++·网络协议·rpc
小钊(求职中)4 小时前
Java开发实习面试笔试题(含答案)
java·开发语言·spring boot·spring·面试·tomcat·maven
小小码农(找工作版)4 小时前
JavaScript 前端面试 4(作用域链、this)
前端·javascript·面试
卷心菜不卷Iris5 小时前
第1章大型互联网公司的基础架构——1.6 RPC服务
网络·网络协议·微服务·rpc·http协议·rpc协议
Asthenia04126 小时前
如何在项目中集成GC日志输出与高效分析?一篇开发者必读的实践指南
后端
码界筑梦坊6 小时前
基于Flask的第七次人口普查数据分析系统的设计与实现
后端·python·信息可视化·flask·毕业设计
uhakadotcom7 小时前
约束求解领域的最新研究进展
人工智能·面试·架构
是纯一呀7 小时前
WebSocket(WS)协议系列(一)基本概念
网络·websocket·网络协议
zhj16953697 小时前
手写简易RPC(实践版)
java·网络·网络协议·rpc
2301_793069827 小时前
HTTP 和RESTful API 基础,答疑
网络协议·http·api·restful