前言

介绍

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

zeromq的英文文档

NetMQ的英文文档

目的

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

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

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


命令

命令结构

Command定义如下

  1. internal struct Command
  2. {
  3. public Command([CanBeNull] ZObject destination, CommandType type, [CanBeNull] object arg = null) : this()
  4. {
  5. Destination = destination;
  6. CommandType = type;
  7. Arg = arg;
  8. }
  9. [CanBeNull]
  10. public ZObject Destination { get; }
  11. public CommandType CommandType { get; }
  12. [CanBeNull]
  13. public object Arg { get; private set; }
  14. public override string ToString()
  15. {
  16. return base.ToString() + "[" + CommandType + ", " + Destination + "]";
  17. }
  18. }

其包含了3个信息:调用者,命令类型和命令参数。

命令产生

还记的《消息队列NetMQ 原理分析1-Context和ZObject》中我们介绍过NetMQ中的命令类型吗?待处理命令全部会存放着Socket的信箱中。当Socket有命令(连接完成、发送完成或接受完成等)需要处理时调用基类ZObjectSendCommand方法。

  1. private void SendCommand([NotNull] Command cmd)
  2. {
  3. m_ctx.SendCommand(cmd.Destination.ThreadId, cmd);
  4. }

ZObject实际调用Context的SendCommand方法

  1. public void SendCommand(int threadId, [NotNull] Command command)
  2. {
  3. m_slots[threadId].Send(command);
  4. }

m_slots[threadId]保存的是当前IO线程的IO信箱IOThreadMailbox,在《消息队列NetMQ 原理分析2-IO线程和完成端口》

我们简单介绍了IOThreadMailbox的结构。

  1. [NotNull] private readonly YPipe<Command> m_commandPipe = new YPipe<Command>(Config.CommandPipeGranularity, "mailbox");

IOThreadMailbox中维护这一个Command管道,该管道实际就是一个先进先出队列,详细解析会在第四章进行介绍。

  1. public void Send(Command command)
  2. {
  3. bool ok;
  4. lock (m_sync)
  5. {
  6. //向管道写入命令
  7. m_commandPipe.Write(ref command, false);
  8. //成功写入会返回false,表示有命令需要处理
  9. ok = m_commandPipe.Flush();
  10. }
  11. if (!ok)
  12. {
  13. //向完成端口传递信号
  14. m_proactor.SignalMailbox(this);
  15. }
  16. }
  17. public bool TryRecv(out Command command)
  18. {
  19. return m_commandPipe.TryRead(out command);
  20. }
  21. public void RaiseEvent()
  22. {
  23. if (!m_disposed)
  24. {
  25. m_mailboxEvent.Ready();
  26. }
  27. }

命令发送完成调用Flush方法更新指针下标。返回ok若为true,表示管道已全部读取完毕,无需发送信号量通知处理。若返回false,则需要向内核发送一个信号,IO线程获取到则调用到指定的命令事件。

IOThreadMailbox的主要就是这三个方法

  1. 当有命令来的时候调用Send方法向管道(队列)写入命令。写完时,会向完成端口传递信号。
  2. 当有命令需要处理时调用TryRecv方法读取
  3. 当完成端口接收到信号需要命令处理时,调用RaiseEvent(实际是信箱的IO线程的RaiseEvent方法)进行处理命令。
  1. public void SignalMailbox(IOThreadMailbox mailbox)
  2. {
  3. //该方法会向完成端口的队列中插入一个信号状态
  4. m_completionPort.Signal(mailbox);
  5. }

有关于完成端口介绍请查看《消息队列NetMQ 原理分析2-IO线程和完成端口》

命令处理

