You can use Pandora Boot to develop High-Speed Service Framework (HSF) applications and implement service registration and discovery, asynchronous calls, and unit testing. A WAR package is required when you deploy an HSF application in Ali-Tomcat, whereas a JAR package is required when Pandora Boot is used. You can package the HSF application into a FatJar package, which is more consistent with the microservice architecture. This method does not depend on Ali-Tomcat, and makes application deployment more flexible. Pandora Boot is an enhancement of Spring Boot.

Prerequisites

Service registration and discovery

This section describes how to develop applications by using Pandora Boot and implement service registration and discovery. The applications include a service provider and a service consumer.
Notice Do not remotely call the HSF service when you start an application. Otherwise, the application fails to be started.

Download the demo source code. For more information, see Demo source code.

Clone the project from GitHub and find the demo project used in this topic in the microservice-doc-demo/hsf-pandora-boot directory.

  1. Create a service provider.
    1. Create a Maven project named hsf-pandora-boot-provider.
    2. Add required dependencies to the pom.xml file.
      <properties>
            <java.version>1.8</java.version>
            <spring-boot.version>2.1.6.RELEASE</spring-boot.version>
            <pandora-boot.version>2019-06-stable</pandora-boot.version>
        </properties>
      
        <dependencies>
            <dependency>
                <groupId>com.alibaba.boot</groupId>
                <artifactId>pandora-hsf-spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
      
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring-boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>com.taobao.pandora</groupId>
                    <artifactId>pandora-boot-starter-bom</artifactId>
                    <version>${pandora-boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
      
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>com.taobao.pandora</groupId>
                    <artifactId>pandora-boot-maven-plugin</artifactId>
                    <version>2.1.11.8</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>

      Although HSF is independent of the web environment, web-related features are required in the application lifecycle. Therefore, the spring-boot-starter-web dependency must be added.

      pandora-hsf-spring-boot-starter implements automatic installation and configuration of HSF. pandora-boot-maven-plugin is a Maven packaging plug-in provided by Pandora Boot. It can compile a Pandora Boot HSF project as an executable FatJar package, which can be deployed and run in Enterprise Distributed Application Service (EDAS) Container.

      dependencyManagement includes the spring-boot-dependencies and pandora-boot-starter-bom dependencies, which manage related versions of Spring Boot and Pandora Boot. You do not need to set parent to spring-boot-starter-parent for your project.

    3. Define a service interface, and create an interface class named com.alibaba.edas.HelloService.
      HSF enables interface-based service communication. When an interface is defined, producers implement and publish specific services by using this interface, and consumers subscribe to and consume services by using this interface.
        public interface HelloService {
            String echo(String string);
        }

      The com.alibaba.edas.HelloService interface provides the echo method.

    4. Add the EchoServiceImpl implementation class of the service provider, and publish the service by using annotations.
        @HSFProvider(serviceInterface = HelloService.class, serviceVersion = "1.0.0")
        public class HelloServiceImpl implements HelloService {
            @Override
            public String echo(String string) {
                return string;
            }
        }

      In HSF applications, a service is uniquely identified by the interface name and service version. Therefore, you must add the com.alibaba.edas.HelloService interface name and the1.0.0 service version to the HSFProvider annotation.

      Note
      • The configuration in the annotation has a higher priority.
      • If they are not configured in the HSFProvider annotation, the global configurations of these properties are searched first in the resources/application.properties file when the service is published.
      • If they are not configured in the HSFProvider annotation or the resources/application.properties file, the default values in the annotation are used.
    5. Configure the application name and the listener port number in the application.properties file in the resources directory.
        spring.application.name=hsf-pandora-boot-provider
        server.port=8081
      
        spring.hsf.version=1.0.0
        spring.hsf.timeout=3000
      Note We recommend that you configure both the service version (spring.hsf.version) and service timeout (spring.hsf.timeout) in the application.properties file.
    6. Add the main function handler for starting the service.
       @SpringBootApplication
        public class HSFProviderApplication {
      
            public static void main(String[] args) {
                // Start Pandora Boot to load the Pandora container. PandoraBootstrap.run(args);
                SpringApplication.run(HSFProviderApplication.class, args);
                // Indicate that the service is started, and set the thread wait time. This prevents the container from exiting after a user runs the service code and exits. PandoraBootstrap.markStartupAndWait();
            }
        }
      Table 1. Service provider properties
      Property Required Description Type Default value
      serviceInterface Yes The interface that provides services. Class java.lang.Object
      serviceVersion No The version of the service. String 1.0.0.DAILY
      serviceGroup No The group name of the service. String HSF
      clientTimeout No This property applies to all methods in the interface. However, if the consumer sets a timeout period for a method by using the methodSpecials property, the set timeout period takes precedence over this property value for this method. Other methods still use the timeout period configured on the provider. Unit: milliseconds. int -1
      corePoolSize No The minimum number of active threads that are allocated from the public thread pool for this service. int 0
      maxPoolSize No The maximum number of active threads that are allocated from the public thread pool for this service. int 0
      delayedPublish No Specifies whether to delay the service publishing. boolean false
      includeFilters No The custom filters for you to choose. String[] Empty
      enableTXC No Specifies whether to enable Global Transaction Service (GTS) for distributed transactions. boolean false
      serializeType No The serialization type of the service interface. Valid values: hessian and java. String hessian
      supportAsynCall No Specifies whether to support asynchronous calls. String false
      Table 2. Service creation and publishing limits
      Parameter Example Maximum size Adjustable
      {Service name}:{Version number} com.alibaba.edas.testcase.api.TestCase:1.0.0 192 bytes No
      Group name aliware 32 bytes No
      Number of services published by a Pandora application N/A 800 Yes. In the left-side navigation pane, click Basic Information. On the Basic Information tab, click Edit next to JMV Parameters in the Application Settings section. Then, in the Application Settings dialog box, choose Custom > Custom Parameters. In the Custom Parameters field, enter -DCC.pubCountMax=1200. You can change the value of the parameter based on the number of published services of the application.
  2. Create a service consumer.
    In this example, a service consumer is created to call services by using the interface in HSFConsumer.
    1. Create a Maven project named hsf-pandora-boot-consumer.
    2. Add required dependencies to the pom.xml file.
      Note The Maven dependencies for the consumer and provider are the same.
        <properties>
            <java.version>1.8</java.version>
            <spring-boot.version>2.1.6.RELEASE</spring-boot.version>
            <pandora-boot.version>2019-06-stable</pandora-boot.version>
        </properties>
      
        <dependencies>
            <dependency>
                <groupId>com.alibaba.boot</groupId>
                <artifactId>pandora-hsf-spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
      
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring-boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>com.taobao.pandora</groupId>
                    <artifactId>pandora-boot-starter-bom</artifactId>
                    <version>${pandora-boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
      
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>com.taobao.pandora</groupId>
                    <artifactId>pandora-boot-maven-plugin</artifactId>
                    <version>2.1.11.8</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    3. Copy the com.alibaba.edas.HelloService service interface published by the service provider to the local environment. The name of the package is also copied.
        public interface HelloService {
            String echo(String string);
        }
    4. Use annotations to inject the service consumer instance to the Spring context.
       @Configuration
        public class HsfConfig {
      
            @HSFConsumer(clientTimeout = 3000, serviceVersion = "1.0.0")
            private HelloService helloService;
      
        }
      Note Configure @HSFConsumer once in the HsfConfig class, and then inject and use it in multiple steps by using @Autowired. An @HSFConsumer is used in multiple places, but you do not need to mark each place where it is used with @HSFConsumer. You need only to write an HsfConfig class and inject it by using @Autowired.
    5. To facilitate testing, you can use SimpleController to expose a /hsf-echo/* HTTP interface. The /hsf-echo/* interface can be used to call the HSF service provider.
        @RestController
        public class SimpleController {
      
            @Autowired
            private HelloService helloService;
      
            @RequestMapping(value = "/hsf-echo/{str}", method = RequestMethod.GET)
            public String echo(@PathVariable String str) {
                return helloService.echo(str);
            }
        }
    6. Configure the application name and the listener port number in the application.properties file in the resources directory.
        spring.application.name=hsf-pandora-boot-consumer
        server.port=8080
      
        spring.hsf.version=1.0.0
        spring.hsf.timeout=1000
      Note We recommend that you set the service version and timeout period in the application.properties file.
    7. Add the main function handler for starting the service.
        @SpringBootApplication
        public class HSFConsumerApplication {
      
            public static void main(String[] args) {
                PandoraBootstrap.run(args);
                SpringApplication.run(HSFConsumerApplication.class, args);
                PandoraBootstrap.markStartupAndWait();
            }
        }
      Table 3. Service consumer properties
      Property Required Description Type Default value
      serviceGroup No The group name of the service. String HSF
      serviceVersion No The version of the service. String 1.0.0.DAILY
      clientTimeout No The timeout period set by the consumer for all methods in an interface, in milliseconds. int -1
      generic No Specifies whether to support generic calls. boolean false
      addressWaitTime No The time used to wait for the service registry (ConfigServer) to push the service provider address, in milliseconds. int 3000
      proxyStyle No The proxy style. Valid values: JDK and Javassist. String jdk
      futureMethods No The list of methods in asynchronous call mode on the service. Default value: Null. This value indicates that all methods are called in asynchronous mode. String[] Empty
      consistent No Specifies whether to use consistent hashing. String Empty
      methodSpecials No The method-level timeout period, number of retries, and method name. com.alibaba.boot.hsf.annotation.HSFConsumer.ConsumerMethodSpecial[] Empty
      Table 4. Global properties for service providers and consumers
      Property Required Description Type Default value
      spring.hsf.version No The global version of the service. You can also use spring.hsf.versions. <Complete service interface name>=<Version number set only for the service interface, in String format>, such as spring.hsf.versions.com.aliware.edas.EchoService="1.0.0", to specify the version of a service. String 1.0.0.DAILY
      spring.hsf.group No The global group name of the service. You can also use spring.hsf.groups. <Complete service interface name>=<Group name set only for the service interface, in String format> to specify the group name for a service. String HSF
      spring.hsf.timeout No The global timeout period of the service. You can also use spring.hsf.timeouts. <Complete service interface name>=<Timeout period, in String format> to specify the timeout period for a service. Integer None
      spring.hsf.max-wait-address-time No The global time used to wait for the service registry (ConfigServer) to push the service provider address, in milliseconds. You can also use spring.hsf.max-wait-address-times. <Complete service interface name>=<Response time, in String format> to specify the property. Integer 3000
      spring.hsf.delay-publish No The global switch for service publishing delay. Valid values: true and false. You can also use spring.hsf.delay-publishes. <Complete service interface name>=<Whether to delay the service publishing, in String format> to specify the property. String None
      spring.hsf.core-pool-size No The minimum number of globally active threads for the service. You can also use spring.hsf.core-pool-sizes. <Complete service interface name>=<Minimum number of active threads, in String format> to specify the property. int None
      spring.hsf.max-pool-size No The maximum number of globally active threads for the service. You can also use spring.hsf.max-pool-sizes. <Complete service interface name>=<Maximum number of active threads, in String format> to specify the property. int None
      spring.hsf.serialize-type No The global serialization type of the service. Valid values: Hessian and Java. You can also use spring.hsf.serialize-types. <Complete service interface parameter>=<Serialization type> to specify the property. String None
      Note Global parameters can be set in the application.properties file of Pandora Boot applications.

Local code development and debugging

  1. Configure the light-weight configuration and registry center.
    The light-weight configuration and registry center must be used for local code development and debugging, which includes a light-weight service registry. For more information, see Start the light-weight configuration registry.
  2. Start the application.
    • Start the application in an integrated development environment (IDE)

      To start the application in IDE, configure the startup parameter -Djmenv.tbsite.net={$IP} in VM options and start the application by calling the main method. In the parameter, {$IP} indicates the IP address of the light-weight configuration center. For example, the value of {$IP} is 127.0.0.1 for the light-weight configuration center that is locally started.

      You can also set jmenv.tbsite.net to the IP address of the light-weight configuration center in the hosts file, without setting JVM parameters. For more information, see Start the light-weight configuration registry.

    • Start the application by using FatJar
      1. Add the taobao-hsf.sar dependency /.m2/com/taobao/pandora/taobao-hsf.sar/2019-06-stable/taobao-hsf.sar-2019-06-stable.jar, on which the startup parameters in later steps depend.
           <dependency>
                <groupId>com.taobao.pandora</groupId>
                <artifactId>taobao-hsf.sar</artifactId>
                <version>2019-06-stable</version>
            </dependency>
      2. To package the Pandora Boot project into a FatJar package by using Maven, add the following plug-in to pom.xml. To prevent conflicts with other packaging plug-ins, do not add configurations of other FatJar plug-ins to the plugin field in build.
            <plugin>
                <groupId>com.taobao.pandora</groupId>
                <artifactId>pandora-boot-maven-plugin</artifactId>
                <version>2.1.11.8</version>
                <executions>
                    <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                    </execution>
                </executions>
            </plugin>
      3. After you add the plug-in, run the mvn clean package command in the home directory of the project to create a package in the target directory. Then, you can find the packaged FatJar file.
      4. Start the application by running the following Java command:
        java -Djmenv.tbsite.net=127.0.0.1 -Dpandora.location=${M2_HOME}/.m2/repository/com/taobao/pandora/taobao-hsf.sar/2019-06-stable/taobao-hsf.sar-2019-06-stable.jar -jar hsf-pandora-boot-provider-1.0.jar
        Note The path specified by -Dpandora.location must be a full path, which must specify the location of taobao-hsf.sar when you start the application in the command line.

    Access the address of the instance where the consumer is located for the consumer to remotely call services of the provider.

    curl localhost:8080/hsf-echo/helloworld
    helloworld

Unit testing

The Pandora Boot unit testing can be started by PandoraBootRunner and seamlessly integrated with SpringJUnit4ClassRunner.

The following example demonstrates how to perform unit testing for service providers:

  1. Add dependencies required for the Pandora Boot and Spring Boot unit testing in Maven.
      <dependency>
          <groupId>com.taobao.pandora</groupId>
          <artifactId>pandora-boot-test</artifactId>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
          <scope>test</scope>
      </dependency>
  2. Compile the test code.
    @RunWith(PandoraBootRunner.class)
      @DelegateTo(SpringJUnit4ClassRunner.class)
      // Add both the Spring Boot startup class and service test class required for unit testing.
      @SpringBootTest(classes = {HSFProviderApplication.class, HelloServiceTest.class })
      @Component
      public class HelloServiceTest {
    
          /**
           * If you are using @HSFConsumer, you must add the service class to @SpringBootTest and use the class to inject objects to prevent abnormal class conversion during generic calls.
           */
          @HSFConsumer(generic = true)
          HelloService helloService;
    
          // Common calls.
          @Test
          public void testInvoke() {
              TestCase.assertEquals("hello world", helloService.echo("hello world"));
          }
          // Generic calls.
          @Test
          public void testGenericInvoke() {
              GenericService service = (GenericService) helloService;
              Object result = service.$invoke("echo", new String[] {"java.lang.String"}, new Object[] {"hello world"});
              TestCase.assertEquals("hello world", result);
          }
          // The return value Mock.
          @Test
          public void testMock() {
              HelloService mock = Mockito.mock(HelloService.class, AdditionalAnswers.delegatesTo(helloService));
              Mockito.when(mock.echo("")).thenReturn("beta");
              TestCase.assertEquals("beta", mock.echo(""));
          }
      }