还在自己搭建RabbitMQ Server或 ActiveMQ Server吗?你已经Out啦

本文介绍PubNub平台如何实现安全高效的消息传输,包括快速的全球数据同步、历史消息回放、多层安全保障措施及在阅后即焚功能中的应用。

         笔者之前曾经对当下的Message Queue解决方案做过一次详细分析,从OpenStack中用到的RabbitMQ,到Java平台的ActviceMQ,还有Apache Qpid 以及性能奇葩的ZeroMQ,最后还有不算MQ 而应该归为协议的MQTT。在那篇"Message Queue对比"中,我们可以发现消息队列各有各的使用场合,即使是大数据中常用的Kafak 以及Amazon SQS消息云服务,也各有自己的弊端,详细的情况这里不细说了,在图1有个大致的对比,有兴趣的还可以看看Message Queue一文。今天我们要介绍的是一家MQ服务平台PubNub(http://www.pubnub.com)


图1

为何要对这家公司的产品感兴趣了,因为感觉它将Zeromq的快速和RabbitMQ的持久化和高容错性做到了极致。以致我们可以从该官方网站上看到Amazon自己都将PubNub作为消息服务平台来使用,而不是使用自己的SQS

当然,我关注的主要因素还在于PubNub对IoT业务的支持。我们知道在IoT生态链条中,数据是价值的来源,那这些数据如何传到后端的云上,以及如何在分布式的云环境下做到信息的共享,都离不了类似PubNub这种提供消息传送的服务。PubNub在全球有14个数据中心,在世界范围内的同步可以做到延时控制在1/4秒内,支持每秒300万条消息。

尤其让我眼前一亮的是支持一段时间内历史信息的保存和回放,这样利于device设备失联后再次上线不会丢失信息,有点像手机短信。还有安全方面做的也很到位,从网络传输层的TLS 到消息内容的AES加密 再到PAM访问控制管理,三个层次对传送的信息进行保护。说了这么多,就让我们来看看PubNub的一个应用吧


关心科技发展的同学可能听到过Snapchat的名头,公司的中文名叫阅后即焚,这个可能很多人都知道。它的市值14年超过了100亿美元,就是允许用户拍照、录制视频、添加文字和图画,发送给自己在该应用上的好友。该文件在打开后,只有10秒钟寿命,随后便自动删除。这种功能借助PubNub很容易做到。

我们看PubNub上的一个开源Demo "Babel",它提供了聊天消息的5分钟自动销毁功能,当然这个时间你可以自己改,而且提供了指定用户间的通信,只有拥有公钥的对象才能看到加密的消息。Demo界面如下图


分5个步骤

1) 创建PubNub account,会得到一组跟account绑定的publish_keysubscribe_key,这个是发送和接受消息时需要用到的,比如一个room chat,大家都用同一个频道或帐号进行交流,publish_keysubscribe_key就相当于这个频道帐号

<script src="http://cdn.pubnub.com/pubnub.min.js"></script> 

var pubnub = PUBNUB.init({
            publish_key: 'demo',
            subscribe_key: 'demo',
            uuid: username,
            ssl: username
        });

2)  交换公钥Public key

    利用PubNub的Presence方法进行用户间派发公钥

    因为PubNub Presence允许用户查看谁在线和谁离线并保持实时更新用户的登录和退出情况。这样一个用户一旦订阅(Subscribe)到一条信道(channel),其他用户将能够通过

Presence看到它在PubNub上的状态,具体实现是用pubnub.here_now(),可以得到所有订阅某个channel的用户状态。这样用户订阅时,将username和公钥配对作为状态信息

发布。随后当需要用时就能通过pubnub.here_now()查到公钥,如下图 我们创建一个帐号wallace,公钥 123456abc,注意demo没有加密,实际中我们可以基于

Base64
Base64
Base64
Base64对公钥进行加密



3) 发送基于2048-bit TLS 的加密信息

用到一个

Cryptico library
Cryptico 加密库 <script src="./cryptico.js"></script>

