[转][转]ZeroMQ 深度探索

标签: | 发表时间:2013-11-01 07:32 | 作者:heiyeshuwu
出处:http://blog.csdn.net/heiyeshuwu


作者:shagoo

来源:

http://blog.csdn.net/shagoo/article/details/8964009
http://blog.csdn.net/shagoo/article/details/9211765




最初认识 ZeroMQ 是被它的名号所吸引,最近在一个高性能中间件的项目中用到了 ZeroMQ,对这个号称“史上最快的消息队列”有了更深层次的了解。如果我们仅仅把 ZeroMQ 看作是一个消息队列,那就完全搞错了,ZeroMQ 是一套智能传输层协议,它不仅为开发者提供了强大的开发包,还包含了一套很棒的通信协议的实现,更值得一提是,它对分布式系统开发有着相当独到的见解,绝对值得我们好好学习。ZeroMQ 的最终目标是加入 Linux 内核,目前 ZeroMQ 已经出现在 YUM 中,相信 ZeroMQ 的未来会越来越美好!

ZeroMQ 特点介绍

1、支持高并发的异步 Socket 框架
2、协议比 TCP 更快、适用于大型集群和分布式计算
3、提供多种消息传递机制,如 inproc/IPC/TCP/multicast 等
4、内置丰富的组合模式,可用于简化大型分步式计算架构
5、提供异步 I/O 模式,适用于可扩展的多核应用开发
6、拥有活跃的开发者社区提供技术支持,发展相当迅速
7、支持超过 30 种的编程语言,如 C/C++/Java/.NET/Python/PHP 等
8、良好的跨平台性,支持多种 OS,如 Linux/Windows/OS X 等
9、拥有 iMatix 公司的商业级别支持,却是完全免费的

ZeroMQ 设计哲学

综合来说,ZeroMQ 的设计哲学在于“权衡”两字,其实 ZeroMQ 的名字就体现了这一点。“Zero”表示从零开始,也就是从无到有;ZeroMQ 不提供现成的安装套件(比如broker),这也意味着使用者必须自己来构建需要的套件,这种做法各有利弊;“利”在于使用者可以自由的构造属于自己的分布式系统,而“弊”则在于门槛比较高,对于不熟悉编程的使用者来说就麻烦大了。不过,相比与通过功能迭代来实现复杂需求的方案来说,ZeroMQ 更倾向于从根本上解决问题,这也正体现了 ZeroMQ 创始人 Pieter Hintjens 的设计哲学。

ZeroMQ 面向用户

ZeroMQ 是为那些对大型分布式系统感兴趣的开发者们而生的!假如你熟悉 C 语言,那么使用 ZeroMQ 将是件非常享受的事情,因为 ZeroMQ 开发包中已经包含了非常丰富的 C 语言的使用范例,有经验的开发者可以快速入手。使用 ZeroMQ 可以为我们节省下大量的编码时间,当然在此之前,我们需要把 ZeroMQ 的基础知识和用法理解透彻,否则误用或者错用的话,后果将是“很严重”的!接下来,我们开始学习 ZeroMQ 的基础知识。

ZeroMQ 学前必读

ZeroMQ 究竟是什么?也许我们应该先“大肆吹嘘”一番,但这显然是毫无意义的,也不是我们技术人员的本色。简单来说,ZeroMQ 是一个更小、更快、更简单的智能传输层协议,它可以帮助我们简化原本非常复杂的事情,实际上,这也就是当初设计 ZeroMQ 时的初衷。

今天,我们的世界变得越来越复杂,我们使用的软件也在随着人类社会的变化而演变。可以预见到的是,未来的软件系统将会变得越来越庞大,就像人类的大脑一样,错综复杂;这个时候,我们必须得把问题分解开来,逐个击破,否则,软件最终只能变成可怕的巨兽,把一切都搞砸。分而治之,讲的就是这个道理,这也就是我们需要分布式系统的原因。

