目录
作用
- 解决系统间的耦合性
- 异步调用
- 减轻服务提供方在流量高峰的压力,削峰
环境
./activemq start > /myactiveMQ/myrunmq.log 带有日志启动
http://192.168.56.10:8162/admin admin/admin
概念
JMS
java message service java消息服务的规范, 类似jdbc, 有各种实现的规范软件, 规范中定义了jms有4部分组成
1.生产者
2.消费者
3.目的地Destination 父类消息容器 -> 实现类 队列queue/主题topic
4.服务端
消息中间件
rabbitMQ(高并发) | ActiveMQ (老牌用户多) |
RocketMQ(阿里产品) | kafka(高吞吐,大数据)
RabbitMQ由基于erlang, 使用amqp协议
*AMQP与JMS区别
JMS | AMQP |
---|---|
操作的规范接口 | 传输协议 |
必须使用java | 跨语言 |
2种消息模型 | 多种消息模型 |
java操作
pom
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-broker</artifactId>
<version>5.14.5</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.14.5</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-spring</artifactId>
<version>5.14.5</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-jms-client</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
队列点对点模式
生产者
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
//生产者
public class ProducerTest {
public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory =
new ActiveMQConnectionFactory(
"admin",
"admin",
"tcp://192.168.56.10:61616");
// 获取connection
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
//创建session 事务,接收模式
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建队列
Destination destination = session.createQueue("queue01");
// 创建生产者
MessageProducer producer = session.createProducer(destination);
for (int i = 0; i < 3; i++) {
TextMessage textMessage = session.createTextMessage("msg from queue" + i);
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
}
}
消费者
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class ConsumerTest {
public static void main(String[] args) {
//创建连接工厂
ActiveMQConnectionFactory activeMQConnectionFactory =
new ActiveMQConnectionFactory(
"admin",
"admin",
"tcp://192.168.56.10:61616");
// 获取connection
Connection connection = null;
Session session = null;
MessageConsumer consumer = null;
try {
connection = activeMQConnectionFactory.createConnection();
//
connection.start();
//创建session 事务,接收模式
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建队列
Destination destination = session.createQueue("queue01");
consumer = session.createConsumer(destination);
while(true){
TextMessage receive = (TextMessage) consumer.receive(4000);
if (null!=receive){
System.out.println("消费者收到的消息为:"+receive.getText());
}else{
break;
}
}
} catch (JMSException e) {
e.printStackTrace();
} finally {
try {
consumer.close();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
执行完代码后观察mq控制台变化
connection.start();
内部用了原子类自旋检查 , 如果调用这个方法, 消费者的excutor不会开始
consumer.receive()
参数中可以设置阻塞时间, 默认不超时, 一直接受消息
消费者也可以使用监听器模式
//消费者也可以使用监听器模式
consumer.setMessageListener(message -> {
if(null!=message&&message instanceof TextMessage){
TextMessage message1 = (TextMessage) message;
try {
System.out.println("消费者收到的消息为:"+message1.getText());
} catch (Exception e) {
e.printStackTrace();
}
}
});
//阻塞 等待控制台输入
System.in.read();
特点:
消息会被保存
消息阻塞接收/异步获取,消息生产后被消费的时间不确定
同一消息只会被消费一次
多个消费者时,采用负载均衡模式,不影响性能
主题topic一对多模式
消费者和生产者的队列的代码中, 改动此行创建队列即可
Destination destination = session.createTopic("topic01");
特点:
消息不会存储,消息生产即消费
一条消息被多个消费者消息
消费者数量多时,影响性能
配置
消息头
生产者发送消息时的参数代表了消息头设置
1.deliveryMode 持久非持久
- 非持久的消息在服务关闭后即清除, 持久化的消息会在服务器上产生临时文件保存
- 默认持久
2.priority 优先级 高级/普通级 消费者优先消费 默认普通级别
3.timeToLive 发送过期时间, 有效期内未发送到服务端将被丢弃
4.JMSMessageID MQ创建的唯一标识 uuid
消息体
textMessage 文本
MapMessage 键值对
byte 字节
object 对象
stream 流消息
属性
键值对, 增加对消息的标识度
持久化
生产者
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
消费者
connection = activeMQConnectionFactory.createConnection();
connection.setClientID("shadow");
//创建session 事务,接收模式
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建队列
Topic topic = session.createTopic("topic_Persist01");
TopicSubscriber durableSubscriber = session.createDurableSubscriber(topic, "topic_Persist01_durableSubscriber01");
connection.start();
while(true){
TextMessage message = (TextMessage)durableSubscriber.receive();
if (null!=message){
System.out.println("消费者收到的消息为:"+message.getText());
}else{
break;
}
}
生产者持久化消息, 消费者订阅topic后, 即使消费者关闭, 生产者发布到话题的消息仍会保存, 等到消费者上线, 会接收到话题中的消息
事务
生产者开启事务后要手动提交, 类似jdbc
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
...
session.commit();
消费者开启事务并获取到消息后, 如果不提交, 将会出现重复消费的情况, 及时消息获取到, 队列中的消息仍然没有出列
签收
1.自动签收 AUTO_ACKNOWLEDGE
2.客户端签收 CLIENT_ACKNOWLEDGE
message.acknowledge();
如果客户端开启了手动签收, 但是没有手动签收, 消息将不会出列, 仍会在队列中
3. 重复签收 DUPS_OK_ACKNOWLEDGE
事务与签收
当有事务的情况下, 一旦提交了, 即使开启了客户端签收, 客户端没有签收, 仍然能保证不会重复消费
事务的优先度大于签收
spring整合
pom
<dependencies>
<!-- activemq核心依赖包 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.10.0</version>
</dependency>
<!-- 嵌入式activemq的broker所需要的依赖包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.1</version>
</dependency>
<!-- activemq连接池 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.15.10</version>
</dependency>
<!-- spring支持jms的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<!--spring相关依赖包-->
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>4.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<!-- Spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.23.RELEASE</version>
</dependency>
</dependencies>
xml
<?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://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启包的自动扫描 -->
<context:component-scan base-package="com.activemq.demo"/>
<!-- 配置生产者 -->
<bean id="connectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<!-- 正真可以生产Connection的ConnectionFactory,由对应的JMS服务商提供 -->
<bean class="org.apache.activemq.spring.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.10.130:61616"/>
</bean>
</property>
<property name="maxConnections" value="100"/>
</bean>
<!-- 这个是队列目的地,点对点的Queue -->
<bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
<!-- 通过构造注入Queue名 -->
<constructor-arg index="0" value="spring-active-queue"/>
</bean>
<!-- 这个是队列目的地, 发布订阅的主题Topic-->
<bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="spring-active-topic"/>
</bean>
<!-- Spring提供的JMS工具类,他可以进行消息发送,接收等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 传入连接工厂 -->
<property name="connectionFactory" ref="connectionFactory"/>
<!-- 传入目的地 -->
<property name="defaultDestination" ref="destinationQueue"/>
<!-- 消息自动转换器 -->
<property name="messageConverter">
<bean class="org.springframework.jms.support.converter.SimpleMessageConverter"/>
</property>
</bean>
</beans>
生产者
@Service
public class SpringProduce {
@Autowired
private JmsTemplate jmsTemplate;
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-activemq.xml");
SpringProduce springMQ_producer = applicationContext.getBean(SpringProduce.class);
springMQ_producer.jmsTemplate.send(
new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("***Spring和ActiveMQ的整合case111.....");
}
}
);
System.out.println("********send task over");
}
}
消费者
SpringConsumer springConsumer = applicationContext.getBean(SpringConsumer.class);
String o = (String)springConsumer.jmsTemplate.receiveAndConvert();
System.out.println(o);
topic生产消费者
<property name="defaultDestination" ref="destinationQueue"/>
消费者监听器
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destinationTopic"></property>
<property name="messageListener" ref="myMessageListener"></property>
</bean>
监听器
@Component
public class MyMessageListener implements MessageListener {
@Override
public void onMessage(Message message) {
if (null!=message&& message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
String text = textMessage.getText();
System.out.println(text);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
Springboot整合
queue
生产者工程
pom
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--spring boot整合activemq的jar包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
xml
server:
port: 8080
#工厂
spring:
activemq:
broker-url: tcp://192.168.56.10:61616
password: admin
user: admin
jms:
# false 队列 true 主题
pub-sub-domain: false
myqueue: springboot-acmq
配置类
@Component
@EnableJms
public class ConfigBean {
@Value("${myqueue}")
private String myqueue;
@Bean // bean id="" class="…"
public Queue queue(){
return new ActiveMQQueue(myqueue);
}
}
/*
@WebAppConfiguration("src/main/resources") : 注解在类上,
用来声明加载的ApplicationContex 是一个WebApplicationContext ,
它的属性指定的是Web资源的位置,默认为 src/main/webapp ,
自定义修改为 resource
* */
@SpringBootTest(classes = AcmqSpringbootApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class TestMQ {
@Resource
private QueueProduce queueProduce;
@Test
public void send(){
queueProduce.send();
}
@Scheduled(fixedDelay = 3000)
public void scheduleSend(){
jmsMessagingTemplate.convertAndSend(queue,"schedule mes:"+ UUID.randomUUID().toString().substring(0,6));
}
}
@SpringBootApplication
@EnableScheduling
public class AcmqSpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(AcmqSpringbootApplication.class, args);
}
}
queue消费者工程同生产者 配置类不要了
@Component
public class QueueConsumer {
//jmstemplate监听器
@JmsListener(destination = "${myqueue}")
public void receive(TextMessage textMessage){
try {
System.out.println(textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
topic
与队列类似 , 只贴改动的地方
生产者
server:
port: 8080
#工厂
spring:
activemq:
broker-url: tcp://192.168.56.10:61616
password: admin
user: admin
jms:
# false 队列 true 主题
pub-sub-domain: true
mytopic: springboot-acmq-topic
@Component
public class TopicProduce {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Topic topic;
public void send(){
jmsMessagingTemplate.convertAndSend(topic,"mes:"+ UUID.randomUUID().toString().substring(0,6));
}
@Scheduled(fixedDelay = 3000)
public void scheduleSend(){
jmsMessagingTemplate.convertAndSend(topic,"schedule mes:"+ UUID.randomUUID().toString().substring(0,6));
}
}
@Component
@EnableJms
public class ConfigBean {
@Value("${mytopic}")
private String myTopic;
@Bean // bean id="" class="…"
public Topic topic(){
return new ActiveMQTopic(myTopic);
}
}
消费者
将读取文件的部分改为topic
${mytopic}