第一个Thrift程序

Thrift是一个rpc框架,也是一个不错的序列化框架,支持多种语言,此处我使用的是Java。

1、当然开始之前需要下载thrift到本地,我这里下载的是最新的0.12.0版本,并设置好环境变量

2、编写IDL

##命令空间
namespace  java com.dxy.learn_netty.thrift

##为了书写习惯,将thrift的类型映射到java中的类型
typedef i16 short
typedef i32 int
typedef i64 long
typedef bool boolean

##编写一个结构体,用于构造数据,进行传输
struct Person{
 1:optional string name,
 2:optional int age,
 3:optional boolean married
}

##定义服务接口,两个方法
service PersonService{

  Person getPersonByName (1:required string name),
  void savePerson(1:required Person person)

}

3、用thrift编译上面编写的IDL文件,命令非常简单,language是语言,这里当然是java了,Thrift filename是IDL文件名xxx.thrift

thrift --gen <language> <Thrift filename>

4、上面的IDL文件经过编译会生成两个java文件,分别是Person.java和PersonService.java,很好理解,一个是对象,一个是服务接口,然后将两个文件拷到项目对应的目录下即可。生成的代码出了一点问题,两个java文件中@Override标识的地方全部报错,提示父类中没有对应方法,不需要添加该注解,在网上也没找到出问题的具体原因,便将所有的@Override都去掉就好了。

5、在项目的pom.xml中引入相应的thrift依赖包

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

6、实现thrift自动生成出来的接口服务PersonService

package com.dxy.learn_netty.thrift;

import org.apache.thrift.TException;

public class PersonServiceImpl implements PersonService.Iface{

	
	public Person getPersonByName(String name) throws TException {
		Person person = new Person();
		person.setName(name);
		person.setAge(20);
		person.setMarried(false);
		return person;
	}

	public void savePerson(Person person) throws TException {
		System.out.println(person.getName());
		System.out.println(person.getAge());
		System.out.println(person.isMarried());
	}

}

7、编写服务端代码

package com.dxy.learn_netty.thrift;

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;

public class ThriftTestServer {
	
	public static void main(String[] args) throws Exception{
		//实例化一个非阻塞的ServerSocket,绑定端口号
		TNonblockingServerSocket serverSocket = new TNonblockingServerSocket(8899);
		//实例化一个半同步半异步服务下的参数对象,设置最小工作线程数为3最大线程数5
		THsHaServer.Args arg = new THsHaServer.Args(serverSocket).minWorkerThreads(3).maxWorkerThreads(5);
		//构造自定义的服务的Processor
		PersonService.Processor<PersonServiceImpl> processor
		               = new PersonService.Processor<PersonServiceImpl>(new PersonServiceImpl());
		
		//在上面构造的参数中设置传输协议是压缩协议
		arg.protocolFactory(new TCompactProtocol.Factory());
		//设置传输方式为TFramedTransport
		arg.transportFactory(new TFramedTransport.Factory());
		//设置服务的Processor
		arg.processorFactory(new TProcessorFactory(processor));
		
		//构造一个半同步半异步
		TServer server = new THsHaServer(arg);
		System.out.println("Thrift Server Started......");
		//启动服务,开启监听
		server.serve();
	}

}

8、编写客户端代码

package com.dxy.learn_netty.thrift;

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;

public class ThriftTestClient {
	
	public static void main(String[] args) throws Exception{
		
		//跟服务端保持一致,使用TFramedTransport传输方式
		TTransport transport = new TFramedTransport(new TSocket("localhost", 8899),600);
		//跟服务端保持一致,使用压缩协议
		TProtocol protocol = new TCompactProtocol(transport);
		//构造一个客户端实例
		PersonService.Client client = new PersonService.Client(protocol);
		
		try {
			//开服务,进行远程连接
			//这个方法会上面定义的TFramedTransport中去,然后再调用到参数TSocket中,并new一个Socket出来
			transport.open();
			Person person = client.getPersonByName("孙悟空");
			System.out.println(person.name);
			System.out.println(person.age);
			System.out.println(person.married);
			
			System.out.println("---------------");
			
			
			Person person2 = new Person();
			person2.setName("猪八戒");
			person2.setAge(40);
			person2.setMarried(true);
			
			client.savePerson(person2);
			
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			//关闭客户端连接
			transport.close();
		}
	}

}

客户端代码中的transport.open();看了一下源码,最后调用到了第一行代码中new出来的TSocket中去了,源码如下

  /**
   * Connects the socket, creating a new socket object if necessary.
   */
  public void open() throws TTransportException {
    if (isOpen()) {
      throw new TTransportException(TTransportException.ALREADY_OPEN, "Socket already connected.");
    }

    if (host_ == null || host_.length() == 0) {
      throw new TTransportException(TTransportException.NOT_OPEN, "Cannot open null host.");
    }
    if (port_ <= 0 || port_ > 65535) {
      throw new TTransportException(TTransportException.NOT_OPEN, "Invalid port " + port_);
    }

    if (socket_ == null) {
      initSocket();
    }

    try {
      socket_.connect(new InetSocketAddress(host_, port_), connectTimeout_);
      inputStream_ = new BufferedInputStream(socket_.getInputStream());
      outputStream_ = new BufferedOutputStream(socket_.getOutputStream());
    } catch (IOException iox) {
      close();
      throw new TTransportException(TTransportException.NOT_OPEN, iox);
    }
  }


  /**
   * Initializes the socket object
   */
  private void initSocket() {
    socket_ = new Socket();
    try {
      socket_.setSoLinger(false, 0);
      socket_.setTcpNoDelay(true);
      socket_.setKeepAlive(true);
      socket_.setSoTimeout(socketTimeout_);
    } catch (SocketException sx) {
      LOGGER.error("Could not configure socket.", sx);
    }
  }

非常明显,这里实例化了一个Socket对象设置了一些参数,然后进行了connect()连接之后获取了BufferedInputStream和BufferedOutputStream对象,接下来应该就是用流对象进行数据传输了吧。

9、分别启动服务端和客户端代码

服务端输出:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Thrift Server Started......
猪八戒
40
true

客户端输出:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
孙悟空
20
false
---------------

到此,第一个Thrift程序就算完成了,初次接触Thrift,给我的感觉是非常像HttpClient的使用,很轻量级,挺好用,还需要花时间继续深入学习,如有任何问题还请各位看官指正,谢谢!

本文代码参考了张龙老师的课程,在此表示感谢。

上一篇:java – 服务器/客户端之间的文件传输


下一篇:java – 使用TFramedTransport时的TTransportException