HSF 应用开发

本篇介绍 HSF 框架的开发应用。

HSF 应用开发

启动轻量级配置及注册中心

使用 Ali-Tomcat 开发应用

使用 Pandora Boot 开发应用

将应用从 HSF 架构迁移到 Dubbo(Ali-Tomcat)

一次调用过程

HSF 的一次调用过程是从服务消费方发起,经过网络抵达服务提供方,再将服务提供方的结果通过网络携带返回,最终返回给用户。该过程涉及多线程交互,同时也涉及 HSF 中的不同领域对象。

HSF 的一次调用过程如下图所示:

SAE产品HSF应用开发之一次调用过程

过程 说明
1 在客户端线程中将用户的请求参数即请求对象进行序列化,并将序列后的内容存放在请求通信的对象中。 说明 请求通信对象对应的是 HSF 协议,包括了请求 ID 等多个与请求对象无关的内容。
2 系统将请求通信对象递交给 I/O 线程,并在 I/O 线程中完成编码。
3 编码完成后,将内容传递到服务提供方的 I/O 线程。客户端线程会等待结果返回。
4 服务提供方的 I/O 线程接收到二进制内容,解码后生成通信请求对象,并将其递交给 HSF 服务端线程。
5 在 HSF 服务端线程完成反序列化还原成请求对象。
6 发起反射调用,并得到结果,即响应对象。
7 响应对象会在 HSF 服务端线程中完成序列化,并存储在通信响应对象中。
8 HSF 服务端线程将通信响应对象递交给 I/O 线程,在 I/O 线程中完成编码。
9 服务提供方将 I/O 线程中完成编码,发送回服务消费方。
10 服务消费方收到二进制内容,在 I/O 线程中完成解码,生成响应通信对象,并唤醒客户端线程。
11 客户端线程根据响应通信对象中的内容进行反序列化,用户收到响应对象,一次远程调用结束。

异步调用

前提条件

在开发应用前,您已经完成以下工作:

同步调用

HSF 的 IO 操作是异步操作,客户端同步调用的本质是执行future.get(timeout)操作,等待服务端的结果返回,这里的 timeout 就是客户端生效的超时时间 (默认 3000 ms)。同步调用时序图如下所示:

SAE服务HSF应用开发异步调用

对于客户端来说,并不是所有的 HSF 服务都是需要同步等待服务端返回结果的,对于这些服务,HSF 提供异步调用的形式,让客户端不必同步阻塞在 HSF 操作上。 异步调用在发起调用时,HSF 服务的调用结果都是返回值的默认值,例如返回类型是 int,则会返回 0,返回类型是 Object,则会返回 null。而真正的结果,是在 HSFResponseFuture 或者回调函数(callback)中获得的。

Future 异步调用

HSF 发起调用后,用户可以在上下文中获取跟返回结果关联的HSFFuture对象,获取对象后调用 HSFFuture.getResponse(timeout) 获取服务端的返回结果。Future 异步调用的时序图如下所示:

