史上最快消息內核——ZeroMQ(轉)


    打算學習一下ZeroMQ,好好的研究研究其代碼,所以轉這篇文章作為學習的開始.......

ZeroMQ是一個很有個性的項目,它原來是定位為“史上最快消息隊列”,所以名字里面有“MQ”兩個字母,但是后來逐漸演變發展,慢慢淡化了消息隊列的身影,改稱為消息內核,或者消息層了。從網絡通信的角度看,它處於會話層之上,應用層之下,有了它,你甚至不需要自己寫一行的socket函數調用就能完成復雜的網絡通信工作。

借用官方的例子:

客戶端(發送N個“Hello”消息到服務端,接受回應):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//
//  Hello World client
//  Connects REQ socket to tcp://localhost:5555
//  Sends "Hello" to server, expects "World" back
//
#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
 
int main () {
     void *context = zmq_init (1);
 
     //  Socket to talk to server
     printf ( "Connecting to hello world server...\n" );
     void *requester = zmq_socket (context, ZMQ_REQ);
     zmq_connect (requester, "tcp://localhost:5555" );
 
     int request_nbr;
     for (request_nbr = 0; request_nbr != 10; request_nbr++) {
         zmq_msg_t request;
         zmq_msg_init_data (&request, "Hello" , 6, NULL, NULL);
         printf ( "Sending request %d...\n" , request_nbr);
         zmq_send (requester, &request, 0);
         zmq_msg_close (&request);
 
         zmq_msg_t reply;
         zmq_msg_init (&reply);
         zmq_recv (requester, &reply, 0);
         printf ( "Received reply %d: [%s]\n" , request_nbr,
             ( char *) zmq_msg_data (&reply));
         zmq_msg_close (&reply);
     }
     zmq_close (requester);
     zmq_term (context);
     return 0;
}

服務端(接收客戶端的消息,返回“World”給客戶端):

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//
//  Hello World server in C++
//  Binds REP socket to tcp://*:5555
//  Expects "Hello" from client, replies with "World"
//
#include <zmq.hpp>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
 
int main () {
     //  Prepare our context and socket
     zmq::context_t context (1);
     zmq::socket_t socket (context, ZMQ_REP);
     socket.bind ( "tcp://*:5555" );
 
     while ( true ) {
         zmq::message_t request;
 
         //  Wait for next request from client
         socket.recv (&request);
         printf ( "Received request: [%s]\n" ,
             ( char *) request.data ());
 
         //  Do some 'work'
         sleep (1);
 
         //  Send reply back to client
         zmq::message_t reply (6);
         memcpy (( void *) reply.data (), "World" , 6);
         socket.send (reply);
     }
     return 0;
}

一個套接字相關的調用都沒有,一個網絡程序就寫好了,生活真美好啊。

與其他的消息隊列相比,ZeroMQ有以下一些特點

1.點對點無中間節點。

傳統的消息隊列都需要一個消息服務器來存儲轉發消息。而ZeroMQ則放棄了這個模式,把側重點放在了點對點的消息傳輸上,並且(試圖)做到極致。以為消息服務器最終還是轉化為服務器對其他節點的點對點消息傳輸上。ZeroMQ能緩存消息,但是是在發送端緩存。ZeroMQ里有水位設置的相關接口來控制緩存量。當然,ZeroMQ也支持傳統的消息隊列(通過zmq_device來實現)。

2.強調消息收發模式。

在點對點的消息傳輸上ZeroMQ將通信的模式做了歸納,比如常見的訂閱模式(一個消息發多個客戶),分發模式(N個消息平均分給X個客戶)等等。下面是目前支持的消息模式配對,任何一方都可以做為服務端。

  • PUB and SUB
  • REQ and REP
  • REQ and XREP
  • XREQ and REP
  • XREQ and XREP
  • XREQ and XREQ
  • XREP and XREP
  • PUSH and PULL
  • PAIR and PAIR

3.以統一接口支持多種底層通信方式(線程間通信,進程間通信,跨主機通信)。

