Openresty/Lua + Thrift + HBase,openrestythrift


1. 问题

在Openresty环境下(也就是lua语言下),如何访问HBase?Lua不方便调用HBase的原生API(Java),所以只能通过别的方式,例如:

  • Rest
  • Thrift
Rest提供的接口不够丰富,比如checkAndPut就没有支持,所以,有必要尝试一下Thrift.


2. Thrift是什么

简单来说,Thrift是一种SOA(Service Oriented Architecture)的实现方式,和WSDL+SOAP那一套类似。过程都是这样的:

  • 以一种接口语言定义接口(对于WSDL+SOAP是.wsdl文件;对于Thrift是.thrift文件);
  • 使用一个编译器,把接口文件(.wsdl或者.thrift)转化为代码(客户端类和服务端虚基类/接口);
  • 继承服务端虚基类/接口,实现服务端逻辑;并部署服务;
  • 基于客户端类,通过少量的开发即可实现客户端,与服务端通信。
Thrift或者WSDL+SOAP框架帮助程序员实现远程方法调用,在整个过程中,程序员不用考虑参数的序列化、网络传输、返回数据的反序列化,甚至包括安全、事务等方面的工作。

3. 环境

  • CentOS 7
  • Luajit:为了很好的和openresty结合,我们使用openresty 1.9.15.1包自带的luajit;它和lua 5.1兼容;
  • Thrift 0.9.2:目前最新版是0.9.3,但是Thrift 0.9.3需要lua 5.2及以上版本,所以我们使用和lua 5.1兼容的Thrift 0.9.2。这为我们带来很多麻烦,因为Thrift 0.9.2有一些bug,见后文Thrift安装部分;
  • HBase 1.2.2:它里面有两个Thrift Server:thrift基于HBase的老API;thrift2基于HBase的新API;我们使用后者。

4. HBase安装与配置

HBase的安装与配置涉及到Hadoop hdfs和zookeeper,这里略去其过程。HBase服务成功启动后,启动Thrift服务:

# /usr/local/hbase-1.2.2/bin/hbase-daemon.sh start thrift2 -p 9090 --infoport 9095



5. Openresty安装

目前我们只需要使用luajit,但为了后续与openresty兼容,干脆这里把openresty安装了,后面实验直接使用它自带的luajit。假定已经安装了PCRE,OpenSSL等依赖:

# cd /tmp/
# wget https://openresty.org/download/openresty-1.9.15.1.tar.gz
# tar zxvf openresty-1.9.15.1.tar.gz
# cd openresty-1.9.15.1/
# ./configure --prefix=/usr/local/openresty-1.9.15.1 --without-lua51 --with-luajit
# gmake
# gmake install
# ln -s /usr/local/openresty-1.9.15.1/luajit/lib/libluajit-5.1.so /usr/lib64/libluajit-5.1.so
# ln -s /usr/local/openresty-1.9.15.1/luajit/lib/libluajit-5.1.so.2 /usr/lib64/libluajit-5.1.so.2
现在,我们就有了luajit:/usr/local/openresty-1.9.15.1/luajit/


6. 安装Thrift

6.1 安装依赖

# yum install readline.x86_64 install readline-devel.x86_64
# yum install libtool.x86_64
# yum install boost.x86_64 boost-devel.x86_64
# yum install byacc.x86_64
# yum install flex.x86_64 flex-devel.x86_64 
# yum install openssl.x86_64 openssl-devel.x86_64

6.2 编译安装Thrift

# cd /tmp/
# git clone https://github.com/apache/thrift
# cd thrift/
# git branch -a
# git checkout -b 0.9.2 origin/0.9.2    <-- 切换到0.9.2版本
# ./bootstrap.sh
# ./configure --prefix=/usr/local/thrift-0.9.2                                \
        LUA=/usr/local/openresty-1.9.15.1/luajit/bin/luajit                   \
        LUA_INCLUDE=-I/usr/local/openresty-1.9.15.1/luajit/include/luajit-2.1 \
        LUA_LIB="-L/usr/local/stor-openresty/luajit/lib -lluajit-5.1"


修改bug1:

# vim lib/lua/src/luabpack.c
106c106
<   size_t len = lua_rawlen(L, 2);
---
>   size_t len = lua_objlen(L, 2);


修改bug2:

