您可以使用 Pandora Boot 开发 HSF 应用,实现服务注册发现、异步调用,并完成单元测试。相比使用 ali-tomcat 部署 HSF 的 WAR 包,Pandora Boot部署的是 JAR 包。直接将 HSF 应用打包成 FatJar,这更加符合微服务的风格,不需要依赖外置的 ali-tomcat 也使得应用的部署更加灵活。Pandora Boot 可以认为是 Spring Boot 的增强。

前提条件

服务注册与发现

介绍如何使用 Pandora Boot 开发应用(包括服务提供者和服务消费者)并实现服务注册与发现。
注意 严禁在应用启动时调用 HSF 远程服务,否则会导致启动失败。

Demo 源码下载:https://github.com/aliyun/alibabacloud-microservice-demo/tree/master/microservice-doc-demo/hsf-pandora-boot

使用 Git 克隆整个项目,并在 microservice-doc-demo/hsf-pandora-boot 文件夹内可以找到本文使用的示例工程。

  1. 创建服务提供者。
    1. 创建命名为 hsf-pandora-boot-provider的 Maven 工程。
    2. pom.xml 中引入需要的依赖。
      <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>

      虽然 HSF 服务框架并不依赖于 Web 环境,但是在应用的生命周期过程中需要使用到 Web 相关的特性,所以需要添加 spring-boot-starter-web 的依赖。

      pandora-hsf-spring-boot-starter 实现了 HSF 配置的自动装配。pandora-boot-maven-plugin 是 Pandora Boot 提供的 maven 打包插件,可以将 Pandora Boot HSF 工程编译为可执行的 FatJar,并在 EDAS Container 中部署运行。

      dependencyManagement 中包含了 spring-boot-dependenciespandora-boot-starter-bom 两个依赖,分别负责 Spring Boot 和 Pandora Boot 相关依赖的版本管理,设置之后,您的工程无需将 parent 设置为 spring-boot-starter-parent

    3. 定义服务接口,创建一个接口类 com.alibaba.edas.HelloService
      HSF 服务框架基于接口进行服务通信,当接口定义好之后,生产者将通过该接口实现具体的服务并发布,消费者也是基于此接口去订阅和消费服务。
        public interface HelloService {
            String echo(String string);
        }

      接口 com.alibaba.edas.HelloService 提供了 echo 方法。

    4. 添加服务提供者的具体实现类 EchoServiceImpl ,并通过注解方式发布服务。
        @HSFProvider(serviceInterface = HelloService.class, serviceVersion = "1.0.0")
        public class HelloServiceImpl implements HelloService {
            @Override
            public String echo(String string) {
                return string;
            }
        }

      在 HSF 应用中,接口名和服务版本才能唯一确定一个服务,所以在注解 HSFProvider 中的需要添加接口名 com.alibaba.edas.HelloService 和服务版本1.0.0

      说明
      • 注解中的配置拥有高优先级。
      • 如果在注解中没有配置,服务发布时会优先在 resources/application.properties 文件中查找这些属性的全局配置。
      • 如果注解和 resources/application.properties 文件中都没有配置,则会使用注解中的默认值。
    5. resources 目录下的 application.properties 文件中配置应用名和监听端口号。
        spring.application.name=hsf-pandora-boot-provider
        server.port=8081
      
        spring.hsf.version=1.0.0
        spring.hsf.timeout=3000
      说明 建议将服务版本(spring.hsf.version)和服务超时(spring.hsf.timeout)都统一配置在 application.properties 中。
    6. 添加服务启动的 main 函数入口。
       @SpringBootApplication
        public class HSFProviderApplication {
      
            public static void main(String[] args) {
                // 启动 Pandora Boot 用于加载 Pandora 容器
                PandoraBootstrap.run(args);
                SpringApplication.run(HSFProviderApplication.class, args);
                // 标记服务启动完成,并设置线程 wait。防止业务代码运行完毕退出后,导致容器退出。
                PandoraBootstrap.markStartupAndWait();
            }
        }
      表 1. 服务提供者属性列表
      属性 是否必配 描述 类型 默认值
      serviceInterface 服务对外提供的接口 Class java.lang.Object
      serviceVersion 服务的版本号 String 1.0.0.DAILY
      serviceGroup 服务的组名 String HSF
      clientTimeout 该配置对接口中的所有方法生效,但是如果客户端通过 methodSpecials 属性对某方法配置了超时时间,则该方法的超时时间以客户端配置为准。其他方法不受影响,还是以服务端配置为准(单位 ms) int -1
      corePoolSize 单独针对这个服务设置最小活跃线程数,从公用线程池中划分出来 int 0
      maxPoolSize 单独针对这个服务设置最大活跃线程数,从公用线程池中划分出来 int 0
      delayedPublish 是否延迟发布 boolean false
      includeFilters 用户可选的自定义过滤器 String[]
      enableTXC 是否开启分布式事务 GTS boolean false
      serializeType 服务接口序列化类型,hessian 或者 java String hessian
      supportAsynCall 是否支持异步调用 String false
      表 2. 服务创建及发布限制
      名称 示例 限制大小 是否可调整
      {服务名}:{版本号} com.alibaba.edas.testcase.api.TestCase:1.0.0 最大192字节
      组名 aliware 最大32字节
      一个Pandora应用实例发布的服务数 N/A 最大800个 是,可在应用基本信息页面单击应用设置右侧的设置,并在下拉列表中选择 JVM,然后在弹出的应用设置对话框中选择自定义 > 自定义参数,在输入框中添加 -DCC.pubCountMax=1200属性参数(该参数值可根据应用实际发布的服务数调整)
  2. 创建服务消费者。
    本示例中,将创建一个服务消费者,通过 HSFConsumer 所提供的 API 接口去调用服务提供者。
    1. 创建一个 Maven 工程,命名为 hsf-pandora-boot-consumer
    2. pom.xml 中引入需要的依赖内容。
      说明 消费者和提供者的 Maven 依赖是相同。
        <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. 将服务提供者所发布的 API 服务接口(包括包名)拷贝到本地,如 com.alibaba.edas.HelloService
        public interface HelloService {
            String echo(String string);
        }
    4. 通过注解的方式将服务消费者的实例注入到 Spring 的 Context 中。
       @Configuration
        public class HsfConfig {
      
            @HSFConsumer(clientTimeout = 3000, serviceVersion = "1.0.0")
            private EchoService echoService;
      
        }
      说明HsfConfig 类里配置一次 @HSFConsumer,然后在多处通过 @Autowired 注入使用。通常一个 HSF Consumer 需要在多个地方使用,但并不需要在每次使用的地方都用 @HSFConsumer 来标记。只需要写一个统一的 HsfConfig 类,然后在其它需要使用的地方,直接通过 @Autowired 注入即可。
    5. 为了便于测试,使用 SimpleController 来暴露一个 /hsf-echo/* 的 HTTP 接口,/hsf-echo/* 接口内部实现调用了 HSF 服务提供者。
        @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. resources 目录下的 application.properties 文件中配置应用名与监听端口号。
        spring.application.name=hsf-pandora-boot-consumer
        server.port=8080
      
        spring.hsf.version=1.0.0
        spring.hsf.timeout=1000
      说明 建议将服务版本和服务超时都统一配置在 application.properties 中。
    7. 添加服务启动的 main 函数入口。
        @SpringBootApplication
        public class HSFConsumerApplication {
      
            public static void main(String[] args) {
                PandoraBootstrap.run(args);
                SpringApplication.run(HSFConsumerApplication.class, args);
                PandoraBootstrap.markStartupAndWait();
            }
        }
      表 3. 服务消费者属性列表
      属性 是否必配 描述 类型 默认值
      serviceGroup 服务的组名 String HSF
      serviceVersion 服务的版本号 String 1.0.0.DAILY
      clientTimeout 客户端统一设置接口中所有方法的超时时间(单位 ms) int -1
      generic 是否支持泛化调用 boolean false
      addressWaitTime 同步等待服务注册中心( ConfigServer )推送服务提供者地址的时间(单位 ms) int 3000
      proxyStyle 代理方式(JDK 或 Javassist) String jdk
      futureMethods 设置调用此服务时需要采用异步调用的方法名列表以及异步调用的方式,默认为空,即所有方法都采用同步调用 String[]
      consistent 负载均衡是否使用一致性哈希 String
      methodSpecials 配置方法级的超时时间、重试次数、方法名称 com.alibaba.boot.hsf.annotation.HSFConsumer.ConsumerMethodSpecial[]
      表 4. 服务提供者和消费者全局配置参数列表
      属性 是否必配 描述 类型 默认值
      spring.hsf.version 服务的全局版本号,还可以使用 spring.hsf.versions.<完整的服务接口名>=<单独为该服务接口设置的版本号,String类型>,例如spring.hsf.versions.com.aliware.edas.EchoService="1.0.0"为具体某个服务设置版本号。 String 1.0.0.DAILY
      spring.hsf.group 服务的全局组名,还可以使用spring.hsf.groups.<完整的服务接口名>=<单独为该服务接口设置的组名,String类型>为具体某个服务设置组名。 String HSF
      spring.hsf.timeout 服务的全局超时时间,还可以使用spring.hsf.timeouts.<完整的服务接口名>=<超时时间,String类型>为具体某个服务设置超时时间。 Integer
      spring.hsf.max-wait-address-time 同步等待服务注册中心( ConfigServer )推送服务提供者地址的全局时间(单位 ms ),还可以使用spring.hsf.max-wait-address-times.<完整的服务接口名>=<等待时间,String类型>为具体某个服务设置的等待服务注册中心(ConfigServer)推送服务提供者地址的时间。 Integer 3000
      spring.hsf.delay-publish 服务延迟发布的全局开关,”true” or “false”,还可以使用spring.hsf.delay-publishes.<完整的服务接口名>=<是否延迟发布,String类型>为具体某个服务设置是否延迟。 String
      spring.hsf.core-pool-size 服务的全局最小活跃线程数,还可以使用spring.hsf.core-pool-sizes.<完整的服务接口名>=<最小活跃线程数,String类型>单独为某服务设置最小活跃线程数。 int
      spring.hsf.max-pool-size 服务的全局最大活跃线程数,还可以使用spring.hsf.max-pool-sizes.<完整的服务接口名>=<最大活跃线程数,String类型>单独为某服务设置最大活跃线程数。 int
      spring.hsf.serialize-type 服务的全局序列化类型,Hessian 或者 Java,还可以使用spring.hsf.serialize-types.<完整的服务接口名>=<序列化类型>单独为某服务设置序列化类型。 String
      说明 全局配置参数可在 Pandora Boot 应用的 application.properties 文件中设置。

本地开发调试。

  1. 配置轻量级配置及注册中心。
    本地开发调试时,需要使用轻量级配置及注册中心,轻量级配置及注册中心包含了服务注册发现服务端的轻量版,详情请参见启动轻量级配置及注册中心
  2. 启动应用。
    • 在 IDE 中启动

      通过 VM options 配置启动参数 -Djmenv.tbsite.net={$IP},通过 main 方法直接启动。其中 {$IP} 为轻量配置中心的 IP 地址。列如本机启动轻量配置中心,则 {$IP}127.0.0.1

      您也可以不配置 JVM 的参数,而是直接通过修改 hosts 文件将 jmenv.tbsite.net 绑定为轻量配置中心的 IP。详情请参见启动轻量级配置及注册中心

    • 通过 FatJar 启动
      1. 增加 taobao-hsf.sar 依赖,这样会下载到我们需要的依赖:/.m2/com/taobao/pandora/taobao-hsf.sar/2019-06-stable/taobao-hsf.sar-2019-06-stable.jar,在后面的启动参数中依赖它。
           <dependency>
                <groupId>com.taobao.pandora</groupId>
                <artifactId>taobao-hsf.sar</artifactId>
                <version>2019-06-stable</version>
            </dependency>
      2. 使用 Maven 将 Pandora Boot 工程打包成 FatJar, 需要在 pom.xml 中添加如下插件。为避免与其他打包插件发生冲突,请勿在 build 的 plugin 中添加其他 FatJar 插件。
            <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. 添加完插件后,在工程的主目录下,执行 maven 命令 mvn clean package 进行打包,即可在 Target 目录下找到打包好的 FatJar 文件。
      4. 通过 Java 命令启动应用。
        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
        说明 -Dpandora.location 指定的路径必须是全路径,使用命令行启动时,必须显示指定 taobao-hsf.sar 的位置。

    访问 consumer 所在机器的地址,可以触发 consumer 远程调用 provider。

    curl localhost:8080/hsf-echo/helloworld
    helloworld

单元测试。

Pandora Boot 的单元测试可以通过 PandoraBootRunner 启动,并与 SpringJUnit4ClassRunner 无缝集成。

我们将演示一下如何在服务提供者中进行单元测试,供大家参考。

  1. 在 Maven 中添加 Pandora Boot 和 Spring Boot 测试必要的依赖。
      <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. 编写测试类的代码。
    @RunWith(PandoraBootRunner.class)
      @DelegateTo(SpringJUnit4ClassRunner.class)
      // 加载测试需要的类,一定要加入 Spring Boot 的启动类,其次需要加入本类。
      @SpringBootTest(classes = {HSFProviderApplication.class, HelloServiceTest.class })
      @Component
      public class HelloServiceTest {
    
          /**
           * 当使用 @HSFConsumer 时,一定要在 @SpringBootTest 类加载中,加载本类,通过本类来注入对象,否则当做泛化时,会出现类转换异常。
           */
          @HSFConsumer(generic = true)
          HelloService helloService;
    
          //普通的调用
          @Test
          public void testInvoke() {
              TestCase.assertEquals("hello world", helloService.echo("hello world"));
          }
          //泛化调用
          @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);
          }
          //返回值 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(""));
          }
      }