RabbitMQ消息队列

RabbitMQ

1消息队列解决了什么问题

流量削峰

应用解耦

日志处理

异步处理

2docker安装

rabbit是lang语言编写的 docker 会将lang语言一块安装

docker pull rabbitmq:3-management

management 代表有图形界面的

5672端口 客户端通讯端口

15672 web页面的端口

docker run -d -p 5672:5672 -p 15672:15672 --name myrabbitmq 479479d8e188

启动之后访问
在这里插入图片描述

账号密码默认都是guest

3添加用户

在这里插入图片描述

virtual hosts 相当于数据库

4添加数据库
在这里插入图片描述

在这里插入图片描述

授权

在这里插入图片描述

4Java操作RabbitMQ

1)、简单队列

模型

在这里插入图片描述

p:消息的生产者

红色:消息队列

c:消费者

3个对象 生产者 队列 消费者

1导包
    <dependency>
        <groupId>com.rabbitmq</groupId>
        <artifactId>amqp-client</artifactId>
        <version>4.0.2</version>
    </dependency>
    <
2 创建获取连接的工具
package com.lijiawei;

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

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/*
获取MQ的连接
 */
public class ConnectionUtil {
    public static Connection getConnection() throws IOException, TimeoutException {
        //定义一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //定义连接信息
        factory.setHost("192.168.75.129:15672");
        factory.setPort(5672);
        //连接那个库
        factory.setVirtualHost("/vhost");
        //设置用户
        factory.setUsername("user_name");
        factory.setPassword("123456"); 
        return factory.newConnection();;
    }
}

2 生产者
package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //获取一个通道 用这个通道传消息
        Channel channel = connection.createChannel();
        //声明消息队列test
        channel.queueDeclare("test",false,false,false,null);
        //使用管道发消息到test队列 消息必须转成字节
        channel.basicPublish("","test",null,"helloworld".getBytes());
        channel.close();
        connection.close();
    }

}

运行程序之后 生产者 会发送一个消息 我们去 web网站去看一眼

在这里插入图片描述

3消费者

用于接收消息队列里的消息

package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/*
    消费着
*/
public class Obtain {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //定义消费者
        QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
        //监听队列
        channel.basicConsume("test",true,queueingConsumer);
        while (true){
            QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
            String s= new String(delivery.getBody());
            System.out.println(s);
        }

    }

}

消费者会一直处于运行状态 监听消息队列

4消费者新API

上面的消费之 里的方法过时了 下面是用新API

package com.lijiawei;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/*
    消费着
*/
public class Obtain {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare("test",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 s = new String(body);
                System.out.println(s);

            }
        };
        //监听消息队列test 用消费者处理
        channel.basicConsume("test",true,defaultConsumer);

    }

}

5简单队列的不足

耦合性高 生产者与消费者一一对应 如果想多个消费者消费队列的消息

队列名变更 消费者和生产者要同时变更

2)、Work Queues

1模型

在这里插入图片描述

1 生产50个消息

package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //从连接中获取 通道
        Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare("test",false,false,false,null);
        //发消息 消息必须转成字节
        for(int i=0;i<50;i++){
            channel.basicPublish("","test",null,(i+"msg").getBytes());
      
            System.out.println("发送消息"+i+"msg");

        }
        channel.close();
        connection.close();
    }

}

只需要写俩个消费者就行

通过测试发现 俩个消费者处理的速度是一样的 平均都处理25个消息

这种方式就叫轮询分发 不管谁忙或谁清闲都不会多给一个或少给一个

任务消息总是你一个我一个

3)、公平分发

轮询分发 效率低 处理慢的和处理快的都处理一样的数量 导致效率低 所以使用公平分发

消息队列只给每个消费者分发一个消息 处理完之后消费者回应一下 消息队列才会在分配消息 最后的效果就是能者多劳

使用公平分发 必须满足下面条件

消费者关闭自动应答 改成手动

通道指定一次最多发几条消息

1消费者
package com.lijiawei;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/*
    消费着
*/
public class Obtain1 {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
         final Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare("test",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 {
                String s = new String(body);
                System.out.println(s);
                //处理完之后告诉消息队列
           channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //监听test队列 如果有消息调用defaultConsumer处理 false关闭自动应答
        channel.basicConsume("test",false,defaultConsumer);

    }

}

2生产者
package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //从连接中获取 通道
        Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare("test",false,false,false,null);
        //一次最多个消费者分发1个消息
        channel.basicQos(1);
        //发消息 消息必须转成字节
        for(int i=0;i<50;i++){
            channel.basicPublish("","test",null,(i+"msg").getBytes());
            System.out.println("发送消息"+i+"msg");

        }
        channel.close();
        connection.close();
    }

}

