Java远程通信技术及原理

在分布式服务框架中,远程服务是怎么通讯的,在 Java 领域中有很多可能实现通讯的技术,如 RMI 、MINA 、ESB 、Burlap 、Hessian 、SOAP 、EJB 和 JMS 等,这些名词之间到底是些什么关系,他们背后到底是基于什么原理实现的?

1. 基本原理

要实现网络机器间的通讯,首先得来看看计算机网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 tcp,udp 等,tcp、udp 都是基于Socket 概念上为应用场景而扩展出的传输协议,网络 IO,主要有 bio、nio、aio 三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。

2. 消息模式

归根结底,企业应用系统就是对数据的处理,而对于一个拥有多个子系统的企业应用系统而言,它的基础支撑无疑是对消息的处理。与对象不同,消息本质上是一种数据结构(当然,对象也可以看成是一种特殊的消息),它包含消费者和服务双方都能识别的数据,这些数据需要在不同的进程(机器)之间进行传递,并可能会被多个完全不同的客户端消费。消息传递相较文件传递与远程过程调用(RPC)而言,似乎更胜一筹,因为它具有更好的平台无关性,并能够很好地支持并发与异步调用。

对于 Web Service 与 RESTful 而言,则可以看作是消息传递技术的一种衍生或封装。

2.1 消息通道(Message Channel)模式

我们常常运用的消息模式是Message Channel(消息通道)模式,如图:


消息通道作为在客户端(消费者,Consumer)与服务端 (生产者,Producer)之间引入的间接层,可以有效地解除二者之间的耦合。只要实现规定双方需要通信的消息格式,以及处理消息的机制与时机,就可以做到消费者对生产者的“无知”。事实上,该模式可以支持多个生产者向消息通道发送消息,因为消费者对生产者的无知性,他不必考虑究竟是哪个生产者发来的消息。

虽然消息通道解除了生产者与消费者之间的耦合,使得我们可以任意地对生产者与消费者进行扩展,但她又同时引入了各自对消息通道的依赖,因为他们必须知道通道资源的位置,要解除这种对通道的依赖,可以考虑引入 Lookup 服务来查找该通道资源。如,在 JMS 中就可以通过 JNDI 来获取消息通道 Queue。若要做到充分的灵活性,可以将与通道相关的信息存储到配置文件中,Lookup 服务首先通过读取配置文件来获得通道。

消息通道通常以队列的形式存在,在这种先进先出的数据结构无疑是最为合适这种处理消息的场景。微软的 MSMQ、IBM MQ、JBoss MQ 以及开源的 RabbitMQ、Apache ActiveMQ 都通过队列实现了 Message Channel 模式。因此,在选择运用Message Channel 模式时,更多地是要从质量属性的层面对各种实现了该模式的产品进行全方位的分析与权衡。如,消息通道对并发的支持以及在性能上的表现;消息通道是否充分地考虑了错误处理;对消息安全的支持;以及关于消息持久化、灾备(fail over)与集群等方面的支持。

2.2 发布者-订阅者(Publisher-Subscriber)模式

一旦消息通道需要支持多个消费者时,就可能面临两种模式的选择:拉模型与推模型。拉模型是由消息的消费者发起的,主动权把握在消费者手中,他会根据自己的情况对生产者发起调用,如图:


拉模型的另一种体现则由生产者在状态发生变更时,通知消费者其状态发生了改变。但得到通知的消费者却会以回调方式,通过调用传递过来的消费者对象获取更多细节消息。

在基于消息的分布式系统中,拉模型的消费者通常以 Batch Job 的形式,根据事先设定的时间间隔,定期侦听通道的情况,一旦发现有消息传递进来,就会转而将消息传递给真正的处理器(也可以看做是消费者)处理消息,执行相关的业务。

推模型的主动权常常掌握在生产者手中,消费者被动地等待生产者发出的通知,这就要求生产者必须了解消费者的相关信息,如图:

对于推模型而言,消费者无需了解生产者。在生产者通知消费者时,传递的往往是消息(或事件),而非生产者本身。同时,生产者还可以根据不同的情况,注册不同的消费者。又或者在封装的通知逻辑中,根据不同的状态变化,通知不同的消费者。

