Apache Thrift学习入门之包含struct结构的thrift文件


=Start=

缘由:

之前在学习Thrift的时候整理了一篇《Apache Thrift学习入门之代码演示》的文章,基本原理以及编写方法里面基本上都有,但是对应的.thrift文件非常简单,在某些场景下可能不太适用,所以,这里把最近用到的包含struct结构的thrift文件及相关样例代码整理一下,方便以后参考。

正文:

参考解答:

假设有一个场景——需要用thrift传map,但是map的key/value类型又不固定,存在String/Integer等多种可能。这里为了演示方便起见,假设key的类型固定为String,value的类型可能为String,也可能为Integer,比如:

paraMap = {
	"methdo": "GET",
	"actionType": "查询",
	"searchId": 123456,
	"status": 200
}

在Java里面可能还比较好处理,new一个Map<String, Object>对象就行了,value随便是什么类型都可以应对。但是Thrift里面是没有Object类型的,所以,对应的.thrift文件该怎么写呢?咨询了别人之后,了解到,可以通过struct来间接实现:

namespace java com.ixyzero.learn.thrift

/**
* 在Thrift中定义 struct 数据结构,类似面向对象编程中的类
* 1、属性需要一个表示数字(如:1,2,3);这个数字在同个作用域内不能重复;
* 2、属性默认都是必输属性,如果需要属性可选在属性前面加上optional关键字;
* 3、可以为属性指定默认值,格式是在属性名称后面用等号。如:num1 =0,0就是num1的默认值;
**/
struct value {
    1: optional i32	intValue;
    2: optional string	strValue;
}

service ParaMapService {
  string sayHello(1:string username)

  void ruleExecute1(1:map<string, string> paramMap)
  void ruleExecute2(1:map<string, i64> paramMap)

  void ruleExecuteX(1:map<string, value> paramMap)
}

通过命令生成代码之后,将对应的ParaMapService.java文件以及value.java文件拷贝到自己的项目对应路径中。并编写实现接口Iface代码:

package com.ixyzero.learn.thrift;

import org.apache.thrift.TException;
import java.util.Map;

public class ParaMapImpl implements ParaMapService.Iface {

    public ParaMapImpl() {
    }

    @Override
    public String sayHello(String username) throws TException {
        return "Hi," + username + " welcome to thrift world";
    }

    @Override
    public void ruleExecute1(Map<String, String> paramMap) throws TException {
        for (String item: paramMap.keySet()) {
            System.out.println("Key: " + item + ",\tValue: " + paramMap.get(item) + ",\tType: " + paramMap.get(item).getClass());
        }
    }

    @Override
    public void ruleExecute2(Map<String, Long> paramMap) throws TException {
        for (String item: paramMap.keySet()) {
            System.out.println("Key: " + item + ",\tValue: " + paramMap.get(item) + ",\tType: " + paramMap.get(item).getClass());
        }
    }

    @Override
    public void ruleExecuteX(Map<String, value> paramMap) throws TException {
        for (String item: paramMap.keySet()) {
            int val = paramMap.get(item).getIntValue();
            String strVal = paramMap.get(item).getStrValue();
            if (val != 0) { // 对于本身值为0的情况会出现误判……
                System.out.print("getIntValue() = " + val + ", ");
            } else {
                System.out.print("getStrValue() = " + strVal + ", ");
            }

            System.out.println("Key: " + item + ",\tValue: " + paramMap.get(item) + ",\tType: " + paramMap.get(item).getClass());
        }
    }

}

再来编写Server端代码,和之前的基本没区别,就是名字不一样:

package com.ixyzero.learn.thrift;

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;

public class ParaMapServer {
    public static final int SERVER_PORT = 8090;

    public void startServer() {
        try {
            System.out.println("ParaMap TSimpleServer start ....");

            TProcessor tprocessor = new ParaMapService.Processor<ParaMapService.Iface>(new ParaMapImpl());

            // 简单的单线程服务模型,一般用于测试
            TServerSocket serverTransport = new TServerSocket(SERVER_PORT);
            TServer.Args tArgs = new TServer.Args(serverTransport);
            tArgs.processor(tprocessor);
            tArgs.protocolFactory(new TBinaryProtocol.Factory());
            // tArgs.protocolFactory(new TCompactProtocol.Factory());
            // tArgs.protocolFactory(new TJSONProtocol.Factory());
            TServer server = new TSimpleServer(tArgs);
            server.serve();

        } catch (Exception e) {
            System.out.println("Server start error!!!");
            e.printStackTrace();
        }
    }

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

}

然后是Client调用端代码:

package com.ixyzero.learn.thrift;

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;

import java.util.HashMap;
import java.util.Map;

public class ParaMapClient {

    public static final String SERVER_IP = "localhost";
    public static final int SERVER_PORT = 8090;
    public static final int TIMEOUT = 30000;

    public void startClient(String userName) {
        TTransport transport = null;
        try {
            transport = new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT);
            // 协议要和服务端一致
            TProtocol protocol = new TBinaryProtocol(transport);
            // TProtocol protocol = new TCompactProtocol(transport);
            // TProtocol protocol = new TJSONProtocol(transport);
            ParaMapService.Client client = new ParaMapService.Client(protocol);
            transport.open();
            long time1 = System.currentTimeMillis();
            String result = client.sayHello(userName);
            long time2 = System.currentTimeMillis();
            System.out.print("spend ");
            System.out.print(time2 - time1);
            System.out.println(" ms to call method client.sayHello()");
            System.out.println("Thrift client result =: " + result);


            Map<String, value> paramMap = new HashMap<>();
            value v = new value();
            v.setStrValue("username"); // 这个方法是自动生成的
            paramMap.put("mis", v); // 往map里put的时候value需要是value类型
            value v2 = new value();
            v2.setIntValue(28);
            paramMap.put("count", v2);
            value v3 = new value();
            v3.setStrValue("28");
            paramMap.put("count_str", v3);
            value v4 = new value();
            v4.setIntValue(200);
            paramMap.put("status", v4);
            System.out.println(paramMap);
            // client.ruleExecute1(paramMap);
            // client.ruleExecute2(paramMap);
            client.ruleExecuteX(paramMap);

        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } finally {
            if (null != transport) {
                transport.close();
            }
        }
    }

    public static void main(String[] args) {
        ParaMapClient client = new ParaMapClient();
        client.startClient("china");
    }

}

 

从上面的代码来看,对于不确定类型的情况Thrift处理起来会比较麻烦(客户端得set,服务端也得get,而且在get的时候还要进行判断),Thrift的规范比较死板(不灵活),但也就是这种死板减少了出错的可能性,按需选择吧。

参考链接:

=END=

,

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注