在分布式系统中,代码之间需要通信,此时我们就必须使用网络、协议、线程这些工具来实现;然而,现实情况是,即使我们已经拥有了这些工具,但实现起来仍然非常费功夫。目前业界可用的网络协议比较有限,比如 TCP/UDP/HTTP/Websocket 等;这些协议要么太复杂,要么太笨重,也许我们会抱怨道,难道没有其他的选择了吗?ZeroMQ 是否会是我们期待的答案?下面,让我们回到两个最基本的问题上,一是如何实现代码之间需要通信,二是如何让通信变得更简单高效。

ZeroMQ HelloWorld

接下来,我们先从一个最简单的例子,即 HelloWorld 项目讲起。这就是网络请求中最基本的“请求-响应”模式(Request-Reply),客户端往服务端发送“Hello”,服务端回应“World”,如图1-1。



以下是 HelloWorld 项目的服务端代码(hwserver.c),熟悉 Socket 编程的同学应该很容易理解其中的语法,即使用 TCP 协议,监听 5555 端口,然后不停地接受、打印并返回信息,每次处理后停止 1 秒。

[cpp]  view plain copy
  1. // Hello World server  
  2.   
  3. #include <zmq.h>  
  4. #include <stdio.h>  
  5. #include <unistd.h>  
  6. #include <string.h>  
  7. #include <assert.h>  
  8.   
  9. int main (void)  
  10. {  
  11.     // Socket to talk to clients  
  12.     void *context = zmq_ctx_new ();  
  13.     void *responder = zmq_socket (context, ZMQ_REP);  
  14.     int rc = zmq_bind (responder, "tcp://*:5555");  
  15.     assert (rc == 0);  
  16.   
  17.     while (1) {  
  18.         char buffer [10];  
  19.         zmq_recv (responder, buffer, 10, 0);  
  20.         printf ("Received Hello\n");  
  21.         zmq_send (responder, "World", 5, 0);  
  22.         sleep (1); // Do some 'work'  
  23.     }  
  24.     return 0;  
  25. }  

以下是 HelloWorld 项目的客户端代码(hwclient.c),逻辑也很简单,向服务端连续发送 10 条消息,接受并打印返回信息。

[cpp]  view plain copy
  1. // Hello World client  
  2. #include <zmq.h>  
  3. #include <string.h>  
  4. #include <stdio.h>  
  5. #include <unistd.h>  
  6.   
  7. int main (void)  
  8. {  
  9.     printf ("Connecting to hello world server…\n");  
  10.     void *context = zmq_ctx_new ();  
  11.     void *requester = zmq_socket (context, ZMQ_REQ);  
  12.     zmq_connect (requester, "tcp://localhost:5555");  
  13.   
  14.     int request_nbr;  
  15.     for (request_nbr = 0; request_nbr != 10; request_nbr++) {  
  16.         char buffer [10];  
  17.         printf ("Sending Hello %d…\n", request_nbr);  
  18.         zmq_send (requester, "Hello", 5, 0);  
  19.         zmq_recv (requester, buffer, 10, 0);  
  20.         printf ("Received World %d\n", request_nbr);  
  21.     }  
  22.     zmq_close (requester);  
  23.     zmq_ctx_destroy (context);  
  24.     return 0;  
  25. }  

服务端代码的运行结果如图1-2。



客户端代码的运行结果如图1-3。



运行结果很容易理解,这就是一个标准的“请求-响应”模式的例子。从中我们可以看到使用 ZeroMQ 的类库实现起来还是很简单的,和基础 Socket 库的用法差不多,实现的功能也差不了多少,但是事实是否如此呢?在下一篇《 ZeroMQ 深度探索(二)》中我们将深入讨论这个问题,未完待续...

TIP:建议大家使用 3.2 以上的版本进行开发。以后所有的的例子都是基于 C 语言的。所有的示例代码可以通过“git clone --depth=1 git://github.com/imatix/zguide.git”获取。


在上一篇《 ZeroMQ 深度探索(一)》中,我们使用 ZeroMQ 完成了基本的“请求-响应”模式,这个例子是基于 TCP 协议的,用法和原生的 Socket API 也差不多,都是“初始化、绑定或连接、发送、接收 ...”的流程,但是在一些特殊的情况下,我们发现了一些有趣的现象。我们尝试在未启动服务端(hwserver)的情况下运行客户端(hwclient),我们发现客户端程序在发送了第一条信息之后就阻塞住了,如图2-1。


图2-1

接着,我们再启动服务端程序,发现客户端程序又开始正常运行了,连续发了 10 条消息后退出。这种现象似乎和 Socket API 的 TCP 通信行为不大相同,接下来我们来验证一下。我们使用 Socket API 来实现一个与前面相同的“请求-响应”模式的例子。下面是服务端代码(hwserver2.c),逻辑和之前的 hwserver.c 类似,监听 6666 端口,不停地接受、打印并返回信息,每次处理后停止 1 秒。

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <sys/types.h>  
  5. #include <sys/socket.h>  
  6. #include <string.h>  
  7. #include <arpa/inet.h>  
  8.   
  9. #define PORTNUM 6666  
  10. #define CONNMAX 5  
  11. #define BUFFSIZE 32  
  12. #define die(err) { perror(err); exit(1); }  
  13.   
  14. void c_action(int sock)  
  15. {  
  16.     char buffer[BUFFSIZE];  
  17.     int received = -1;  
  18.     char *send_s = "World";  
  19.     // 接收消息  
  20.     while ((received = recv(sock, buffer, BUFFSIZE, 0)) > 0) {  
  21.         buffer[received] = 0;  
  22.         printf ("Recv %s\n", buffer);  
  23.         // 发送反馈  
  24.         if (send(sock, send_s, received, 0) != received) {  
  25.             die("failed to send");  
  26.         }  
  27.         printf ("Send %s\n", send_s);  
  28.         sleep(1);  
  29.     }  
  30.     close(sock);  
  31. }  
  32.   
  33. int main(void)  
  34. {  
  35.     struct sockaddr_in s_addr, c_addr;  
  36.     int s_sock, c_sock;  
  37.   
  38.     if ((s_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {  
  39.         die("failed to create socket");  
  40.     }  
  41.   
  42.     memset(&s_addr, 0, sizeof(s_addr));  
  43.     s_addr.sin_port = htons(PORTNUM);  
  44.     s_addr.sin_family = AF_INET;  
  45.   
  46.     if (bind(s_sock, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0) {  
  47.         die("failed to bind");  
  48.     }  
  49.   
  50.     if (listen(s_sock, CONNMAX) < 0) {  
  51.         die("failed to listen");  
  52.     }  
  53.   
  54.     while (1) {  
  55.         unsigned int c_addr_len = sizeof(c_addr);  
  56.         if ((c_sock = accept(s_sock, (struct sockaddr *)&c_addr, &c_addr_len)) < 0) {  
  57.             die("failed to accept");  
  58.         }  
  59.         c_action(c_sock);  
  60.     }  
  61.   
  62.     close(s_sock);  
  63.     return 0;  
  64. }  
然后是客户端代码(hwclient2.c),逻辑和之前的 hwclient.c 相同,向服务端连续发送 10 条消息,接受并打印返回信息。
[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <sys/types.h>  
  5. #include <sys/socket.h>  
  6. #include <string.h>  
  7. #include <arpa/inet.h>  
  8.   
  9. #define PORTNUM 6666  
  10. #define BUFFSIZE 32  
  11. #define die(err) { perror(err); exit(1); }  
  12.   
  13. int main(void)  
  14. {  
  15.     int c_sock;  
  16.     struct sockaddr_in c_addr;  
  17.     char buffer[BUFFSIZE];  
  18.     int msgcount, received;  
  19.   
  20.     if ((c_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {  
  21.         die("failed to create socket");  
  22.     }  
  23.   
  24.     memset(&c_addr, 0, sizeof(c_addr));  
  25.     c_addr.sin_port = htons(PORTNUM);  
  26.     c_addr.sin_family = AF_INET;  
  27.       
  28.     if (connect(c_sock, (struct sockaddr *)&c_addr, sizeof(c_addr)) < 0) {  
  29.         die("failed to connect");  
  30.     }  
  31.   
  32.     msgcount = 0;  
  33.     char *send_s = "Hello";  
  34.     int len_i = strlen(send_s);  
  35.     while (msgcount < 10) {  
  36.         received = 0;  
  37.         // 发送消息  
  38.         if (send(c_sock, send_s, len_i, 0) != len_i) {  
  39.             die("failed to send");  
  40.         }  
  41.         printf("Send %s\n", send_s);  
  42.         // 接收反馈  
  43.         while(received < len_i){  
  44.             int bytes = 0;  
  45.             if ((bytes = recv(c_sock, buffer, BUFFSIZE-1, 0)) < 1) {  
  46.                 die("failed to recv");  
  47.             }  
  48.             received += bytes;  
  49.             buffer[bytes] = '\0';  
  50.             printf("Recv %s\n", buffer);  
  51.         }  
  52.         msgcount++;  
  53.     }  
  54.   
  55.     close(c_sock);  
  56.     return 0;  
  57. }  
接着,我们也在未启动 hwserver2 的情况下运行 hwclient2,发现程序直接报错“Connection refused”的连接错误(如图2-2),说明连接不上服务器,这显然是符合常理的。相比之下,ZeroMQ 的表现就显得比较怪异了。因为我们在未启动 hwserver 的情况下运行 hwclient,发现程序并没有报连接错误,反而是在发送过一条消息之后阻塞住了(如图2-1);接着我们尝试启动 hwserver,发现 hwclient 又继续运行下去了,直至把 10 条消息发送完毕。


图2-2

TIP:关于 hwserver 和 hwclient 的代码请参考《ZeroMQ 深度探索(一)》

从以上现象可以看出,ZeroMQ 的 zmq_connect 方法其实只是建立了一个“虚连接”,和 Socket 的 connect 方法完全不同;实际上,从 ZeroMQ 的源码中也可以看出这点。起初我也感觉这个逻辑很奇怪,但实际上正因为有了这个特性,当我们使用 ZeroMQ 构建分布式系统的时候就不需要关心节点启动先后顺序的问题,为我们提供了不少便捷。但是,如果不善用这个特性极有可能导致严重的问题。比如,我们想使用 ZeroMQ 进行无状态模式发送,即类似于 HTTP 的“发送-接收-结束”的模式;假如在发送的过程中网络断线了,就会导致大量请求被阻塞住,严重者可导致服务器资源被耗尽!

如果要解决以上问题,一般的思路是设置超时,ZeroMQ 可以通过使用 zmq_poll 方法或者设置 ZMQ_LINGER 参数来设置请求超时,但是这也可能导致一些问题。超时时间设置太小容易丢失数据,设置太长又会影响运行效率,我们需要的是一个更可靠的网络通信方案。一种简单直接的方式就是对客户端程序进行改造,使之在不稳定的网络环境中也可以稳定运行,请参考以下代码实现。
[cpp]  view plain copy
  1. #include <zmq.h>  
  2. #include <stdio.h>  
  3. #include <unistd.h>  
  4. #include <string.h>  
  5. #include <assert.h>  
  6.   
  7. int main (void)  
  8. {  
  9.     //  Socket to talk to clients  
  10.     void *context = zmq_ctx_new ();  
  11.     void *responder = zmq_socket (context, ZMQ_REP);  
  12.     int rc = zmq_bind (responder, "tcp://*:5555");  
  13.     assert (rc == 0);  
  14.   
  15.     char buffer [10];  
  16.     char *send_s = "World";  
  17.     while (1) {  
  18.         // 接收消息  
  19.         zmq_recv (responder, buffer, 10, 0);  
  20.         buffer[5] = 0;  
  21.         printf ("Recv %s\n", buffer);  
  22.         // 发送反馈  
  23.         zmq_send (responder, send_s, 5, 0);  
  24.         printf ("Send %s\n", send_s);  
  25.         sleep(1);  
  26.     }  
  27.     return 0;  
  28. }  
首先是改造过的 ZeroMQ 的服务端代码(hwserver3.c),我们只是在原有代码(hwserver.c)上稍作修改,添加了获取到客户端请求之后返回“World”字符串的逻辑。

然后就是改造过的 ZeroMQ 的客户端代码(hwclient3.c)了,客户端的改动就大了,以下是其主要逻辑要点:
1、循环发送“Hello”字符串到服务端,然后接收返回的字符串“World”并打印出来。
2、当发现连不上服务端时,重试 3 次;如果仍然连不上,则主动结束客户端。
3、设置 ZMQ_LINGER 为 1 毫秒,表示连接不上,立即返回,不会阻塞。
[cpp]  view plain copy
  1. #include <zmq.h>  
  2. #include <string.h>  
  3. #include <stdio.h>  
  4. #include <unistd.h>  
  5.   
  6. #define SERVER_ENDPOINT "tcp://localhost:5555"  
  7. #define REQUEST_TIMEOUT 3000 // msecs, (> 1000!)  
  8. #define REQUEST_RETRIES 3 // retry before we abandon  
  9.   
  10. void *zmq_socket_new (void *context)  
  11. {  
  12.     int linger = 1;  
  13.     void *zsocket = zmq_socket (context, ZMQ_REQ);  
  14.     zmq_setsockopt(zsocket, ZMQ_LINGER, &linger, sizeof(linger));  
  15.     zmq_connect (zsocket, SERVER_ENDPOINT);  
  16.     return zsocket;  
  17. }  
  18.   
  19. int main (void)  
  20. {  
  21.     void *context = zmq_ctx_new ();  
  22.     void *zsocket = zmq_socket_new(context);  
  23.   
  24.     char buffer [255];  
  25.     char *send_s = "Hello";  
  26.     int retries_left = REQUEST_RETRIES;  
  27.   
  28.     while (retries_left) {  
  29.         // 发送消息  
  30.         zmq_send (zsocket, send_s, strlen(send_s), 0);  
  31.         printf ("Send %s\n", send_s);  
  32.         // 重试次数  
  33.         int expect_reply = 1;  
  34.         while (expect_reply) {  
  35.             // 停止重试  
  36.             if (retries_left == 0) {  
  37.                 printf("Server offline, abandoning ...\n");  
  38.                 break;  
  39.             }  
  40.             // 多路复用  
  41.             zmq_pollitem_t items [] = { { zsocket, 0, ZMQ_POLLIN, 0 } };  
  42.             int rc = zmq_poll (items, 1, REQUEST_TIMEOUT);  
  43.             if (rc == -1) break; // Interrupted  
  44.             if (items [0].revents & ZMQ_POLLIN) {  
  45.                 // 接收反馈  
  46.                 int size = zmq_recv (zsocket, buffer, 10, 0);  
  47.                 if (size > 255) size = 255;  
  48.                 buffer[size] = 0;  
  49.                 printf ("Recv %s\n", buffer);  
  50.                 if (buffer) {  
  51.                     retries_left = REQUEST_RETRIES;  
  52.                     expect_reply = 0;  
  53.                 }  
  54.             }  
  55.             // 重试连接  
  56.             else {  
  57.                 printf("Retry connecting ...\n");  
  58.                 zmq_close (zsocket);  
  59.                 zsocket = zmq_socket_new(context);  
  60.                 // 重发消息  
  61.                 zmq_send (zsocket, send_s, strlen(send_s), 0);  
  62.                 printf ("Send %s\n", send_s);  
  63.                 --retries_left;  
  64.             }  
  65.         }  
  66.     }  
  67.     zmq_close (zsocket);  
  68.     zmq_ctx_destroy (context);  
  69.     return 0;  
  70. }  
我们可以进行如下尝试,先启动服务端程序(hwserver3),然后再启动客户端程序(hwclient3),就可以看到客户端和服务端正在通信,客户端发送“Hello”,服务端反馈“World”,一切正常。接着我们停止服务端程序,我们马上发现客户端开始尝试重连(提示“Retry connecting ...”);接着我们马上重新打开服务端,就会发现客户端和服务端又恢复通信了;然后我们把服务端程序再次停止,我们看到客户端尝试重连 3 次之后,最终停止了(提示“Server offline, abandoning ...”),如图2-3所示。


图2-3

以上的设计模式被我们称之为“客户端信任”的模式,通过这种设计,我们建立了一个可控的、相对稳定的 C/S 通信模型。当然,从以上代码中我们也可以看到 ZeroMQ 中多路复用的用法,也就是 int zmq_poll (zmq_pollitem_t *items, int nitems, long timeout); 方法的使用,三个参数分别是 poll 项列表、poll 项个数以及 poll 超时时间(毫秒),其中 zmq_pollitem_t 的结构如下:

typedef struct
{
    void //*socket//;
    int //fd//;
    short //events//;
    short //revents//;
} zmq_pollitem_t;

此外,ZeroMQ 支持多种多路复用模式(参考源码 poller.hpp),列举如下:

1、select(支持unix/windows)
2、poll(支持unix)
3、epoll(支持linux)
4、kqueue(支持freebsd)
5、devpoll(zmq自研的poll)

其中,Linux 下默认使用的是 epoll 方式;当然,在编译的时候也可以通过 --with-poller 参数来配置所需的多路复用模式。话说回来,ZeroMQ 的网络通信模型和 Socket 还是有很多不同的,使用的时候一定要特别注意。在下篇中我们将介绍 ZeroMQ 消息的包装方式,进一步理解 ZeroMQ 网络通信的细节,学习其构建分布式系统的理念。



作者:heiyeshuwu 发表于2013-10-31 23:32:52 原文链接
阅读:8 评论:0 查看评论

相关 [zeromq 深度] 推荐:

[转][转]ZeroMQ 深度探索

- - heiyeluren的blog(黑夜路人的开源世界)
最初认识 ZeroMQ 是被它的名号所吸引,最近在一个高性能中间件的项目中用到了 ZeroMQ,对这个号称“史上最快的消息队列”有了更深层次的了解. 如果我们仅仅把 ZeroMQ 看作是一个消息队列,那就完全搞错了,ZeroMQ 是一套智能传输层协议,它不仅为开发者提供了强大的开发包,还包含了一套很棒的通信协议的实现,更值得一提是,它对分布式系统开发有着相当独到的见解,绝对值得我们好好学习.

ZeroMQ(java)中监控Socket

- - CSDN博客架构设计推荐文章
基本上ZeroMQ(java)中基本的代码都算是过了一遍了吧,不过觉得它在日志这一块貌似基本没有做什么工作,也就是我们通过日志来知道ZeroMQ都发生了什么事情. 而且由于ZeroMQ中将连接的建立和重连接都进行了隔离,用户不需要做什么事情来维护连接,当然这样做的好处是使程序员的编码工作变少了,但是当然也有不好的地方,那就是用户失去了对ZeroMQ整个运行阶段的控制.

[转]史上最快消息内核——ZeroMQ

- 乾 - heiyeluren的blog(黑夜路人的开源世界)
来源:http://blog.dccmx.com/2011/02/zeromq/ . ZeroMQ是一个很有个性的项目,它原来是定位为“史上最快消息队列”,所以名字里面有“MQ”两个字母,但是后来逐渐演变发展,慢慢淡化了消息队列的身影,改称为消息内核,或者消息层了. 从网络通信的角度看,它处于会话层之上,应用层之下,有了它,你甚至不需要自己写一行的socket函数调用就能完成复杂的网络通信工作.

ZeroMQ--使用jzmq进行编程

- - 编程语言 - ITeye博客
如果没有安装libtool、libuuid-devel则需要先安装,否则安装失败. 出现java.lang.UnsatisfiedLinkError: /usr/local/lib/libjzmq.so.0.0.0: libzmq.so.1: cannot open shared object file: No such file or directory异常 .

ZeroMQ(java)入门之Requerst/Response模式

- - CSDN博客架构设计推荐文章
自己最开始是在cloud foundry中接触过消息服务器(nats),或者说是消息中间件,也算是初步知道了一个消息服务器对于分布式的网络系统的重要性,后来自己也曾想过在一些项目中使用它,尤其是在一些分布式的环境下,可以极大的方便整个系统的实现. 在中间通过一个消息中间件,可以很方便的将各个woker的数据发送到最终的统计服务器来做数据的统计,从而减少很多在网络通信中会消耗的编程时间.

zmq-rpc:基于zeromq网络层编写的protobuf RPC框架

- Shengbin - codedump
阅读过zmq的代码之后,感觉这个网络层是我目前见过最高效的–线程之间使用lockfree的消息队列保存消息,可以启动多个I/O线程分担压力等等特性.于是决定基于它写一个protobuf RPC的框架.. 另外,这里使用的protobuf是旧版本2.3.0,新版本2.4.1的生成的RPC service接口跟原来不太一致,暂时还没有去研究它.BTW,升级版本之后导致原来的接口发生变化这是一个很操蛋的事情..

为什么我希望用C而不是C++来实现ZeroMQ

- - 博客 - 伯乐在线
开始前我要先做个澄清:这篇文章同Linus Torvalds这种死忠C 程序员吐槽C++的观点是不同的. 在我的整个职业生涯里我都在使用C++,而且现在C++依然是我做大多数项目时的首选 编程语言. 自然的,当我从2007年开始做 ZeroMQ( ZeroMQ项目主页)时,我选择用C++来实现.

消息中间件的技术选型心得-RabbitMQ、ActiveMQ和ZeroMQ

- - haohtml's blog
RabbitMQ、ActiveMQ和ZeroMQ都是极好的消息中间件,但是我们在项目中该选择哪个更适合呢. 下面我会对这三个消息中间件做一个比较,看了后你们就心中有数了. RabbitMQ是AMQP协议领先的一个实现,它实现了代理(Broker)架构,意味着消息在发送到客户端之前可以在中央节点上排队.

深度搜索

- - 译言最新精选
译者: HorseHour 原文地址: streamhacker.com. 当我们准备发布 Weotta时,我们已经为如何描述它犯了难. 我们使用了机器学习和自然语言处理吗. 我们最终觉得“深度搜索”是对我们工作最贴切的描述,它是一个超越了基本文本搜索的复杂搜索系统的简洁描述. 无需赘言,不管怎么看,我们都不是这个领域唯一的一家公司;谷歌和很多其他公司都在对深度搜索的各个方面进行研究.

Cookie深度解析

- - CSDN博客互联网推荐文章
       最近在公司做了Web端单点登录(SSO)功能,基于Cookie实现,做完之后感觉有必要总结一下,本文着重讲解Cookie,下文会说明单点登录的实现方案.        众所周知,Web协议(也就是HTTP)是一个无状态的协议. 一个Web应用由很多个Web页面组成,每个页面都有唯一的URL来定义.