两种模型各有优势,拉模型的好处是在于可以进一步解除消费者对通道的依赖,通过后台任务去定期访问消息通道。坏处是需要引入一个单独的服务进程,以 Schedule 形式执行。而对于推模型而言,消费通道事实上会作为消费者观察的主体。一旦发现消息进入,就会通知消费者执行对消息的处理。无论推模型,拉模型,对于消息对象而言,都可能采用类似 Observer 模式的机制,实现消费者对生产者的订阅,因此这种机制通常又被称为Publisher-Subscriber模式,如图:


通常情况下,发布者和订阅者都会被注册到用于传播变更的基础设施(即消息通道)上。发布者会主动地了解消息通道,使其能够将消息发送到通道中;消息通道一旦接收到消息,会主动地调用注册在通道中的订阅者,进而完成对消息内容的消费。

对于订阅者而言,有两种处理消息的方式。一种是广播机制,这时消息通道中的消息在出列的同时,还需要复制消息对象,将消息传递给多个订阅者。如,有多个子系统都需要获取从 CRM 系统传来的客户信息,并根据传递过来的客户信息,进行相应的处理。此时消息通道又被称为 Propagation 通道。另一种方式则属于抢占机制,他遵循同步方式,在同一时间只能有一个订阅者能够处理该消息。实现 Publisher-Subscriber 模式的消息通道会选择当前空闲的唯一订阅者,并将消息出列,并传递给订阅者的消息处理方法。

2.3 消息路由(Message Router)模式

无论是 Message Channel 模式,还是 Publisher-Subscriber 模式,队列在其中都扮演了举足轻重的角色。然而,在企业应用系统中,当系统变得越来越复杂时,对性能的要求会越来越高,此时对于系统而言,可能就需要支持同时部署多个队列,并可能要求分布式部署不同的队列。这些队列可以根据定义接收不同的消息,例如订单处理的消息,日志消息,查询任务消息鞥。这时,对于消息的生产者和消费者而言,并不适宜承担决定消息传递路径的职责。事实上,根据单一职责原则,这种职责分配也是不合理的,它既不利于业务逻辑的重用,也会造成生产者、消费者与消息队列之间的耦合,从而影响系统的扩展。

既然这三种对象(组件)都不宜承担这样的职责,就有必要引入一个新的对象专门负责传递路径的选择功能,这就是所谓的Message Router 模式,如图:


通过消息路由,我们可以配置路由规则指定消息传递的路径,以及指定具体的消费者消费对应的生产者。如指定路由的关键字,并由他来绑定具体的队列与指定的生产者(或消费者)。路由的支持提供了消息传递与处理的灵活性,也有利于提高整个系统的消息处理能力。同时,路由对象有效地封装了寻找与匹配消息路径的逻辑,就好似一个调停者(Meditator),负责协调消息、队列与路径寻址之间的关系。

3. 应用级协议

远程服务通讯,需要达到的目标是在一台计算机发起请求,另外一台机器在接收到请求后进行相应的处理并将结果返回给请求端,这其中又会有诸如 one way request、同步请求、异步请求等请求方式,按照网络通信原理,需要实现这个需要做的是将请求转换成流,通过传输协议传输至远端,远端计算机在接收到请求的流后进行处理,处理完毕后将结果转化为流,并通过传输协议返回给调用端。

原理是这样的,但为了应用的方便,业界推出了很多基于此原理之上的应用级的协议,使得大家可以不用去直接操作这么底层的东西,通常应用级的远程通信协议会提供:

1. 为了避免直接做流操作这么麻烦,提供一种更加易或贴合语言的标准传输格式;

2. 网络通信机制的实现,就是替你完成将传输格式转化为流,通过某种传输协议传输至远端计算机,远端计算机在接收到流后转化为传输格式,并进行存储或以某种方式通知远端计算机。

所以我们学习应用级的远程通信协议时,我们可以带着这几个问题进行学习:

1. 传输的标准格式是什么?

2. 怎么样将请求转化为传输的流?

3. 怎么接收和处理流?

4. 传输协议是?