如果你想把本機多進程的軟件放到跨主機的環境里去執行,通常要將IPC接口用套接字重寫一遍。非常麻煩。而有了ZeroMQ就方便多了,只要把通信協議從"ipc:///xxx"改為"tcp://*.*.*.*:****"就可以了,其他代碼通通不需要改,如果這個是從配置文件里讀的話,那么程序就完全不要動了,直接復制到其他機器上就可以了。以為ZeroMQ為我們做了很多。

4.異步,強調性能。

ZeroMQ設計之初就是為了高性能的消息發送而服務的,所以其設計追求簡潔高效。它發送消息是異步模式,通過單獨出一個IO線程來實現,所以消息發送調用之后不要立刻釋放相關資源哦,會出錯的(以為還沒發送完),要把資源釋放函數交給ZeroMQ讓ZeroMQ發完消息自己釋放。

目前ZeroMQ還不是非常成熟(本文寫作時最新版是2.0.10版),設計上還有一點點小缺陷,比如不能得到客戶端的IP,丟消息等。不過,開發很活躍,很有潛力。另外,ZeroMQ配合Protocol buffer使用真是絕了。

from:http://blog.dccmx.com/2011/02/zeromq/

 

ZeroMQ的想法和初衷是好的,讓你用串行的思路寫異步的消息處理程序。並且在此基礎之上提煉出了常見的消息模式,讓你寫起程序來更加省心。庫替你完成了異步消息發送、故障恢復、甚至連磁盤的buffer都提供了。不可謂不全面。同時提供的接口非常簡潔。

但是,用很少的接口封裝復雜的邏輯通常的結果就是:當你的業務場景跟庫的設計者設想的業務場景一樣時,你回很爽。但當業務有差別,或者對細節有要求時,你會很慘。用trick能解決已經算是較好的情況了。

欺騙性質的封裝。

當你zmq_connect返回成功的時候,不要高興,這並不意味着你真的創建好鏈接了,因為即使你連一個根本沒人監聽的地址也會成功,為什么呢,因為ZeroMQ認為它提供了故障恢復自動重連的機制。后面會自動重連上的。可是如果是地址錯了或者對方掛了,這輩子都不會連上了,這庫就傻掉了。

再比如,你發個消息,又返回成功,也不要高興,這並不意味着對方已經能收到了。因為消息是異步發送的,而且ZeroMQ提供了消息緩存,暫時發不出去,ZeroMQ認為后面是能發得出去的。於是消息被緩存起來。於是你想設下這個緩存,發現最低為1,也就是說至少緩存一條消息。如果這消息發不出去,你也收不回來。程序如果要停,消息就丟了。這跟ZeroMQ實現異步的方式有關。ZeroMQ是用線程的方式實現異步的。如果不緩存到IO線程,那還怎么個異步法。

過度抽象且不提供細節。

ZeroMQ建立在socket之上,隱藏了細節。但是,有時候當你需要這些細節的時候,就很糾結了。比如我收到一個消息,我想知道底層連接的信息,似乎是不大可能的,因為一個ZeroMQ套接字后面可能有很多底層的套接字(而且你不可控,也不可知)。類似的情況還有不能訪問消息緩沖等等。

總之,ZeroMQ把網絡通信從頭到腳封裝了一遍,提供了一個整體的解決方案。如果我們全盤接受這個解決方案,那么沒有問題,但如果只需要一部分,並且對其他部分有要求,那么會很難受。如果應用場景稍微底層一些就會發現,本來可靠的tcp被這么一封裝變得不可靠了,只能把它當作一個高級的UDP來用,弱要強調消息傳輸的可靠性,還需要用戶做很多工作。略顯無奈。

當然,一下抱怨也是基於特定場景(在一些場景下ZeroMQ還是很完美的)。說明ZeroMQ本不是為此而生。又何必強求。但是官方的態度似乎是想將它做成新一代的通用的socket,這應該還有很長的路要走。

from: http://blog.dccmx.com/2011/12/dark-side-of-zeromq/

 

 官方的教程:http://www.zeromq.org/intro:read-the-manual


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM