ZeroMQ指南:第1章:基本概念

1 拯救世界(Fixing the World)

如何解释ZeroMQ?
  • 用它可以完成的精彩的事情来解释:ZeroMQ是打了激素的套接字(It's sockets on steroids);ZeroMQ是带有路由的邮箱。
  • 用人们对它的评价来解释:ZeroMQ很快;复杂性不见了,ZeroMQ让事情变得简单;ZeroMQ解放了思维。
  • 通过比较来解释:ZeroMQ更小、更简单。
而文档作者带领大家回忆为什么创造ZeroMQ,因为当时作者所处的境况跟读者今天的境况非常相似。
 
       编程是穿上盛装的一门科学,好像艺术一样,因为大部分人不理解软件的本质,也没有人告诉他们。软件的本质不是算法、数据结构、语言和抽象。软件只是我们创建、使用、丢弃的工具。软件的本质就是人类的本质。软件变得复杂的时候,人们将其划分成容易理解和使用的部分,从而可以相互合作,解决非常大的问题。
 
       现代软件需要运行在互联的世界中。代码仅仅“强壮而沉默(strong and silent)”是不够的。代码需要相互交流。代码要像人类大脑一样工作(数万亿的神经细胞各自独立地处理消息):没有中央控制的高度并行的网络,没有单点故障,可以解决非常复杂的问题。
       但是,如果还是使用线程、协议、网络等来创建软件,则这个目标就像一场梦一样,几乎没法达到。即使是使用少量套接字连接少量程序都很不好搞。万亿数量级?开销是没法想象的。计算机互联太难,解决这个问题的软件和服务需要耗费数十亿美元。
       开源软件让我们可以有效地共享知识,解决了上世纪80年代的软件危机。但是今天我们面临另一场软件危机。只有大的、富有的公司可以创建互联的应用,也就是云。虽然国际互联网提供了大量潜在互联的节点,但是大部分人没法有效地利用它们:没有把代码联系起来的方法,无法把大脑连接起来解决大的问题(健康、教育、经济、运输等等)。
     
       人们企图用很多方法来解决软件互联的挑战:成千上万的IETF规范,每个都解决问题的一部分。
       直到今天,人们依然使用原始的UDP和TCP,以及HTTP、WebSockets等专利协议来连接应用程序。过程还是痛苦的,进度缓慢,难以扩展,并且潜在的是中心化的。分布式的P2P体系主要是闹着玩的,而不是用来工作的。
 
要拯救这个世界,需要做两件事:
  • 解决“如何在任何地点连接任何代码到任何代码(how to connect any code to any code, anywhere)”的问题
  • 将解决方法封装到尽可能简单的建造块中,让人们容易理解和使用
      ZeroMQ看起来像个嵌入的网络库,但是起到了并发框架的作用。ZeroMQ提供可以在进程内、进程间、TCP和多播等各种传输端点间传递消息的套接字。使用扇出、发布-订阅、任务分布、请求-回应等模式,可以对套接字进行N到N连接。ZeroMQ很快,足以用于集群产品。ZeroMQ的异步IO模型让你可以扩展到多核应用,就像异步消息处理任务一样。ZeroMQ有多种语言的API,可以运行在大多数操作系统中。ZeroMQ来自iMatix,使用LGPL开源协议。

2 获取示例代码

     示例代码在Git代码仓库中。最简单的方法是克隆代码仓库:
git clone --depth=1 https://github.com/imatix/zguide.git

3 请求-回应模式

     从一个简单例子开始:客户端发送Hello给服务器;服务器回应World。
3.1 服务器代码
#include <zmq.h>
#include <zmq_helpers.h>
#include <stdio.h>
#pragma comment(lib,"libzmq-v100-mt.lib")
int main(void){
    void* ctx = zmq_ctx_new();
    void* responder = zmq_socket(ctx,ZMQ_REP);
    zmq_bind(responder,"tcp://*:5555");
    while(1){
        zmq_msg_t request;
        zmq_msg_init(&request);
        zmq_recvmsg(responder,&request,0);
        printf("收到%s\n",(char*)zmq_msg_data(&request));
        zmq_msg_close(&request);
        s_sleep(1000);
        zmq_msg_t reply;
        zmq_msg_init_size(&reply,6);
        memcpy(zmq_msg_data(&reply),"World",6);
        printf("发送World\n");
        zmq_sendmsg(responder,&reply,0);
        zmq_msg_close(&reply);
    }
    zmq_close(responder);
    zmq_ctx_destroy(ctx);
    return 0;
}
   
     以上是使用C语言的服务器代码,当然也可以使用C++、Python、PHP等语言来编写服务器。:
     

3.2 客户端代码

#include <zmq.h>
#include <zmq_helpers.h>
#include <stdio.h>
#pragma comment(lib,"libzmq-v100-mt.lib")
int main(void){
    void* ctx = zmq_ctx_new();
    void* client = zmq_socket(ctx,ZMQ_REQ);
    zmq_connect(client,"tcp://localhost:5555");
    for(int idx = 0; idx < 10++idx){
        zmq_msg_t request;
        zmq_msg_init_size(&request,6);
        memcpy(zmq_msg_data(&request),"Hello",6);
        printf("发送Hello\n");
        zmq_sendmsg(client,&request,0);
        zmq_msg_close(&request);
        zmq_msg_t reply;
        zmq_msg_init(&reply);
        zmq_recvmsg(client,&reply,0);
        printf("收到%s\n",(char*)zmq_msg_data(&reply));
        zmq_msg_close(&reply);
    }
    zmq_close(client);
    zmq_ctx_destroy(ctx);
    return 0;
}
     ZeroMQ编程中,ZeroMQ套接字代替了TCP套接字。

3.3 关于字符串

     ZeroMQ并不了解要发送的数据的格式,而只是知道其字节大小。
     C语言中,字符串由一个空字节表示结束。可以这样发送带空字节结束标志的C字符串:
     
zmq_send (requester, "Hello", 6, 0);

     但是,使用其他语言编程时,字符串可能不带空字节结束标志。比如说,用Python发送同样的字符串:
     
socket.send ("Hello")

     ZeroMQ字符串带有长度前缀,在线路上发送的时候没有空字节结束标志
   
      C语言中正确接收ZeroMQ字符串的代码:   
static char *s_recv (void *socket) {
    zmq_msg_t message;
    zmq_msg_init (&message);
    if (zmq_recvmsg (socket, &message, 0<= 0)
        return (NULL);
    int size = zmq_msg_size (&message);
    char *string = (char*)malloc (size + 1);
    memcpy (string, zmq_msg_data (&message), size);
    zmq_msg_close (&message);
    string [size] = 0;
    return (string);
}
      同理,C语言中发送字符串的代码
static int s_send (void *socket, char *string) {
    int rc;
    zmq_msg_t message;
    zmq_msg_init_size (&message, strlen (string));
    memcpy (zmq_msg_data (&message), string, strlen (string));
    rc = zmq_sendmsg (socket, &message, 0);
    zmq_msg_close (&message);
    return (rc);
}
     辅助代码文件zhelpers.h:

3.4 报告ZeroMQ版本(c语言版本)

#include <zmq.h>

int main (void)
{
    int major, minor, patch;
    zmq_version (&major, &minor, &patch);
    printf ("Current 0MQ version is %d.%d.%d\n", major, minor, patch);
    return 0;
}

4 发布-订阅模式

       第二种典型的模式是单方数据发布:服务器把更新推送到一系列客户端。来看一个推送包含邮政编码、温度、相对湿度的天气更新的示例。

4.1 服务器代码

#include <zmq.h>
#include <zmq_helpers.h>
#include <stdio.h>
#pragma comment(lib,"libzmq-v100-mt.lib")
int main(void){
    void* ctx = zmq_ctx_new();
    void* publisher = zmq_socket(ctx,ZMQ_PUB);
    zmq_bind(publisher,"tcp://*:5556");
    srand((unsigned int)time(NULL));
    while(1){
        int zipcode,temperature,relhumidity;
        zipcode = randof(10);
        temperature = randof(215- 80;
        relhumidity = randof(50+ 10;
        char report[20];
        snprintf(report,20,"%d %d %d",zipcode,temperature,relhumidity);
        printf("发布天气预报: %s\n",report);
        s_send(publisher,report);
    }
    zmq_close(publisher);
    zmq_ctx_destroy(ctx);
    return 0;
}

4.2 客户端代码

#define _CRT_SECURE_NO_WARNINGS
#include <zmq.h>
#include <zmq_helpers.h>
#include <stdio.h>
#pragma comment(lib,"libzmq-v100-mt.lib")
int main(int argc,char* argv[]){
    void* ctx = zmq_ctx_new();
    void* subscriber = zmq_socket(ctx,ZMQ_SUB);
    zmq_connect(subscriber,"tcp://localhost:5556");
    char* filter = ((argc > 1? argv[1: "6");
    zmq_setsockopt(subscriber,ZMQ_SUBSCRIBE,filter,strlen(filter));
    long total_temp = 0;
    int update_cnt;
    for(update_cnt = 0; update_cnt < 100++update_cnt){
        char* report = s_recv(subscriber);
        printf("收到天气预报: %s\n",report);
        
        int zip,tmp,rel;
        sscanf(report,"%d %d %d",&zip,&tmp,&rel);
        total_temp += tmp;
        free(report);
    }
    printf("邮编: %s 平均温度: �\n",filter,(int)(total_temp / update_cnt));
    zmq_close(subscriber);
    zmq_ctx_destroy(ctx);
    return 0;
}

4.3 注解

  • 订阅
    • 必须使用zmq_setsockopt在订阅套接字上设置订阅,否则收不到任何消息
    • 可以在订阅套接字上设置多个订阅,套接字会接收满足任何一个订阅条件的消息
    • 可以分别取消各个订阅
  • 异步
    • 发布-订阅套接字对是异步的
    • 在订阅套接字上发送消息(调用zmq_send)会导致错误
    • 在发布套接字上接收消息(调用zmq_recv)会导致错误
  • 绑定和连接
    • 理论上说,哪一端调用connect,哪一端调用bind是没有关系的。但是对于发布-订阅套接字,最好是对发布套接字调用bind;对订阅套接字调用connect。否则订阅套接字可能收到较早的消息,也就是订阅套接字启动之前的消息。
  • 订阅者总是会错过发布者发送的第一个消息
    • 因为不知道订阅者开始获取消息的精确时间。即使是先启动订阅者,等待一会儿再启动发布者,订阅者也总是会错过发布者发布的第一个消息。因为订阅者连接到发布者是需要时间的,而发布者可能已经在发送消息了。这种“慢加入(slow joiner)”特征不容易理解。假设有两个节点执行下述动作:
      • 订阅者连接到某端点,接收并计数消息
      • 发布者绑定到某端点并且立即发送1000条消息
    • 这种情况下订阅者很可能接收不不到任何消息。
    • 建立TCP连接所需的握手需要毫秒级的时间。建立连接期间ZeroMQ可以发送很多消息。为便于讨论,假设建立连接需要5毫秒,而链路每秒可以处理100万条消息。在订阅者连接到发布者的5毫秒时间内,发布者只用1毫秒的时间就可以发送完1000条消息(而此时订阅者还没有连接上,所以这1000条消息被丢弃)。
    • 第二章会解释如何同步发布者和订阅者,以便只有在订阅者连接上并且准备好之后,发布者才开始发布消息。有一种简单而愚蠢的延迟发布的方法,就是sleep。最好不要使用这种方法,因为它很脆弱,也不雅观,还很慢。当然现在可以使用sleep,到第二章再看怎么正确地同步。
    • 另一种替代的同步方法是,将发布的数据流看做是无限的,没有起点,没有终点。本节的例子就是这么做的:可以任意启动、停止、重启服务器,而客户端程序不管这些,它只是在收集到1000条更新消息后,计算平均值,输出,然后退出。
  • 发布-订阅模式的一些特征
    • 订阅者可以连接到多个发布者,只要多次调用connect就可以了。数据会交错地到达,一个发布者发布的数据不会淹没其他发布者的数据。
    • 如果没有订阅者连接,发布者会简单地丢弃所有消息。
    • 如果使用TCP,并且订阅者较慢,则消息会在发布者那边排队。后面会介绍如何使用“高水位标记”来阻止发布者对消息进行排队。
    • 当前版本的ZeroMQ中,消息过滤发生在订阅者那一端,而不是在发布者这边。也就是说,在TCP中,发布者会发送所有消息到所有订阅者中,而订阅者会丢弃不想要的消息。

5 管线模式

      示例代码实现了超级计算:
  • 一个设备生成可以并行执行的任务
  • 很多工作者可以处理这些任务
  • 一个汇聚点(sink)从工作者进程收集任务执行结果
     

5.1 任务发生器

#include <zmq.h>
#include <zmq_helpers.h>
#include <stdio.h>
#pragma comment(lib,"libzmq-v100-mt.lib")
int main(void){
    void* ctx = zmq_ctx_new();
    void* sender = zmq_socket(ctx,ZMQ_PUSH);
    zmq_bind(sender,"tcp://*:5557");
    void* sink = zmq_socket(ctx,ZMQ_PUSH);
    zmq_connect(sink,"tcp://localhost:5558");
    printf("请在工作者就绪后按回车...");
    getchar();
    printf("开始向工作者发送任务...\n");
    // 通知汇聚点,即将开始工作,请准备汇集任务执行结果
    s_send(sink,"0");
    srand((unsigned int)time(NULL));
    int task_cnt;
    int total_ms = 0;
    for(task_cnt = 0; task_cnt < 100++task_cnt){
        int workload = randof(100+ 1;
        total_ms += workload;
        
        char szwork[10];
        snprintf(szwork,10,"%d",workload);
        
        printf("M ",workload);
        if ((task_cnt + 1% 10 == 0){
            printf("\n");
        }
        fflush(stdout);
        s_send(sender,szwork);
    }
    printf("预期开销: %d 毫秒\n",total_ms);
    s_sleep(1000);
    zmq_close(sink);
    zmq_close(sender);
    zmq_ctx_destroy(ctx);
    return 0;
}

5.2 工作者

#include <zmq.h>
#include <zmq_helpers.h>
#include <stdio.h>
#pragma comment(lib,"libzmq-v100-mt.lib")
int main(void){
    void* ctx = zmq_ctx_new();
    void* receiver = zmq_socket(ctx,ZMQ_PULL);
    zmq_connect(receiver,"tcp://localhost:5557");
    void* sender = zmq_socket(ctx,ZMQ_PUSH);
    zmq_connect(sender,"tcp://localhost:5558");
    while(1){
        // 接收任务
        char* p_work = s_recv(receiver);
        fflush(stdout);
        printf("%s\n",p_work);
        // 执行任务
        s_sleep(atoi(p_work));
        free(p_work);
        
        // 通知汇聚点已经完成一个任务
        s_send(sender,"");
    }
    zmq_close(receiver);
    zmq_close(sender);
    zmq_ctx_destroy(ctx);
    return 0;
}

5.3 汇聚点

#include <zmq.h>
#include <zmq_helpers.h>
#include <stdio.h>
#pragma comment(lib,"libzmq-v100-mt.lib")
int main(void){
    void* ctx = zmq_ctx_new();
    void* sink = zmq_socket(ctx,ZMQ_PULL);
    zmq_bind(sink,"tcp://*:5558");
    // 等待任务发生器通知开始
    char* p_start = s_recv(sink);
    free(p_start);
    int64_t start_time = s_clock();
    printf("开始收集任务执行结果\n");
    // 收集个任务执行结果
    int task_cnt;
    for(task_cnt = 0; task_cnt < 100++task_cnt){
        char* p_done = s_recv(sink);
        free(p_done);
        printf(".");
        if ((task_cnt + 1% 10 == 0){
            printf("\n");
        }
        fflush(stdout);
    }
    printf("100个任务实际耗时: %d 毫秒\n",int(s_clock() - start_time));
    zmq_close(sink);
    zmq_ctx_destroy(ctx);
    return 0;
}

5.4 注解

  • 工作者上面连接到任务发生器,下面连接到汇聚点。可以任意添加工作者。任务发生器和汇聚点是这个体系的“静态”组件,而工作者是“动态”组件。
  • 必须同步工作者的启动和运行。这种情况在ZeroMQ中很常见,而且没有容易的解决方案。连接需要一定的时间才能完成。一系列工作者连接到任务发生器的时候,第一个连接上的工作者将在其他工作者还在连接中的这个较短时间内处理所有的工作负载。如果不能同步工作者的启动,系统可能不会并行地运行。
  • 任务发生器的PUSH套接字将任务平等地分发给工作者,这就是负载均衡。后面会详细讨论负载均衡。
  • 汇聚点的PULL套接字从工作者平等地收集结果,这叫做公平排队。

管道线模式也存在“慢加入(slow joiner)”的问题,这会让PUSH套接字不能正确地进行负载均衡。

6 ZeroMQ编程

请注意
  • 一步一步地学习ZeroMQ。简单的API隐藏了很多可能性。请慢慢学习,掌握每种可能性。
  • 编写优雅的代码。龌龊的代码会隐藏问题,让别人很难帮助你。
  • 边编程边测试。程序出现问题的时候,你应该能定位问题到5行代码之内。
  • 程序不能正确工作时,切分代码,分别测试,看是哪一部分有问题。
  • 必要的时候才进行抽象(类、方法等等)。复制/粘贴大量代码的时候会同时复制/粘贴错误。
  • 最好不要在线程间传递套接字。从ZeroMQ 2.1版本开始,线程间传递套接字是合法的了,但是还是很危险,除非使用“全内存护栏(full memory barrier)”。

6.1 正确获取上下文

  • 调用zmq_ctx_new创建上下文
  • 一个进程只应该创建和使用一个上下文对象
  • 虽然可以创建多个上下文,但是两个及更多个上下文被看做是单独的ZeroMQ实例
  • 在main函数开始处调用zmq_ctx_new,结尾处调用zmq_ctx_destroy
  • 使用fork()创建子进程的时候,每个子进程需要有自己的上下文。如果在主进程中调用fork()之前调用zmq_ctx_new,则子进程可以继承上下文,而不用再调用zmq_ctx_new。

6.2 干净地退出

  • 使用Python等具有自动垃圾收集功能的语言时不用关注资源的释放,但是使用C/C++的时候就要注意了,要在使用完资源后正确地释放,否则可能导致内存泄露、程序不稳定等问题。
  • ZeroMQ对于程序的退出要求非常严格。如果调用zmq_ctx_destroy的时候还有打开的套接字,则调用会永远挂起。即使关闭了所有套接字,如果还有未决的connect或者send调用,zmq_ctx_destroy还是会无限等待,除非关闭套接字前将其LINGER选项设置为0了。
  • 只需要关注三种ZeroMQ对象:消息、套接字和上下文。简单程序中的处理规则比较简单:
    • 使用完消息之后总是调用zmq_msg_close来关闭
    • 如果打开和关闭很多套接字,则你可能需要重新设计你的应用程序
    • 退出程序的时候,关闭套接字,然后调用zmq_ctx_destroy,这会销毁上下文
  • 如果使用多线程,则会复杂得多。简单地说,一般要遵循的规则是:
    • 不要企图在多个线程中使用同一个套接字
    • 设置所有套接字的LINGER选项为0(relingerfy),关闭所有套接字,在主线程中销毁上下文
    • 这会使得附着线程(共享相同上下文的线程)中任何阻塞的receive或者poll或者send操作返回错误。请捕捉这个错误,在附着线程中重新设置LINGER选项(relingerize),关闭套接字。
    • 不要两次销毁同一个上下文。主线程中的zmq_ctx_destroy会阻塞直到它所知道的所有套接字安全地关闭了。

6.3 为什么需要ZeroMQ

     今天的很多应用程序都由各种组件构成,这些组件分布在某种类型的网络中,要么是LAN,要么是Internet。所以很多应用开发者都需要某种类型的消息传递。一些开发者使用消息队列产品,但是更多的时候开发者会自己处理,使用TCP或者UDP。使用TCP或者UDP并不难,但是从A向B发送一些字节与任何方式的可靠消息传递是有很大的不同的。

6.3.1 可重用的消息层

     以原始TCP为例,要实现可重用的消息层需要解决以下所有或者大部分问题:
  • 如何处理IO?是阻塞地处理,还是在后台处理?这是一个主要的设计决策。阻塞IO让体系难以扩展,而后台IO很难做对。
  • 如何处理动态组件,比如说,临时离开的部分?是不是正式地划分“客户端”和“服务器”组件,并且强制要求服务器不会离线?如果服务器要连接到服务器该怎么办?是不是每隔几秒尝试重连?
  • 如何在线路中表示消息?如何对消息分帧以便于写入和读取,并且不会缓冲区溢出,而且对小的消息高效,又能处理很大的视频数据?
  • 如何处理不能立即发送的消息?特别是,是不是要等待组件重新上线?是丢弃消息,放入到数据库中,还是放入内存队列中?
  • 在哪里存放消息队列?如果组件从队列中读取消息很慢,队列填满时会发生什么?我们的策略是?
  • 如何处理消息丢失?是等待新的数据,还是请求重发,或者创建某种类型的可靠层,保证消息不会丢失?如果可靠层本身崩溃了怎么办?
  • 需要不同的网络传输端点时怎么办?比如说,需要用多播代替TCP单播?或者要使用IPv6?我们是要重写应用程序,还是将传输端点抽象到某层中?
  • 如何路由消息?能不能把相同的消息发送给多个端点?能把回应发送给原始的请求者吗?
  • 如何编写其他语言的API?是重新实现线路层协议,还是重新封装库?如果使用前者,如何保证协议栈的稳定和高效?如果是后者,如何保证互操作性?
  • 如何表示数据,以便可以在多种体系结构中正确读取?是否强制要求对数据类型使用某种特定的编码?这是消息传递系统的任务,还是更高层的任务?
  • 如何处理网络错误?是等待然后重试,还是沉默地忽略错误,还是终止程序(abort)?
     为什么很多项目需要消息传递系统,但是人们还是一次又一次地在代码中使用TCP套接字,以一种复杂的方式来解决上述问题?
     原因在于创建可重用的消息传递系统非常难。所以很少有这方面的开源项目,而商业的消息传递产品又复杂、昂贵、不灵活,并且脆弱。2006年iMatix设计的AMQP可能是第一个开源的可重用消息系统。AMQP比其他很多设计更好地工作,但是仍然相对复杂、昂贵和脆弱:需要好几周才能学会使用,要好几个月才能创建在大负载下不会崩溃的稳定架构。
     很多消息系统,像AMQP,在解决上面列表中的问题时引入了“代理(broker)”的概念,让“代理”处理寻址、路由和排队。这让客户端-服务器协议或者一系列API建立在某种没有文档的协议之上,让应用程序与代理对话。在大型网络中代理可以降低复杂性。但是在产品(如ZooKeeper)中添加基于代理的消息让事情变得更糟,而不是更好。这意味着额外加入一个大盒子,一个新的单点故障源。代理会很快成为瓶颈,带来新的管理风险。如果软件支持,我们可以添加第二个、第三个、第四个...代理,形成某种故障切换模式(some fail-over scheme)。人们就是这么做的。这创建了更多的活动部分,带来了更多的复杂性,更多的东西可能出问题。
     中心化的代理需要专门的操作队伍,需要日夜观察代理的运行情况。需要工作机器,需要备份机器,需要人来管理这些机器。只有大型的、有很多活动部分、由很多人的团队花几年时间创建的应用才值得这么做。
     这让小到中型的应用开发者陷入两难的境地:要么避免网络编程,创建不能扩展的单体应用;要么跳入网络编程的陷阱,创建脆弱、复杂而难以维护的应用。或者使用消息产品,创建依赖于昂贵而较容易使用的代理技术。没有完美的选择。

6.3.2 ZeroMQ的特征

     我们需要的是以简单、廉价的方式实现消息传递任务,可以在任何应用中工作,几乎没有开销的东西。应该只需要链接到一个库,没有任何其他开销。没有额外的活动部分,没有额外的风险。应该可以运行在任何操作系统中,可以工作在任何编程语言中。
     这就是ZeroMQ:一个高效的嵌入库,解决了为使应用程序能够优雅地在网络中伸缩所需解决大部分问题,而没有太大的开销。ZeroMQ的特征如下:
  • 在后台线程中异步地处理IO。后台线程使用无需锁的数据结构与应用线程通信,所以ZeroMQ应用程序不需要锁、信号量,或者其他等待状态。
  • 组件可以动态地加入和退出,ZeroMQ会自动重新连接。这意味着可以以任何次序启动组件。可以创建“面向服务架构(service-oriented architectures)”,其中的服务可以在任何时候加入或者退出网络。
  • 在需要的时候自动对消息排队。这种处理是智能的,在排队前会尽量让消息靠近接收者。
  • 有处理队列溢出的方法(“高水位标记”)。队列满的时候,ZeroMQ会自动阻塞发送者,或者丢弃消息,取决于你正在使用的消息传递类型(模式)。
  • ZeroMQ让应用程序可以使用传输端点相互交流:TCP、多播、进程内、进程间。使用不同的传输端点时不用修改代码。
  • 根据消息传递模式的不同,使用不同的策略来安全地处理慢速/阻塞的接收者。
  • 使用请求-应答、发布-订阅等多种模式来路由消息。这些模式定义了如何创建网络拓扑结构。
  • 需要降低互联的各部分间的复杂性的时候,可以在网络中放置模式扩展的“设备”(小的代理)。
  • 通过在线路中使用简单的帧,可以精确地传递整个消息。发送10K的消息,则会收到10K的消息。
  • 不对消息格式做任何假定。消息是从零到数G字节的块。需要在高层使用其他产品来表示数据,如Google的Protocol Buffers、XDR等等。
  • 智能地处理网络错误。有时候重试,有时候告诉你操作失败。
  • 降低能耗。使用较少的CPU时间来做更多事情意味着使用更少的能源,而且较老的机器可以使用更长的时间。
        ZeroMQ指南:第1章:基本概念
       ZeroMQ让网络扩展应用的开发方式起了革命性的变化。表面上看只是简单的套接字API,调用zmq_recv和zmq_send。快速的消息处理是其核心,应用程序被切分成一系列的消息处理任务。这很优雅而自然。并且是可扩展的:每个任务映射到一个节点,而节点间使用任何传输端点相互交流。一个进程中两个节点(节点是一个线程)、一个机器中两个节点(节点是一个进程)、一个网络中两台机器(节点是机器)都是可以的,而不需要修改应用代码。

6.4 问题解决

  • 创建原型,让自己学习和测试设计得各个方面。增加压力直到崩溃,以了解设计的强壮程度。
  • 进行测试。创建测试框架,确保能够使用真实的环境,进行认真的测试。最好是一个团队写代码,另一个团队企图让它崩溃。
  • 联系iMatix,看看它能给你什么帮助。

     总之:如果没有证明体系结构在真实环境中可以工作,则它很可能在最坏的情况下会崩溃。
   
     ZeroMQ封装了传输层的TCP连接,客户程序是看不到TCP连接的。可以使用阻塞的发送或者接收,或者轮询(poll),但是只能与(ZeroMQ)套接字交流,不能与套接字管理的(TCP)连接交流。ZeroMQ的套接字不等于TCP套接字。
ZeroMQ—指导 由iMatix公司的首席执行官Pieter Hintjens 编写。感谢Bill Desmarais, Brian Dorsey, CAF, Daniel Lin, Eric Desgranges, Gonzalo Diethelm, Guido Goldstein, Hunter Ford, Kamil Shakirov, Martin Sustrik, Mike Castleman, Naveen Chawla, Nicola Peduzzi, Oliver Smith, Olivier Chamoux, Peter Alexander, Pierre Rouleau, Randy Dryburgh, John Unwin, Alex Thomas, rofl0r, Mihail Minkov, Jeremy Avnet, Michael Compton, and Zed Shaw的贡献,也感谢Stathis Sideris,因为Ditaa.。 请对所有的意见和勘误表进行问题跟踪。这个版本覆盖了0MQ2.0的版本,发表于周二2010年11月9日,9时32分19秒。 第一——基础的东西 修理(fixing)这个世界 怎么解释0MQ?有些人会说它的所有美好的事情。它是类固醇(steroids)上的套接字。它像有路由的邮箱。它很快。别人想分享它的启蒙,当这一切变得越来越明显,人们开始顿悟了。事情变得更简单。再也不复杂了。它打开了人们的思维。其他人喜欢通过做比较的方式来解释。它更小,更简单,但是看起来仍然很熟悉。就我个人而言,我希望回忆起我们为什么要开发0MQ,因为,这是读者们也很想知道的问题。 编程是装扮成艺术的科学,因为我们中的大多数不了解软件的物理过程。如果学过这方面的知识的话,也学的很少。软件的物理过程不是算法,数据结构,语言和抽象。这些只是我们研发,使用,然后扔掉的工具。软件真正的物理过程实际上是人们的思维过程。 ……
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值