hive客户端协议(Thrift的TBinaryProtocol二进制协议)学习

由于项目需要使用hive client,我们就找了hive的php版本的client,首先看一段网上找的demo

<?php
// 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权限认证,服务端会默认给选择一个跟账号名一样的数据库,而且不能自由切换,这样就不能一个账号连接多个库了

$client->execute('use database');

然后,我使用tcpdump抓了下beeline的数据包,然后和我们的数据包对比了下,发现在OpenSession请求时候的数据不一样。
下面是我们程序的数据包

  0x0000:  4500 004f 500d 4000 4006 4c15 0a10 8584  E..OP.@.@.L.....
  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命令行连接时的数据包

  0x0000:  4500 0073 9cff 4000 4006 879c 0aca fc2c  E..s..@.@......,
  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 80010001 版本+消息类型(请求)
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命令行的数据包(这里我格式化了,看起来更清晰)。

  8001 0001  P....O.....G....
  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能力又涨了新姿势…

转载请注明:小Y » hive客户端协议(Thrift的TBinaryProtocol二进制协议)学习

赞 (11) 评论 (0) 分享 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址