hsf-guide简介准备开始总体架构领域链路服务发现与扩展 准备开始 总体架构 领域 链路 服务发现与扩展

HSF(High-speed Service Framework)高速服务框架,是阿里系主要采用的服务框架,其目的是作为桥梁联通不同的业务系统,解耦系统之间的实现依赖。其高速体现在底层的非阻塞I/O以及优秀的序列化机制上。

准备开始

解决的问题

HSF框架的诞生来自于阿里系诸多系统进行服务化改造的诉求,也就是说HSF本身就是对应用服务化进行支撑的框架。

一般意义上,一个公司的业务系统发展脉络基本都是类似,从单个应用到多个应用,从本地调用到远程调用,随着发展,需要对远程服务进行高效的资源管理,这个过程中,RPC框架解决了主要的技术问题。

思考一个场景:商品信息展示页面在打开时,需要获取商品信息以及卖家(会员)信息,将二者填充到模板并进行渲染输出。

单应用

业务规模很小的时候,所有的业务系统可能就在一个应用中,通过本地调用来进行相互访问。

因为业务比较简单,商品和会员两个业务直接使用接口调用,二两者的开发都是同时进行的,最终作为一个应用发布。这种开发模型在初期效率较高,但随着业务的发展,许多问题就暴露出来了,而产生问题的根源在于分工:商品业务由自己的发展方向,会员也有自己的业务目标。

二者在一个应用中相互纠缠在一起,面对快速发展的业务,这种开发模型和架构不利于业务发展,主要体现在以下方面:

  • 技术设计, 两块业务会分裂出独立的架构师,对于整体系统的设计将会出现分化,都需要考虑对方的设计,导致架构设计不能同本身业务密切结合;
  • 实现架构,分工不同的开发团队对于开发细节和实现方式在一段时间后会有一定差别;
  • 编码风格,对于常见的问题处理,也包括编码风格在一个团队内能统一,而多个团队只能共用粗粒度的规则,相互维护修改代码成本高;
  • 发布频度,团队业务发展快慢区别导致发布的频度不一样,团队之间需要相互配合和知会;
  • 稳定性,一个团队的严重问题,将影响到另一个团队,稳定性难以提升。

分拆应用(多应用)

如果由不同的应用来分别承担上面所诉功能,例如商品应用承担商品信息的管理,会员应用承担会员核心业务的实现。将这些功能分布到不同的系统后,就可以做到技术实现的多样性以及适合性,带来发布的自由度以及系统的稳定性会极大地提升。

经过演化,一个单机系统变成了一组复杂的分布式系统,而在一个系统时,相互调用直接在本地完成,而变为多个系统时,相互之间进行通信的方式就不能依赖本地,而必须走远程,因此一个高效、稳定的RPC框架就变得非常重要。

治理应用(服务运维)

随着业务的不断发展,可以想象xxxCenter雨后春笋般出现,每个Center都有很多服务。如果把这些服务都理解为资源的话,对于资源的管理就变得愈发重要,在RPC框架刚开始使用的时候,可能只有几个应用,几十个服务,如果规模扩充到上万应用,几十万个服务,RPC调用反而不是重头戏,而重要的是如何能高效的组织这些服务。

一般来说,需要(或者最好能够具备)服务治理的能力:

  • 服务的方便检索,查询服务,包括服务的提供者与消费者信息
  • 服务的快捷测试
  • 服务的路由,根据调用的服务名等运行时信息,服务消费方能够路由到对应的服务提供方指定的机器上
  • 服务的归组,能够在统一机器资源维度上,让服务提供方具备服务自动归组的能力

围绕提升服务运维这个主题,服务治理还有更多的能力可以去设想,有更多的特性可以去做,而这些特性都已经超越了一个普通RPC框架的范畴。

这些特性才是RPC框架进化为分布式服务框架的关键。

HSF的基本结构

HSFz作为远程服务框架,具备客户端负载均衡、服务发现以及去中心化等多种分布式特性。HSF内部领域分为三个维度:规则、地址和服务,三者相互“独立”且又“关联”。

规则

规则是HSF(服务端和客户端)在运作的时候状态信息的体现,比如:服务端发布的服务在某些IP端将会以哪个分组进行发布。这些信息在应用启动时能够结合应用的特性针对应用或某个服务接口进行个性化配置,是HSF“状态”的体现,目前HSF是使用Diamond这个产品完成规则的存储与通知的。

地址

