RabbitMQ

73 阅读7分钟

官网

www.rabbitmq.com/

环境准备

  • RabbitMQ 3.8.17
  • Erlang 23.3

什么是RabbitMQ

  • 一款基于AMQP(Advanced Message Queuing Protocol)用于软件之间通信的中间件,实现了服务之间的高度解耦
  • 多用在分布式系统中储存转发的消息,在易用性,拓展性,高可用性等方面表现很好

为什么要使用RabbitMQ

待补充

RabbitMQ的使用场景

待补充

RabbitMQ主要由什么组成?

  • 生产者
  • 交换机
  • 消息队列
  • 消费者

图示:

image.png

RabbitMQ JAVA客户端连接并使用

工程结构

image.png

导入依赖

<parent>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-parent</artifactId>  
<version>2.7.12</version>  
</parent>  
  
<dependencies>  
<dependency>  
<groupId>com.rabbitmq</groupId>  
<artifactId>amqp-client</artifactId>  
<version>4.0.3</version> 
</dependency>  
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-logging</artifactId>  
</dependency>  
</dependencies>

由于amqp-client 内部依赖日志文件, 所以导入了日志的依赖包

HELLOWORLD

图示模型

image.png

生产者将消息发送到消息队列中, 消费者直接从消息队列取出

生产者代码

public class Producer_helloworld {  
private static final String QUEUE_NAME="hello";  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel();  
String message="放下过往,他只会遮蔽未来";  
channel.basicPublish("",QUEUE_NAME,null,message.getBytes());  
channel.close();  
connection.close();  
}  
}
        > 变量解析
        > ConnectionFactory connectionFactory = new ConnectionFactory(); 创建连接工厂
        
        > Connection connection = connectionFactory.newConnection(); 通过工厂创建连接对象
        
        > Channel channel = connection.createChannel();   创建通道这个通道指的是整体,可以理解为通道将生产者,消息队列,消费者给连起来, 通道里面包含所有的内容
        >
        > String message="放下过往,他只会遮蔽未来"; 定义要发送的信息
        
        > channel.basicPublish("",QUEUE\_NAME,null,message.getBytes()); 将消息发送出去
        > 参数分析,第一个参数为”“ 代表交换机,我们这里是P发送给队列,C从队列中拿 所以不需要交换机 这里填空值
        
        >第二个参数为QUEUE_NAME,这里代表要将这个消息发送到哪个消息队列里面
        
        > 第三个参数为null, 这里代表发送消息的属性
        
        > 第四个参数代表要发送的内容
        
        关闭通道,关闭连接

image.png

被红圈圈起来的这个整体代表chanel

消费者代码

public class Cousumer_helloworld {  
private final static String QUEUE_NAME="hello";  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  //创建连接
connectionFactory.setHost("127.0.0.1");   //我们要接受哪个主机发送的消息?
Connection connection = connectionFactory.newConnection();   //建立连接
Channel channel = connection.createChannel();   //创建通道,要注意通道包含P 消息队列 C
channel.queueDeclare(QUEUE_NAME,false,false,false,null);     //声明队列 
DefaultConsumer defaultConsumer=new DefaultConsumer(channel){  
@Override  
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
      String message=new String(body,"UTF-8"); //内容  
      System.out.println(message);  
           }  
      };  
     channel.basicConsume(QUEUE_NAME,true,defaultConsumer);  
   }  
}

代码解析

    channel.queueDeclare(QUEUE_NAME,false,false,false,null);  
    这句是为了 声明一个队列,也可以理解为创建一个队列, 这个队列名字叫做QUEUE_NAME="HELLO", 我们连接的主机是127.0.0.1 上面主机发送出来了一条消息,发送给了 名字叫hello 的队列, 我们这里声明了一个hello 的队列, 这样就可以收到主机发送出来的消息了,后面的三个false分别代表,队列是否持久化,队列是否独占,队列不使用的时候是否要自动删除,最后一个null代表 队列参数为空

Work Queues

原理图示

image.png

生产者代码

public class Producer_work_queues {  
private final static String QUEUE_WORKQUEUES_NAME="workqueues";   //定义队列名,为发送消息做准备
  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();   //创建连接工厂
Connection connection = connectionFactory.newConnection();   //创建工厂
Channel channel = connection.createChannel();   //创建通道 
String message="HELLO WORKQUEUES";   //定义消息
channel.basicPublish("",QUEUE_WORKQUEUES_NAME,null,message.getBytes());  //将消息发送到空交换机,上面准备好的队列名的哪个队列, 数据参数为空, 数据信息  
channel.close();   
connection.close();  
}  
}

1号消费者代码

