消息中间件
1、什么是消息中间件?
关注于数据的发送和接收,利用高效可靠地异步消息传递机制集成分布式系统。
2、什么是JMS? java - api
Java 消息服务(Java Message Service)即JMS, 是一个Java 平台(不能跨语言)中关于面向消息中间件的API,用户两个应用程序之间,或分布式系统中发送消息,进行异步通信。
3、AMQP协议(为了跨语言) Wire-protocaol, 只支持 byte[] 二进制的消息类型
AMQP(advanced message queuing protocol) 是一个提供统一消息服务的应用层标准协议,给予此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。
4、常用的消息中间件
<1> ActiveMQ 完全支持JMS1.1与J2EE 1.4规范的JMS Provider实现。
支持多种语言 Java c c++ c# Ruby Perl Python PHP,支持的应用协议:OpenWire, Stomp REST, WS Notification, Xmpp, AMQP (没有JMS 因为不是应用协议,只是开发规范)。
<2> RabbitMQ 是一个开源的AMQP实现,服务器端使用Erlang语言编写,用于在分布式系统中存储转发消息,在易用性和扩展性 高可用等方便变现不俗
支持多种客户端, 如 Python Ruby .Net java jms c php等
<3> kafka 是一种高吞吐量的分布式发布订阅消息系统,是一个分布式的,分区的,可靠地分布式日志存储服务,它通过一种独一无二的设计提供了一个消息系统的功能。不是一个严格的消息中间件,
主要是用来做日志储存。即使是非常普通的硬件kafka也可以支持每秒数百万的消息。
比对:
5、消息模式
主题和队列两种模式。
队列模型: 客户端包含生产者和消费者
队列中的消息只能被一个消费者消费
消费者可以随时消费队列中的消息
主题模型: 客户端包括发布者和订阅者
主题中的消息被所有的订阅者消费
消费者不能消费订阅之前就发送到主题中的消息
6、JMS 编码接口之间的关系
7、队列模式
在linux上安装activemq部分此处省略
安装完后打开activemq平台,访问地址http://192.168.37.128:8161,登录账号和密码都是admin
下面我们来创建一个生产者:
public class AppProducer { private static final String URL = "tcp://192.168.37.128:61616"; private static final String QUEUE_NAME = "queue-test"; public static void main(String[] args) throws Exception{ // 创建连接工厂 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(URL); // 建立连接 Connection connection = connectionFactory.createConnection(); // 启动连接 connection.start(); // 创建会话 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // 创建一个目标 Destination destination = session.createQueue(QUEUE_NAME); // 创建一个生产者 MessageProducer producer = session.createProducer(destination); // 发送消息 for(int i=0; i < 100; i++){ // 创建消息 TextMessage textMessage = session.createTextMessage("test" + i); // 发送消息 producer.send(textMessage); System.out.println("send" + textMessage.getText()); } // 关闭连接 connection.close(); } }
运行成功后,我们查看activemq平台,发现队列中有100个待消费的消息:
ok, 下面我们来创建一个消费者:
public class AppConsumer { private static final String URL = "tcp://192.168.37.128:61616"; private static final String QUEUE_NAME = "queue-test"; public static void main(String[] args) throws Exception{ // 创建连接工厂 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(URL); // 建立连接 Connection connection = connectionFactory.createConnection(); // 启动连接 connection.start(); // 创建会话 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // 创建一个目标 Destination destination = session.createQueue(QUEUE_NAME); // 创建一个消费者 MessageConsumer consumer = session.createConsumer(destination); // 消费消息 consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message arg0) { TextMessage textMessage = (TextMessage)arg0; System.out.println("receive" + textMessage); } }); } }
运行成功后,我们查看activemq平台,发现队列中100个待消费的消息全部被消费:
8 主题模式 主题模式下,消费者必须要比订阅者先启动,否则不会收到消息。
主题模式和队列模式,代码机会相同,只需要在创建目标的时候改成创建主题。
public class AppTopicProducer { private static final String URL = "tcp://192.168.37.128:61616"; private static final String QUEUE_NAME = "queue-test"; public static void main(String[] args) throws Exception{ // 创建连接工厂 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(URL); // 建立连接 Connection connection = connectionFactory.createConnection(); // 启动连接 connection.start(); // 创建会话 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // 创建一个目标 Destination destination = session.createTopic(QUEUE_NAME); // 创建一个生产者 MessageProducer producer = session.createProducer(destination); // 发送消息 for(int i=0; i < 100; i++){ // 创建消息 TextMessage textMessage = session.createTextMessage("test" + i); // 发送消息 producer.send(textMessage); System.out.println("send" + textMessage.getText()); } // 关闭连接 connection.close(); } }
public class AppTopicConsumer { private static final String URL = "tcp://192.168.37.128:61616"; private static final String QUEUE_NAME = "queue-test"; public static void main(String[] args) throws Exception{ // 创建连接工厂 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(URL); // 建立连接 Connection connection = connectionFactory.createConnection(); // 启动连接 connection.start(); // 创建会话 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // 创建一个目标 Destination destination = session.createTopic(QUEUE_NAME); // 创建一个消费者 MessageConsumer consumer = session.createConsumer(destination); // 消费消息 consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message arg0) { TextMessage textMessage = (TextMessage)arg0; System.out.println("receive" + textMessage); } }); } }
之所以 会是 200个消息,但是只消费了 100个, 是因为在 消费者订阅之前, 就已经产生了100个消息,所以这100个不会被消费。
9 ActiveMQ集群配置
为什么要对消息中间件集群?
实现高可用,以排除单点故障引起的服务中断
实现负载均衡,以提升效率为更多的客户提供服务
集群方式:
客户端集群:让多个消费者消费同一队列
Broker clusters:多个Broker之间同步消息
Master Slave:实现高可用
客户端配置
ActiveMQ 失效转移(failover)
允许当其中一台消息服务器宕机时,客户端在传输层上重新连接到其他消息服务器。
语法:failover:(url1,...,urln)?transportOptions
transportOptions 说明:
randomize 默认为true, 表示在URL列表中选择URL链接时 是否采用随机策略
initialReconnectDelay 默认为10,单位毫秒,表示第一次尝试重连之间等待时间
maxReconnectDelay 默认30000, 单位毫秒,最长重连的时间间隔
Broker Cluster 集群配置
原理:
节点A的消息 可以同步到 节点B, 节点B的消息也可以同步到节点A上, 节点A产生的消息可以被节点B的消费者消费, 节点B产生的消息可以被节点A的消费者消费。
Master/Slave集群配置
集群方案
Share nothing storage master/slave (已过时,5.8+后移除)
Shared storage master/slave 共享存储
Replicated LevelDB Store 基于复制的LevelDB Store === > 基于zookeeper 的 master 选择方案
共享存储集群的原理
当前 节点A获得锁资源,成为master , 节点B就无法获得锁资源,一直在等待。如果某个时刻,节点A挂掉,那么节点B会获得锁资源,成为master, 节点A 成为slave
下面是 基于复制的LevelDB Store 的原理
基于zk, 此时节点A被zk选举为Master, 此时节点A作为与外界沟通的口子,当A接收到新的信息,则会在本地进行存储,并且通过zk 传输到节点B和节点C上进行本地存储。
两种集群方式对比:
master/slave 方式 只支持高可用,但是不支持负载均衡。
Broker Cluster 支持 负载均衡,但是不支持高可用。
下面来看一种 即可以高可用,又可以负载均衡的方式。
我们使用三台服务器的完美集群方案:
节点B和节点C实现高可用, 节点A为 节点B 或者C 的负载均衡。 但是此方案,一旦节点B 和 节点C 全部挂掉,那么整个系统也就挂掉了,所以我们要使用更多太服务器来防止多台服务器宕机的场景。