SAE应用HSF应用开发之Future异步调用

  • API 形式配置 HSF 服务

    HSF 提供了方法级别的异步调用配置,格式为name:${methodName};type:future,由于只用方法名字来标识方法,所以并不区分重载的方法。同名的方法都会被设置为同样的调用方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean();
    hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.OrderService");
    hsfApiConsumerBean.setVersion("1.0.0");
    hsfApiConsumerBean.setGroup("HSF");
    // [设置] 异步future调用
    List<String> asyncallMethods = new ArrayList<String>();
    // 格式:name:{methodName};type:future
    asyncallMethods.add("name:queryOrder;type:future");
    hsfApiConsumerBean.setAsyncallMethods(asyncallMethods);

    hsfApiConsumerBean.init(true);

    // [代理] 获取HSF代理
    OrderService orderService = (OrderService) hsfApiConsumerBean.getObject();
    // ---------------------- 调用 -----------------------//
    // [调用] 发起HSF异步调用, 返回null
    OrderModel orderModel = orderService.queryOrder(1L);
    // 及时在当前调用上下文中,获取future对象;因为该对象是放在ThreadLocal中,同一线程中后续调用会覆盖future对象,所以要及时取出
    HSFFuture hsfFuture = HSFResponseFuture.getFuture();

    // do something else

    // 这里才真正地获取结果,如果调用还未完成,将阻塞等待结果,5000ms是等待结果的最大时间
    try {
    System.out.println(hsfFuture.getResponse(5000));
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    HSF 默认的超时配置是 3000ms,如果过了超时时间,业务对象未返回,这时调用 HSFFuture.getResponse 会抛出超时异常;HSFFuture.getResponse(timeout),如果这里的 timeout 时间内,业务结果没有返回,也没有超时,可以调用多次执行 getResponse 去获取结果。

  • Spring 配置 HSF 服务

    Spring 框架在应用中广泛使用,如果不想以 API 的形式配置 HSF 服务,您可以使用 Spring XML 的形式进行配置,上述例子中 API 配置等同于如下 XML 配置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <bean id="orderService" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
    <property name="interfaceName" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
    <property name="version" value="1.0.0"/>
    <property name="group" value="HSF"/>
    <!--[设置] 订阅服务的接口 -->
    <property name="asyncallMethods">
    <list>
    <value>name:queryOrder;type:future</value>
    </list>
    </property>
    </bean>
  • 注解配置 HSF 服务

    SpringBoot 广泛使用的今天,使用注解装配 SpringBean 也成为一种选择,HSF 也支持使用注解进行配置,用来订阅服务。

    在项目中增加依赖 starter。

    1
    2
    3
    4
    <dependency>
    <groupId>com.alibaba.boot</groupId>
    <artifactId>pandora-hsf-spring-boot-starter</artifactId>
    </dependency>

    通常一个 HSF Consumer 会在多个地方使用,但并不需要在每次使用的地方都用 @HSFConsumer 来标记。只需要写一个统一个 Config 类,然后在其它需要使用的地方,直接 @Autowired 注入即可。上述例子中的 API 配置等同于如下注解配置。

    1
    2
    3
    4
    5
    @Configuration
    public class HsfConfig {
    @HSFConsumer(serviceVersion = "1.0.0", serviceGroup = "HSF", futureMethods = "sayHelloInFuture")
    OrderService orderService;
    }

    在使用时直接注入即可。

    1
    2
    @Autowired
    OrderService orderService;

Callback 异步调用

客户端配置为 callback 方式调用时,需要配置一个实现了 HSFResponseCallback 接口的 listener,结果返回之后,HSF 会调用 HSFResponseCallback 中的方法。时序图如下所示:

Callback异步调用

  • API 形式配置 HSF 服务

    callback 的调用上下文

    用户在调用前还可以通过 CallbackInvocationContext.setContext(Object obj),来设置一个关于本次调用的上下文信息,该信息存放在 threadlocal 中。在 listener 的回调函数中,可以通过 CallbackInvocationContext.getContext() 来获取该对象。

    回调函数示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class CallbackHandler implements HSFResponseCallback {

    // 业务异常时会触发
    @Override
    public void onAppException(Throwable t) {
    t.printStackTrace();
    }

    // 业务返回结果
    @Override
    public void onAppResponse(Object result) {
    // 取callback调用时设置的上下文
    Object context = CallbackInvocationContext.getContext();
    System.out.println(result.toString() + context);
    }
    //HSF异常
    @Override
    public void onHSFException(HSFException e) {
    e.printStackTrace();
    }
    }

    接口 callback 方法配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean();
    hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.OrderService");
    hsfApiConsumerBean.setVersion("1.0.0");
    hsfApiConsumerBean.setGroup("HSF");
    // [设置] 异步callback调用
    List<String> asyncallMethods = new ArrayList<String>();
    asyncallMethods.add("name:queryOrder;type:callback;listener:com.alibaba.middleware.hsf.CallbackHandler");
    hsfApiConsumerBean.setAsyncallMethods(asyncallMethods);
    hsfApiConsumerBean.init(true);
    // [代理] 获取HSF代理
    OrderService orderService = (OrderService) hsfApiConsumerBean.getObject();
    // 可选步骤,设置上下文。CallbackHandler中通过api可以获取到CallbackInvocationContext.setContext("in callback");
    // 发起调用
    orderService.queryOrder(1L); // 这里返回的其实是 null
    // 清理上下文
    CallbackInvocationContext.setContext(null);
    // do something else

    在调用线程中可以设置上下文,然后在 listener 中获取使用。相对于 Future 异步调用,callback 会立即知晓结果的返回。

  • Spring 配置 HSF 服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <bean id="CallHelloWorld" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
    <!--[设置] 订阅服务的接口 -->
    <property name="interfaceName" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
    <!--[设置] 服务的版本 -->
    <property name="version" value="1.0.0"/>
    <!--[设置] 服务的归组 -->
    <property name="group" value="HSF"/>
    <property name="asyncallMethods">
    <list>
    <!--future的含义为通过Future的方式去获取请求执行的结果,例如先调用下远程的接口,接着在同一线程继续做别的事情,然后再在同一线程中通过Future来获取结果 -->
    <!--name:methodName;type:future|callback-->
    <value>name:queryOrder;type:callback;listener:com.alibaba.middleware.hsf.CallbackHandler</value>
    </list>
    </property>
    </bean>
  • 注解配置 HSF 接口方法为 callback 调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @AsyncOn(interfaceName = OrderService.class, methodName = "queryOrder")
    public class CallbackHandler implements HSFResponseCallback {

    @Override
    public void onAppException(Throwable t) {
    t.printStackTrace();
    }

    @Override
    public void onAppResponse(Object result) {
    // 取callback调用时设置的上下文
    Object context = CallbackInvocationContext.getContext();
    System.out.println(result.toString() + context);
    }

    @Override
    public void onHSFException(HSFException e) {
    e.printStackTrace();
    }
    }

    注意 回调函数是由单独的线程池( LinkedBlockingQueue 无限队列)来调用的,不要做太费时间的操作,避免影响其他请求的 onAppResponse 回调。callback 线程默认的 corePoolSize、maxPoolSize 是实例 CPU 数目。 下面的 -D 参数可以去自定义配置。

    • CALLBACK 线程池最小配置:-Dhsf.callback.min.poolsize
    • CALLBACK 线程池最大配置:-Dhsf.callback.max.poolsize

泛化调用

相对于需要依赖业务客户端 JAR 包的正常调用,泛化调用不需要依赖二方包,使用其特定的 GenericService 接口,传入需要调用的方法名、方法签名和参数值进行调用服务。 泛化调用适用于一些网关应用(没办法依赖所有服务的二方包),其中 hsfops 服务测试也是依赖泛化调用功能。

前提条件

在开发应用前,您已经完成以下工作:

API 形式配置 HSF 服务

将 HSFConsumerBean 配置 generic 为 true,HSF 客户端将忽略加载不到接口的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean();
hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.OrderService");
hsfApiConsumerBean.setVersion("1.0.0");
hsfApiConsumerBean.setGroup("HSF");
// [设置] 泛化配置
hsfApiConsumerBean.setGeneric("true");
hsfApiConsumerBean.init(true);

// 使用泛化接口获取代理
GenericService genericOrderService = (GenericService) hsfApiConsumerBean.getObject();
// ---------------------- 调用 -----------------------//
// [调用] 发起HSF泛化调用, 返回map类型的result
Map orderModelMap = (Map) genericOrderService.$invoke("queryOrder",
// 方法入参类型数组(xxx.getClass().getName())
new String[] { Long.class.getName() },
// 参数,如果是pojo,则需要转成Map
new Object[] { 1L});

GenericService 提供的 $invoke 方法包含了真实调用的方法名、入参类型和参数,以便服务端找到该方法。由于没有依赖服务端的 API JAR 包,传入的参数如果是自定义的 DTO,需要转成客户端可以序列化的 Map 类型。

调用方法和参数说明

  • 方法没有入参,可以只传 methodName: service.$invoke("sayHello", null, null)。

  • 方法类型有泛型的,列如 List<String>,只需要传 java.util.List,即 List.class.getName() 的值,不要传成 java.util.List<String>,否则会出现方法找不到的错误。

  • 调用方法在不确定格式的情况下,可以写个单元测试,测试时依赖需要泛化调用的二方包,使用 HSF 提供的工具类 com.taobao.hsf.util.PojoUtils 的 generalize() 方法来生成一个 pojo Bean 的 Map 描述格式。

    1
    Map pojoMap = (Map) PojoUtils.generalize(new OrderModel());
  • 传递参数为 pojo 的 demo。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class User {
    private String name;
    private int age;
    // 需要是标准的pojo格式,这里省略getter setter
    }

    // 直接使用map去构造pojo对应的泛化参数
    Map param = new HashMap<String, Object>();
    param.put("age", 11);
    param.put("name","Miles");
    // 当传递的参数是声明参数类型的子类时,需要传入class字段,标明该pojo的真实类型(服务端需要有该类型)
    param.put("class", "com.taobao.User");

Spring 配置 HSF 服务

Spring 框架是在应用中广泛使用的组件,如果不想通过 API 的形式配置 HSF 服务,可以使用 Spring XML 的形式进行配置,上述例子中的 API 配置等同于如下 XML 配置。

1
2
3
4
5
6
7
8
9
<bean id="CallHelloWorld" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
<!--[设置] 订阅服务的接口 -->
<property name="interfaceName" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
<!--[设置] 服务的版本 -->
<property name="version" value="1.0.0"/>
<!--[设置] 服务的归组 -->
<property name="group" value="HSF"/>
<property name="generic" value="true"/>
</bean>

注意事项

  • 泛化调用,如果客户端没有接口类,路由规则默认不生效。

  • 泛化调用性能会比正常调用差。

  • 配置

    1
    -D hsf.generic.throw.exception=true (默认是false, 把异常泛化成map返回)

    抛出业务异常。

    本地存在异常类,如果不是 RuntimeException 类型或其子类,则会抛出 UndeclaredThrowableException,这是由于 com.taobao.hsf.remoting.service.GenericService 上没有声明该异常,可以通过 getCause 获取真实异。

    本地没有该异常类,则抛出 com.taobao.hsf.util.GenericInvocationException。

调用上下文

请求上下文包括一次调用相关的属性,例如调用的地址,调用方的应用名,超时时间等属性和用户在接口定义的参数之外传递自定义的数据。

设置和获取本次调用上下文

com.taobao.hsf.util.RequestCtxUtil 提供设置和获取调用上下文的静态方法,基于 ThreadLocal 工作, getXXX 操作会将 XXX 属性从当前 ThreadLocal 变量中 remove,仅作用于当前线程的单次调用。

具体属性的设置和获取如下:

  • 客户端

    方法 说明
    setRequestTimeout() 设置单次调用的超时时间。
    setUserId() 设置本次调用的单元化服务的 User ID(泛化调用中需要通过此方法配置)。
    getProviderIp() 获取最近一次调用的服务端的 IP。
    setTargetServerIp(String ip) 设置当前线程下一次调用的目标服务器 IP(此 IP 必须包含在内存已提供服务的地址列表里)。
    setDirectTargetServerIp(String targetIp) 设置当前线程下一次调用的目标服务器 IP(绕过注册中心,忽略内存里的地址列表)。
  • 服务端

    方法 说明
    getClientIp() 服务端获取调用方 IP。
    getAppNameOfClient() 服务端获取调用方的应用名。
    isHttpRequest() 是否是 HTTP 调用。
    getHttpHeader(String key) 获取 HTTP 请求的 Header 属性。

传递自定义请求上下文

RpcContext 提供一种不修改接口,向服务端额外传递数据的方式。参数可以是自定义的 DO 或者基本类型。要保证对端也有该对应的类型,并且可以能够被序列化。

  • 客户端发起调用前,设置上下文。

    1
    2
    3
    4
    5
    6
    //setup context before rpc call
    RPCContext rpcContext = RPCContext.getClientContext();
    rpcContext.putAttachment("tetantId", "123");

    //rpc call,context 也会传到远端
    orderService.queryOrder(1L);
  • 服务端业务方法内,获取上下文。

    1
    2
    3
    //get context data
    RPCContext rpcContext = RPCContext.getServerContext();
    String myContext = (String)rpcContext.getAttachment("tetantId");

序列化方式选择

序列化的过程是将 Java 对象转成 byte 数组在网络中传输,反序列化会将 byte 数组转成 Java 对象。

简介

序列化的选择需要考虑兼容性,性能等因素,HSF 的序列化方式支持 Java、hessian2,默认是 hessian2。序列化方式的对比和配置(只在服务端配置 HSFApiProviderBean)如下表所示。

序列化方式 Maven依赖 配置 兼容性 性能
hessian2 hsf-io-serialize-hessian2 setPreferSerializeType("hessian2") 好 好
java hsf-io-serialize-java setPreferSerializeType("java") 佳 一般

前提条件

在开发应用前,您已经完成以下工作:

API 形式配置 HSF 服务

1
2
HSFApiProviderBean hsfApiProviderBean = new HSFApiProviderBean();
hsfApiProviderBean.setPreferSerializeType("hessian2");

Spring 配置 HSF 服务

Spring 框架是在应用中广泛使用的组件,如果不想通过 API 的形式配置 HSF 服务,可以使用 Spring XML 的形式进行配置,上述例子中的 API 配置等同于如下 XML 配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean class="com.taobao.hsf.app.spring.util.HSFSpringProviderBean" init-method="init">
<!--[设置] 发布服务的接口 -->
<property name="serviceInterface" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
<!--[设置] 服务的实现对象target必须配置 [ref],为需要发布为HSF服务的spring bean id-->
<property name="target" ref="引用的BeanId"/>
<!--[设置] 服务的版本 -->
<property name="serviceVersion" value="1.0.0"/>
<!--[设置] 服务的归组 -->
<property name="serviceGroup" value="HSF"/>
<!--[设置] 服务的响应时间 -->
<property name="clientTimeout" value="3000"/>
<!--[设置] 服务传输业务对象时的序列化类型 -->
<property name="preferSerializeType" value="hessian2"/>
</bean>

超时配置

本文介绍开发 HSF 应用过程中如何进行超时配置。

前提条件

在开发应用前,您已经完成以下工作:

背景信息

有关网络调用的请求,都需要配置超时,HSF 的默认超时时间是 3000ms。客户端和服务端都可以设置超时,默认优先采用客户端的配置,如果客户端没有配置,使用服务端的超时配置。 在服务端设置超时时,需要考虑到业务本身的执行耗时,加上序列化和网络通讯的时间。所以推荐服务端给每个服务都配置个默认的时间。当然客户端也可以根据自己的业务场景配置超时时间,例如一些前端应用,需要用户快速看到结果,可以把超时时间设置小一些。

配置的作用范围、作用域,按照优先级由高到低如下表所示。

优先级 API 范围 作用域
0 com.taobao.hsf.util.RequestCtxUtil#setRequestTimeout 客户端 单次调用
1 HSFApiConsumerBean#setMethodSpecials 客户端 方法
2 HSFApiConsumerBean#setClientTimeout 客户端 接口
3 -D defaultHsfClientTimeout 客户端 所有接口
4 HSFApiProviderBean#setMethodSpecials 服务端 方法
5 HSFApiProviderBean#setClientTimeout 服务端 接口

说明 客户端配置优先于服务端,方法优先于接口。

客户端超时配置

  • API 形式配置 HSF 服务。

    配置 HSFApiConsumerBean 的 clientTimeout 属性,单位是 ms,我们把接口的超时配置为 1000ms,方法 queryOrder 配置为 100ms,代码如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    HSFApiConsumerBean consumerBean = new HSFApiConsumerBean();
    // 接口级别超时配置
    consumerBean.setClientTimeout(1000);
    //xxx
    MethodSpecial methodSpecial = new MethodSpecial();
    methodSpecial.setMethodName("queryOrder");
    // 方法级别超时配置,优先于接口超时配置
    methodSpecial.setClientTimeout(100);
    consumerBean.setMethodSpecials(new MethodSpecial[]{methodSpecial});
  • Spring 配置 HSF 服务。

    Spring 框架是在应用中广泛使用的组件,如果不想通过 API 的形式配置 HSF 服务,可以使用 Spring XML 的形式进行配置,上述例子中的 API 配置等同于如下 XML 配置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <bean id="CallHelloWorld" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
    ...
    <property name="clientTimeout" value="1000" />
    <property name="methodSpecials">
    <list>
    <bean class="com.taobao.hsf.model.metadata.MethodSpecial">
    <property name="methodName" value="queryOrder" />
    <property name="clientTimeout" value="100" />
    </bean>
    </list>
    </property>
    ...
    </bean>
  • 注解配置。

    SpringBoot 广泛使用的今天,使用注解装配 SpringBean 也成为一种选择,HSF 也支持使用注解进行配置,用来订阅服务。

    1. 在项目中增加依赖 starter。

      1
      2
      3
      4
      <dependency>
      <groupId>com.alibaba.boot</groupId>
      <artifactId>pandora-hsf-spring-boot-starter</artifactId>
      </dependency>
    2. 在代码中注解配置。

      通常一个 HSF Consumer 需要在多个地方使用,但并不需要在每次使用的地方都用 @HSFConsumer 来标记。只需要写一个统一个 Config 类,然后在其它需要使用的地方,直接 @Autowired 注入即可上述例子中的 API 配置等同于如下注解配置。

      1
      2
      @HSFConsumer(clientTimeout = 1000, methodSpecials = @HSFConsumer.ConsumerMethodSpecial(methodName = "queryOrder", clientTimeout = "100"))
      private OderService orderService;
  • 客户端全局接口超时配置。

    • 在启动参数中添加 -D defaultHsfClientTimeout=100
    • 在代码中添加 System.setProperty("defaultHsfClientTimeout", “100”)

服务端方法超时配置

  • API 配置 HSF 服务。

    配置 HSFApiProviderBean 的 clientTimeout 属性,单位是 ms,代码如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    HSFApiProviderBean providerBean = new HSFApiProviderBean();
    // 接口级别超时配置
    providerBean.setClientTimeout(1000);
    // xxx
    MethodSpecial methodSpecial = new MethodSpecial();
    methodSpecial.setMethodName("queryOrder");
    // 方法级别超时配置,优先于接口超时配置
    methodSpecial.setClientTimeout(100);
    providerBean.setMethodSpecials(new MethodSpecial[]{methodSpecial});
  • Spring 配置 HSF 服务。

    Spring 框架是在应用中广泛使用的组件,如果不想通过 API 的形式配置 HSF 服务,可以使用 Spring XML 的形式进行配置,上述例子中的 API 配置等同于如下 XML 配置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <bean class="com.taobao.hsf.app.spring.util.HSFSpringProviderBean" init-method="init">
    ...
    <property name="clientTimeout" value="1000" />
    <property name="methodSpecials">
    <list>
    <bean class="com.taobao.hsf.model.metadata.MethodSpecial">
    <property name="methodName" value="queryOrder" />
    <property name="clientTimeout" value="2000" />
    </bean>
    </list>
    </property>
    ...
    </bean>
  • 注解配置 HSF 服务。

    注入即可上述例子中的 API 配置等同于如下注解配置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @HSFProvider(serviceInterface = OrderService.class, clientTimeout = 3000)
    public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderDAO orderDAO;

    @Override
    public OrderModel queryOrder(Long id) {
    return orderDAO.queryOrder(id);
    }
    }

