服务注册中心的设计与实现

14 篇文章 0 订阅
8 篇文章 0 订阅

目录

0、系统结构

1、系统设计

2、系统实现

2.1.1、命名空间

 2.1.2、服务

 2.2、服务提供方

2.3、心跳机制 

2.4、服务消费方 

2.5、服务方代理类

2.6、服务暴露

5.7、服务信息更新 

2.8、负载均衡 

2.9、SPI机制


0、系统结构

实现采用:Springboot,Maven,Vue

sunglow:注册中心实现

https://gitee.com/feiniua/sunglow

sunglow-RPC: RPC框架实现

https://gitee.com/feiniua/sunglow-rpc

sunglow-frontend:前端展示(vue)

https://gitee.com/feiniua/sunglow-frontend

sunglow-demo:一个服务端和客户端的demo

https://gitee.com/feiniua/sunglow-demo

1、系统设计

服务注册中心的主要功能,如下图 :

 

系统功能结构

系统主要分为三个模块。

  1. 注册中心模块:负责维护服务信息,提供服务注册接口,服务信息获取接口,同时根据心跳机制,与服务提供方维持心跳,从而及时检测到服务提供方是否下线,从而在注册中心中将其信息剔除。
  2. 服务提供方模块:将本地的相关服务通过端口暴露出去,可供服务消费方进行服务消费;与注册中心通过定时任务保持心跳机制,方便注册中心及时进行服务下线。
  3. 服务消费方模块:从注册中心拉取服务相关信息,从而消费服务提供者提供出来的服务;同时当注册中心服务信息有更新的时候,及时更新本地服务节点信息,重新生成代理对象使用。

 

系统设计图

本服务注册中心的主要功能包括:

  1. 注册中心端:服务注册、服务信息获取、服务下线、心跳机制。如图

 

注册中心用例图

        2.服务提供端:服务暴露、心跳机制。如图。

 

 服务提供端用例图

        3.服务消费端:服务消费、服务信息拉取。如图

 

服务消费端用例图

  1. 1、分析建模
    1. 对象模型

系统类图如图:

 

系统类图

  1. 2、动态模型

1.服务模型

服务注册的核心是服务对象和实例对象,外部对象主要是服务提供方和服务消费方。服务提供方首先注册自身服务,当没有服务时自动创建服务,再创建相关实例对象。然后服务消费方拉取服务相关信息,再进行服务消费。事件跟踪图如所示。

 

注册事件跟踪图

当服务提供方注册自身服务到注册中心后,还需要和注册中心保持心跳机制,避免自身下线时,服务中心可以及时感知,从而使服务消费方可以及时调整自身配置,不再访问到失效的服务节点。其事件跟踪图如图所示。

 

心跳事件跟踪图

当服务启动之后处于待注册状态,当服务注册后,如果注册成功,则该服务为已注册状态;如果注册失败,则变成待重连状态。在待重连状态中,会不断向注册中心进行服务注册。在已注册状态下,当服务心跳发生超时之后,会变成超时状态,然后当超过一定时候后,会对其进行服务下线操作。其服务状态图如所示。

服务状态图

2、系统实现

2.1.1、命名空间

系统初始化默认创建public命名空间,可以通过命名空间进行分组划分生成环境,如UAT(沙箱)、PRO(生产)、TEST(测试)。图所示。

命名空间页面

 命名空间新建

 2.1.2、服务

当有服务提供端启动后,会自动注册服务到注册中心,该页面便展示出了所有的服务信息,供展示查询排查问题。服务会通过分组进行归类,一个命名空间下不同分组信息的Service会相互隔离。

 Service页面

 2.2、服务提供方

服务提供方只需要引入相关sunglow-RPC-core依赖到项目中,然后添加相关注解即可自动注册服务。如图所示。

 

服务提供方启动类实现

EnableSunglowServer注解的作用是启动服务暴露机制,暴露出的应用名为studentApp,端口使用的是19999,默认扫描路径为全局扫描。

而后通过SunglowService注解标明该实例将被进行服务暴露,供其他服务消费。并在启动后注册服务成功后自动添加NodeRecord记录,记录服务节点注册信息。如图所示。

 

服务提供方提供的服务

服务启动时,还会通过引入的PRC依赖,自动往配置文件中的注册中心地址进行服务注册,同时维护一个5s的定时任务,定时向注册中心发送心跳请求,防止自身被注册中心剔除。流程如图。

服务注册流程图

2.3、心跳机制 

注册中心内部会维护一个Map,存放节点和节点最后的心跳请求时间。然后会启动一个定时任务,每过5s检查所有服务的节点的心跳请求时间判断其节点是否超时,超时时间设定为10s,删除时间设定为30s。当节点超时时,会将其节点状态标记为1(0为正常);当节点超过删除时间后,会自动将节点剔除,避免对服务消费方造成影响。

