=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=