当有命令需要处理时,完成端口会接收到信号。

  1. private void Loop()
  2. {
  3. ...
  4. int timeout = ExecuteTimers();
  5. int removed;
  6. if (!m_completionPort.GetMultipleQueuedCompletionStatus(timeout != 0 ? timeout : -1, completionStatuses, out removed))
  7. continue;
  8. for (int i = 0; i < removed; i++)
  9. {
  10. try
  11. {
  12. if (completionStatuses[i].OperationType == OperationType.Signal)
  13. {
  14. var mailbox = (IOThreadMailbox)completionStatuses[i].State;
  15. mailbox.RaiseEvent();
  16. }
  17. ...
  18. }
  19. ...
  20. }
  21. ...
  22. }

在线程轮询方法Loop中,当接收到需要处理的数据时,首先会判断是否是信号,若为信号,则将状态(参数)转化为IOThreadMailbox类型,同时调用RaiseEvent方法处理命令。

  1. public void Ready()
  2. {
  3. Command command;
  4. while (m_mailbox.TryRecv(out command))
  5. command.Destination.ProcessCommand(command);
  6. }

当有命令需要处理时,会调用IOThreadMailboxTryRecv方法从管道(队列,先进先出)中获取第一个命令进行处理。

创建Socket(SocketBase)

在介绍回收线程工作之前,我们先看下创建一个新的Socket做了哪些工作,这里的Socket实际是NetMQ中的SocketBase

  1. RequestSocket socket = new RequestSocket();
  2. socket.Connect("tcp://127.0.0.1:12345");

NetMQSocket是NetMQ的Socket的基类。

  1. public RequestSocket(string connectionString = null) : base(ZmqSocketType.Req, connectionString, DefaultAction.Connect)
  2. {
  3. }
  1. internal NetMQSocket(ZmqSocketType socketType, string connectionString, DefaultAction defaultAction)
  2. {
  3. m_socketHandle = NetMQConfig.Context.CreateSocket(socketType);
  4. m_netMqSelector = new NetMQSelector();
  5. Options = new SocketOptions(this);
  6. m_socketEventArgs = new NetMQSocketEventArgs(this);
  7. Options.Linger = NetMQConfig.Linger;
  8. if (!string.IsNullOrEmpty(connectionString))
  9. {
  10. var endpoints =
  11. connectionString.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
  12. .Select(a => a.Trim()).Where(a=> !string.IsNullOrEmpty(a));
  13. foreach (string endpoint in endpoints)
  14. {
  15. if (endpoint[0] == '@')
  16. {
  17. Bind(endpoint.Substring(1));
  18. }
  19. else if (endpoint[0] == '>')
  20. {
  21. Connect(endpoint.Substring(1));
  22. }
  23. else if (defaultAction == DefaultAction.Connect)
  24. {
  25. Connect(endpoint);
  26. }
  27. else
  28. {
  29. Bind(endpoint);
  30. }
  31. }
  32. }
  33. }

首先会根据Socket的类型创建对应的Socket,调用的是ContextCreateSocket方法。具体的请看创建SocketBase。最终创建方法是调用SocketBaseCreate方法

  1. public static SocketBase Create(ZmqSocketType type, [NotNull] Ctx parent, int threadId, int socketId)
  2. {
  3. switch (type)
  4. {
  5. ...
  6. case ZmqSocketType.Req:
  7. return new Req(parent, threadId, socketId);
  8. ...
  9. default:
  10. throw new InvalidException("SocketBase.Create called with invalid type of " + type);
  11. }
  12. }

创建完后,就对地址进行解析。若有多个地址,则可用,分隔。

  1. var endpoints =
  2. connectionString.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
  3. .Select(a => a.Trim()).Where(a=> !string.IsNullOrEmpty(a));

解析完成后则用默认的方式进行绑定或连接,如RequestSocket默认为连接,而ResponseSocket则为绑定。