不过应用级的远程通信协议并不会在传输协议上做什么多大的改进,主要是在流操作方面,让应用层生成流和处理流的这个过程更加的贴合所使用的语言或标准。至于传输协议都是可选的,在Java领域中知名的有:RMI、XML-RPC、Binary-RPC、SOAP、CORBA、JMS、HTTP。

3.1 RMI(远程方法调用)

RMI 是个典型的为 Java 定制的远程通信协议,我们都知道,在 single vm 中,我们可以通过直接调用java object instance来实现通信,那么在远程通信时,如果也能按照这种方式当然是最好了,这种远程通信的机制成为RPC(Remote Procedure Call),RMI 正是朝着这个目标而诞生的。

RMI 采用 stubs 和 skeletons 来进行远程对象(remote object)的通讯。stub 充当远程对象的客户端代理,有着和远程对象相同的远程接口,远程对象的调用实际是通过调用该对象的客户端代理对象 stub 来完成的,通过该机制 RMI 就好比它是本地工作,采用 tcp/ip 协议,客户端直接调用服务端上的一些方法。优点是强类型,编译期可检查错误,缺点是只能基于 JAVA 语言,客户机与服务器紧耦合。

看下基于 RMI 的一次完整的远程通信过程的原理:

1. 客户端发起请求,请求转交至 RMI 客户端的 stub 类;

2. stub 类将请求的接口、方法、参数等信息进行序列化;

3. 基于 socket 将序列化后的流传输至服务器端;

4. 服务器端接收到流后转发至相应的 skelton 类;

5. skelton 类将请求的信息反序列化后调用实际的处理类;

6. 处理类处理完毕后将结果返回给 skelton 类;

7. skelton 类将结果序列化,通过 socket 将流传送给客户端的 stub;

8. stub 在接收到流后反序列化,将反序列化后的 java object 返回给调用者。

根据原理来回答之前学习应用级协议带着的问题:

1. 传输的标准格式是什么?是 Java ObjectStream。

2. 怎么样将请求转化为传输的流?基于 Java 串行化机制将请求的 java Object 信息转化为流。

3. 怎么接收和处理流?根据采用的协议启动相应的监听端口,当有流进入后基于 java 串行化机制将流进行反序列化,并根据 RMI 协议获取到相应的处理对象信息,进行调用并处理,处理完毕后的结果同样基于 java 串行化机制进行返回。

4. 传输协议是?socket

3.2 XML-RPC

RPC 使用 C/S 方式,采用 http 协议,发送请求到服务器,等待服务器返回结果。这个请求包括一个参数集合一个文本集,通常形成“classname.methodname”形式。优点是跨语言跨平台,C 端、S 端有更大的独立性,缺点是不支持对象,无法在编译器检查错误,只能在运行期检查。

XML-RPC 也是一种和 RMI 类似的远程调用的协议,它和 RMI 的不同之处在于它以标准的 xml 格式来定义请求的信息(请求的对象、方法、参数等),这样的好处是什么呢?就是在跨语言通讯时也可以使用。

看下 XML-RPC 协议的一次远程通信过程:

1. 客户端发起请求,按照 XML-RPC 协议将请求信息进行填充;

2. 填充完毕后将 xml 转化为流 ,通过传输协议进行传输;

3. 在接收到流后转化为 xml,按照 XML-RPC 协议获取请求的信息并进行处理;

4. 处理完毕后将结果按照 XML-RPC 协议写入 xml 中并返回。

同样回答问题:

1. 传输的格式标准是?XML

2. 怎么样将请求转化为传输的流?将 XML 转化为流。

3. 怎么接收和处理流?通过监听的端口获取到请求的流,转化为 XML,并根据协议获取请求的信息,进行处理并将结果写入XML 中返回。

4. 传输协议是?http

3.3 Binary-RPC

Binary-RPC 看名字就知道和 XML-RPC 是差不多的,不同之处在于传输的标准格式由 XML 转为了二进制的格式。

回答问题:

1. 传输的标准格式是?二进制文件

2. 怎么样将请求转化为传输的流?将二进制格式文件转化为流。

3. 怎么接收和处理流?通过监听端口获取到请求的流,转化为二进制文件,根据协议获取请求的信息,进行处理并将结果写入XML 中返回。

