服务框架的几种服务调用形式

1. 同步调用

同步服务调用是最经常使用的一种服务调用方式,它的工做原理和使用都很是简单,分布式服务框架默认都须要支持这种调用形式。缓存

它的工做原理以下:客户端发起远程服务调用请求,用户线程完成消息序列化以后,将消息投递到通讯框架,而后同步阻塞,等待通讯线程发送请求并接收到应答以后,唤醒同步等待的用户线程,用户线程获取到应答以后返回。服务器

它的工做原理图如图:网络

  1. 消费者调用服务端发布的接口,接口调用由分布式服务框架包装成动态代理,发起远程服务调用。
  2. 消费者线程调用通讯框架的消息发送接口以后,直接或者间接调用 wait()方法,同步阻塞等待应答。
  3. 通讯框架的 I/O线程经过网络将请求消息发送给服务端。
  4. 服务端返回应答消息给消费者,由通讯框架负责应答消息的反序列化。 
  5. I/O线程获取到应答消息以后,根据消息上下文找到以前同步阻塞的业务线程, notify()阻塞的业务线程,返回应答给消费者,完成服务调用。

为了防止服务端长时间不返回应答消息致使客户端用户线程被挂死,用户线程等待的时候须要设置超时时间,这个超时时间与服务端或者客户端配置的超时时间对应。框架

2. 异步服务调用

基于 JDK的 Future机制,能够很是方便地实现异步服务调用。 在实际项目中,每每会扩展 JDK的 Future,提供 Future-Listener机制,它支持主动获取和被动异步回调通知两种模式,适用于不一样的业务场景。
在实际项目中,每每会扩展 JDK的 Future,提供 Future-Listener机制,它支持主动获取和被动异步回调通知两种模式,适用于不一样的业务场景。异步

2.1 添加Listener的异步服务调用

异步服务调用的工做原理如图 :分布式

                

异步服务调用的工做流程以下:
1)消费者调用服务端发布的接口,接口调用由分布式服务框架包装成动态代理,发起远程服务调用。
2)通讯框架异步发送请求消息,若是没有发生 I/O异常,返回。
3)请求消息发送成功后, I/O线程构造 Future对象,设置到 RPC上下文中。
4)用户线程经过 RPC上下文获取 Future对象。
5)构造 Listener对象,将其添加到 Future中,用于服务端应答异步回调通知。
6)用户线程返回,不阻塞等待应答。
7)服务端返回应答消息,通讯框架负责反序列化等。性能

8)I/O线程将应答设置到 Future对象的操做结果中。
9)Future对象扫描注册的监听器列表,循环调用监听器的operationComplete方法,将结果通知给监听器,监听器获取到结果以后,继续后续业务逻辑的执行,异步服务调用结束。测试

2.2 无Listener的异步服务调用

另一种异步服务调用形式,就是不添加 Listener,用户连续发起 N次服务调用,而后依次从 RPC上下文中获取 Future对象,最终再主动 get结果,业务线程阻塞,相比于老的同步服务调用,它的阻塞时间更短,其工做原理如图。
                优化

2.3 异步服务调用的优势

  1. 化串行为并行,提高服务调用效率,减小业务线程阻塞时间。
  2. 化同步为异步,避免业务线程阻塞。

串行到并行的优化原理如图:因为每次服务调用都是同步阻塞,三个服务调用总耗时为T = T1 + T2 + T3spa

            

采用异步服务调用以后的优化效果,如图:
                

采用异步服务调用模式,最后调用三个服务异步操做结果 Future的 get方法同步等待应答,它的总执行时间 T = Max(T1, T2, T3),相比于同步服务调用,性能提高效果很是明显。

3. 并行服务调用

串行服务调用比较简单,但在一些业务场景中,须要采用并行服务调用来下降 E2E的时延:

  1. 多个服务之间逻辑上不存在互相依赖关系,执行前后顺序没有严格的要求,逻辑上能够被并行执行。
  2. 长流程业务,调用多个服务,对时延比较敏感,其中有部分服务逻辑上无上下文关联,能够被并行调用。

并行服务调用的目标主要有两个:
1)下降业务 E2E时延。
2)提高整个系统的吞吐量。咱们以手游购买道具流程为例,对并行服务调用进行说明,如图。

            

实现并行服务调用的几种技术方案:

  1. JDK 7的 Fork/Join,能够实现子任务的并行执行和结果汇聚(推荐)
  2. BPM的 Parallel Gateway
  3. 批量串行服务调用

关于批量串行服务调用的流程以下:

               

1)服务框架提供批量服务调用接口供消费者使用,它的定义样例以下:

2)平台的并行服务调用器建立并行 Future,缓存批量服务调用上下文信息。
3)并行服务调用器循环调用普通的 Invoker,经过循环的方式执行单个服务调用,获取到单个服务的 Future以后设置到 Parallel Future中。
4)返回 Parallel Future给消费者。
5)普通 Invoker调用通讯框架的消息发送接口,发起远程服务调用。
6)服务端返回应答,通讯框架对报文作反序列化,转换成业务对象更新 Parallel Future的结果列表。
7)消费者调用 Parallel Future的 get(timeout)方法, 同步阻塞,等待全部结果所有返回。
8) Parallel Future经过对结果集进行判断,看全部服务调用是否都已经完成(包括成功、失败和异常)。
9)全部批量服务调用结果都已经返回, Notify消费者线程,消费者获取到结果列表,完成批量服务调用,流程继续执行。

经过批量服务调用 + Future机制,咱们实现了并行服务调用,并且没有建立新的线程,用户不用担忧依赖线程上下文的功能出异常。

4. 泛化调用

泛化调用一般包含两种模式:泛化引用和泛化实现。泛化引用主要用于客户端没有 API接口及数据模型的场景,参数及返回值中的全部 POJO均用 Map表示,一般用于框架集成,好比实现一个通用的服务测试框架。泛化实现主要用于服务器端没有 API接口及数据模型的场景,参数及返回值中的全部 POJO均用 Map表示,一般用于框架集成,好比实现一个通用的远程服务Mock框架。泛化调用的设计要点以下。
1)分布式服务框架提供泛化接口,供服务提供者实现和消费者引用,它的参考定义以下:

2)消费者若是引用泛化接口,则直接将请求参数转换成 Map,应答消息也自动转换成 Map。 3)服务提供者若是使用泛化实现发布服务,则自动将请求参数转换成 Map,调用GenService的泛化实现类,应答消息自动包装成 Map返回。 泛化调用因为比较灵活,没有服务契约,所以在实际项目中慎用,它一般用于测试集成、系统上线以后的回声测试等。

相关文章
相关标签/搜索