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

Synchronous calls

I/O operations in HSF are performed asynchronously. In synchronous calls, the client executes future.get(timeout) and waits for the response from the server. Here, timeout is the timeout period for the client's waiting time, which is 3,000 ms by default. The following diagram is the synchronous call sequence diagram.

For the client, not all HSF services need to synchronously wait for the server to respond. For these services, HSF allows the client to call these services in asynchronous mode. In this way, the client is not blocked due to synchronous calls in HSF. In asynchronous call mode, the default value is returned for all HSF calls. For example, if the type of the return value is integer, 0 is returned. If the type of the return value is object, Null is returned. The real result is obtained by the HSFResponseFuture or callback function.

Future asynchronous calls

After making an HSF call, you can obtain the HSFFuture object related to the return result in the context, and then call HSFFuture.getResponse(timeout) to obtain the result returned by the server. The following figure shows the sequence diagram of a future asynchronous call.

  • Configure a Future asynchronous call for HSF services through API programming

    HSF allows you to call methods to configure asynchronous calls in the format of name:${methodName};type:future. The methods are identified by name, and therefore the reloaded methods are not differentiated. Methods that have the same name are set with the same call method.

    HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean();
    hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.OrderService");
    hsfApiConsumerBean.setVersion("1.0.0");
    hsfApiConsumerBean.setGroup("HSF");
    // [Set] asynchronous future call
    List<String> asyncallMethods = new ArrayList<String>();
    // Format: name:{methodName};type:future
    asyncallMethods.add("name:queryOrder;type:future");
    hsfApiConsumerBean.setAsyncallMethods(asyncallMethods);
    
    hsfApiConsumerBean.init(true);
    
    // [Agent] Get the HSF agent.
    OrderService orderService = (OrderService) hsfApiConsumerBean.getObject();
    // ---------------------- Call -----------------------//
    // [Call] Initiate an HSF asynchronous call and return null.
    OrderModel orderModel = orderService.queryOrder(1L);
    // Get the future object in the context of the current call in time, because the object is placed in `ThreadLocal` and will be overwritten during subsequent calls in the same thread.
    HSFFuture hsfFuture = HSFResponseFuture.getFuture();
    
    // do something else
    
    // The result is obtained in this step. If the call is not completed, the call request is blocked and the client has to 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 timeout period is 3,000 ms. If the service object is not returned after the timeout period expires, an error is thrown when you call HSFFuture.getResponse. If the service result is not returned within the timeout period specified in HSFFuture.getResponse(timeout) but the request does not time out, you can call getResponse multiple times to get the result.

  • Configure a Future asynchronous call for HSF services through Spring configuration

    Spring is a framework that is widely used by applications. You can configure HSF services through Spring XML. The following XML configuration has the same effect as the preceding API programming:

    <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"/>
        <! --Set the interface for service subscription. -->
        <property name="asyncallMethods">
            <list>
                <value>name:queryOrder;type:future</value>
            </list>
         </property>
    </bean>
  • Configure a Future asynchronous call for HSF services through annotations

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

    Add the starter dependency to the project.

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

    An HSF consumer is used in multiple places, but you do not need to mark each place where it is used with @HSFConsumer. You only need to write the Config class and annotate other places with @Autowired. The following annotation configuration has the same effect as the preceding API programming:

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

    Directly add the annotation at the place where you need to use the HSF consumer.

    @Autowired
    OrderService orderService;

Callback asynchronous calls

In callback calls, you must configure a listener that has implemented HSFResponseCallback. After the result is returned, HSF calls the method in HSFResponseCallback. The following figure shows the call sequence diagram.

  • Configure a callback asynchronous call for HSF services through API programming

    Callback call context

    Before making the call, you can use CallbackInvocationContext.setContext(Object obj) to set a context for the current call and save the settings to threadlocal. In the listener's callback function, you can use CallbackInvocationContext.getContext() to get the object.

    Callback function example

    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) {
            // Get the context set for the callback call.
            Object context = CallbackInvocationContext.getContext();
    
            System.out.println(result.toString() + context);
        }
        //HSF error
        @Override
        public void onHSFException(HSFException e) {
            e.printStackTrace();
        }
    }

    Configure the callback method through API programming

    HSFApiConsumerBean hsfApiConsumerBean = new HSFApiConsumerBean();
    hsfApiConsumerBean.setInterfaceName("com.alibaba.middleware.hsf.guide.api.service.OrderService");
    hsfApiConsumerBean.setVersion("1.0.0");
    hsfApiConsumerBean.setGroup("HSF");
    // [Set] asynchronous callback 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);
    // [Agent] Get the HSF agent.
    OrderService orderService = (OrderService) hsfApiConsumerBean.getObject();
     // Set the context, which is optional. You can obtain the context from CallbackHandler through the corresponding API:
    CallbackInvocationContext.setContext("in callback");
    //Initiate a call.
    orderService.queryOrder(1L); // Null is returned here.
    // Clear the context.
    CallbackInvocationContext.setContext(null);
    // do something else

    You can set the context in the call thread, and then obtain and use it in the listener. Compared with future asynchronous call mode, you can immediately know the returned result through callback calls.

  • Configure a callback asynchronous call for HSF services through Spring configuration
    <bean id="CallHelloWorld" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean">
        <! --Set the interface for service subscription. -->
        <property name="interfaceName" value="com.alibaba.middleware.hsf.guide.api.service.OrderService"/>
        <! --[Set] the version of the service. -->
        <property name="version" value="1.0.0"/>
        <! --[Set] the group to which the service belongs. -->
        <property name="group" value="HSF"/>
        <property name="asyncallMethods">
            <list>
                <! --future means getting the request execution result by calling the Future method. For example, call the remote interface first, perform other operations in the same thread, and then get the result in the same thread by calling the Future method. -->
                <! --name:methodName;type:future|callback-->
                <value>name:queryOrder;type:callback;listener:com.alibaba.middleware.hsf.CallbackHandler</value>
            </list>
         </property>
    </bean>
  • Configure a callback asynchronous call for HSF services through annotations
    @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) {
            // Get the context set for the callback call.
            Object context = CallbackInvocationContext.getContext();
    
            System.out.println(result.toString() + context);
        }
    
        @Override
        public void onHSFException(HSFException e) {
            e.printStackTrace();
        }
    
    
    }
    Notice The callback function is called by a separate thread pool (LinkedBlockingQueue infinite queue). Do not perform time-consuming operations, to avoid affecting the onAppResponse callback of other requests. The default corePoolSize and maxPoolSize of the callback thread are equal to the number of instance CPUs. You can customize the -D parameter:
    • Minimum size of the callback thread pool: -Dhsf.callback.min.poolsize
    • Maximum size of the callback thread pool: -Dhsf.callback.max.poolsize