分享

【远程调用框架】如何实现一个简单的RPC框架(五)优化三:软负载中心设计与实现

 quasiceo 2018-09-06

1.前言

在博客【远程调用框架】如何实现一个简单的RPC框架(一)想法与设计中我们介绍了“服务注册查找中心”,负责服务信息的管理即服务的注册以及查找,在目前为止的实现中,我们采用web应用的方式,以http协议接口的方式为服务发布者以及调用者提供使用接口。服务发布者在发布服务的同时需要将服务的信息注册到“服务注册查找中心”,供服务调用者进行查询。但是这种方式,有一个很大的弊端就是:无法正确及时感知服务的上线与下线,并及时将服务信息的更改推送到服务调用端。
因此这里我们将“服务注册查找中心”改造成一个“软负载中心”来代替“服务注册查找中心”的职责。软负载中心主要负责服务地址等信息的聚合与管理,同时能正确进行服务上下线的感知并推送给服务的订阅者。
本博客内容部分摘自《大型网站系统与Java中间件实践》

2.主要功能与结构

2.1 软负载中心的主要功能

2.1.1 聚合地址信息

无论是服务框架中需要用到的服务提供者地址,还是消息中间件系统中的消息中间件应用的地址,都需要由软负载中心去聚合地址列表,形成一个可供服务调用者及消息的发送者、接收者直接使用的列表。

2.1.2 生命周期感知

软负载中心需要能对服务的上下线自动感知,并且根据这个变化去更新服务地址数据,想成新的地址列表后,把数据传给需要数据的调用者活着消息的发送者接收者。

2.2 软负载中心的结构

软负载中心包括两部分(1)软负载中心的服务端;(2)软负载中心的客户端

2.2.1 服务端

服务端主要负责:

  • (1)感知提供服务的机器是否在线。与服务发布者保持长连接,感知服务存活状态,并即使更新服务地址信息;
  • (2)聚合提供者的机器信息。对地址信息进行管理;
  • (3)并且负责把数据传给使用数据的应用。当某一个服务的地址信息有变化时,及时将信息推送给该服务的订阅者

2.2.2 客户端

客户端承载了两个角色,

  • (1)作为服务提供者,客户端负责与软负载中心建立长连接,把服务提供者提供服务的具体信息主动传给服务端,并且随着提供服务的变化去更新数据;
  • (2)作为服务使用者,客户端负责与软负载中心建立长连接,向服务端告知自己所需要使用的数据并负责接收服务端的推送,更新数据,还要进行本地的数据缓存,通过本地的数据缓存,使得每次去请求服务获取列表都是一个本地操作,从而提升效率和性能。

3.设计

3.1 软负载中心维护的三类重要数据

软负载中心需要管理服务地址等信息供服务调用者查询使用,同时需要保存服务的订阅关系信息,在服务地址信息发生变化时,将最新结果推送给订阅该服务的相关客户端用户,同时也要维护软负载中心与服务发布端/调用端的连接。因此,软负载中心内部有三部分重要的数据:

3.1.1 聚合数据

就是聚合后的地址信息列表。

3.1.2 订阅关系

在软负载中心,需要数据的应用(服务使用者等)把自己需要的数据信息高速软负载中心,这就是一个订阅关系,订阅的粒度和聚合数据的粒度是一致的。当聚合数据有变化时,也是通过订阅关系的数据找到需要通知的数据订阅者,然后去进行数据更新的通知。
3.1.3 连接数据
是指连接到软负载中心的节点和软负载中心已经建立的连接的管理。采用的是长连接的方式,当订阅的数据产生变化时,通过订阅关系找到需要通知的使用者id,在连接数据这里就能找到对应的连接,然后进行数据的发送,完成对应用的数据更新。

3.2 软负载中心工作模式

这里写图片描述
- (1)服务发布者需要进行服务注册时,主动与软负载中心建立socket长连接,发送服务注册信息DO,同时通过发送心跳包保持与软负载中心的长连接;
- (2)软负载中心在收到服务发布者发送的心跳包后更新该服务地址的最后更新时间,以此来判断服务是否存活,实时更新服务的地址列表;
- (3)服务调用者在进行服务调用时会查询本地缓存获取服务的地址列表,而调用者也会与软负载中心建立长连接,实时更新本地缓存;
- (4)所有聚合数据有变的情况下,都要根据订阅关系,即时讲服务的最新信息推送给该服务的订阅者