# vim lib/lua/Makefile
385a386,387
> LUA_INCLUDE=-I/usr/local/openresty-1.9.15.1/luajit/include/luajit-2.1
> LUA_LIB=-lluajit-5.1
516,517c518,519
< libluasocket_la_CPPFLAGS = $(AM_CPPFLAGS) -I/usr/include/lua5.2 -DLUA_COMPAT_MODULE
< libluasocket_la_LDFLAGS = $(AM_LDFLAGS) -llua5.2 -lm
---
> libluasocket_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE
> libluasocket_la_LDFLAGS = $(AM_LDFLAGS) $(LUA_LIB) -lm
519,520c521,522
< libluabpack_la_CPPFLAGS = $(AM_CPPFLAGS) -I/usr/include/lua5.2 -DLUA_COMPAT_MODULE
< libluabpack_la_LDFLAGS = $(AM_LDFLAGS) -llua5.2 -lm
---
> libluabpack_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE
> libluabpack_la_LDFLAGS = $(AM_LDFLAGS) $(LUA_LIB) -lm
523,524c525,526
< libluabitwise_la_CPPFLAGS = $(AM_CPPFLAGS) -I/usr/include/lua5.2 -DLUA_COMPAT_MODULE
< libluabitwise_la_LDFLAGS = $(AM_LDFLAGS) -llua5.2 -lm
---
> libluabitwise_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE
> libluabitwise_la_LDFLAGS = $(AM_LDFLAGS) $(LUA_LIB) -lm
529,530c531,532
< liblualongnumber_la_CPPFLAGS = $(AM_CPPFLAGS) -I/usr/include/lua5.2 -DLUA_COMPAT_MODULE
< liblualongnumber_la_LDFLAGS = $(AM_LDFLAGS) -llua5.2 -lm
---
> liblualongnumber_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE
> liblualongnumber_la_LDFLAGS = $(AM_LDFLAGS) $(LUA_LIB) -lm

修改bug3:

# vim lib/lua/Makefile
508a509
>                       liblualongnumber.la \
510,511c511
<                       libluabitwise.la \
<                       liblualongnumber.la
---
>                       libluabitwise.la

好,现在开始make并make install。若make因缺失依赖而失败,需要make clean,重新configure,重新fix上面的bug,然后重试make。

# make
# make install

另外,make install不会拷贝lua库,所以需要手动拷贝

# mkdir /usr/local/thrift-0.9.2/lualib
# cp lib/lua/*.lua /usr/local/thrift-0.9.2/lualib/

修改bug4:错误 Thrift.lua:43: malformed number near '0.9.2'

# vim /usr/local/thrift-0.9.2/lualib/Thrift.lua
43c43
< version = 0.9.2
---
> --version = 0.9.2

修改bug5:创建TBufferedTransport实例时,误创建为的TTransportBase实例。

# vim /usr/local/thrift-0.9.2/lualib/TBufferedTransport.lua
40c40
<   return TTransportBase:new(obj)
---
>   return TTransportBase.new(self,obj)

Thrift安装完毕!


7. 访问HBase

7.1 找到.thrift文件

下载HBase 1.2.2源代码,里面包含两个.thrift文件。它们是接口定义文件,类似于WSDL+SOAP里面的.wsdl文件:

# find . -name "*.thrift"
./hbase-thrift/src/main/resources/org/apache/hadoop/hbase/thrift2/hbase.thrift
./hbase-thrift/src/main/resources/org/apache/hadoop/hbase/thrift/Hbase.thrift
我们只需要上面那个,它是thrift2的接口定义。


7.2 编译.thrift文件生成lua文件

# cd /home
# mkdir test
# cd test/
# cp /home/hbase-1.2.2/hbase-thrift/src/main/resources/org/apache/hadoop/hbase/thrift2/hbase.thrift .
# /usr/local/thrift-0.9.2/bin/thrift -r --gen lua hbase.thrift
# ls
gen-lua  hbase.thrift
# ll gen-lua/           <--生成的目录和文件
total 120
-rw-r--r--. 1 root root   136 Aug 30 10:04 hbase_constants.lua
-rw-r--r--. 1 root root 74532 Aug 30 10:04 hbase_THBaseService.lua
-rw-r--r--. 1 root root 38447 Aug 30 10:04 hbase_ttypes.lua


修改bug6: 生成代码错误

