消息中间件ActiveMQ

第四章 activemq

ActiveMQ主要用来解决应用程序之间的通信(消息传递问题),使用mq,应用间通信并不是直接进行宣城调用,而是通过消息队列进行一步通信

应用场景:高并发,对实时性要求不高的分布式应用系统

解决的问题:解耦系统、异步通信、削峰(消除流量的高峰)

1. JMS

JMS(Java Message Service)是javaEE的规范之一,这个规范指出消息的传递必须是异步的、非阻塞的,可以实现系统解耦增加系统的灵活性

1.1 核心API

  1. ConnectionFactory:连接工场,用于创建Connection;
  2. Connection:客户端与JMS的一次连接
  3. Session:客户端与JMS的一次会话,由Connection创建,可以用来创建消息的生产者和消费者,也可以创建消息的目的地和消息;
  4. Destination:生产者生产消息的目的地,消费者消费消息的来源,由Session创建;
    • Queue:队列
    • Topic:主题
  5. MessageProducer:消息的生产者,由Session创建,用于发送MQ消息;
  6. MessageConsumer:消息的消费者,由Session创建,用于消费消息;
  7. Message:消息,由Session创建
    • TextMessage
    • MapMessage
    • ObjectMessage
    • BytesMessage
    • StreamMessage
  8. MessageListener:消息监听器

1.2 JMS消息类型

  1. 点对点 (P2P)

一条消息只能呗一个消费者消费,被消费之后自动从消息队列剔除

  1. 发布/订阅(Pub/Sub)

一条消息可以被多个消费者同时消费,生产者和消费者有时间上的依赖性,也就是生产者在发送消息时,应该至少有一个消费者处于在线状态

2.什么是ActiveMQ

ActiveMQ是最受欢迎的、功能强大的开源消息传递和集成服务器,ActveMQ速度快,支持多语言的客户端和协议,很容易进行企业级集成,支持许多高级特性,完全支持JMS1.1和J2EE1.4;

​ 其它消息队列:

  • RabbitMQ:不是JMS实现,实现的AMQP协议;
  • RocketMQ:阿里开发的,目前贡献给Apache了,自研的消息协议;
  • Kafka:自研的消息协议;

ActiveMQ特点

  1. 支持多语言客户端和协议,如Java、C、C++、Ruby、Perl、Python、PHP
  2. 支持许多高级特性,如消息分组、虚拟目的地、通配符、组合目的地;
  3. 完全支持JMS1.1和J2EE1.4;
  4. 可以很容易集成到spring应用程序中;
  5. 通过大部分j2ee服务器的测试,如TomEE、JBoss、WebLogic等;
  6. 支持高效的JDBC持久化方式;
  7. 集群的支持;

3.安装与使用

解压完成以后,进入 apache-activemq-5.15.3\bin\win64\ 双击activemq.bat 等命令行窗口出现的时候,打开浏览器输入网址

http://localhost:8161/admin
username:  admin
password:  admin

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EoHCM0AO-1602493466751)(https://ae01.alicdn.com/kf/H89f01bf6e84649228abc75f91fcf0d41u.png)]

4 第一个实例

  1. 父工程引入公共依赖

引入依赖的时候可以使用</dependencyManagement> 管理父项目的依赖,经过此标签管理之后,子工程就可以在需要的时候引入相关的依赖,而不需要引入其他的依赖,且不需要输入版本号,子工程依赖如下所示

<dependencies>
 <dependency>
     <groupId>javax.jms</groupId>
     <artifactId>javax.jms-api</artifactId>
 </dependency>
</dependencies>
<dependencies>
    <dependency>
        <groupId>javax.jms</groupId>
        <artifactId>javax.jms-api</artifactId>
        <version>2.0.1</version>
    </dependency>

    <!-- spring-jms -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>5.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.2.6.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-broker</artifactId>
        <version>5.15.3</version>
        <!-- 排除低版本的jackson -->
        <exclusions>
            <exclusion>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-pool</artifactId>
        <version>5.15.3</version>
    </dependency>

    <!-- 引入高版本的jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.5</version>
    </dependency>
</dependencies>

4.1 ActiveMQ 套路

4.1.1创建消息生产者
  1. 创建连接工厂[ConnectionFactory]
//前两个工作的时候应该会给账号密码,和最后一个是activemq的连接地址
ConnectionFactory factory  = new ActiveMQConnectionFactory(
    null, null, "tcp://localhost:61616");
  1. 创建连接对象 并调用 start 方法
//创建完成之后必调用start方法!!!
Connection connection = factory.createConnection();
connection.start();
  1. 创建session,最重要的就是session

创建队列消息和创建生产者都是由session创建

  • 第一个参数: 是否开启事务
  • 第二个参数:手动签收还是自动签收
常量数值签收方式
Session.AUTO_ACKNOWLEDGE1自动签收
Session.CLIENT_ACKNOWLEDGE2客户端手动签收
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE)
  1. 创建Destination,如果队列不存在则自动创建
