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 配置 generictrue,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-2021 Shadowalker
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信