# vim gen-lua/hbase_ttypes.lua
508c508
<     oprot:writeListBegin(TType.STRUCT, string.len(self.columns))
---
>     oprot:writeListBegin(TType.STRUCT, #self.columns)<strong>
</strong>


7.3 使用生成的lua文件访问HBase

# mkdir mycode
# vim mycode/client.lua

#!/usr/local/openresty-1.9.15.1/luajit/bin/luajit

require('TSocket')
require('TBufferedTransport')
require('TFramedTransport')
--require('THttpTransport')
--require('TCompactProtocol')
--require('TJsonProtocol')
require('TBinaryProtocol')
require('liblualongnumber')

require('hbase_THBaseService')


local client = nil

function teardown()
  if client then
    -- close the connection
    client:close()
  end
end

function parseArgs(rawArgs)
  local opt = {
    protocol='binary',
    transport='buffered',
    port='9090',
  }
  for i, str in pairs(rawArgs) do
    if i > 0 then
      k, v = string.match(str, '--(%w+)=(%w+)')
      assert(opt[k] ~= nil, 'Unknown argument')
      opt[k] = v
    end
  end
  return opt
end

function assertEqual(val1, val2, msg)
  assert(val1 == val2, msg)
end

function testBasicClient(rawArgs)
  local opt = parseArgs(rawArgs)
  local socket = TSocket:new{
    port = tonumber(opt.port)
  }
  assert(socket, 'Failed to create client socket')
  socket:setTimeout(5000)

  local transports = {
    buffered = TBufferedTransport,
    framed = TFramedTransport,
    http = THttpTransport,
  }
  assert(transports[opt.transport] ~= nil)

  local transport = transports[opt.transport]:new{
    trans = socket,
    isServer = false
  }

  local protocols = {
    binary = TBinaryProtocol,
    compact = TCompactProtocol,
    json = TJSONProtocol,
  }
  assert(protocols[opt.protocol] ~= nil)
  local protocol = protocols[opt.protocol]:new{
    trans = transport
  }
  assert(protocol, 'Failed to create binary protocol')

  client = THBaseServiceClient:new{
    protocol = protocol
  }
  assert(client, 'Failed to create client')

  -- Open the transport
  local status, _ = pcall(transport.open, transport)
  assert(status, 'Failed to connect to server')


  --1. check if a row exists
  local tget = TGet:new{
      row="UUUU1234_bucket1"
  }

  local ok,ret = pcall(client.exists, client, "bucket", tget)
  if not ok or not ret then
      print("client:exists failed")
  else
      print("client:exists succeeded:")
      print("\t", ret)
  end

  --2. get a row
  local ok,ret = pcall(client.get, client, "bucket", tget)
  if not ok or not ret then
      print("client:get failed")
  else
      print("client:get succeeded:")
      local row=ret.row
      for i,v in pairs(ret.columnValues) do
          print("\t",row, v.family..":"..v.qualifier, v.value)
      end
  end

end

testBasicClient(arg)
teardown()

我是参照thrit源代码中test_basic_client.lua搞通的,前文提到的bug5也是在这个测试中发现。当然,HBase里的数据是我通过hbase shell手动插入的,这里只测试它是否存在,并get出来。


7.4 测试

为了方便运行,我写了个脚本:

# vim client.sh

#!/bin/bash


TEST_HOME=/home/test
THRIFT_HOME=/usr/local/thrift-0.9.2
LUA_HOME=/usr/local/openresty-1.9.15.1/luajit

rm -fr gen-lua
$THRIFT_HOME/bin/thrift -r --gen lua hbase.thrift

export LUA_PATH="$THRIFT_HOME/lualib/?.lua;$TEST_HOME/gen-lua/?.lua;$TEST_HOME/mycode/?.lua;;"
export LUA_CPATH="$THRIFT_HOME/lib/?.so;;"

$LUA_HOME/bin/luajit mycode/client.lua

运行结果:

# ./client.sh
client:exists succeeded:
                true
client:get succeeded:
                UUUU1234_bucket1        exattrs:content movies
                UUUU1234_bucket1        info:ctime      Thu, 11 Aug 2016 02:14:45 +0000
                UUUU1234_bucket1        info:mtime      Thu, 11 Aug 2016 02:14:45 +0000
                UUUU1234_bucket1        quota:enabled   yes
                UUUU1234_bucket1        quota:objects   1024
                UUUU1234_bucket1        quota:size_mb   1024000
                UUUU1234_bucket1        stats:mb_rounded        0
                UUUU1234_bucket1        stats:objects   0
                UUUU1234_bucket1        stats:size_bytes        0
                UUUU1234_bucket1        ver:tag BBBB1234
                UUUU1234_bucket1        ver:version     0


8. 下一步

研究HBase Thrift API支持的接口(是否比REST丰富);

测试Thrift的性能;

相关内容

    暂无相关文章