//1.创建Queue
Queue queue = session.createQueue("hello");
//2.创建Topic队列
Topic topic = session.createTopic("topic");
  1. 创建生产者
//消息发布者
MessageProduce producer = session.createProdecer(queue);
MessageProduce producer = session.createProdecer(topic);
  1. 创建消息

消息类型一共有五种,分别是**TextMessage[文本消息]、MapMessage[Map消息,键值对类型]、ObjectMessage**

剩下的两种不是很常用 BytesMessage、StreamMessage

//文本类型消息
TextMessage msg = session.createTextMessage("这是文本类型的消息");
//Map类型消息
MapMessage mapMsg = session.createMapMessage();
mapMsg.setString("name","mapMsg");
mapMsg.setInt("id",1);
...
//ObjectMessage   
  1. 发送消息
producer.send(msg);
  1. 关闭连接
producer.close();
session.close();
connection.close();
4.2.2 创建消息消费者
  1. 创建ConnectionFactory
ConnectionFactory factory = new ActiveMQConnectionFactory(null,null,"tcp://localhost:61616");
  1. 创建Connection**并调用start方法**
Connection connection = factory.createConnection();
connection.start();
  1. 创建session
//参数1,是否开启事务
//参数2.客户端签收消息的方式
Session session = connection.ceateSession(false,Session.AUTO_ACKNOWLEDGE);
  1. 创建Destination 获取队列上的消息
//如果消息队列没有名字为queue的队列,则会自动创建一个queue消息
//这里的参数名称对应上边生产者填写的参数一致
Queue queue = session.createQueue("queue");
Topic topic = session.createTopic("topic");
  1. 创建消费者
MessageConsumer consumer = session.createConsumer(queue);
MessageConsumer csm = session.createConsumer(topic);
  1. 消费消息

当创建session的时候设置为手动签收的情况下,在消费完消息之后可以选择性的手动签收消息

msg.acknowledge();
//此处的消息可以是其他的版本
//如MapMessage等·
Message message = consumer.receive();
if(message instanceof TextMessage){
    TextMessage msg = (TextMessage)message;
    System.out.println("消息是:"+ msg.getText());
    //客户端手动签收消息
    //msg'.acknowledge();
}

如果是Topic的话

如果是topic 的话,就要创建一个消息监听器,来消费消息

//普通版本
consumer.setMessageListener(new MessageListener() {
    @Override
    public void onMessage(Message message) {
		//todo
    }
});
//lambda表达式
consumer.setMessageListener(message -> {
    if(message instanceof MapMessage){
        MapMessage msg = (MapMessage) message;
        try {
            System.out.println(msg.getInt("id") + msg.getString("topic"));
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
});
  1. 关闭连接
consumer.close();
session.close();
connection.close();

4.2 java版本的

4.2.1 p2p[点对点]
  1. 创建生产者
package com.etoak.p2p;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

public class HelloProducer {

	public static void main(String[] args) throws JMSException {
		// 1. 创建ConnectionFactory
		ConnectionFactory factory = new ActiveMQConnectionFactory(//
				null, null, "tcp://localhost:61616");

		// 2. 创建Connection, 并且调用start()方法
		Connection connection = factory.createConnection();
		connection.start();

		// 3. 创建Session
		// 参数一:是否开启事务
		// 参数二:客户端签收消息的方式
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

		// 4. 创建Destination, 如果不创建的队列不存在,那么自动创建
		Queue helloQueue = session.createQueue("hello");

		// 5. 创建生产者
		MessageProducer producer = session.createProducer(helloQueue);

		// 6. 创建消息
		for (int i = 0; i < 10; i++) {
			TextMessage msg = session.createTextMessage("这是第" + i + "条消息");
			// 7. 发送消息
			producer.send(msg);
		}

		producer.close();
		session.close();
		connection.close();
		System.out.println("发送结束");
	}
}
  1. 创建消费者
package com.yanyuyu.queue;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class HelloConsumer {
    public static void main(String[] args) throws JMSException {
        // 1.创建ConnectionFactory
        ConnectionFactory factory = new ActiveMQConnectionFactory(null,null,"tcp://localhost:61616");
        // 2.创建Connection
        Connection connection = factory.createConnection();
        connection.start();
        // 3.创建session
        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
        // 4.创建队列
        Queue hello = session.createQueue("hello");
        // 5.创建消费者
        MessageConsumer consumer = session.createConsumer(hello);
        // 6.消费消息
        Message message = consumer.receive();
        if(message instanceof TextMessage){
            TextMessage msg = (TextMessage) message;
            System.out.println(msg.getText());
        }
        // 7.关闭连接
        consumer.close();
        session.close();
        connection.close();
    }
}
4.2.2 Topic [发布/订阅]

发布此类消息的时候必须要有一个消费者在线,否则是网页端是显示不了订阅消息的

  1. 创建消息生产者
package com.yanyuyu.topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class TopicProducer {

    public static void main(String[] args) throws JMSException {
        // 1.创建ConnectionFactory
        ConnectionFactory factory =
                new ActiveMQConnectionFactory(null, null, "tcp://localhost:61616");
        // 2.创建Connection
        Connection connection = factory.createConnection();
        connection.start();

        // 3.创建session
        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);

        // 4.创建队列
        Topic firstTopic = session.createTopic("firstTopic");
        // 5.创建消息
        MapMessage mapMessage = session.createMapMessage();
        mapMessage.setString("topic","firstTopic");
        mapMessage.setInt("id",1);

        // 6.创建生产者
        MessageProducer producer = session.createProducer(firstTopic);

        // 7.发送消息
        producer.send(mapMessage);

        // 8.关闭连接
        producer.close();
        session.close();
        connection.close();
    }
}
  1. 创建消息消费者
package com.yanyuyu.topic;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class TopicConsumer {

    public static void main(String[] args) throws JMSException {
        //1.创建ConnectionFactory
        ConnectionFactory factory = new ActiveMQConnectionFactory(null, null, "tcp://localhost:61616");
        //2.创建connection 并调用start方法
        Connection connection = factory.createConnection();
        connection.start();
        //3.创建session
        Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
        //4.创建destination ,获得消息队列
        Topic firstTopic = session.createTopic("firstTopic");
        //5.创建消费者
        MessageConsumer consumer = session.createConsumer(firstTopic);

        //6.创建消息监听器
        consumer.setMessageListener(message -> {
            if(message instanceof MapMessage){
                MapMessage msg = (MapMessage) message;
                try {
                    System.out.println(msg.getInt("id") + msg.getString("topic"));
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        });

    }
}

4.2.3 selector选择器
  1. 创建生产者
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

public class SelectorProducer {
	public static void main(String[] args) throws JMSException {
		ConnectionFactory factory = new ActiveMQConnectionFactory(//
				null, null, "tcp://localhost:61616");
		Connection conn = factory.createConnection();
		conn.start();

		// 修改客户端消息签收方式为手动签收
		Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);

		Queue queue = session.createQueue("selector");
		MessageProducer producer = session.createProducer(queue);
		// 设置消息持久化
		producer.setDeliveryMode(DeliveryMode.PERSISTENT);

		// 创建两条消息
		TextMessage msg = session.createTextMessage();
		msg.setText("趵突泉北路6号");

		// 设置选择器
		msg.setIntProperty("age", 11);
		msg.setStringProperty("name", "etoak");

		TextMessage msg2 = session.createTextMessage();
		msg2.setText("山大路数码港大厦");

		// 设置选择器
		msg2.setIntProperty("age", 2);
		msg2.setStringProperty("name", "etoak");

		producer.send(msg);
		producer.send(msg2);

		producer.close();
		session.close();
		conn.close();
		System.out.println("发送完成");

	}
}
  1. 创建消费者
import java.util.concurrent.TimeUnit;

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

import org.apache.activemq.ActiveMQConnectionFactory;

public class SelectorConsumer {
	public static void main(String[] args) throws JMSException {

		// 1. 创建ConnectionFactory
		ConnectionFactory factory = new ActiveMQConnectionFactory(//
				null, null, "tcp://localhost:61616");

		// 2. 创建Connection, 并且调用start()方法
		Connection connection = factory.createConnection();
		connection.start();

		// 3. 创建Session
		// 参数一:是否开启事务
		// 参数二:客户端签收消息的方式
		Session session = connection.createSession(false, //
				Session.CLIENT_ACKNOWLEDGE);

		// 4. 创建Destination, 是为了获取队列上的消息
		Queue helloQueue = session.createQueue("selector");

		// 5. 创建消费者
		MessageConsumer consumer = session.createConsumer(helloQueue, //
				"age = 11 and name = 'etoak' ");

		consumer.setMessageListener(message -> {

			if (message instanceof TextMessage) {
				TextMessage text = (TextMessage) message;
				try {
					System.out.println(text.getText().toString());
					
					// 手动签收消息,通知队列删除消息
					text.acknowledge();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		});

	}
}

4.3 activemq 整合 springmvc

这个工程全部采用配置文件的方式创建,容器没有相关的xml 文件

4.3.1 配置容器
  1. 创建容器代替web.xml
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import com.etoak.config.AppConfig;
import com.etoak.config.RootConfig;

public class Initializer extends AbstractAnnotationConfigDispatcherServletInitializer{
	
    //spring 容器
	@Override
	protected Class<?>[] getRootConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {RootConfig.class};
	}
	/**
	 * spring mvc 容器
	 */
	@Override
	protected Class<?>[] getServletConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {AppConfig.class};
	}

	@Override
	protected String[] getServletMappings() {
		// TODO Auto-generated method stub
		return new String[] {"/"};
		
	}
	
}

  1. 根据上边填写的内容创建spring容器 RootConfig.java
import java.io.IOException;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import com.alibaba.druid.pool.DruidDataSource;

//添加Configuration
@Configuration
@ComponentScan(basePackages = { "com.etoak" }, //
		excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, //
				classes = { Controller.class, RestController.class, //
						ControllerAdvice.class, EnableWebMvc.class }) }, //
		includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, //
				classes = { Service.class, Repository.class }) })
