ActiveMQ
Mq的分类
- kafka
- RabbitMQ
- RocketMQ
- ActiveMQ

为什么要使用MQ
现在让我们来假设一个场景
许多学生要向教师询问问题,学生们有问题了,就会排成队
MQ特性
解耦,异步,削峰
LInux安装软件
-
windows下载安装包 tar,用ftp软件传到linux的opt上
-
在opt目录下解压
tar -zxvf 文件名 -
在根目录下新建一个myactivemq目录
mkdir myactivemq
-
将解压后的文件复制到myactivemq中
#是在opt目录下,即解压文件所在的目录下进行复制 cp -r apache-activemq-5.15.12 /myactivemq/ -
进入到/myactivemq/apache-activemq-5.15.12 /bin,启动相应文件即可
[root@iZm5edj71ir6mgk9ys4b4fZ bin]# pwd /myactivemq/apache-activemq-5.15.12/bin [root@iZm5edj71ir6mgk9ys4b4fZ bin]# ll total 144 -rwxr-xr-x 1 root root 21535 Apr 18 16:44 activemq -rwxr-xr-x 1 root root 6189 Apr 18 16:44 activemq-diag -rw-r--r-- 1 root root 16405 Apr 18 16:44 activemq.jar -rw-r--r-- 1 root root 5607 Apr 18 16:44 env drwxr-xr-x 2 root root 78 Apr 18 16:44 linux-x86-32 drwxr-xr-x 2 root root 78 Apr 18 16:44 linux-x86-64 drwxr-xr-x 2 root root 82 Apr 18 16:44 macosx -rw-r--r-- 1 root root 83820 Apr 18 16:44 wrapper.jar [root@iZm5edj71ir6mgk9ys4b4fZ bin]# ./activemq start -
activemq的默认端口是61616,查询activemq是否启动
#第一种查询方法 [root@iZm5edj71ir6mgk9ys4b4fZ bin]# ps -ef|grep activemq|grep -v grep root 27892 1 1 16:47 pts/0 00:00:06 /usr/bin/java -Xms64M -Xmx1G -Djava.util.logging.config.file=logging.properties -Djava.security.auth.login.config=/myactivemq/apache-activemq-5.15.12//conf/login.config -Dcom.sun.management.jmxremote -Djava.awt.headless=true -Djava.io.tmpdir=/myactivemq/apache-activemq-5.15.12//tmp -Dactivemq.classpath=/myactivemq/apache-activemq-5.15.12//conf:/myactivemq/apache-activemq-5.15.12//../lib/: -Dactivemq.home=/myactivemq/apache-activemq-5.15.12/ -Dactivemq.base=/myactivemq/apache-activemq-5.15.12/ -Dactivemq.conf=/myactivemq/apache-activemq-5.15.12//conf -Dactivemq.data=/myactivemq/apache-activemq-5.15.12//data -jar /myactivemq/apache-activemq-5.15.12//bin/activemq.jar start #第二种查询方法 [root@iZm5edj71ir6mgk9ys4b4fZ bin]# netstat -anp|grep 61616 tcp6 0 0 :::61616 :::* LISTEN 27892/java #第三种查询方法 [root@iZm5edj71ir6mgk9ys4b4fZ bin]# lsof -i:61616 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 27892 root 132u IPv6 545080 0t0 TCP *:61616 (LISTEN) -
带日志方式的启动
[root@iZm5edj71ir6mgk9ys4b4fZ bin]# ./activemq start > /myactivemq/myrunmq.log
ActiveMQ
两大模式特性
队列和主题,统称为目的地
-
在点对点对的消息传送域中,目的地被称为队列

-
在发布订阅的消息传送域中,目的地被称为主题

队列

主题

主题和队列的区别

连接到ActiveMQ的管理界面
注意:activemp的那个后台管理界面不是默认的服务端口61616,而是8161
-
首先启动activemq服务
[root@iZm5edj71ir6mgk9ys4b4fZ bin]# ./activemq start #注意:在activemq的bin目录下启动 -
浏览器访问 Ip地址:8161
我的是这样的
http://47.104.109.159:8161/正确访问出现如下界面

-
点击以下内容,会提示输入账号和密码,默认账号密码都为admin

-
正确进入后
-