地址是调用的载体,它表示提供服务的一台机器(ip),服务端需要将地址发送到注册中心让客户端能够进行服务发现,客户端需要通过注册中心订阅一个服务的地址。服务和地址的对应关系是多对多的关系,一个服务可以由多个地址提供,一个地址可以提供多种服务。

当一个服务有了新地址(机器)或者减少了地址(机器)时,注册中心会通知这个服务的订阅方将地址增加或减少,这个注册中心就是ConfigServer,它负责存储地址信息以及地址变更的推送。

服务

服务是调用方和提供方交流的依凭,一般是一个接口,表示一个业务行为以及相关的数据含义。HSF提供了服务提供方的暴露以及调用方Client的生成。通过使用HSFApiProviderBean能够暴露一个服务,将机器的地址注册到configserver,并且能够通过12200端口进行服务提供,通过HSFApiConsumerBean能够包装出一个客户端,它是服务接口的一个代理,并且它从configserver上订阅了服务的地址列表,能够在这个列表上完成随机调用,做到负载均衡和HA。

下图展示了HSF框架的一级视图

首先看蓝色部分,这部分描述了服务发布。每个应用启动时,将会把自身地址(IP)和发布的服务名作为服务信息发布到注册中心。这样在注册中心汇集了所有的服务发布方的数据,并且注册中心提供服务检索的能力,也就是给出服务名,注册中心告知哪些机器提供了这个服务。

其次看红色部分,这部分描述了服务订阅。当客户端启动时,它向注册中心请求需要消费的服务地址,注册中心将提供了对应服务的地址(IP)列表推给客户端,此刻客户端已经准备就绪,可以发起远程调用了。

最后就是调用部分,客户端调用服务,根据注册中心推送的服务对应的地址列表,随机选择一个地址(IP),然后进行调用。

如果没有建立连接,则建立连接后进行调用,客户端与服务端的连接是长连接。一般情况下一个客户端和服务端之间只有一个长连接,因为HSF是使用非阻塞I/O进行数据传输的,I/O在普遍意义上不是瓶颈。

服务提供方和服务调用方在完成了服务发布和订阅之后,便同注册中心之间没有强依赖关系了。但是不是意味着说服务提供方和消费方与注册中心之间的连接就断开了,它们同注册中心的连接是长连接,可以想象,在注册中心一端维护着大量的连接。

注册中心、服务消费方和服务提供方构成了最基本的SOA环境,但事实上远没有那么简单:可以想象数万的消费方和提供方,它们需要强大的运维治理能力才能高效的组织在一起,比如说:服务需要进行归组,面对不同的消费方提供不同的QoS;客户端对于服务的调用需要进行路由。

特定的服务接口调用需要路由到特定的服务分组上去。因此规则中心(或叫服务治理)是对HSF服务进行管理、运维和治理的一个管理系统。

服务归组(或分组):是指服务提供方将机器的集群按照自己的管理方式分为若干个分组(或集合),这样可以将整体的服务拆散到不同的分组中,针对不同的服务消费方,让其使用特定的分组,以期望提升QoS。

本章小结

面对分布式系统构建的复杂性,一款RPC框架不仅要从高性能的通信层面考虑问题,也要从RPC的治理层面思索如何更高效的组织已有服务,让其产生更大的价值。

总体架构

二级视图

一级视图是说明软件在大图中的位置,二级视图就是展示软件具体的模块划分,下图就是HSF框架的二级视图。

可以看到HSF框架主要分为4块领域(共11层)以及在每层中的扩展点。HSF就是以围绕着扩展点来实现功能的,这些扩展点会在服务注册发现过程或者调用过程中发挥作用,用户可以通过扩展它们来完成诸如:拦截调用、监听地址变动等需求。

这4块领域从下到上,分别是:框架、应用、服务和配置。

框架提供了基础功嫩,负责通信、线程、协议、序列化以及编码相关的工作,它们提供了良好的抽象,框架之上的域只需要基于这些抽象就能完成一次高性能的调用。
应用主要面向服务框架的注册和发现过程,是HSF完成分布式调用的基础,它用来支撑服务。服务的粒度比应用小,它包含了调用链路、地址路由以及负载均衡等功能。在服务之上是配置,用户使用API来对各层进行配置,并生成调用的代理或暴露服务。

沿着客户端配置链路可以看到,用户配置了调用的接口、版本以及分组后,可以指定负载均衡策略、注册中心类型以及支持何种协议,当配置完成后,就可以生成客户端代理,开始远程调用了。服务端配置有所不同,除了配置注册中心外,还可以配置序列化方式以及线程池,这些都会影响服务端的服务能力。

