一 數據庫
Python可以和數據庫進行交互,這裏也有一些交互的模塊,比如MySQLdb、pymysql等,但是3.x 已經不再支持MySQLdb,所以你安裝的時候會報錯
我們以pymysql爲例子:
1.1 創建連接
pymysql有幾種創建連接的方式
import pymysql
conn = pymysql.Connect(host="localhost",port=3306,user="root",password="123456",database="employee",charset
='utf8')
conn1 = pymysql.connect(host="localhost",port=3306,user="root",password="123456",database="ofbiz",charset=
'utf8')
conn2 = pymysql.Connection(host="localhost",port=3306,user="root",password="123456",database="ofbiz",charset=
'utf8')
其實connect==Connection=Connect
1.2 簡單的例子
# 創建遊標
cursor = conn.cursor()
# 查詢操作,並返回收影響行數
effect_row = cursor.execute("select * from empinfo")
# 更新操作,並返回受影響行數
effect_row = cursor.execute("update empinfo set DeptID = 3 where empID = %s", (1,))
# 創建多條記錄,並返回受影響行數,執行多次
effect_row = cursor.executemany("insert into deptinfo(DeptName) values(%s)", [("財務部"),("公關部")])
print(effect_row)
# 提交,不然無法保存新建或者修改的數據
conn.commit()
# 關閉遊標
cursor.close()
# 關閉連接
conn.close()
1.3 獲取查詢數據
utils = DBUtils()
conn = utils.getConnection(host,port,user,password,
db,charset)
cursor = conn.cursor()
# 查詢數據
cursor.execute("SELECT invoice_id,invoice_type_id,party_id_from,party_id,status_id,invoice_date FROM invoice")
# 獲取剩餘結果的第一行數據
row_1 = cursor.fetchone()
# 獲取剩餘結果前n行數據
rows_n = cursor.fetchmany(3)
# 獲取剩餘結果所有數據
rows_all = cursor.fetchall()
utils.foreach(rows_all)
utils.commitAndClose(conn,cursor)
1.4 獲取新創建數據的自增ID
utils = DBUtils()
conn = utils.getConnection(host,port,user,password,
db,charset)
cursor = conn.cursor()
effect_row = cursor.executemany("insert into deptinfo(DeptName) values(%s)", [("市場部"), ("廣告部")])
new_id = cursor.lastrowid
print(new_id)
utils.commitAndClose(conn,cursor)
1.5 移動遊標
utils = DBUtils()
conn = utils.getConnection(host,port,user,password,db,charset)
cursor = conn.cursor()
# 查詢數據
cursor.execute("SELECT invoice_id,invoice_type_id,party_id_from,party_id,status_id,invoice_date FROM invoice")
# 移動遊標:在fetch數據時按照順序進行,可以使用cursor.scroll(num, mode)來移動遊標位置,如
# 從0開始移動2行數據
cursor.scroll(2,"absolute")
row_1 = cursor.fetchone()
# 結果應該是第三行的數據 8002
utils.foreach(row_1)
# 從當前遊標8003開始往下移動2行數據,
cursor.scroll(2, "relative")
# 獲取剩餘結果的第一行數據,即8005
row_1 = cursor.fetchone()
utils.foreach(row_1)
utils.commitAndClose(conn,cursor)
1.6 fetch數據類型
fetch默認返回的是一個元組類型的數據,如果你希望返回字典類型數據,怎麼辦?
utils = DBUtils()
conn = utils.getConnection(host,port,user,password,
db,charset)
cursor = conn.cursor()
# 遊標設置爲字典類型
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 查詢數據
cursor.execute("SELECT invoice_id,invoice_type_id,party_id_from,party_id,status_id,invoice_date FROM invoice")
row_1 = cursor.fetchone()
utils.foreachMap(row_1)
utils.commitAndClose(conn,cursor)
1.7 調用存儲過程
utils = DBUtils()
conn = utils.getConnection(host,port,user,password,db,charset)
cursor = conn.cursor()
# 遊標設置爲字典類型
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 調用無參存儲過程 等價於cursor.execute("call p2()")
cursor.callproc('p2')
# 調用有參存儲過程
cursor.callproc('p1', args=("nicky",28))
# 獲取執行完存儲的參數,參數@開頭
cursor.execute("select @p1,@_p1_1,")
row_1 = cursor.fetchone()
utils.foreachMap(row_1)
utils.commitAndClose(conn,cursor)
1.8 防止pymysql SQL 注入
第一:字符串拼接造成的注入
比如:
utils = DBUtils()
conn = utils.getConnection(host,port,user,password,db,charset)
cursor = conn.cursor()
user = "jacky' or '1'-- "
deptno = "u1pass"
sql = "select username,job from empinfo where username='%s' and deptno='%s'" % (user, deptno)
# 拼接語句被構造成下面這樣,永真條件,此時就注入成功了。因此要避免這種情況需使用pymysql提供的參數化查詢。
# SELECT username,job From empinfo WHERE username='jacky' or 1'-- and deptno='%s'" % (user, deptno)
row_count = cursor.execute(sql)
row_1 = cursor.fetchone()
utils.commitAndClose(conn,cursor)
解決辦法: 使用pymysql提供的參數化查詢
utils = DBUtils()
conn = utils.getConnection(host,port,user,password,db,charset)
cursor = conn.cursor()
user = "jacky' or '1'-- "
deptno = "u1pass"
# 避免注入,使用pymysql提供的參數化語句
row_count = cursor.execute("select username,job from empinfo where username=%s and deptno=%s" % (user, deptno))
row_1 = cursor.fetchone()
utils.commitAndClose(conn,cursor)
它在內部解析 的時候,會對特殊進行轉義
第二:使用存mysql儲過程動態執行SQL防注入
1.9 使用with簡化連接過程
每次都連接關閉很麻煩,使用上下文管理,簡化連接過程
import pymysql
import contextlib
#定義上下文管理器,連接後自動關閉連接
@contextlib.contextmanager
def mysql(host, port, user, password, db, charset='utf8'):
conn = pymysql.connect(host=host, port=port, user=user, password=password, db=db, charset=charset)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
try:
yield cursor
finally:
conn.commit()
cursor.close()
conn.close()
# 執行sql
with mysql() as cursor:
print(cursor)
row_count = cursor.execute("select * from invoice")
row_1 = cursor.fetchone()
print(row_count, row_1)
二 Memcached 相關python操作
import memcache
class MemcachedUtils(object):
def __init__(self,hostname,port):
self.hostname =
hostname
self.port =
port
def getMemcachedClient(self,debug):
#{'192.168.3.40:11211'} 或者['192.168.3.40:11211s']
# _servers = {"%s:%s"%(self.hostname,self.port)}
_servers = ["%s:%s" % (self.hostname,
self.port)]
c = memcache.Client(_servers,debug=debug)
return c
tools = MemcachedUtils("192.168.3.40",11211)
client = tools.getMemcachedClient(True)
# add(k,v):添加鍵值對,
不能添加重複的key,否則報錯
# client.add('foo','bar')
# replace():更新鍵值對,如果key存在則更新,不存在則異常
client.replace('foo','balance')
# set(k,v):設置鍵值對,相當於添加,和add的區別在於,它不存在則添加,存在則修改
client.set('dname','科技部')
# set_multiu({k1:v1,k2:v2}):設置多個鍵值對,它不存在則添加,存在則修改
client.set_multi({'food':'noodle','foo':'hah'})
# append(k): 修改指定key的值,在該值後面追加內容
client.append('foo','_end')
# prepend(k):修改指定key的值,在該值前面插入內容
client.prepend('foo','start_')
# incr(k): 根據k值使得對應值加1
client.incr('num')
# decr(k): 根據k值使得對應值減1
client.decr('num')
# delete(k): 根據key刪除鍵值對
client.delete('foo')
# delete([k1,k2]): 根據多個key,刪除多個鍵值對
client.delete_multi('x','y')
# get(k):查詢鍵值對
client.get('foo')
# get_multi([k1,k2]):批量查詢
client.get(['k1','k2'])
'''
每次執行gets時,會從memcache中獲取一個自增的數字,通過cas去修改gets的值時,
會攜帶之前獲取的自增值和memcache中的自增值進行比較,如果相等,則可以提交,如
果不想等,那表示在gets和cas執行之間,又有其他人執行了gets(獲取了緩衝的指定值
),如此一來有可能出現非正常數據,則不允許修改。
'''
v = client.gets('product_count')
# ...
# 如果有人在gets之後和cas之前修改了product_count,那麼,下面的設置將會執行失敗,剖出異常,從而避免非正常數據的產生
client.cas('product_count',
"899")
三 Redis
import redis
'''
redis-py提供兩個類Redis和StrictRedis用於實現Redis的命令,StrictRedis用於實現大部分官方的命令,
並使用官方的語法和命令,Redis是StrictRedis的子類,用於向後兼容舊版本的redis-py。
'''
r = redis.Redis(host="192.168.3.40",port=6379)
r.set('foo','bar')
print(r.get('foo'))
'''
redis 連接池
redis-py使用connection pool來管理對一個redis server的所有連接,避免每次建立、釋放連接的開銷。默認,
每個Redis實例都會維護一個自己的連接池。可以直接建立一個連接池,然後作爲參數Redis,
這樣就可以實現多個Redis實例共享一個連接池。
'''
pool = redis.ConnectionPool(host='192.168.3.40',
port=6379)
r = redis.Redis(connection_pool=pool)
r.set('foo', 'bar1')
print(str(r.get('foo'),'utf8'))
3.1 string操作
'''
set(name, value, ex=None, px=None, nx=False, xx=False)
ex:過期時間(秒)
px: 過期時間(毫秒)
nx: 當只有Name不存在時,當前set操作才執行
xx: 只有當Name存在時,才執行當前操作
'''
r.set('foo','bar2',ex=1,xx=True)
# 結果爲bar2
print(str(r.get('foo'),'utf8'))
r.set('foo','bar3',ex=1,nx=True)
# 結果爲bar2
print(str(r.get('foo'),'utf8'))
'''
setnx(name,value):
不能存在才修改
setex(name,value,time): 設置過期時間(秒)
psetex(name, time_ms, value):設置過期時間(毫秒)
mset(*args, **kwargs): 批量設置值
mget:批量獲取
mget('key1','key2') or mget(['key1','key2'])
getset(name, value): 設置新的值並獲取原來的值
getrange(key, start, end)
'''
r.setnx('foo','bar3')
# 還是打印bar1,因爲foo已經存在
print(str(r.get('foo'),'utf8'))
r.setex('address','NewYork Royal Road #4',1)
print(str(r.get('address'),'utf8'))
r.psetex('message',1000,'haha')
print(str(r.get('message'),'utf8'))
r.mset(name="Nicky",age=28,gender="male",hobby=["booking","sporting"])
r.mset({'key1':'value1','key2':'value2'})
resultList = r.mget(['name','age'])
resultList = r.mget('gender','hobby')
for result in resultList:
print(str(result,'utf8'))
x = r.getset('foo','new bar')
print(str(x,'utf8'))
# getrange(name,start,end):
根據字節獲取子序列
r.set('name','鄭涵月')
v = r.getrange('name',0,2)
# 鄭
# setrange(name, offset, value):修改字符串內容,從指定字符串索引開始向後替換(新值太長時,則向後添加)
r.setrange('name',3,'欣寒')
v = r.get('name')
# strlen(name): 返回name對應值的長度
len = r.strlen('name')
# 9
# incr(self, name, amount=1):自增
r.set('age','28')
r.incr('age',amount=2)
# age=30
# incrbyfloat(self, name, amount=1.0):浮點數自增
r.incrbyfloat('age',amount=2.5)
# 32.5
# decr(self, name, amount=1) 自減
# r.decr('age',amount=2)
# append(key, value):在key值後面加上value
r.append('name',',我喜歡你')
print(str(r.get('name'),'utf-8'))
3.2 hash操作
對提供的字符串等進行hash操作,我們就可以根據得到的hash值去標記桶,只要hash值相同,就往這個桶裏扔
# hset(key,field,value):將哈希表 key
中的字段 field
的值設爲 value 。
r.hset('myhash','color','yellow')
r.hset('myhash','length','5')
r.hset('myhash','weight','20')
# hash表中批量設置鍵值對
r.hmset('myhash', {'country':'china','address':'四川成都'})
# 在hash表中根據key獲取value
v = r.hget('myhash','address')
# 在hash表中批量獲取
v = r.hmget('myhash',['color','length','length','weight'])
# hgetall(name):獲取該hash表中所有value
v = r.hgetall('myhash')
# hlen(name) 獲取hash表對應的hash中鍵值對的個數
print(r.hlen('myhash'))
# hkeys(name): 獲取name對應的hash中所有的key的值
# hvals(name): 獲取name對應的hash中所有的value的值
r.hkeys('myhash')
r.hvals('myhash')
# hexists(name, key):檢查name對應的hash是否存在當前傳入的key
r.hexists('myhash','color')
# hdel(name,*keys)將name對應的hash中指定key的鍵值對刪除
r.hdel('myhahs',['k1','k2'])
# hincrby(name,key,amount=1):自增name對應的hash中的指定key的值,不存在則創建key=amount
# hincrbyfloat(name, key, amount=1.0) 同理
# hscan(name, cursor=0, match=None, count=None)
# 量式迭代獲取,對於數據大的數據非常有用,hscan可以實現分片的獲取數據,並非一次性將數據全部獲取完,從而放置內存被撐爆
# name,redis的name
# cursor,遊標(基於遊標分批取獲取數據)
# match,匹配指定key,默認None
表示所有的key
# count,每次分片最少獲取個數,默認None表示採用Redis的默認分片個數
# 如:
# 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
# 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
# ...
# 直到返回值cursor的值爲0時,表示數據已經通過分片獲取完畢
# hscan_iter(name, match=None, count=None):利用yield封裝hscan創建生成器,實現分批去redis中獲取數據
# match,匹配指定key,默認None
表示所有的key
# count,每次分片最少獲取個數,默認None表示採用Redis的默認分片個數
3.3 list操作
r = redis.Redis(host="192.168.3.40",port=6379)
# lpush(name,values):在name對應的list中添加元素,每個新的元素都添加到列表的最左邊
# rpush(name, values) 表示從右向左操作
# r.lpush('numList',11,22,33)
# r.rpush('numList',44,55,66)
# lpushx(name,value):
# rpushx(name, value) 表示從右向左操作
# r.lpushx('numList',11)
# llen(name):name對應的list元素的個數
v = r.llen('numList')
'''
linsert(name, where, refvalue, value)):在name對應的列表的某一個值前或後插入一個新值
# 參數:
name,redis的name
where,BEFORE或AFTER
refvalue,標杆值,即:在它前後插入數據
value,要插入的數據
'''
'''
r.lset(name, index, value):# r.lset(name, index, value):
# name,redis的name
# index,list的索引位置
# value,要設置的值
'''
r.lset('foo',2,'hah')
# r.lrem(name, value, num):在name對應的list中刪除指定的值
# name,redis的name
# value,要刪除的值
# num, num=0,刪除列表中所有的指定值;
# num=2,從前到後,刪除2個;
# num=-2,從後向前,刪除2個
# lpop(name):在name對應的列表的左側獲取第一個元素並在列表中移除,返回值則是第一個元素
# rpop:從右往左操作
# lindex(name, index):在name對應的列表中根據索引獲取列表元素
elements = r.lindex('numList',11)
# lrange(name, start, end)
# name,redis的name
# start,索引的起始位置
# end,索引結束位置
r.lrange('numList',0,2)
# ltrim(name, start, end):在name對應的列表中移除沒有在start-end索引之間的值
r.ltrim('NUMlIST',2,3)
# rpoplpush(src, dst):從一個列表取出最右邊的元素,同時將其添加至另一個列表的最左邊
# src,要取數據的列表的name
# dst,要添加數據的列表的name
r.lpush('list1',20)
r.lpush('list2',10,30,40)
r.rpoplpush('list2','list1')
# 結果就是[20 10]
# 從一個列表的右側移除一個元素並將其添加到另一個列表的左側
# src,取出並要移除元素的列表對應的name
# dst,要插入元素的列表對應的name
# r.brpoplpush('list1', 'list2', timeout=2)
# r.pop(src)從給定列表的末尾處取出一個元素,並從隊列移除
# r.lpop('list2')
# 自定義增量迭代
def list_iter(r,name):
count = r.llen(name)
for index in range(count):
yield r.lindex(name,index)
def foreach(r,name):
elements = list_iter(r,name)
for item in elements:
print(str(item,
"UTF-8") + " ")
foreach(r,'list1')
foreach(r,'list2')
3.4 set操作
'''
Set集合就是不允許重複的列表
'''
# sadd(name,values):向集合添加元素
r.sadd('文科','語文','數學','英語','文綜')
r.sadd('理科','語文','數學','英語','理綜')
# scard(name):獲取name對應的集合中元素個數
print(r.scard('文科'))
# sdiff(keys, *args):在第一個name對應的集合中且不在其他name對應的集合的元素集合
elements = r.sdiff('文科','理科')
for i in elements:
print(str(i,"UTF-8"))
# sdiffstore(dest, keys, *args)獲取第一個name對應的集合中且不在其他name對應的集合,再將其新加入到dest對應的集合中
r.sdiffstore('高一','文科','理科')
# sinter(keys, *args): 獲取集合的交集
elements = r.sinter('文科','理科')
for i in elements:
print(str(i,"UTF-8"))
# sinterstore(dest, keys, *args):獲取多個集合交集,然後放到一個新的目的集合
r.sinterstore('高一','文科','理科')
# sunion(keys, *args):獲取多個集合的並集
r.sunion('文科','理科')
# sunionstore(dest,keys, *args):獲取多一個name對應的集合的並集,並將結果保存到dest對應的集合中
r.sunionstore('高三','文科','理科')
# sismember(name, value): 檢查value是否是name集合成員
r.sismember('文科','語文')
# smembers(name):獲取某個集合所有成員
r.smembers('理科')
# smove(src, dst, value):將某一個成員從src移到dest集合中
r.smove('文科','理科','文綜')
for i in r.smembers('理科'):
print(str(i,"UTF-8"))
# spop(name): 從集合的右側(尾部)移除一個成員,並將其返回
r.spop('高一')
# srandmember(name, numbers):從name對應的集合中隨機獲取 numbers
個元素
r.srandmember('文科',2)
# 在name對應的集合中刪除某些值
r.srem('高一',
'語文','數學')
'''
用於增量迭代分批獲取元素,避免內存消耗太大
sscan(name, cursor=0, match=None, count=None)
sscan_iter(name, match=None, count=None)
'''
elements = r.sscan('文科',count=2)
for item in elements[1]:
print(str(item,'UTF-8'))
3.5 有序集合
'''
有序集合,在集合的基礎上,爲每元素排序;元素的排序需要根據另外一個值來進行比較,
所以,對於有序集合,每一個元素有兩個值,即:值和分數,分數專門用來做排序。
'''
# zadd(name, *args, **kwargs): 添加元素,且需要提供分數
r.zadd('animals','lions',1,'tiger',2,'bulk',3)
r.zadd('vegetables',tomato=1,eggplant=2)
# zcard(name):獲取name對應的有序集合元素的數量
r.zcard('animals')
#zcount(name, min, max): 計算分數在[min,max]之間的元素的數量
r.zcount('animals',2,3)
# zincrby(name, value, amount):自增name對應的有序集合的 name
對應的分數
r.zincrby('vegetables','tomato',amount=40)
'''
可以按照範圍獲取數據
r.zrange( name, start, end, desc=False, withscores=False,score_cast_func=float)
# name,redis的name
# start,有序集合索引起始位置(非分數)
# end,有序集合索引結束位置(非分數),如果爲-1,表示區全部數據,-2表示倒數第2爲止
# desc,排序規則,默認按照分數從小到大排序
# withscores,是否獲取元素的分數,默認只獲取元素的值
# score_cast_func,對分數進行數據轉換的函數
'''
#r.zadd('moutains','泰山',80,'黃山',85,'峨眉山',90,'衡山',75,'恆山',60,'武當山','75','青城山',70)
elements = r.zrange('moutains',start=0,end=-1,desc=True,score_cast_func=int)
'''
從大到小排序,不用我們自己指定是否降序
zrevrange(name, start, end, withscores=False, score_cast_func=float)
'''
elements = r.zrevrange('moutains',start=0,end=3,score_cast_func=int)
'''
zrangebyscore(name, min, max, start=None, num=None, withscores=False,score_cast_func=float)
按照分數範圍獲取name對應的有序集合的元素,自動排序
# name:redis的name
# min: 最小分數值
# max: 最大分數值
# start: 從分數的結果哪一個索引位開始
# num: 從索引位開始取多少個數據
'''
elements = r.zrangebyscore('moutains',min=75,max=100,start=1,num=2,score_cast_func=int)
'''
從大到小排序
zrevrangebyscore(name, max, min, start=None, num=None, withscores=False,score_cast_func=float)
'''
elements = r.zrevrangebyscore('moutains',min=75,max=100,start=1,num=2,score_cast_func=int)
# zrank(name, value) 獲取某個值在 name對應的有序集合中的排行,升序
# zrevrank(name, value),從大到小排序,降序
rank = r.zrank('moutains','武當山')
rank = r.zrevrank('moutains','武當山')
# zrem(name,values):刪除name對應的有序集合中值是values的成員
r.zrem('moutains',['武當山','青城山'])
# zremrangebyrank(name, min, max):根據rank進行範圍刪除
elements = r.zremrangebyrank('moutains',min=0,max=2)
# zremrangebyscore(name, min, max):根據分數範圍刪除
elements = r.zremrangebyrank('moutains',min=60,max=70)
# zscore(name, value):獲取name對應有序集合中 value
對應的分數
r.zscore('moutains','峨眉山')
'''
zinterstore(dest, keys, aggregate=None)
# 獲取兩個有序集合的交集,如果遇到相同值不同分數,則按照aggregate進行操作
# aggregate的值爲: SUM MIN MAX
zunionstore(dest, keys, aggregate=None)
# 獲取兩個有序集合的並集,如果遇到相同值不同分數,則按照aggregate進行操作
# aggregate的值爲: SUM MIN MAX
'''
'''
zscan(name, cursor=0, match=None, count=None, score_cast_func=float)
zscan_iter(name, match=None, count=None,score_cast_func=float)
'''
3.6 其他命令
#
根據刪除redis名字中的任意數據類型
r.delete('animal','animals')
# 檢測redis的name是否存在
v = r.exists('animals')
# 根據正則獲取redis的name
s = r.keys("*")
# 爲某個redis的某個name設置超時時間
r.expire('mountains',1000)
# 對redis的name重命名爲
r.rename('animal', 'animals')
# move(name, db))將redis的某個值移動到指定的db下
# 隨機獲取一個redis的name(不刪除)
r.randomkey()
# type(name):獲取name對應值的類型
3.7 管道
Redis python每一次執行請求都會在連接池申請連接,和歸還連接,如果想要在一個請求中執行多個命令,你可以使用piepline,並且默認情況下一次pipline 是原子性操作。
pool = redis.ConnectionPool(host='192.168.3.40',
port=6379)
r = redis.Redis(connection_pool=pool)
pipe = r.pipeline(transaction=True)
pipe.set('name', 'alex')
pipe.lpush('elements',12,3,4,5)
pipe.execute()
3.8 發佈訂閱
class
MessageQueue:
def __init__(self):
self.__conn = redis.Redis(host="192.168.3.40",port=6379)
self.subscribe_channel = 'music'
self.publish_channel =
'music'
def publish(self,
msg):
self.__conn.publish(self.publish_channel,
msg)
return True
def subscribe(self):
pub = self.__conn.pubsub()
pub.subscribe(self.subscribe_channel)
pub.parse_response()
return pub
mq = MessageQueue()
sub = mq.subscribe()
while True:
msg = sub.parse_response()
print(msg)
from db.tmp import MessageQueue
mq = MessageQueue()
mq.public('你好美')
四 RabbitMQ
import pika
'''
對於RabbitMQ來說,生產和消費不再針對內存裏的一個Queue對象,而是某臺服務器上的RabbitMQ
Server實現的消息隊列。
'''
class MQTools(object):
def getConnection(self):
params = pika.ConnectionParameters(host='192.168.3.40',port=22222)
conn = pika.BlockingConnection(params)
return conn
class Producer(object):
def handle(self):
tools = MQTools()
conn = tools.getConnection()
channel = conn.channel()
channel.queue_declare(queue='animals')
channel.basic_publish(exchange='',routing_key='animals',body='tiger')
conn.close()
class Consumer(object):
def handle(self):
tools = MQTools()
conn = tools.getConnection()
channel = conn.channel()
channel.queue_declare(queue='animals')
channel.basic_consume(self.callback,queue='animals',no_ack=True)
channel.start_consuming()
def callback(ch,
method, properties,
body):
print(" [x] Received %r"
% body)
'''
1、acknowledgment
消息不丟失
no-ack = False,如果消費者遇到情況(its channelis closed, connection is
closed, or TCP connection is lost)掛掉了,那麼,RabbitMQ會重新將該任務添加到隊列中
'''
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.3.40'))
channel = connection.channel()
channel.queue_declare(queue='animals')
def callback(ch,
method, properties,
body):
print(" [x] Received %r"
% body)
import time
time.sleep(10)
print('ok')
ch.basic_ack(delivery_tag
= method.delivery_tag)
channel.basic_consume(callback,queue='animals',no_ack=False)
print(' [*] Waiting formessages. To exit press CTRL+C')
channel.start_consuming()
'''
2 durable 消息不丟失
'''
# 生產者
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.3.40'))
channel = connection.channel()
# make message persistent
channel.queue_declare(queue='animal',
durable=True)
channel.basic_publish(exchange='',routing_key='animal',
body='lions!',properties=pika.BasicProperties(delivery_mode=2,
# make message persistent
))
print(" [x] Sent'Hello World!'")
connection.close()
# 消費者
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4'))
channel = connection.channel()
# make message persistent
channel.queue_declare(queue='hello',
durable=True)
def callback(ch,
method, properties,
body):
print(" [x] Received %r"
% body)
import time
time.sleep(10)
ch.basic_ack(delivery_tag
= method.delivery_tag)
channel.basic_consume(callback,queue='hello',no_ack=False)
print(' [*] Waiting formessages. To exit press CTRL+C')
channel.start_consuming()
'''
3、消息獲取順序
默認消息隊列裏的數據是按照順序被消費者拿走,例如:消費者1
去隊列中獲取奇數序列的任務,消費者1去隊列中
獲取偶數序列的任務。channel.basic_qos(prefetch_count=1)
表示誰來誰取,不再按照奇偶數排列
'''
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4'))
channel = connection.channel()
# make message persistent
channel.queue_declare(queue='hello')
def callback(ch,
method, properties,
body):
print(" [x] Received %r"
% body)
import time
time.sleep(10)
ch.basic_ack(delivery_tag
= method.delivery_tag)
channel.basic_qos(prefetch_count=1)
channel.basic_consume(callback,queue='hello',no_ack=False)
print(' [*] Waiting formessages. To exit press CTRL+C')
channel.start_consuming()
'''
4、發佈訂閱
發佈訂閱和簡單的消息隊列區別在於,發佈訂閱會將消息發送給所有的訂閱者,而消息隊列中的數據被消費一次便消失。
所以,RabbitMQ實現發佈和訂閱時,會爲每一個訂閱者創建一個隊列,而發佈者發佈消息時,會將消息放置在所有相關隊列中。
'''
# 發佈者
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.3.40'))
channel = connection.channel()
channel.exchange_declare(exchange='logs',type='fanout')
message = ' '.join(sys.argv[1:]) or
"info: Hello World!"
channel.basic_publish(exchange='logs',routing_key='',body=message)
print(" [x] Sent%r"
% message)
connection.close()
# 訂閱者
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.3.40'))
channel = connection.channel()
channel.exchange_declare(exchange='logs',type='fanout')
result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue
channel.queue_bind(exchange='logs',queue=queue_name)
print(' [*] Waiting forlogs. To exit press CTRL+C')
def callback(ch,
method, properties,
body):
print(" [x] %r"
% body)
channel.basic_consume(callback,queue=queue_name,no_ack=True)
channel.start_consuming()
'''
5、關鍵字發送
之前事例,發送消息時明確指定某個隊列並向其中發送消息,RabbitMQ還支持根據關鍵字發送,
即:隊列綁定關鍵字,發送者將數據根據關鍵字發送到消息exchange,exchange根據關鍵字
判定應該將數據發送至指定隊列。
'''
#生產者
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.3.40'))
channel = connection.channel()
channel.exchange_declare(exchange='direct_logs',type='direct')
severity = sys.argv[1] if len(sys.argv) >
1 else 'info'
message = ' '.join(sys.argv[2:]) or
'Hello World!'
channel.basic_publish(exchange='direct_logs',routing_key=severity,body=message)
print(" [x] Sent%r:%r"
% (severity, message))
connection.close()
# 消費者
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.3.40'))
channel = connection.channel()
channel.exchange_declare(exchange='direct_logs',type='direct')
result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue
severities = sys.argv[1:]
if not severities:
sys.stderr.write("Usage:%s [info] [warning] [error]\n"
% sys.argv[0])
sys.exit(1)
for severity in severities:
channel.queue_bind(exchange='direct_logs',queue=queue_name,routing_key=severity)
print(' [*] Waiting forlogs. To exit press CTRL+C')
def callback(ch,
method, properties,
body):
print(" [x] %r:%r"
% (method.routing_key,
body))
channel.basic_consume(callback,queue=queue_name,no_ack=True)
channel.start_consuming()
'''
6、模糊匹配
exchange type = topic
在topic類型下,可以讓隊列綁定幾個模糊的關鍵字,之後發送者將數據發送到exchange,
exchange將傳入”路由值“和 ”關鍵字“進行匹配,匹配成功,則將數據發送到指定隊列
# 表示可以匹配 0
個或多個單詞
* 表示只能匹配一個單詞
'''
# 生產者
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.3.40'))
channel = connection.channel()
channel.exchange_declare(exchange='topic_logs',type='topic')
routing_key = sys.argv[1] if len(sys.argv) >
1 else 'anonymous.info'
message = ' '.join(sys.argv[2:]) or
'Hello World!'
channel.basic_publish(exchange='topic_logs',routing_key=routing_key,body=message)
print(" [x] Sent%r:%r"
% (routing_key, message))
connection.close()
# 消費者
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='192.168.3.40'))
channel = connection.channel()
channel.exchange_declare(exchange='topic_logs',type='topic')
result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue
binding_keys = sys.argv[1:]
if not binding_keys:
sys.stderr.write("Usage:%s [binding_key]...\n"
% sys.argv[0])
sys.exit(1)
for binding_key in binding_keys:
channel.queue_bind(exchange='topic_logs',queue=queue_name,routing_key=binding_key)
print(' [*] Waiting forlogs. To exit press CTRL+C')
def callback(ch,
method, properties,
body):
print(" [x] %r:%r"
% (method.routing_key,
body))
channel.basic_consume(callback,queue=queue_name,no_ack=True)
channel.start_consuming()