服务端线程池配置

本文介绍开发 HSF 应用过程中如何进行服务端线程池配置。

前提条件

在开发应用前,您已经完成以下工作:

服务线程池业务示意图

HSF 服务端线程池主要分为 IO 线程和业务线程,其中 IO 线程模型就是 netty reactor 网络模型中使用的。我们主要讨论业务线程池的配置。业务线程池分为默认业务线程池和服务线程池,其中服务线程池是从默认线程池中分割出来的。

服务线程业务示意图

默认线程池配置

服务端线程池是用来执行业务逻辑的线程池,线程池默认的 core size 是 50,max size 是 720,keepAliveTime 500s。队列使用的是 SynchronousQueue,没有缓存队列,不会堆积用户请求。 当服务端线程池所有线程(720)都在处理请求时,对于新的请求,会立即拒绝,返回 Thread pool is full 异常。可以使用下面 VM 参数(-D 参数)进行配置。

  • 线程池最小配置:-D hsf.server.min.poolsize
  • 线程池最大配置:-D hsf.server.max.poolsize
  • 线程收敛的存活时间:-D hsf.server.thread.keepalive

服务线程池配置

对于一些慢服务、并发高,可以为其单独配置线程池,以免占用过多的业务线程,影响应用的其他服务的调用。

  • API 形式配置 HSF 服务

    1
    2
    3
    4
    HSFApiProviderBean hsfApiProviderBean = new HSFApiProviderBean();
    //...
    hsfApiProviderBean.setCorePoolSize("50");
    hsfApiProviderBean.setMaxPoolSize("200");
  • Spring 配置 HSF 服务

    Spring 框架是在应用中广泛使用的组件,如果不想通过 API 的形式配置 HSF 服务,可以使用 Spring XML 的形式进行配置,上述例子中的 API 配置等同于如下 XML 配置。

    1
    2
    3
    4
    5
    6
    <bean class="com.taobao.hsf.app.spring.util.HSFSpringProviderBean" init-method="init">
    <!--[设置] 发布服务的接口 -->
    <property name="serviceInterface" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
    <property name="corePoolSize" value="50" />
    <property name="maxPoolSize" value="200" />
    </bean>
  • 注解配置 HSF 服务

    SpringBoot 广泛使用的今天,使用注解装配 SpringBean 也成为一种选择,HSF 也支持使用注解进行配置,用来发布服务。

    1. 在项目中增加依赖 starter。

      1
      2
      3
      4
      <dependency>
      <groupId>com.alibaba.boot</groupId>
      <artifactId>pandora-hsf-spring-boot-starter</artifactId>
      </dependency>
    2. 将 @HSFProvider 配置到实现的类型。

      上述例子中的 API 配置等同于如下注解配置。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      @HSFProvider(serviceInterface = OrderService.class, corePoolSize = 50, maxPoolSize = 200)
      public class OrderServiceImpl implements OrderService {
      @Autowired
      private OrderDAO orderDAO;

      @Override
      public OrderModel queryOrder(Long id) {
      return orderDAO.queryOrder(id);
      }
      }