所以注册中心这个机制就要求服务提供方同样需要去维护一个定时任务,定时向注册中心发起心跳请求,更新其最后的心跳时间。如图所示。

 

图注册中心心跳机制

同时服务提供方也需要其对应的心跳机制与注册中心保持一致。服务提供方在往注册中心注册完服务之后,会将服务实例信息放置缓存中,然后启动定时任务。定时任务会有两种状态,一种是心跳任务,则遍历所有的服务实例发送心跳请求;当心跳任务执行过程中出现异常时,则设置任务为重连任务,因为大概率是注册中心下线无法连接了。重连任务则是遍历所有的实例再次进行注册,如果其过程中出现异常则继续重连,直到执行重连任务成功,再将其任务设置为心跳任务,继续执行心跳请求。如图所示。

 服务提供方心跳机制

2.4、服务消费方 

服务消费方只需要引入sunglow-RPC-core依赖,会通过SpringSPI机制去自动进行bean装配。

通过SunglowReference注解标记的字段,会自动注入代理实现Bean,而后其内部会调用远程的服务提供方中的相关实现类,获取结果后再返回。

其实现是通过SpringSPI提供的spring.factories,在其中加入AutoConfiguration的类,然后Spring容器启动时会自动装配。在这里通过自动装配机制往Spring中添加了一个BeanPostProcessor,其会遍历所有的bean,获取到带有@SunglowReference注解的字段,然后对其生成代理对象赋值。

当代理对象进行服务消费时,注册中心会记录当前服务消费记录和当前服务选择的节点消费记录,以便后续排查问题和统计访问量等。

2.5、服务方代理类

JDK动态代理,通过InvocationHandler和接口类生成新的代理对象,具体处理逻辑由InvocationHandler实现。

本系统中,会将方法调用(类名、方法名、参数等)封装成Request对象,然后通过LoadBalance去选择合适的节点,然后使用ClientExecutor去执行Request对象。

ClientExecutor是一个接口,默认实现是通过创建远程服务提供方的Socket对象,将Request对象通过序列化器转化成字节数组发送到服务提供方。当数据返回后,再讲返回的字节数组反序列化成Response对象。

然后根据返回的Response对象结果,判断是否需要熔断处理,是否直接返回结果。比如当系统连接失败之后,抛出异常,便可以基于此调用相关的RollbackFactory接口。

RollbackFactory接口返回的是一个接口实现,当需要熔断处理时,会使用返回出的接口对象去处理。

同时在ClientExecutor调用之前,会先调用filter链,可以在filter链中添加日志打印,耗时统计等操作,增加系统的扩展性。

如图所示

代理对象处理过程 

2.6、服务暴露

服务提供方会通过Exporter对外提供服务消费功能。默认实现是通过线程池加socket的方式实现的,会有一个accept线程池负责处理socket连接,然后具体的业务逻辑由work线程池去处理。

首先会将连接生成socket对象的inputStream进行解析,将字节数组转化成Request对象,然后调用ServerExecutor去处理Request对象。ServerExecutor的默认实现:从ServerServiceHolder(容器启动时,所有被注解标注的对象Bean缓存)中获取到相关服务实例对象,然后根据Request中的请求方法名、请求参数去获取到Method,而后执行该方法获取到返回值。而后将Response对象序列化后通过socket返回。流程如图所示。

服务暴露流程图 

5.7、服务信息更新 

服务消费方生成了代理对象之后,会将其注解@SunglowReference标注的字段和其代理对象保存起来。而后通过定时从注册中心拉取服务信息下来,重新生成代理类并赋值的方式去更新代理对象。对于服务消费方而言,这个过程是无感知的。

对于更新信息的操作,一般会有推和拉两种方式。拉便是定时任务进行拉取,对服务端无太高要求,但会有一定的滞后性。推则是服务端发现有更新时进行信息推送,这个方式需要服务端有较高要求,并且需要及时发现自身配置有更新,同时在推送时也给服务端造成较大影响,所以最终选择使用拉取的方式实现服务信息的更新。如图所示。

 配置更新过程

2.8、负载均衡 

服务消费方生成的代理对象时,需要传入服务的相关实例信息,然后会在JDK动态代理时,将方法调用封装成Request对象之前,通过LoadBanlace接口选择一个具体的ServerInstance,然后将实例信息配置到Request对象上。当ClientExecutor去处理Request时,自动连接到相关实例,完成软负载均衡。默认的负载均衡策略是轮询式的。

2.9、SPI机制

对于系统中比较核心的模块,将其定义成了接口,可以引入自定义实现类去替换原有核心逻辑。比如序列化器、服务提供端Request处理器、服务消费端Request处理器、负载均衡器、网络IO处理器等。

其具体实现采用Java的ServiceLoader去加载项目resoucres目录下的META-INF/services中的文件,然后将其中的服务实现类加载进来,再根据Order注解中设置的优先级顺序,去完成替换操作。如图所示。

 META-INF路径

SPI机制

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥牛火锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值