每个类都加       channel.basicQos(1); //指定一次发一个消息
消费者类加   channel.basicAck(envelope.getDeliveryTag(),false); //告诉消息队列处理完了一个消息
消费者类加  channel.basicConsume("test",false,defaultConsumer);//关闭自动应答

4)、消息应答与消息持久化

boolean autoAck =false

消费者类加 channel.basicConsume(“test”,autoAck ,defaultConsumer);//false关闭自动应答

boolean autoAck =true(自动确认模式) 一旦消息队列把消息给消费者 这个消息就会从内存中删除(不管你处理成功),如果杀死正在处理消息的消费者 这个消息就会丢失

boolean autoAck =true(手动确认模式) 如果有一个消费者挂掉 那么正在处理的这个消息就会给其他消费者处理,处理成功之后 给消息队列一个响应 消息队列才会删除这个消息

如果消息队列挂了 所有消息都丢了 那怎么办?持久化?

在代码中有这么一句话 现在解释

boolean durable =false

channel.queueDeclare(“test”,durable,false,false,null);

如果设置过一次 就不能更改 rabbitMQ不允许重新定义一个已存在的队列

5)、订阅消息

1模型

在这里插入图片描述

一个生产者 多个消费者 每个消费者都有自己的队列 生产者将消息发送给交换机 每个队列都要绑定交换机
在这里插入图片描述

2代码

消息不再发给队列还是发给交换机

代码就不用定义队列 只要定义交换机 给交换机发消息即可

package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //从连接中获取 通道
        Channel channel = connection.createChannel();
        //声明交换机 类型为fanout分发 名字叫myExchange w
        channel.exchangeDeclare("myExchange","fanout");
        //发消息给交换机
        channel.basicPublish("myExchange","",null,"helloworld".getBytes());
        channel.close();
        connection.close();
    }

}

发送之后 web管理界面就会有一个交换机

在这里插入图片描述

交换机是有了 消息呢?丢失了 因为交换机没有存储能力 只有队列有存储能力

消费者

定义队列 并且将队列和交换机绑定

package com.lijiawei;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/*
    消费着
*/
public class Obtain {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare("test",false,false,false,null);
        //队列与交换机绑定
        channel.queueBind("test","myExchange","");
        //定义消费者
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                      AMQP.BasicProperties properties, byte[] body) throws IOException {
                String s = new String("1:"+body);
                System.out.println(s);
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //监听test队列 如果有消息调用defaultConsumer处理
        channel.basicConsume("test",false,defaultConsumer);

    }

}

web

在这里插入图片描述
6)、交换机

简单模式下

生产者通过这个代码发送消息给test队列 “” 代表匿名交换机

channel.basicPublish("",“test”,null,“helloworld”.getBytes());

订阅模式下

这个代码指定了交换机 下面的“” 代表路由路由键 空代表不处理路由

channel.basicPublish(“myExchange”,"",null,“helloworld”.getBytes());

在声明队列的时候 fanout是什么? 不处理路由键

channel.exchangeDeclare(“myExchange”,“fanout”);

6)、路由模式

1模型

在这里插入图片描述

路由模式必须是direct

channel.exchangeDeclare(“myExchange”,“direct”);

生产者

package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //从连接中获取 通道
        Channel channel = connection.createChannel();
        //声明交换机 类型direct 名字叫myExchange_direct
        channel.exchangeDeclare("myExchange_direct","direct");
        //发消息给交换机
        channel.basicPublish("myExchange_direct","info",null,"helloworld".getBytes());
        channel.close();
        connection.close();
    }

}

消费者

//队列与交换机绑定 写key
channel.queueBind(“test”,“myExchange”,"");

消费者1

package com.lijiawei;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/*
    消费着
*/
public class Obtain {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare("test3",false,false,false,null);
        channel.basicQos(1);
        //队列与交换机绑定
        channel.queueBind("test3","myExchange_direct","error");
        //定义消费者
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                      AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("1收到了消息");
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //监听test队列 如果有消息调用defaultConsumer处理
        channel.basicConsume("test3",false,defaultConsumer);

    }

}

