zmq的pubsub模式下inproc,ipc,tcp,epgm的通信性能测试结果以及分析(二)

zmq的pubsub模式下inproc,ipc,tcp,epgm的通信性能测试结果以及分析(二),第1张

将结果整理如图:

[if !vml]

[endif]

            (epgm后面四个时间因为过大,没有放进图中)

       通过对比,我们可以看出:

[if !supportLists]1.    [endif]四种方式的传输时间都随pub端发送消息的空间大小的增加而增加,在消息超过512kb之后,传输时间的增加速度变大,且tcp和ipc这两种方式最为明显。

[if !supportLists]2.    [endif]三种通信方式中,inproc 的速度远小于其他两种,特别是随着消息字节数增加,其性能优势愈发明显。

[if !supportLists]3.    [endif]比较tcp ,ipc和epgm ,可以看出在字节数小于65536的大多数情况下,三者差距不大。

[if !supportLists]4.    [endif]综合来看,inproc 的通信性能在当前情景下最有优势。

结果分析

对于以上的测试结果中inproc通信性能优势,通过参考 http://api.zeromq.org/4-2:zmq-inprocd 说明,可知zmq inproc是在单个进程中的不同线程之间传输。也就是说,不同的线程之间共享使用同一个ZMQ 环境上下文,如下,将新创建的上下文传递给子线程。

 

[if !vml]

[endif]

   已知zmq 通信用于node 和node之间,可以是主机或者是进程。结合以上inproc的特点,可以表明,对于一个采用inproc 的sub端来说,它会与同一进程中不同线程中的节点通信。而对于采用ipc 的传输来说,它会与同一主机不同进程上的节点通信,对于tcp 的sub端来说会与其他远程主机(本次测试都在本机)上的节点通信。

因此,使用inproc或ipc这样的传输,在正确的上下文中使用它们时,这两者比tcp传输速度更快,更高效。

Epgm采用的是udp协议对数据进行传输,而udp的设计传输极限不超过65535个字节,因此会在传输层进行数据分包,间隔一段时间再发送, 所以再字节数超过65535的测试中,时间都远超其他几种。

一些想法

基于zmq的特点,实际上在大多数情况下的远程服务器之间的传输,都只需要使用TCP传输,可以使用多对套接字。因为这样的应用场景,相对与socket的1对1 的关系,ZMQ 可以用N:M 的关系,在开发上zmq省略的细节,降低了开发难度。因此,如果要通过网络与其他计算机通信,请使用TCP。 

而在上文的测试中表明了inproc使用在同一个进程的线程之间,ipc表示本地进程之间,tcp表示远程进程之间。 因此,如果我们的场景是在多个不同的进程之间切换,但都在同一个逻辑服务器实例上,那么IPC传输可能是最佳选择。如果是在同一个进程之间,那么inproc 更为适合。Zmq的sub/pub可以在同一进程中,也可以在多个进程中(在远程和同一服务器实例中都可以)。我们可以根据应用场景的不同进行选择。

参考官方文档,可以知道epgm常用在mutilecasts的模式下。测试中也提示了我们使用过程中的注意事项,例如一次性传输数据超出限制,如果存在有多个发送端,某pub端发送的一次数据量过大,则可能出现sub端收到的包顺序混乱的情况。

ZMQ JAVA使用经验之 ZMQ简介怎么解决:

ZMQ被称为史上最快消息队列,它处于会话层之上,应用层之下,使用后台异步线程完成消息的接受和发送,完美的封装了Socket API,大大简化了编程人员的复杂度,被称为史上最强大的消息中间件。ZMQ是用C语言编写的,30s内完成消息的传输,能够兼容多个平台,多种语言,可以使用多种方式实现N对N的Socket连接。本文仅以JAVA版本的ZMQ API为例,介绍ZMQ。

ZMQ与传统的TCP Socket相比,具有以下优点:

1) ZMQ发送和接受的是具有固定长度的二进制对象,ZMQ的消息包最大254个字节,前6个字节是协议,然后是数据包。数据包由3个部分组成,第一个字节是包的长度,第二个字节是包的一些属性,最后是包的内容。如果超过255个字节(有一个字节表示包属性),则ZMQ会自动分包传输;而对于TCP Socket,是面向字节流的连接,编程人员需要处理数据块与数据块之间的边界问题,而ZMQ能够保证每次用户发送和接受的都是一个完整的数据块;

2) 传统的TCP Socket的连接是1对1的,可以认为“1个Socket=1个连接”,每一个线程独立的维护一个Socket。但是ZMQ摒弃了这种1对1的模式,ZMQ的Socket可以很轻松的实现1对N,N对1和N对N的连接模式,一个ZMQ的Socket可以自动的维护一组连接,用户无法操作这些连接,用户只能操作套接字,而不是连接本身,所以说ZMQ的世界里,连接是私有的。这里大家关心的一点是,一个Socket是如何识别来自多个Socket的连接的,这里以请求响应模式为例介绍ZMQ是如何实现一个Socket连接N个Socket的;

