Thrift基础(一)Hello World

Ggaomiss 2015-03-22

昨天写的 thrift 基础的例子


1、thrift 是什么:

thrift 是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。thrift 最初由 facebook 开发,07年四月开放源码,08年5月进入apache孵化器。(这段话是百度的
现在的官方网站:http://thrift.apache.org/ 最新版0.9.2


2、开发简单的例子
实际上thrift就是生成一个各个语言下都能通用的Entity类,Service接口和通讯协议。
首先要到官网上下载thrift的可执行程序,用于生成上边写的Entity和Service接口。
下载地址:http://thrift.apache.org/download
下载 Thrift compiler 即可,我是 windows 直接改名称为 thrift.exe 放到了系统的 windows 目录下。所以到时候命令行直接执行命令即可(懒得配单独的环境变量啊)。

开发:
使用的是Java
先是maven的配置

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

 
PS:话说才发现 storm 里也有 thrift 的包不过包名怎么都改成了 org.apache.thrift7 了。

首先是Entity类和Service接口:
随便找个文本文件管理器比如Notepad++或者SublimeText之类的写一个文件,文件名helloworld.thrift
内容:

namespace java com.nanxiaoqiang.test.thrift.demo1// java下的包路径
// 生成方式 bash:thrift -r -gen java helloworld.thrift
struct Helloworld{
    1:i32 id;
    2:string name;
}

service HelloworldService{
    bool insertHelloworld(1:Helloworld helloworld),
    bool removeHelloworld(1:i32 id),
    Helloworld getHelloworld(1:i32 id)
}

 
然后命令行下 thrift -r -gen java helloworld.thrift 即可生成两个文件:Helloworld.javaHelloworldService.java

PS:关于这个IDL类型的文件到底应当怎么写,下边的是参考文档:

http://thrift.apache.org/docs/types 官方文档

http://wiki.apache.org/thrift/ Wiki百科

http://www.cnblogs.com/tianhuilove/archive/2011/09/05/2167669.html    tianhuilove的博客


接着是具体Service的实现,模拟了一个类似于CURD的方法内容

package com.nanxiaoqiang.test.thrift.demo1;

import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.thrift.TException;

import com.nanxiaoqiang.test.thrift.demo1.HelloworldService.Iface;

/**
 * Handler 数据处理接口
 *
 * @author nanxiaoqiang
 *
 * @version 0.1
 *
 * @since 2015年3月21日
 *
 */
public class HelloworldServiceImpl implements Iface {

    private static Logger logger = LogManager
            .getLogger(HelloworldServiceImpl.class.getName());

    ConcurrentHashMap<Integer, Helloworld> map = new ConcurrentHashMap<>();

    public HelloworldServiceImpl() {
        Helloworld h = new Helloworld();
        h.setId(0);
        h.setName("NULL");
        map.put(h.getId(), h);
        logger.info("constructor HelloworldServiceImpl:add new Helloworld,now map size is "
                + map.size());
    }

    @Override
    public boolean insertHelloworld(Helloworld helloworld) throws TException {
        if (helloworld == null || helloworld.getId() == 0) {
            logger.info("error object of Helloworld. map size is " + map.size());
            return false;
        } else {
            logger.info(ToStringBuilder.reflectionToString(helloworld,
                    ToStringStyle.MULTI_LINE_STYLE));
            map.put(helloworld.getId(), helloworld);
            logger.info("insert complete. map size is " + map.size());
            return true;
        }
    }

    @Override
    public boolean removeHelloworld(int id) throws TException {
        if (id == 0) {
            logger.info("can not remove with id 0. map size is " + map.size());
            return false;
        } else if (map.containsKey(id)) {
            logger.info(ToStringBuilder.reflectionToString(map.get(id),
                    ToStringStyle.MULTI_LINE_STYLE));
            map.remove(id);
            logger.info("removed complete with id " + id + ". map size is "
                    + map.size());
            return true;
        } else {
            logger.info("can not find object with id " + id + ". map size is "
                    + map.size());
            return false;
        }
    }

    @Override
    public Helloworld getHelloworld(int id) throws TException {
        if (map.containsKey(id)) {
            Helloworld h = map.get(id);
            logger.info(ToStringBuilder.reflectionToString(h,
                    ToStringStyle.MULTI_LINE_STYLE));
            return h;
        } else {
            logger.info("can not find object with id " + id + ". map size is "
                    + map.size());
            return null;
        }
    }

}

 

接着是 Server 端:

package com.nanxiaoqiang.test.thrift.demo1;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;

/**
 * 一个基本的Thrift的例子,Server 服务模型,线程安全
 * 
 * @author nanxiaoqiang
 * 
 * @version 0.1
 * 
 * @since 2015年3月21日
 *
 */
public class Server {
	private static Logger logger = LogManager.getLogger(Server.class.getName());

	public static final int SERVER_PORT = 9999;

	public Server() {
	}

	public void startServer() {
		logger.info("准备启动TProcessor");
		TProcessor tprocessor = new HelloworldService.Processor<HelloworldService.Iface>(
				new HelloworldServiceImpl());

		// 简单的单线程服务模型,一般用于测试
		try {
			// 支持的服务模型
			// TSimpleServer – 简单的单线程服务模型,常用于测试
			// TThreadedServer - 多线程服务模型,使用阻塞式IO,每个请求创建一个线程。
			// TThreadPoolServer – 线程池服务模型,使用标准的阻塞式IO,预先创建一组线程处理请求。
			// TNonblockingServer – 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式)
			// 处理大量更新的话,主要是在TThreadedServer和TNonblockingServer中进行选择。
			// TNonblockingServer能够使用少量线程处理大量并发连接,但是延迟较高;
			// TThreadedServer的延迟较低。
			// 实际中,TThreadedServer的吞吐量可能会比TNonblockingServer高,
			// 但是TThreadedServer的CPU占用要比TNonblockingServer高很多。
			TServerSocket serverTransport = new TServerSocket(SERVER_PORT);

			TServer.Args tArgs = new TServer.Args(serverTransport);

			tArgs.processor(tprocessor);

			// 支持的传输格式
			// TBinaryProtocol – 二进制格式.
			// TCompactProtocol – 压缩格式
			// TJSONProtocol – JSON格式
			// TSimpleJSONProtocol –提供JSON只写协议, 生成的文件很容易通过脚本语言解析。
			// TDebugProtocol – 使用易懂的可读的文本格式,以便于debug
			tArgs.protocolFactory(new TBinaryProtocol.Factory());// 传输格式,二进制
			// tArgs.protocolFactory(new TCompactProtocol.Factory()); // 传输格式,压缩
			// tArgs.protocolFactory(new TJSONProtocol.Factory());// 传输格式,JSON
			TServer server = new TSimpleServer(tArgs);
			server.serve();
			logger.info("启动完成.");

		} catch (TTransportException e) {
			logger.error(e.getMessage());
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		Server server = new Server();
		server.startServer();
	}

}

 

Client 端:

package com.nanxiaoqiang.test.thrift.demo1;

import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

public class Client {

    private static Logger logger = LogManager.getLogger(Client.class.getName());

    public static final String SERVER_IP = "localhost";
    public static final int SERVER_PORT = 9999;
    public static final int TIMEOUT = 30000;// 超时

    public Client() {
    }

    public void startclient() {
        // 支持的通信方式(数据传输方式)(Transport)
        // TFileTransport:文件(日志)传输类,允许client将文件传给server,允许server将收到的数据写到文件中。
        // THttpTransport:采用Http传输协议进行数据传输
        // TSocket:采用TCP Socket进行数据传输
        // TZlibTransport:压缩后对数据进行传输,或者将收到的数据解压
        // 下面几个类主要是对上面几个类地装饰(采用了装饰模式),以提高传输效率。
        // TBufferedTransport:对某个Transport对象操作的数据进行buffer,即从buffer中读取数据进行传输,或者将数据直接写入buffer
        // TFramedTransport:以frame为单位进行传输,非阻塞式服务中使用。同TBufferedTransport类似,也会对相关数据进行buffer,同时,它支持定长数据发送和接收。
        // TMemoryBuffer:从一个缓冲区中读写数据
        TTransport transport = new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT);
        // 协议要和服务端一致
        TProtocol protocol = new TBinaryProtocol(transport);
        // TProtocol protocol = new TCompactProtocol(transport);
        // TProtocol protocol = new TJSONProtocol(transport);
        HelloworldService.Client client = new HelloworldService.Client(protocol);

        try {
            transport.open();

            // 测试1
            Helloworld h1 = null;
            boolean b1 = client.insertHelloworld(h1);
            logger.info("传输一个null值进行insertHelloworld方法,返回结果:" + b1);
            TimeUnit.SECONDS.sleep(1);

            // 测试2
            Helloworld h2 = new Helloworld();
            h2.setId(1);
            h2.setName("Hello World!Thrift!");
            boolean b2 = client.insertHelloworld(h2);
            logger.info("传输一个Helloworld对象进行insertHelloworld方法,返回结果:" + b2);
            TimeUnit.SECONDS.sleep(1);

            // 测试3
            Helloworld h3 = client.getHelloworld(0);
            logger.info("通过方法getHelloworld得到id为0的Helloworld对象,返回结果:"
                    + ToStringBuilder.reflectionToString(h3,
                            ToStringStyle.MULTI_LINE_STYLE));
            TimeUnit.SECONDS.sleep(1);

            // 测试4
            Helloworld h4 = client.getHelloworld(1);
            logger.info("通过方法getHelloworld得到id为1的Helloworld对象,返回结果:"
                    + ToStringBuilder.reflectionToString(h4,
                            ToStringStyle.MULTI_LINE_STYLE));
            TimeUnit.SECONDS.sleep(1);

            // 测试5
            // 此段会报错!不能返回null对象
            // 报错内容:org.apache.thrift.TApplicationException: getHelloworld failed: unknown result
            // Helloworld h5 = client.getHelloworld(2);
            // logger.info("通过方法getHelloworld得到id为2的Helloworld对象,返回结果:"
            // + ToStringBuilder.reflectionToString(h5,
            // ToStringStyle.MULTI_LINE_STYLE));
            // TimeUnit.SECONDS.sleep(1);

            // 测试6
            boolean b6 = client.removeHelloworld(0);
            logger.info("通过方法removeHelloworld删除id为0的Helloworld对象,返回结果:" + b6);
            TimeUnit.SECONDS.sleep(1);

            // 测试7
            boolean b7 = client.removeHelloworld(1);
            logger.info("通过方法removeHelloworld删除id为1的Helloworld对象,返回结果:" + b7);
            TimeUnit.SECONDS.sleep(1);

            // 测试8
            boolean b8 = client.removeHelloworld(2);
            logger.info("通过方法removeHelloworld删除id为2的Helloworld对象,返回结果:" + b8);
            TimeUnit.SECONDS.sleep(1);
           
        } catch (TTransportException e) {
            logger.error(e.getMessage());
            e.printStackTrace();
        } catch (TException e) {
            logger.error(e.getMessage());
            e.printStackTrace();
        } catch (InterruptedException e) {
            logger.error(e.getMessage());
            e.printStackTrace();
        } finally {
            if (transport != null)
                transport.close();
            logger.info("transport关闭");
        }

    }

    public static void main(String[] args) {
        Client c = new Client();
        c.startclient();
    }

}

然后分别运行Server和Client即可返回执行结果。

PS:

  1. 因为是测试,所以用的是单线程的Server,实际上最好用TNonblockingServer。
  2. null值是不能返回做交互的。会抛出org.apache.thrift.TApplicationException: getHelloworld failed: unknown result。

参考(受益良多):
http://www.cnblogs.com/mumuxinfei/p/3873709.html             Thrift 个人实战--初次体验Thrift
http://blog.csdn.net/amuseme_lu/article/details/6262572     Apache Thrift的简单使用
http://gemantic.iteye.com/blog/1199214                               thrift的使用介绍





相关推荐