4. 传输协议是?http

3.4 SOAP

SOAP 原意是 Simple Object Access Protocol,是一个用于分布式环境的、轻量级的、基于 XML 进行信息交换的通信协议,可以认为 SOAP 是 XML RPC 的高级版,两者的原理完全相同,都是 http+xml,不同的仅在于两者定义的 XML 规范不同,SOAP也是 Web Service 采用的服务调用协议标准。

Web Service 提供的服务是基于 web 容器的,底层使用 http 协议,类似于一个远程服务提供者,比如天气预报服务,对各地的客户端提供天气预报,是一种请求应答的机制,是跨系统跨平台的。就是通过一个 servlet,提供服务出去。

首先客户端从服务器获得 Web Service 的 WSDL,同时在客户端生成一个代理类(Proxy Class),这个代理类负责与WebService 服务器进行 Reqeust 和 Response 。当一个数据(XML 格式的)被封装成 SOAP 格式的数据流发送到服务器端的时候,就会生成一个进程对象并且把接收到这个 Request 的 SOAP 包进行解析,然后对事物进行处理,处理结束以后再对这个计算结果进行 SOAP 包装,然后把这个包作为一个 Request 发送给客户端的代理类(Proxy Class),同样地,这个代理类也对这个 SOAP 包进行解析处理,继而进行后续操作。这就是 WebService 的一个运行过程。

Web Service 大体上分为5个层次:

1. Http 传输信道

2. XML 的数据格式

3. SOAP 封装格式

4. WSDL 的描述方式

5. UDDI UDDI 是一种目录服务,企业可以使用它对 WebService 进行注册和搜索。

3.5 JMS

JMS 是实现 java 领域远程通信的一种手段和方法,基于 JMS 实现远程通信时和 RPC 是不同的,虽然可以做到 RPC 的效果,但因为不是从协议级别定义的,因此我们不认为 JMS 是个 RPC 协议,但它确实是个远程通信协议,在其他的语言体系中也存在这类似 JMS 的东西,可以统一的将这类机制称为消息机制,而消息机制呢,通常是高宁发、分布式领域推荐的一种通信机制,这里主要一个问题是容错。

JMS 是 Java 的消息服务,JMS 的客户端之间可以通过 JMS 服务进行异步的消息传输。JMS 支持两种消息模型:Point-to-Point(p2p)和 Publish/Subscribe(Pub/Sub),即点对点和发布订阅模型。

来看 JMS 中的一次远程通信过程:

1. 客户端将请求转化为符合 JMS 规定的 Message;

2. 通过 JMS API 将 Message 放入 JMS Queue 或 Topic 中;

3. 如为 JMS Queue,则发送中相应的目标 Queue 中,如为 Topic,则发送给订阅了此 Topic 的 JMS Queue。

4. 处理端则通过轮询 JMS Queue,来获取消息,接收到消息后根据 JMS 协议来解析 Message 并处理。

同样来回答问题:

1. 传输的标准格式是?JMS 规定的 Message

2. 怎么样将请求转化为传输的流?将参数信息放入 Message 中即可。

3. 怎么接收和处理流?轮询 JMS Queue 来接收 Message,接收到后进行处理,处理完毕后仍然是以 Message 的方式放入Queue 中发送或 Multicast。

4. 传输协议是?不限。

基于 JMS 也是常用的实现远程异步调用的方法之一。

4. 之间的区别

4.1 RPC 和 RMI

1. RPC 跨语言,而 RMI 只支持 java 。

2. RMI 调用远程对象方法,允许方法返回 java 对象以及基本的数据类型,而 RPC 不支持对象的概念,传送到 RPC 服务的消息由外部数据表示(External Data Representation,XDR)语言表示,这种语言抽象了字节序类和数据类型结构之间的差异。只有由 XDR 定义的数据类型才能被传递,可以说 RMI 是面向对象方式的 Java RPC。

