ZeroMQ(一)

本文图片来源网络,侵权联系删除

一、ZeroMQ简介:

是一个处理数据传输的库,重点在传输上,看起来它像是在socket上面封装了一层,让我们可以很容易的利用它来做N对M的数据传输,在分布式系统中很方便,在接收端它有round-robin负载均衡。
号称最快的消息队列系统,专门为高吞吐量/低延迟的场景开发,在金融界的应用中经常使用,偏重于实时数据通信场景

  • ZeroMQ是一个库,不是消息中间件;
  • 类似于java中的JMS或者MOM;
  • 与Netty类似,支持0拷贝
  • 支持进程内、进程间、TCP以及广播等多种通讯模式;
  • Zero消息组成:字节数+实际内容;
  • 支持的语言也比较多:C,C++,JAVA,Node.js等
  • zeromq是一个库,所以我们可以在原有的接口上任意的扩展,而其他一些消息中间件更像是成品,扩展性不高;
  • 由于接收缓存有限,消息没有很好的持久化,当缓存满了以后,就会造成消息的丢失,这是一个缺点.

在这里插入图片描述
二、优点和缺点:

优点:

  • 可伸缩、可扩展
  • 组网灵活
  • 高性能,并没有使用互斥锁之类的影响性能的东西

缺点:

  • 消息无法持久化:如果对方没有来得及接收,消息可能会丢
  • 可靠性和可用性不是很足
  • 高并发不足,一般用于服务器模块内部通信,或者对并发要求不高的小型服务器(默认为1024并发量)

三、灵活的组网架构:

常用的三种基本模型:

  • 请求应答模型(Request-Reply)
  • 订阅发布模型(Subscribe-Publish)
  • 流水线模型(Pipeline)

扩展模型

  • 路由器模型
  • 经销商模型
  • 扩展订阅发布模型

四、三种基本模型:

请求应答模型(Request-Reply):
在这里插入图片描述
该模型和我们经常见到的CS有点像,双方可以相互发送数据;
代码示例:

#ifdef WIN32
void
#else
void*
#endif
thread_client(void* args)
{
	void* context = zmq_ctx_new(); //zmq_init(2);
	//zmq_ctx_set();
	void* request = zmq_socket(context, ZMQ_REQ);
	int ret = zmq_connect(request, "tcp://127.0.0.1:9527");
	printf("%d zmq_connect:%d\n", GETTID(), ret);
	for (int i = 0; i < 10; i++) {
		char buffer[1024] = "client request!\n";
		ret = zmq_send(request, buffer, strlen(buffer), 0);
		printf("%d zmq_send:%d %s", GETTID(), ret, buffer);
		ret = zmq_recv(request, buffer, sizeof(buffer), 0);
		printf("%d zmq_recv:%d %s\n", GETTID(), ret, buffer);
		SLEEP(100);
	}
	ret = zmq_send(request, "exit", 4, ZMQ_DONTWAIT);
	printf("%d zmq_send:%d exit\n", GETTID(), ret);
	ret = zmq_close(request);
	printf("%d zmq_close:%d exit\n", GETTID(), ret);
	ret = zmq_ctx_destroy(context);
	//zmq_term()
	//zmq_ctx_term(context);
	printf("%d zmq_ctx_destroy:%d exit\n", GETTID(), ret);
	printf("%d exit\n", GETTID());
	THREADEND();
}


#ifdef WIN32
void
#else
void*
#endif
thread_server(void* args)
{
	void* context = zmq_ctx_new();
	void* reply = zmq_socket(context, ZMQ_REP);
	int ret = zmq_bind(reply, "tcp://*:9527");
	printf("zmq_bind:%d\n", ret);
	int i = 1;
	while (true) {
		char buffer[1024];
		memset(buffer, 0, sizeof(buffer));
		zmq_recv(reply, buffer, sizeof(buffer), 0);
		printf("data from client:%s\n", buffer);
		if (strcmp(buffer, "exit") == 0) {
			//zmq_recv(reply, buffer, sizeof(buffer), 0);
			break;
		}
		char ack[64] = "";
		snprintf(ack, sizeof(ack), "server reply:ok! %d\n", i++);
		zmq_send(reply, ack, strlen(ack), 0);
	}
	SLEEP(500);
	printf("server close!\n");
	zmq_close(reply);
	zmq_ctx_destroy(context);
	printf("%d server exit\n", GETTID());
	THREADEND();
}

