最近在部署一个比赛环境,其中有一个需求,去一个服务器不断拉取文件进行分析,然后向这个服务器反馈分析结果。
问题一
初步使用多进程生产消费者模型,由于分析速度慢,拉取速度快,这个时候就出现一个重复拉取的问题,比如A文件拉取过来,然后扔给消费者处理,等了一会还是没有消费完,这个时候生产者又开始拉文件了,导致A文件又被拉取一次。
为了避免这个问题,设置一个全局共享变量,生产者一次性拉取若干文件,然后交给消费者处理,等待所有的文件都处理完毕之后,再向生产者发送一个反馈消息,告诉生产者可以再次生产数据了,之后生产者收到消息继而进行下一次的任务获取。
因为用到多进程,默认不能像多线程那样共享全局变量,这里需要通过进程共享变量实现。
manager = multiprocessing.Manager()
# 设置存放数据flag
ready = manager.Value('b', 1)
同时需要注意的一点,要通过进程池的方式实现多进程,否则值还是无法进行传递。
示例:
#coding=utf8
import multiprocessing
import random
import time
lock = multiprocessing.Lock()
def produce(q,ready):
while True:
if ready.value:
print("收到消息,开始生产数据")
for i in range(20):
q.put(1)
print("停止生产")
ready.value = 0
time.sleep(2)
def comsume(q,ready):
while True:
if q.empty():
# 告诉生产者,我这里消费完了,可以生产数据了
ready.value = 1
time.sleep(2)
continue
task = q.get()
time.sleep(task)
if __name__ == '__main__':
manager = multiprocessing.Manager()
ready = manager.Value('b',1)
pool = multiprocessing.Pool(10)
q = manager.Queue(20)
pool.apply_async(func=produce, args=(q, ready,))
for i in range(8):
pool.apply_async(func=comsume,args=(q,ready,))
pool.close()
pool.join()
输出:
收到消息,开始生产数据
停止生产
收到消息,开始生产数据
停止生产
收到消息,开始生产数据
停止生产
收到消息,开始生产数据
停止生产
收到消息,开始生产数据
停止生产
收到消息,开始生产数据
停止生产
收到消息,开始生产数据
停止生产
收到消息,开始生产数据
停止生产
问题二
如何使用requests进行自定义构造内容包上传文件
requests官方文档里的标准文件上传格式是这样的
>>> url = 'http://httpbin.org/post'
>>> multiple_files = [
('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
>>> r = requests.post(url, files=multiple_files)
>>> r.text
{
...
'files': {'images': 'data:image/png;base64,iVBORw ....'}
'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
...
}
也就是说我们要先获得一个文件的句柄,调用open()函数,如果我们只有文件内容,如果想通过这种方式就要先把他存储下来,然后再open打开上传,非常麻烦。
后面研究了下这个requests函数,下面是构造file数据包代码。
files = to_key_val_list(files or {})
...
if isinstance(v, (tuple, list)):
if len(v) == 2:
fn, fp = v
elif len(v) == 3:
fn, fp, ft = v
else:
fn, fp, ft, fh = v
else:
fn = guess_filename(v) or k
fp = v
if isinstance(fp, (str, bytes, bytearray)):
fdata = fp
elif hasattr(fp, 'read'):
fdata = fp.read()
elif fp is None:
continue
else:
fdata = fp
rf = RequestField(name=k, data=fdata, filename=fn, headers=fh)
先判断输入的变量v是不是一个元组,如果是二元组,则fn = v[0] , fp = v[1]
之后再看fp 是不是一个(str, bytes, bytearray)或是否具有read属性,也就是说支持f.read 以及f这两种类型,如果都不是那么fdata = fp,然后filename=fn,继续构造数据包。注意到传入的files字段会通过to_key_val_list进行处理,因此我们构造以下两种方式即可直接构造文件内容上传,示例如下:
r = requests.post("http://qq.com/",files={'file': ("filename.txt",filedata)})
print(r.request.body.decode("utf-8"))
或
r = requests.post("http://100.65.6.50/phpscan.php",files=[('file',("1.txt",filedata))])
print(r.request.body.decode("utf-8"))
输出:
--5b32e3e126eb8751351f4b8b7a7e742d
Content-Disposition: form-data; name="file"; filename="1.txt"
123
--5b32e3e126eb8751351f4b8b7a7e742d--