前言

介绍

  1. [NetMQ](https://github.com/zeromq/netmq.git)是ZeroMQ的C#移植版本,它是对标准socket接口的扩展。它提供了一种异步消息队列,多消息模式,消息过滤(订阅),对多种传输协议的无缝访问。
  2. 当前有2个版本正在维护,版本3最新版为3.3.4,版本4最新版本为4.0.0-rc5。本文档是对4.0.0-rc5分支代码进行分析。

zeromq的英文文档

NetMQ的英文文档

目的

对NetMQ的源码进行学习并分析理解,因此写下该系列文章,本系列文章暂定编写计划如下:

  1. 消息队列NetMQ 原理分析1-Context和ZObject
  2. 消息队列NetMQ 原理分析2-IO线程和完成端口
  3. 消息队列NetMQ 原理分析3-命令产生/处理和回收线程
  4. 消息队列NetMQ 原理分析4-Session和Pipe
  5. 消息队列NetMQ 原理分析5-Engine
  6. 消息队列NetMQ 原理分析6-TCP和Inpoc实现
  7. 消息队列NetMQ 原理分析7-Device
  8. 消息队列NetMQ 原理分析8-不同类型的Socket
  9. 消息队列NetMQ 原理分析9-实战

友情提示: 看本系列文章时最好获取源码,更有助于理解。


Context

NetMQ有一个Context对象,用于初始化并保存当前NetMQ底层的对象状态,如IO线程、回收线程、进程间传输节点字典、插槽m_slots(用于保存IO对象,回收对象和socket对象的Mailbox)、初始化但未用到的Socket对象指针数组以及当前Mailbox(用于接收终止信号)等。

初始化Context

当创建第一个Socket对象时会初始化IO线程,回收线程以及工作线程。默认Socket数量1024个,IO线程1个,回收线程1个。

m_slots = new Mailbox[m_slotCount];//m_soltCount = 1 + 1 + 1024

m_slots[0]保存的是Context的Mailbox。

m_slots[TermTid] = m_termMailbox;//用于当前Context接收终止信号

m_slots[1]保存的是回收对象的Mailbox,保存完毕后就会启动回收对象轮询线程。

  1. m_reaper = new Reaper(this, ReaperTid);//ReaperTid = 1
  2. m_slots[ReaperTid] = m_reaper.Mailbox;
  3. m_reaper.Start();

m_slots[2]保存的是IO线程对象的Mailbox。

  1. for (int i = 2; i != ios/*ios = 1,默认用1个io线程*/ + 2; i++)
  2. {
  3. IOThread ioThread = new IOThread(this, i);
  4. m_ioThreads.Add(ioThread);
  5. m_slots[i] = ioThread.Mailbox;
  6. ioThread.Start();
  7. }

其余1024个slot保存的是socket对象的Mailbox,当socket还没使用是,slots保存的是null,占个位置,同时m_emptySlots。

  1. //m_soltCount = 1 + 1 + 1024
  2. for (int i = (int)m_slotCount - 1; >= (int)ios + 2; i--)
  3. {
  4. m_emptySlots.Push(i);
  5. m_slots[i] = null;
  6. }

创建SocketBase

无论是什么类型的Socket全都是在Context中进行创建或释放的。NetMQ中不同Socket都继承自SocketBase,在Context未中止且Socket未满时,会从m_emptySlots栈中Pop出一个未使用的指针。若创建失败,则重新加回到栈中,否则更新当前使用的Socket的集合加入该Socket并更新m_slots的Mailbox

  1. //slot是当前socket在s_slots中的位置,也用于生成SocketBase的`ThreadId`
  2. int slot = m_emptySlots.Pop();
  3. // sid是生成并递增的唯一的socket ID,用于SocketBase创建MailBox命名用,并无实际其他作用。
  4. int sid = Interlocked.Increment(ref s_maxSocketId);
  5. s = SocketBase.Create(type, this, slot, sid);
  6. if (s == null)
  7. {
  8. m_emptySlots.Push(slot);
  9. return null;
  10. }
  11. m_sockets.Add(s);
  12. m_slots[slot] = s.Mailbox;

释放SocketBase

Reaper要释放某个SocketBase时,最终会调用Context的DestroySocket方法。

  1. tid = socket.ThreadId;
  2. //重新加入到可用socket栈中
  3. m_emptySlots.Push(tid);
  4. //关闭连接
  5. m_slots[tid].Close();
  6. //清空引用
  7. m_slots[tid] = null;
  8. // 从当前使用socket集合移除
  9. m_sockets.Remove(socket);
  10. //若当前接收到中止信号且当前socket全部已释放时停止回收线程
  11. if (m_terminating && m_sockets.Count == 0)
  12. m_reaper.Stop();

缓存进程内通信Socket

NetMQ除了支持TCP以外还支持inproc(进程内通讯),ipc(进程间通讯),pgm和epgm(多路广播)等传输协议。

Context会用一个字典管理当前使用inpoc的socket。

当inpoc的socket进行绑定时会加入到字典缓存中。释放时会从字典缓存中移除。当使用inpoc协议连接时,增加当前绑定inpoc地址的连接数。

ZObject

ZObject是NetMQ的Session(状态),IOThread(IO线程),Repear(回收线程),Pipe(管道),Own(所属关系)对象的基类,它是包含2个信息,当前全局Context对象,以及当前对象处理的线程Id。所有socket最终都是继承自该对象。因此ZObject对象需要知道IO对象接收到不同命令时如何进行处理命令。

NetMQ中一共定义了一下的命令类型

  1. public enum CommandType
  2. {
  3. // 发送给IO线程表示当前对象需要停止
  4. Stop,
  5. // 发送给IO线程表示当前对象需要注册到IO线程中
  6. Plug,
  7. // 将创建的对象Session的加入到当前Socket的所属集合中
  8. Own,
  9. // 附加engine到Session中
  10. Attach,
  11. // 建立session到Socket之间的管道,在握手之前调用inc_seqnum.
  12. Bind,
  13. // 通过写管道发送通知给读管道多少信息可读
  14. ActivateRead,
  15. // 通过读管道发送通知给写读管道多少信息可写
  16. ActivateWrite,
  17. // 创建一个新的管道后通过读管道发送给写管道
  18. // 参数是管道类型,然而,他的目的地是私有的,因此我们必须用void指针, however,
  19. Hiccup,
  20. // 通过读管道发送到写管道告诉他中止所有管道
  21. PipeTerm,
  22. // 写管道对PipeTerm命令响应
  23. PipeTermAck,
  24. // 通过IO对象发送给socket请求终端IO对象
  25. TermReq,
  26. // 通过socket发送给IO对象他自己开始关闭
  27. Term,
  28. // 通过IO对象发送给socket让它知道已经关闭
  29. TermAck,
  30. // 将关闭套接字的所有权转移给回收线程.
  31. Reap,
  32. // 关闭套接字通知回收线程他已经释放
  33. Reaped,
  34. // 当所有socket都被释放通过回收线程发送给 term 线程
  35. Done
  36. }

根据不同命令类型进行处理,处理方式由具体的Socket子类去重载。

  1. public void ProcessCommand(Command cmd)
  2. {
  3. switch (cmd.CommandType)
  4. {
  5. case CommandType.ActivateRead:
  6. ProcessActivateRead();
  7. break;
  8. case CommandType.ActivateWrite:
  9. ProcessActivateWrite((long)cmd.Arg);
  10. break;
  11. case CommandType.Stop:
  12. ProcessStop();
  13. break;
  14. case CommandType.Plug:
  15. ProcessPlug();
  16. ProcessSeqnum();
  17. break;
  18. case CommandType.Own:
  19. ProcessOwn((Own)cmd.Arg);
  20. ProcessSeqnum();
  21. break;
  22. case CommandType.Attach:
  23. ProcessAttach((IEngine)cmd.Arg);
  24. ProcessSeqnum();
  25. break;
  26. case CommandType.Bind:
  27. ProcessBind((Pipe)cmd.Arg);
  28. ProcessSeqnum();
  29. break;
  30. case CommandType.Hiccup:
  31. ProcessHiccup(cmd.Arg);
  32. break;
  33. case CommandType.PipeTerm:
  34. ProcessPipeTerm();
  35. break;
  36. case CommandType.PipeTermAck:
  37. ProcessPipeTermAck();
  38. break;
  39. case CommandType.TermReq:
  40. ProcessTermReq((Own)cmd.Arg);
  41. break;
  42. case CommandType.Term:
  43. ProcessTerm((int)cmd.Arg);
  44. break;
  45. case CommandType.TermAck:
  46. ProcessTermAck();
  47. break;
  48. case CommandType.Reap:
  49. ProcessReap((SocketBase)cmd.Arg);
  50. break;
  51. case CommandType.Reaped:
  52. ProcessReaped();
  53. break;
  54. default:
  55. throw new ArgumentException();
  56. }
  57. }

处理进程间通信协议

当创建进程间通信socket时,会调用ZObejct的RegisterEndpoint将socket对象加入到Context的使用inpoc协议的socket字段缓存中,而ZObject实际是调用Context的方法RegisterEndpoint,释放使用inpoc协议的socket和使用inpoc进行连接方式和RegisterEndpoint一样。

  1. protected void RegisterEndpoint(String addr, Ctx.Endpoint endpoint)
  2. {
  3. //m_ctx是在ZObejct初始化是传进来的Context引用
  4. m_ctx.RegisterEndpoint(addr, endpoint);
  5. }

多个IO线程

默认的IO线程数量是1个,当然也可以使用多个IO线程并发去处理,因此当创建监听对象或创建连接时则需要进行负载均衡,平分到多个IO线程去处理,切换IO线程也是在Context中实现的。

  1. protected IOThread ChooseIOThread(long affinity)
  2. {
  3. return m_ctx.ChooseIOThread(affinity);
  4. }
  1. public IOThread ChooseIOThread(long affinity)
  2. {
  3. //affinity表示哪些IO线程有资格,默认为0表示所有IO线程都可以处理。
  4. if (m_ioThreads.Count == 0)
  5. return null;
  6. // Find the I/O thread with minimum load.
  7. int minLoad = -1;
  8. IOThread selectedIOThread = null;
  9. for (int i = 0; i != m_ioThreads.Count; i++)
  10. {
  11. if (affinity == 0 || (affinity & (1L << i)) > 0)
  12. {
  13. //获取IO线程socket载入次数
  14. int load = m_ioThreads[i].Load;
  15. //这里对IO线程进行负载均衡
  16. if (selectedIOThread == null || load < minLoad)
  17. {
  18. minLoad = load;
  19. selectedIOThread = m_ioThreads[i];
  20. }
  21. }
  22. }
  23. return selectedIOThread;
  24. }

总结

该篇介绍了Context和ZObject。NetMQ所有的socket对象创建,释放都离不开Context,由于Context内部对必要操作都加了锁,因此它是线程安全的。




微信扫一扫二维码关注订阅号杰哥技术分享

本文地址:https://www.cnblogs.com/Jack-Blog/p/6287458.html

作者博客:杰哥很忙

欢迎转载,请在明显位置给出出处及链接)