服务注册发现链路比较简单,它们贯穿在协议流程中,负责注册地址或订阅服务。

调用链路从客户端发起调用开始,经历了客户端的选址和负载均衡后,将参数对象完成序列化,经过框架协议编码后,通过该网络层发送出去。服务端接受到数据后进行解码,解码获得的二进制数据派发到服务端线程完成反序列化,生成出参数对象,最终完成反射调用。

关于扩展

每一层的实现都是可以替换的,替换的关键在于每一层有自己的核心抽象。以注册中心Registry这一层为例,主要的抽象就是Registry以及围绕注册中心推送地址的地址监听器AddressListener,地址推送逻辑都是基于AddressListener来编写的。因此用户只要实现一个Registry,就完全可以替换掉这一层的实现,比如:基于consul的客户端,实现一个Registry,就可以将服务注册发现的功能切换到consul,而原有在这层中的AddressListener等扩展可以完全不受影响,Registry的上下层更不会受影响。

HSF的功能都是依托每一层中的扩展API,而不是依赖具体的实现。比如:Diamond客户端,所以当一层中的抽象适配到其他实现上时,HSF的功能就自然完成移植。

领域

HSF框架主要分成了4块领域(共11层)以及在每层中的扩展点。这4块领域从下到上,分别是:框架、应用、服务和配置。

配置领域

配置是用户对HSF框架进行配置的入口,比如常用的HSFApiConsumerBean或基于Spring的HSFSpringConsumerBean,用户使用API来对各层进行配置,并生成调用的代理或暴露服务。

框架领域

框架提供了基础功能,负责通信、线程、协议、序列化以及编解码相关的工作,它们提供了良好的抽象,框架之上的域只需要基于这些抽象就能完成一次高性能的调用。

HSF可以支持多个应用,而多个应用会共享框架提供的服务,框架这一层的目的是完成一次高效的P2P调用,在这个领域中分为以下层:

名称 功能 扩展点介绍
Packet 请求对象到通信对象的转换工作,能够将调用层的抽象转换为网络层定义的抽象,也完成反向转换的工作 PacketFactory负责实现对象转换工作,如果需要支持不同协议,就需要扩展它
Serialize 完成序列化/反序列化工作 Serializer对应一种序列化协议,比如:hessian2、fastjson等,如果要增加序列化协议,就需要扩展它
ThreadPool 线程池管理服务,维护了HSF框架对于线程资源的申请、分配以及线程指标的获取 ThreadPoolManager完成线程的创建工作,服务端线程动态的创建可以选择扩展它
Frame 完成网络层协议编解码工作 Framer负责完成不同RPC框架网络层协议的编解码工作,该扩展工作在IO线程上,因此要求其扩展不能阻塞
Stream 抽象的网络链接层,使用方法可以通过Stream来发起调用以及接受响应,开发者可以将Stream适配到不同的NIO框架上 StreamLifecyListener用于监听网络链接的建立、销毁等事件。扩展它可以知晓HSF和外部建立的链接,StreamMessageListener用于监听网络层发送以及接受的数据

框架领域内,层之间依赖关系以及基本交互关系如下图所示:

可以看到框架最主要的是提供能够支持协议扩展的,基于长连接的远程调用。

应用领域

应用主要面向服务框架的注册和发现过程,是HSF完成分布式调用的基础,它用来支撑服务。

应用领域内的组件主要用来支持服务,在这个领域中分为以下层:

名称 功能 扩展点介绍
Registry 定义了注册中心客户端的抽象,完成服务注册以及服务订阅的工作,开发者可以将Registry适配到不同的注册中心实现上 AddressListener可以监听到服务对应的地址列表,AddressListenerInterceptor完成在地址监听器收到地址前的拦截工作
Protocol 定义了协议和流程,能够识别注册中心下推地址的类型,完成发布和消费服务的工作 ProtocalInterceptor可以拦截服务在发布和订阅中的过程

应用领域中,层之间依赖关系以及基本交互关系如下图所示:

服务领域

服务的粒度比应用小,它包含了调用链路、地址路由以及负载均衡等功能,运行时调用都在这个领域展开,它们使用应用和框架提供的服务。