3. 在方法调用上,RMI 中,远程接口使每个远程方法都具有方法签名。如果一个方法在服务器上执行,但是没有相匹配的签名被添加到这个远程接口上,那么这个新方法就不能被 RMI 客户方所调用。在 RPC 中,当一个请求到达 RPC 服务器时,这个请求就包含了一个参数集合一个文本值,通常形成“classname.methodname”的形式。这就向 RPC 服务器表明,被请求的方法在为“classname”的类中,名叫“methodname”。然后,RPC 服务器就去搜索与之相匹配的类和方法,并把它作为那种方法参数类型的输入。这里的参数类型是与 RPC 请求中的类型是匹配的。一旦匹配成功,这个方法就被调用了,其结果被编码后返回给客户方。

4. RPC 本身没有规范,但基本的工作机制是一样的,即:serialization/deserialization+stub+skeleton,宽泛地讲,只要能实现远程调用,都是 RPC。

5. 在 Java 里提供了完整的 sockets 通讯接口,但 sockets 要求客户端和服务器端必须进行应用级协议的编码交换数据,采用sockets 是非常麻烦的。一个代替 sockerts 的协议是 RPC(Remote Procedure Call),他抽象出了通讯接口用于过程调用,使得编程者调用一个远程过程和调用本地过程同样方便。RPC 系统采用 XDR 来编码远程调用的参数和返回值。但 RPC 并不支持对象,所以,面向对象的远程调用 RMI 成为必然选择。采用 RMI,调用远程对象和调用本地对象同样方便。RMI 采用 JRMP(Java Remote Method Protocol)通讯协议,是构建在 TCP/IP 协议上的一种远程调用方法。 

4.2 JMS 与 RMI

1. 采用 JMS 服务,对象是在物理上被异步从网络的某个 JVM 上直接移动到另一个 JVM 上(是消息通知机制),而 RMI 对象是绑定在本地 JVM 中,只有函数参数和返回值是通过网络传送的(是请求应答机制)。

2. RMI 一般都是同步的,也就是说,当 client 调用 Server 的一个方法的时候,需要等到对象的返回,才能继续执行 client 端,这个过程调用本地方法感觉上是一样的,这也是 RMI 的一个特点。JMS 一般只是一个点发出一个Message 到 Message Server,发出之后一般不关心谁用了这个 message。所以,一般 RMI 的应用是紧耦合,JMS 的应用相对来说是松散耦合应用。

4.3 WebService 与 RMI

RMI 是在 tcp 协议上传递可序列化的 java 对象,只能用在 java 虚拟机上,绑定语言,客户端和服务端都必须是 java。WebService 没有这个限制,WebService 是在 http 协议上传递 xml 文本文件,与语言和平台无关。

4.4 WebService 与 JMS

WebService 专注于远程服务调用,jms 专注于信息交换。

大多数情况下 WebService 是两系统间的直接交互(Consumer Producer),而大多数情况下 jms 是三方系统交互。当然,JMS 也可以实现 reqeust-response 模式的通信,只要 Consumer 或 Producer 其中一方兼任 broker 即可。

JMS 可以做到异步调用完全隔离了客户端和服务提供者,能够抵御流量洪峰;WebService 服务通常为同步调用,需要有复杂的对象转换,相比 SOAP,现在 JSON,rest 都是很好的 http 架构方案;

JMS 是 java 平台上的消息规范。一般 JMS 消息不是一个 xml,而是一个 java 对象,很明显,jms 没考虑异构系统,说白了,JMS 就没考虑非 java 的东西。

5. 可选实现技术

目前 java 领域可用于实现远程通讯的框架,知名的有:JBoss-Remoting、Spring-Remoting、Hession、Burlap、XFire(Axis)、ActiveMQ、Mina、Mule、EJB3 等。

我们在学习这些远程通讯的框架,应该带着什么问题去学习呢?

1. 是基于什么协议实现的?

2. 怎么发起 请求?

3. 怎么将请求转化为符合协议的格式的?

4. 使用什么传输协议传输?

5. 响应端基于什么机制来接收请求?

6. 怎么将流还原为传输格式的?

7. 处理完毕后怎么回应?

参考:https://blog.csdn.net/u014490157/article/details/51726663

猜你喜欢

转载自blog.csdn.net/weixin_42294335/article/details/80860150
今日推荐