在普通maven项目应用中使用MQ

新人学习,本文从B站学习借鉴而来,链接:https://www.bilibili.com/video/BV14A411q7pF/?p=9

本文会介绍maven项目创建,通过项目介绍简单模式Simple、工作模式Work、订阅模式Publish/Subscribe、路由模式Routing这四种常用的模式

maven项目创建步骤:

1.首先,创建maven项目。

这里我创建了聚合项目(也可以不聚合,producer和consumer分开放也行,这里我是为了方便打包),该聚合项目包含producer和consumer,根据模式不同,消费者consumer会有1到多个,继续往下就知道了。

2.然后,配置pom.xml文件(添加依赖)、日志文件。

四种模式的生产者和消费者都要配置pom.xml和日志,值得注意的是日志是一样的可以直接复制过去,但pom.xml文件头部信息不同,把依赖部分添加即可,不要把整个pom.xml直接拷贝过去,否则会出现一些bug。

3.编写类,实现发送接收消息功能。

分别在producer和consumer中,写发送消息的类sendMsg和接受消息的类receiveMsg,这两个类是主要类。

还有一个工具类ConnectionUtil,用于连接rabbitmq的,连接是一样的所以producer和consumer中,这个工具类除了头部包名不同,具体内容一样,复制的时候注意头部别一起复制了。

以简单模式为例,一个生产者一个消费者,配置后目录:

简单项目maven目录

producer/consumer的pom.xml的依赖部分:
simple父项目的pom.xml不用添加这部分依赖

  <dependencies>
  
  	<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
	<dependency>
	    <groupId>com.rabbitmq</groupId>
	    <artifactId>amqp-client</artifactId>
	    <version>5.5.0</version>
	</dependency>
  	
  	<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
	<dependency>
	    <groupId>org.slf4j</groupId>
	    <artifactId>slf4j-log4j12</artifactId>
	    <version>1.7.25</version>
	    <scope>test</scope>
	</dependency>
	 
	 <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
	<dependency>
	    <groupId>org.apache.commons</groupId>
	    <artifactId>commons-lang3</artifactId>
	    <version>3.9</version>
	</dependency>
	 
  </dependencies>

在src/main/resources目录下,添加log4j.properties日志:

log4j.properties:

log4j.rootLogger=DEBUG,A1 log4j.logger.com.taotao = DEBUG 
log4j.logger.org.mybatis = DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

1.简单模式

在下图中,“ P”是我们的生产者,“ C”是我们的消费者。中间的框是一个队列-RabbitMQ代表使用者保留的消息缓冲区。
简单模式
生产者将消息发送到“ hello”队列。使用者从该队列接收消息。

1.1.消息生产者

首先,创建帮助类ConnectionUtil.java。这个是一个连接MQ的工具类,是为了代码简洁,所以另外创建一个连接的工具类,使用的话直接导入该包调用即可。
ConnectionUtil:

package com.rbmq.consumer.utils;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class ConnectionUtil {
	
	public static Connection getConnection() throws Exception {
        //定义连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        // 设置服务地址
        factory.setHost("127.0.0.1");
        //端口
        factory.setPort(5672);
        // 设置账号信息,虚拟主机、用户名、密码,这里不特指某一个虚拟主机,所以用默认的“/”
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");
        
        // 通过工程获取连接
        Connection connection = factory.newConnection();
        return connection;
   }
}

生产者发送消息,SendMsg.java

package com.rbmq.producer.service;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rbmq.producer.utils.ConnectionUtil;

public class sendMsg {
	
