消息_消息队列 - CSDN
  • redis是一个key-value存储系统。它支持存储的value类型相对更多,包括string...RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。
  • 用户能即时的收到来自系统或者其他用户发来的消息,在web界面右下角弹窗提醒,用户还能标记消息是否已阅状态。 即时通讯 概念:即时通讯(实时通信,Instant Messaging,简称IM)是一个实时通信系统,允许两人或...
    • 需求

    用户能即时的收到来自系统或者其他用户发来的消息,在web界面右下角弹窗提醒,用户还能标记消息是否已阅状态。

    • 即时通讯

    概念:即时通讯(实时通信,Instant Messaging,简称IM)是一个实时通信系统,允许两人或多人使用网络实时的传递文字消息、文件、语音与视频交流。

    即时通讯有多方面的应用:即时消息、群组聊天、游戏、系统控制、地理定位、中间件和云计算、数据同步、网络语音通话、身份服务。

    • 为什么使用即时通讯

    情景:

    “我们已经到了吗?”,车后面的小孩儿问道,“没,还没有到”,一个成年人回答到。过了一会儿,小孩儿问道:“我们现在到了吗?”。这种时间短但频率高的重复性交换是一个轮询系统的真实例子:小孩儿不知道汽车什么时候到达目的地,这样他经常向开车的成年人确认。那就是,小孩儿正在轮询信息。通常成年人回复不会花太长时间:“当我们到那儿的时候,我会告诉你”。在这个例子中,这有助于和小孩儿一起旅行的成年人保持头脑清楚;在英特网上,它具有节省带宽和服务器资源的双重目的(不会再有“它变化了吗?”,“没,还没有”的交换),并且确保订阅人接收到更新和网络传递给他们的信息是一样的,而不是只有在下次由订阅人轮询来源。这跟我们平常基于“请求响应”方式来开发网站的理念不同,它属于”发布订阅”的概念,或者说“推送”的概念。

     

    • XMPP

    可扩展通讯与表示协议(XMPP)是一项用于实时通讯的开放技术。它使用可扩展标记

    语言(xml)作为交换信息的基本格式。所谓协议,就是实现即时通讯的一组约定。

    XMPP可以提供以下核心服务:信道加密、认证、出席、联络清单、一对一消息、多方通讯消息、通知、服务发现、能力广告、结构化数据表单、工作流管理、对等网络媒体会话。

    1XMPP 的 client-server 架构

    XMPP,在本质上,是一种 XML 流技术。当你准备开始和 XMPP 服务器会话,你打开一

    个长时间在线的 TCP 连接,然后和服务器协商一个 XML 流(服务器也同样也打开一个流,

    例如在每个方向有一个流)。是免费的、开源的、基于可拓展通讯和表示协议(XMPP)、采用Java编程语言开发的实时协作服务器。 Openfire安装和使用都非常简单,并利用Web进行管理。单台服务器可支持上万并发用户。

     

    XML 流 例子:

    C: <stream:stream>

    C: <presence/>

    C: <iq type="get">

    <query xmlns="jabber:iq:roster"/>

    </iq>

    S: <iq type="result">

    <query xmlns="jabber:iq:roster">

    <item jid="suke@skh.whu.edu.cn/>

    <item jid="gmz@skh.whu.edu.cn"/>

    <item jid="beta@skh.whu.edu.cn"/>

    </query>

    </iq>

    C: <message from="suke@skh.whu.edu.cn"

    to="beta@skh.whu.edu.cn">

    <body>Off with his head!</body>

    </message>

    S: <message from="lj@skh.whu.edu.cn"

    to="cyl@skh.whu.edu.cn ">

    <body>You are all pardoned.</body></message>

    C: <presence type="unavailable"/>

    C: </stream:stream>

    • XMPP的实现

    服务端:djabberd、ejabberd、jabberd、jabberd2、Prosody、Tigase等。

    openfire

    是免费的、开源的、基于可拓展通讯和表示协议(XMPP)、采用Java编程语言开发的实时协作服务器。 Openfire安装和使用都非常简单,并利用Web进行管理。单台服务器可支持上万并发用户。

    客户端(桌面版):Coccinella、Jeti、Psi、Swift、Tkabber等(类似于rtx、qq、tim、微信

    Spark

    Spark 是一个面向商业的客户端,并和 Openfire 服务器紧密关联。除了和 Openfire 服务器 的 集 成 外 , 它 也 提 供 了 半 专 有 的 音 频 和 视 频 通 讯 能 力 。 许 可 证 : LGPL 。 网 址 :http://www.igniterealtime.org/projects/spark/。

     

    客户端(web):Jabbear、JWChat、SparkWeb等(类似于wechat网页版)

    工具库:

    Smack(java)

    Smack 是一个非常流行、全功能的库,用于编写客户端、机器人和其他的应用程序。它

    由创建了 Openfire 服务器和 Spark 客户端的开发者编写。许可证: Apache。网址:

    http://www.igniterealtime .org/projects/smack/。

     

    Strophe(JavaScript)

    Strophe 是一个具有良好文档化的库,用于开发 XMPP 客户端,包括强大的 TLS 和 SASL支持。Strophe 有两种形式:C(libstrophe)和 JavaScript(strophejs)。许可证:GPL。网址:http://code.stanziq.com/strophe/。

    • 消息中心实现

    数据库设计

    活动图

    提交信息:标题(内容)、类型、附加属性(url、数据)、业务标识、发送方信息、接受方信息等。发送方信息、接受方信息包含用户名、系统标识。

    中间件:可选。比如可以是spring cloud的Feign、spring cloud stream、rabbitmq。考虑到消息的高并发性,推荐使用消息中间件spring cloud stream、rabbitmq。

    信息处理:对提交的信息进行验证、存储等。

    发送消息:使用smack api与openfire交互,将消息发送给相应的客户端。实现已封装在simba-component-xmpp包中。

    消息中心发送逻辑关键代码:

    自定义拓展节点MessageXMPP

    <messageCenter>    --消息中心

    <infoId>5376228f-abca-4353-ae00-e4042f96903a</infoId>    --消息主体id

    <type>1</type>    --消息类型

    <url>/</url>    --附加属性

    <appSn>xfgl</appSn>    --消息来源应用标识

    </messageCenter>

    simba-component-xmpp关键代码:

    创建链接

    发送消息

    消息提醒:消息接收者的web界面右下角出现窗口提醒消息。

    Strophe.js关键代码:

    添加消息监听处理器

    connection.addHandler(function(message));

    登录认证

    connection.connect(jid, pwd, function(status));

     

    阅读消息:用户点击消息。

    更新消息状态:更新状态位。

    其他处理:可选。可根据不同的消息类型,进行不同的处理。如通知服务调用者当前消息已阅读。

     

     

     

    展开全文
  • 消息推送方案

    2018-11-09 17:20:29
    对于需要即使通知用户的业务,例如告警等,需要消息推送功能,保证通知的及时性。   应用场景需求 消息推送涉及到消息的发送和接收,即方案既要能在后端中使用,也要能在前端和移动端使用,所以需要能适配java 和...
    • 目的

    对于需要即使通知用户的业务,例如告警等,需要消息推送功能,保证通知的及时性。

     

    • 应用场景需求

    消息推送涉及到消息的发送和接收,即方案既要能在后端中使用,也要能在前端和移动端使用,所以需要能适配java 和 javaScript 语言。

     

    • 选择方案

        常规思路如下三种:

    轮询(Pull)方式:客户端定时向服务器发送询问消息,一旦服务器有变化则立即同步消息。

    推送(Push)方式:移动终端现在服务器端注册并告知关注的消息主体,服务器获得相关的消息之后,根据主体主动推送给移动终端。

    常连接方式:移动终端与服务器端保持常连接,保证消息下发的及时性。

     

    通过检索分析,选择使用push方式,并且使用MQTT协议来实现。此协议开销小,能高效地使用网络,也是物联网选择使用的一个协议。并且由于使用较多,说明资料相对来说比较丰富,便于开发使用。

    消息中转代理服务使用Apache的开源工具 ActiveMQ Artemis, 它是一个多协议消息中转服务,支持STOMP, AMQP, MQTT, Openwire, SSL, and WebSockets。

    服务端和客户端之间通过发布和订阅方式来实现消息推送,消息的中转即通过Artemis来实现。

     

     

     

     

     

     

     

    MQTT协议针对消息传送提供三种服务质量:

    “至多一次”

    消息根据底层因特网协议网络尽最大努力进行传递。 可能会丢失消息。

    “至少一次”

    保证消息抵达,但可能会出现重复。

    “刚好一次”

    确保只收到一次消息。

    因此我们可以根据具体的业务需要来设置服务质量参数QoS,同时通过主题Topic来区分不同的业务来推送给不同的用户。

     

     

    关于中转服务器的选择,分析了三种服务器,都是开源项目,分别为Apache的 Apollo 、Artemis 还有国内的EMQ, 其中Apollo 操作虽然简洁,但是已经很久没有更新维护了,而国内的EMQ 从使用来看不如Apache的便利,并且其使用Erlang语言开发,非常规开发语言,对于后期使用维护不太方便。综合来看,Artemis的功能比较完备,并且版本一直有在更新维护,使用的是Java语言开发,遇到问题可以从开源社区寻求获取解决方案。

     

    中转消息服务器Artemis信息显示页面

    连接会话:

     

    服务器使用情况:

     

     

     

     

    • 具体使用流程

     

     

     

     

     

     

     

     

    五:参考文档

     

    1、消息中转服务Artemis 说明:

    http://activemq.apache.org/artemis/

    其开源项目代码地址:

    https://github.com/apache/activemq-artemis

     

    1. 后台Java(包括App)使用库和代码参考:

    https://github.com/eclipse/paho.mqtt.java

    https://github.com/eclipse/paho.mqtt.android

     

    1. 前台JavaScript使用库和代码参考:

    https://github.com/eclipse/paho.mqtt.javascript

    注意使用对应的库,来进行对应的调用,其JS库有两个版本:

    @namespace Paho

    @namespace Paho.MQTT

     

    1. MQTT 协议说明:

    https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html

    中文翻译版本:

    https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html

     

    1. 各种代理服务

    https://github.com/mqtt/mqtt.github.io/wiki/brokers

     

    展开全文
  • 林炳文Evankaka原创作品。...  Windows是一个消息(Message)...应用程序想要实现的功能由消息来触发,并且靠对消息的响应和处理来完成。必须注意的是,消息并非是抢占性的,无论事件的缓急,总是按照到达的先后派对,
                林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka
    

         Windows是一个消息(Message)驱动系统。Windows的消息提供了应用程序之间、应用程序与Windows系统之间进行通信的手段。应用程序想要实现的功能由消息来触发,并且靠对消息的响应和处理来完成。必须注意的是,消息并非是抢占性的,无论事件的缓急,总是按照到达的先后派对,依次处理(一些系统消息除外),这样可能使一些实时外部事件得不到及时处理。

          Windows的应用程序一般包含窗口(Window),它主要为用户提供一种可视化的交互方式,窗口是总是在某个线程(Thread)内创建的。Windows系统通过消息机制来管理交互,消息(Message)被发送,保存,处理,一个线程会维护自己的一套消息队列(Message Queue),以保持线程间的独占性。队列的特点无非是先进先出,这种机制可以实现一种异步的需求响应过程。

    目录:

    1、消息

    2  、消息类型

    3 、消息队列(Message Queues)

    4 、队列消息(Queued Messages)和非队列消息(Non-Queued Messages)

    5 、PostMessage(PostThreadMessage), SendMessage

    6 、GetMessage, PeekMessage

    7 、TranslateMessage, TranslateAccelerator

    8、(消息死锁( Message Deadlocks)

    9、BroadcastSystemMessage

    10、消息的处理

    11、MFC的消息映射

    12、消息反射机制


    1、消息

        消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键

    都会使Windows发送一个消息给应用程序。

        消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来

    说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做MSG,MSG含有来自windows应用程序消息队列的消息信息,它在

    Windows中声明如下: 
    typedef struct tagMsg 

    HWND hwnd;          // 接受该消息的窗口句柄 
    UINT message;         // 消息常量标识符,也就是我们通常所说的消息号 
    WPARAM wParam;     // 32位消息的特定附加信息,确切含义依赖于消息值 
    LPARAM lParam;       // 32位消息的特定附加信息,确切含义依赖于消息值 
    DWORD time;         // 消息创建时的时间 
    POINT pt;             // 消息创建时的鼠标/光标在屏幕坐标系中的位置 
    }MSG; 
        消息可以由系统或者应用程序产生。系统在发生输入事件时产生消息。举个例子, 当用户敲键, 移动鼠标或者单击控件。系统也
    产生消息以响应由应用程序带来的变化, 比如应用程序改变系统字体,改变窗体大小。应用程序可以产生消息使窗体执行任务,或者与其他应用程序中的窗口通讯。

    2、消息类型

    1) 系统定义消息(System-Defined Messages)
            在SDK中事先定义好的消息,非用户定义的,其范围在[0x0000, 0x03ff]之间, 可以分为以下三类: 
    1> 窗口消息(Windows Message)
           与窗口的内部运作有关,如创建窗口,绘制窗口,销毁窗口等。可以是一般的窗口,也可以是Dialog,控件等。 
    如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL... 
    2> 命令消息(Command Message)
            与处理用户请求有关, 如单击菜单项或工具栏或控件时, 就会产生命令消息。 
    WM_COMMAND, LOWORD(wParam)表示菜单项,工具栏按钮或控件的ID。如果是控件, HIWORD(wParam)表示控件消息类型 
    3> 控件通知(Notify Message)
            控件通知消息, 这是最灵活的消息格式, 其Message, wParam, lParam分别为:WM_NOTIFY, 控件ID,指向NMHDR的指针。NMHDR包含控件通知的内容, 可以任意扩展。 
    2) 程序定义消息(Application-Defined Messages)
            用户自定义的消息, 对于其范围有如下规定: 
    WM_USER: 0x0400-0x7FFF      (ex. WM_USER+10) 
    WM_APP(winver> 4.0): 0x8000-0xBFFF (ex.WM_APP+4) 
    RegisterWindowMessage: 0xC000-0xFFFF

    3、消息队列(Message Queues)

     Windows中有两种类型的消息队列 
    1) 系统消息队列(System Message Queue)
            这是一个系统唯一的Queue,设备驱动(mouse, keyboard)会把操作输入转化成消息存在系统队列中,然后系统会把此消息放到目标窗口所在的线程的消息队列(thread-specific message queue)中等待处理 
    2) 线程消息队列(Thread-specific Message Queue)
            每一个GUI线程都会维护这样一个线程消息队列。(这个队列只有在线程调用GDI函数时才会创建,默认不创建)。然后线程消息队列中的消息会被送到相应的窗口过程(WndProc)处理. 
    注意: 线程消息队列中WM_PAINT,WM_TIMER只有在Queue中没有其他消息的时候才会被处理,WM_PAINT消息还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理。

    4、队列消息(Queued Messages)和非队列消息(Non-Queued Messages)

    1)队列消息(Queued Messages)
            消息会先保存在消息队列中,消息循环会从此队列中取消息并分发到各窗口处理  、如鼠标,键盘消息。 
    2) 非队列消息(NonQueued Messages)
            消息会绕过系统消息队列和线程消息队列直接发送到窗口过程被处理  如: WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR, WM_WINDOWPOSCHANGED 
    注意: postMessage发送的消息是队列消息,它会把消息Post到消息队列中; SendMessage发送的消息是非队列消息, 被直接送到窗口过程处理.

    队列消息和非队列消息的区别
            从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数时系统才给线程创建一个消息队列。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。
         对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、 WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由 Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。
         一般来讲,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外,同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新窗口的次数。

          非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。系统发送非队列消息通知窗口,系统发送消息通知窗口。例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息,例如下面我们要谈到的函数。

    5 、PostMessage(PostThreadMessage), SendMessage
            PostMessage:把消息放到指定窗口所在的线程消息队列中后立即返回。 PostThreadMessage:把消息放到指定线程的消息队列中后立即返回。 
            SendMessage:直接把消息送到窗口过程处理, 处理完了才返回。

    PostMessage(异步)和SendMessage(同步)的区别

    a、 PostMessage 是异步的,SendMessage 是同步的。

             PostMessage 只把消息放到队列,不管消息是不是被处理就返回,消息可能不被处理;

            SendMessage等待消息被处理完了才返回,如果消息不被处理,发送消息的线程将一直处于阻塞状态,等待消息的返回。

    b、 同一个线程内:

              SendMessage 发送消息时,由USER32.DLL模块调用目标窗口的消息处理程序,并将结果返回,SendMessage 在同一个线程里面发送消息不进入线程消息队列;PostMessage 发送的消息要先放到消息队列,然后通过消息循环分派到目标窗口(DispatchMessage)。

    c、不同线程:

                 SendMessage 发送消息到目标窗口的消息队列,然后发送消息的线程在USER32。DLL模块内监视和等待消息的处理结果,直到目标窗口的才处理返回,SendMessage在返回之前还需要做许多工作,如响应别的线程向它发送的SendMessage().PostMessge() 到别的线程的时候最好使用PostThreadMessage   代替。PostMessage()的HWND 参数可以为NULL,相当于PostThreadMessage() + GetCrrentThreadId.

    d、系统处理消息。

           系统只处理(marshal)系统消息(0--WM_USER),发送用户消息(用户自己定义)时需要用户自己处理。

            使用PostMessage,SendNotifyMessage,SendMessageCallback等异步函数发送系统消息时,参数不可以使用指针,因为发送者不等待消息的处理就返回,接收者还没有处理,指针就有可能被释放了,或则内容变化了。

    e、在Windows 2000/XP,每个消息队列最多只能存放一定数量的消息,超过的将不会被处理就丢掉。系统默认是10000;:[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows] USERPostMessageLimit

    6 、GetMessage, PeekMessage
    PeekMessage会立即返回    可以保留消息 
    GetMessage在有消息时返回  会删除消息

    PeekMessage和GetMessage函数的主要区别有:
            a. GetMessage的主要功能是从消息队列中“取出”消息,消息被取出以后,就从消息队列中将其删除;而PeekMessage的主要功能是“窥视”消息,如果有消息,就返回true,否则返回false。也可以使用PeekMessage从消息队列中取出消息,这要用到它的一个参数(UINT wRemoveMsg),如果设置为PM_REMOVE,消息则被取出并从消息队列中删除;如果设置为PM_NOREMOVE,消息就不会从消息队列中取出。
            b. 如果GetMessage从消息队列中取不到消息,则线程就会被操作系统挂起,等到OS重新调度该线程时,两者的性质不同:使用GetMessage线程仍会被挂起,使用PeekMessage线程会得到CPU的控制权,运行一段时间。
            c、GetMessage每次都会等待消息,直到取到消息才返回;而PeekMessage只是查询消息队列,没有消息就立即返回,从返回值判断是否取到了消息。
    我们也可以说,PeekMessage是一个具有线程异步行为的函数,不管消息队列中是否有消息,函数都会立即返回。而GetMessage则是一个具有线程同步行为的函数,如果消息队列中没有消息的话,函数就会一直等待,直到消息队列中至少有一条消息时才返回。
    如果消息队列中没有消息,PeekMessage总是能返回,这就相当于在执行一个循环,如果消息队列一直为空, 它就进入了一个死循环。GetMessage则不可能因为消息队列为空而进入死循环。

    联系:

            在Windows的内部,GetMessage和PeekMessage执行着相同的代码,Peekmessage和Getmessage都是向系统的消息队列中取得消息,并将其放置在指定的结构。

    区别:

    PeekMessage:有消息时返回TRUE,没有消息返回FALSE 

    GetMessage:有消息时且消息不为WM_QUIT时返回TRUE,如果有消息且为WM_QUIT则返回FALSE,没有消息时不返回。

    GetMessage:取得消息后,删除除WM_PAINT消息以外的消息。

    PeekMessage:取得消息后,根据wRemoveMsg参数判断是否删除消息。PM_REMOVE则删除,PM_NOREMOVE不删除。

    The PeekMessage function normally does not remove WM_PAINT messages from the queue. WM_PAINT messages remain in the queue until they are processed. However, if a WM_PAINT message has a null update region, PeekMessage does remove it from the queue.

    不能用PeekMessage从消息队列中删除WM_PAINT消息,从队列中删除WM_PAINT消息可以令窗口显示区域的失效区域变得有效(刷新窗口),如果队列中包含WM_PAINT消息程序就会一直while循环了。

    7 、TranslateMessage, TranslateAccelerator
             TranslateMessage: 把一个virtual-key消息转化成字符消息(character message),并放到当前线程的消息队列中,消息循环下一次取出处理。 
            TranslateAccelerator: 将快捷键对应到相应的菜单命令。它会把WM_KEYDOWN 或 WM_SYSKEYDOWN转化成快捷键表中相应的WM_COMMAND 或WM_SYSCOMMAND消息, 然后把转化后的 WM_COMMAND或WM_SYSCOMMAND直接发送到窗口过程处理, 处理完后才会返回。

    8、(消息死锁( Message Deadlocks)
    假设有线程A和B, 现在有以下下步骤 
            1) 线程A SendMessage给线程B, A等待消息在线程B中处理后返回 
            2) 线程B收到了线程A发来的消息,并进行处理, 在处理过程中,B也向线程A SendMessgae,然后等待从A返回。  因为此时, 线程A正等待从线程B返回, 无法处理B发来的消息, 从而导致了线程A,B相互等待, 形成死锁。多个线程也可以形成环形死锁。 
    可以使用 SendNotifyMessage或SendMessageTimeout来避免出现死锁。

    9、BroadcastSystemMessage
            我们一般所接触到的消息都是发送给窗口的, 其实, 消息的接收者可以是多种多样的,它可以是应用程序(applications), 可安装驱动(installable drivers), 网络设备(network drivers), 系统级设备驱动(system-level device drivers)等, 
    BroadcastSystemMessage这个API可以对以上系统组件发送消息。

    10、消息的处理
            接下来我们谈一下消息的处理,首先我们来看一下VC中的消息泵:

    while(GetMessage(&msg, NULL, 0, 0))
    {
           if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg))

                TranslateMessage(&msg);
                DispatchMessage(&msg);
           }
    }

    TranslateMessage(转换消息):

            用来把虚拟键消息转换为字符消息。由于Windows对所有键盘编码都是采用虚拟键的定义,这样当按键按下时,并不得字符消息,需要键盘映射转换为字符的消息。

    TranslateMessage函数

            用于将虚拟键消息转换为字符消息。字符消息被投递到调用线程的消息队列中,当下一次调用GetMessage函数时被取出。当我们敲击键盘上的某个字符键时,系统将产生WM_KEYDOWN和WM_KEYUP消息。这两个消息的附加参数(wParam和lParam)包含的是虚拟键代码和扫描码等信息,而我们在程序中往往需要得到某个字符的ASCII码,TranslateMessage这个函数就可以将WM_KEYDOWN和WM_ KEYUP消息的组合转换为一条WM_CHAR消息(该消息的wParam附加参数包含了字符的ASCII码),并将转换后的新消息投递到调用线程的消息队列中。注意,TranslateMessage函数并不会修改原有的消息,它只是产生新的消息并投递到消息队列中。

    也就是说TranslateMessage会发现消息里是否有字符键的消息,如果有字符键的消息,就会产生WM_CHAR消息,如果没有就会产生什么消息。

    DispatchMessage(分派消息):

    把 TranslateMessage转换的消息发送到窗口的消息处理函数,此函数在窗口注册时已经指定。

            首先,GetMessage从进程的主线程的消息队列中获取一个消息并将它复制到MSG结构,如果队列中没有消息,则GetMessage函数将等待一个消息的到来以后才返回。如果你将一个窗口句柄作为第二个参数传入GetMessage,那么只有指定窗口的的消息可以从队列中获得。GetMessage也可以从消息队列中过滤消息只接受消息队列中落在范围内的消息。这时候就要利用GetMessage/PeekMessage指定一个消息过滤器。这个过滤器是一个消息标识符的范围或者是一个窗体句柄,或者两者同时指定。当应用程序要查找一个后入消息队列的消息是很有用。WM_KEYFIRST 和 WM_KEYLAST 常量用于接受所有的键盘消息。 WM_MOUSEFIRST 和 WM_MOUSELAST 常量用于接受所有的鼠标消息。
    然后TranslateAccelerator判断该消息是不是一个按键消息并且是一个加速键消息,如果是,则该函数将把几个按键消息转换成一个加速键消息传递给窗口的回调函数。处理了加速键之后,函数TranslateMessage将把两个按键消息WM_KEYDOWN和WM_KEYUP转换成一个 WM_CHAR,不过需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然将传递给窗口的回调函数。    
    处理完之后,DispatchMessage函数将把此消息发送给该消息指定的窗口中已设定的回调函数。如果消息是WM_QUIT,则 GetMessage返回0,从而退出循环体。应用程序可以使用PostQuitMessage来结束自己的消息循环。通常在主窗口的 WM_DESTROY消息中调用。

    11、MFC的消息映射
             使用MFC编程时,消息发送和处理的本质和Win32相同,但是,它对消息处理进行了封装,简化了程序员编程时消息处理的复杂性,它通过消息映射机制来处理消息,程序员不必去设计和实现自己的窗口过程。
    说白了,MFC中的消息映射机制实质是一张巨大的消息及其处理函数对应表。消息映射基本上分为两大部分:
    在头文件(.h)中有一个宏DECLARE_MESSAGE_MAP(),它放在类的末尾,是一个public属性的;与之对应的是在实现部分(.cpp)增加了一个消息映射表,内容如下:
    BEGIN_MASSAGE_MAP(当前类,当前类的基类)
    //{{AFX_MSG_MAP(CMainFrame)
    消息的入口项
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    但是仅是这两项还不足以完成一条消息,要是一个消息工作,必须还有以下3个部分去协作:
    1、在类的定义中加入相应的函数声明;
    2、在类的消息映射表中加入相应的消息映射入口项;
    3、在类的实现中加入相应的函数体;
    消息的添加
    (1)、利用Class Wizard实现自动添加
            在菜单中选择View -&gt; Class Wizard激活Class Wizard,选择Message Map标签,从Class name组合框中选取我们想要添加消息的类。在Object IDs列表框中,选取类的名称。此时,Messages列表框显示该类的可重载成员函数和窗口消息。可重载成员函数显示在列表的上部,以实际虚构成员函数的大小写字母来表示。其他为窗口消息,以大写字母出现。选中我们要添加的消息,单击Add Funtion按钮,Class Wizard自动将该消息添加进来。
    有时候,我们想要添加的消息在Message列表中找不到,我们可以利用Class Wizard上Class Info标签以扩展消息列表。在该页中,找到Message Filter组合框,通过它可以改变首页中Messages列表框中的选项。
    (2)、手动添加消息
     如果Messages列表框中确实没有我们想要的消息,就需要我们手工添加:
            1)在类的.h文件中添加处理函数的声明,紧接着在//}}AFX_MSG行之后加入声明,注意,一定要以afx_msg开头。
    通常,添加处理函数声明的最好的地方是源代码中Class Wizard维护的表的下面,在它标记其领域的{{ }}括弧外面。这些括弧中的任何东西都有可能会被Class Wizard销毁。
            2)接着,在用户类的.cpp文件中找到//}}AFX_MSG_MAP行,紧接在它之后加入消息入口项。同样,也放在{{ }}外面。
            3)最后,在该文件中添加消息处理函数的实体。
    对于能够使用Class Wizard添加的消息,尽量使用Class Wizard添加,以减少我们的工作量;对于不能使用Class Wizard添加的消息和自定义消息,需要手动添加。总体说来,MFC的消息编程对用户来说,相对比较简单,在此不再使用实例演示。
            12、消息反射机制
    什么叫消息反射?
             父窗口将控件发给它的通知消息,反射回控件进行处理(即让控件处理这个消息),这种通知消息让控件自己处理的机制叫做消息反射机制。
            通过前面的学习我们知道,一般情况下,控件向父窗口发送通知消息,由父窗口处理这些通知消息。这样,父窗口(通常是一个对话框)会对这些消息进行处理,换句话说,控件的这些消息处理必须在父窗口类体内,每当我们添加子控件的时候,就要在父窗口类中复制这些代码。很明显,这对代码的维护和移植带来了不便,而且,明显背离C++的对象编程原则。
            从4.0版开始,MFC提供了一种消息反射机制(Message Reflection),可以把控件通知消息反射回控件。具体地讲,对于反射消息,如果控件有该消息的处理函数,那么就由控件自己处理该消息,如果控件不处理该消息,则框架会把该消息继续送给父窗口,这样父窗口继续处理该消息。可见,新的消息反射机制并不破坏原来的通知消息处理机制。
    消息反射机制为控件提供了处理通知消息的机会,这是很有用的。如果按传统的方法,由父窗口来处理这个消息,则加重了控件对象对父窗口的依赖程度,这显然违背了面向对象的原则。若由控件自己处理消息,则使得控件对象具有更大的独立性,大大方便了代码的维护和移植。
            实例M8:简单地演示MFC的消息反射机制。(见附带源码 工程M8)
         开VC++ 6.0,新建一个基于对话框的工程M8。
    在该工程中,新建一个CMyEdit类,基类是CEdit。接着,在该类中添加三个变量,如下:
    private:
    CBrush m_brBkgnd;
    COLORREF m_clrBkgnd;
    COLORREF m_clrText;
    在CMyEdit::CMyEdit()中,给这三个变量赋初值:
    {
    m_clrBkgnd = RGB( 255, 255, 0 );
    m_clrText = RGB( 0, 0, 0 );
    m_brBkgnd.CreateSolidBrush(RGB( 150, 150, 150) );
    }
    打开ClassWizard,类名为CMyEdit,Messages处选中“=WM_CTLCOLOR”,您是否发现,WM_CTLCOLOR消息前面有一个等号,它表示该消息是反射消息,也就是说,前面有等号的消息是可以反射的消息。
    消息反射函数代码如下:
    HBRUSH CMyEdit::CtlColor(CDC* pDC, UINT nCtlColor)
    {
        // TODO: Change any attributes of the DC here
        pDC-&gt;SetTextColor( m_clrText );//设置文本颜色
        pDC-&gt;SetBkColor( m_clrBkgnd );//设置背景颜色
         //请注意,在我们改写该函数的内容前,函数返回NULL,即return NULL;
        //函数返回NULL将会执行父窗口的CtlColor函数,而不执行控件的CtlColor函数
        //所以,我们让函数返回背景刷,而不返回NULL,目的就是为了实现消息反射
        return m_brBkgnd; //返回背景刷
    }
    在IDD_M8_DIALOG对话框中添加一个Edit控件,使用ClassWizard给该Edit控件添加一个CMyEdit类型的变量m_edit1,把Edit控件和CMyEdit关联起来。

    林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka

    展开全文
  • 消息总线VS消息队列

    2015-02-18 17:31:33
    消息队列跟消息总线进行了对比,并说明了对于企业应用,封装消息总线的必要性。

    前段时间实现了一个基于RabbitMQ的消息总线,实现的过程中自己也在不断得思考、总结以及修正。需要考虑各个维度:效率、性能、网络、吞吐量、甚至需要自己去设想API可能的使用场景、模式。不过能有一件事情,自己愿意去做,在走路、吃饭、坐公交的时候都在思考如何去改进它,然后在实践的过程中,促使去思考并挖掘自己知识面的空白,也是一件让人开心的事情。

    借此记录下自己在实现的过程中,以及平时的一些想法。

    这是第一篇,先谈谈消息总线跟消息队列的区别,以及对于企业级应用需要将消息队列封装成消息总线的必要性。

    消息总线跟消息队列有何区别?如果有人问你这个问题,你的答案是什么?如果你的消息总线是基于一个已经相当成熟的消息队列或者消息系统做二次封装。为什么需要用你的客户端,而不直接用原始的(这是一个大家都相信权威的时代,请注意这里用的是相信,而不是迷信,你确实应该相信权威,至少比相信一个新手来得靠谱,当然我这里指的权威,是正面的意思)?

    那么我从以下几点来谈谈我对这个问题的思考:

    • 消息队列clientAPI权限太大,clientAPI信任级别太高
    • 消息队列clientAPI面向技术,消息总线clientAPI面向技术+业务
    • 消息队列无法隐藏通信细节
    • 消息队列无法实施实时管控
    • 总线的优势:统一入口,简化拦截成本
    这里为了理解简单,你就暂且先把RabbitMQ当做是个消息队列,其实它不只是个消息队列,其他的一些基于JMS的消息队列对于回答这个问题而言,也能成立。

    (1)消息队列clientAPI权限太大,信任级别太高
    这一点不仅仅是哪一个服务端组件的客户端driver的实现是这样,绝大部分其实都是这样的,它们的client其实是对服务端组件(或者称之为服务器)协议的翻译。这些服务器大都带有commandline interface(这几乎是标配)。其实,CLI跟在程序中使用的各种语言的client库没有区别本质区别,它们相对于server而言都是client——都是对server实现的protocol的翻译或者转换,而这些API都是对这些包装过的协议的调用。因此它们都存在一些“management”形式的接口:比如create,delete,remove某个component之类的。没错,你去看所有带client的组件的实现,它们都包含了这些API(这不是对错的问题,这些client本身就没有也不应该假设你的使用场景)。比如你看看redis的client:jedis——它甚至具备了flushAll,flushDB的功能(清空所有redis数据),除了能关闭server还有什么事它不能做?而就RabbitMQ而言,它的officialnative java client,可以创建/删除其通信的核心组件:exchange,queue。你能直接将这些client散布到各个业务系统里去而不加阻拦?你当然有必要做二次封装以移除这些高危的managementAPI。

    (2)消息队列clientAPI面向技术而消息总线clientAPI面向技术+业务
    消息队列的clientAPI大都面向协议、通信实现,面向可用性以及高性能,如果归类一下那就是面向技术,除了通信场景它不会去模拟业务场景。而消息总线需要带着业务场景去实现需要支持的机制。
    当你去搜索任何一个消息队列的时候,它的advantage里都有一条:生产者与消费者解耦,就像下面这样:

    就生产者跟消费者模型而言,这确实是消息队列的优势。不过这种优势也被限制在一些特定的使用场景下,比如:单一业务的消息排队处理。因此通用消息队列的场景更适用于单一职责的生产者跟消费者模型;而我们期待的消息总线却是企业里各个系统中消息的通信,侧重点在于通信上。消息队列只是提供了一种非常适合于消息通信的实现机制(消息有序,消息缓存等),因此消息总线是在消息队列提供的技术支撑上封装出适合消息交互的业务场景。

    (3)消息队列无法隐藏通信细节
    对于企业内的系统交互,我们希望它尽可能保证数据的安全性。而数据通常都暂存在队列中,因此保证数据的安全性就顺其自然得转变成保证消息队列访问的安全性:你总是不应该让没有经过授权的客户端去访问本不应该访问到的队列。可惜的是RabbitMQ官方的客户端达不到这种要求,它要访问一个队列,需要知道真实队列的名称,需要知道其路由路径。而就连接一个队列而言,我们认为它提供了太多的信息,但这是没办法的事情,因为它的exchange以及queue的混搭机制非常灵活,所以你得提供一个称之为routingkey的路由路径。而不管怎样,如果你把这个信息开放给调用端去填写,几乎肯定会暴露你服务端exchange以及queue的路由机制以及拓扑结构。因此我们需要做什么?我们需要找到一种通信机制,让它对外只需要知道有个proxy节点,而不需要去关注真实的queue的名称;然后想一个办法把其routingkey隐藏在消息总线内部。

    (4)消息队列无法实施实时管控
    如果你在企业内各个系统之间引入消息总线,很显然访问控制是必须提供的。比如对某个队列实施消息大小限制,激活/禁用某个队列等。
    之前我们提到过消息队列不是面向业务的,它自身没有过多得考虑数据的安全性以及对访问的安全控制机制。而且我们也几乎很难去改造一个消息队列的服务端实现,除非它是基于拦截器/插件模式的。即便RabbitMQ是支持插件的,但对于erlang这样一个受众不是特别广泛的语言,你去给它写插件一不小心就会走到坑里去,并且RabbitMQ官方也已经申明了它们十分不建议你自己去编写插件。考虑到诸多不便,我们只能在客户端上做文章。毫无疑问,我们的实时管控信息还是必须存储在服务端(只是它是一个独立的服务端),但原生的client很显然是不支持这种机制的,因此我们需要在原生client外部封装订阅实时管控信息以及实施访问控制的逻辑代码。

    (5)总线的优势:统一入口,简化拦截成本
    无论是消息总线还是服务总线,其实所谓的总线就是进行先收拢再发散的过程。先收拢,从统一的入口进去,完成必要的统一处理逻辑;再发散,按照路由规则,路由到各个组件去处理。事实上这就是代理的作用:屏蔽内部细节,对外统一入口。在基于代理的基础上,我们可以对消息总线上所有的消息做日志记录(因为所有消息的通信都必须经过代理),并且还是在不切断RabbitMQ自身Channel的基础上,而如果想在路由上实现一个Proxy,那基本上离不开一个树形拓扑结构。

    写在最后

    这篇主要谈了消息总线跟消息队列的区别。其实市面上已经有一些成熟的消息队列可以开箱即用,如果你针对消息队列来封装出一个消息总线,总有人会认为是否有这个必要性。如果没有这些开源的消息队列,那么完全有你自己来实现消息总线的话,你还是需要实现出一个跟市面上类似的MQ或者MessageBroker(见POSA卷4),因此消息队列只是实现消息总线的基础,或者是它的消息通信方式;而选择基于一些成熟的MessageBroker来进行开发,既能省去很多的工作量,又能享有它们提供的稳定性以及社区的贡献。

    如果你现在就希望看到它的实现机制,可以移步到github

    展开全文
  • 即每发送一个消息,同步落盘后才返回生产者消息发送成功,即生产者收到确认发送消息成功,才继续之后操作,这样只要生产者得到了消息发送生成的返回,事后除了硬盘损坏,都可以保证不会消息丢失。 2、同步落盘怎么...

    1、消息丢失的问题

    1. 当你系统需要保证百分百消息不丢失,你可以使用生产者每发送一个消息,Broker 同步返回一个消息发送成功的反馈消息。
    2. 即每发送一个消息,同步落盘后才返回生产者消息发送成功,即生产者收到确认发送消息成功,才继续之后操作,这样只要生产者得到了消息发送生成的返回,事后除了硬盘损坏,都可以保证不会消息丢失。

    2、同步落盘怎么才能快

    1. 使用 FileChannel + DirectBuffer 池,使用堆外内存,加快内存拷贝
    2. 使用数据和索引分离,当消息需要写入时,使用 commitlog 文件顺序写,当需要定位某个消息时,查询index 文件来定位,从而减少文件IO随机读写的性能损耗

    3、消息堆积的问题

    1. 后台定时任务每隔72小时,删除旧的没有使用过的消息信息
    2. 根据不同的业务实现不同的丢弃任务,选择不同的策略淘汰任务,例如FIFO/LRU等
    3. 消息定时转移,或者对某些重要的 TAG 型(支付型)消息真正落库

    4、定时消息的实现

    实现定时消息的原理是:创建特定时间精度的 MessageQueue,例如生产者需要定时1s之后被消费者消费,你只需要将此消息发送到特定的 Topic,例如:MessageQueue-1 表示这个 MessageQueue 里面的消息都会延迟一秒被消费,然后 Broker 会在 1s 后发送到消费者消费此消息,使用 newSingleThreadScheduledExecutor 实现。

    5、顺序消息的实现

    1. 与定时消息同原理,生产者生产消息时指定特定的 MessageQueue ,消费者消费消息时,消费特定的 MessageQueue,其实单机版的消息中心在一个 MessageQueue 就天然支持了顺序消息
    2. 注意:同一个 MessageQueue 保证里面的消息是顺序消费的前提是:消费者是串行的消费该 MessageQueue,因为就算 MessageQueue 是顺序的,但是当并行消费时,还是会有顺序问题,但是串行消费也同时引入了两个问题:

    引入锁来实现串行
    前一个消费阻塞时后面都会被阻塞

    6、消息重复发送的避免

    • 因为在网络延迟的情况下,消息重复发送的问题不可避免的发生,如果非要实现消息不可重复发送,那基本太难,因为网络环境无法预知,还会使程序复杂度加大,因此默认允许消息重复发送。
    • 最简单的解决方案是每条消费记录有个消费状态字段,根据这个消费状态字段来是否消费或者使用一个集中式的表,来存储所有消息的消费状态,从而避免重复消费
    • 采用消息幂等消费,可以查询关于消息幂等消费的解决方案。

    7、广播消费与集群消费

    • 广播消费,订阅该 Topic 的消息者们都会消费每个消息
    • 集群消费,订阅该 Topic 的消息者们只会有一个去消费某个消息

    广播消费与集群消费在消息落盘区别:具体表现在消息消费进度的保存上。

    • 广播消费,由于每个消费者都独立的去消费每个消息,因此每个消费者各自保存自己的消息消费进度。
    • 集群消费下,订阅了某个 Topic,而旗下又有多个 MessageQueue,每个消费者都可能会去消费不同的 MessageQueue,因此总体的消费进度保存在 Broker 上集中的管理
    展开全文
  • 一、消息中间件相关知识 1、概述 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。当今市面上有很多主流的消息...
  • 如《消息总线消息必达》所述,MQ消息必达,架构上有两个核心设计点: (1)消息落地 (2)消息超时、重传、确认   再次回顾消息总线核心架构,它由发送端、服务端、固化存储、接收端四大部分组成。   为...
  • 消息队列( message queuing )使用消息将应用程序连接起来。这些消息通过像RabbitMQ 这样的消息代理服务器在应用程序之间路由。这就像是在应用程序之间放置一个邮局。 RabbitMQ是一个消息代理和队列服务器。 AMQP...
  • 操作系统----消息传递

    2016-09-28 20:37:15
    消息传递是以消息为单位进行进程之间一种通信的手段,消息是由一组消息组成的集合,包括了消息头和消息尾,消息的传递实现需要两个原语分别是Send()和Seceive(),Send()负责发送消息,Receive()是负责接受消息的,当...
  • 之前有篇文件介绍了生产消费者模式(http://blog.csdn.net/canot/article/details/51541920),当时是通过BlockingQueue阻塞队列来实现,以及在Redis中使用...而实际项目中往往是通过JMS使用消息队列来实现这两种模式的。J
  • 本文大概围绕如下几点进行阐述:为什么使用消息队列?使用消息队列有什么缺点?消息队列如何选型?如何保证消息队列是高可用的?如何保证消息不被重复消费?如何保证消费的可靠性传输?如何保证消息的顺序性?我们围绕...
  • 在大型平台的分布式项目中,消息队列MQ具有重要的作用,经常用在边缘业务功能的处理中,比如日志管理【下面将以Bug日志保存为例】,因为像日志保存、新用户注册发送邮件等操作都不是主干业务,可以放在消息队列异步...
  • 要说微信最让人恶心的发明,消息撤回绝对能上榜。 比如你现在正和女朋友用微信聊着天,或者跟自己喜欢的女孩子聊着天,一个不留神,你没注意到对方发的消息就被她及时撤回了,这时你很好奇,好奇她到底发了什么?...
  • 周末测试了一下RabbitMQ的性能,RabbitMQ是使用Erlang编写的一个开源的消息队列,本身支持很多的协议:AMQP,XMPP, SMTP, STOMP,也正是如此,使的它变的非常重量级,更适合于企业级的开发。个人认为,在互联网开发...
  • Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序...
  • 最近几天发现手机QQ很安静,一个消息都没有,这不正常,万一有什么紧急事情呢(比如:佳人有约、基友搞基哈哈哈),收不到消息,就不好了。 尝试用别的QQ发一条消息试试,还真是接收不到,只有打开QQ才能查看消息,...
  • 已经有几位读者抱怨“柳峰只用到文本消息作为示例,从来不提图文消息,都不知道图文消息该如何使用”,好吧,我错了,原本以为把基础API封装完、框架搭建好,再给出一个文本消息的使用示例,大家就能够照猫画虎的,...
  • posix消息队列与system v消息队列的差别: (1)对posix消息队列的读总是返回最高优先级的最早消息,对system v消息队列的读则可以返回任意指定优先级的消息。 (2)当往一个空队列放置一个消息时,posix消息队列...
  • 1、消息队列(以下简称MQ)天生就是处理高并发的有力工具,因为他可以把一个完整的流程拆为多部分,并发进行,或者不是很重要的步骤模块延迟进行。大家所熟悉的是消息队列在大基数用户项目的注册模块和电商项目的...
1 2 3 4 5 ... 20
收藏数 2,007,023
精华内容 802,809
关键字:

消息