微服务设计笔记(8)—— RPC 调用方式
发布日期:2021-06-29 21:01:52 浏览次数:2 分类:技术文章

本文共 2455 字,大约阅读时间需要 8 分钟。

RPC(Remote Procedure Call)—远程过程调用,它是一种不需要了解底层网络技术的协议,就可以通过网络,请求远程服务器上的服务。

我们可以调用本地的一个 RPC , 实际上,响应结果是由远程服务器返回的 。RPC 有很多种类型 , 比如 SOAP、Thrift、 protocol buffers 等等 )。不同的技术栈,可以通过其接口定义,很方便地生成客户端或服务端的桩代码 。

  • SOAP(Simple Object Access Protocol),即简单对象访问协议。它是交换数据的一种协议规范,是一种轻量的、简单的、基于XML的协议,它被设计成可在 WEB 上交换结构化或固化的信息。
  • Thrift 是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言服务 。它是由 Facebook 为支持 “ 大规模跨语言服务” 而开发的 。
  • protocol buffer 是 google 的一个开源项目。它可串行化结构化的数据。就像 XML ,但它比 XML 更小 、 更快 、 也更简单 。 我们可以定义自己的数据结构,然后使用代码生成器所生成的代码来读写这个数据结构 。甚至可以在无需重新部署程序的情况下更新自定义的数据结构。

比如, 我们可以让一个 Java 服务对外表现为一个 SOAP 服务接口 , 调用方可以依据使用 WSDL( Web Service Definition Language,Web 服务描述语言 ) 接口定义内容,来生成基于 .NET 的客户端代码 。 这些技术都有一个共同点 , 那就是使用本地调用的方式和远程服务器进行交互。

Java RMI、 Thrift、 protocol buffers 是以二进制作为消息格式;而 SOAP 用的是 XML,而且绑定特定的网络协议(HTTP)。不同的网络协议,特性也不同。比如, TCP 协议能够保证消息送达对端;而 UDP 虽然会丢包,但开销较小。 所以我们可以根据实际应用场景来选择不同的技术栈。

这些 RPC 实现一般会提供工具,快速生成服务端或客户端的桩代码 , 这样我们就可以直接开始编码 。

实际应用中,RPC 调用方式并没有那么好。一开始,问题还不那么明显 , 但慢慢就会暴露出来 , 其带来的负面影响要远远大于一开始快速编码所带来的好处。

(1)耦合

比如 Java RMI(Remote Method Invocation), 会导致服务端和客户端紧密耦合 , 因为双方都必须使用相同的 Java 技术栈。而 Thrift 和 protocol buffers 可以支持不同编程语言 , 从而在一定程度上缓解了这个问题 。

(2)远程调用的复杂性

RPC 的原意是隐藏远程调用的复杂性 。但远程调用特定涉及网络通信时间、对传输对象的序列化与反序列化,这样都会影响性能。

还有网络本身并不可靠。所以即使客户端和服务端都正常,也会因为网络问题,导致服务调用失败。还有黑客攻击情况也要予以考虑。

(3)脆弱性

假设我们使用 Java RMI 定义了一个服务接口:

import java.rmi.Remote;import java.rmi.RemoteException;public interface CustomerRemote extends Remote {    /**     * 查找客户     *     * @param id     * @return     * @throws RemoteException     */    Customer find(String id) throws RemoteException;    /**     * 创建客户     * @param firstname     * @param surname     * @param email     * @return     * @throws RemoteException     */    Customer create(String firstname, String surname, String email) throws RemoteException;}

在这个接口定义中 , “创建客户” 方法,接受姓名及电子邮件作为入参 。 如果客户端希望只通过电子邮件就可以创建客户,我们可以在这个接口中,新定义一个方法 , 如下所示:

Customer create( String email) throws RemoteException;

因为重新定义了接口,所以所有的客户端都需要重新生成桩,即使某些客户端根本不需要这个新方法 。这是一个普遍现象,所以认为RPC 调用方式是脆弱的。

此外,还有一种形式的脆弱。 现在让我们来看看 Customer 对象:

import java.io.Serializable;public class Customer implements Serializable {    private String firstName;    private String surName;    private String email;    private String age;}

这里的 Customer 客户对象,除了之前在接口中所看到的 firstName、surName 和 age 之外,还定义了 age 属性。后来发现这个属性,完全没有任何客户端在使用它,是一个冗余字段。但不能直接在服务端删除它,因为会影响各个调用者的 Customer 客户对象,即使是基于二进制消息格式的 RPC 也存在同样的问题,即服务端和客户端无法实现部署分离。


如果一定要选用 RPC 调用方式,那么注意不要对远程调用过度抽象,让客户端留意网络调用的影响。还要确保我们可以独立地升级服务端接口,而不是强迫客户端升级。

转载地址:https://deniro.blog.csdn.net/article/details/101158608 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:说说 Python 中如何连接或复制列表
下一篇:微服务设计笔记(7)—— 编排与协同

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年04月28日 20时21分45秒