API 手册

在 HSF 应用的 API 中,最关键的是创建 ProviderBean 和 ConsumerBean 相关的 API。

背景信息

根据用户使用的场景不同,主要分为4个关键的类。

  • com.taobao.hsf.app.api.util.HSFApiProviderBean: 通过 API 编程的方式创建 Provider Bean。
  • com.taobao.hsf.app.api.util.HSFApiConsumerBean: 通过 API 编程的方式创建 Consumer Bean。
  • com.taobao.hsf.app.spring.util.HSFSpringProviderBean: 通过 Spring 配置的方式创建 Provider Bean。
  • com.taobao.hsf.app.spring.util.HSFSpringConsumerBean: 通过 Spring 配置的方式创建 Consumer Bean。

其中,HSFSpringXxxBean 的配置属性与 HSFApiXxxBean 的 setter 方法相对应。接下来就分别以 ProviderBean 和 ConsumerBean 的视角介绍这 4 个类的 API。

ProviderBean

  • API 编程方式 - HSFApiProviderBean

    通过配置并初始化 com.taobao.hsf.app.api.util.HSFApiProviderBean 即可完成 HSF 服务的发布。

    对于一个服务,com.taobao.hsf.app.api.util.HSFApiProviderBean 的配置及初始化过程只需配置一次。此外,考虑到 HSFApiProviderBean 对象比较复杂,建议缓存。

    • 示例代码:

      1
      2
      3
      4
      5
      6
      7
      8
      // 实例化并配置Provider Bean
      HSFApiProviderBean hsfApiProviderBean = new HSFApiProviderBean();
      hsfApiProviderBean.setServiceInterface("com.taobao.hsf.test.HelloWorldService");
      hsfApiProviderBean.setTarget(target); // target 为 serviceInterface 指定接口的实现对象hsfApiProviderBean.setServiceVersion("1.0.0");
      hsfApiProviderBean.setServiceGroup("HSF");

      // 初始化 Provider Bean,发布服务
      hsfApiProviderBean.init();
    • 可配置属性表:

      除上述示例代码中设置的属性,HSFApiProviderBean 还包含许多其他可配置的属性,均可通过对应的 setter 方法进行设置。

      属性名 类型 是否必选 默认值 含义
      serviceInterface String 是 无 设置 HSF 服务对外提供的业务接口。客户端通过此属性进行订阅。
      target Object 是 无 设置 serviceInterface 指定接口的服务实现对象。
      serviceVersion String 否 1.0.0 设置服务的版本号。客户端通过此属性进行订阅。
      serviceGroup String 否 HSF 设置服务的组别。客户端通过此属性进行订阅。
      serviceDesc String 否 null 设置服务的描述,从而方便管理。
      clientTimeout int 否 3000 设置响应超时时间(单位:毫秒)。如果服务端在设置的时间内没有返回,则抛出 HSFTimeOutException。
      methodSpecials MethodSpecial[] 否 空 设置服务中某些方法的响应超时时间。通过设置 MethodSpecial.methodName 指定方法名,通过设置 MethodSpecial.clientTimout 指定当前方法的超时时间,优先级高于当前服务端的 clientTimeout。
      preferSerializeType String 否 hessian2 针对 HSF2,设置服务的请求参数和响应结果的序列化方式。可选值有 Java、hessian、hessian2、json 和 kryo。
      corePoolSize 值为整型的String 否 0 配置服务单独的线程池,并指定最小活跃线程数量。若不设置该属性,则默认使用 HSF 服务端的公共线程池。
      maxPoolSize 值为整型的String 否 0 配置服务单独的线程池,并指定最大活跃线程数量。若不设置该属性,则默认使用 HSF 服务端的公共线程池。
  • Spring 配置方式 - HSFSpringProviderBean

    通过在 Spring 配置文件中,配置一个 class 为 com.taobao.hsf.app.spring.util.HSFSpringProviderBean 的 bean,即可完成 HSF 服务的发布。

    示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    <bean id="helloWorldService" class="com.taobao.hsf.test.HelloWorldService" />

    <bean class="com.taobao.hsf.app.spring.util.HSFSpringProviderBean" init-method="init">
    <!-- [必选] 设置HSF服务对外提供的业务接口 -->
    <property name="serviceInterface" value="com.taobao.hsf.test.HelloWorldService" />

    <!-- [必选] 设置serviceInterface指定接口的服务实现对象,即需要发布为HSF服务的spring bean id -->
    <property name="target" ref="helloWorldService" />

    <!-- [可选] 设置服务的版本号,默认为1.0.0 -->
    <property name="serviceVersion" value="1.0.0" />

    <!-- [可选] 设置服务的组别,默认为HSF -->
    <property name="serviceGroup" value="HSF" />

    <!-- [可选] 设置服务的描述,从而方便管理,默认为null -->
    <property name="serviceDesc" value="HelloWorldService providered by HSF" />

    <!-- [可选] 设置响应超时时间(单位:毫秒)。如果服务端在设置的时间内没有返回,则抛出HSFTimeOutException -->
    <!-- 默认为3000 ms -->
    <property name="clientTimeout" value="3000"/>

    <!-- [可选] 设置服务中某些方法的响应超时时间。优先级高于上面的clientTimeout -->
    <!-- 通过设置MethodSpecial.methodName指定方法名,MethodSpecial.clientTimout指定方法的超时时间 -->
    <property name="methodSpecials">
    <list>
    <bean class="com.taobao.hsf.model.metadata.MethodSpecial">
    <property name="methodName" value="sum" />
    <property name="clientTimeout" value="2000" />
    </bean>
    </list>
    </property>

    <!-- [可选] 设置服务的请求参数和响应结果的序列化方式。可选值包含java、hessian、hessian2、json和kryo -->
    <!-- 默认为hessian2 -->
    <property name="preferSerializeType" value="hessian2"/>

    <!-- [可选] 配置服务单独的线程池,并指定最小活跃线程数量。若不设置该属性,则默认使用HSF服务端的公共线程池 -->
    <property name="corePoolSize" value="10"/>

    <!-- [可选] 配置服务单独的线程池,并指定最大活跃线程数量。若不设置该属性,则默认使用HSF服务端的公共线程池 -->
    <property name="maxPoolSize" value="60"/>
    </bean>

