文章主要是本人学习记录仅供参考,有错误或不足的地方请指出谢谢!
RabbitMq启动服务后本地web网页:http://192.168.148.130:15672/
常用命令:
1、添加开机启动rabbitmq服务:
chkconfig rabbitmq-server on
2、启动服务:
/sbin/service rabbitmq-server start
3、查看服务状态:
/sbin/service rabbitmq-server status
4、停止服务:
/sbin/service rabbitmq-server stop
项目创建:创建一个父工程,再创建两个子工程,其均为springboot
在父工程的pom中添加如下代码(子工程就不用再加了)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
在两个子工程中的application.yml中添加如下配置
spring:
rabbitmq:
//Rabbitmq的主机名(在虚拟机运行的就填虚拟机地址)
host: 192.168.***.***
//端口号,默认为5672
port: 5672
//Rabbitmq的虚拟机,默认为/
virtual-host: /
//用户名和密码
username: admin
password: admin
在web端的Rabbitmq中添加simple.queue
1.直接发送到队列而不经过交换机
一个发送者对应一个接收者
消费者(接收者)
在consumer里新建一个包用来监听消息,与启动类在一个包内,再创建一个监听类
在类里面写如下内容,同时该类要添加注解@Component
//queues写的是队列名称
@RabbitListener(queues = "simple.queue")
public void listener(String name){
System.out.println(name);
}
发送者
在测试方法中写
//用Autowired可能会出现找不到bean的情况
@Resource
private RabbitTemplate rabbitTemplate;
@Test
void simpleTest() {
//设置队列名
String queueName="simple.queue";
//设置发送的信息
String msg="Hello mq";
rabbitTemplate.convertAndSend(queueName,msg);
}
开始测试
先在consumer中直接运行启动类,开启监听,监听时程序不会自动结束而是一直运行等待接收消息(不会自动结束),再在publisher(发送者)中启动测试类
接收成功
一个发送者对应多个接收者
消费者(接收者)
//queues为队列名
@RabbitListener(queues = "work.queue")
public void listenerWork1(String name) throws InterruptedException {
System.out.println("消费者1收到了:"+name);
//Thread.sleep(20);
}
@RabbitListener(queues = "work.queue")
public void listenerWork2(String name) throws InterruptedException {
System.err.println("消费者2收到了:"+name);
//Thread.sleep(200);
}
发送者
@Test
void workTest() throws InterruptedException {
String queueName="work.queue";
for (int i = 0; i < 50; i++) {
String msg="Hello work_"+i;
rabbitTemplate.convertAndSend(queueName,msg);
Thread.sleep(20);
}
}
开始测试
根据运行结果可以得出结论,队列内的数据被取出后就不存在了,再看web版的Rabbitmq中
但假设两个接收的速度不同会发生什么?
接收速度差异测试
@RabbitListener(queues = "work.queue")
public void listenerWork1(String name) throws InterruptedException {
System.out.println("消费者1收到了:"+name);
Thread.sleep(20);//休眠模拟性能
}
@RabbitListener(queues = "work.queue")
public void listenerWork2(String name) throws InterruptedException {
System.err.println("消费者2收到了:"+name);
Thread.sleep(200);
}
运行
那么问题来了,即使性能不同但是两者总数相同,故应当优化此问题,在Rabbitmq中能够进行设置,在刚刚写配置的yml中添加如下内容
spring:
rabbitmq:
host: 192.168.148.130
port: 5672
virtual-host: /
username: admin
password: admin
listener:
simple:
prefetch: 1
perfetch的作用可以比喻为吃完一块饼干才能吃下一块而不是两个人平均分总共的饼干数量
开始测试
由运行结果来看,这样是比较合理的,能力越大责任越大
通过交换机来进行消息的发送
广播交换机-Fanout
在rabbitmq中直接使用或者创建fanout交换机,注意名称和类型即可
点开交换机在这个位置填入队列名称就行,其他的先不管,不要输错字了
在发送者的Test中添加如下代码
@Test
void testSendFanout() {
//依然是熟悉的交换机名
String exchangeName="amq.fanout";
//要发送的信息msg
String msg="Hello fanout";
//三个参数,第一个是交换机的名字,第二个是路由密钥没了就空着,第三个是消息
rabbitTemplate.convertAndSend(exchangeName,"",msg);
}
在接收者的监听类中写入
@RabbitListener(queues = "fanout.queue1")
public void listenerFanout1(String name) {
System.out.println("消费者1收到了:"+name);
}
@RabbitListener(queues = "fanout.queue2")
public void listenerFanout2(String name) {
System.err.println("消费者2收到了:"+name);
}
开始测试
路由交换机-Direct
都是相似的,只是把fanout换成了direct
在web版rabbitmq中直接用或者创建direct类型的交换机
创建的话注意名称和类型就可以
在绑定交换机的时候要把routing key记住,这个是一会儿能取到数据的钥匙
发送者中的代码是
@Test
void testSendDirect() {
String exchangeName="amq.direct";
String msg="Hello direct";
//其中第二个参数就是fanout没有用到的内容
rabbitTemplate.convertAndSend(exchangeName,"queue1",msg);
}
接收者的代码是
@RabbitListener(queues = "direct.queue1")
public void listenerDirect1(String name) {
System.out.println("消费者1收到了:"+name);
}
@RabbitListener(queues = "direct.queue2")
public void listenerDirect2(String name) {
System.err.println("消费者2收到了:"+name);
}
开始测试
当key为queue1时
当key为queue2时
当key为common时
当key并不存在时
简单来说就是你要有正确的key才能发送到相应的队列才会被消费者读取到,这个key并不唯一,可能队列1和2有共同的key,如此两者会都收到消息和fanout一样了。
拓展版路由交换机-Topic
这种交换机和direct很是相似,但是其中略有不同,它能够更灵活,它能够使用通配符#并且能够进行单词的分割用 . 完成,其中#可以代表0个或多个词,*代表1个词,和习惯略有不同需要注意一下例如
如图是一个topic交换机绑定了两个队列,其中第一个,任何queue1.开头的都能收到内容,第二个则是任何以.news结尾的都能收到内容,代码验证如下
示例1
发送者代码
@Test
void testSendTopic() {
String exchangeName="amq.topic";
String msg="Hello topic";
rabbitTemplate.convertAndSend(exchangeName,"queue1.hello",msg);
}
接收者代码
@RabbitListener(queues = "topic.queue1")
public void listenerTopic1(String name) {
System.out.println("消费者1收到了:"+name);
}
@RabbitListener(queues = "topic.queue2")
public void listenerTopic2(String name) {
System.out.println("消费者2收到了:"+name);
}
开始测试
输入的key和topic里设的并不完全一样但是依然能读到数据
示例2
发送者
@Test
void testSendTopic() {
String exchangeName="amq.topic";
String msg="Hello topic";
rabbitTemplate.convertAndSend(exchangeName,"queue1.news",msg);
}
开始测试
如结果所示,当同时满足两者的规则后均可以收到消息。
用Java代码创建和绑定队列交换机
相信大家看出来了,有一个很大的缺陷是必须用web版的Rabbitmq来进行内容的操作,这样的操作不仅很费眼,而且也容易出错特别是在企业环境下,因此我们在Java代码中实现是更好的
创建一个config包,其中写rabbitConfig,内容是
两种方法二选其一即可
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("Hello");
// return ExchangeBuilder.fanoutExchange("Hello").build();
}
运行后
其他几个也差不多