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, while a JAR package is required if Pandora Boot is used. You can package the HSF application into a FatJar package, which is more in line with the microservice architecture. This method does not depend on Ali-Tomcat, making application deployment more flexible . Pandora Boot is an enhancement of Spring Boot.

Prerequisites

Service registration and discovery

The following describes how to use Pandora Boot to develop applications (including a service provider and service consumer) and implement service registration and discovery.
Notice Do not call the HSF service remotely when starting an application. Otherwise, the application fails to be started.

Download the source code demo at https://github.com/aliyun/alibabacloud-microservice-demo/tree/master/microservice-doc-demo/hsf-pandora-boot.

Clone the project from GitHub and find the sample 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 the HSF service framework is independent of the web environment, web-related features are required during 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 EDAS Container.

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

    3. Define a service interface, and create the interface class com.alibaba.edas.HelloService.
      The HSF service framework enables service communication over interfaces. When an interface is defined, providers implement and publish specific services by using this interface, and consumers also 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 version. Therefore, you need to add the com.alibaba.edas.HelloService interface name and the 1.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 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 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 has been started, and a thread waiting time is set. 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 name of the service group. 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. The unit is millisecond. Integer -1
      corePoolSize No The minimum number of active threads that are allocated from the public thread pool for this service. Integer 0
      maxPoolSize No The maximum number of active threads that are allocated from the public thread pool for this service. Integer 0
      delayedPublish No Specifies whether to delay the service publishing. Boolean false
      includeFilters No The custom filters for you to choose. String[] Null
      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 restrictions
      Name 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 None 800 Yes. On the Basic Information page, you can click Settings on the right of Application Settings and select JVM in the drop-down list. In the Application Settings dialog box, choose Custom > Custom Parameters and enter -DCC.pubCountMax=1200 in the input box. You can change the value of the parameter based on the number of published services.
  2. Create a service consumer.
    In this example, a service consumer is created to call services through 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 service interface com.alibaba.edas.HelloService, including the package name, published by the service provider to the local environment.
        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 EchoService echoService;
      
        }
      Note Configure @HSFConsumer once in the HsfConfig class, and then inject and use it in multiple steps through @Autowired. An HSF consumer is used in multiple places, but you do not have to mark each place where it is used with @HSFConsumer. You only need to write a HsfConfig class and inject it through @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 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 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 name of the service group. 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. Integer -1
      generic No Specifies whether to support generic calls. Boolean false
      addressWaitTime No The time used to wait for the service registry (Config Server) to push the service provider address, in milliseconds. Integer 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. The default value is null, indicating that all methods are called in asynchronous mode. String[] Null
      consistent No Specifies whether to use consistent hashing. String Null
      methodSpecials No The method-level timeout period, number of retries, and method name. com.alibaba.boot.hsf.annotation.HSFConsumer.ConsumerMethodSpecial[] Null
      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 only set for the service interface, in string>, for example, 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 only set for the service interface, in string> 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> 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 (Config Server) 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> 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> 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> to specify the property. Integer 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> to specify the property. Integer 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 registry.
    The light-weight configuration registry 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 directly start the application by calling the main method. In the parameter, {$IP} indicates the IP address of the light-weight configuration registry. For example, the value of {$IP} is 127.0.0.1 for the light-weight configuration registry that is started locally.

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

    • Start the application by using FatJar
      1. Add the taobao-hsf.sar dependency, specifically, the /.m2/com/taobao/pandora/taobao-hsf.sar/2019-06-stable/taobao-hsf.sar-2019-06-stable.jar dependency, which will be depended on by the startup parameters later.
           <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 any 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 adding the plug-in, run the mvn clean package command in the home directory of the project to create a package in the target directory.
      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 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 it 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);
          }
          // Return the 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(""));
          }
      }