Thrift 环境搭建及 Java Demo 运行

2021年08月26日 19:32 · 阅读(572) ·

开发环境

名称 版本
操作系统 Windows 10 X64
JDK JDK1.8(jdk-8u151-windows-x64)
IntelliJ IDEA IntelliJ IDEA 2021.2.1
Maven Maven 3.6.0
Thtift Thtift 0.9.3

参考

windows下thrift的安装(一)

Thrift简明教程

Thrift 入门教程

【maven】maven-thrift-plugin 插件使用 + Java项目中thrift使用的正确姿势

Thrift 简介

Thrift 最初由 Facebook 开发的,后来提交给了 Apache 基金会将 Thrift 作为一个开源项目。

当时 Facebook 开发使用它是为了解决系统中各系统间大数据量的传输通信以及系统之间语言环境不同需要跨平台的特性。

所以 Thrift 是支持跨语言,比如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml 都支持。

Thrift 是一个典型的 CS 结构,客户端和服务端可以使用不同的语言开发。

既然客户端和服务端能使用不同的语言开发,那么一定就要有一种中间语言来关联客户端和服务端的语言,没错,这种语言就是 IDL(Interface Description Language)。

Thrift 通过一个中间语言 IDL(接口定义语言)来定义 RPC 的数据类型和接口。这些内容写在以

这些内容写在以 .thrift 结尾的文件中,然后通过特殊的编译器来生成不同语言的代码,以满足不同需要的开发者。

比如 Java 开发者,就可以生成 Java 代码,C++开发者可以生成 C++ 代码,生成的代码中不但包含目标语言的接口定义,方法,数据类型,还包含有 RPC 协议层和传输层的实现代码。

● 在最上层是用户自行实现的业务逻辑代码。
● 第二层是由 Thrift 编译器自动生成的代码,主要用于结构化数据的解析,发送和接收。
● TServer 主要任务是高效的接受客户端请求,并将请求转发给 Processor 处理。
● Processor 负责对客户端的请求做出响应,包括 RPC 请求转发,调用参数解析和用户逻辑调用,返回值写回等处理。
● 从 TProtocal 以下部分是 Thrift 的传输协议和底层I/O通信。
● TProtocal 是用于数据类型解析的,将结构化数据转化为字节流给 TTransport 进行传输。
● TTransport 是与底层数据传输密切相关的传输层,负责以字节流方式接收和发送消息体,不关注是什么数据类型。
● 底层 IO 负责实际的数据传输,包括 Socket、文件和压缩数据流等。

Thrift 服务开发流程

Thrift 服务开发流程,简单概述如下:

● 定义 IDL 文件(xxx.thrift文件)
● 用 xxx.thrift 文件生成 Java 代码(服务接口文件)
● 服务端实现(创建服务):实现服务接口,开启服务器
● 客户端实现(服务消费):引入接口,进行远程调用

Thrift 下载安装

下载地址

thrift-0.9.3.tar.gz 下载地址:http://archive.apache.org/dist/thrift/0.9.3/
thrift-0.9.3.exe 下载地址:http://archive.apache.org/dist/thrift/0.9.3/

这里我下载 thrift-0.9.3.exe

本地安装
1.拷贝 thrift-0.9.3.exeD:\Program Files\Thtift 目录
2.配置环境变量,计算机-属性-高级系统设置-环境变量(N)...

创建和修改下面两个环境变量

环境变量名称 操作
Thtift_Home D:\Program Files\Thtift 新增
Path 在最后添加变量值 ;%Thtift_Home% 修改

接下来测试 thrift 的环境变量是否安装正确,Ctrl+R 输入 cmd 打开DOS窗口,在窗口输入 thrift-0.9.3 -version
输出 Thrift version 0.9.3,配置成功

  1. C:\WINDOWS\system32>thrift-0.9.3 -version
  2. Thrift version 0.9.3

Thrift Java Demo 案例

Demo 代码

thrift-demo-master.zip

定义一个 IDL 接口描述文件-QueryService.thrift

