2步测试Thrift接口——使用shell+python

省下重复劳作的时间,坐下来喝一杯茶,工具完善生活。

题图:from Zoommy

一、引子

由于Thrift每个接口都依赖特定IDL结构,因此,针对不同的接口需要编写不同的代码。

也因此,每次开发完成Thrift server端后,如要测试,都要开发对应的client端进行测试。

参考官方文档可以发现,client端代码说简单也不简单(毕竟要写 :) ),重点是这些代码在测试过后即沦为废品,实在是浪费时间精力。

那么,到底能不能找到一种偷懒的方式呢?答案是肯定的。将一切重复劳动工具化,这是程序猿天生的责任。

官方文档:Thrift Java TutorialThrift Python TutorialThrift PHP Tutorial

二、为何使用python client测试

由于只是一个简单的client端开发,因此使用java等重武器总会有牛刀杀鸡的感觉。这种时候当然是选择轻巧灵便的脚本语言——python。(这也要感谢Thrift是跨语言的)

此外,python也有其独有优势。

  1. MacOS、Linux原生自带python
  2. python脚本执行简便,一条命令即可

因此选择python来实现测试client端也就是顺理成章的事情了。

三、如何使用python client

参考官方文档:Thrift Python Tutorial

  • 搞到IDL文件(由server端提供)
  • 本机装有thrift服务
    pip install thrift
    Mac如何安装pip:https://pip.readthedocs.io/en/stable/installing/

  • 使用thrift命令,生成目标代码
    thrift -r --gen py example.thrift

  • 编写python client端代码
    编写一个python脚本,接收4个参数:1:server的ip,2:server的端口,3:python脚本中方法名,4:调用的远程方法与参数列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 一个示例文件,名为client.py
import sys
import glob
sys.path.append('gen-py')
from test1 import HeartbeatThriftService
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
def test1_HeartbeatThriftService():
# Make socket
transport = TSocket.TSocket(sys.argv[1], int(sys.argv[2]))
# Buffering is critical. Raw sockets are very slow
transport = TTransport.TFramedTransport(transport)
# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the protocol encoder
client = HeartbeatThriftService.Client(protocol)
# Connect!
transport.open()
try:
msg = eval('client.' + sys.argv[4])
print msg
except Thrift.TException, ex:
print '%s' % (ex.message)
# Close!
print 'transport close'
transport.close()
if __name__ == '__main__':
method = eval(sys.argv[3])
method()
  • 执行命令调用server端接口
1
2
# 一条示例命令
python client.py 172.18.201.100 9001 test1_HeartbeatThriftService "getServiceStatus()"

OK!
到这里我们就完成了测试client的编写。
但是到这里为止,我们还是要为不同的server接口编写不同client代码,完全没有实现工具自动化。
别急,下一步,我们就可以使用shell脚本进行代码的自动生成,将3、4两步的繁琐编码替换执行一条命令。

四、shell脚本自动化生成代码(支持多个IDL文件同时生成)

使用shell脚本是这里的核心,通过shell脚本自动生成代码,可以省去每次编写client端代码的繁琐过程,这样,测试所需步骤即可简化为2步!

  1. 在thrift文件(IDL)目录下,执行shell脚本生成代码
  2. 在相同目录下,执行python脚本测试thrift接口

现写好脚本如下,方便大家参考交流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/bin/bash
# shell开始
rm -rf gen-py/
rm client.py
# 使用'thrift -r --gen example.thrift'命令,根据所有idl文件生成代码
find . -name "*.thrift" | sed 's/^\.\///' | xargs -I {} thrift -r --gen py {}
# import sys
# import glob
# sys.path.append('gen-py')
echo "import sys\nimport glob\nsys.path.append('gen-py')\n\n" >> client.py
# from test import testService
find ./gen-py -name "*-remote" | sed 's/^\.\/gen-py\///; s/-remote$//' | sed 's/\// import /; s/^/from /' >> client.py
# from thrift import Thrift
# from thrift.transport import TSocket
# from thrift.transport import TTransport
# from thrift.protocol import TBinaryProtocol
echo "\nfrom thrift import Thrift\nfrom thrift.transport import TSocket\nfrom thrift.transport import TTransport\nfrom thrift.protocol import TBinaryProtocol\n\n\n" >> client.py
methodNames=$(find ./gen-py -name "*-remote" | sed 's/^\.\/gen-py\///; s/-remote$//' | sed 's/\//_/')
# 开始生成方法体
for name in $methodNames; do
var="\ndef "$name"():\n\
\t# Make socket\n\
\ttransport = TSocket.TSocket(sys.argv[1], int(sys.argv[2]))\n\n\
\t# Buffering is critical. Raw sockets are very slow\n\
\ttransport = TTransport.TFramedTransport(transport)\n\n\n\
\t# Wrap in a protocol\n\
\tprotocol = TBinaryProtocol.TBinaryProtocol(transport)\n\n\n\
\t# Create a client to use the protocol encoder\n\
\tclient = "$(echo $name | sed 's/.*_//; s/^//; s/$//')".Client(protocol)\n\n\n\
\t# Connect!\n\ttransport.open()\n\n\n\
\ttry:\n\
\t\tmsg = eval('client.' + sys.argv[4])\n\
\t\tprint msg\n\
\texcept Thrift.TException, ex:\n\
\t\tprint '%s' % (ex.message)\n\n\n\
\t# Close!\n\
\tprint 'transport close'\n\
\ttransport.close()\n\n"
echo $var >> client.py
echo $name
done
echo "\nif __name__ == '__main__':\n\tmethod = eval(sys.argv[3])\n\tmethod()" >> client.py
:<<BLOCK
生成的文件示例
import sys
import glob
sys.path.append('gen-py')
from test1 import HeartbeatThriftService
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
def test1_HeartbeatThriftService():
# Make socket
transport = TSocket.TSocket(sys.argv[1], int(sys.argv[2]))
# Buffering is critical. Raw sockets are very slow
transport = TTransport.TFramedTransport(transport)
# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the protocol encoder
client = HeartbeatThriftService.Client(protocol)
# Connect!
transport.open()
try:
msg = eval('client.' + sys.argv[4])
print msg
except Thrift.TException, ex:
print '%s' % (ex.message)
# Close!
print 'transport close'
transport.close()
if __name__ == '__main__':
method = eval(sys.argv[3])
method()
BLOCK

五、2步测试Thrift接口

通过上边的工具化,现在针对不同的thrift接口,我们都只需要两步即可进行测试。

举个例子:位于目录thrift下,其内包含test1.thrift,需要手动操作的过程如下

  1. sh genThriftTest.sh (即上述shell脚本)
  2. python client.py 172.18.201.100 9001 test1_HeartbeatThriftService "getServiceStatus()" (ip、端口、方法皆为本次测试所用,针对不同测试可以自定义)

六、扩展应用

由于每次调用都简化为一次命令,因此,也可以很容易的编写批量测试脚本,这样也将自动化测试thrift的工作变得更加简单了。