RPC全称Remote Procedure Call,即远程过程调用。其本质上其实就是主机A通过某种网络协议向支持相同协议的主机B发送一个任务执行命令,并且在某些情况下,还能支持任务执行结果的返回。
几乎每一个RPC都有着自己的网络协议定义,如果要按照TCP/IP协议栈划分,这些RPC协议通HTTP/HTTPS协议一样属于应用层协议,不过相比较于HTTP/HTTPS协议来说,RPC协议在功能和性能之间更偏重与性能,即RPC框架是一种高性能的网络通信框架。
RPC通信单独来说只是一种高性能的网络通信技术,和http相比除了快和简单并没有任何更为出采的地方,更不可能取代http在网络通信中的地位。但是技术是要服务于架构的,只有在特定的场景下才能发挥其长处,在实际开发中,RPC框架多用于微服务架构中。
微服务是近些年才提出的概念,至于什么是微服务,在我查阅了许多资料之后也并没有找到让人豁然开朗的定义。要想了解什么是微服务,还是要看一下Java服务端开发的演变过程才能够有一个浅显的认识。
Java服务端开发在早期多指B/S架构下的JavaWeb应用程序开发。开发者遵循Servlet规范将开发好的JavaWeb程序打包成war包运行在支持http/https协议的Servlet容器中,比如tomcat,jetty等。而且当时互联网用户还比较少,JavaWeb应用程序还都比较简单,一般就是一台机器,如果挂了就人工重启。
随着时代的发展,互联网用户越来越多,网站的并发访问量也越来越大,一台机器已经无法满足我们的需求了,就产生了集群的概念。集群简单来说就是一群运行着相同程序的服务器,这群服务器对外提供的可能只是一个域名或者反向代理的机器地址,至于网站到底有多少服务器以及用户到底访问的是哪台服务器,用户是无感知的。
随着用户变得更多,集群愈发的庞大,我们每一次更新功能都需要将应用重新发布在所有服务器上,哪怕我们仅仅是改了一个数据库的地址。而且当应用越来越复杂时,代码体积也会越来越庞大,每一次重新运行都需要停机很长时间。对于一些用户量特别多且与金钱密切相关的系统中,这是我们不能接受的,其中最典型的就是电商。
为了改善上面的痛点,架构师根据业务自身的特性将整个系统拆分成一个个的小的系统,比如对于用户身份验证有专门的登录系统,购物车有专门的购物车系统,这些独立的功能各自有各自的实现,但应用最终组合起来依然满足业务的需求,而且解决了上述痛点,这各阶段应该叫做应用切分。
应用切分虽然解决了应用臃肿的问题,但由于应用之间是互相独立的,所以有些本来相同的业务代码变得不可复用,比如在应用切分中,购物车系统和支付系统都需要订单系统的支持。但是由于代码不是共用的,在购物车系统和支付系统中都有着一套和订单系统进行交互的业务代码,比如http请求接口,数据转换等。
为了解决在 应用切分钟,系统间代码冗余度过高的问题,服务化架构出现了。在服务化架构中,系统依然被拆分为多个应用,但与此同时, 应用与应用之间相互冗余的部分也被抽取出来了,作为一个服务单独存在。比如上面讲到的购物车系统和支付系统中查询订单的业务代码,被提取出来作为一个订单查询服务而单独存在。
而服务化又带来了新的痛点。服务之间相互依赖,一个服务既可以作为Provider对外提供功能,又需要作为Consumer来依靠其他服务的实现,所以需要进行服务的治理。然而应用并不关心服务治理的细节,更不关心服务到底提供哪些功能。比如在APP上要实现一个下单成功的功能,总不能在APP客户端既要调用订单系统的服务去更新订单,又要调用购物车系统提供的服务去更新购物车吧。而是只需要一个API,调用成功就表示用户成功支付,订单已完成。而这一的一个个应用层之间使用的API,也可以被应用层看做一个个的服务,这就是微服务架构。其中用来对这些微服务中的API进行统一管理的模块通常被称为网关。
在微服务和服务化架构中,不同的服务之间想要互相通信就只能通过网络,而系统之所以被做成服务化,其中一条主要的原因就是因为访问量巨大,所以不同的功能模块之间的网络通信是一个极其频繁的事情。HTTP协议作为一个功能强大的网络通信协议,随之带来的问题是开销太大,用在微服务架构下过犹不及。因此,RPC通信协议产生了,协议简单随之而来的是性能优越。
早在Java1.2的时候,JDK就提供了RPC功能——RMI
RMI的使用起来也很简单,只需要定义一个服务的提供者,服务的调用者以及服务的具体实现:
![54fca273e1ebbc5f50d49bb022fc61a9.png](https://i-blog.csdnimg.cn/blog_migrate/7bf978f6f3cb105591e5e6db654fcc86.png)
![9d72ce4253277b9d857978816a620478.png](https://i-blog.csdnimg.cn/blog_migrate/9e37a4dfefd6ccfd060ce9b0cfcda3db.png)
![513c6a1fed6f1c49f22d3bd3a808ed23.png](https://i-blog.csdnimg.cn/blog_migrate/d7dcbcafcfd2af657566cf7215d3d0b8.jpeg)
![005994608cf9a352b2d4b474f0543251.png](https://i-blog.csdnimg.cn/blog_migrate/96f02a03b6f306ba839c1bbf2cd3b81c.jpeg)
![2c5c437755e8372b9c6945d2a93b1b8b.png](https://i-blog.csdnimg.cn/blog_migrate/1361c119cde45f7969d8f4f8cb1450bf.jpeg)
整体结构很简单,服务提供者通过LocateRegistry注册实现了Remote接口的服务。服务的调用者通过Naming,根据服务的地址返回服务的实现类注册的实例,然后服务调用方就可以在本地使用这个实例实现功能了。运行结果如下:
![a761542927458386205476330af083e9.png](https://i-blog.csdnimg.cn/blog_migrate/c338f360ebf6852de021b00e512076d5.png)
Rmi底层也是通过TCP协议实现的,我们可以在系统监控中看到Provider监听了12345这个端口:
![c219952477bdb48cceec2c529911eb40.png](https://i-blog.csdnimg.cn/blog_migrate/013ce3f5fc39478f61ec7733fd96b6ca.png)
![3a807ae21ffc64f3f71fc5add0aa3940.png](https://i-blog.csdnimg.cn/blog_migrate/9810c047f4232526f68aa965ba29ffe7.png)
我们可以通过Wireshark抓包来看下Rmi协议交互的基本流程。因为Provider和Caller都在本地,由于底层优化,流量不会通过网卡,所以Wireshark是抓不到包的,可以装一个npcap工具就行了:
wireshark如何抓取本机包 - Avatarx - 博客园www.cnblogs.com![958ec35f2b9e20d4dc65ceeaf4e547b2.png](https://i-blog.csdnimg.cn/blog_migrate/34e0d58e5ef37bfffa8ec0efba814fdb.jpeg)
之后在Wireshark中使用Npcap的网卡适配器就能抓到本地数据了
![506c848bf6a64249ba6ce23d7372c6ab.png](https://i-blog.csdnimg.cn/blog_migrate/dfe447c54304f2165ba1638c236726a8.png)
![64fe204d34d23c741740c207543052c1.png](https://i-blog.csdnimg.cn/blog_migrate/45b5126449f49174f1389081f3a21fcc.jpeg)
可以看到最长的一个数据包长达328个字节,即使数据看起来应该经过了特殊处理,还是能够看到其返回的应该是服务实现类实例的一个代理:
![6000d91ee42f6fddd7c98e573ac20494.png](https://i-blog.csdnimg.cn/blog_migrate/f07b31bbb49ebdbe3cb06b3333770ceb.jpeg)
不过由于RMI底层是通过BIO来实现的,必然无法应用在复杂场景下,所以一些基于NIO的RPC框架产生了,比较典型的有阿里巴巴的Dubbo、HSF,Spring的Spring Cloud等。