创建连接

  1. 首先对地址进行解析,判断当前是tcp还是其他协议。然后会根据协议类型创建对应的Socket,具体的协议类型分析请查看《消息队列NetMQ 原理分析6-TCP和Inpoc实现》

    1. private static void DecodeAddress([NotNull] string addr, out string address, out string protocol)
    2. {
    3. const string protocolDelimeter = "://";
    4. int protocolDelimeterIndex = addr.IndexOf(protocolDelimeter, StringComparison.Ordinal);
    5. protocol = addr.Substring(0, protocolDelimeterIndex);
    6. address = addr.Substring(protocolDelimeterIndex + protocolDelimeter.Length);
    7. }
  2. 负载均衡选择一个IO线程。

  3. 创建Session,SocketSession的关系如图所示

  4. 创建管道,创建管道会创建一对单向管道,形成“一个”双向管道。头尾分别连接SocketSession,如上图所示。创建管道完毕后需要设置管道的回调事件,管道1设置回调为Socket的回调方法,管道2设置为Session的回调方法。

具体关于SessionPipe的内容请查看《消息队列NetMQ 原理分析4-Session、Option和Pipe》

  1. 处理SocketSession的关系
  1. protected void LaunchChild([NotNull] Own obj)
  2. {
  3. // Specify the owner of the object.
  4. obj.SetOwner(this);
  5. // Plug the object into the I/O thread.
  6. SendPlug(obj);
  7. // Take ownership of the object.
  8. SendOwn(this, obj);
  9. }
  • Session的宿主设置为该Socket
  1. private void SetOwner([NotNull] Own owner)
  2. {
  3. Debug.Assert(m_owner == null);
  4. m_owner = owner;
  5. }
  • 为IO对象设置Session,当管道有数据交互时,Session的回调方法就会触发。
  1. protected void SendPlug([NotNull] Own destination, bool incSeqnum = true)
  2. {
  3. if (incSeqnum)
  4. destination.IncSeqnum();
  5. SendCommand(new Command(destination, CommandType.Plug));
  6. }

SessionBaseProcessPlug会被触发

  1. protected override void ProcessPlug()
  2. {
  3. m_ioObject.SetHandler(this);
  4. if (m_connect)
  5. StartConnecting(false);
  6. }
  • 将当前Session加入到SocketSession集合中,
  1. protected void SendOwn([NotNull] Own destination, [NotNull] Own obj)
  2. {
  3. destination.IncSeqnum();
  4. SendCommand(new Command(destination, CommandType.Own, obj));
  5. }

SocketBase的父类方法SendOwn(Own方法)方法会被触发,将Session加入到集合中

  1. protected override void ProcessOwn(Own obj)
  2. {
  3. ...
  4. // Store the reference to the owned object.
  5. m_owned.Add(obj);
  6. }

创建绑定

  1. 首先对地址进行解析,判断当前是tcp还是其他协议。然后会根据协议类型创建对应的Socket,具体的协议类型分析请查看《消息队列NetMQ 原理分析6-TCP和Inpoc实现》

    1. private static void DecodeAddress([NotNull] string addr, out string address, out string protocol)
    2. {
    3. const string protocolDelimeter = "://";
    4. int protocolDelimeterIndex = addr.IndexOf(protocolDelimeter, StringComparison.Ordinal);
    5. protocol = addr.Substring(0, protocolDelimeterIndex);
    6. address = addr.Substring(protocolDelimeterIndex + protocolDelimeter.Length);
    7. }
  2. 负载均衡选择一个IO线程。

  3. 处理SocketSession的关系

  1. protected void LaunchChild([NotNull] Own obj)
  2. {
  3. // Specify the owner of the object.
  4. obj.SetOwner(this);
  5. // Plug the object into the I/O thread.
  6. SendPlug(obj);
  7. // Take ownership of the object.
  8. SendOwn(this, obj);
  9. }
  • Listener的宿主设置为该Socket
  1. private void SetOwner([NotNull] Own owner)
  2. {
  3. Debug.Assert(m_owner == null);
  4. m_owner = owner;
  5. }
  • 为IO对象设置Listener,当管道有数据交互是,Listener的回调方法就会触发。
  1. protected void SendPlug([NotNull] Own destination, bool incSeqnum = true)
  2. {
  3. if (incSeqnum)
  4. destination.IncSeqnum();
  5. SendCommand(new Command(destination, CommandType.Plug));
  6. }