名称 功能 扩展点介绍
InvocationHandler 定义了调用的请求和响应,以责任链的形式实现了调用的拦截处理模式 CLientFilter拦截客户端发起的请求和收到的响应ServerFilter拦截服务端收到的请求和发送的响应
Router 定义了路由选址过程,一次调用传入Router,Router会根据请求上下文在一组地址列表中选择一堆符合要求的地址列表 AbstractMultiTargetRouter选址扩展,能够返回多个符合要求的Router,AbstractSingleTargetRouter选址扩展,返回单个符合要求的Router。
LoadBalance 定义了负载均衡的抽象,提供从一组地址中选择一个调用地址的能力 LoadBalancer负载均衡策略的抽象,比如随机选择或轮训

服务领域内,层之间的依赖关系以及基本交互关系如下图所示:

链路

链是HSF框架运行时的表现,作为使用者首先接触的是配置链路,使用者通过HSF框架提供的API来完成客户端代理的生成或服务端服务的暴露。在配置链路执行过程中会伴随着服务的发布和订阅,只有通过服务发布才能够让外部系统感知到,客户端只有通过服务订阅才能获取到提供服务的地址信息。调用链路开始于客户端通过HSF客户端(一般是服务接口的代理)发起调用,客户端的参数以及请求上下文会被放置到请求对象中发送到服务端,经过服务端处理完成后,结果被写回调用端,这样一次调用就完成了。

配置链路

配置是用户对HSF框架进行配置的入口,比如常用的HSFApiConsumerBean或基于Spring的HSFSpringConsumerBean,用户使用API来对各层进行配置,并生成调用的代理或暴露服务

服务发布/订阅链路

服务发布链路是将本机地址信息发布到注册中心Registry,注册中心会将收到的地址列表在一定的时间窗口将汇聚结果推送到客户端。发布链路基于Protocol,并通过ProtocolInterceptor的拦截,最终发布到Registry。

服务订阅链路是根据服务接口信息向Registry订阅服务地址,注册中心会将地址列表推送到客户端,客户端根据地址列表进行选址调用。

调用链路

调用链路是指客户端发起一次调用,通过网络将请求内容传递到服务端,由服务端处理完成后再将结果返回客户端的过程。调用链路是服务框架运行时的表现,它对于框架的使用者屏蔽了集群的概念,模糊了本地调用和远程调用之间的区别,将路由选址、负载均衡、序列化、协议编解码以及线程调度隐藏起来,使使用者以很低的代价能够构建起分布式服务调用

前面提到了调用链路将分布式调用中很多细节对用户做出了屏蔽,而这些屏蔽的内容就是调用链路在执行过程中需要面对的问题。

  • 调用如何进行拦截和处理?
  • 调用如何在一批地址列表上进行路由选址?
  • 调用如何在给定的地址列表上选址一个?
  • 调用如何完成序列化?
  • 序列化后的二进制内容如何被发送到服务端?
  • 服务端如何编解码调用请求?
  • 服务端如何分派线程处理请求?
  • 服务端如何反序列化请求,将其还原?
  • 服务端如何根据调用语义完成调用执行?

以上问题只是发生在客户端发出请求,抵达服务端并完成处理的过程。

以上时序是客户端发起调用需要穿过HSF的主要组件,其中InvocationHandler负责调用的拦截和处理,Router负责跟进请求上下文来进行路由选择,当选择到了一批地址后会交给LoadBalance,从中选出一个服务地址ServerURL。然后调用层根据这个ServerURL和请求上下文,使用Stream进行请求数据发送。

Stream使用Packet提供的服务进行编码,当然会先通过Serialize提供的序列化器进行序列化,最终将请求的内容二进制化,并将其交给Frame完成远程调用协议的编码,最终使用IO Channel将数据发送到服务端。

请求到达服务端后的处理过程如下图所示:

IO收到网络请求后,通过Frame进行协议解析获得RequestPacket,分派给ThreadPool中的业务线程处理请求,业务线程通过RequestPacket调用Serialize反序列化请求参数等信息,返回Invocation给调用链InvocationHandler发起调用,最终反射调用业务实例ServiceInstance的方法获得业务响应后,框架将其包装为RpcRequest后返回给客户端。

本章小结

HSF框架是按照模块来组织的,各个模块通过相互配合来完成分布式服务调用。本章给出了HSF框架的基本、架构,然后从两个维度对HSF架构做了介绍。

从领域维度上,介绍了HSF基本的结构,这是HSF概念的静态表现,讲述了框架分解的层以及每一层所负责的工作

从链路维度上,以运行时的视角观察这些领域是如何配合来解决服务发现和远程调用这两个核心问题。

服务发现与扩展