安装Thrift
在官网下载编译器:https://thrift.apache.org/download
我本机是windows,下载后的文件解压出 thrift-0.13.0.exe,重命名为:thrift.exe,随便放在一个路径下即可。
配置系统变量Path:
测试安装是否成功:
.thrift 文件编写
《Apache Thrift学习(1):Thrift详细介绍》中介绍了 .thrift 文件的构成,这里不在赘述,详细的文档请大家查阅官网。
//java的包名
namespace java com.leolee.thrift.person
//定义数据类型的别名
typedef i16 short
typedef i32 int
typedef i64 long
typedef bool boolean
typedef string String
struct Person {
1: optional String name,
2: optional int age,
3: optional boolean married
}
//自定义异常
exception DataException {
1: optional String msg,
2: optional String callStack,
3: optional String data
}
//接口
service PersonService {
Person getPersonByName(1: required String name) throws (1: DataException dataException),
void savePerson(1: required Person person) throws (1: DataException dataException),
}
这里需要注意的是Thrift支持使用 typedef 定义类型的别名,也必须要有字段的标识符如:”1:“。在序列化数据传输中用于标识字段的属性。
生成Java code
在对应目录生成代码:
thrift --gen java src/thrift/data.thrift
该命令会通过相对路径的 .thrift 文件在当前目录下生成Java code。
生成了DataException(自定义异常)、Person(struct对应的java对象)、PersonService(业务接口层)
将生成Java文件复制到项目中,并引入项目的Thrift相关依赖:
编写客户端和服务端代码
首先完成PersonService的实现类:
package com.leolee.thrift.person.serviceImpl;
import com.leolee.thrift.person.DataException;
import com.leolee.thrift.person.Person;
import com.leolee.thrift.person.PersonService;
import org.apache.thrift.TException;
/**
* @ClassName PersonServiceImpl
* @Description: 实现在data.thrift中定义的接口
* @Author LeoLee
* @Date 2020/9/6
* @Version V1.0
**/
public class PersonServiceImpl implements PersonService.Iface {
@Override
public Person getPersonByName(String name) throws DataException, TException {//此处
System.out.println("Got client param:" + name);
Person person = new Person();
person.setAge(25);
person.setName(name);
person.setMarried(false);
System.out.println("getPersonByName调用成功");
return person;
}
@Override
public void savePerson(Person person) throws DataException, TException {
System.out.println(person.getName());
System.out.println(person.getAge());
System.out.println(person.isMarried());
System.out.println("savePerson调用成功");
}
}
服务端:
package com.leolee.thrift.person;
import com.leolee.thrift.person.serviceImpl.PersonServiceImpl;
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TTransportException;
/**
* @ClassName ThriftServer
* @Description: thrift服务端
* @Author LeoLee
* @Date 2020/9/6
* @Version V1.0
**/
public class ThriftServer {
public static void main(String[] args) throws TTransportException {
TNonblockingServerSocket serverSocket = new TNonblockingServerSocket(8899);//socket通道对象创建
THsHaServer.Args arg = new THsHaServer.Args(serverSocket).minWorkerThreads(2).maxWorkerThreads(4);
PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());
//工厂
arg.protocolFactory(new TCompactProtocol.Factory());//协议层工厂对象,该对象会将二进制字节码,最大限度的压缩
arg.transportFactory(new TFramedTransport.Factory());//构建Framed传输层工厂对象
arg.processorFactory(new TProcessorFactory(processor));
TServer server = new THsHaServer(arg);
System.out.println("Thrift server started");
//死循环,源源不断的接受socket中传递的请求
server.serve();
}
}
客户端:
package com.leolee.thrift.person;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
/**
* @ClassName ThriftClient
* @Description: Thrift 客户端
* @Author LeoLee
* @Date 2020/9/6
* @Version V1.0
**/
public class ThriftClient {
public static void main(String[] args) {
//服务端transport用的是TFramedTransport,客户端也要用TFramedTransport
TTransport transport = new TFramedTransport(new TSocket("127.0.0.1", 8899), 60);//对应服务端的传输层方法[TFramed]
TProtocol protocol = new TCompactProtocol(transport);//对应服务端的协议层方法[TCompact]
PersonService.Client client = new PersonService.Client(protocol);
try {
//打开连接
transport.open();
Person person = client.getPersonByName("LeoLee");
System.out.println("打印getPersonByName请求结果:");
System.out.println(person.getName());
System.out.println(person.getAge());
System.out.println(person.isMarried());
System.out.println("------------------------");
Person person2 = new Person();
person2.setName("LeoLee");
person2.setAge(25);
person2.setMarried(false);
client.savePerson(person2);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
transport.close();
}
}
}
可以看到,与Netty的Socket通讯很相似:
- 服务端绑定了端口号(8899)
- 设置服务端通道通信参数并添加Service层接口
THsHaServer.Args arg = new THsHaServer.Args(serverSocket).minWorkerThreads(2).maxWorkerThreads(4);
PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());
//工厂
arg.protocolFactory(new TCompactProtocol.Factory());
arg.transportFactory(new TFramedTransport.Factory());
arg.processorFactory(new TProcessorFactory(processor));
TServer server = new THsHaServer(arg);
- 客户端设置对应服务端连接信息以及建立通道
TTransport transport = new TFramedTransport(new TSocket("127.0.0.1", 8899), 60);
TProtocol protocol = new TCompactProtocol(transport);
PersonService.Client client = new PersonService.Client(protocol);
transport.open();
运行测试
依次启动服务端和客户端
服务端:
客户端:
需要代码的来这里拿嗷:demo项目地址