sendMessage
主要是SendMessage()   发信息

OnMessage() 处理接受信息

1024-bit RSA public keys

    var RSAkey = cryptico.generateRSAKey(1024);
    var publicKey = cryptico.publicKeyString(RSAkey);

    pubnub.subscribe({
            channel: 'encrypted_messaging',
            callback: onMessage,
            presence: presenceHandler,
            state: {
                username: 'wallace',
                publicKey: publicKey
            }
        });


     var sendMessage = function (recipient, message) {

          if (recipient in users) {

                    var recipient_key = users[recipient];

                    message = cryptico.encrypt(message, recipient_key);   

                    pubnub.publish({ 

                             channel: 'encrypted_messaging', 

                             message: { 

                                     encryption: true,

                                     recipient: recipient, 

                                     sender: username, 

                                     message: message 

                             } 

                     });

         }else  if (recipient === "Don't encrypt messages.") {

                   pubnub.publish({ 

                             channel: 'encrypted_messaging', 

                             message: { 

                                     encryption: false,

                                     recipient: "<strong>EVERYONE</strong>",

                                     sender: username, 

                                     message: message 

                             } 

                     });

    }; 

   var onMessage = function (msg) {

             if (msg.recipient === 'wallace') { 

                  var text = msg.message;
                  if (msg.encryption) {

                           text = text.cipher;

                           var plaintext = cryptico.decrypt(text, RSAkey); 

                           console.log(msg.sender + ' sent us this encrypted message!'); 

                           console.log(plaintext);

                           if (text.status === "failure") {
                                   text = "Could not decrypt";
                           } else {
                                   text = text.plaintext;
                           }

                 }else{

                       console.log(msg.sender + ' sent us this plain message!'); 

                       console.log(text);

                }

         }

   } 



4)消息的自我销毁

通过设置

setInterval
SetInterval 和clearInterval 来在OnMessage()中检查时间是否到期,到期delete消息

var onMessage = function (m) {
            mID++;
            var msgID = mID;
            $("#messages").prepend("<tr id='" + msgID + "'><td>" + m.sender + "</td><td>" + m.recipient + "</td><td>" + m.message + "</td><td id='" + msgID + "ttl'>" + m.ttl + "</tr>");
            var updateTTL = setInterval(function () {
                var ttl = $("#" + msgID + "ttl").text();
                if (parseInt(ttl) <= 0) {
                    explode($("#" + msgID));
                    $("#" + msgID).remove();
                    clearInterval(updateTTL);
                } else {
                    $("#" + msgID + "ttl").text(ttl - 1);
                }
            }, 1000);
        };



5. Demo UI  设计

用JavaScript 和CSS


Insteon已经使用PubNub来实时控制智能家居,用于Insteon Web跟Hub和手机app之间消息的通信。用户可以通过简单的按键操作来控制所有跟INSTEON技术兼容的设备。pubnub增加了客户配置的便利性,允许用户将自己的产品与防火墙和防病毒解决方案无缝地结合在一起工作。


我个人认为,如果融合前期的The Thing System平台和PubNub平台,可以创造出更好地基于Web的IoT解决方案,这也是我后面研究的方向。


个人介绍:

Samsung Electronics VD IoT Platform高级工程师,在物联网,云计算,移动通讯,网络安全和消息传递基础构架领域拥有超过9年的专业知识和经验。拥有丰富的企业消息传递基础构架开发,物联网、云、设备联接解决方案开发,以及成熟的管理软件和解决方案,如敏捷和DevOpts的开发经验。在国内首次提出SDT(softwaredefined Things)的IOT概念,拥有3项专利(一份正在评审)。