ConsumerBean

  • API 配置方式 - HSFApiConsumerBean

    通过配置并初始化 com.taobao.hsf.app.api.util.HSFApiConsumerBean 完成 HSF 服务的订阅。

    对于同一个服务,com.taobao.hsf.app.api.util.HSFApiConsumerBean 的配置及初始化过程只需配置一次。

    由于 HSFApiConsumerBean 对象内容多,建议将该对象以及获取到的 HSF 代理进行缓存。

    说明 在 HSF 内部 HSFApiConsumerBean 对服务的配置是缓存起来的。即如果对某个订阅的服务进行了多次配置,只有第一次的配置生效。

    • 示例代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      // 实例化并配置 Consumer Bean
      HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean();
      hsfApiConsumerBean.setInterfaceName("com.taobao.hsf.test.HelloWorldService");
      hsfApiConsumerBean.setVersion("1.0.0");
      hsfApiConsumerBean.setGroup("HSF");

      // 初始化 Consumer Bean,订阅服务
      // true表示等待地址推送(超时时间为3000毫秒),默认为false(异步)
      hsfApiConsumerBean.init(true);

      // 获取 HSF 代理
      HelloWorldService helloWorldService = (HelloWorldService) hsfApiConsumerBean.getObject();

      // 发起 HSF 调用
      String helloStr = helloWorldService.sayHello("Li Lei");
    • 可配置属性表

      除上述示例代码中设置的属性,HSFApiConsumerBean 还包含许多其他可配置的属性,均可通过对应的 setter 方法进行设置。

      属性名 类型 是否必选 默认值 含义
      interfaceName String 是 无 设置需要订阅服务的接口名。客户端通过此属性进行订阅。
      version String 是 无 设置需要订阅服务的版本号。客户端通过此属性进行订阅。
      group String 是 无 设置需要订阅服务的组别。客户端通过此属性进行订阅。
      clientTimeout int 否 无 设置请求超时时间(单位:毫秒)。如果客户端在设置的时间内没有收到服务端响应,则抛出 HSFTimeOutException。 若客户端设置了clientTimeout,则优先级高于服务端设置的 clientTimeout。否则,在服务的远程调用过程中,使用服务端设置的 clientTimeout。
      methodSpecials MethodSpecial[] 否 空 设置服务中某些方法的请求超时时间。通过设置 MethodSpecial.methodName 指定方法名,通过设置 MethodSpecial.clientTimout 指定当前方法的超时时间,优先级高于当前客户端的 clientTimeout。
      maxWaitTimeForCsAddress int 否 无 设置同步等待 ConfigServer 推送地址的时间(单位:毫秒),从而避免因地址还未推送到就发起服务调用造成的 HSFAddressNotFoundException。一般建议设置为 5000 毫秒,即可满足推送等待时间。
      asyncallMethods List 否 null 设置需要异步调用的方法列表。List中的每一个字符串的格式为:name:方法名 type:异步调用类型 listener:监听器其中 listener 只对 callback 类型的异步调用生效。type 的类型有:future:通过 Future 的方式去获取请求执行的结果。callback:当远程服务的调用完成后,HSF 会使用响应结果回调此处配置的 listener,该 listener 需要实现 HSFResponseCallback 接口。
      proxyStyle String 否 jdk 设置服务的代理模式,一般不用配置。如果要拦截这个 consumer bean,需要配置成 javassist。
  • Spring 配置方式 - HSFSpringConsumerBean

    通过在 Spring 配置文件中,配置一个 class为com.taobao.hsf.app.api.util.HSFSpringConsumerBean 的 bean,即可实现服务的订阅。

    示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    <bean id="helloWorldService" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
    <!-- [必选] 设置需要订阅服务的接口名 -->
    <property name="interfaceName" value="com.taobao.hsf.test.HelloWorldService" />

    <!-- [必选] 设置需要订阅服务的版本号 -->
    <property name="version" value="1.0.0" />

    <!-- [必选] 设置需要订阅服务的组别 -->
    <property name="group" value="HSF" />

    <!-- [可选] 设置请求超时时间(单位:毫秒)。如果客户端在设置的时间内没有收到服务端响应,则抛出HSFTimeOutException -->
    <!-- 若客户端设置了clientTimeout,则优先级高于服务端设置的clientTimeout。否则,在服务的远程调用过程中,使用服务端设置的clientTimeout。-->
    <property name="clientTimeout" value="3000" />

    <!-- [可选] 设置服务中某些方法的请求超时时间,优先级高于当前客户端的clientTimeout -->
    <!-- 通过设置MethodSpecial.methodName指定方法名,通过设置MethodSpecial.clientTimout指定当前方法的超时时间 -->
    <property name="methodSpecials">
    <list>
    <bean class="com.taobao.hsf.model.metadata.MethodSpecial">
    <property name="methodName" value="sum" />
    <property name="clientTimeout" value="2000" />
    </bean>
    </list>
    </property>

    <!-- [可选] 设置同步等待ConfigServer推送地址的时间(单位:毫秒)-->
    <!-- 从而避免因地址还未推送到就发起服务调用造成的HSFAddressNotFoundException -->
    <!-- 一般建议设置为5000毫秒,即可满足推送等待时间 -->
    <property name="maxWaitTimeForCsAddress" value="5000"/>

    <!-- [可选] 设置需要异步调用的方法列表。List中的每一个字符串的格式为:-->
    <!-- name:方法名 -->
    <!-- type:异步调用类型 -->
    <!-- listener:监听器 -->
    <!-- 其中,listener只对callback类型的异步调用生效-->
    <!-- type的类型有: -->
    <!-- future: 通过Future的方式去获取请求执行的结果 -->
    <!-- callback: 当远程服务的调用完成后,HSF会使用响应结果回调此处配置的listener,该listener需要实现HSFResponseCallback接口 -->
    <property name="asyncallMethods">
    <list>
    <value>name:sayHello;type:callback;listener:com.taobao.hsf.test.service.HelloWorldServiceCallbackHandler</value>
    </list>
    </property>

    <!-- [可选] 设置服务的代理模式,一般不用配置。如果要拦截这个consumer bean,需要配置成javassist -->
    <property name="proxyStyle" value="jdk" />
    </bean>