3.3 软负载中心设计

3.3.1 通信数据结构设计

  • (1)客户端向服务端发送的请求数据包DO
    数据包DO代表服务发布端以及服务调用端与软负载中心服务端长连接中传递的数据包数据结构。长连接中客户端向服务端传送的数据主要包含四种类型,服务发布者发送的注册服务信息以及为了保持长连接发送的心跳数据、服务调用端发送的服务地址查询信息以及为了保持长连接发送的心跳数据。数据结构如下图所示:
    这里写图片描述
  • (2)服务端向客户端发送的响应数据包DO
    服务端接收到客户端的请求进行相应的处理后,响应给客户端的数据结构包括:对于心跳包的响应、服务地址查询结果、服务注册结果。数据结构如下图所示:
    这里写图片描述

3.3.2 服务端维护三种数据结构设计

  • (1)聚合数据:Map类型数据结构,key为服务唯一标识,value为ServiceInfoDO数据类型;
  • (2)订阅关系:Map类型数据结构,key为服务唯一标识,value为订阅该服务的连接唯一标识列表;
  • (3)连接数据:Map类型数据结构,key为连接唯一标识,value为socket长连接对象;
  • (4)连接数据:Map类型数据结构,key为连接唯一标识,value为该连接的ObjectOutputStream;

3.3.3 客户端接口设计

  • (1)作为服务发布者:a 注册服务,与软负载中心保持长连接,发送心跳包;b 关闭连接,停止服务;
  • (2)作为服务调用者:a 订阅服务,与软负载中心保持长连接,查询服务地址,并接收软负载中心发送数据,更新本地缓存;b 查询服务信息(查询本地缓存); c 关闭连接,删除订阅关系
    本地缓存:map类型的数据结构,key为服务唯一标识,value为服务地址列表。

4.实现与使用

4.1 资源下载

软负载中心的工程代码,可访问 实现一个简单的软负载中心 进行下载;

4.2 使用示例

pom中写上对软负载中心客户端的依赖,如下:

<dependency>
    <groupId>whu.edu.lcrpc.configserver</groupId>
    <artifactId>configserver-client</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

4.2.1 服务发布

使用方式如下代码:

//软负载中心客户端的帮助类
IServiceProviderClient serviceProviderClient = new ServiceProviderClientImpl();

//定义一个服务信息的数据结构
ServiceInfoDO serviceInfoDO = new ServiceInfoDO();
serviceInfoDO.setInterfaceName("interfacename");
serviceInfoDO.setVersion("version");
serviceInfoDO.setIp("10.129.34.19");
serviceInfoDO.setImplClassName("implClassName");
//注册服务
if (serviceProviderClient.serviceRegistry(serviceInfoDO)){
    System.out.println("服务[" + serviceInfoDO.getInterfaceName() + "_" + serviceInfoDO.getVersion() + "]注册成功");
}else {
    System.out.println("服务[" + serviceInfoDO.getInterfaceName() + "_" + serviceInfoDO.getVersion() + "]注册失败");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

4.2.3 服务订阅

使用方式如下代码:

//软负载中心客户端提供的辅助类
ServiceConsumerClientImpl serviceConsumerClient = new ServiceConsumerClientImpl();

try {
    //订阅服务,服务的唯一标识为"interfacename_version"
    serviceConsumerClient.subscribeServiceInfo("interfacename_version");
    //订阅服务完成后,查询服务的地址列表
    Set<String> ips = serviceConsumerClient.queryServiceInfo("interfacename_version");
    //输出该服务的地址列表
    System.out.println("服务[" + "interfacename_version" + "]的地址列表为: " + ips);
} catch (NoServiceFoundException noServiceFound) {
    System.out.println("服务[" + "interfacename_version" + "]未找到");
    noServiceFound.printStackTrace();
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多