Loading... # RPC [马士兵:30行代码透彻解析RPC,包你听懂](https://www.bilibili.com/video/BV17Z4y1s7cG) [什么是RPC?RPC好处?常用的RPC框架?](https://blog.csdn.net/qianlia/article/details/105508138) [**关于RPC**](https://www.zhihu.com/question/25536695) ## 0. 前置问题 1. rpc是什么 2. rpc使用tcp实现 3. rpc工作原理 4. rpc的应用 5. rpc与http之间的关系 ## 1. 概述 > RPC(Remote Procedure Call)远程过程调用。也就是说两台服务器A,B, 一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。 > > 比较正式的描述是:一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。 ## 2. 为什么要使用RPC > 如果我们开发简单的单一应用,逻辑简单、用户不多、流量不大,那我们用不着。 > > 当我们的系统访问量增大、业务增多时,我们会发现一台单机运行此系统已经无法承受。此时,我们可以将业务拆分成几个互不关联的应用,分别部署在各自机器上,以划清逻辑并减小压力。此时,我们也可以不需要RPC,因为应用之间是互不关联的。 > > 当我们的业务越来越多、应用也越来越多时,自然的,我们会发现有些功能已经不能简单划分开来或者划分不出来。此时,可以将公共业务逻辑抽离出来,将之组成独立的服务Service应用 。而原有的、新增的应用都可以与那些独立的Service应用 交互,以此来完成完整的业务功能。 > > 所以此时,我们急需一种高效的应用程序之间的通讯手段来完成这种需求,所以你看,RPC大显身手的时候来了! > > 其实描述的场景也是服务化 、微服务和分布式系统架构的基础场景。即RPC框架就是实现以上结构的有力方式。 ## 3. 简要原理  ``` client stub:客户端存根,存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。 ``` ``` server stub 服务端存根,接收客户端发送过来的消息,将消息解包,并调用本地的方法 ``` ## 4. RPC与HTTP > 首先需要指正,这两个并不是并行概念。RPC 是一种**设计**,就是为了解决**不同服务之间的调用问题**,完整的 RPC 实现一般会包含有 **传输协议** 和 **序列化协议** 这两个。 > > 而 HTTP 是一种传输协议,RPC 框架完全可以使用 HTTP 作为传输协议,也可以直接使用 TCP,使用不同的协议一般也是为了适应不同的场景。 > > 使用 TCP 和使用 HTTP 各有优势: > > **传输效率**: > > - TCP,通常自定义上层协议,可以让请求报文体积更小 > - HTTP:如果是基于HTTP 1.1 的协议,请求中会包含很多无用的内容 > > **性能消耗**,主要在于序列化和反序列化的耗时 > > - TCP,可以基于各种序列化框架进行,效率比较高 > - HTTP,大部分是通过 json 来实现的,字节大小和序列化耗时都要更消耗性能 > > **跨平台**: > > - TCP:通常要求客户端和服务器为统一平台 > - HTTP:可以在各种异构系统上运行 > > **总结**: > RPC 的 TCP 方式主要用于公司内部的服务调用,性能消耗低,传输效率高。HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等。 > > 作者:牛客网 > 链接:https://www.zhihu.com/question/25536695/answer/1846152026 > 来源:知乎 > 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 --- > > ## HTTP 调用其实也是一种特殊的 RPC > > HTTP1.0 协议时,HTTP 调用还只能是短链接调用,一个请求来回之后连接就会关闭。HTTP1.1 在 HTTP1.0 协议的基础上进行了改进,引入了 KeepAlive 特性可以保持 HTTP 连接长时间不断开,以便在同一个连接之上进行多次连续的请求,进一步拉近了 HTTP 和 RPC 之间的距离。 > >  > > 当 HTTP 协议进化到 2.0 之后,Google 开源了一个建立在 HTTP2.0 协议之上的通信框架直接取名为 gRPC,也就是 Google RPC,这时 HTTP 和 RPC 之间已经没有非常明显的界限了。所以在后文我们不再明确强调 RPC 和 HTTP 请求调用之间的细微区别了,直接统一称之为 RPC。 > >  > > ## HTTP VS RPC (普通话 VS 方言) > > HTTP 与 RPC 的关系就好比普通话与方言的关系。要进行跨企业服务调用时,往往都是通过 HTTP API,也就是普通话,虽然效率不高,但是通用,没有太多沟通的学习成本。但是在企业内部还是 RPC 更加高效,同一个企业公用一套方言进行高效率的交流,要比通用的 HTTP 协议来交流更加节省资源。整个中国有非常多的方言,正如有很多的企业内部服务各有自己的一套交互协议一样。虽然国家一直在提倡使用普通话交流,但是这么多年过去了,你回一趟家乡探个亲什么的就会发现身边的人还是流行说方言。 > > 如果再深入一点说,普通话本质上也是一种方言,只不过它是官方的方言,使用最为广泛的方言,相比而言其它方言都是小语种,小语种之中也会有几个使用比较广泛比较特色的方言占比也会比较大。这就好比开源 RPC 协议中 Protobuf 和 Thrift 一样,它们两应该是 RPC 协议中使用最为广泛的两个。 ## 5. RPC的应用 netty、dubbo、motan ## 6. RPC要解决的问题 - protocol:传输协议 - proxy:client代理,服务引用方调用方法通过代理发送远程消息 - codec:协议编解码压缩等 - transport:协议传输 - registry:注册中心,服务注册服务发现 - cluster:负载均衡,服务容错策略 - 其他:服务降级,服务隔离,服务治理 ## 7. 如何实现一个简单的RPC [如何实现一个简单的RPC](https://www.jianshu.com/p/5b90a4e70783) ProviderApp ```java public class ProviderApp { private static Logger log = LoggerFactory.getLogger(ProviderApp.class); private Calculator calculator = new CalculatorImpl(); public static void main(String[] args) throws IOException { new ProviderApp().run(); } private void run() throws IOException { ServerSocket listener = new ServerSocket(9090); try { while (true) { Socket socket = listener.accept(); try { // 将请求反序列化 ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); Object object = objectInputStream.readObject(); log.info("request is {}", object); // 调用服务 int result = 0; if (object instanceof CalculateRpcRequest) { CalculateRpcRequest calculateRpcRequest = (CalculateRpcRequest) object; if ("add".equals(calculateRpcRequest.getMethod())) { result = calculator.add(calculateRpcRequest.getA(), calculateRpcRequest.getB()); } else { throw new UnsupportedOperationException(); } } // 返回结果 ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); objectOutputStream.writeObject(new Integer(result)); } catch (Exception e) { log.error("fail", e); } finally { socket.close(); } } } finally { listener.close(); } } } ``` ComsumerApp ```java public class ComsumerApp { private static Logger log = LoggerFactory.getLogger(ComsumerApp.class); public static void main(String[] args) { Calculator calculator = new CalculatorRemoteImpl(); int result = calculator.add(1, 2); log.info("result is {}", result); } } ``` CalculatorRemoteImpl ```java public class CalculatorRemoteImpl implements Calculator { public static final int PORT = 9090; private static Logger log = LoggerFactory.getLogger(CalculatorRemoteImpl.class); public int add(int a, int b) { List<String> addressList = lookupProviders("Calculator.add"); String address = chooseTarget(addressList); try { Socket socket = new Socket(address, PORT); // 将请求序列化 CalculateRpcRequest calculateRpcRequest = generateRequest(a, b); ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); // 将请求发给服务提供方 objectOutputStream.writeObject(calculateRpcRequest); // 将响应体反序列化 ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); Object response = objectInputStream.readObject(); log.info("response is {}", response); if (response instanceof Integer) { return (Integer) response; } else { throw new InternalError(); } } catch (Exception e) { log.error("fail", e); throw new InternalError(); } } private CalculateRpcRequest generateRequest(int a, int b) { CalculateRpcRequest calculateRpcRequest = new CalculateRpcRequest(); calculateRpcRequest.setA(a); calculateRpcRequest.setB(b); calculateRpcRequest.setMethod("add"); return calculateRpcRequest; } private String chooseTarget(List<String> providers) { if (null == providers || providers.size() == 0) { throw new IllegalArgumentException(); } return providers.get(0); } public static List<String> lookupProviders(String name) { List<String> strings = new ArrayList(); strings.add("127.0.0.1"); return strings; } } ``` 思考:现存的问题? 1. 缺乏通用性 Dubbo 动态代理 2. 服务注册中心 要调用服务,首先你需要一个服务注册中心,告诉你对方服务都有哪些实例。Dubbo的服务注册中心是可以配置的,官方推荐使用Zookeeper。如果使用Zookeeper的话,要怎样往上面注册实例,又要怎样获取实例,这些都是要实现的。 3. 负载均衡 一般服务器在部署的时候都是集群部署的,在发现服务的时候可能有多台机器,这个时候需要特定的负载均衡算法选择具体的服务器,进行调用,这就要用到负载均衡了。负载均衡的策略肯定不只一种,要怎样把策略做成可配置的?又要如何实现这些策略? 4. 结果缓存 每次调用查询接口时都要真的去Server端查询吗?是不是要考虑一下支持缓存? 5. ... ... Last modification:July 18th, 2021 at 11:17 am © 允许规范转载