种类
-
基本消息队列(BasicQueue)
-
工作消息队列(WorkQueue)
-
发布订阅(Publish Subscribe)
3.1 广播(Fanout Exchange)
3.2 路由(Direct Exchage)
3.3 主题(Topic Exchage)
SpringAMQP
AMQP (Advanced Message Queuing Protocol):在应用程序或之间传递消息的开放标准
SpringAMQP:基于AMQP的一套API规范,提供了模板来发送和接收消息
基本消息队列
只包含三个角色:
publisher: 消息发布者,将消息发布到queue
queue: 存储消息内容
consumer: 负责接受并缓存信息
publisher对应代码:
package cn.itcast.mq.helloworld;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class PublisherTest {
@Test
public void testSendMessage() throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("guest");
factory.setPassword("123");
// 1.2.建立连接
Connection connection = factory.newConnection();
// 2.创建通道Channel
Channel channel = connection.createChannel();
// 3.创建队列
String queueName = "simple.queue";
channel.queueDeclare(queueName, false, false, false, null);
// 4.发送消息
String message = "hello, rabbitmq!";
channel.basicPublish("", queueName, null, message.getBytes());
System.out.println("发送消息成功:【" + message + "】");
// 5.关闭通道和连接
channel.close();
connection.close();
}
}
consumer对应代码:
package cn.itcast.mq.helloworld;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConsumerTest {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("guest");
factory.setPassword("123");
// 1.2.建立连接
Connection connection = factory.newConnection();
// 2.创建通道Channel
Channel channel = connection.createChannel();
// 3.创建队列
String queueName = "simple.queue";
channel.queueDeclare(queueName, false, false, false, null);
// 4.订阅消息
channel.basicConsume(queueName, true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
// 5.处理消息
String message = new String(body);
System.out.println("接收到消息:【" + message + "】");
}
});
System.out.println("等待接收消息。。。。");
}
}
改造基本消息队列
父工程导入AMQP依赖:
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
在publisher和consumer中编写application.yml
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: 123
virtual-host: /
publisher编写测试类:
@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendMsg(){
String name = "simple.queue";
String massage = "hello,amqp!";
rabbitTemplate.convertAndSend(name,massage);
}
}
consumer编写消费逻辑,监听simple.queue:
@Component
public class AmqpListener {
@RabbitListener(queues = "simple.queue")
public void listenerSimple(String msg){
System.out.println(msg);
}
}
工作队列
publisher发送50条信息:
@Test
public void workQueue() throws InterruptedException {
String name = "simple.queue";
String massage = "hello,amqp!__";
for (int i = 0; i < 50; i++) {
rabbitTemplate.convertAndSend(name,massage+i);
Thread.sleep(20);
}
}
创建两个工作队列:
@RabbitListener(queues = "simple.queue")
public void listenerWorker1(String msg) throws InterruptedException {
System.out.println("111111111:"+msg);
Thread.sleep(20);
}
@RabbitListener(queues = "simple.queue")
public void listenerWorker2(String msg) throws InterruptedException {
System.out.println("222222222:"+msg);
Thread.sleep(200);
}
上述消费者将会平均分配消息队列内容,无论处理速度
若需要考虑消费队列处理能力,可设置消费预取限制
listener:
simple:
prefetch: 1
发布、订阅模式
与之前的区别:允许将一个消息发送给多个消费者,实现方式是加入了交换机
将队列1和队列2绑定到交换机
@Configuration
public class FanoutConfig {
// fanout
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("xy.fanout");
}
// fanout.queue1
@Bean
public Queue fanoutQueue1(){
return new Queue("fanout.queue1");
}
// 绑定队伍1到交换机
@Bean
public Binding fanoutBinding1(@Qualifier("fanoutQueue1") Queue fanoutBinding1, FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutBinding1).to(fanoutExchange);
}
// fanout.queue2
@Bean
public Queue fanoutQueue2(){
return new Queue("fanout.queue2");
}
// 绑定队伍2到交换机
@Bean
public Binding fanoutBinding2(@Qualifier("fanoutQueue2")Queue fanoutQueue2, FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
}
}
编写publisher:
@Test
public void sendExchange(){
//交换机
String name = "xy.fanout";
//发送信息
String msg = "hello,fanout";
//发送
rabbitTemplate.convertAndSend(name,"",msg);
}
创建两个consumer:
@RabbitListener(queues = "fanout.queue1")
public void listenerFanout1(String msg) throws InterruptedException {
System.out.println("fanout.q1:"+msg);
Thread.sleep(200);
}
@RabbitListener(queues = "fanout.queue2")
public void listenerFanout2(String msg) throws InterruptedException {
System.out.println("fanout.q2:"+msg);
Thread.sleep(200);
}
Dircet Exchange
接收到的消息将会根据规则到指定的queue
队列1将会监听关键字中带有red,blue的消息
队列2将会监听关键字中带有red,yellow的消息
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "xy.direct",type = "direct"),
key = {"red","blue"}
))
public void listenerDir1(String msg) throws InterruptedException {
System.out.println("direc.q1:"+msg);
Thread.sleep(200);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2"),
exchange = @Exchange(name = "xy.direct",type = "direct"),
key = {"red","yellow"}
))
public void listenerDir2(String msg) throws InterruptedException {
System.out.println("direc.q2:"+msg);
Thread.sleep(200);
}
发送关键词为red的消息,此时队列1和队列2都可以收到
@Test
public void sendDirExchange(){
//交换机
String name = "xy.direct";
//发送信息
String msg = "hello,fanout";
//发生
rabbitTemplate.convertAndSend(name,"red",msg);
}
Topic Exchange
与DirectExchage类似,区别在于routingKey必须是多个单词的列表,并且以“.”分割
通配符:
#: 代表0或多个单词
*: 代指一个单词
队列1只会接受以china开头的消息,队列2只会接受以news结尾的消息
@RabbitListener(bindings = @QueueBinding(
value = @Queue("topic.q1"),
exchange = @Exchange(name="xy.topic",type = ExchangeTypes.TOPIC),
key="china.#"
))
public void listenerTop1(String msg) throws InterruptedException {
System.out.println("fanout.top1:"+msg);
Thread.sleep(200);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue("topic.q2"),
exchange = @Exchange(name="xy.topic",type = ExchangeTypes.TOPIC),
key="#.news"
))
public void listenerTop2(String msg) throws InterruptedException {
System.out.println("fanout.top2:"+msg);
Thread.sleep(200);
}
发送关键词为china.news,此时队列1和队列2都会收到
@Test
public void sendTopicExchange(){
//交换机
String name = "xy.topic";
//发送信息
String msg = "这是消息内容";
//发生
rabbitTemplate.convertAndSend(name,"china.news",msg);
}
消息转换器
测试发送object类型的消息
config中申明队列:
@Bean
public Queue objectQueue(){
return new Queue("object.queue");
}
consumer:
@RabbitListener(queues = "object.queue")
public void listenerObject(Map msg) throws InterruptedException {
System.out.println(+msg);
Thread.sleep(200);
}
publisher:
@Test
public void objectQueue(){
HashMap<Object, Object> map = new HashMap<>();
map.put("name","xy");
map.put("age",22);
rabbitTemplate.convertAndSend("object.queue",map);
}
默认对象序列化是基于JDK的ObjectOutputStream完成,可修改为JSON方式序列化
在publisher中引入依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
publisher启动类声明转换器
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}