public class Cosumer_work_queue_01 {  
private final static String QUEUE_WORKQUEUES_NAME="workqueues";  
  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();   //创建连接工厂
connectionFactory.setHost("127.0.0.1");   //连接到哪个主机?
Connection connection = connectionFactory.newConnection();  //建立连接 
Channel channel = connection.createChannel();   //创建通道
channel.queueDeclare(QUEUE_WORKQUEUES_NAME,false,false,false,null); //声明一个队列,  
channel.basicQos(1);   //我是第一个拿的
DefaultConsumer defaultConsumer=new DefaultConsumer(channel){  
@Override  
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
System.out.println(new String(body,"UTF-8"));  
}  
};  
channel.basicConsume(QUEUE_WORKQUEUES_NAME,true,defaultConsumer);  //监听这个队列,有新消息接收的时候调用defaultconsumer中的方法 打印输出
}  
}

2号消费者代码

public class Cosumer_work_queue_02 {  
private final static String QUEUE_WORKQUEUES_NAME="workqueues";  
  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
connectionFactory.setHost("127.0.0.1");  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel();  
channel.queueDeclare(QUEUE_WORKQUEUES_NAME,false,false,false,null);  
channel.basicQos(2);    //注意这里
DefaultConsumer defaultConsumer=new DefaultConsumer(channel){  
@Override  
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
System.out.println(new String(body,"UTF-8"));  
}  
};  
channel.basicConsume(QUEUE_WORKQUEUES_NAME,true,defaultConsumer);  
}  
}

总结 我们会发现 1号消费者和2号消费者代码几乎一摸一样, 只是中间由两条 不一样 , channel.basicQos(1); channel.basicQos(2); 现在是两个消费者都接受一条队列的消息, 当生产者发送消息的时候, 第一次会被一号消费者先接收到,二号消费者接收不到, 当生产者再次发送消息的时候, 一号消费者接收不到,二号消费者可以接收到, 就是两个消费者轮流接受生产者发送的信息,此时的chanel 代表什么呢?

image.png

chanel 代表这个整体!

Publish

生产者

public class Producer_publish {  
private final static String EXCHANGE_NAME="exchange";  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel();  
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");  
String message="hello exchange";  
channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes());  
channel.close();  
connection.close();  
}  
}

消费者01

public class Consumer_subscribe_01 {  
private final static String EXCHANGE_NAME="exchange";  
private final static String QUEUE_NAME_01="exchange_queue_01";  
  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
connectionFactory.setHost("127.0.0.1");  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel();  
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");  
channel.queueDeclare(QUEUE_NAME_01,false,false,false,null);  
channel.queueBind(QUEUE_NAME_01,EXCHANGE_NAME,"");  
DefaultConsumer defaultConsumer=new DefaultConsumer(channel){  
@Override  
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
System.out.println(new String(body,"UTF-8"));  
}  
};  
channel.basicConsume(QUEUE_NAME_01,true,defaultConsumer);  
}  
}

消费者02

public class Consumer_subscribe_02 {  
private final static String EXCHANGE_NAME="exchange";  
private final static String QUEUE_NAME_02="exchange_queue_02";  
  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
connectionFactory.setHost("127.0.0.1");  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel();  
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");  
channel.queueDeclare(QUEUE_NAME_02,false,false,false,null);  
channel.queueBind(QUEUE_NAME_02,EXCHANGE_NAME,"");  
DefaultConsumer defaultConsumer=new DefaultConsumer(channel){  
@Override  
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
System.out.println(new String(body,"UTF-8"));  
}  
};  
channel.basicConsume(QUEUE_NAME_02,true,defaultConsumer);  
}  
}

路由模式

生产者

public class Producer_routing {  
private final static String EXCHANGE_NAME="routing_exchange";  
private final static String QUEUE_NAME_01="routing_queue_01";  
private final static String QUEUE_NAME_02="routing_queue_02";  
private final static String ROUTING_KEY_NAME_01="red";  
private final static String ROUTING_KEY_NAME_02="blue";  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel();  
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT); //声明交换机  
/* channel.queueDeclare(QUEUE_NAME_01,false,false,false,null); //声明消息队列  
channel.queueDeclare(QUEUE_NAME_02,false,false,false,null); //声明消息队列  
channel.queueBind(QUEUE_NAME_02,EXCHANGE_NAME,ROUTING_KEY_NAME_02); //绑定消息队列,  
channel.queueBind(QUEUE_NAME_01,EXCHANGE_NAME,ROUTING_KEY_NAME_01); //绑定消息队列*/  
String message="hello routing";  
// 推送消息, 交换机名称 路由key 名称 消息属性 , 消息内容  
channel.basicPublish(EXCHANGE_NAME,ROUTING_KEY_NAME_01,null,message.getBytes());  
channel.basicPublish(EXCHANGE_NAME,ROUTING_KEY_NAME_02,null,message.getBytes());  
channel.close();  
connection.close();  
}  
}

消费者01

