HSF学习

HSF(High-Speed Service Framework),是一个高性能的分布式服务框架。一个分布式框架提供的最基本的功能就是远程调用(A机器调用B机器上的一个对象的方法,B机器将方法到返回值发送回A机器)。HSF面向接口编程,以“Service”的形式进行远程调用,简单的说就是接口的具体实现(服务)运行在服务器上,服务器将这些接口暴露出来,客户端拿到这些接口之后通过接口调用具体的服务。在服务发布和调用过程中主要涉及到下面这些知识。

1.服务注册

服务注册的基本要素就是

1.1 将服务metadata发布到ConfigServer上

ConfigServer就是存放ServiceID – ip地址映射关系(多对多)的机器,服务的元信息包括在HSFSpringProviderBean中配置的信息和服务所在的ip地址。服务的发布流程非常复杂

首先在beans里面配置一个HSFSpringProviderBean以及一些属性,必要的有serviceInterface和target。在HSFSpringProviderBean里面还包装了一层 HSFApiProviderBean,HSFApiProviderBean里面包装了ServiceMetadata元数据类(关于服务的配置信息基本都在这儿)和MetadataService类,当所有配置的属性初始化完成之后,就开始调用init方法做一些发布之前的配置信息检查,再调用publish方法准备发布服务(这里面涉及到一个HSFServiceContainer单例类),通过HSFServiceContainer得到ProcessComponent的对象,调用ProcessComponent.publish()方法正真的发布服务.实际的发布过程挺复杂。

1.2. 将服务对象注册到本地Server容器,

将某一个接口的具体实现注册到本地的Server容器里面,等待客户端调用

2. 服务发现

为了调用一个HFS服务,客户端首先要从ConfigServer订阅这个服务的dataId,及时拿到服务发布方的地址列表。。ConfigServer负责管理地址,异步推送。服务提供者将服务发布到某一个机器上,并向ConfigServer报告服务所在的机器ip和服务对应的ddataId。当服务消费者需要调用服务的时候,首先向ConfigServer请求服务对应的ip,根据配置的规则(服务寻址)返回某一个ip,之后由服务消费者向对应的服务提供者ip发起TCP连接,建立通信。整个服务发现的过程就完成了

3. 远程调用

分布式框架主要特点就是远程调用,在提供强大的远程调用能力时不损失本地调用的语义简洁性. 由于HSF自身屏蔽了异步网络之间调用的差异性,同时HSF采用面向接口的实现形式,整个远程调用的关键就是拿到和服务端暴露出来的借口并编写正确清晰的业务逻辑。
调用分类

  1. Sync,这个就是一般同步调用,不适合于耗时长的服务调用。
  2. Future,这个和Java中的Future模式一样,在需要的时候通过HSFResponseFuture.getResponse(timeout)返回结果
  3. Callback,这个是属于回调类型,需要注意的是由于只用方法名字来标识方法,所以并不区分重载的方法。同名的方法都会被设置为同样的调用方式。
    回调函数是由 io 线程来调用, 所以不要在拿到结果后做费时的操作。
    不能在 onReponse 里边再发起 hsf 调用,目前这种做法可能导致 io 线程挂起,无法恢复。
  4. 泛化调用,就是完全剥离的对api的依赖,以字符串的形式提供远程调用的接口名,方法名,参数类型等

4. 动态代理

动态代理主要用于实现无侵入式的代码扩展和远程调用,动态代理在远程调用中主要是通过在InvocationHandler的invoke方法里面将需要调用的对象的名字,方法的名字,参数等信息发送给服务端,等待服务端的返回。因为HSF使用的是基于接口的编程,所以其中的代理主要使用的是jdk自带的动态代理。

5. 序列化/反序列化

