Python队列及在微信机器人中的应用
本文来源于i春秋学院,未经允许严禁转载。
最近打算更新微信机器人,发现机器人的作者将代码改进了很多,但去掉了sqlite数据库,需要自己根据需求设计数据库,跟作者沟通得到的建议是为了防止消息并发导致数据库死锁,建议另开一个进程读写数据库,将消息加入一个队列中,因为对Python了解有限,队列和多线程更不是我擅长的内容,于是最近疯狂Google、百度,探索着实现了此功能。写此文记录下基本概念和实现方法
0x00 Python队列
队列是线程中交换数据的形式。
创建一个队列对象
import Queue q = Queue.Queue(maxsize = ) #maxsize是队列长度,不限制长度可以不不赋值
将一个值放入队列
q.put()
将一个值从队列取出
q.get()
三种队列及构造函数
- FIFO队列先进先出: class Queue.Queue(maxsize)
- LIFO类似于堆,即先进后出: class Queue.LifoQueue(maxsize)
- 优先级队列: class Queue.PriorityQueue(maxsize)
队列的常用方法
q.qsize() #返回队列的大小 q.empty() #如果队列为空,返回True,反之False q.full() #如果队列满了,返回True,反之False q.full #与 maxsize 大小对应 q.get([block[, timeout]]) #获取队列,block:是否阻塞等待,timeout等待时间 q.get_nowait() #相当q.get(False) q.put(item[, block[, timeout]) #非阻塞写入队列,block:是否阻塞等待,timeout等待时间 q.put_nowait(item) #相当q.put(item, False) q.task_done() #在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号 q.join() #等到队列为空,再执行别的操作
几个例子
一个线程往队列里写入随机数,另一个线程从队列里取数字(阻塞等待)
#!/usr/bin/env python
#coding:utf8
import random,threading,time
from Queue import Queue
#Producer thread
class Producer(threading.Thread):
def __init__(self, t_name, queue):
threading.Thread.__init__(self,name=t_name)
self.data=queue
def run(self):
for i in range(): #随机产生10个数字 ,可以修改为任意大小
randomnum=random.randint(,)
print "%s: %s 生成了一个数字 %d 并把它扔进了队列!" % (time.ctime(), self.getName(), randomnum)
self.data.put(randomnum) #将数据依次存入队列
time.sleep()
print "%s: %s finished!" %(time.ctime(), self.getName())
#Consumer thread
class Consumer_all(threading.Thread):
def __init__(self, t_name, queue):
threading.Thread.__init__(self, name=t_name)
self.data = queue
def run(self):
while :
try:
# print self.data
val_even = self.data.get()
print "%s: %s 从队列里取出了 %d !" % (time.ctime(), self.getName(), val_even)
time.sleep()
except Exception, e:
print '取数据失败'
continue
#Main thread
def main():
queue = Queue()
producer = Producer('Pro.', queue)
consumer_all = Consumer_all('Con_all.', queue)
producer.start()
consumer_all.start()
if __name__ == '__main__':
main()
运行结果
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
Wed Aug :: : Pro. finished!
如果将入队的时间间隔修改,出队的程序将阻塞运行,将“ time.sleep(1)”改为“ time.sleep(10)”,再次运行
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列! Wed Aug :: : Con_all. 从队列里取出了 ! Wed Aug :: : Pro. finished!
会发现当队列没有值的时候程序会阻塞等待队列有值才继续运行。
稍微修改程序,一个线程往队列里写入随机数,另一个线程从队列里取数字(非阻塞等待)
#!/usr/bin/env python
#coding:utf8
import random,threading,time
from Queue import Queue
#Producer thread
class Producer(threading.Thread):
def __init__(self, t_name, queue):
threading.Thread.__init__(self,name=t_name)
self.data=queue
def run(self):
for i in range(): #随机产生10个数字 ,可以修改为任意大小
randomnum=random.randint(,)
print "%s: %s 生成了一个数字 %d 并把它扔进了队列!" % (time.ctime(), self.getName(), randomnum)
self.data.put(randomnum) #将数据依次存入队列
time.sleep()
print "%s: %s finished!" %(time.ctime(), self.getName())
#Consumer thread
class Consumer_all(threading.Thread):
def __init__(self, t_name, queue):
threading.Thread.__init__(self, name=t_name)
self.data = queue
def run(self):
while :
try:
# print self.data
val_even = self.data.get(,) # get(self, block=True, timeout=None) ,1就是阻塞等待,5是超时5秒
print "%s: %s 从队列里取出了 %d !" % (time.ctime(), self.getName(), val_even)
time.sleep()
except Exception, e: # 等待输入,超过5秒 就报异常
print '取数据失败'
continue
#Main thread
def main():
queue = Queue()
producer = Producer('Pro.', queue)
consumer_all = Consumer_all('Con_all.', queue)
producer.start()
consumer_all.start()
if __name__ == '__main__':
main()
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. 生成了一个数字 并把它扔进了队列!
Wed Aug :: : Con_all. 从队列里取出了 !
取数据失败
Wed Aug :: : Pro. finished!
取数据失败
取数据失败
取数据失败
可以看出,取数据的线程在超过设定的等待时间后会抛出异常并继续往下执行。
0x01 使用队列将微信机器人消息存入MongoDB
使用上面的例子,稍作修改,将接收到的消息扔进队列,开启另一个线程取数据,取到后将消息格式化并存入数据库
#!/usr/bin/env python
#coding:utf8
import threading,time,pymongo
from pymongo import MongoClient
from Queue import Queue
#消息入队
class MsgInQueue():
def __init__(self, queue):
threading.Thread.__init__(self)
self.data=queue
def putmsgqueue(self,msg):
self.data.put(msg)
print 'put msg in queue success:'+msg['Content']
###消息出队并存入数据库
class MsgOutQueue2db(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.data = queue
#建立MongoDB连接
self.conn = MongoClient()
#数据库
self.db = self.conn.wechatRobot
#数据表
self.messages = self.db.messages
def run(self):
while :
try:
# print self.data
#从队列里取消息
msg = self.data.get(, ) # get(self, block=True, timeout=None) ,1就是阻塞等待,5是超时5秒
print "%s: %s get %s from queue !" % (time.ctime(), self.getName(), msg['Content'].encode('utf-8'))
try:
#格式化消息数据
m = dict(groupname=msg['FromUserName'].encode('utf-8'),
time=msg['CreateTime'],
username=msg['ActualUserName'],
usernickname=msg['ActualNickName'].encode('utf-8'),
message=msg['Content'].encode('utf-8'),
messagetype=msg['MsgType']
)
print m
#存入数据库
self.db.messages.insert(m)
time.sleep()
except Exception, e:
print e
continue
except Exception, e:
continue
主线程
def complex_reply():
queue = Queue()
outqueue = MsgOutQueue2db(queue)#实例化出队入库类
outqueue.start()#开启线程
@itchat.msg_register('Text', isGroupChat = True)
def text_reply(msg):
# print itchat.__client.storageClass.groupDict
print itchat.__client.storageClass.chatroomList
print msg
inqueue=MsgInQueue(queue)#实例化入队类
inqueue.putmsgqueue(msg)#消息入队
if msg['isAt']:
print msg
itchat.send(u'@%s\u2005I received: %s'%(msg['ActualNickName'], msg['Content']), msg['FromUserName'])
消息存入数据库:

通过多线程和MongoDB的结合,有效防止消息过多导致数据库死锁的问题,也更加模块化,可以根据真实需求更换其他数据库。后面我将结合MongoDB插入、更新数据快的特点写一下我如何设计群聊统计功能,在Python方面和MongoDB方面我都是小白,如有更好的建议请多指教,我们共同学习有关Python多线程的课程请参考《python安全编程入门》
Python队列及在微信机器人中的应用的更多相关文章
- [bigdata] 使用Redis队列来实现与机器无关的Job提交与执行 (python实现)
用例场景: 定时从远程多台机器上下载文件存入HDFS中.一开始采用shell 一对一的方式实现,但对于由于网络或者其他原因造成下载失败的任务无法进行重试,且如果某台agent机器down机,将导致它对 ...
- Python队列服务 Python RQ Functions from the __main__ module cannot be processed by workers.
在使用Python队列服务 Python RQ 时候的报错: Functions from the __main__ module cannot be processed by workers. 原因 ...
- 【python】10分钟教你用python下载和拼接微信好友头像图片
前言 相信微信大家是用得再多也不过了.那么,对于python+微信,又能玩出什么新的花样呢?下面小编就给大家带来一个好玩的东西.用python下载所有的微信好友的头像,然后拼接成一张大图.这样,大家就 ...
- python与shell通过微信企业号发送消息
python与shell通过微信企业号发送信息,脚本来源于网络,做好搬运工,哈哈,相应的参考链接放在末位 shell版本: #!/bin/bash # CropID="xxxx" ...
- Python使用JsAPI发起微信支付 Demo
Python使用JsAPI发起微信支付 Demo 这个是基于Django框架. 了解更多,可以关注公众号"轻松学编程" 1.公众号设置.微信商户号设置 这些都可以在官网查得到, 公 ...
- Python + Appium 自动化操作微信入门看这一篇就够了
简介 Appium 是一个开源的自动化测试工具,支持 Android.iOS 平台上的原生应用,支持 Java.Python.PHP 等多种语言. Appium 封装了 Selenium,能够为用户提 ...
- pycharm上的python虚拟环境移到离线机器上
Pycharm的Terminal 中执行: 查看现有的包到requirements.txt中 pip freeze > requirements.txt 生成依赖包 D:\machangwei\ ...
- 网上售卖几百一月的微信机器,Python几十行代码就能搞定
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 故事胶片 PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...
- python队列、线程、进程、协程
目录: 一.queue 二.线程 基本使用 线程锁 自定义线程池 生产者消费者模型(队列) 三.进程 基本使用 进程锁 进程数据共享 默认数据不共享 queues array Manager.dict ...
随机推荐
- Navicat for MySQL使用
Navicat for MySQL使用 导出数据库表与结构 新数据库导入
- Logstash使用grok插件解析Nginx日志
grok表达式的打印复制格式的完整语法是下面这样的: %{PATTERN_NAME:capture_name:data_type}data_type 目前只支持两个值:int 和 float. 在线g ...
- nexus的安装和简介(3)
从私服下载jar包 没有配置nexus之前,如果本地仓库没有,去中央仓库下载,通常在企业中会在局域网内部署一台私服服务器,有了私服本地项目首先去本地仓库找jar,如果没有找到则连接私服从私服下载ja ...
- java 多线程学习
一.概念 程序.进程.线程 程序 是计算机指令的集合. 进程 是一个运行中的程序,它指的是从代码加载,执行到执行结束这样一个完整过程.每个进程占用不同的内存空间. 线程 是进程中某个单一顺 ...
- RDD认知
1.RDD又叫弹性分布式数据集 2.抽象 3.带泛型,支持多种数据类型 4.集合是可以进行分区 例如(1,2,3,4,5,6,7,8,9)这个数组是可以进行分区的(1,2,3) (4,5,6) ( ...
- JavaScript onclick传递对象参数(easyui传递一行数据时)错误:uncaught SyntaxError: Unexpected identifier
JavaScript onclick传递对象参数(easyui传递一行数据时)错误:uncaught SyntaxError: Unexpected identifier 博主遇到的是用onclick ...
- [leetcode]58. Length of Last Word最后一个词的长度
Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the l ...
- [leetcode]19. Remove Nth Node From End of List删除链表倒数第N个节点
Given a linked list, remove the n-th node from the end of list and return its head. Example: Given l ...
- 百度地图sdk sha1秘钥获取有种想吐的赶脚
撸代码坐的腰算背疼还只是弄一个不是项目里边需要的升级版本的so 日 需要sha1 指纹秘钥,还有项目包, 才能用百度地图sdk 这个找sha1 获取废了20分钟, 显示全盘找keytool.exe ...
- Javascript组成--ECMAScript,DOM,BOM
ECMAScript 部分 ECMAScript是一个标准,JS只是它的一个实现,其他实现包括ActionScript; “ECMAScript可以为不同种类的宿主环境提供核心的脚本编程能力”,即EC ...