原起
由于项目需要使用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协议的格式编码
消息类型重点说下
看完后尝试解析,我打印了数据包的拼接过程,然后果然有惊喜,这个包使用的是表示方式一:
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);
透过现象看本质,解决方案就是如此简单
----------伟大的分割线-----------
PHP饭米粒(phpfamily) 由一群靠谱的人建立,愿为PHPer带来一些值得细细品味的精神食粮!
饭米粒只发原创或授权发表的文章,不转载网上的文章
所发的文章,均可找到原作者进行沟通。
也希望各位多多打赏(算作稿费给文章作者),更希望大家多多投搞。
投稿请联系:
shenzhe163@gmail.com
本文由 李云飞 授权 饭米粒 发布,转载请注明本来源信息和以下的二维码(长按可识别二维码关注)