3)ZMQ使用异步后台线程处理接受和发送请求,这意味着发送完消息,不可以立即释放资源,消息什么时候发送用户是无法控制的,同时,ZMQ自动重连,这意味着用户可以以任意顺序加入到网络中,服务器也可以随时加入或者退出网络;

ZMQ之所以能够在无状态的网络中实现1对N的连接,关键在于信封的机制,信封里保存了应答目标的位置。ZMQ涉及到请求-响应模式的Socket一共有4种类型:

DEALER是一种负载均衡,它会将消息分发给已连接的节点,并使用公平队列的机制处理接受到的消息。

REQ发送消息时会在消息顶部插入一个空帧,接受时会将空帧移去。其实REQ是建立在DEALER之上的,但REQ只有当消息发送并接受到回应后才能继续运行。

ROUTER在收到消息时会在顶部添加一个信封,标记消息来源。发送时会通过该信封决定哪个节点可以获取到该条消息。

REP在收到消息时会将第一个空帧之前的所有信息保存起来,将原始信息传送给应用程序。在发送消息时,REP会用刚才保存的信息包裹应答消息。REP其实是建立在ROUTER之上的,但和REQ一样,必须完成接受和发送这两个动作后才能继续。

在了解了4种类型的Socket之后,我们就不难理解ZMQ的信封机制了。ZMQ信封机制的核心是Router Socket,它的工作原理如下:

从ROUTER中读取一条消息时,ZMQ会包上一层信封,上面注明了消息的来源。向ROUTER写入一条消息时(包含信封),ZMQ会将信封拆开,并将消息递送给相应的对象。当REQ Socket向ROUTER Socket发送一条请求后,REP会从ROUTER收到一条消息,消息格式如下:

第三帧是REP从应用程序收到的数据,第二帧是空帧,是REQ在发送ROUTER数据之前添加的,用来表示结束,第一帧即信封,是ROUTER添加的,主要用来记录消息来源;整个数据包处理过程如下:

对于REQ Socket,可以在创建Socket的时候,为该Sock指定标示符,此时的Socket称为持久Socket,没有指定标示符的我们称为瞬时Socket,ROUTER会自动为瞬时Socket生成一个标示符;

这样REP返回包含信封的数据给ROUTER,ROUTER就可以根据信封上的标示符将该消息发送到对应的REQ上;

ZMQ使用注意事项:

ZMQ是在发送端缓存消息的,可以通过阈值控制消息的溢出;

ZMQ不可以线程之间共享Socket,否则会报org.zeromq.ZMQException: Operation cannot be accomplished in current state错误。

ZMQ一个进程只允许有一个Context,new Context(int arg) arg表示后台线程的数量;

ZMQ的Socket类有一个Linger参数,可以通过SetLinger设置,主要用于表示该Socket关闭以后,未发送成功的消息是否还保存,如果设置为-1表示该消息将永久保存(除非宕机,ZMQ是不持久化消息的),如果为0表示所有未发送成功的消息在Socker关闭以后都将立即清除,如果是一个正数,则表示该消息在Socket关闭后多少毫秒内被删除;这个方法非常有用,尤其在控制发送失败时,是否重发消息。

微服务是什么?

微服务是一种架构风格,它包括多个彼此间进行通信的独立进程。在设计上,这些进程具有高度的可扩展性、相互解耦而且一次只完成一个较小的任务。这些服务都拥有自己的资源以及通过网络实现彼此间通信的进程。

相比于靠后端的 单体结构

来封装所有服务器逻辑的传统客户端-服务器架构(C/S架构)而言,微服务架构的差异性体现在关注点分离(Separation of

concern)。这种设计模式更易于维护,使得灵活性、可扩展性及容错能力更强。但是这种分布式架构所的不足之处体现在如果设计不合理就会使得排错及维

护变得复杂。

一个简单微服务的例子

让我们来分析这样的一个场景:你正在使用微服务模式构建一个电子商务网店。

对于一个电商网店上的常见商品,好比说iPhone,其详情页会显示:

。产品的及基本信息

。你的购买历史

。哪些人买了iPhone也买了手机套

。与苹果手机相关的优惠和折扣

。店家的数据

。送货方式

。推荐商品等等

此外,这个简单的产品详情页的接口将有多个版本的来匹配web、移动端以及用于第三方应用程序的REST API。

在微服务模式中数据分布在多个服务之间。在这个例子中,服务包括:

。产品详情服务

。商家服务

。支付服务

。优惠及折扣服务

。库存服务

。定价服务

。回顾服务

。推荐服务

这些独立的服务是如何被访问的呢?

解决办法是使用一个API网管,它作为所有客户端的单一入口并且根据需求调用分布在整个基础架构中的特定微服务。以上模式的行业应用案例是NetFlix API网关,它具有支持不同设备的多个API客户端。你可以点击此处 了解更多 。

构建一个简单的微服务

目前有很多方法可以用于构建你的微服务。

在本文中我们将使用ZeroMQ来创建两个进程之间的通信。ZeroMQ提供了用于在套接字之上开发可扩展、分布式systed的构建块。它使用椭圆曲线密码体制(第四版)来实现安全性,并提供了即刻开启的 通讯模式 。