D:\Program Files\Thtift\QueryService.thrift

  1. namespace java com.demo.thrift.service
  2. service QueryService{
  3. string query(1:string query)
  4. }

这里定义了一个 QueryService 服务,包含一个 query 方法,入参和返回值都是一个 string 类型的参数。

Thrift 生成代码-QueryService.java

控制台进入 D:\Program Files\Thtift,输入 thrift-0.9.3.exe -r -gen java QueryService.thrift

生成后的目录结构如下:

  1. D:\PROGRAM FILES\THTIFT
  2. └─gen-java
  3. └─com
  4. └─demo
  5. └─thrift
  6. └─service
  7. └─QueryService.java

创建 Java 项目

目录结构如下

拷贝 QueryService 到对应路径

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.demo.thrift</groupId>
  6. <artifactId>thrift-demo</artifactId>
  7. <version>1.0-SNAPSHOT</version>
  8. <packaging>war</packaging>
  9. <dependencies>
  10. <dependency>
  11. <groupId>junit</groupId>
  12. <artifactId>junit</artifactId>
  13. <version>4.12</version>
  14. <scope>test</scope>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.apache.thrift</groupId>
  18. <artifactId>libthrift</artifactId>
  19. <version>0.8.0</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.slf4j</groupId>
  23. <artifactId>slf4j-log4j12</artifactId>
  24. <version>1.5.8</version>
  25. </dependency>
  26. </dependencies>
  27. </project>

QueryService 实现类-QueryServiceImpl

  1. package com.demo.thrift.service.impl;
  2. import com.demo.thrift.service.QueryService;
  3. import org.apache.thrift.TException;
  4. public class QueryServiceImpl implements QueryService.Iface {
  5. @Override
  6. public String query(String query) throws TException {
  7. return "Hello," + query;
  8. }
  9. }

服务端和客户端-简单版

服务端-Server

创建服务器端实现代码,将 QueryServiceImpl 作为具体的处理器传递给 Thrift 服务器,代码如下:

  1. package com.demo.thrift;
  2. import com.demo.thrift.service.QueryService;
  3. import com.demo.thrift.service.impl.QueryServiceImpl;
  4. import org.apache.thrift.TProcessor;
  5. import org.apache.thrift.protocol.TBinaryProtocol;
  6. import org.apache.thrift.server.TServer;
  7. import org.apache.thrift.server.TSimpleServer;
  8. import org.apache.thrift.transport.TServerSocket;
  9. public class Server {
  10. public static final int SERVER_PORT = 8888;
  11. public static void main(String[] args) {
  12. try {
  13. System.out.println("Thrify Server TSimple Start ....");
  14. TProcessor tprocessor = new QueryService.Processor<QueryService.Iface>(new QueryServiceImpl());
  15. // 简单的单线程服务模型,一般用于测试
  16. TServerSocket serverTransport = new TServerSocket(SERVER_PORT);
  17. TServer.Args tArgs = new TServer.Args(serverTransport);
  18. tArgs.processor(tprocessor);
  19. tArgs.protocolFactory(new TBinaryProtocol.Factory());
  20. // tArgs.protocolFactory(new TCompactProtocol.Factory());
  21. // tArgs.protocolFactory(new TJSONProtocol.Factory());
  22. TServer server = new TSimpleServer(tArgs);
  23. server.serve();
  24. } catch (Exception e) {
  25. System.out.println("Server Start Error!!!");
  26. e.printStackTrace();
  27. }
  28. }
  29. }