序列化一般用于本地持久化存储和网络传输,在这里使用序列化主要就是将客户端和服务端需要传递到对端的对象转化成字节数组,以便于网络传输。序列化的产品比较多,HSF的序列化是通过Hessian来完成的。下面将Java自带的序列化和Hessian做一个对比:

  1. Java序列化会把要序列化的对象类的元数据和业务数据全部序列化从字节流,而且是把整个继承关系上的东西全部序列化了。它序列化出来的字节流是对那个对象结构到内容的完全描述,包含所有的信息,因此效率较低而且字节流比较大。但是由于确实是序列化了所有内容,所以可以说什么都可以传输,因此也更可用和可靠。
  2. hessian序列化,它的实现机制是着重于数据,附带简单的类型信息的方法。就像Integer a = 1,hessian会序列化成I 1这样的流,I表示int or Integer,1就是数据内容。而对于复杂对象,通过Java的反射机制,hessian把对象所有的属性当成一个Map来序列化,产生类似M className1 propertyName1 I 1 propertyName S stringValue这样的流,包含了基本的类型描述和数据内容。而在序列化过程中,如果一个对象之前出现过,hessian会直接插入一个R index这样的块来表示一个引用位置,从而省去再次序列化和反序列化的时间。Hessian序列化直接忽略了serialVersionUID,同时在遇到子类和父类中有同名变量的时候,对子类进行序列化子类的值被父类的值覆盖的问题。

6. 超时机制

超时机制是远程方法调用过程中配置调用超时的超时时间,可以在服务端和客户端同时配置,同时配置可以细化到接口和接口中的方法。clientTimeout:对接口中的所有方法生效,methodspecial,对指定的某几个方法生效。四个方法的优先级规则如下: 客户端methodspecial > 客户端clientTimeout > 服务端methodspecial > 服务端clientTimeout。超时了HSF会throw HSFTimeoutException,由业务代码catch之后处理,比如重试、fail fast等等。

7. 机房路由

机房路由主要指的是分布在所有服务器上的各个服务的调用规则,主要通过Diamond配置中心配置的

  1. 路由规则,可以通过groovy脚本指定接口路由,方法路由,参数路由。
  2. 归组规则,用于对发布了同一 HSF 服务的所有机器进行统一归组的规则 。通过xml文件配置
  3. 同机房优先规则,保证 HSF 服务消费者在请求 HSF 服务时,优先选择与服务消费者同机房的服务提供者。主要需要指定一个阈值,生效阀值的计算方法: 服务可用比例=本机房可用机器数量/所有服务机器数量 当服务可用比例 >= threshold 时,启用本地机房优先策略,当服务可用比例 < threshold 时,本地机房优先策略关闭,服务仍然采用随机调用的方式。通过xml形式配置
  4. 权重规则,如果用户不想对流量进行硬性导向,但倾向于将流量多往某几台机 器上引导,这时候就可以将这几台机器的权重规则配置大一点。某一台机器的调用概率计算公式:某一台机器的权重/所有机器的权重之和。
  5. 全局规则,配置HSF日志级别,虚机房等
  6. TPS限流,允许应用提供方指定某个接口的TPS,当单位时间内的TPS达到设定值时,该接口将停止对外提供服务,所有的请求都会被拦截(立刻返回,错误消息TPS限流),直到下一个刷新时间点。支持接口级别和方法级别。
  7. 支持应用级别,接口级别和方法级别,只有白名单中的应用才可以调用,通过Diamond推送到服务端生效。

总结

HSF作为一个高性能分布式服务框架,主要使用了ConfigServer来作为注册中心,Diamond来做软负载,通过接口变成实现动态代理。它的高性能应该主要体现在Diamond中规则的配置和ConfigServer的服务的动态注册和发现。从HSF的整体结构上来看,还涉及到了协议层,以及Netty网络流传输。
HSF整体结构
下面这几点配合链接里面流程图和源码看对于深入理解HSF很有启发,

  1. HSF服务发布流程
  2. HSF服务消费流程
  3. 服务端发布与处理请求,客户端订阅与调用流程
  4. 容器启动流程
坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章

Fork me on GitHub