由于项目需要使用hive client,我们就找了hive的php版本的client,首先看一段网上找的demo
// set THRIFT_ROOT to php directory of the hive distribution
$GLOBALS['THRIFT_ROOT'] = '/lib/php/';
// load the required files for connecting to Hive
require_once $GLOBALS['THRIFT_ROOT'] . 'packages/hive_service/ThriftHive.php';
require_once $GLOBALS['THRIFT_ROOT'] . 'transport/TSocket.php';
require_once $GLOBALS['THRIFT_ROOT'] . 'protocol/TBinaryProtocol.php';
// Set up the transport/protocol/client
$transport = new TSocket('localhost', 10000);
$protocol = new TBinaryProtocol($transport);
$client = new ThriftHiveClient($protocol);
$transport->open();
// run queries, metadata calls etc
$client->execute('SELECT * from src');
var_dump($client->fetchAll());
$transport->close();
然后在测试的时候出现了一个问题,使用下面的代码不能选择数据库。原因呢,是因为我们的hive使用sasl权限认证,服务端会默认给选择一个跟账号名一样的数据库,而且不能自由切换,这样就不能一个账号连接多个库了
然后,我使用tcpdump抓了下beeline的数据包,然后和我们的数据包对比了下,发现在OpenSession请求时候的数据不一样。
下面是我们程序的数据包
0x0010: 0aca 0429 b978 2710 c5c7 a509 4bdc bdae ...).x'.....K...
0x0020: 5018 006f 9ec8 0000 0000 0023 8001 0001 P..o.......#....
0x0030: 0000 000b 4f70 656e 5365 7373 696f 6e00 ....OpenSession.
0x0040: 0000 000c 0001 0800 0100 0000 0700 00 ...............
下面是使用beeline命令行连接时的数据包
0x0010: 0aca 0429 952c 2710 f919 1dbc 7e2d 48e8 ...).,'.....~-H.
0x0020: 5018 00e5 164f 0000 0000 0047 8001 0001 P....O.....G....
0x0030: 0000 000b 4f70 656e 5365 7373 696f 6e00 ....OpenSession.
0x0040: 0000 010c 0001 0800 0100 0000 070d 0004 ................
0x0050: 0b0b 0000 0001 0000 000c 7573 653a 6461 ..........use:da
0x0060: 7461 6261 7365 0000 0007 6861 776b 6579 tabase....hawkey
0x0070: 6500 00 e..
很容易发现少了一串东西吧,怎么办呢?先看源码,源码中没看出来什么信息。然后找了官方文档(java的文档),也没找到解决办法。只能上终极大招了:解析协议!
转载一篇网上关于协议的文章Thrift的TBinaryProtocol二进制协议分析
容我盗个图,这张图上列举出了所有的报文格式编码
消息类型重点说下(这段也是盗的):
表示方式一:四个字节的版本(含调用类型),四个字节的消息名称长度,消息名称,四个字节的流水号,消息负载数据的值,一个字节的结束标记。
表示方式二:四个字节的消息名称长度,消息名称,一个字节调用类型,四个字节的流水号,消息负载数据的值,一个字节的结束标记。
对严格的thrift消息,必须包含32位版本信息。(从数据包中可以看出来使用的是包含32位版本信息的表达式一)
若有32为版本信息,函数调用(请求:1,响应:2,异常:3,无返回值的请求:4)被包含到32为版本中,不独立出现。
看完后尝试解析,我打印了数据包的拼接过程,然后果然有惊喜,这个包使用的是表示方式一:
TSaslClientTransport:write 0000000b 消息名称长度
TSaslClientTransport:write 4f70656e53657373696f6e 消息名称
TSaslClientTransport:write 00000001 请求流水号
TCLIService_OpenSession_args:write 30
函数的第一个参数
TSaslClientTransport:write 0c 表示结构体TType.STRUCT = 12
TSaslClientTransport:write 0001 参数编号1
TSaslClientTransport:write 08 i32
TSaslClientTransport:write 0001 结构体元素编号1
TSaslClientTransport:write 00000007 值
TSaslClientTransport:write 00 结构体结束
TCLIService_OpenSession_args:write 3131
TSaslClientTransport:write 00 参数结束
结合代码,发现其中有个seqid,目前还不知道什么作用,不过不用关心,因为后面的数据都可以解释通了,然后对照beeline命令行的数据包(这里我格式化了,看起来更清晰)。
0000 000b ....
4f70 656e 5365 7373 696f 6e OpenSession
0000 0001 seqid
0c
0001
08
0001
0000 0007
0d 表示map TType.MAP = 13
0004 元素编号4
0b0b 表示 key:TType.STRING = 11 and val :TType.STRING = 11
0000 0001 map元素个数1
0000 000c key长度12
7573 653a 6461 7461 6261 7365 use:database key
0000 0007 val长度7
6861 776b 6579 65 hawkeye val
00 结构体结束
00 参数结束
然后所有的数据都可以解释通了,这是一个map,是OpenSession的结构体参数里面的第四个元素,然后结合代码。问题得以解决!其实就加一行代码就ok了!
关于需要sasl认证的代码我就不粘了,网上可以找到
$vals['configuration'] = ['use:database' => 'hawkeye']; //加这么一行代码
$sorq = new TOpenSessionReq($vals);
...
总结:debug能力又涨了新姿势…