java向activemq队列中写入数据
生产者
package com.sun.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProducer {
public static final String ACTIVEMQ_URL="tcp://47.104.109.159:61616";
public static final String QUEUE_NAME="queue";
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
//两个参数,第一个是事务,第二个是签收机制
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(QUEUE_NAME);
MessageProducer producer = session.createProducer(queue);
for (int i = 0; i < 3; i++) {
TextMessage textMessage = session.createTextMessage("创建了第" + i++ + "个消息");
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("消息发送到MQ完成");
}
}
消费者
package com.sun.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumer {
public static final String ACTIVEMQ_URL="tcp://47.104.109.159:61616";
public static final String QUEUE_NAME="queue";
public static void main(String[] args) throws JMSException, IOException {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
//两个参数,第一个是事务,第二个是签收机制
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(QUEUE_NAME);
//创建消费者
MessageConsumer consumer = session.createConsumer(queue);
//同步阻塞方法receive()
//订阅者通过接口调用消费者的resive方法来接收消息,receive方法能够在接收到消息之前
//(或超时之前)会一直阻塞
// while (true)
// {
// //生成的数据类型要和接收的数据类型一致
// TextMessage message = (TextMessage) consumer.receive(4000L);
// if(message!=null)
// {
// System.out.println("消费者接收到消息"+message.getText());
// }
// else {
// break;
// }
// }
// consumer.close();
// session.close();
// connection.close();
//第二种:通过消息监听的方式来消费消息
//异步非阻塞监听
//默认采用轮询分发的方式来消费消息
//例如:当已经有两个消费者的时候,这时候生产六条消息,会被两个消费者平均消费掉
//1号消费者消费代号为1,3,5消息,2号消费者消费代号为2,4,6的消息
consumer.setMessageListener(new MessageListener()
{
@Override
public void onMessage(Message message) {
if(null!=message&&message instanceof TextMessage)
{
TextMessage textMessage=(TextMessage)message;
try {
System.out.println("消费者接收到消息"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
//保证控制台不消失
System.in.read();
consumer.close();
session.close();
connection.close();
}
}
主题
注意:先订阅再发布才可以正常发送消息!先发布再订阅的话,消息是废消息,订阅者接收不到订阅时间之前发布的消息
生产者
package com.sun.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProducer_topic {
public static final String ACTIVEMQ_URL="tcp://47.104.109.159:61616";
public static final String TOPIC_NAME="topic";
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
//两个参数,第一个是事务,第二个是签收机制
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(TOPIC_NAME);
MessageProducer producer = session.createProducer(topic);
for (int i = 1; i <= 3; i++) {
TextMessage textMessage = session.createTextMessage("创建了第" + i + "个Topic消息");
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("Topic消息发送到MQ完成");
}
}
消费者
package com.sun.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumer_topic {
public static final String ACTIVEMQ_URL="tcp://47.104.109.159:61616";
public static final String TOPIC_NAME="topic";
public static void main(String[] args) throws JMSException, IOException {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
//两个参数,第一个是事务,第二个是签收机制
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(TOPIC_NAME);
//创建消费者
MessageConsumer consumer = session.createConsumer(topic);
//同步阻塞方法receive()
//订阅者通过接口调用消费者的resive方法来接收消息,receive方法能够在接收到消息之前
//(或超时之前)会一直阻塞
// while (true)
// {
// //生成的数据类型要和接收的数据类型一致
// TextMessage message = (TextMessage) consumer.receive(4000L);
// if(message!=null)
// {
// System.out.println("消费者接收到消息"+message.getText());
// }
// else {
// break;
// }
// }
// consumer.close();
// session.close();
// connection.close();
//第二种:通过消息监听的方式来消费消息
//异步非阻塞监听
//默认采用轮询分发的方式来消费消息
//例如:当已经有两个消费者的时候,这时候生产六条消息,会被两个消费者平均消费掉
//1号消费者消费代号为1,3,5消息,2号消费者消费代号为2,4,6的消息
consumer.setMessageListener((message)->{
if(null!=message&&message instanceof TextMessage)
{
TextMessage textMessage=(TextMessage)message;
try {
System.out.println("消费者接收到消息"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
//保证控制台不消失
System.in.read();
consumer.close();
session.close();
connection.close();
}
}
spring整合ActiveMQ
1. 导入依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-all -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.xbean/xbean-spring -->
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>4.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jms -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-pool -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.15.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aspectj/aspectjrt -->
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
2. 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--设置扫描的包-->
<context:component-scan base-package="com.sun.spring"/>
<!--设置连接ActiveMQ的服务器的地址-->
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://47.104.109.159:61616"/>
</bean>
</property>
<property name="maxConnections" value="100"/>
</bean>
<!--设置连接ActiveMQ的连接模式为队列-->
<bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="spring-active-queue"/>
</bean>
<!--设置连接ActiveMQ的连接模式为主题-->
<bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="spring-active-topic"/>
</bean>
<!--spring提供的jms的工具类,它可以进行消息的发送、接收等-->
<bean id="jmdTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactory"/>
<!--队列还是主题,只需要在这里设置接口 ref引用的那个id里面的类是对应的队列或者主题即可-->
<property name="defaultDestination" ref="destinationTopic"/>
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter"/>
</property>
</bean>
</beans>
3. 生产者
注意:主题的话,先启动消费者,再启动生产者
package com.sun.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import javax.jms.*;
@Service
public class springMQ_producer {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
springMQ_producer producer= (springMQ_producer) context.getBean("springMQ_producer");
producer.jmsTemplate.send(new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage textMessage = session.createTextMessage("spring-active");
return textMessage;
}
});
System.out.println("send over");
}
}
4. 消费者
package com.sun.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
@Service
public class springMQ_consumer {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
springMQ_consumer consumer= (springMQ_consumer) context.getBean("springMQ_consumer");
String retValue = (String) consumer.jmsTemplate.receiveAndConvert();
System.out.println("消费者收到的消息"+retValue);
}
}
5. 消息模型
不管是使用队列还是主题,只需要在配置文件中改就可以了,生产者和消费者不需要要改变
6. 实现不启动消费者
不启动消费者,直接通过配置监听完成
1. 加入配置文件
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory"/>
<!--与id为jmdTemplate中的name为defaultDestination的 ref一致即可-->
<property name="destination" ref="destinationTopic"/>
<property name="messageListener" ref="myMessageListener"/>
</bean>
2. 加入一个类继承MessageListener接口
package com.sun.spring;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@Component
public class myMessageListener implements MessageListener {
@Override
public void onMessage(Message message) {
if(null!=message&& message instanceof TextMessage)
{
TextMessage textMessage= (TextMessage) message;
try {
System.out.println(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
3. 直接启动生产者即可
MQ命令
./activemq start
./activemq restart
./activemq stop
#查询activemq是否启动的三种方式
lsof -i:61616
netstat -anp|grep 61616
ps -ef|grep activemq|grep -v grep
#关闭防火墙
service iptables status
service iptables start
service iptables stop
JMS
JMS开发步骤

什么是javaEE

什么是JMS

jMS的组成和结构
- 生产者
- 消费者
- 消息
- mq
Message
消息头
常用的属性
JMSDestination
分为队列和属性
JMSDeliveryMode

JMSException

JMSPriority

JMSMessageID
消息属性
消息属性是什么?

如果需要去除消息头字段以外的值,可以使用消息属性
识别/去重/重点标注等操作非常有用的方法
消息体
消息体:封装具体的消息数据
注意:发送的消息和接收的消息,消息格式必须相同
五种消息格式
- TestMessage
- MapMessage
- BytesMessage
- StreamMessage
- ObjectMessage
JMS的可靠性
持久性
默认是持久化

队列的持久性

主题的持久性
如果消费者已经订阅了,即使消费者宕机了,也会等消费者重新上线之后,将消息发送过去
生产者
package com.sun.activemq;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProducer_topic {
public static final String ACTIVEMQ_URL="tcp://47.104.109.159:61616";
public static final String TOPIC_NAME="topic";
public static void main(String[] args) throws JMSException {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
//两个参数,第一个是事务,第二个是签收机制
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(TOPIC_NAME);
MessageProducer producer = session.createProducer(topic);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
//改动的地方
connection.start();
for (int i = 1; i <= 3; i++) {
TextMessage textMessage = session.createTextMessage("创建了第" + i + "个Topic消息");
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("Topic消息发送到MQ完成");
}
}
事务transaction
事务偏生产者,签收偏消费者
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
生产者
- false,直接用send提交即可
- true,在send提交之后,要用session.commit提交才行
消费者
- false,直接可以消费
- true,必须用session.commit提交,否则会出现消息被重复消费的情况
签收
非事务情况下的签收
- 自动签收(默认) AUTO_ACKNOWLEDGE
- 手动签收
- CLIENT_ACKNOWLEDGE
- 客户端调用acknowledge方法手动签收
- 允许重复消息 DUPS_OK_ACKNOWLEDGE
事务
- 生产事务开启,只有commit后才能将全部消息变为已消费;否则消息会被重复消费
- 消费生产者
- 消费消费者
签收和事务的关系
- 在事务性会话中,当一个事务被成功提交则消息被自动签收,如果事务回滚,则消息会被再次消费
- 非事务性会话中,消息何时被确认取决于创建会话时的应答模式(acknowledge mode)
点对点总结

发布订阅总结

消息队列的比较

发布订阅
Broker
是什么
相当于一个ActiveMQ的服务器实例
说白了,Broker其实就是用代码的形式启动ActiveMQ将MQ嵌入到java代码中,以便随时用随时启动,在用的时候再去启动这样能节省了资源,也保证了可靠性
配置多个实例
// activemq/conf目录下
[root@iZm5edj71ir6mgk9ys4b4fZ conf]# cp activemq.xml activemq02.xml
// activemq/bin目录下
./activemq start xbean:file:/myactivemq/apache-activemq-5.15.12/conf/activemq02.xml
代码实现
注意:需要到导入一个这样的包,否则可能会报错
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
public class EmbedBroker {
public static void main(String[] args) throws Exception {
BrokerService brokerService = new BrokerService();
brokerService.setUseJmx(true);
brokerService.addConnector("tcp://localhost:61616");
brokerService.start();
}
}
生产者和消费者正常实现,只需要访问地址改为
tcp://localhost:61616即可

浙公网安备 33010602011771号