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