SOA解决方案——HSF(High-speed Service Framework)是阿里系主要采用的服务框架,其目的是作为桥梁联通不同的业务系统,解耦系统之间的实现依赖。
HSF简介
▐ 背景
单体应用的主要问题是不同的业务相互纠缠在一起,面对快速发展的业务,这种开发模型和架构不利于业务发展。为了解决这个问题,需要对应用进行拆分。将不同的业务分拆到多个应用中,让不同的应用分别承担不同的功能。
在单体应用时代,某个服务想要调用其他服务在本地就可以完成,而应用拆分为多个系统后,相互之间进行通信的方式就不能依赖本地,而必须要走远程,此时,一个高效、稳定的RPC框架就变得非常重要。
随着业务的不断发展,承载不同业务的应用数量以及单个应用下的服务数量都急剧膨胀,对于服务的管理变得愈发重要,在RPC框架刚开始使用的时候,可能只有几个应用,几十个服务,如果规模扩充到上万应用,几十万个服务,RPC调用反而不是重头戏,而重要的是如何能高效的组织这些服务。
一款优秀的RPC框架一般需要(或者最好能够具备)以下服务治理能力:
- 服务的方便检索,查询服务,包括服务的提供者与消费者信息
- 服务的快捷测试,能够简单、高效的进行服务测试
- 服务的路由,根据调用的服务名等运行时信息,服务消费方能够路由到对应的服务提供方指定的机器上
- 服务的归组,能够在统一机器资源维度上,让服务提供方具备服务自动归组的能力
这些特性都已经超越了一个普通RPC框架的范畴,而提供这些能力的RPC框架才能被称之为SOA(Service-Oriented Architecture)框架。
▐ 是什么
阿里SOA解决方案——HSF(High-speed Service Framework),高速服务框架。该框架是阿里系主要采用的服务框架,其目的是作为桥梁联通不同的业务系统,解耦系统之间的实现依赖。其高速体现在底层的非阻塞I/O以及优秀的序列化机制上,实现了同步和异步调用方式,并且有一套软负载体系,实现分布式应用。
▐ 特性
- 高性能的服务调用
低侵入,HSF基于Java接口完成透明的RPC调用,用户对服务是否在本地不做感知,不侵入用户代码。
高性能,HSF提供基于非阻塞I/O上的高性能调用。
多语言,多语言支持完善,提供了C++以及Node.js客户端,支持HTTP REST调用。
- 大流量的场景应对
客户端负载均衡,HSF在客户端基于服务地址列表做负载均衡,不需要借助其他负载均衡设备,高效完成负载均衡工作。
多种选址策略,HSF客户端在调用时提供了多种选址策略。
上下线策略,HSF提供了优雅上下线的能力,保证服务在重启时对客户端的影响面减到最小,客户端调用在服务端重启时表现平滑。
- 全方位的服务治理
服务管理功能,HSF运维平台提供了服务查询、测试和Mock功能,支持用户通过服务名(一般是接口名+版本号)查询服务的提供者,或者通过输入参数对已有的服务进行调用测试。
规则管理功能,HSF运维平台支持使用归组、路由以及同机房等规则对客户端发起的调用进行干预,使客户端调用变得更加智能。
▐ 基本结构
功能结构图:
HSF功能结构上分为6个部分,分别是:「服务消费方」、「服务提供方」、「地址注册中心」、「持久化配置中心」、「元数据存储中心」和「HSF运维平台」(HSF 控制台),它们组合在一起可以提供全功能的分布式服务,其中服务消费方、服务提供方和地址注册中心是必需的,上述功能结构的介绍如下表:
▐ 调用过程
作为服务消费方,客户端线程首先会将用户的参数也就是请求对象进行序列化,将序列化之后的内容放置到请求通信对象中,请求通信对象对应的是HSF协议,它包含诸如请求Id等多个与请求对象无关的内容。请求通信对象会提交给I/O线程,在I/O线程中完成编码,最终发送到服务提供方,此时客户端线程会等待结果返回,处于等待状态。
服务提供方的I/O线程接收到二进制内容,解码后生成通信请求对象并将其递交给HSF服务端线程,在HSF服务端线程完成反序列化还原成请求对象,然后发起反射调用,得到结果,也就是响应对象。响应对象会在HSF服务端线程中完成序列化,并放置到通信响应对象中。HSF服务端线程会将通信响应对象提交给I/O线程,在I/O线程中完成编码,最终发送回服务消费方。
服务消费方收到二进制内容,在I/O线程中完成解码,生成响应通信对象,并唤醒客户端线程,客户端线程会根据响应通信对象中的内容完成反序列化,最终拿到响应对象,一次远程调用结束。、
基本使用
▐ 服务接口定义
在接口定义模块中定义接口,将其打为jar包,发布到Maven仓库中。
public interface HelloWorldService { /** * 根据参数中指定的名字,生成问候语 * * @param name 被问候的姓名 * @return 问候语 */ String sayHi(String name); }
▐ 业务代码实现
业务模块依赖接口定义模块,实现接口,编写业务代码。
<!-- 依赖接口定义模块 --> <dependency> <groupId>com.alibaba.middleware</groupId> <artifactId>hsf-guide-api</artifactId> </dependency>
// 实现接口 public class HelloWorldServiceImpl implements HelloWorldService { @Override public String sayHi(String name) { // 编写业务代码 if (name == null || name.length() == 0) { return null; } return "Hi, " + name + "! Welcome to the HSF world."; } }
▐ 服务发布
- API的方式
<!-- 依赖业务模块 --> <dependency> <groupId>com.alibaba.middleware</groupId> <artifactId>hsf-guide-biz</artifactId> </dependency> <!-- 依赖HSF --> <dependency> <groupId>com.taobao.hsf</groupId> <artifactId>hsf-all</artifactId> </dependency>
在Main方法中将服务发布出去。
// [定义] 服务的实现 Object target = new HelloWorldServiceImpl(); // [设置] HSF服务发布逻辑 HSFApiProviderBean hsfApiProviderBean = new HSFApiProviderBean(); // [设置] 发布服务的接口 hsfApiProviderBean.setServiceInterface("com.alibaba.middleware.hsf.guide.api.service.HelloWorldService"); // [设置] 服务的实现对象 hsfApiProviderBean.setTarget(target); // [设置] 服务的版本 hsfApiProviderBean.setServiceVersion("1.0.0"); // [设置] 服务的归组 hsfApiProviderBean.setServiceGroup("HSF"); // [设置] 服务的响应时间 hsfApiProviderBean.setClientTimeout(3000); // [设置] 服务传输业务对象时的序列化类型 hsfApiProviderBean.setSerializeType("hessian2"); // [发布] HSF服务 hsfApiProviderBean.init();
- 注解的方式
<!-- 依赖starter --> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>pandora-hsf-spring-boot-starter</artifactId> <version>2023-04-release</version> </dependency>
将@HSFProvider
配置到业务模块的实现类上。
@HSFProvider(serviceInterface = HelloWorldService.class, serviceGroup = "HSF", serviceVersion = "1.0.0", clientTimeout = 3000, serializeType = "hessian2") public class HelloWorldServiceImpl implements HelloWorldService { @Override public String sayHi(String name) { if (name == null || name.length() == 0) { return null; } return "Hi, " + name + "! Welcome to the HSF world."; } }
▐ 服务调用
- API的方式
在Main方法中调用服务端业务代码。
HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean(); // [设置] 订阅服务的接口 hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.HelloWorldService"); // [设置] 服务的版本 hsfApiConsumerBean.setVersion("1.0.0"); // [设置] 服务的组别 hsfApiConsumerBean.setGroup("HSF"); // [订阅] HSF服务,同步等待地址推送,默认false(异步),同步默认超时时间为3000ms hsfApiConsumerBean.init(true); // [代理] 获取HSF代理 HelloWorldService helloWorldService = (HelloWorldService) hsfApiConsumerBean.getObject(); // [调用] 像调用本地接口一样,发起HSF调用 String hi = helloWorldService.sayHi("松张"); System.out.println(hi);
- 注解的方式
创建配置类,用@HSFConsumer
标记要调用的接口。
@Configuration public class HsfConfig { @HSFConsumer(serviceVersion = "1.0.0", serviceGroup = "HSF") HelloWorldService helloWorldService; }
通过注入的方式使用。
@Autowired HelloWorldService helloWorldService;
调用方式
同步调用也可以叫阻塞调用,它将阻塞当前线程,然后执行调用,调用完毕后再继续向下进行。
HSF的IO操作都是异步的,客户端同步调用的本质是做future.get(timeout)
操作,等待服务端的结果返回,这里的timeout就是客户端生效的超时时间(默认3000ms)。
HSF默认的同步调用时序图:
对于客户端来说,并不是所有的HSF服务都是需要同步等待服务端返回结果的,对于这些服务,HSF提供异步调用的形式,让客户端不必同步阻塞在HSF操作上。异步调用在发起调用时,HSF服务的调用结果都是返回类型的默认值,如返回类型是int,则会返回0,返回类型是Object,则会返回null。而真正的结果,是在HSFResponseFuture或者回调函数(callback)中获得的。
▐ Future异步调用
HSF发起调用后,用户可以在上下文中获取跟返回结果关联的HSFFuture对象,然后用户可以在任意时刻调用HSFFuture.getResponse(timeout)
获取服务端的返回结果。Future异步调用时序图:
API配置客户端Future异步调用。
HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean(); hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.HelloWorldService"); hsfApiConsumerBean.setVersion("1.0.0"); hsfApiConsumerBean.setGroup("HSF"); // [设置] 异步future调用 List<String> asyncCallMethods = new ArrayList<String>(); // [格式] name:{methodName};type:future asyncCallMethods.add("name:sayHi;type:future"); hsfApiConsumerBean.setAsyncallMethods(asyncCallMethods); hsfApiConsumerBean.init(true); HelloWorldService helloWorldService = (HelloWorldService) hsfApiConsumerBean.getObject(); String hi = helloWorldService.sayHi("松张"); // 运行后控制台打印null System.out.println(hi); // 及时在当前调用上下文中获取future对象;因为该对象是放在ThreadLocal中的,同一线程中后续调用会覆盖future对象,所以要及时取出 HSFFuture hsfFuture = HSFResponseFuture.getFuture(); // do something else try { // 这里才是真正地获取结果,如果调用还未完成,将阻塞等待结果,3000ms是等待结果的最大时间 System.out.println(hsfFuture.getResponse(3000)); } catch (Throwable e) { e.printStackTrace(); }
▐ Callback异步调用
客户端配置为Callback方式调用时,需要配置一个实现了HSFResponseCallback
接口的Listener,结果返回之后,HSF会调用HSFResponseCallback
中的onAppResponse
方法。
// 实现了HSFResponseCallback接口的Listener public class MyCallbackHandler implements HSFResponseCallback { @Override public void onAppException(Throwable t) { t.printStackTrace(); } @Override public void onAppResponse(Object o) { // 取callback调用时设置的上下文 Object context = CallbackInvocationContext.getContext(); // 打印远程调用结果 + callback调用时设置的上下文 System.out.println(o.toString() + context); } @Override public void onHSFException(HSFException e) { e.printStackTrace(); } }
Callback异步调用时序图:
API配置客户端Callback异步调用:
HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean(); hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.HelloWorldService"); hsfApiConsumerBean.setVersion("1.0.0"); hsfApiConsumerBean.setGroup("HSF"); // [设置] 异步callback调用 List<String> asyncCallMethods = new ArrayList<String>(); // [格式] name:{methodName};type:callback;listener:{listenerFullyQualifiedName} asyncCallMethods.add("name:sayHi;type:callback;listener:com.alibaba.middleware.hsf.guide.client.handler.CallbackHandler"); hsfApiConsumerBean.setAsyncallMethods(asyncCallMethods); hsfApiConsumerBean.init(true); HelloWorldService helloWorldService = (HelloWorldService) hsfApiConsumerBean.getObject(); // 可选步骤,设置上下文。CallbackHandler中通过api可以获取到 CallbackInvocationContext.setContext(" in callback"); String hi = helloWorldService.sayHi("松张"); // 运行后控制台打印null System.out.println(hi); // 清理上下文 CallbackInvocationContext.setContext(null); // do something else
高速服务框架HSF的基本原理(下):https://developer.aliyun.com/article/1480830