JVM -D 启动配置参数

本文介绍 HSF 应用开发时 JVM -D 启动参数的配置信息。

-D hsf.server.port

指定 HSF 的启动服务绑定端口,默认为 12200。如果在本地启动多个 HSF Provider,则需要修改此端口。

-D hsf.server.max.poolsize

指定 HSF 的服务端最大线程池大小,默认值为720。

-D hsf.server.min.poolsize

指定 HSF 的服务端最小线程池大小,默认值为50。

-D hsf.client.localcall

打开或者关闭本地优先调用,默认值为true。

-D pandora.qos.port

指定 Pandora 监控端口,默认值为12201。如果在本地启动多个 HSF Provider,则需要修改此端口。

-D hsf.http.enable

是否开启 HTTP 端口,默认为 true。

-D hsf.http.port

指定 HSF 暴露的 HTTP 接口,默认值为12220。如果在本地启动多个 HSF Provider,则需要修改此端口。

-D hsf.run.mode

指定 HSF 客户端是否指定 target 进行调用,即绕开 ConfigServer。值为1,表示不允许指定 target 调用;值为0,表示允许指定 target 调用。默认值为1时,不推荐指定为0。

-D hsf.shuthook.wait

HSF 优雅关闭的等待时间,单位是 ms,默认是10000。

