標籤:
簡介: zeromq中介軟體,他是一個輕量級的訊息中介軟體,傳說是世界上最快的訊息中介軟體,為什麼這麼說呢? 因為一般的訊息中介軟體都需要啟動Message Service器,但是zeromq這廝盡然沒有Message Service器,他壓根沒有訊息中介軟體的架子,但是這並不能掩蓋他的強大。 通過和activemq,rabbitmq對比,顯然功能上沒有前兩者這麼強大,他不支援訊息的持久化,但是有訊息copy功能,他也不支援崩潰恢複,而且由於他太快了,可能用戶端還沒啟動,服務端的訊息就已經發出去了,這個就容易丟訊息了,但是zeromq自由他的辦法,就先說這麼多了。先來看看怎麼在python中引入這個強大的利器。 我自己之所以,學習體會一下,主要原因,是想在練習過程中體會其中的應用原理及邏輯,最好是能感知到其中的設計思想,為以後,自己做東西積攢點經驗. 另外最近也比較關注自動化營運的一些東西.網上說saltstack本身就用的zeromq做訊息佇列.所以更引起了我的興趣. 安裝: 我的作業系統是ubuntu 14.04的 python zeromq 環境安裝參考這裡的官網 下面測試: 一,C/S模式: server 端代碼: #!/usr/bin/env python # coding:utf8 #author: [email protected] import zmq #調用zmq相關類方法,邦定連接埠 context = zmq.Context() socket = context.socket(zmq.REP) socket.bind(‘tcp://*:10001‘) while True: #迴圈接受用戶端發來的訊息 msg = socket.recv() print "Msg info:%s" %msg #向用戶端伺服器發端需要執行的命令 cmd_info = raw_input("client cmd info:").strip() socket.send(cmd_info) socket.close() client 端代碼: import zmq import time import commands context = zmq.Context() socket = context.socket(zmq.REQ) socket.connect(‘tcp://127.0.0.1:10001‘) def execut_cmd(cmd): s,v = commands.getstatusoutput(cmd) return v while True: #擷取目前時間 now_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()) socket.send("now time info:[%s] request execution command:‘\n‘,%s"%(now_time,result)) recov_msg = socket.recv() #調用execut_cmd函數,執行伺服器發過來的命令 result = execut_cmd(recov_msg) print recov_msg,‘\n‘,result, time.sleep(1) #print "now time info:%s cmd status:[%s],result:[%s]" %(now_time,s,v) continue socket.close() 注意:此模式是經典的接聽模式,不能同時send多個資料, 這種模式說是主要用於遠程調用和任務分配,但我愚笨,還是理解不透.後面有時間,再回過來好好看看, 測試: req端 # python zmq-server-cs-v01.py rep端 # python zmq-client-cs-v01.py 二,發布訂閱模式(pub/sub) pub 發布端代碼如下: #!/usr/bin/env python # coding:utf8 #author: [email protected] import itertools import sys,time,zmq def main(): if len(sys.argv) != 2: print ‘Usage: publisher‘ sys.exit(1) bind_to = sys.argv[1] all_topics = [‘sports.general‘,‘sports.football‘,‘sports.basketball‘,‘stocks.general‘,‘stocks.GOOG‘,‘stocks.AAPL‘,‘weather‘] ctx = zmq.Context() s = ctx.socket(zmq.PUB) s.bind(bind_to) print "Starting broadcast on topics:" print "%s" %all_topics print "Hit Ctrl-c to stop broadcasting." print "waiting so subscriber sockets can connect...." print time.sleep(1) msg_counter = itertools.count() try: for topic in itertools.cycle(all_topics): msg_body = str(msg_counter.next()) #print msg_body, print ‘Topic:%s,msg:%s‘ %(topic,msg_body) s.send_multipart([topic,msg_body]) #s.send_pyobj([topic,msg_body]) time.sleep(0.1) except KeyboardInterrupt: pass print "Wating for message queues to flush" time.sleep(0.5) s.close() print "Done" if __name__ == "__main__": main() sub 端代碼: #!/usr/bin/env python # coding:utf8 #author: [email protected] import zmq import time,sys def main(): if len(sys.argv) < 2: print "Usage: subscriber [topic topic]" sys.exit(1) connect_to = sys.argv[1] topics = sys.argv[2:] ctx = zmq.Context() s = ctx.socket(zmq.SUB) s.connect(connect_to) #manage subscriptions if not topics: print "Receiving messages on ALL topics...." s.setsockopt(zmq.SUBSCRIBE,‘‘) else: print "Receiving messages on topics: %s..." %topics for t in topics: s.setsockopt(zmq.SUBSCRIBE,t) print try: while True: topics,msg = s.recv_multipart() print ‘Topic:%s,msg:%s‘ %(topics,msg) except KeyboardInterrupt: pass print "Done...." if __name__ == "__main__": main() 注意: 這裡的發布與訂閱角色是絕對的,即發行者無法使用recv,訂閱者不能使用send,官網還提供了一種可能出現的問題:當訂閱者消費慢於發布, 此時就會出現資料的堆積,而且還是在發布端的堆積(有朋友指出是堆積在消費端,或許是新版本改進,需要讀者的嘗試和反饋,thx!),顯然, 這是不可以被接受的。至於解決方案,或許後面的"分而治之"就是吧 測試: pub端: 發布端 #python zmq-server-pubsub-v02.py tcp://127.0.0.1:10001 sub端:訂閱端 #python zmq-server-cs-v01.py tcp://127.0.0.1:10001 sports.football 三,push/pull 分而治之模式. 任務發布端代碼 #!/usr/bin/env python # coding:utf8 #author: [email protected] import zmq import random import time context = zmq.Context() #socket to send messages on sender = context.socket(zmq.PUSH) sender.bind(‘tcp://*:5557‘) print ‘Press Enter when the workers are ready:‘ _ = raw_input() print "Sending tasks to workers...." #The first messages is "0" and signals start to batch sender.send(‘0‘) #Initialize random mumber generator random.seed() #send 100 tasks total_msec = 0 for task_nbr in range(100): #Random workload from 1 to 100 msecs #print task_nbr, workload = random.randint(1,100) total_msec += workload sender.send(str(workload)) print "Total expected cost:%s msec:%s workload:%s" %(total_msec,task_nbr,workload) work端代碼如下: #!/usr/bin/env python # coding:utf8 #author: [email protected] import sys,time,zmq import commands context = zmq.Context() #socket to receive messages on receiver = context.socket(zmq.PULL) receiver.connect(‘tcp://127.0.0.1:5557‘) #Socket to send messages to sender = context.socket(zmq.PUSH) sender.connect("tcp://127.0.0.1:5558") #Process tasks forever while True: s = receiver.recv() #Simple progress indicator for the viewer print s, sys.stdout.write("%s ‘\t‘ "%s) sys.stdout.flush() #Do the work time.sleep(int(s)*0.001) #Send results to sink sender.send(s) pull端代碼如下: #!/usr/bin/env python # coding:utf8 #author: [email protected] import sys import time import zmq context = zmq.Context() #Socket to receive messages on receiver = context.socket(zmq.PULL) receiver.bind("tcp://*:5558") #Wait for start of batch s = receiver.recv() #Start our clock now tstart = time.time() #Process 100 confirmations total_msec = 0 for task_nbr in range(100): s = receiver.recv() if task_nbr % 10 == 0: print task_nbr, print s, sys.stdout.write(‘:‘) else: print s, #print task_nbr, sys.stdout.write(‘.‘) #Calculate and report duration of batch tend = time.time() print "Total elapsed time:%d msec "%((tend-tstart)*1000) 注意點: 這種模式與pub/sub模式一樣都是單向的,區別有兩點: 1,該模式下在沒有消費者的情況下,發行者的資訊是不會消耗的(由發行者進程維護) 2,多個消費者消費的是同一列資訊,假設A得到了一條資訊,則B將不再得到 這種模式主要針對在消費者能力不夠的情況下,提供的多消費者並行消費解決方案(也算是之前的pub/sub模式的 那個"堵塞問題"的一個解決方案策略吧) 其實所謂的分就是pull端去搶push端發出來的任務.誰搶著算誰的. 測試: #python zmq-server-pushpull-v03.py #python zmq-work-pushpull-v03.py #python zmq-client-pushpull-v03.py
網路編程之python zeromq學習系列之一