	 public static void main(String[] argv) throws Exception {
	        // 获取到连接以及mq通道
	        Connection connection = ConnectionUtil.getConnection();   // 相当于数据库中的创建连接
	        // 从连接中创建通道
	        Channel channel = connection.createChannel();	// 相当于数据库中的 statement
	        // 声明(创建)队列,如果存在就不创建,不存在就创建
	        // 参数1 队列名,
	        // 参数2 durable: 是否持久化, 队列的声明默认是存放到内存中的,如果rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,保存到Erlang自带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库
	        // 参数3 exclusive:是否排外的,有两个作用,一:当连接关闭时connection.close()该队列是否会自动删除; 二:该队列是否是私有的private,如果不是排外的,可以使用两个消费者都访问同一个队列,没有任何问题,如果是排外 的,会对当前队列加锁,其他通道channel是不能访问的,如果强制访问会报异常: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=405, reply-text=RESOURCE_LOCKED - cannot obtain exclusive access to locked queue 'queue_name' in vhost '/', class-id=50, method-id=20)一般等于true的话 用于一个队列只能有一个消费者来消费的场景
	        // 参数4 autoDelete:是否自动删除,当最后一个消费者断开连接之后队列是否自动被删除,可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0时队列就会自动删除
	        // 参数5 arguments:  参数
	        //channel.queueDeclare("queue1", false, false, true, null);
	        
	        // 消息内容
	         String message = "Hello consumer!";
	        // 参数1 交换机,此处无
	        // 参数2 发送到哪个队列
	        // 参数3 属性
	        // 参数4 内容
	         channel.basicPublish("", "queue1", null, message.getBytes());
	        // 将消息发动到数据库
	         System.out.println(" 发送数据: '" + message + "'");
	        //关闭通道和连接
	         channel.close();
	         connection.close();
	    }
}

测试结果:
测试发送

1.2.消息消费者

同样,首先创建工具类ConnectionUtil.java,这个类是连接mq的,没有连接mq,接收不到消息
然后,编写接收消息类receiveMsg

package com.rbmq.consumer.service;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rbmq.consumer.utils.ConnectionUtil;

public class receiveMsg {
	
	public static void main(String[] args) throws Exception {

		Connection connection = ConnectionUtil.getConnection(); // 相当于jdbc里的数据库连接
		Channel channel = connection.createChannel();// 相当于jdbc操作的statement
		
		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag,Envelope envelope,
					AMQP.BasicProperties properties,byte[] body) {
				//body为从队列中获取的数据
				 String msg = new String(body);
				System.out.println("接收到的消息:"+msg);
			}
			
		};
		
		channel.basicConsume("queue1", true, consumer);
	
	}
}

测试结果:
测试接收

2.工作模式

工作队列,生产者把消息发送到队列中,由队列分配到消费者,采用的是先到先得的原则,即C1、C2共同争抢当前的消息队列内容,谁先拿到谁负责消费消息。例如超市低价促销先到先得。
工作模式

基本项目步骤和上面的一样,都是:

创建maven项目
导入依赖和日志
编写实现类实现

项目创建和依赖、日志、ConnectionUtil帮助类一样的,不同的是多了一个消费者:
workm模式项目目录

2.1.消息生产者

sendMsg:

package com.rbmq.producer.service;

import java.util.Scanner;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rbmq.producer.utils.ConnectionUtil;

public class sendMsg {
	
	 public static void main(String[] argv) throws Exception {
		 
		 	System.out.println("请输入消息:");
		 	Scanner scaner = new Scanner(System.in);
		 	String msg=null;
		 	while(!"quit".equals(msg=scaner.nextLine())) {
		 		
		 		// 获取到连接以及mq通道
		        Connection connection = ConnectionUtil.getConnection();   
		        // 从连接中创建通道
		        Channel channel = connection.createChannel();	
		        // 参数1 交换机,此处无
		        // 参数2 发送到哪个队列
		        // 参数3 属性
		        // 参数4 内容
		         channel.basicPublish("", "queue4", null, msg.getBytes());
		        // 将消息发动到数据库
		         System.out.println(" 发送数据: '" + msg + "'");
		        //关闭通道和连接
		         channel.close();
		         connection.close();
		 	}	        
	    }
}

测试环节(中间有丢包情况,所以多发了一次hello msg3):
测试发送

2.2.消息消费者1

package com.rbmq.consumer.service;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rbmq.consumer.utils.ConnectionUtil;

public class receiveMsg {
	
