浅析jms之ActiveMQ

1.什么是Jms

jms(java消息服务)是一个java的标准,就是使用消息代理的API。

名词解析:

消息代理:就是一个应用给另一个应用发送消息时,会将消息先给消息代理,消息会确保消息到达目得地。这个消息代理类似于邮局,会把我们的邮件寄到想要寄的人。

目得地:就是接收消息的地方。目得地只关注消息应该从哪里获得。而不关心消息是谁取走的。

activeMQ:是支持jms标准的工具。

2.ActiveMQ环境的搭建

首先肯定得需要java环境。

去  http://activemq.apache.org/activemq-5111-release.html 地址下载开发包及源码。


在下载的包中bin中activemq是ActiveMQ服务器启动的文件。



启动activemq文件,结果:



点击地址:    

http://localhost:8161/admin/     
这里直接用的别人的配置,activemq原始的端口号是61616

已配置好的开发环境: http://download.csdn.net/detail/ya_1249463314/9752773


进入activemq服务启动的主页面:

这里面Queues就是队列的内容的相关数据的显示(点对点模型稍后会介绍)。Topics就是主题的内容的相关数据的显示(发布订阅模型稍后会介绍)。


activemq-all-5.11.1.jar包,是开发需要的jar包,如果开发需要导入。


3.点对点模型的解析

在jms中会有两种不同的消息路由方式。对应的两种通用的目的地分别是队列和主题。这两种模型分别是点对点模型和发布订阅模型。

什么是点对点模型?

就是意味着每一条消息都有一个发送者和一个接受者(是一个一对一的关系)。当消息代理得到消息时,它将消息收入一个队列中。当接收者请求队列中的下一条消息时,消息会从队列中取出,并将消息传递给接收者。注意一点是虽然消息队列中的每一条消息只传给一个接收者,但是这不意味着只能使用一个接收者从队列中获取消息。一般我们都是使用几个接收者来处理队列中的消息。只是每个接收者都会处理自己所接收的消息。

举例:

在银行中办理业务时,当银行柜员业务完成后,他就闲着没啥事了,然后排队的队伍中最前面的人会被叫到,然后去办业务。所以我们会被哪个柜员叫到是不确定,也可以说随机的。这样就充分保证了柜员人员利用率的提高。如果是每个人专门负责什么业务的话,柜员完成任务就会闲着,不能保证充分利用。(尽管现在银行去办理业务都是给张带号的纸不采用排队了,实际上就是相当于使用号码排队了)。

java实现点对点模型

(1)方式1:不使用监听器

消息生产者:

/*消息生产者*/
public class QueueProducer {
	//默认的连接用户名
	private static final String USERNAME=ActiveMQConnectionFactory.DEFAULT_USER;
	//默认的连接密码
	private static final String PASSWORD=ActiveMQConnectionFactory.DEFAULT_PASSWORD;
	//默认的连接地址
	private static final String URL=ActiveMQConnectionFactory.DEFAULT_BROKER_URL;
	//发送消息的条数
	private static final int MessageNum=10;
	
