This topic describes how to perform asynchronous calls in High-speed Service Framework (HSF).

Prerequisites

Synchronous calls

I/O operations in HSF are asynchronous operations. In synchronous calls, your consumer performs the future.get(timeout) operation and waits for the response from the provider. timeout specifies the time-out period of the consumer. The default time-out period is 3,000 ms.

Your consumer does not need to call all the HSF services in a synchronous manner. For some HSF services, the consumer does not need to wait for the response from the provider. For these services, HSF provides an asynchronous method so that the consumer services are not blocked due to synchronous calls in HSF. In an asynchronous call, default values are always returned for all the calls for HSF services. For example, if the data type of the return value is INT, 0 is returned. If the data type of the return value is OBJECT, null is returned. To obtain the actual results, call the HSFResponseFuture interface or invoke the callback function.

Use the Future interface for asynchronous calls

After you initiate an HSF call, you can obtain the HSFFuture object that is associated with the returned result from the context. Then, call HSFFuture.getResponse(timeout) to obtain the result that is returned from the provider.

  • Use APIs to configure HSF services

    HSF allows you to call methods to configure asynchronous calls in the format of name:${methodName};type:future. The methods are identified only by name. Therefore, repeatedly loaded methods are not differentiated. The same calling method is specified for the methods that have the same name.

    HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean();
    hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.OrderService");
    hsfApiConsumerBean.setVersion("1.0.0");
    hsfApiConsumerBean.setGroup("HSF");
    // [Specify] the Future interface to make an asynchronous call: List<String> asyncallMethods = new ArrayList<String>();
    // Format: name:{methodName};type:future
    asyncallMethods.add("name:queryOrder;type:future");
    hsfApiConsumerBean.setAsyncallMethods(asyncallMethods);
    
    hsfApiConsumerBean.init(true);
    
    // [Proxy] Obtain the HSF proxy: OrderService orderService = (OrderService) hsfApiConsumerBean.getObject();
    // ---------------------- Call -----------------------//
    // [Call] Initiate an HSF asynchronous call. null is returned.
    OrderModel orderModel = orderService.queryOrder(1L);
    // Obtain the future object from the context of the current call at the earliest opportunity. The future object will be overwritten during the subsequent calls in the same thread because the object is placed in ThreadLocal. Therefore, you must obtain the object at the earliest opportunity: HSFFuture hsfFuture = HSFResponseFuture.getFuture();
    
    // do something else
    
    // The actual result is obtained in this step. If the call is not completed, the call request is blocked and the consumer must wait for the result. The maximum waiting time is 5,000 ms. try {
        System.out.println(hsfFuture.getResponse(5000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    In HSF, the default time-out period is 3,000 ms. If the service object is not returned after the time-out period expires, a time-out error is returned when you call HSFFuture.getResponse. timeout in HSFFuture.getResponse(timeout) specifies the time-out period. If the service result is not returned before the specified time-out period expires and the request does not time out, you can call getResponse multiple times to obtain the result.

  • Use Spring to configure HSF services

    Spring is a framework that is widely used in applications. If you do not want to use APIs to configure HSF services, you can configure them in the Spring XML format. The following XML configuration has the same effect as the preceding API configuration.

    <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"/>
        <!--[Specify] the interface for service subscription. -->
        <property name="asyncallMethods">
            <list>
                <value>name:queryOrder;type:future</value>
            </list>
         </property>
    </bean>
  • Use annotations to configure HSF services

    Spring Boot is widely used. You can use annotations to configure Spring beans, configure HSF services, and subscribe to HSF services.

    Add the starter dependency to the project.

    <dependency>
        <groupId>com.alibaba.boot</groupId>
        <artifactId>pandora-hsf-spring-boot-starter</artifactId>
    </dependency>

    In most cases, an HSF consumer is used in multiple places, but you do not need to use @HSFConsumer to mark each place where the consumer is used. You need only to write a unified Config class and use @Autowired for injection at the place where the consumer is used. The following annotation configuration has the same effect as the preceding API configuration:

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

    Use @Autowired for injection at the place where the consumer is used.

    @Autowired
    OrderService orderService;

Use the callback method for asynchronous calls

If your consumer is configured to use the callback method, you must configure a listener that has implemented the HSFResponseCallback interface. After the result is returned, HSF calls the method in HSFResponseCallback.

  • Use APIs to configure HSF services

    Call context for the callback

    Before you initiate a call, you can use CallbackInvocationContext.setContext(Object obj) to specify the context information for the current call and save the information to ThreadLocal. In the callback function of a listener, you can use CallbackInvocationContext.getContext() to obtain the context.

    Sample callback function

    public class CallbackHandler implements HSFResponseCallback {
    
        // It is triggered when a service error occurs.
        @Override
        public void onAppException(Throwable t) {
            t.printStackTrace();
        }
    
        // The returned result.
        @Override
        public void onAppResponse(Object result) {
            // Obtain the context that is specified for the callback: Object context = CallbackInvocationContext.getContext();
    
            System.out.println(result.toString() + context);
        }
        // HSF exceptions.
        @Override
        public void onHSFException(HSFException e) {
            e.printStackTrace();
        }
    }

    Use API programming to configure the callback method

    HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean();
    hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.OrderService");
    hsfApiConsumerBean.setVersion("1.0.0");
    hsfApiConsumerBean.setGroup("HSF");
    // [Specify] the callback method to make an asynchronous call: List<String> asyncallMethods = new ArrayList<String>();
    asyncallMethods.add("name:queryOrder;type:callback;listener:com.alibaba.middleware.hsf.CallbackHandler");
    hsfApiConsumerBean.setAsyncallMethods(asyncallMethods);
    hsfApiConsumerBean.init(true);
    // [Proxy] Obtain the HSF proxy: OrderService orderService = (OrderService) hsfApiConsumerBean.getObject();
     // Optional. Specify the context. You can obtain the context from CallbackHandler by calling the API: CallbackInvocationContext.setContext("in callback");
    // Initiate the call: orderService.queryOrder(1L); // The actual return value is null.
    // Clear the context: CallbackInvocationContext.setContext(null);
    // do something else

    You can specify the context in the calling thread, and then obtain and use the context in the listener. Compared with the Future interface that is used for asynchronous calls, the callback method allows you to immediately obtain the returned result.

  • Use Spring to configure HSF services
    <bean id="CallHelloWorld" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
        <!--[Specify] the interface for service subscription. -->
        <property name="interfaceName" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
        <!--[Specify] the version of the service. -->
        <property name="version" value="1.0.0"/>
        <!--[Specify] the group to which the service belongs. -->
        <property name="group" value="HSF"/>
        <property name="asyncallMethods">
            <list>
                <!--In the code, future means that the Future interface is used to obtain the execution result for the request. For example, a remote interface is called first, other operations are performed in the same thread, and then Future is called to obtain the result in the same thread. -->
                <!--name:methodName;type:future|callback-->
                <value>name:queryOrder;type:callback;listener:com.alibaba.middleware.hsf.CallbackHandler</value>
            </list>
         </property>
    </bean>
  • Use annotations to configure the callback method that is used to make an asynchronous call for HSF services
    @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) {
            // Obtain the context that is specified for the callback: Object context = CallbackInvocationContext.getContext();
    
            System.out.println(result.toString() + context);
        }
    
        @Override
        public void onHSFException(HSFException e) {
            e.printStackTrace();
        }
    
    
    }
    Notice The callback function is invoked by an independent thread pool (LinkedBlockingQueue infinite queue). To prevent the onAppResponse callback operations for other requests from being affected, we recommend that you do not perform time-consuming operations. The default corePoolSize value and the default maxPoolSize value for the callback thread are the same as the number of instance CPUs. You can customize the following -D parameters:
    • -Dhsf.callback.min.poolsize: the minimum size of the callback thread pool.
    • -Dhsf.callback.max.poolsize: the maximum size of the callback thread pool.