-D hsf.publish.delayed

是否所有的服务都需要延迟发布,默认是false,不需要延迟发布 。

-D hsf.server.ip

指定需要绑定的 IP 地址。在多网卡情况下默认绑定第一个网卡,通过该参数指定需要绑定的 IP。

-D HsfBindHost

指定需要绑定的 Host。在多网卡情况下默认绑定和上报给地址注册中心第一个网卡的IP地址,通过该参数可以指定需要绑定的 Host,列如-DHsfBindHost=0.0.0.0将 HSF Server 端口绑定本机所有网卡。

-D hsf.publish.interval=400

指定发布服务之间的时间间隔。HSF 服务发布时会瞬间暴露出去,在应用启动时如果承受不住压力,可以配置该参数。默认值是 400,单位 ms。

-D hsf.client.low.water.mark=32 -D hsf.client.high.water.mark=64 -D hsf.server.low.water.mark=32 -D hsf.server.high.water.mark=64

指定客户端或者服务端的每个 channel 写缓冲的限制。

  • 客户端每个 channel 的写缓冲的限制,单位为 KB,一旦超过高水位,channel 禁写,新的请求放弃写出,直接报错。禁写之后,等到缓冲区低于低水位才能恢复。
  • 服务端每个 channel 的写缓冲的限制,单位为 KB,超过高水位时,新的响应放弃写出,客户端收不到响应会超时。缓冲区低于低水位时才能恢复写。
  • 高低水位需成对设置,并且需要高水位大于低水位。

-D hsf.generic.remove.class=true

获取泛化调用的结果,但不输出class字段信息。

-D defaultHsfClientTimeout

全局的客户端超时配置。

-D hsf.invocation.timeout.sensitive

hsf.invocation.timeout.sensitive 默认值设置为 false,决定 HSF 调用时间是否包含创建连接、选址等耗时逻辑。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2017-2026 Shadowalker
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信