public class Consumer_routing_01 {  
private final static String EXCHANGE_NAME="routing_exchange";  
private final static String ROUTING_KEY_NAME_01="red"; //路由键名 red  
private final static String QUEUE_NAME_01="routing_queue_01"; //队列名 01  
  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
connectionFactory.setHost("127.0.0.1");  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel(); //创建通道  
channel.queueDeclare(QUEUE_NAME_01,false,false,false,null); //声明队列  
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT); //声明交换机  
channel.queueBind(QUEUE_NAME_01,EXCHANGE_NAME,ROUTING_KEY_NAME_01); //队列和交换机 绑定 队列名, 交换机名, 路由名  
DefaultConsumer defaultConsumer=new DefaultConsumer(channel){  
@Override  
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
System.out.println(new String(body,"UTF-8"));  
}  
};  
channel.basicConsume(QUEUE_NAME_01,true,defaultConsumer); //监听队列 01 的信息  
}  
}

消费者02

public class Consumer_routing_02 {  
private final static String EXCHANGE_NAME="routing_exchange";  
private final static String ROUTING_KEY_NAME_02="blue";  
private final static String QUEUE_NAME_02="routing_queue_01";  
  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
connectionFactory.setHost("127.0.0.1");  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel();  
channel.queueDeclare(QUEUE_NAME_02,false,false,false,null);  
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);  
channel.queueBind(QUEUE_NAME_02,EXCHANGE_NAME,ROUTING_KEY_NAME_02);  
DefaultConsumer defaultConsumer=new DefaultConsumer(channel){  
@Override  
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
System.out.println(new String(body,"UTF-8"));  
}  
};  
channel.basicConsume(QUEUE_NAME_02,true,defaultConsumer);  
}  
}

Topic模式

生产者

public class Producer_topics {  
private final static String EXCHANGE_NAME="topic_exchange";  
private final static String ROUNTING_KEY_01="J.kkk";  
private final static String ROUNTING_KEY_02="G.";  
  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel();  
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);  
String message1="怒火焚身!";  
String message2="混元寒冰,朔风凛冽";  
channel.basicPublish(EXCHANGE_NAME,ROUNTING_KEY_01,null,message1.getBytes());  
channel.basicPublish(EXCHANGE_NAME,ROUNTING_KEY_02,null,message2.getBytes());  
channel.close();  
connection.close();  
}  
}

消费者

public class Consumer_topics_01 {  
private final static String EXCHANGE_NAME="topic_exchange"; //交换机名字  
private final static String QUEUE_NAME="jicanghai"; // 队列名字  
private final static String ROUTING_KEY="J.#"; // 路由KEy  
  
public static void main(String[] args) throws IOException, TimeoutException {  
ConnectionFactory connectionFactory = new ConnectionFactory();  
connectionFactory.setHost("127.0.0.1");  
Connection connection = connectionFactory.newConnection();  
Channel channel = connection.createChannel();  
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);  
channel.queueDeclare(QUEUE_NAME,false,false,false,null);  
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,ROUTING_KEY); //队列和交换机 通过路由key 绑定  
DefaultConsumer defaultConsumer=new DefaultConsumer(channel){  
@Override  
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
System.out.println(new String(body,"UTF-8"));  
}  
};  
channel.basicConsume(QUEUE_NAME,true,defaultConsumer); //监听队列 01 的信息  
}  
}

SpringBoot 整合RabbitMQ

导入依赖

<dependencies>  
<dependency>  
<groupId>com.rabbitmq</groupId>  
<artifactId>amqp-client</artifactId>  
<version>4.0.3</version><!--此版本与spring boot 1.5.9版本匹配-->  
</dependency>  
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-logging</artifactId>  
</dependency>  
</dependencies>

路由模式测试

编写生产者配置类

@Configuration  
public class RabbitMQConfiguration {  
public final static String EXCHANGE_NAME_Routing="exchange_routing";  
  
@Bean  
public Exchange getExchange(){  
return ExchangeBuilder.topicExchange(EXCHANGE_NAME_Routing).build();  
}  
}

生产者测试类

@SpringBootTest  
@RunWith(SpringRunner.class)  
public class ProducerTest {  
@Autowired  
private AmqpTemplate amqpTemplate;   

@Value("${mq.message.routing_key}")  

private String ROUTING_KEY;  

@Test  
public void testSending(){  
amqpTemplate.convertAndSend(RabbitMQConfiguration.EXCHANGE_NAME_Routing,ROUTING_KEY,"怒火焚身!");  
}  
}

消费者配置类

@Configuration  
public class RabbitMQConfiguration {  
public final static String EXCHANGE_NAME_Routing="exchange_routing";  
  
  
@Value("${mq.consumer.queue.name}")  
private String queueName;  
  
@Value("${mq.message.routing_key}")  
private String routing_key;  
  
@Bean  
public Exchange getExchange(){  
return ExchangeBuilder.topicExchange(EXCHANGE_NAME_Routing).build();  
}  
@Bean  
public Queue getQueue(){  
return new Queue(queueName,false,false,false);  
}  
@Bean  
public Binding bindingQueueExchange(Queue queue,Exchange exchange){  
return BindingBuilder.bind(queue).to(exchange).with(routing_key).noargs();  
}  
}

消费者处理接受内容类

@Component  
public class ReceiveMsg {  
@RabbitListener(queues = "routing_queue")  
public void doneMsg(String msg){  
System.out.println(msg);  
}  
}

测试结果

image.png