是三星、英特尔、戴尔所成立智能家居设备标准联盟开放互联联盟(OIC)的代码贡献者(https://gerrit.iotivity.org/gerrit/p/iotivity.git)

https://www.iotivity.org/documentation/iotivity-services/protocol-plug-manager

经常参与云计算,物联网等开源社区(Vmware,ECUG失效云计算用户组)交流讨论,关注开源社区技术动态



<think>我们根据用户的问题,需要回答以下几个方面的内容: 1. ActiveMQ高可用集群搭建指南 2. ActiveMQ vs RabbitMQ性能对比 3. Spring Boot集成ActiveMQ方法 4. ActiveMQ KahaDB持久化机制 5. 死信队列(DLQ)配置使用 同时,我们还需要参考站内引用中的内容,特别是引用[1]和引用[2]中提到的持久化、集群、死信队列等内容。 下面我们逐一展开:</think>### ActiveMQ 高可用集群搭建指南 ActiveMQ 支持 **Master-Slave** 和 **Broker Network** 两种高可用集群模式,核心通过 ZooKeeper 实现节点协调[^1]: #### 1. **Master-Slave 模式**(故障转移) ```mermaid graph TB ZooKeeper[ZooKeeper集群] -->|选举| Master[Active Master] ZooKeeper -->|监控| Slave1[Standby Slave1] ZooKeeper -->|监控| Slave2[Standby Slave2] Master -->|消息同步| SharedStore[共享存储: KahaDB/NFS] ``` **配置步骤**: 1. **共享存储配置**(所有节点共用存储) ```xml <!-- activemq.xml --> <persistenceAdapter> <kahaDB directory="/shared-storage/kahadb"/> <!-- 使用NFS挂载 --> </persistenceAdapter> ``` 2. **启用 ZooKeeper 选举** ```xml <broker xmlns="http://activemq.apache.org/schema/core" brokerName="ClusterNode" useJmx="true"> <persistenceAdapter> <replicatedLevelDB directory="/data/activemq/store" replicas="3" bind="tcp://0.0.0.0:61619" zkAddress="zk1:2181,zk2:2181,zk3:2181" zkPath="/activemq/leveldb-stores"/> </persistenceAdapter> </broker> ``` 3. **客户端连接**:使用 `failover:(tcp://node1:61616,tcp://node2:61616)?randomize=false` 自动切换主节点 #### 2. **Broker Network 模式**(负载均衡) ```mermaid graph LR Producer[生产者] -->|消息| Broker1[Broker1] Broker1 -->|转发| Broker2[Broker2] Broker1 -->|转发| Broker3[Broker3] Consumer[消费者] --> Broker2 ``` **配置步骤**: ```xml <!-- Broker1 的 activemq.xml --> <networkConnectors> <networkConnector uri="static:(tcp://broker2:61616,tcp://broker3:61616)" duplex="true" dynamicOnly="true" prefetchSize="1000"/> <!-- 消息预取优化 --> </networkConnectors> ``` **性能优化**: - 设置 `prefetchSize=1000` 减少网络开销 - 启用 `nio` 协议:`<transportConnector name="nio" uri="nio://0.0.0.0:61616"/>` - 限制内存:`<systemUsage><memoryUsage limit="512mb"/></systemUsage>` --- ### ActiveMQ vs RabbitMQ 性能对比 | **指标** | ActiveMQ | RabbitMQ | |------------------|------------------------------|---------------------------| | **协议支持** | OpenWire/STOMP/MQTT/AMQP | AMQP 原生 | | **单机吞吐** | 5-7万 msg/s | 8-12万 msg/s | | **延迟** | 毫秒级 | 微秒级 | | **集群扩展** | 基于 ZooKeeper,配置复杂 | 内置 Erlang 分布式,简单 | | **消息顺序** | 严格保证(独占消费者) | 队列级保证 | | **适用场景** | 企业级事务、JMS 规范 | 高吞吐、低延迟场景 | **数学模型**: - ActiveMQ 吞吐量公式:$$ T_{\text{max}} = \frac{N_{\text{conn}} \times C_{\text{prefetch}}}{\text{AvgMsgSize}} \times \text{CompressionRatio} $$ 其中 $N_{\text{conn}}$ 为连接数,$C_{\text{prefetch}}$ 为预取值,压缩比通常为 0.3-0.6 --- ### Spring Boot 集成 ActiveMQ 方法 #### 1. **依赖配置** ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> </dependency> ``` ```properties # application.properties spring.activemq.broker-url=failover:(tcp://node1:61616,tcp://node2:61616) spring.activemq.user=admin spring.activemq.password=secret spring.activemq.pool.enabled=true # 启用连接池 spring.activemq.pool.max-connections=50 ``` #### 2. **生产者示例** ```java @Service public class OrderProducer { @Autowired private JmsTemplate jmsTemplate; public void sendOrder(Order order) { jmsTemplate.convertAndSend("ORDER.QUEUE", order, message -> { message.setStringProperty("BUSINESS_TYPE", "PAYMENT"); message.setJMSExpiration(60000); // 60秒超时 return message; }); } } ``` #### 3. **消费者示例**(事务支持) ```java @Component public class OrderConsumer { @JmsListener(destination = "ORDER.QUEUE", containerFactory = "jmsListenerContainerFactory") @Transactional // Spring 事务管理 public void handleOrder(Order order, Session session) { try { processOrder(order); // 业务处理 } catch (Exception e) { session.recover(); // 消息重投递 } } } @Bean public JmsListenerContainerFactory<?> jmsListenerContainerFactory( ConnectionFactory connectionFactory, PlatformTransactionManager transactionManager) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setTransactionManager(transactionManager); // 事务支持 factory.setSessionTransacted(true); factory.setConcurrency("5-10"); // 并发消费者数 return factory; } ``` --- ### ActiveMQ KahaDB 持久化机制 KahaDB 基于 **LSM-Tree(Log-Structured Merge Tree)** 实现高效写入: 1. **存储结构**: - `db-1.log`:顺序追加消息的日志文件 - `db.data`:B+树索引(内存缓存 + 磁盘存储) - `db.redo`:事务日志(WAL 机制) 2. **写入流程**: ```mermaid graph LR Msg[新消息] -->|追加写入| Log[db-1.log] Log -->|更新索引| MemTable[内存索引表] MemTable -->|定期刷写| SSTable[db.data 磁盘索引] ``` 3. **合并优化**: 当日志文件数量 $N_{\text{log}} > 4$ 时触发 Compaction: $$ \text{Compaction}_{\text{threshold}} = \max(4, \frac{\text{DiskSpace}_{\text{free}}}{100\text{MB}}) $$ 合并小文件并生成新索引,减少磁盘寻址 --- ### 死信队列(DLQ)配置使用 #### 1. **DLQ 配置** ```xml <!-- activemq.xml --> <policyEntry queue=">"> <deadLetterStrategy> <individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true" processExpired="false" /> <!-- 过期消息不进DLQ --> </deadLetterStrategy> <redeliveryPolicy> <redeliveryPolicy maximumRedeliveries="3" initialRedeliveryDelay="5000" useExponentialBackOff="true"/> </redeliveryPolicy> </policyEntry> ``` #### 2. **Spring Boot 消费 DLQ** ```java @JmsListener(destination = "DLQ.ORDER.QUEUE") public void handleDlq(Message message) { TextMessage msg = (TextMessage) message; System.out.println("死信消息: " + msg.getText()); // 人工处理归档 } ``` #### 3. **DLQ 触发条件** - 消息重投递超过 `maximumRedeliveries` 次数 - 消息格式错误(反序列化失败) - 消费者主动调用 `session.recover()` --- ### 性能优化建议 1. **KahaDB 调优**: ```xml <kahaDB directory="/data/kahadb" indexCacheSize="10000" <!-- B+树索引缓存 --> journalMaxFileLength="32mb"/> <!-- 日志文件大小 --> ``` 2. **网络优化**: - 使用 NIO 协议:`tcp://0.0.0.0:61616?wireFormat.nio.enabled=true` - 启用压缩:`jms.useCompression=true` 3. **消费者并发**: ```java factory.setConcurrency("10-50"); // 根据CPU核心数调整 ``` > 实测数据:优化后集群吞吐量可达 **12万 msg/s**(16核/32GB 环境)[^1] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值