ActiveMQ--Day8

一:入门简介

1:JMS简介

       全称:Java Message Service 中文:Java消息服务。

       JMS是Java的一套API标准,最初的目的是为了使应用程序能够访问现有的MOM系统(MOM是Message Oriented Middleware的英文缩写,指的是利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。);后来被许多现有的MOM供应商采用,并实现为MOM系统。【常见MOM系统包括Apache的ActiveMQ、阿里巴巴的RocketMQ、IBM的MQSeries、Microsoft的MSMQ、BEA的RabbitMQ等。(并非全部的MOM系统都遵循JMS规范)】。基于JMS实现的MOM,又被称为JMS Provider。

       “消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。消息被发送到队列中。“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。

       消息队列的主要特点是异步处理,主要目的是减少请求响应时间和解耦。所以主要的使用场景就是将比较耗时而且不需要即时(同步)返回结果的操作作为消息放入消息队列。同时由于使用了消息队列,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦和。如:

       跨系统的异步通信,所有需要异步交互的地方都可以使用消息队列。就像我们除了打电话(同步)以外,还需要发短信,发电子邮件(异步)的通讯方式。

       多个应用之间的耦合,由于消息是平台无关和语言无关的,而且语义上也不再是函数调用,因此更适合作为多个应用之间的松耦合的接口。基于消息队列的耦合,不需要发送方和接收方同时在线。

       在企业应用集成(EAI)中,文件传输,共享数据库,消息队列,远程过程调用都可以作为集成的方法。

       应用内的同步变异步,比如订单处理,就可以由前端应用将订单信息放到队列,后端应用从队列里依次获得消息处理,高峰时的大量订单可以积压在队列里慢慢处理掉。由于同步通常意味着阻塞,而大量线程的阻塞会降低计算机的性能。

       消息驱动的架构(EDA),系统分解为消息队列,和消息制造者和消息消费者,一个处理流程可以根据需要拆成多个阶段(Stage),阶段之间用队列连接起来,前一个阶段处理的结果放入队列,后一个阶段从队列中获取消息继续处理。

       应用需要更灵活的耦合方式,如发布订阅,比如可以指定路由规则。

       跨局域网,甚至跨城市的通讯,比如北京机房与广州机房的应用程序的通信。

2:ActiveMQ

       多种语言和协议编写客户端。语言: Java,C,C++,C#,Ruby,Perl,Python,PHP。应用协议: OpenWire,Stomp REST,WS Notification,XMPP,AMQP

       通过了常见J2EE服务器(如 Geronimo,JBoss 4,GlassFish,WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ可以自动的部署到任何兼容J2EE 1.4 商业服务器上

       完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务),对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去。支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA,支持通过JDBC和journal提供高速的消息持久化,支持Ajax,支持与Axis的整合,WebServices,可以很容易的调用内嵌JMS provider进行测试,,从设计上保证了高性能的集群。客户端-服务器,点对点

2.1:Destination

       目的地,JMS Provider(消息中间件)负责维护,用于对Message进行管理的对象。MessageProducer需要指定Destination才能发送消息,MessageConsumer需要指定Destination才能接收消息。

2.2:Producer

       消息生成者(客户端、生成消息),负责发送Message到目的地。应用接口为MessageProducer。在JMS规范中,所有的标准定义都在javax.jms包中。

2.3:Consumer【Receiver】

      消息消费者(处理消息),负责从目的地中消费【处理|监听|订阅】Message。应用接口为MessageConsumer

2.4:Message

      消息(Message),消息封装一次通信的内容。常见类型有:StreamMessage、BytesMessage、TextMessage、ObjectMessage、MapMessage。

2.5:ConnectionFactory

      链接工厂, 用于创建链接的工厂类型。 注意,不能和JDBC中的ConnectionFactory混淆。

2.6:Connection

      链接. 用于建立访问ActiveMQ连接的类型, 由链接工厂创建. 注意,不能和JDBC中的Connection混淆。

2.7:Session

      会话, 一次持久有效有状态的访问. 由链接创建. 是具体操作消息的基础支撑。

2.8:Queue & Topic

      Queue是队列目的地,Topic是主题目的地。都是Destination的子接口。

      Queue特点: 队列中的消息,默认只能由唯一的一个消费者处理。一旦处理消息删除。

      Topic特点:主题中的消息,会发送给所有的消费者同时处理。只有在消息可以重复处理的业务场景中可使用。

2.9:PTP

       Point to Point。点对点消息模型。就是基于Queue实现的消息处理方式。

2.10:PUB & SUB

       Publish & Subscribe 。消息的发布/订阅模型。是基于Topic实现的消息处理方式。

二:安装

1:安装步骤

配置jdk,然后直接解压即可

 http://activemq.apache.org

2:打开和关闭

./activemq start #启动
./activemq stop  #关闭
./activemq restart #重启

