Python搭建HTTP服务
背景
本次我们要为一个自动化测试工具搭建一个Http服务,以方便一个本地的测试工具被大家在网络中共享使用。
一、方案选择:
python Socket
二、选择理由
-
底层测试工具由Python编写,因此使用python搭建HTTP服务兼可操作性强,同时可以对工具进行二次开发
-
Socket模块,为python自带的库,使用socket搭建HTTP服务非常简单方便并且可以编程
我们直接通过架构图来了解一下本次的服务架构
从架构图中可以看到整个流程为:
-
用户发送httpRequset到httpserver
-
httpServer解析请求,生成调用测试工具所需的命令/用例,调用工具对外接口
-
testtool执行测试,生成结果并返回给httpServer
-
httpServer将结果封装成JSON字符串,返回给用户
1.首先,我们需要建立一个服务,建立服务我们考虑以下四个因素:
1) url : HTTP服务 url,可以是ip地址也可以是域名等等,如果不传则默认本机ip地址
2) port : HTTP服务端口号,注意不要填写已被占用端口号,默认8901
3) recvNum : HTTP服务最大连接数,超过此连接数则无法请求成功 ,默认20
4) logLevel : HTTP服务后台日志级别 ,1:debug,2:info,3:warning,4:error ,默认 2
将这四个因素作为服务脚本的参数,方便在启动服务时,对服务进行设置
这里设计,参数传入形式为name=value,这样设计的好处为参数可传可不传,参数位置无需固定
2.之后就是使用socket库建立一个服务了
这里有两点要注意:
1)self.url是外部传入的url参数,当外部传入了url参数时,使用外部url参数作为服务地址。如果没有传入url,则通过
myname =socket.getfqdn(socket.gethostname())
host = socket.gethostbyname(myname)
方法获取本机ip地址作为url
2)listenSocket =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
建立一个socket对象,对象的属性为socket.AF_INET(使用互联网基础协议ipv4), socket.SOCK_STREAM(流式socket(tcp))
listenSocket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
为listenSocket对象设置更多属性,socket.SOL_SOCKET(在套接字级别), socket.SO_REUSEADDR(打开或关闭地址复用功能),1(开启)。总体来说这句话的意思就是设置允许多个客户端连接服务,无需等待
关于这两个方法的参数说明,提供一个明细列表:
AF_INET:ipv4, AF_INET6:ipv6 , AF_UNIX:只能够用于单一的Unix系统进程间通信
SOCK_STREAM:流式socket(TCP) ,数据报式socket(UDP)
在套接字级别上(SOL_SOCKET),option_name可以有以下取值:
SO_DEBUG,打开或关闭调试信息。
SO_REUSEADDR,打开或关闭地址复用功能。
SO_DONTROUTE,打开或关闭路由查找功能。
SO_BROADCAST,允许或禁止发送广播数据。
SO_SNDBUF,设置发送缓冲区的大小。其上限为256 * (sizeof(struct sk_buff) + 256),下限为2048字节。
SO_RCVBUF,设置接收缓冲区的大小。上下限分别是:256 * (sizeof(struct sk_buff) + 256)和256字节。
SO_KEEPALIVE,套接字保活。
SO_OOBINLINE,紧急数据放入普通数据流。
SO_NO_CHECK,打开或关闭校验和。
SO_PRIORITY,设置在套接字发送的所有包的协议定义优先权。
SO_LINGER,如果选择此选项,close或 shutdown将等到所有套接字里排队的消息成功发送或到达延迟时间后才会返回.否则, 调用将立即返回。
SO_PASSCRED,允许或禁止SCM_CREDENTIALS控制消息的接收。
SO_TIMESTAMP,打开或关闭数据报中的时间戳接收。
SO_RCVLOWAT,设置接收数据前的缓冲区内的最小字节数。
SO_RCVTIMEO,设置接收超时时间。
SO_SNDTIMEO,设置发送超时时间。
SO_BINDTODEVICE,将套接字绑定到一个特定的设备上。
SO_ATTACH_FILTER和SO_DETACH_FILTER。
3)listenSocket.bind((host,self.port))
绑定地址和端口
4)listenSocket.listen(self.recvNum)
监听地址,同时设置最大连接数
到此为止服务就建立完成了,但光建立服务是不够的,我们还需接收请求,并对请求做出回应
1.接收请求
1) 通常情况下,HTTP服务是一直存在的,除非外部干预,否则HTTP就不会停止,因此这里我们可以使用while True死循环保证服务一直处于工作状态
2)接收客户端发送的请求时,我们设置一个接受内容的最大字节数
2.请求解析
接收到请求后,对请求内容进行解析,得到我们需要的数据
这里专门定义了一个类,来进行请求内容的解析,大致的功能就是把请求的正文提取出来,生成一个字典数据类型,将用例信息存放在字典中。
同时,我们还能看到我们在处理完请求以后,立即生成了一个jobId,并返回给客户端,这步操作的作用是,防止后台测试工具处理时间较长,造成http连接超时,同时也是为了防止用户长时间等待,但并不知道请求是否发送成功。因此先返回一个唯一id,告诉用户服务已经在处理,这样用户就可以根据id来等待返回结果了。
调用测试工具,预先需要做几个准备工具
-
首先,测试工具需提供一个对外的接口,以供外部调用,并将请求传递给测试工具
-
其次,测试工具需要解析请求内容,以便在内部执行
-
最后,测试工具对外接口要提供返回值,以便外部调用及时接收结果
因此不满则这三个条件的工具,需要对工具进行一定程度上的二次开发,以满足要求
这里我们已经将后台的testtool进行了改造,并提供了一个对外接口Script_new .RunTest. run_test()。详细情况就不再这里进行介绍了。
另外,这里建议大家,如果返回结果较为复杂,不是简单0/1这种形式的话,就好把返回结果组织成json字符串返回,以便客户端解析。
如果想要得到json字符串,建议大家现将结果存在一个 dict容器中。这样做的好处是,dict可以通过json.dumps()方法直接转换成json字符串,省去数据转换的过程,高效快捷。
这里我们可以看到返回的responseBody 就是一个dict
通过 json.dumps 我们可以直接得到json字符串,最后通过sendall方法发送结果
1.由于HTTP服务要长期存在,因此,代码不能在出现异常时就崩溃退出。需要添加异常捕获机制,将异抛出,并记录在日志中。
并且异常捕获的位置和捕获单位都需要特别注意
2.建议返回结果后,主动关闭连接,即clientConnection.close()
3.添加必要的日志信息,以便时刻掌握服务状态
相关文章