ListenerProcessPlug会被触发

  1. protected override void ProcessPlug()
  2. {
  3. m_ioObject.SetHandler(this);
  4. m_ioObject.AddSocket(m_handle);
  5. //接收异步socket
  6. Accept();
  7. }
  • 将当前Listener加入到SocketListener集合中,
  1. protected void SendOwn([NotNull] Own destination, [NotNull] Own obj)
  2. {
  3. destination.IncSeqnum();
  4. SendCommand(new Command(destination, CommandType.Own, obj));
  5. }

SocketBase的父类方法SendOwn(Own方法)方法会被触发,将Listener加入到集合中

  1. protected override void ProcessOwn(Own obj)
  2. {
  3. ...
  4. // Store the reference to the owned object.
  5. m_owned.Add(obj);
  6. }

SocketBase的创建处理就完成了

回收线程

(垃圾)回收线程是专门处理(清理)异步关闭的Socket的线程,它在NetMQ中起到至关重要的作用。

  1. internal class Reaper : ZObject, IPollEvents
  2. {
  3. ...
  4. }

Reaper是一个ZObject对象,同时实现了IPollEvents接口,该接口的作用是当有信息接收或发送时进行处理。回收线程实现了InEvent方法。

  1. internal interface IPollEvents : ITimerEvent
  2. {
  3. void InEvent();
  4. void OutEvent();
  5. }

InEvent方法实现和IO线程的Ready方法很像,都是遍历需要处理的命令进行处理。

  1. public void InEvent()
  2. {
  3. while (true)
  4. {
  5. Command command;
  6. if (!m_mailbox.TryRecv(0, out command))
  7. break;
  8. command.Destination.ProcessCommand(command);
  9. }
  10. }

初始化回收线程

  1. public Reaper([NotNull] Ctx ctx, int threadId)
  2. : base(ctx, threadId)
  3. {
  4. m_sockets = 0;
  5. m_terminating = false;
  6. string name = "reaper-" + threadId;
  7. m_poller = new Utils.Poller(name);
  8. m_mailbox = new Mailbox(name);
  9. m_mailboxHandle = m_mailbox.Handle;
  10. m_poller.AddHandle(m_mailboxHandle, this);
  11. m_poller.SetPollIn(m_mailboxHandle);
  12. }
  1. 初始化回收线程是会创建一个Poller对象,用于轮询回收SocketBase
  2. 初始化回收线程会创建一个Mailbox对象用于Command的收发

MailBox

  1. internal class Mailbox : IMailbox{
  2. ...
  3. }

MailBoxIO线程IOThreadMailbox一样,实现了IMailbox接口。

释放SocketBase

当有SocketBase需要释放时,会向完成端口发送Reap信号。

  1. public void Close()
  2. {
  3. // Mark the socket as disposed
  4. m_disposed = true;
  5. //工作线程向Socket邮箱发送Reap信号
  6. //回收线程会做剩下的工作
  7. SendReap(this);
  8. }

发送回收命令

向回收线程的邮箱发送当前SocketBase的回收命令

  1. protected void SendReap([NotNull] SocketBase socket)
  2. {
  3. SendCommand(new Command(m_ctx.GetReaper(), CommandType.Reap, socket));
  4. }

处理回收命令

Reap接收到释放信号进行处理

  1. protected override void ProcessReap(SocketBase socket)
  2. {
  3. // Add the socket to the poller.
  4. socket.StartReaping(m_poller);
  5. ++m_sockets;
  6. }

SocketBase回收

  1. 将当前Socket的加入到回收线程的中,当Socket接收到数据时,由回收线程回调该Socket的处理事件进行处理。
  2. 当前Socket终止处理
  3. 最后确认释放
  1. internal void StartReaping([NotNull] Poller poller)
  2. {
  3. m_poller = poller;
  4. m_handle = m_mailbox.Handle;
  5. m_poller.AddHandle(m_handle, this);
  6. m_poller.SetPollIn(m_handle);
  7. Terminate();
  8. CheckDestroy();
  9. }