消费者2

package com.lijiawei;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/*
    消费着
*/
public class Obtain1 {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare("test4",false,false,false,null);
        channel.basicQos(1);
        //队列与交换机绑定这个队列有3个路由key
        channel.queueBind("test4","myExchange_direct","error");
        channel.queueBind("test4","myExchange_direct","info");
        channel.queueBind("test4","myExchange_direct","warning");
        //定义消费者
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                      AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("2收到了消息");
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //监听test队列 如果有消息调用defaultConsumer处理
        channel.basicConsume("test4",false,defaultConsumer);

    }

}

7)、主题模式

在这里插入图片描述
#匹配多个

*匹配一个

主题模式 交换机必须是topic类型

生产者

package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //从连接中获取 通道
        Channel channel = connection.createChannel();
        //声明交换机 类型为fanout分发 名字叫myExchange w
        channel.exchangeDeclare("myExchange_topic","topic");
        //发消息给交换机
        channel.basicPublish("myExchange_topic","good.add",null,"helloworld".getBytes());
        channel.close();
        connection.close();
    }

}

消费者1

package com.lijiawei;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/*
    消费着
*/
public class Obtain {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare("test5",false,false,false,null);
        channel.basicQos(1);
        //队列与交换机绑定
        channel.queueBind("test5","myExchange_topic","a.#");
        //定义消费者
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                      AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("1收到了消息");
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //监听test队列 如果有消息调用defaultConsumer处理
        channel.basicConsume("test5",false,defaultConsumer);

    }

}

消费者2

package com.lijiawei;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/*
    消费着
*/
public class Obtain1 {
    public static void main(String[] args) throws IOException, TimeoutException{
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        final Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare("test6",false,false,false,null);
        channel.basicQos(1);
        //队列与交换机绑定
        channel.queueBind("test6","myExchange_topic","good.#");
        //定义消费者
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                      AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("2收到了消息");
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //监听test队列 如果有消息调用defaultConsumer处理
        channel.basicConsume("test6",false,defaultConsumer);

    }

}

一共五种

[

5.消息确认机制

如何确定生产会将消息发送出去之后到达了rabbitMQ 服务器默认是不知道的

通过俩种方式解决

​ AMQP事务

​ Confirm模式

1)、事务机制

用下面三个方法

txSelect:用户将当前channel设置成transation模式 开启事务

txCommit:提交

txRollback:回滚事务

通过事务机制 如果执行成功呢么一定会传到消息队列 失败就会报错

生产者

package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
    public static void main(String[] args) throws IOException, TimeoutException{
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("test7",false,false,false,null);
        try {
            //开启事务
            channel.txSelect();
            channel.basicPublish("","test7",null,"helloworld".getBytes());
            int a = 1/0;
            channel.txCommit();
        }catch (Exception e){
            channel.txRollback();
            System.out.println("send message failure");
        }finally {
            channel.close();
            connection.close();
        }
    }
}

消费者

package com.lijiawei;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Obtain {
    public static void main(String[] args) throws IOException, TimeoutException{
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("test7",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 {
                String s= new String(body);
                System.out.println(s);
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //监听test队列 如果有消息调用defaultConsumer处理
        channel.basicConsume("test7",true,defaultConsumer);

    }

}

这中事务机制会降低吞吐量 所以有了第二种处理方式

2)、Confirm模式

Conifrm 最大的好处是异步

confirmSelect 开启Confirm模式

1)、发送一条消息
package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("test8",false,false,false,null);
        //将channel设置成confirm模式
        channel.confirmSelect();
        channel.basicPublish("","test8",null,"helloworld".getBytes());
        //该模式发送消息后会返回boolean值 true代表入队成功
        if(channel.waitForConfirms()){
            System.out.println("入队成功");
        }else {
            System.out.println("入队失败");
        }
        channel.close();
        connection.close();

    }
}

2)、批量发送

全部发送之后判断

package com.lijiawei;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Send {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("test8",false,false,false,null);
        //将channel设置成confirm模式
        channel.confirmSelect();
        for (int i = 0; i < 10; i++) {
            channel.basicPublish("","test8",null,("helloworld"+i).getBytes());
        }

        //该模式发送消息后会返回boolean值 true代表入队成功
        if(channel.waitForConfirms()){
            System.out.println("入队成功");
        }else {
            System.out.println("入队失败");
        }
        channel.close();
        connection.close();

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值