订阅发布模型:也叫广播订阅模式
在这里插入图片描述
发布订阅模型的数据流向是单向的,发布者负责发布topic消息,也就是说发布者只能给订阅者发送消息,而不能接收订阅者的消息;
订阅者只能订阅消息,只能接收订阅了发布者有的topic,不能向发布者发布消息.

#ifdef WIN32
void
#else
void*
#endif
thread_publish(void* args)
{
	void* context = zmq_init(4);
	void* publish = zmq_socket(context, ZMQ_PUB);
	zmq_bind(publish, "tcp://*:9527");
	SLEEP(100);
	char message[64] = "";
	for (int i = 0; i < 10; i++) {
		snprintf(message, sizeof(message), "The %d Message!", i + 1);
		zmq_send(publish, message, strlen(message), 0);
		SLEEP(100);
	}
	zmq_send(publish, "exit", 4, 0);
	SLEEP(100);
	zmq_close(publish);
	zmq_ctx_destroy(context);
	THREADEND();
}


#ifdef WIN32
void
#else
void*
#endif
thread_subscribe(void* args)
{
	void* context = zmq_init(4);
	void* subscriber = zmq_socket(context, ZMQ_SUB);
	zmq_connect(subscriber, "tcp://localhost:9527");
	zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", 0);
	while (true) {
		char buf[64] = "";
		zmq_recv(subscriber, buf, sizeof(buf), 0);
		printf("%d recv:%s\n", GETTID(), buf);
		if (strcmp(buf, "exit") == 0) {
			break;
		}
	}
	zmq_close(subscriber);
	zmq_ctx_destroy(context);
	THREADEND();
}

流水线模型:也可以理解为分治
在这里插入图片描述
流水线模型: 可以是一个大任务划分为多个子任务worker,子任务之间的互相关联的,最后这些worker将结果push到slink进行组装,使得整个大任务的完成,如果添加了多个不同的任务,这些任务是可以并发执行的。
也可以是同一个任务,比如造车,可能需要用不同的原料、不同的颜色等,也是可以直接push给worker的。

这三个worker可以是做不同的事情:比如做自行车:
1.造轮子 2.造屁股坐  3.造支架,最后进行sink组装。

当然也可以做相同的事情,这样看起来只是单纯的并发。

举个例子:将本地的文件通过网络发送给其他用户,任务切割如下:

  1. 打开本地文件和网络;
  2. 读取文件的内容,并传输到网络上直到完毕;
  3. 通知用户数据发送完成.

优点:提高CPU的并行处理,充分利用CPU资源。
代码示例:

#ifdef WIN32
void
#else
void*
#endif
thread_push(void* args)
{
	void* context = zmq_ctx_new();
	void* push = zmq_socket(context, ZMQ_PUSH);
	zmq_bind(push, "tcp://*:9527");
	for (int i = 0; i < 10; i++) {
		zmq_msg_t msg;
		zmq_msg_init_size(&msg, 6);
		memcpy(zmq_msg_data(&msg), "hello!", 6);
		zmq_msg_send(&msg, push, 0);
		SLEEP(100);
		zmq_msg_close(&msg);
	}
	zmq_msg_t msg;
	zmq_msg_init_size(&msg, 4);
	memcpy(zmq_msg_data(&msg), "exit", 4);
	zmq_msg_send(&msg, push, 0);
	zmq_msg_close(&msg);
	zmq_close(push);
	zmq_ctx_destroy(context);
	THREADEND();
}



#ifdef WIN32
void
#else
void*
#endif
thread_pull(void* args) {
	void* context = zmq_ctx_new();
	void* pull = zmq_socket(context, ZMQ_PULL);
	zmq_connect(pull, "tcp://localhost:9527");
	while (true) {
		zmq_msg_t msg;
		zmq_msg_init(&msg);
		zmq_msg_recv(&msg, pull, 0);
		char pData[128] = "";
		memcpy(pData, zmq_msg_data(&msg), zmq_msg_size(&msg));
		zmq_msg_close(&msg);
		printf("%s\n", pData);
		if (strcmp(pData, "exit") == 0) {
			break;
		}
	}
	zmq_close(pull);
	zmq_ctx_destroy(context);
	THREADEND();
}

补充: Zeromq可以同时添加很多的任务,并发数线程数可以在设置上下文的时候指定。
函数接口说明
官网
想了解消息中间件的可以点这里,网上一篇不错的文章
下一篇文章聊聊zeromq的几个扩展模型。

发布了237 篇原创文章 · 获赞 98 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/KingOfMyHeart/article/details/100146088