终止处理
  1. 终止Socket时,直接终止即可

默认情况下NetMQLinger值被设置为-1,就是说如果网络读写没有进行完是不能退出的。如果Linger被设置为0,那么中断时会丢弃一切未完成的网络操作。如果Linger被设置的大于0,那么将等待Linger毫秒用来完成未完成的网络读写,在指定的时间里完成或者超时都会立即返回。

  1. 若终止的是Session,则需要发送请求清理关联Socket的当前Session对象
  1. protected void Terminate()
  2. {
  3. ...
  4. if (m_owner == null)
  5. {
  6. // 释放的是Socket,Owner为空
  7. ProcessTerm(m_options.Linger);
  8. }
  9. else
  10. {
  11. // 释放的是Session则会关联一个Socket
  12. SendTermReq(m_owner, this);
  13. }
  14. }
终止SocketBase
  1. 终止SocketBase时,需要先中断当前SocketBase关联的SessionBase
  2. 然后增加需要终端请求响应的个数,当全部都响应了则处理第四步骤
  3. 清空当前关联的Session集合
  4. 最后当Session全部终止后发送给当前Socket宿主终端响应(TermAck)
  1. protected override void ProcessTerm(int linger)
  2. {
  3. ...
  4. // 断开所有session的连接
  5. foreach (Own it in m_owned)
  6. {
  7. SendTerm(it, linger);
  8. }
  9. RegisterTermAcks(m_owned.Count);
  10. m_owned.Clear();
  11. CheckTermAcks();
  12. }
终止当前Socket关联的Session
  1. 如果终端管道命令在终止命令前处理了,则立即终止当前Session
  2. 标记当前准备终止
  3. Ligner大于0 则等到N毫秒后再终止终止SocketSession之间的管道
  4. 检查管道是否还有数据要读取
  1. protected override void ProcessTerm(int linger)
  2. {
  3. if (m_pipe == null)
  4. {
  5. ProceedWithTerm();
  6. return;
  7. }
  8. m_pending = true;
  9. if (linger > 0)
  10. {
  11. Debug.Assert(!m_hasLingerTimer);
  12. m_ioObject.AddTimer(linger, LingerTimerId);
  13. m_hasLingerTimer = true;
  14. }
  15. // 是否需要等待一定时间后消息处理完再终止管道.
  16. m_pipe.Terminate(linger != 0);
  17. // TODO: Should this go into pipe_t::terminate ?
  18. // In case there's no engine and there's only delimiter in the
  19. // pipe it wouldn't be ever read. Thus we check for it explicitly.
  20. m_pipe.CheckRead();
  21. }
终止管道