关于ZMQ,还有很多 优点 。MQ即是针对异步工作而设计的线程化消息队列。谈论太多zeroMQ的内容已经超出了本文的范畴,你可以阅读 使用zeromq 以及 zeromq用于分布式系统 。

我们要使用的另一个工具是 Docker 。本文假设读者对Docker已经有了基础的了解。

ZeroMQ有很多种通讯模式,为了开始我们的工作,让我们用ZeroMQ和Flask来配置一个简单的PUB-SUB。下图展示了组件之间的关系和数据流。

1&3 - 一个flask服务器运行在5000端口上而且其URL是 /downcase/ 。该URL用来接受(GET)请求,而所有格式为的请求将收到回应:答谢字符将会转换为小写字符并返回。

2 - 回应的消息也被发送给同一个容器中的ZMQ发布者(Publisher)

4,5 - ZMQ订阅者(subscriber)持续监听并将来自ZMQ服务器的消息保存到名为 subscriber.log 的文件中

创建服务器

首先看一下我们的Dockerfile

<pre><code>

FROM ubuntu:14.04

RUN apt-get update

RUN apt-get install -y --force-yes python python-dev python-setuptools software-properties-common gcc python-pip

RUN apt-get clean all

RUN pip install pyzmq

RUN pip install Flask

ADD zmqserver.py /tmp/zmqserver.py

Flask Port

EXPOSE 5000

Zmq Sub Server

EXPOSE 4444

CMD ["python","/tmp/zmqserver.py"]

</code></pre>

我们选择Ubuntu

14.04作为容器操作系统。我们安装了基本的软件包。通过pip,我们安装pyzmq(zeromq的Python绑定)同时也安装了Flask。接着

我们导出端口5000(flask服务器)和4444(发布者运行的端口)。此外,我们复制了包含所有flask及zeromq

pythond代码的脚本文件 zmqserver.py 并运行它。

现在我们来看一下zmqserver.py的内容:

server.py

import time

import zmq

HOST = '127.0.0.1'

PORT = '4444'

_context = zmq.Context()

_publisher = _context.socket(zmq.PUB)

url = 'tcp://{}:{}'.format(HOST, PORT)

def publish_message(message):

try:

_publisher.bind(url)

time.sleep(1)

_publisher.send(message)

except Exception as e:

print "error {}".format(e)

finally: _publisher.unbind(url)

from flask import Flask

from flask import request

app = Flask(__name__)

@app.route("/downcase/", methods=['GET'])

def lowerString():

_strn = request.args.get('param')

response = 'lower case of {} is {}'.format(_strn, _strn.lower()) publish_message(response)

return response

if __name__ == '__main__':

app.run(host='0.0.0.0', debug=False)

ZMQ发布者运行在4444端口上。我们创建了一个context并且声明了URL。我们运行了flask app,它通过URL /downcase/ 把GET获得的参数 Param 转换成小写字符,这就是服务的应答。应答的字符串是 published ,它作为一个消息把相同的字符串返回给浏览器。

为了构建以上的Docker映像(image),我们执行以下的命令:

sudo docker build -t docker-zmq-pub

并且在该映像之上执行:

docker run --name docker-pub-server -p 5000:5000 -p 4444:4444 -t docker-zmq-pub

我们把容器中的端口5000和4444映射到这台主机上,于是无论客户端在哪里,它们都可以订阅这个发布者。

订阅者客户端

client.py

import zmq

import sys

import time

import logging

import os

HOST = '127.0.0.1'

PORT = '4444'

logging.basicConfig(filename='subscriber.log', level=logging.INFO)

class ZClient(object):

def __init__(self, host=HOST, port=PORT):

"""Initialize Worker"""

self.host = host

self.port = port

self._context = zmq.Context()

self._subscriber = self._context.socket(zmq.SUB)

print "Client Initiated"

def receive_message(self):

"""Start receiving messages"""

self._subscriber.connect('tcp://{}:{}'.format(self.host, self.port))

self._subscriber.setsockopt(zmq.SUBSCRIBE, b"")

while True:

print 'listening on tcp://{}:{}'.format(self.host, self.port)

message = self._subscriber.recv()

print message

logging.info(

'{} - {}'.format(message, time.strftime("%Y-%m-%d %H:%M")))

if __name__ == '__main__':

zs = ZClient()

zs.receive_message()

我们声明了发布者的IP地址及端口,当前它运行在同一个的主机上因此地址是127开头。我们在URL tcp://IP:PORT 上进行监听。一旦我们收到一个消息,就将其附上时间戳后记录到名为 subscriber.log 的文件中。运行客户端要做的所有工作就是执行 python <name_of_client_file>.py 。如果你在以上的架构上进行构建,它可以很好地充当近实时的日志聚合引擎。

我在Unbuntu主机上对以上的代码进行了测试。这里所用的代码保管在 github 上。这是一个如何配置zmq、docker和python服务器的基础讲解,在我的下一片文章中我们会使用我们已经学习的东西构建简单的微服务。


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/471506.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-06-06
下一篇2023-06-06

发表评论

登录后才能评论

评论列表(0条)

    保存