29_异步消息_01_JMS
- 异步消息主要目的是为了系统与系统之间的通信。所谓异步详细即消息发送者无须等待消息接收者的处理及返回,甚至无须关系消息是否发送成功(类似UDP协议)
- 在异步消息中有两个很重要的概念,及消息代理(message broker)和目的地(destination)
- 当消息发送者发送消息后,消息将由消息代理接管,消息代理保证消息传递到指定的目的地
- 异步消息主要有两种形式的目的地:队列(queue)和主题(topic)
- 队列用于点对点式(point-to-point)的消息通信
- 主题用于发布/订阅式(public/subscribe)的消息通信
点对点式(point-to-point)
- 当消息发送者发送消息,消息代理获得消息后,将消息放进一个队列(queue)里,当有消息接收者来接收这条消息时,消息将从队列里取出来传递给接收者,这时候队列里就没有了这条消息。
- 点对点确保的是每一条消息只有唯一的发送者和接收者,但这并不能说明只有一个接收者可以从队列了接收消息,因为队列中有很多消息(不同的发送者发送的消息都会被保存再这个队列中),点对点式只能保证每一条消息只有唯一的发送者和接收者。
发布/订阅(publish/subscribe)
- 消息发送者发送消息到主题(topic),而多个消息接收者监听这个主题。此时的消息发送者和接收者分别叫做发布者和订阅者
1,企业级消息代理
- JMS(Java Message Service)即Java消息服务,是基于 JVM 消息代理的规范
- ActiveMQ,HornetQ 是一个 JMS 消息代理的实现
- AMQP(Advanced 高级 Message 消息 Queuing 队列 Protocol 协议)也是一个消息代理的规范,它不仅兼容 JMS,还支持跨语言和平台。
- AMQP 的主要实现有 RabbitMQ
2,Spring 的支持
- Spring 为 JMS 和 AMQP 的支持分别来自于
spring-jms
和spring-rabbit
- 它们分别需要
ConnectionFactory
的实现来连接消息代理,并分别体统了JmsTemplate
、RabbitTemplate
来发送消息 - Spring 为
JMS
、AMQP
提供了@JmsListener
、@RabbbitListener
注解在方法上监听消息代理发布的消息 - 通过
@EnableJMS
和@EnableRabbit
来开启支持
3,SpringBoot 的支持
- 提供了自动配置:
org.springframework.boot.autoconfigure.jms
下 - 支持JMS 的实现: ActiveMQ,HornetQ,Artemis(由HornetQ捐赠给ActiveMQ的代码库形成的 ActiveMQ 的子项目)
- Spring Boot 为我们定义了
ActiveMQConnectionFactory
的Bean
作为连接,并通过spring.activemq
为前缀的属性来配置ActiveMQ
的连接属性
#消息代理的地址
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=
spring.activemq.password=
spring.activemq.in-memory=true
spring.activemq.pool.enabled=false
- Spring Boot 在
JmsAutoConfiguration
为我们配置好了JmsTemplate
,且为我们开启了注解式消息监听的支持,即自动开启@EnableJms
- Spring Boot 对
AMQP
的自动配置支持位于org.springframeword.boot.autoconfigure.amqp
下,它为我们配置了连接的ConnectionFactory
和RabbitTemplate
,且为我们开启了注解式消息监听,即自动开启@EnableRabbit
。 RabbitMQ
的配置可通过spring.rabbitmq
来配置RabbitMQ
#rabbitmq 服务器地址,默认为localhost
#spring.rabbitmq.host=localhost
#rabbitmq端口,默认为5672
#spring.rabbitmq.port5672
#spring.rabbitmq.username=
#spring.rabbitmq.password=
4,docker下运行 ActiveMQ
镜像为容器
- 61616 :消息代理的端口
- 8161 :ActiveMQ 的管理界面端口
开启虚拟机对上述两个端口的端口映射后,重启虚拟机(VMware Fusion Mac版本)
docker run -d -p 61616:61616 -p 8161:8161 cloudesire/activemq
5,配置好后,浏览器访问 localhost:8161
端口,访问ActiveMQ的管理界面
默认账户密码为:admin/admin
6,登录成功
7,新建Spring Boot项目
创建时无需勾选任何依赖。
手动在pom.xml中添加 spring-jms
和 activemq-client
的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
</dependency>
8,在 application.properties
中配置消息代理地址
spring.activemq.broker-url=tcp://localhost:61616
实际情况下,消息的发布者和接收者一般都是分开的,而这里我们为了演示简单,将消息发送者和接收者放在同一个程序里。
9,消息定义
package com.zyf;
import org.springframework.jms.core.MessageCreator;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
/**
* Created by zyf on 2018/3/15.
*/
public class Msg implements MessageCreator {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("zyf:hello");
}
}
10,消息发送及定义目的地
package com.zyf;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jms.core.JmsTemplate;
/**
* 实现CommandLineRunner,重写的run方法
* 会在程序启动后执行
* 这样当程序启动后,就会执行run,向目的地发送消息
*/
@SpringBootApplication
public class Springboot19Application implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(Springboot19Application.class, args);
}
/**
* 注入Spring Boot为我们配置好的jmsTemplate
*/
@Autowired
JmsTemplate jmsTemplate;
@Override
public void run(String... strings) throws Exception {
//通过jmsTemplate的send方法向:my-destination 目的地发送一个Msg消息
//此时就会在消息代理上定义了一个目的地叫 my-destination
jmsTemplate.send("my-destination",new Msg());
}
}
11,消息监听
package com.zyf;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
/**
* Created by zyf on 2018/3/15.
*/
@Component
public class Receive {
/**
* 注解@JmsListener是Spring 4.1提供的新特性,用来简化JMS开发
* 只需通过destination属性指定要监听的目的地,即可接收到该目的地的消息
* @param message
*/
@JmsListener(destination = "my-destination")
public void receiveMessage(String message){
System.out.println("接收到:<"+message+">");
}
}