管道状态如下所示

  1. private enum State
  2. {
  3. /// <summary> Active 表示在中断命令开始前的状态 </summary>
  4. Active,
  5. /// <summary> Delimited 表示在终端命令接收前从管道接收到分隔符</summary>
  6. Delimited,
  7. /// <summary> Pending 表示中断命令已经从管道接收,但是仍有待定消息可读</summary>
  8. Pending,
  9. /// <summary> Terminating 表示所有待定消息都已经读取等待管道终止确认信号返回 </summary>
  10. Terminating,
  11. /// <summary> Terminated 表示终止命令是由用户显示调用 </summary>
  12. Terminated,
  13. /// <summary> Double_terminated 表示用户调用了终止命令同时管道也调用了终止命令 </summary>
  14. DoubleTerminated
  15. }
  1. 终止当前管道

    若当前状态为TerminatedDoubleTerminatedTerminating不再处理终止命令
  1. public void Terminate(bool delay)
  2. {
  3. //判断当前状态是否可处理终止命令
  4. ...
  5. if (m_state == State.Active)
  6. {
  7. // 向另一个管道发送终止命令然后等待确认终止
  8. SendPipeTerm(m_peer);
  9. m_state = State.Terminated;
  10. }
  11. else if (m_state == State.Pending && !m_delay)
  12. {
  13. // 若有待处理数据,但是不等待直接终止,则向另一个管道发送确认终止.
  14. m_outboundPipe = null;
  15. SendPipeTermAck(m_peer);
  16. m_state = State.Terminating;
  17. }
  18. else if (m_state == State.Pending)
  19. {
  20. //若有待处理数据但是需要等到则不处理.
  21. }
  22. else if (m_state == State.Delimited)
  23. {
  24. //若已经获取到限定符但是还没有收到终止命令则忽略定界符,然后发送终止命令给另一个管道
  25. SendPipeTerm(m_peer);
  26. m_state = State.Terminated;
  27. }
  28. else
  29. {
  30. // 没有其他状态
  31. Debug.Assert(false);
  32. }
  33. //停止向外发送的消息
  34. m_outActive = false;
  35. if (m_outboundPipe != null)
  36. {
  37. //抛弃未发送出的消息.
  38. Rollback();
  39. // 这里不会再先查水位,所以即使管道满了也可再写入,向管道写入定界符 .
  40. var msg = new Msg();
  41. msg.InitDelimiter();
  42. m_outboundPipe.Write(ref msg, false);
  43. Flush();
  44. }
  45. }
  1. 终止另一个管道
  1. protected override void ProcessPipeTerm()
  2. {
  3. // 这是一个简单的例子有道管道终止
  4. //若没有更多待处理消息需要读取,或者这个管道已经丢去待处理数据,我们直接将状态设置为正在终止(terminating),否则我们搁置待处理状态直到所有待处理消息被发送
  5. if (m_state == State.Active)
  6. {
  7. if (!m_delay)
  8. {
  9. //不需要等到消息处理
  10. m_state = State.Terminating;
  11. m_outboundPipe = null;
  12. //发送终止确认
  13. SendPipeTermAck(m_peer);
  14. }
  15. else
  16. m_state = State.Pending;
  17. return;
  18. }
  19. // 若定界符碰巧在终止命令之前到达,将状态改为正在终止
  20. if (m_state == State.Delimited)
  21. {
  22. m_state = State.Terminating;
  23. m_outboundPipe = null;
  24. SendPipeTermAck(m_peer);
  25. return;
  26. }
  27. // 当管道并发关闭,则状态改为DoubleTerminated
  28. if (m_state == State.Terminated)
  29. {
  30. m_state = State.DoubleTerminated;
  31. m_outboundPipe = null;
  32. SendPipeTermAck(m_peer);
  33. return;
  34. }
  35. // pipe_term is invalid in other states.
  36. Debug.Assert(false);
  37. }
  1. 确认终止
  1. protected override void ProcessPipeTermAck()
  2. {
  3. // 通知Socket或Session中断当前管道 .
  4. Debug.Assert(m_sink != null);
  5. m_sink.Terminated(this);
  6. // 若正则处理或double_terminated这里不做任何事
  7. // 简化释放管道,在已终止状态,我们必须在释放这个管道之前确认
  8. //其他状态都是非法的
  9. if (m_state == State.Terminated)
  10. {
  11. m_outboundPipe = null;
  12. SendPipeTermAck(m_peer);
  13. }
  14. else
  15. Debug.Assert(m_state == State.Terminating || m_state == State.DoubleTerminated);
  16. // 删除所有管道中的未读消息,然后释放流入管道
  17. var msg = new Msg();
  18. while (m_inboundPipe.TryRead(out msg))
  19. {
  20. msg.Close();
  21. }
  22. m_inboundPipe = null;
  23. }