	public static void main(String[] args) throws Exception {

		Connection connection = ConnectionUtil.getConnection(); // 相当于jdbc里的数据库连接
		Channel channel = connection.createChannel();// 相当于jdbc操作的statement
		
		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag,Envelope envelope,
					AMQP.BasicProperties properties,byte[] body) {
				//body为从队列中获取的数据
				 String msg = new String(body);
				System.out.println("consumer1接收到的消息:"+msg);
			}
		};
		channel.basicConsume("queue4", true, consumer);
	}
}

测试结果:
消费者1接收信息测试

2.3.消息消费者2

package com.rbmq.consumer.service;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rbmq.consumer.utils.ConnectionUtil;

public class receiveMsg {
	
	public static void main(String[] args) throws Exception {

		Connection connection = ConnectionUtil.getConnection(); // 相当于jdbc里的数据库连接
		Channel channel = connection.createChannel();// 相当于jdbc操作的statement
		
		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag,Envelope envelope,
					AMQP.BasicProperties properties,byte[] body) {
				//body为从队列中获取的数据
				 String msg = new String(body);
				System.out.println("consumer2接收到的消息:"+msg);
			}
			
		};
		channel.basicConsume("queue4", true, consumer);

	}
}

测试结果:
消费者2接收消息测试

3.订阅模式

一次向许多消费者发送消息,X代表交换机rabbitMQ内部组件,消息产生者将消息放入交换机,交换机发布订阅把消息发送到所有消息队列中,对应消息队列的消费者拿到消息进行消费,例如广告。
订阅模式

项目和上面的基本不变,主要改变发送接收模式参数即可。

3.1.消息生产者

sendMsg代码和上面的工作模式基本没有改变,改变的只有这一行:

 channel.basicPublish("ex1", "", null, msg.getBytes());

注释:把消息发送到交换机ex1上而不是队列中,而在rabbitmq管理页面上交换机ex1绑定了队列1和队列2

测试环节:
订阅模式

3.2.消息消费者1

同样,receiveMsg1也基本没有改变,改变的是这一行:

channel.basicConsume("queue1", true, consumer);

消息消费者1接收的是交换机ex1所绑定的queue1队列

测试结果:
测试接收

3.3.消息消费者2

同样,receiveMsg2也基本没有改变,改变的是这一行:

channel.basicConsume("queue2", true, consumer);

消息消费者1接收的是交换机ex1所绑定的queue2队列

测试结果:
测试接收2
注:这两个消费者都提前运行,同时接收到这两个消息。

4.路由模式

路由模式,消息生产者将消息发送给交换机按照路由判断,路由是字符串(info) ,交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息。
路由模式
项目同样没有多大改动

4.1.消息生产者

sendMsg主要代码,其余不变:

				// 获取到连接以及mq通道
		        Connection connection = ConnectionUtil.getConnection();   
		        // 从连接中创建通道
		        Channel channel = connection.createChannel();	
		        // 参数1 交换机
		        // 参数2 路由key,如果前面的交换机为空,这里是队列参数
		        // 参数3 属性
		        // 参数4 内容
		        if(msg.startsWith("a")) {
		        	channel.basicPublish("ex2", "a", null, msg.getBytes());
		        }else if(msg.startsWith("b")) {
		        	channel.basicPublish("ex2", "b", null, msg.getBytes());
		        }
		         
		        // 将消息发动到数据库
		         System.out.println(" 发送数据: '" + msg + "'");
		        //关闭通道和连接
		         channel.close();
		         connection.close();

测试环节:
消息发送测试

4.2.消息消费者1

channel.basicConsume("queue3", true, consumer);

交换机ex2绑定了queue3,路由key为a,消费者1直接监听队列3

测试结果:
消费者1
消费者1只接受路由key为a的消息,前面把字符串以a开头的路由key设置为a

4.3.消息消费者2

channel.basicConsume("queue4", true, consumer);

交换机ex2绑定了queue4,路由key为b,消费者2直接监听队列4

测试结果:
消费者2
消费者2只接受路由key为b的消息,前面把字符串以b开头的路由key设置为b

结语:

通过四个项目的实现,对rabbitmq的几种常用的模式有比较好的了解,不足的地方是,经常出现丢包现象。
附上项目代码:
链接:https://pan.baidu.com/s/1jv-WZv9zQ6fU_jnQQRb73Q
提取码:pkar

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值