	public static void main(String[] args) {
		ConnectionFactory connectionFactory;//连接工厂生产Connection
		Connection connection = null;
		//发送或者接收的线程
		Session session;
		//消息的目得地
		Destination destination;
		//消息生产者
		MessageProducer messageProducer;
		//实例化连接工厂
		connectionFactory =new ActiveMQConnectionFactory(QueueProducer.USERNAME, QueueProducer.PASSWORD, QueueProducer.URL);
		//通过连接工厂获取连接
		try {
			connection =connectionFactory.createConnection();
			connection.start();
			session =connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
			destination =session.createQueue("FirstQueue");
			messageProducer =session.createProducer(destination);
			sendMessage(session,messageProducer);
			session.commit();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if(connection !=null){
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}
	//发送消息
	public static void sendMessage(Session session,MessageProducer messageProducer) throws Exception{
		for(int i=0;i<QueueProducer.MessageNum;i++){
			TextMessage message =session.createTextMessage("ActiveMQ发送的消息 "+i);
			System.out.println("发送消息:ActiveMq发送的消息"+i);
			messageProducer.send(message);
		}
	}
}
解释:生产者自然是消息的发送者,也就是消息传递的源头。至于连接姓名、密码和地址和JDBC创建连接的配置方式是类似的。
使用连接工厂创建连接。使用连接创建会话线程用来做发送或接收的。使用会话创建带名字的队列,然后将此目得地给消息生产者。然后发送消息,注意要提交会话,要不消息发不出去的。

运行结果:

开始没有生产者发送消息,状态:


消息数量0、消费者数量0、消息进队列0、消息出队列0。

运行程序,生产者发送了消息,状态:


消息数量10、消息消费者数量0,、消息进队列10条、消息出队列0条。

消息接收者:

/*消息消费者*/
public class QueueConsumer {
	private static final String USERNAME=ActiveMQConnectionFactory.DEFAULT_USER;
	private static final String PASSWORD=ActiveMQConnectionFactory.DEFAULT_PASSWORD;
	private static final String URL=ActiveMQConnectionFactory.DEFAULT_BROKER_URL;
	
	public static void main(String[] args) {
		ConnectionFactory connectionFactory;
		Connection connection = null;
		Session session;
		Destination destination;
		//消息的消费者
		MessageConsumer messageConsumer;
		connectionFactory =new ActiveMQConnectionFactory(QueueConsumer.USERNAME, QueueConsumer.PASSWORD, QueueConsumer.URL);
		
		try {
			connection =connectionFactory.createConnection();
			connection.start();
			session =connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
			destination =session.createQueue("FirstQueue");
			messageConsumer =session.createConsumer(destination);
			while(true){
				TextMessage message =(TextMessage) messageConsumer.receive(100000);
				if(message !=null){
					System.out.println("收到的消息:"+message.getText());
				}else{
					break;
				}
			}
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
解释:连接配置和前面一样。这里创建消息消费者。目得地必须和前面的消息生产者一样,要不收不到消息代理所存的消息。然后使用循环,只要有数据都遍历出来。

运行结果:


消息数量0、消息消费者1位、消息进队还是原来的10条、消息消费出队列10条。

(2)方式2:使用监听器

生产者和上例一样。主要是消费者

队列监听器:

public class QueueListener implements MessageListener{

	@Override
	public void onMessage(Message message) {
		// TODO Auto-generated method stub
		try {
			System.out.println("收到的消息:"+((TextMessage)message).getText());
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
消息消费者:

public class QueueConsumer2 {
	private static final String USERNAME=ActiveMQConnectionFactory.DEFAULT_USER;
	private static final String PASSWORD=ActiveMQConnectionFactory.DEFAULT_PASSWORD;
	private static final String URL=ActiveMQConnectionFactory.DEFAULT_BROKER_URL;
	
	public static void main(String[] args) {
		ConnectionFactory connectionFactory;
		Connection connection = null;
		Session session;
		Destination destination;
		MessageConsumer messageConsumer;
		connectionFactory =new ActiveMQConnectionFactory(QueueConsumer2.USERNAME, QueueConsumer2.PASSWORD, QueueConsumer2.URL);
		try {
			connection =connectionFactory.createConnection();
			connection.start();
			session =connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
			destination =session.createQueue("FirstQueue");
			messageConsumer =session.createConsumer(destination);
			messageConsumer.setMessageListener(new QueueListener());
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
解释:直接给消息消费者传递队列监听器就可以了。

4.发布订阅模型解析

什么是发布订阅模型?

就是消息会发送一个主题,与队列类似,多个接收者都可以监听一个主题。消息不再是只投递给一个接收者,而是主题的所有订阅者都会接收到此消息的副本。(一对多的关系)。

举例:

就是我们平时都会买书,提前预购的人,相当于订阅了这本书,书一出版,这些人都会收到书籍。相当于一本书,很多人都会收到书。

java实现发布订阅模型

消息发布者:

/*消息发布者*/
public class TopicProducer {
	//默认的连接用户名
	private static final String USERNAME=ActiveMQConnectionFactory.DEFAULT_USER;
	//默认的连接密码
	private static final String PASSWORD=ActiveMQConnectionFactory.DEFAULT_PASSWORD;
	//默认的连接地址
	private static final String URL=ActiveMQConnectionFactory.DEFAULT_BROKER_URL;
	//发送消息的条数
	private static final int MessageNum=10;
	
	public static void main(String[] args) {
		ConnectionFactory connectionFactory;
		Connection connection = null;
		Session session;
		Destination destination;
		MessageProducer messageProducer;
		//实例化连接工厂
		connectionFactory =new ActiveMQConnectionFactory(TopicProducer.USERNAME, TopicProducer.PASSWORD, TopicProducer.URL);
		//通过连接工厂获取连接
		try {
			connection =connectionFactory.createConnection();
			connection.start();
			session =connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
			destination =session.createTopic("FirstTopic");
			//创建消息生产者
			messageProducer =session.createProducer(destination);
			sendMessage(session,messageProducer);
			session.commit();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			if(connection !=null){
				try {
					connection.close();
				} catch (JMSException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	//发送消息  
	public static void sendMessage(Session session,MessageProducer messageProducer) throws Exception{
		for(int i=0;i<TopicProducer.MessageNum;i++){
			TextMessage message =session.createTextMessage("ActiveMQ发布的消息 "+i);
			System.out.println("发布消息:ActiveMq发布的消息"+i);
			messageProducer.send(message);
		}
	}
}
消息订阅者1监听器:

public class TopicListener implements MessageListener{

	@Override
	public void onMessage(Message message) {
		// TODO Auto-generated method stub
		try {
			System.out.println("订阅者1收到的消息:"+((TextMessage)message).getText());
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
消息订阅者1:

/*消息订阅者1*/
public class TopicConsumer1 {
	private static final String USERNAME=ActiveMQConnectionFactory.DEFAULT_USER;
	private static final String PASSWORD=ActiveMQConnectionFactory.DEFAULT_PASSWORD;
	private static final String URL=ActiveMQConnectionFactory.DEFAULT_BROKER_URL;
	
	public static void main(String[] args) {
		ConnectionFactory connectionFactory;
		Connection connection = null;
		Session session;
		Destination destination;
		MessageConsumer messageConsumer;
		connectionFactory =new ActiveMQConnectionFactory(TopicConsumer1.USERNAME, TopicConsumer1.PASSWORD, TopicConsumer1.URL);
		
		try {
			connection =connectionFactory.createConnection();
			connection.start();
			session =connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
			destination =session.createTopic("FirstTopic");
			messageConsumer =session.createConsumer(destination);
			messageConsumer.setMessageListener(new TopicListener());
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
消息订阅者2监听器:

public class TopicListener2 implements MessageListener{

	@Override
	public void onMessage(Message message) {
		// TODO Auto-generated method stub
		try {
			System.out.println("订阅者2收到的消息:"+((TextMessage)message).getText());
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
消息订阅者2:

/*消息订阅者2*/
public class TopicConsumer2 {
	private static final String USERNAME=ActiveMQConnectionFactory.DEFAULT_USER;
	private static final String PASSWORD=ActiveMQConnectionFactory.DEFAULT_PASSWORD;
	private static final String URL=ActiveMQConnectionFactory.DEFAULT_BROKER_URL;
	
	public static void main(String[] args) {
		ConnectionFactory connectionFactory;
		Connection connection = null;
		Session session;
		Destination destination;
		MessageConsumer messageConsumer;
		connectionFactory =new ActiveMQConnectionFactory(TopicConsumer2.USERNAME, TopicConsumer2.PASSWORD, TopicConsumer2.URL);
		
		try {
			connection =connectionFactory.createConnection();
			connection.start();
			session =connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
			destination =session.createTopic("FirstTopic");
			messageConsumer =session.createConsumer(destination);
			messageConsumer.setMessageListener(new TopicListener2());
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
注意:一定是先运行消息订阅者,然后再运行消息发布者(在生活中我们先预定书再等别人出书的过程)。

运行结果:

运行两个消息订阅者结果:


消息主题:FirstTopic、订阅者数量2、消息进队列0、消息出队列0。

运行消息发布者结果:


消息主题FirstTopic、消息订阅者数量2、消息进队列10条(因为消息发布者就发布10条)、消息出队列20条(由于是两个订阅者,每人取了10条,这里大概可以猜到这个出队列数是使用计数器的方式做的)。

补充:

消息接收的方式:

Session.AUTO_ACKNOWLEDGE : 当消费者成功的从receive方法返回的时候,或者从Listener中onMessage方法成功返回时候,会话自动确认客户收到的消息。

Session.CLIENT_ACKNOWLEDGE : 客户通过消息的acknowledge方法确认消息。但是需要注意的是,在这种模式中,确认是在会话层上进行的。确认一个被消费的消息将自动确认所有已被会话消费的消息。例如:如果一个消息消费者已经消费了10条消息。然后确认消费第5个信息时,那么所有10个消息都被确认。

Session.DUPS_OK_ACKNOWLEDGE : 这种方式是使会话迟钝地确认消息地提交。如果Provider失败,那么可能会导致一些重复的消息。如果是重复的消息,那么Provider必须把消息头的JMSRedelivered字段设置为true。

S



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值