整体回收Socket流程图如下:

  1. public virtual void InEvent()
  2. {
  3. // 回收线程命令会调用此事件
  4. try
  5. {
  6. ProcessCommands(0, false);
  7. }
  8. catch
  9. {
  10. // ignored
  11. }
  12. finally
  13. {
  14. CheckDestroy();
  15. }
  16. }
  1. private void CheckDestroy()
  2. {
  3. // socket释放完则做最后的清除和释放工作.
  4. if (m_destroyed)
  5. {
  6. // 从回收线程移除轮询
  7. m_poller.RemoveHandle(m_handle);
  8. // 释放socke.
  9. DestroySocket(this);
  10. // 通知已释放.
  11. SendReaped();
  12. // Deallocate.
  13. base.ProcessDestroy();
  14. }
  15. }

总结

该篇介绍命令处理方式和回收线程回收Socket,顺便介绍了下创建SocketBase的细节性问题。以便对释放Socket有更清晰的认识。


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

作者博客:杰哥很忙

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

消息队列NetMQ 原理分析3-命令产生/处理和回收线程的更多相关文章

  1. 消息队列NetMQ 原理分析1-Context和ZObject

    前言 介绍 NetMQ是ZeroMQ的C#移植版本,它是对标准socket接口的扩展.它提供了一种异步消息队列,多消息模式,消息过滤(订阅),对多种传输协议的无缝访问. 当前有2个版本正在维护,版本3 ...

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

    消息队列NetMQ 原理分析2-IO线程和完成端口 前言 介绍 目的 IO线程 初始化IO线程 Proactor 启动Procator线程轮询 处理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. redis作为消息队列的原理

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

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

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

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

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

随机推荐

  1. vector 对象中存放指针类型数据

    <<C++ Primer>> 第四版Exercise Section 5.6 的5.1.6 有一道题是这样的:编写程序定义一个vector对象,其每个元素都是指向string类 ...

  2. java之JDK的环境变量配置

    JDK是什么? JDK是整个java开发的核心,它包含了JAVA的运行环境,JAVA工具和JAVA基础的类库. JDK包含的基本组件包括 java –--------> 运行编译后的java程序 ...

  3. 微信开发模式 api 接口文档简介

    微信公众平台分为订阅号和服务号,服务号提供9大接口,需要通过微信认证后才能使用这些接口.认证费用300元.下面是接口的大致介绍: 1. 语音识别:通过语音识别接口,用户发送的语音,将会同时给出语音识别 ...

  4. mac下安装nginx问题解决

    需要在mac上安装nginx,按照下面的博客链接一步步安装,但是碰到了些问题.下面写一下我的解决方式. (http://stevendu.iteye.com/blog/1535466) 1. 安装PC ...

  5. php表单提交--文件

    创建一个文件上传表单 允许用户从表单上传文件是非常有用的. 请看下面这个供上传文件的 HTML 表单: <!doctype html> <html> <head> ...

  6. Spring Data JPA 实例查询

    一.相关接口方法     在继承JpaRepository接口后,自动拥有了按"实例"进行查询的诸多方法.这些方法主要在两个接口中定义,一是QueryByExampleExecut ...

  7. ipconfig显示IP地址情况

    1.以太网适配器 Local Area Connection 若电脑是本地连接,则ipv4地址是本机的ip地址,默认网关一般为本机所连接路由器的地址. 2.无线局域网适配器 Wireless Netw ...

  8. 老司机实战Windows Server Docker:5 Windows Server Dockerfile葵花宝典

    前面两篇(简单运维1.简单运维2)介绍了一些Windows Server Docker相关的基本运维知识.今天这一篇,Windows Server Dockerfile葵花宝典,涵盖了许多典型场景的W ...

  9. 小练习,判断X的奇偶性

    package lianxi1; public class text { public static void main(String[] args) { ; ==) { System.out.pri ...

  10. 【转】AS3多种天气预报调用代码分享

    今天我们来介绍利用weather.com.cn上的天气预报功能,这里介绍了大家常用的,其它的大家可以自己去下载. 我们这里的天气预览不需要js来调用,只要用iframe就可以了,更不需要ASP/' t ...