Python之隊列和數據庫

一 數據庫

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中的自增值進行比較,如果相等,則可以提交,如
果不想等,那表示在getscas執行之間,又有其他人執行了gets(獲取了緩衝的指定值
),如此一來有可能出現非正常數據,則不允許修改。
'''
v = client.gets('product_count')
# ...
#
如果有人在gets之後和cas之前修改了product_count,那麼,下面的設置將會執行失敗,剖出異常,從而避免非正常數據的產生
client.cas('product_count', "899")

 
三 Redis

import redis

'''
redis-py
提供兩個類RedisStrictRedis用於實現Redis的命令,StrictRedis用於實現大部分官方的命令,
並使用官方的語法和命令,RedisStrictRedis的子類,用於向後兼容舊版本的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
redisname
# 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
redisname
where
BEFOREAFTER
refvalue
,標杆值,即:在它前後插入數據
value
,要插入的數據
'''

'''
r.lset(name, index, value):# r.lset(name, index, value):
# name
redisname
    # index
list的索引位置
    # value
,要設置的值
'''
r.lset('foo',2,'hah')

# r.lrem(name, value, num):name對應的list中刪除指定的值
# name
redisname
    # 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
redisname
# 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): 計算分數在[minmax]之間的元素的數量
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
redisname
# 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
redisname
# 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')

# 檢測redisname是否存在
v = r.exists('animals')

根據正則獲取redisname
s = r.keys("*")

# 爲某個redis的某個name設置超時時間
r.expire('mountains',1000)

# redisname重命名爲
r.rename('animal', 'animals')

# move(name, db))redis的某個值移動到指定的db

#
隨機獲取一個redisname(不刪除)
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還支持根據關鍵字發送,
即:隊列綁定關鍵字,發送者將數據根據關鍵字發送到消息exchangeexchange根據關鍵字
 
判定應該將數據發送至指定隊列。
'''
#生產者
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()

 
發佈了317 篇原創文章 · 獲贊 115 · 訪問量 66萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章