基于 Thrift + Spring Boot 的微服务开发

整个项目源码参见 https://github.com/henryhyn/thrift-server

先决条件

安装 thrift

$ brew install thrift
$ thrift -version
Thrift version 0.11.0

如果需要安装 0.10.0, 请执行如下命令

brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/16ebe5f1843e6cb54856311ff0f676be53007329/Formula/thrift.rb

创建 Maven 项目

创建 Maven 项目 thrift-server, 包含两个模块 thrift-apithrift-service, 前者定义 API 接口, 后者是具体的实现.

父项目

父项目 thrift-server 的 POM 文件定义

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.9.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>thrift-server</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>

<modules>
  <module>thrift-api</module>
  <module>thrift-service</module>
</modules>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.6.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>

接口子项目

API 接口子项目 thrift-api 的 POM 文件主要定义

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.thrift</groupId>
      <artifactId>thrift-maven-plugin</artifactId>
      <version>0.10.0</version>
      <configuration>
        <thriftExecutable>/usr/local/bin/thrift</thriftExecutable>
      </configuration>
      <executions>
        <execution>
          <id>thrift-sources</id>
          <phase>generate-sources</phase>
          <goals>
            <goal>compile</goal>
          </goals>
        </execution>
        <execution>
          <id>thrift-test-sources</id>
          <phase>generate-test-sources</phase>
          <goals>
            <goal>testCompile</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

<dependencies>
  <dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.11.0</version>
  </dependency>
</dependencies>

注意, 这里使用的是 thrift-maven-plugin 插件, 而不是 maven-thrift-plugin, 该插件将在编译之前, 将 thrift 文件生成 Java 代码.

新建文件 thrift-api/src/main/thrift/service.thrift, 内容如下

namespace java com.example.thrift.api
service HelloService {
    string greet(1:string name)
}

实现子项目

服务实现子项目 thrift-service 的 POM 文件主要定义

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

<dependencies>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
  </dependency>
  <dependency>
    <groupId>com.example</groupId>
    <artifactId>thrift-api</artifactId>
    <version>1.0-SNAPSHOT</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
  </dependency>
</dependencies>

新建入口类文件 thrift-service/src/main/java/com/example/Application.java, 内容如下

@Slf4j
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

创建 HelloService.Iface 的实现类 com.example.service.HelloServiceImpl, 内容如下

@Service("helloService")
public class HelloServiceImpl implements HelloService.Iface {
    @Override
    public String greet(String para) throws TException {
        return String.format("Hello %s!", para);
    }
}

编写抽象测试类 com.example.AbstractTest, 内容如下

@RunWith(SpringRunner.class)
@SpringBootTest
public abstract class AbstractTest {
}

创建服务测试类 com.example.service.HelloServiceTest, 首先测试本地调用

@Autowired
private HelloService.Iface helloService;

@Test
public void testLocal() {
    try {
        log.info("本地调用服务...{}", helloService.greet("Local"));
    } catch (TException e) {
        log.error("本地调用异常.", e);
    }
}

执行测试输出 本地调用服务...Hello Local!, 说明接口实现没有问题.

远程调用

服务端

Application.javamain 方法中增加如下逻辑

try {
    TProcessor tprocessor = new HelloService.Processor<>(new HelloServiceImpl());
    TServerSocket serverTransport = new TServerSocket(9898);
    TServer.Args tArgs = new TServer.Args(serverTransport);
    tArgs.processor(tprocessor);
    tArgs.protocolFactory(new TBinaryProtocol.Factory());
    TServer server = new TSimpleServer(tArgs);
    server.serve();
    log.info("服务端开启....");
} catch (TTransportException e) {
    log.error("服务端开启异常.", e);
}

将项目构建打包 mvn clean package, 将生成的 thrift-service-1.0-SNAPSHOT.jar 可执行 jar 包移到项目以外, 比如 /tmp 目录, 然后用 java -jar xxx.jar 启动服务端.

客户端

在测试类 HelloServiceTest 中增加远程调用测试

@Test
public void testRemote() {
    try (TTransport transport = new TSocket("localhost", 9898, 30000)) {
        TProtocol protocol = new TBinaryProtocol(transport);
        HelloService.Client helloService = new HelloService.Client(protocol);
        transport.open();
        log.info("远程调用服务...{}", helloService.greet("Remote"));
    } catch (TException e) {
        log.error("远程调用异常.", e);
    }
}

执行测试输出 远程调用服务...Hello Remote!, 说明远程调用服务也没有问题. 以上我们就构建了一个简单的单线程微服务模型.

参考文献