3:Web访问

http://ip:8161/admin/
#默认账号密码admin,admin,保存在conf/jetty-realm.properties中

三:ActiveMQ应用

1:PTP处理模式(Queue)

1.1:概念

       消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费、其它的则不能消费此消息了。当消费者不存在时,消息会一直保存,直到有消费消费

1.2:代码

1.2.1:普通PTP

1.2.1.1:生产者

package hhxy.first;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * 发送一个字符串文本消息到ActiveMQ
 * @author 李伟康
 *
 */
public class TextProducer {
	/**
	 * 发送消息到ActiveMQ,具体的消息内容为参数信息
	 * 开发JMS相关代码,使用的接口类型一般是javax.jms包下的
	 * @throws JMSException 
	 */
	public void sendTextMessage(String datas){
		//连接工厂
		ConnectionFactory factory=null;
		//连接
		Connection connection=null;
		//目的地
		Destination destination=null;
		//会话
		Session session=null;
		//消息发送者
		MessageProducer producer=null;
		//消息对象
		Message message=null;
		try {
			//创建连接工厂,连接ActiveMQ服务器的连接工厂
			//创建工厂,构造方法有三个参数,分别为用户名,密码,地址
			//无参构造,有默认的连接地址localhost
			//单参数构造,无验证模式的,没有用户的验证
			//三个参数,有认证+指定地址,默认端口61616,在activemq.xml查看
			
			factory=new ActiveMQConnectionFactory("admin", "admin", 
					"tcp://192.168.206.11:61616");
			//通过工厂创建连接对象
			//创建连接的方法有审核,其中有createConnection(String username,String password);
			//可以在创建连接工厂时,只传递连接地址,不传递用户
			connection=factory.createConnection();
			//建议启动连接,消息的发送者不是必须启动连接,消息的消费者必须启动连接
			//发送者在发送时会检验连接,未启动,自动启动
			connection.start();
			
			//通过连接对象,创建会话对象,必须绑定目的地
			/**
			 *  创建会话的时候,必须传递两个参数,分别逮代表是否支持事务,和如何确认消息处理
			 * transacter-是否支持事务,
			 * true-支持,第二个参数无效,一般用Session.SESSION_TRANSACTED
			 * false-不支持,第二个参数必须传递且有效
			 * 
			 * acknowleageMode-如何确认消息的处理,使用确认机制实现
			 * AUTO_ACKNOWLEAGE-自动确认消息,常用
			 * CLIENT_ACKNOWLEAGE-手动确认,消费者处理后,必须手动确认
			 * DUPS_OK_ACKNOWLEAGE-有副本的手动确认,一个消息多次使用,可以容忍消息重复时使用
			 */
			
			session=connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			destination=session.createQueue("first-mq");
			
			//通过会话对象,创建消息的发送者
			//创建的消息发送者,发送的消息一定指定到目的地
			producer=session.createProducer(destination);
			//创建文本消息对象在,作为具体的数据载体
			message=session.createTextMessage(datas);
			//发送消息到目的地
			producer.send(message);
			System.out.println("消息已发送");
		}catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally {
			//回收资源
			if(producer!=null) {
				try {
					producer.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(session!=null) {
				try {
					session.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(connection!=null) {
				try {
					connection.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	public static void main(String[] args ) {
		TextProducer producer=new TextProducer();
		producer.sendTextMessage("测试ActiveMQ-queue");
	}
}

1.2.1.2:消费者

package hhxy.first;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

public class TextConsumer {

	public String receiveTextMessage(){
		String resultCode="";
		ConnectionFactory factory=null;
		Connection connection=null;
		Session session=null;
		Destination destination=null;
		//消息的消费者,用于接收消息的对象
		MessageConsumer consumer=null;
		Message message=null;
		try {
			factory=new ActiveMQConnectionFactory("admin", "admin", 
					"tcp://192.168.206.11:61616");
			connection=factory.createConnection();
			//消息的消费者必须启动连接,否则无法处理消息
			connection.start();
			session=connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			destination=session.createQueue("first-mq");
			//创建消息消费者对象,在指定的目的地中获取消息
			consumer=session.createConsumer(destination);
			//获取队列中的消息,recever方法是一个主动获取消息的方法,执行一次拉取一个消息
			message=consumer.receive();
			//处理文本消息
			resultCode=((TextMessage)message).getText();
		}catch (Exception e) {
			// TODO: handle exception
		}finally {
			//回收资源
			if(consumer!=null) {
				try {
					consumer.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(session!=null) {
				try {
					session.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(connection!=null) {
				try {
					connection.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return resultCode;
	}
	public static void main(String[] args){
		// TODO Auto-generated method stub
		TextConsumer textConsumer=new TextConsumer();
		String resultCode=textConsumer.receiveTextMessage();
		System.out.println(resultCode);
	}

}

1.2.2:带监听的PTP

1.2.2.1:生产者

package listener;

import java.util.Random;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;

public class ObjectProducer {

	public void sendMessage(Object object){
		//连接工厂
		ConnectionFactory factory=null;
		//连接
		Connection connection=null;
		//目的地
		Destination destination=null;
		//会话
		Session session=null;
		//消息发送者
		MessageProducer producer=null;
		//消息对象
		Message message=null;
		try {
			//创建连接工厂,连接ActiveMQ服务器的连接工厂
			//创建工厂,构造方法有三个参数,分别为用户名,密码,地址
			//无参构造,有默认的连接地址localhost
			//单参数构造,无验证模式的,没有用户的验证
			//三个参数,有认证+指定地址,默认端口61616,在activemq.xml查看
			
			factory=new ActiveMQConnectionFactory("admin", "admin", 
					"tcp://192.168.206.11:61616");
			//通过工厂创建连接对象
			//创建连接的方法有审核,其中有createConnection(String username,String password);
			//可以在创建连接工厂时,只传递连接地址,不传递用户
			connection=factory.createConnection();
			//建议启动连接,消息的发送者不是必须启动连接,消息的消费者必须启动连接
			//发送者在发送时会检验连接,未启动,自动启动
			connection.start();
			
			//通过连接对象,创建会话对象,必须绑定目的地
			/**
			 *  创建会话的时候,必须传递两个参数,分别逮代表是否支持事务,和如何确认消息处理
			 * transacter-是否支持事务,
			 * true-支持,第二个参数无效,一般用Session.SESSION_TRANSACTED
			 * false-不支持,第二个参数必须传递且有效
			 * 
			 * acknowleageMode-如何确认消息的处理,使用确认机制实现
			 * AUTO_ACKNOWLEAGE-自动确认消息,常用
			 * CLIENT_ACKNOWLEAGE-手动确认,消费者处理后,必须手动确认
			 * DUPS_OK_ACKNOWLEAGE-有副本的手动确认,一个消息多次使用,可以容忍消息重复时使用
			 */
			
			session=connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			destination=session.createQueue("test-listener");
			
			//通过会话对象,创建消息的发送者
			//创建的消息发送者,发送的消息一定指定到目的地
			producer=session.createProducer(destination);
			Random r=new Random();
			for(int i=0;i<100;i++) {
				Integer data=r.nextInt(100);
				//创建消息对象,消息的数据载体是一个可序列化的对象
				message=session.createObjectMessage(data);
				producer.send(message);
			}
			System.out.println("消息已发送");
		}catch (Exception e) {
			// TODO: handle exception
		}finally {
			//回收资源
			if(producer!=null) {
				try {
					producer.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(session!=null) {
				try {
					session.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(connection!=null) {
				try {
					connection.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ObjectProducer producer=new ObjectProducer();
		producer.sendMessage(null);
	}

}

1.2.2.2:监听者

package listener;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * 使用监听者模式,实现消费的处理(消费)
 * @author 李伟康
 *
 */
public class ConsumerListener {

	/**
	 * 处理消息
	 */
	public void consumerMessage(){
		ConnectionFactory factory=null;
		Connection connection=null;
		Session session=null;
		Destination destination=null;
		//消息的消费者,用于接收消息的对象
		MessageConsumer consumer=null;
		try {
			factory=new ActiveMQConnectionFactory("admin", "admin", 
					"tcp://192.168.206.11:61616");
			connection=factory.createConnection();
			//消息的消费者必须启动连接,否则无法处理消息
			connection.start();
			session=connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			destination=session.createQueue("test-listener");
			//创建消息消费者对象,在指定的目的地中获取消息
			consumer=session.createConsumer(destination);
			consumer.setMessageListener(new MessageListener() {
				/**
				 * 监听器一旦注册,在线程关闭前永久有效
				 * 处理消息的方式:只要有消息处理,自动调用onMessage方法,处理消费
				 * 监听器可以处理若干,注册多个监听器,相当于集群
				 * ActiveMQ自动的循环调用多个监听器,处理队列中的消息,实现并行处理
				 * 
				 * 处理消息的方法就是监听方法。
				 * 监听的事件是:消息,消息未处理
				 * 要处理的具体内容:消息处理
				 */
				@Override
				public void onMessage(Message message) {
					try {
						//acknowledge()就是确认方法,确认后MQ删除对应消息
						message.acknowledge();
						ObjectMessage om=(ObjectMessage)message;
						Object data=om.getObject();
						System.out.println(data);
					} catch (Exception e) {
						// TODO: handle exception
					}
				}
			});
			//阻塞当前代码,保证listener代码未结束,如果代码结束,监听器关闭
			System.in.read();
			
		}catch (Exception e) {
			// TODO: handle exception
		}finally {
			//回收资源
			if(consumer!=null) {
				try {
					consumer.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(session!=null) {
				try {
					session.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(connection!=null) {
				try {
					connection.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ConsumerListener listener=new ConsumerListener();
		listener.consumerMessage();
	}

}

2:Publish/Subscribe处理模式(Topic)

2.1:概念

       消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。当生产者发布消息,不管是否有消费者。都不会保存消息一定要先有消息的消费者,后有消息的生产者。

2.2:代码

2.2.1:发布者

package topic;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;

public class TopicProducer {
	public void sendTextMessage(String datas){
		//连接工厂
		ConnectionFactory factory=null;
		//连接
		Connection connection=null;
		//目的地
		Destination destination=null;
		//会话
		Session session=null;
		//消息发送者
		MessageProducer producer=null;
		//消息对象
		Message message=null;
		try {
			//创建连接工厂,连接ActiveMQ服务器的连接工厂
			//创建工厂,构造方法有三个参数,分别为用户名,密码,地址
			//无参构造,有默认的连接地址localhost
			//单参数构造,无验证模式的,没有用户的验证
			//三个参数,有认证+指定地址,默认端口61616,在activemq.xml查看
			
			factory=new ActiveMQConnectionFactory("admin", "admin", 
					"tcp://192.168.206.11:61616");
			//通过工厂创建连接对象
			//创建连接的方法有审核,其中有createConnection(String username,String password);
			//可以在创建连接工厂时,只传递连接地址,不传递用户
			connection=factory.createConnection();
			//建议启动连接,消息的发送者不是必须启动连接,消息的消费者必须启动连接
			//发送者在发送时会检验连接,未启动,自动启动
			connection.start();
			
			//通过连接对象,创建会话对象,必须绑定目的地
			/**
			 *  创建会话的时候,必须传递两个参数,分别逮代表是否支持事务,和如何确认消息处理
			 * transacter-是否支持事务,
			 * true-支持,第二个参数无效,一般用Session.SESSION_TRANSACTED
			 * false-不支持,第二个参数必须传递且有效
			 * 
			 * acknowleageMode-如何确认消息的处理,使用确认机制实现
			 * AUTO_ACKNOWLEAGE-自动确认消息,常用
			 * CLIENT_ACKNOWLEAGE-手动确认,消费者处理后,必须手动确认
			 * DUPS_OK_ACKNOWLEAGE-有副本的手动确认,一个消息多次使用,可以容忍消息重复时使用
			 */
			
			session=connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			destination=session.createTopic("test-topic");
			
			//通过会话对象,创建消息的发送者
			//创建的消息发送者,发送的消息一定指定到目的地
			producer=session.createProducer(destination);
			//创建文本消息对象在,作为具体的数据载体
			message=session.createTextMessage(datas);
			//发送消息到目的地
			producer.send(message);
			System.out.println("消息已发送");
		}catch (Exception e) {
			// TODO: handle exception
		}finally {
			//回收资源
			if(producer!=null) {
				try {
					producer.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(session!=null) {
				try {
					session.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(connection!=null) {
				try {
					connection.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TopicProducer producer=new TopicProducer();
		producer.sendTextMessage("测试ActiveMQ-topic");
	}

}

2.2.2:订阅者

package topic;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

public class TopicConsumer {
	public String receiveTextMessage(){
		String resultCode="";
		ConnectionFactory factory=null;
		Connection connection=null;
		Session session=null;
		Destination destination=null;
		//消息的消费者,用于接收消息的对象
		MessageConsumer consumer=null;
		Message message=null;
		try {
			factory=new ActiveMQConnectionFactory("admin", "admin", 
					"tcp://192.168.206.11:61616");
			connection=factory.createConnection();
			//消息的消费者必须启动连接,否则无法处理消息
			connection.start();
			session=connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			destination=session.createTopic("test-topic");
			//创建消息消费者对象,在指定的目的地中获取消息
			consumer=session.createConsumer(destination);
			//获取队列中的消息,recever方法是一个主动获取消息的方法,执行一次拉取一个消息
			message=consumer.receive();
			//处理文本消息
			resultCode=((TextMessage)message).getText();
		}catch (Exception e) {
			// TODO: handle exception
		}finally {
			//回收资源
			if(consumer!=null) {
				try {
					consumer.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(session!=null) {
				try {
					session.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(connection!=null) {
				try {
					connection.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return resultCode;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TopicConsumer consumer=new TopicConsumer();
		String resultCode=consumer.receiveTextMessage();
		System.out.println(resultCode);
	}

}

3:PTP和PUB/SUB简单对比

 

Topic

Queue

概要

Publish Subscribe messaging 发布订阅消息

Point-to-Point点对点

有无状态

topic数据默认不落地,是无状态的。

Queue数据默认会在mq服务器上以文件形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kahadb下面。也可以配置成DB存储。

完整性保障

并不保证publisher发布的每条数据,Subscriber都能接受到。

Queue保证每条数据都能被receiver接收。消息不超时。

消息是否会丢失

一般来说publisher发布消息到某一个topic时,只有正在监听该topic地址的sub能够接收到消息;如果没有sub在监听,该topic就丢失了。

Sender发送消息到目标Queue,receiver可以异步接收这个Queue上的消息。Queue上的消息如果暂时没有receiver来取,也不会丢失。前提是消息不超时。

消息发布接收策略

一对多的消息发布接收策略,监听同一个topic地址的多个sub都能收到publisher发送的消息。Sub接收完通知mq服务器

一对一的消息发布接收策略,一个sender发送的消息,只能有一个receiver接收。receiver接收完后,通知mq服务器已接收,mq服务器对queue里的消息采取删除或其他操作。

四: ActiveMQ安全认证

      ActiveMQ也提供了安全认证。就是用户名密码登录规则。ActiveMQ如果需要使用安全认证的话,必须在activemq的核心配置文件中开启安全配置。配置文件就是conf/activemq.xml

      在conf/activemq.xml配置文件的broker标签中增加下述内容:

<plugins>
            <!--  use JAAS to authenticate using the login.config file on the classpath to configure JAAS -->
<!--  添加jaas认证插件activemq在login.config里面定义,详细见login.config-->

            <jaasAuthenticationPlugin configuration="activemq" />
            <!--  lets configure a destination based authorization mechanism -->
            <authorizationPlugin>
                <map>
                    <authorizationMap>
                        <authorizationEntries>
                            <authorizationEntry topic=">" read="admins" write="admins" admin="admins" />
                            <authorizationEntry queue=">" read="admins" write="admins" admin="admins" />
                            <authorizationEntry topic="ActiveMQ.Advisory.>" read="admins" write="admins" admin="admins"/>
                            <authorizationEntry queue="ActiveMQ.Advisory.>" read="admins" write="admins" admin="admins"/>
                        </authorizationEntries>
                    </authorizationMap>
                </map>
            </authorizationPlugin>
        </plugins>

开启认证后,认证使用的用户信息由其他配置文件提供:conf/login.config

activemq {
    org.apache.activemq.jaas.PropertiesLoginModule required
        org.apache.activemq.jaas.properties.user="users.properties"
        org.apache.activemq.jaas.properties.group="groups.properties";
};

user代表用户信息配置文件,group代表用户组信息配置文件。寻址路径为相对当前配置文件所在位置开始寻址:

conf/users.properties

#用户名=密码
admin=admin

conf/groups.properties

#用户组名=用户名,用户名
admins=admin

五:ActiveMQ的持久化

         ActiveMQ中,持久化是指对消息数据的持久化。在ActiveMQ中,默认的消息是保存在内存中的。当内存容量不足的时候,或ActiveMQ正常关闭的时候,会将内存中的未处理的消息持久化到磁盘中。具体的持久化策略由配置文件中的具体配置决定。ActiveMQ的默认存储策略是kahadb。如果使用JDBC作为持久化策略,则会将所有的需要持久化的消息保存到数据库中。
所有的持久化配置都在conf/activemq.xml中配置,配置信息都在broker标签内部定义。

1:kahadb方式

       是ActiveMQ默认的持久化策略。kahadb是一个文件型数据库。是使用内存+文件保证数据的持久化的。kahadb可以限制每个数据文件的大小。不代表总计数据容量。kahadb的配置文件如下(默认系统配置):

<persistenceAdapter>
    <!-- directory:保存数据的目录; journalMaxFileLength:保存消息的文件大小 -->
    <kahaDB directory="${activemq.data}/kahadb" journalMaxFileLength="16mb"/>
</persistenceAdapter>

2:jdbc持久化方式

2.1: 在activemq.xml中配置文件:

<!-- 在<broker>标签外配置 -->

<bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" 
destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/activemq?relaxAutoCommit=true"/>
    <property name="username" value="activemq"/>
    <property name="password" value="activemq"/>
    <property name="maxActive" value="200"/>
    <property name="poolPreparedStatements" value="true"/>
</bean>
<!-- dataSource指定持久化数据库的bean,createTablesOnStartup是否在启动的时候创建数据表,默认值是true,这样每次启动都会去创建数据表了,一般是第一次启动的时候设置为true,之后改成false。-->

<!-- 将<broker>标签中的<persistenceAdapter>改为如下 -->
<broker brokerName="test-broker" persistent="true" 
xmlns="http://activemq.apache.org/schema/core">
    <persistenceAdapter>
        <jdbcPersistenceAdapter dataSource="#mysql-ds" createTablesOnStartup="false"/>
    </persistenceAdapter>
</broker>

2.2:导包

将mysql的jdbc驱动包复制到activemq的lib文件夹下

2.3:新建数据库

配置成功后,需要在数据库中创建对应的database,否则无法访问。表格ActiveMQ可以自动创建。

create database activemq charset=utf8;

2.4:数据库表信息

2.4.1:activemq_msgs表

activemq_msgs用于存储消息,Queue和Topic都存储在这个表中:
ID:自增的数据库主键
CONTAINER:消息的Destination
MSGID_PROD:消息发送者客户端的主键
MSG_SEQ:是发送消息的顺序,MSGID_PROD+MSG_SEQ可以组成JMS的MessageID
EXPIRATION:消息的过期时间,存储的是从1970-01-01到现在的毫秒数
MSG:消息本体的Java序列化对象的二进制数据
PRIORITY:优先级,从0-9,数值越大优先级越高

2.4.2:activemq_acks表

activemq_acks用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存:
主要的数据库字段如下:
CONTAINER:消息的Destination
SUB_DEST:如果是使用Static集群,这个字段会有集群其他系统的信息
CLIENT_ID:每个订阅者都必须有一个唯一的客户端ID用以区分
SUB_NAME:订阅者名称
SELECTOR:选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性AND和OR操作
LAST_ACKED_ID:记录消费过的消息的ID。

2.4.3:activemq_lock表

activemq_lock在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,
其他的只能作为备份等待Master Broker不可用,才可能成为下一个Master Broker。
这个表用于记录哪个Broker是当前的Master Broker。
只有在消息必须保证有效,且绝对不能丢失的时候。使用JDBC存储策略。
如果消息可以容忍丢失,或使用集群/主备模式保证数据安全的时候,建议使用levelDB或Kahadb。

六:API简介

1:Producer API

1.1:发送消息

send(Message message);发送消息到默认目的地,就是创建Producer时指定的目的地。
send(Destination destination, Message message); 发送消息到指定目的地,Producer不建议绑定目的地。也就是创建Producer的时候,不绑定目的地。session.createProducer(null)。
send(Message message, int deliveryMode, int priority, long timeToLive);发送消息到默认目的地,且设置相关参数。deliveryMode-持久化方式(DeliveryMode.PERSISTENT| DeliveryMode.NON_PERSISTENT)。priority-优先级。timeToLive-消息有效期(单位毫秒)。
send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive); 发送消息到指定目的地,且设置相关参数。

1.2:消息有效期

       消息过期后,默认会将失效消息保存到“死信队列(ActiveMQ.DLQ)”。不持久化的消息,在超时后直接丢弃,不会保存到死信队列中。死信队列名称可配置,死信队列中的消息不能恢复。
       死信队列是在activemq.xml中配置的:

1.3:消息优先级

       我们可以在发送消息时,指定消息的权重,broker可以建议权重较高的消息将会优先发送给Consumer。在某些场景下,我们通常希望权重较高的消息优先传送;不过因为各种原因,priority并不能决定消息传送的严格顺序(order)。
       JMS标准中约定priority可以为0~9的整数数值,值越大表示权重越高,默认值为4。不过activeMQ中各个存储器对priority的支持并非完全一样。比如JDBC存储器可以支持0~9,因为JDBC存储器可以基于priority对消息进行排序和索引化;但是对于kahadb/levelDB等这种基于日志文件的存储器而言,priority支持相对较弱,只能识别三种优先级(LOW: < 4,NORMAL: =4,HIGH: > 4)。

1.3.1:开启

        在broker端,默认是不存储priority信息的,我们需要手动开启,修改activemq.xml配置文件,在broker标签的子标签policyEntries中增加下述配置:

<policyEntry queue=">" prioritizedMessages="true"/>

        不过对于“非持久化”类型的消息(如果没有被swap到临时文件),它们被保存在内存中,它们不存在从文件Paged in到内存的过程,因为可以保证优先级较高的消息,总是在prefetch的时候被优先获取,这也是“非持久化”消息可以担保消息发送顺序的优点。
        Broker在收到Producer的消息之后,将会把消息cache到内存,如果消息需要持久化,那么同时也会把消息写入文件;如果通道中Consumer的消费速度足够快(即积压的消息很少,尚未超过内存限制,我们通过上文能够知道,每个通道都可以有一定的内存用来cache消息),那么消息几乎不需要从存储文件中Paged In,直接就能从内存的cache中获取即可,这种情况下,priority可以担保“全局顺序”;不过,如果消费者滞后太多,cache已满,就会触发新接收的消息直接保存在磁盘中,那么此时,priority就没有那么有效了。
       在Queue中,prefetch的消息列表默认将会采用“轮询”的方式(roundRobin,注意并不是roundRobinDispatch)[备注:因为Queue不支持任何DispatchPolicy],依次添加到每个consumer的pending buffer中,比如有m1-m2-m3-m4四条消息,有C1-C2两个消费者,那么: m1->C1,m2->C2,m3->C1,m4->C2。这种轮序方式,会对基于权重的消息发送有些额外的影响,假如四条消息的权重都不同,但是(m1,m3)->C1,事实上m2的权重>m3,对于C1而言,它似乎丢失了“顺序性”。

1.3.2:强顺序

<policyEntry queue=">" strictOrderDispatch="true"/>

       strictOrderDispatch“严格顺序转发”,这是区别于“轮询”的一种消息转发手段;不过不要误解它为“全局严格顺序”,它只不过是将prefetch的消息依次填满每个consumer的pending buffer。比如上述例子中,如果C1-C2两个消费者的buffer尺寸为3,那么(m1,m2,m3)->C1,(m4)->C2;当C1填充完毕之后,才会填充C2。由此这种策略可以保证buffer中所有的消息都是“权重临近的”、有序的。(需要注意:strictOrderDispatch并非是解决priority消息顺序的问题而生,只是在使用priority时需要关注它)。

1.3.3:严格顺序

<policyEntry queue=">" prioritizedMessages="true" useCache="false" expireMessagesPeriod="0" queuePrefetch="1"/>

       useCache=false来关闭内存,强制将所有的消息都立即写入文件(索引化,但是会降低消息的转发效率);queuePrefetch=1来约束每个consumer任何时刻只有一个消息正在处理,那些消息消费之后,将会从文件中重新获取,这大大增加了消息文件操作的次数,不过每次读取肯定都是priority最高的消息。

2:Consumer API

2.1:消息的存储

       Consumer拉取消息后,如果没有做确认acknowledge,此消息不会从MQ中删除。消息的如果拉去到consumer后,未确认,那么消息被锁定。如果consumer关闭的时候仍旧没有确认消息,则释放消息锁定信息。消息将发送给其他的consumer处理。消息一旦处理,应该必须确认。类似数据库中的事务管理机制。

2.2:消息的过滤

对消息消费者处理的消息数据进行过滤。这种处理可以明确消费者的角色,细分消费者的功能。设置过滤:

Session.createConsumer(Destination destination, String messageSelector);

        过滤信息为字符串,语法类似SQL92中的where子句条件信息。可以使用诸如AND、OR、IN、NOT IN等关键字。详细内容可以查看javax.jms.Message的帮助文档。消息的生产者在发送消息的的时候,必须设置可过滤的属性信息,所有的属性信息设置方法格式为:setXxxxProperty(String name, T value)。 其中方法名中的Xxxx是类型,如setObjectProperty/setStringProperty等

七:Spring整合ActiveMQ

参考:

https://www.cnblogs.com/ll409546297/p/6898155.html

八:ActiveMQ集群的搭建

使用ZooKeeper+ActiveMQ实现主从和集群:

http://activemq.apache.org/replicated-leveldb-store.html

1:Master-Slave       

       主从模式是一种高可用解决方案。在ZooKeeper中注册若干ActiveMQ Broker,其中只有一个Broker提供对外服务(Master),其他Broker处于待机状态(Slave)。当Master出现故障导致宕机时,通过ZooKeeper内部的选举机制,选举出一台Slave替代Master继续对外提供服务。

1.1:安装ZooKeeper

主机

服务端口

投票端口

选举端口

192.168.159.130

2181

2881

3881

192.168.159.130

2182

2882

3882

192.168.159.130

2183

2883

3883

1.1.1:解压缩

tar -zxf zookeeper

1.1.2:复制

cp -r zookeeper /opt/module/zookeeper1

1.1.3:创建data数据目录

在zookeeper1目录中创建子目录data目录

mkdir data

1.1.4:编写Zookeeper配置文件

vi /usr/module/zookeeper1/conf/zoo.cfg

1.1.5:复制两份同样的Zookeeper

cp zookeeper1 zookeeper2 -r
cp zookeeper1 zookeeper3 -r

1.1.6:为Zookeeper服务增加服务命名

在每个Zookeeper应用内的data目录中增加文件myid,内部定义每个服务的编号. 编号要求为数字,是正整数,可以使用回声命名快速定义myid文件

1.1.7:修改Zookeeper配置文件 zoo.cfg

修改端口号,提供多节点服务命名

port=2181 客户端访问端口. 三个Zookeeper实例不能端口相同.
server.编号=IP:投票端口:选举端口
投票端口: 用于决定正在运行的主机是否宕机.
选举端口: 用于决定哪一个Zookeeper服务作为主机.
三个Zookeeper应用配置一致.
server.1=192.168.120.132:2881:3881
server.2=192.168.120.132:2882:3882
server.3=192.168.120.132:2883:3883

1.1.8:启动Zookeeper测试

要至少启动两个Zookeeper启动. 启动单一Zookeeper,无法正常提供服务。

1.2:安装ActiveMQ

在同一个Linux中安装三个ActiveMQ实例,使用不同端口实现同时启动。端口分配如下:

主机

M-S通讯端口

服务端口

jetty端口

192.168.159.130

62626

61616

8161

192.168.159.130

62627

61617

8162

192.168.159.130

62628

61618

8163

1.2.1:安装ActiveMQ实例

1.2.2:修改配置信息

1.2.2.1:修改jetty端口

修改conf/jetty.xml中的端口配置。分别是8161、8162、8163

<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
      <!-- the default port number for the web console -->
      <property name="port" value="8161"/>
</bean>

1.2.2.2:统一所有主从节点Broker命名

修改conf/activemq.xml文件。修改broker标签属性信息,统一所有节点的broker命名。

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="mq-cluster" dataDirectory="${activemq.data}">

1.2.2.3:修改持久化配置

修改conf/activemq.xml文件。修改broker标签中子标签persistenceAdapter相关内容。

replicas属性代表当前主从模型中的节点数量。按需配置。

bind属性中的端口为主从实例之间的通讯端口。代表当前实例对外开放端口是什么,三个实例分别使用62626、62627、62628端口。

zkAddress属性代表ZooKeeper安装位置,安装具体情况设置。

zkPath是ActiveMQ主从信息保存到ZooKeeper中的什么目录内。

hostname为ActiveMQ实例安装Linux的主机名,可以在/etc/hosts配置文件中设置。设置格式为:IP  主机名。 如: 127.0.0.1  mq-server

<persistenceAdapter>
  <!-- <kahaDB directory="${activemq.data}/kahadb"/> -->
    <replicatedLevelDB
      directory="${activemq.data}/levelDB"
      replicas="3"
      bind="tcp://0.0.0.0:62626"
      zkAddress="192.168.159.130:2181,192.168.159.130:2182,192.168.159.130:2183"
      zkPath="/activemq/leveldb-stores"
      hostname="mq-server"
      />
</persistenceAdapter>

1.2.2.4:修改服务端口

         修改ActiveMQ对外提供的服务端口。原默认端口为61616。当前环境使用的端口为:61616、61617、61618。修改conf/activemq.xml配置文件。修改broker标签中子标签transportConnectors的相关配置(第一行标签处)。

<transportConnectors>
  <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB -->
  <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
  <transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
  <transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
  <transportConnector name="mqtt" uri="mqtt://0.0.0.0:1883?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
  <transportConnector name="ws" uri="ws://0.0.0.0:61614?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
</transportConnectors>

1.3:启动主从

         将三个ActiveMQ实例分别启动。${activemq-home}/bin/active start。启动后,可以查看日志文件,检查启动状态,日志文件为${activemq-home}/data/activemq.log。

1.4:查看主从状态

1.4.1:使用客户端连接ZooKeeper

${zkHome}/bin/zkCli.sh

1.4.2:查看状态信息

连接成功后,可以使用命令‘ls’查看ZooKeeper中的目录结构,如:

ls /
ls /activemq/leveldb-stores

找到对应的内容后,可以使用命令‘get’查看ZooKeeper中的数据内容

get /activemq/leveldb-stores/00000000005

其中主节点的elected及address属性一定有数据。从节点则数据为‘null’。

2:集群

准备多份主从模型。在所有的ActiveMQ节点中的conf/activemq.xml中增加下述配置:(每个主从模型中的networkConnector都指向另外一个主从模型)

networkConnectors>
    <!--  duplex="false":代表该主从不向其他主从复制数据,duplex="true":代表双向复制数据 -->
    <networkConnector uri="static://(tcp://ip:port,tcp://ip:port)" duplex="false">
    </networkConnector>
</networkConnectors>

注意配置顺序,Networks相关配置必须在持久化相关配置之前。如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://activemq.apache.org/schema/core">
    <broker xmlns="http://activemq.apache.org/schema/core" brokerName="mq-cluster" dataDirectory="${activemq.data}" >
        <networkConnectors>
            <networkConnector uri=" static://(tcp://ip:port,tcp://ip:port)" duplex="false"/>
        </networkConnectors>
        <persistenceAdapter>
            < replicatedLevelDB directory = "xxx"/>       
        </persistenceAdapter>
    </broker>
</beans>

如: 主从模型1:192.168.159.129 ,主从模型2:192.168.159.130

在主从模型1的所有节点activemq.xml配置文件中增加标签:

<networkConnectors>
    <networkConnector uri="static://(tcp://192.168.159.130:61616,tcp://192.168.159.130:61617,tcp://192.168.159.130:61618)" duplex="false"/>
</networkConnectors>

在模型2中所有节点增加配置:

<networkConnectors>
    <networkConnector uri="static://(tcp://192.168.159.129:61616,tcp://192.168.159.129:61617,tcp://192.168.159.129:61618)" duplex="false"/>
</networkConnectors>

 

 

 


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值