消息队列NetMQ 原理分析1-Context和ZObject的更多相关文章

  1. 消息队列NetMQ 原理分析2-IO线程和完成端口

    消息队列NetMQ 原理分析2-IO线程和完成端口 前言 介绍 目的 IO线程 初始化IO线程 Proactor 启动Procator线程轮询 处理socket 获取超时时间 从完成端口获取处理完的状 ...

  2. 消息队列NetMQ 原理分析3-命令产生/处理和回收线程

    消息队列NetMQ 原理分析3-命令产生/处理和回收线程 前言 介绍 目的 命令 命令结构 命令产生 命令处理 创建Socket(SocketBase) 创建连接 创建绑定 回收线程 释放Socket ...

  3. 消息队列NetMQ 原理分析4-Socket、Session、Option和Pipe

    消息队列NetMQ 原理分析4-Socket.Session.Option和Pipe 前言 介绍 目的 Socket 接口实现 内部结构 Session Option Pipe YPipe Msg Y ...

  4. 消息队列NetMQ 原理分析5-StreamEngine、Encord和Decord

    消息队列NetMQ 原理分析5-StreamEngine,Encord和Decord 前言 介绍 目的 StreamEngine 发送数据 接收数据 流程分析 Encoder V2Encoder V1 ...

  5. Netty构建分布式消息队列实现原理浅析

    在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子, ...

  6. PHP消息队列用法实例分析

    这篇文章主要介绍了PHP消息队列用法,结合实例形式分析了PHP消息队列用于Linux下进程间通信的相关技巧,需要的朋友可以参考下   该消息队列用于linux下,进程通信 队列状态信息:具体参考手册

  7. Rabbimq必备基础之对高级消息队列协议AMQP分析及Rabbitmq本质介绍

    MQ的一个产品... [消息队列] 1. MSMQ windows自带的一个服务... [petshop],message存放在文件系统中. 最原始的消息队列... [集群,消息确认,内存化,高可用, ...

  8. redis作为消息队列的原理

    Redis队列功能介绍 List 转:https://blog.csdn.net/cestlavieqiang/article/details/84197736 常用命令: Blpop删除,并获得该列 ...

  9. 自制MFC消息响应定位器+原理分析

    mfc里面有张消息映射表(MESSAGE_MAP),消息都是通过这张表来分发到相应函数里的. 这个是我自制的定位器,从vc6.0到现在的2013生成的mfc都可以用,全静态扫描并已处理动态基址. 下面 ...

随机推荐

  1. IISExpress配置文件

    ASP.NET MVC IISExpress配置文件的一个坑 现象: 昨天在处理PBS系统问题的时候意外发现两个js错误(而同样的代码在同事机器上都没有问题),如下图. 图1 图2 图3 原因分析: ...

  2. 记一次内存泄漏DUMP分析

    自从进入一家创业公司以后,逐渐忙成狗,却无所收获,感觉自身的技术能力用武之地很少,工作生活都在业务逻辑中颠倒. 前些天线上服务内存吃紧,让运维把DUMP拿下来,分析一下聊以自慰. 先来统计一下大对象信 ...

  3. Gimp教程:制作彩色的网站横幅

    效果图: Step1.新建900x200的透明图层 Step2.点选画笔工具,在左下方的设置界面进行如下: 设置画笔,动态,颜色,勾选应用抖动,然后试着在图层上画一画,调节一下画笔大小,相信你能作出如 ...

  4. 基于jQuery的上下无缝滚动应用(单行或多行)

    工作中遇到的一个js代码,现在对.trigger('mouseleave'),仍一知半解... <script>$(function(){//单行应用var _wrap=$('ul.lin ...

  5. CentOS下JAVA WEB 环境搭建

    首先介绍下我的软件环境.虚拟机Vmware9.0(已经汉化),CentOS6.4(选择安装语言为简体中文),xshell4.0(强大的安全终端模拟软件),xftp4.0(FTP工具). 方便大家环境搭 ...

  6. 几乎没用到过的css 样式

    1. :focus  选择器用于选取获得焦点的元素. 案例:http://www.w3school.com.cn/tiy/t.asp?f=css_sel_focus 2. clearfix清除浮动闭合 ...

  7. MVC Bootstrap极速开发框架

    ASP.NET MVC Bootstrap极速开发框架 前言 每次新开发项目都要从头开始设计?有木有一个通用的快速开发框架?并且得是ASP.NET MVC  And Bootstrap?数据库不要手工 ...

  8. Unit Of Work-工作单元

    Unit Of Work-工作单元 阅读目录: 概念中的理解 代码中的实现 后记 掀起了你的盖头来,让我看你的眼睛,你的眼睛明又亮呀,好像那水波一模样:掀起了你的盖头来,让我看你的脸儿,看看你的脸儿红 ...

  9. 在线web编辑器

    真正在线编辑的在线web编辑器 最近正在研究开发一款在线web编辑器架构,这是一款真正傻瓜式的web编辑器,可以在正常浏览页面的情况进行编辑,经过测试,对于一般网页页面来说非常好用方便,操作更简单. ...

  10. ASP.NET MVC应用程序实现下载功能

    ASP.NET MVC应用程序实现下载功能 上次Insus.NET有在MVC应用程序实现了上传文件的功能<MVC应用程序显示上传的图片> http://www.cnblogs.com/in ...