@PropertySource(value="classpath:jdbc.properties")
@MapperScan(basePackages = {"com.etoak.mapper"}) //<MapperScannerConfigu..>
@EnableTransactionManagement //相当于 <tx:annotation-driven>
@Import(ActiveMQConfig.class)
public class RootConfig {
	
	@Value("${m.driver}")
	private String driver;
	
	@Value("${m.url}")
	private String url;
	
	@Value("${m.username}")
	private String username;
	
	@Value("${m.pwd}")
	private String password;
	
	@Bean
	public DataSource dataSource() {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName(this.driver);
		dataSource.setUrl(this.url);
		dataSource.setUsername(this.username);
		dataSource.setPassword(this.password);
		return dataSource;
	}
	
	@Bean
	public SqlSessionFactoryBean sessionFactoryBean() {
		SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
		factory.setDataSource(this.dataSource());
		factory.setTypeAliasesPackage("com.etoak");
		
		PathMatchingResourcePatternResolver resovler = new PathMatchingResourcePatternResolver();
		try {
			factory.setMapperLocations(resovler.getResources("classpath:/mappers/*.xml"));
		}catch(IOException e) {
			e.printStackTrace();
		}
		return factory;
	}
	
	@Bean
	public DataSourceTransactionManager tx() {
		return new DataSourceTransactionManager(this.dataSource());
	}
	
	
}
  1. 配置activeMQ
package com.etoak.config;

import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.core.JmsTemplate;

/**
 * spring 整合 ActiveMQ
 */
@Configuration
public class ActiveMQConfig {

	@Bean
	public ActiveMQConnectionFactory mqFactory() {
		ActiveMQConnectionFactory factory = new//
		ActiveMQConnectionFactory(null, null, "tcp://localhost:61616");
		// 是否允许异步发送
		factory.setUseAsyncSend(true);
		return factory;
	}

	@Bean
	public ConnectionFactory connectionFactory() {
		CachingConnectionFactory factory = new CachingConnectionFactory();
		factory.setTargetConnectionFactory(this.mqFactory());
		// 缓存session
		factory.setSessionCacheSize(20);
		return factory;
	}

	@Bean
	public JmsTemplate jmsTemplate() {
		JmsTemplate jmsTemplate = new JmsTemplate();
		jmsTemplate.setConnectionFactory(this.connectionFactory());
		// QOS values (持久化, 优先级, 消息生存时间)
		jmsTemplate.setExplicitQosEnabled(true);
		// 设置持久化
		jmsTemplate.setDeliveryMode(DeliveryMode.PERSISTENT);

		// 设置客户端签收消息模式
		jmsTemplate.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
		return jmsTemplate;
	}
}

4.4 将activemq消息持久化到数据库

X.错误集锦

  1. java.lang.AbstractMethodError: org.apache.activemq.ActiveMQConnection.createSession()Ljavax/jms/Session;

引发原因:创建session的时候没有填写相应的参数

解决办法:创建session的时候添加相关参数

Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值