客户端-Client

  1. package com.demo.thrift;
  2. import com.demo.thrift.service.QueryService;
  3. import org.apache.thrift.TException;
  4. import org.apache.thrift.protocol.TBinaryProtocol;
  5. import org.apache.thrift.protocol.TProtocol;
  6. import org.apache.thrift.transport.TSocket;
  7. import org.apache.thrift.transport.TTransport;
  8. import org.apache.thrift.transport.TTransportException;
  9. public class Client {
  10. public static final int SERVER_PORT = 8888;
  11. public static final String SERVER_IP = "localhost";
  12. public static final int TIMEOUT = 30000;
  13. public static void main(String[] args) {
  14. TTransport transport = null;
  15. try {
  16. transport = new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT);
  17. // 协议要和服务端一致
  18. TProtocol protocol = new TBinaryProtocol(transport);
  19. // TProtocol protocol = new TCompactProtocol(transport);
  20. // TProtocol protocol = new TJSONProtocol(transport);
  21. QueryService.Client client = new QueryService.Client(protocol);
  22. transport.open();
  23. String result = client.query("Luoma");
  24. System.out.println("Thrify Client Result = " + result);
  25. } catch (TTransportException e) {
  26. e.printStackTrace();
  27. } catch (TException e) {
  28. e.printStackTrace();
  29. } finally {
  30. if (null != transport) {
  31. transport.close();
  32. }
  33. }
  34. }
  35. }

测试-简单的服务端和客户端

运行 Server 的 main 方法

运行 Client 的 main 方法,可以看到,已经运行成功

服务端-线程池服务模型-ServerThreadPool

线程池服务模型,使用标准的阻塞式 IO,预先创建一组线程处理请求。

这里是 Server 的升级版本。

  1. package com.demo.thrift;
  2. import com.demo.thrift.service.impl.QueryServiceImpl;
  3. import com.demo.thrift.service.QueryService;
  4. import org.apache.thrift.TProcessor;
  5. import org.apache.thrift.protocol.TBinaryProtocol;
  6. import org.apache.thrift.server.TThreadPoolServer;
  7. import org.apache.thrift.transport.TServerSocket;
  8. import org.apache.thrift.transport.TTransportException;
  9. public class ServerThreadPool {
  10. public static final int SERVER_PORT = 8888;
  11. public void startServer(){
  12. try {
  13. System.out.println("Thrify Server Start ... ");
  14. TServerSocket serverTransport = new TServerSocket(SERVER_PORT);
  15. TThreadPoolServer.Args tArgs = new TThreadPoolServer.Args(serverTransport);
  16. TProcessor tProcessor = new QueryService.Processor<QueryService.Iface>(new QueryServiceImpl());
  17. tArgs.processor(tProcessor);
  18. tArgs.protocolFactory(new TBinaryProtocol.Factory());
  19. // 线程池服务模型,使用标准的阻塞式IO,预先创建一组线程处理请求。
  20. TThreadPoolServer server = new TThreadPoolServer(tArgs);
  21. server.serve();
  22. } catch (TTransportException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. public static void main(String[] args){
  27. ServerThreadPool serviceServer = new ServerThreadPool();
  28. serviceServer.startServer();
  29. }
  30. }

测试-线程池服务模型-ServerThreadPool

运行 ServerThreadPool 的 main 方法

运行 Client 的 main 方法,可以看到,已经运行成功

服务端和客户端-非阻塞式 IO,指定 TFramedTransport 数据传输

使用非阻塞式 IO,服务端和客户端需要指定 TFramedTransport 数据传输的方式。

服务端-ServerTNonblocking

  1. package com.demo.thrift;
  2. import com.demo.thrift.service.QueryService;
  3. import com.demo.thrift.service.impl.QueryServiceImpl;
  4. import org.apache.thrift.TProcessor;
  5. import org.apache.thrift.protocol.TBinaryProtocol;
  6. import org.apache.thrift.server.TNonblockingServer;
  7. import org.apache.thrift.transport.TFramedTransport;
  8. import org.apache.thrift.transport.TNonblockingServerSocket;
  9. import org.apache.thrift.transport.TTransportException;
  10. public class ServerTNonblocking {
  11. public static final int SERVER_PORT = 8888;
  12. public void startServer(){
  13. try {
  14. System.out.println("Thrify Server Start ... ");
  15. TNonblockingServerSocket tnbSocket = new TNonblockingServerSocket(SERVER_PORT);
  16. TNonblockingServer.Args tArgs = new TNonblockingServer.Args(tnbSocket);
  17. TProcessor tProcessor = new QueryService.Processor<QueryService.Iface>(new QueryServiceImpl());
  18. tArgs.processor(tProcessor);
  19. tArgs.protocolFactory(new TBinaryProtocol.Factory());
  20. tArgs.transportFactory(new TFramedTransport.Factory());
  21. // 线程池服务模型,使用标准的阻塞式IO,预先创建一组线程处理请求。
  22. TNonblockingServer server = new TNonblockingServer(tArgs);
  23. server.serve();
  24. } catch (TTransportException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. public static void main(String[] args){
  29. ServerTNonblocking serviceServer = new ServerTNonblocking();
  30. serviceServer.startServer();
  31. }
  32. }

客户端-ClientTNonblocking

  1. package com.demo.thrift;
  2. import com.demo.thrift.service.QueryService;
  3. import org.apache.thrift.TException;
  4. import org.apache.thrift.protocol.TBinaryProtocol;
  5. import org.apache.thrift.protocol.TProtocol;
  6. import org.apache.thrift.transport.TFramedTransport;
  7. import org.apache.thrift.transport.TSocket;
  8. import org.apache.thrift.transport.TTransport;
  9. import org.apache.thrift.transport.TTransportException;
  10. public class ClientTNonblocking {
  11. public static final int SERVER_PORT = 8888;
  12. public static final String SERVER_IP = "localhost";
  13. public static final int TIMEOUT = 30000;
  14. public void startClient(String username){
  15. TTransport tTransport = null;
  16. try {
  17. tTransport = new TFramedTransport(new TSocket(SERVER_IP,SERVER_PORT,TIMEOUT));
  18. // 协议要和服务端一致
  19. TProtocol protocol = new TBinaryProtocol(tTransport);
  20. //TProtocol protocol = new TCompactProtocol(tTransport);
  21. //TProtocol protocol = new TJSONProtocol(tTransport);
  22. QueryService.Client client = new QueryService.Client(protocol);
  23. tTransport.open();
  24. String result = client.query(username);
  25. System.out.println("Thrify Client Result = " + result);
  26. } catch (TTransportException ex) {
  27. ex.printStackTrace();
  28. } catch (TException ex) {
  29. ex.printStackTrace();
  30. }finally {
  31. if(tTransport != null){
  32. tTransport.close();
  33. }
  34. }
  35. }
  36. public static void main(String[] args){
  37. ClientTNonblocking client = new ClientTNonblocking();
  38. client.startClient("Luoma");
  39. }
  40. }

测试-指定 TFramedTransport 数据传输

运行 ServerTNonblocking 的 main 方法

运行 ClientTNonblocking 的 main 方法,可以看到,已经运行成功

IDEA 安装配置 Thrift

参考

IDEA中Thrift插件配置

IntelliJ IDEA 2018.3

直接在 IDEA 界面中配置

打开 IDEA 的插件中心,搜索 Thrift 即可安装,但是我的 IDEA 搜索不出来

正常情况应该是这样,不知道是不是和我 IDEA 版本有关系

手动下载 Thrift 插件安装

可以去如下地址下载Thrift插件:https://plugins.jetbrains.com/plugin/7331-thrift-support/versions

然后去 IDEA中 Install plugin from disk... 选择下载的 zip 包安装

报错,试了另外几个压缩包,都不行,看来是 IntelliJ IDEA 2018.3 这个版本太老旧了。

这里我下载当前最新的 IDEA 版本 IntelliJ IDEA 2021.2.1

IntelliJ IDEA 2021.2.1

直接在 IDEA 界面中配置

打开 IDEA 的插件中心,搜索 Thrift 即可安装

安装完重启 IDEA 即可

安装完成的成功标志是 Compiler 中出现了 Thrift 编译器

手动下载 Thrift 插件安装

可以去如下地址下载Thrift插件:https://plugins.jetbrains.com/plugin/7331-thrift-support/versions

然后去 IDEA中 Install plugin from disk... 选择下载的 zip 包安装

安装完重启 IDEA 即可

安装完成的成功标志是 Compiler 中出现了 Thrift 编译器

IDEA 使用 Thrift 插件生成 Java 代码

前面的步骤已经下载安装了 Thrift

这里为了方便,我们把 D:\Program Files\Thtift\thrift-0.9.3.exe 修改为 D:\Program Files\Thtift\thrift.exe

控制台测试

  1. C:\WINDOWS\system32>thrift -version
  2. Thrift version 0.9.3

项目结构

新建一个项目,项目结构如下

QueryService.thrift

  1. namespace java com.demo.thrift.service
  2. service QueryService{
  3. string query(1:string query)
  4. }

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.demo.thrift</groupId>
  6. <artifactId>thrift-demo</artifactId>
  7. <version>1.0-SNAPSHOT</version>
  8. <packaging>war</packaging>
  9. <name>thrift-demo-master</name>
  10. <properties>
  11. <failOnMissingWebXml>false</failOnMissingWebXml>
  12. </properties>
  13. <dependencies>
  14. <dependency>
  15. <groupId>org.apache.thrift</groupId>
  16. <artifactId>libthrift</artifactId>
  17. <version>0.9.2</version>
  18. </dependency>
  19. </dependencies>
  20. <build>
  21. <finalName>${project.name}</finalName>
  22. <plugins>
  23. <plugin>
  24. <groupId>org.apache.thrift.tools</groupId>
  25. <artifactId>maven-thrift-plugin</artifactId>
  26. <version>0.1.11</version>
  27. <configuration>
  28. <!-- 如果thrift执行文件的路径已经设置到PATH环境变量,可以直接简写。否则需要给出完整执行路径 -->
  29. <thriftExecutable>thrift</thriftExecutable>
  30. <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
  31. </configuration>
  32. <executions>
  33. <execution>
  34. <id>thrift-sources</id>
  35. <phase>generate-sources</phase>
  36. <goals>
  37. <goal>compile</goal>
  38. </goals>
  39. </execution>
  40. <execution>
  41. <id>thrift-test-sources</id>
  42. <phase>generate-test-sources</phase>
  43. <goals>
  44. <goal>testCompile</goal>
  45. </goals>
  46. </execution>
  47. </executions>
  48. </plugin>
  49. </plugins>
  50. </build>
  51. </project>

运行编译生成代码

在右侧的 Maven-插件,点击 thrift-thrift:compile

控制台输出

  1. [INFO] Scanning for projects...
  2. [INFO]
  3. [INFO] --------------------< com.demo.thrift:thrift-demo >---------------------
  4. [INFO] Building thrift-demo-master 1.0-SNAPSHOT
  5. [INFO] --------------------------------[ war ]---------------------------------
  6. [INFO]
  7. [INFO] --- maven-thrift-plugin:0.1.11:compile (default-cli) @ thrift-demo ---
  8. [INFO] ------------------------------------------------------------------------
  9. [INFO] BUILD SUCCESS
  10. [INFO] ------------------------------------------------------------------------
  11. [INFO] Total time: 0.993 s
  12. [INFO] Finished at: 2021-09-01T15:25:07+08:00
  13. [INFO] ------------------------------------------------------------------------
  14. 进程已结束,退出代码为 0

再查看项目结构,已经自动生成了项目文件

错误总结

thrift did not exit cleanly. Review output for more information.

  1. [INFO]
  2. [INFO] ------< com.luoma.oa.fm.etms.receipt:luoma-receipt-contract >-------
  3. [INFO] Building luoma-receipt-contract 1.0-SNAPSHOT
  4. [INFO] from pom.xml
  5. [INFO] --------------------------------[ jar ]---------------------------------
  6. [INFO]
  7. [INFO] --- thrift:0.1.11:compile (default-cli) @ luoma-receipt-contract ---
  8. [ERROR] thrift failed output:
  9. [ERROR] thrift failed error: 'thrift' �����ڲ����ⲿ���Ҳ���ǿ����еij���
  10. �����������
  11. thrift did not exit cleanly. Review output for more information.
  • 解决方案

参考:Thrift did not exit cleanly

  • Settings -> Build,Execution,Deployment -> Build Tools -> Maven -> Runner

  • 环境变量设置,找到 PATH

  • 最后添加 Thrift 安装目录 C:\Program Files\Thtift