RabbltMQ基本介绍以及基本模型的使用

不同MQ特点

1.ActiveMQ

ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。它是一个完全支持JMS规范的的消息中间件。

丰富的API,多种集群架构模式让ActiveMQ在业界成为老牌的消息中间件,在中小型企业 颇受欢迎!

2.Kafka

Kafka是LinkedIn开源的分布式发布-订阅消息系统,目前归属于Apache顶级项目。Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,

一开始的目的就是用于日志收集和传输。0.8版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。

3.RocketMQ

RocketMQ是阿里开源的消息中间件,它是纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。

RocketMQ思路起源于Kafka,但并不是Kafka的一个Copy,它对消息的可靠传输及事务性做了优化,

目前在阿里集团被广泛应用于交 易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。

4.RabbitMQ

RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。

AMQP协议更多用在企业系统内对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在

其次。

RabbitMQ比Kafka可靠,Kafka更适合IO高吞吐的处理,一般应用在大数据日志处理或对实时性(少量延迟),可靠性(少量丢数据)

要求稍低的场景使用,比如ELK日志收集。

web页面介绍

MQweb页面介绍

connections:无论生产者还是消费者,都需要与RabbitMQ建立连接后才可以完成消息的生产和消费,在这里可以查看连接情况`

channels:通道,建立连接后,会形成通道,消息的投递获取依赖通道。

Exchanges:交换机,用来实现消息的路由

Queues:队列,即消息队列,消息存放在队列中,等待消费,消费后被移除队列。

AMQP协议

 前期准备工作

工具类RabbltMQUtils

package utils;/**
 * @Author: 冷某
 * @Description: TODO
 * @DateTime: 
 **/

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

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

/**
 * @作者: 冷某
 * @类名: RabbltMQUtils类
 * @创建时间: 
 * @描述: mq连接工具类
 * @修改原图
 * @修改作者
 * @修改时间
 */
public class RabbltMQUtils {
    private static ConnectionFactory connectionFactory;

    static {
        //创建连接工厂
        connectionFactory = new ConnectionFactory();
    }

    //定义提供连接对象的方法
    public static Connection getConnection() {
        try {
            //设置连接mq的主机
            connectionFactory.setHost("101.42.157.178");
            //设置端口号
            connectionFactory.setPort(5672);
            //设置连接哪个虚拟主机
            connectionFactory.setVirtualHost("/ems");
            //设置访问虚拟主机的用户名密码
            connectionFactory.setUsername("ems");
            connectionFactory.setPassword("123");
            return connectionFactory.newConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //关闭通道和关闭连接的方法
    public static void closeConnectionAndChanel(Channel channel, Connection connection) {
        try {
            if (channel != null) {
                //关闭通道
                channel.close();
            }
            if (connection != null) {
                //关闭连接
                connection.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

导入RabbltMQ的相关依赖

<!--引入RabbitMQ相关依赖-->
<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.14.2</version>
</dependency>

第一种模型(直连)

p:生产者,相当于发送消息的程序

C:消费者:消息的接受者,会一直等待消息到来。

queue:消息队列,图中红色部分。类似一个邮箱,

可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。

生产者

//生产消息
@Test
public void testSendMessage() throws IOException, TimeoutException {
    //通过自定义工具类拿到连接对象
    Connection connection = RabbltMQUtils.getConnection();
    //获取连接中的通道
    Channel channel = connection.createChannel();
    //通道绑定对应的消息队列
    //参数1:队列的名字 如果不存在会自动创建
    //参数2:定义队列特性是否要持久化  true持久化  false不持久化
    //参数3:是否独占队列  true独占  false不独占
    //参数4:是否在消费完成后自动删除队列 true自动删除 false不自动删除
    //参数5:额外附加参数
    channel.queueDeclare("hello",false,false,false,null);
    //发布消息
    //参数1:交换机名称 参数2:队列名称 参数3:传递消息的额外设置 参数4:消息的具体内容
    channel.basicPublish("","hello",null,"hello mq".getBytes());
    //通过自定义工具类关闭连接
    RabbltMQUtils.closeConnectionAndChanel(channel,connection);
}

第二种模型(work quene) 

p:生产者:任务的发布者

C1:消费者-1,领取任务并且完成任务,假设完成速度较慢

C2:消费者-2:领取任务并完成任务,假设完成速度快

让多个消费者绑定一个队列,共同消费队列中的信息

生产者:

public static void main(String[] args) throws IOException {
    //获取连接对象
    Connection connection = RabbltMQUtils.getConnection();
    //获取通道对象
    Channel channel = connection.createChannel();
    //通过通道声明队列
    channel.queueDeclare("work", true, false, false, null);
    for (int i = 0; i < 10; i++) {
        //生产消息
        channel.basicPublish("", "work", null, (i+"hello work quene").getBytes());
    }
    //关闭资源
    RabbltMQUtils.closeConnectionAndChanel(channel, connection);
}

 消费者1:

 public static void main(String[] args) throws IOException {
        //获取连接
        Connection connection = RabbltMQUtils.getConnection();
        //获取通道对象
        Channel channel = connection.createChannel();
        channel.queueDeclare("work",true,false,false,null);
        channel.basicConsume("work",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body){
                try {
                    //设置消息处理速度为1秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("消费者-1:"+new String(body));
            }
        });
    }
}

消费者2:

public static void main(String[] args) throws IOException {
        //获取连接
        Connection connection = RabbltMQUtils.getConnection();
        //获取通道对象
        Channel channel = connection.createChannel();
        channel.queueDeclare("work",true,false,false,null);
        channel.basicConsume("work",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body){
                System.out.println("消费者-2:"+new String(body));
            }
        });
    }
}

工具类在上面

消息确认和能者多劳机制

完成一项任务可能需要几秒钟,如果其中一个消费者开始了一项长期任务,但只完成了一部分就死了,会发生什么情况。

在我们当前的代码中,一旦RabbitMQ将消息传递给使用者,它就会立即将其标记为删除。在这种情况下,

如果您杀死一个worker,我们将丢失它刚刚处理的消息。我们还将丢失发送给该特定工作进程但尚未处理的所有消息。

但我们不想失去任何任务。如果一个worker死了,我们希望把任务交给另一个工人

保证了我们的消息永不丢失

public static void main(String[] args) throws IOException {
    //获取连接
    Connection connection = RabbltMQUtils.getConnection();
    //获取通道对象
    final Channel channel = connection.createChannel();
    channel.basicQos(1);//每一次只能消费一个消息
    channel.queueDeclare("work",true,false,false,null);
    /*
     * 参数1:队列名称
     * 参数2:消息自动确认 true 消费者向RabbltMQ确定消费信息
     * */
    channel.basicConsume("work",true,new DefaultConsumer(channel){
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            try {
                //设置消息处理速度为1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("消费者-1:"+new String(body));
            /*
            * 参数1:确认队列中那个具体消息
            * 参数2:是否开启多个消息同时确认
            * */
            channel.basicAck(envelope.getDeliveryTag(),false);
        }
    });
}

第三种模型(fanout也称广播)

消息发送流程:

可以有多个消费者

每个消费者都有自己的queue(队列)

每个队列都绑定了到了Exchange(交换机)

生产者的消息发送到了交换机,交换机来决定发送到哪个队列,生产者无法决定

交换机把消息发送给了绑定过的所有队列

队列的消费者都能拿到消息,实现一条消息被多个消费者消费

生产者

public static void main(String[] args) throws IOException {
    //获取连接对象
    Connection connection = RabbltMQUtils.getConnection();
    //获取通道对象
    Channel channel = connection.createChannel();
    /*
    *将通道声明指定交换机,一条消息多个消费者同时消费
    * 参数1:交换机名称
    * 参数2:交换机的类型 fanout广播类型
    * */
    channel.exchangeDeclare("logs","fanout");
    //发送消息
    channel.basicPublish("logs","",null,"fanout type message".getBytes());
    //关闭资源
    RabbltMQUtils.closeConnectionAndChanel(channel, connection);
}

 消费者1,2,3

public static void main(String[] args) throws IOException {
    //获取连接对象
    Connection connection = RabbltMQUtils.getConnection();
    //连接通道
    Channel channel = connection.createChannel();
    /*
    * 通道绑定对应的交换机
    * 参数1:交换机名称
    * 参数2:交换机类型
    * */
    channel.exchangeDeclare("logs","fanout");
    /*
    * 临时队列
    * 减轻了RabbltMQ存放队列的数量
    * */
    String queue = channel.queueDeclare().getQueue();
    /*
    * 绑定交换机和队列
    * 参数1:队列名称
    * 参数2:交换机名称
    * */
    channel.queueBind(queue,"logs","");
    //消费消息
    channel.basicConsume(queue,true,new DefaultConsumer(channel){
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("消费者1:"+new String(body));
        }
    });
}

第四种模型(Routing)

1.Routing之订阅模型-Direct(直连)

在Fanout模式中,一条消息,会被所有订阅的队列都消费,但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。

在Direct模型下:队列与交换机的绑定,不能是任意绑定了,

而是要指定一个RoutingKey(路由key)消息的发送方在 向 Exchange发送消息时,

也必须指定消息的 RoutingKey。Exchange不再把消息交给每一个绑定的队列,

而是根据消息的Routing Key进行判断,只有队列的Routingkey与消息的 Routing key完全一致,

才会接收到消息

P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。

X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列

C1:消费者,其所在队列指定了需要routing key 为 error 的消息

C2:消费者,其所在队列指定了需要routing key 为 info、error、warning 的消息

生产者

public static void main(String[] args) throws IOException {
    //获取连接对象
    Connection connection = RabbltMQUtils.getConnection();
    //获取通道对象
    Channel channel = connection.createChannel();
    /*
     * 通过通道声明交换机
     * 参数1:交换机名称
     * 参数2:direct路由模式
     * */
    channel.exchangeDeclare("logs_direct", "direct");
    //发送消息
    String routingKey = "info";
    channel.basicPublish("logs_direct", routingKey, null, ("这是direct模型发布的基于route key: [" + routingKey + "] 发送的消息").getBytes());
    //关闭连接
    RabbltMQUtils.closeConnectionAndChanel(channel, connection);
}

 

消费者1

public static void main(String[] args) throws IOException {
    Connection connection = RabbltMQUtils.getConnection();
    Channel channel = connection.createChannel();
    //通道声明交换机以及交换的类型
    channel.exchangeDeclare("logs_direct","direct");
    //创建一个临时队列
    String queue = channel.queueDeclare().getQueue();
    //基于route key绑定队列和交换机
    channel.queueBind(queue,"logs_direct","error");
    //获取消费的消息
    channel.basicConsume(queue,true,new DefaultConsumer(channel){
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("消费者1"+new String(body));
        }
    });
}

 消费者2

public static void main(String[] args) throws IOException {
    Connection connection = RabbltMQUtils.getConnection();
    Channel channel = connection.createChannel();
    //通道声明交换机以及交换的类型
    channel.exchangeDeclare("logs_direct","direct");
    //创建一个临时队列
    String queue = channel.queueDeclare().getQueue();
    //临时队列与交换机进行绑定
    channel.queueBind(queue,"logs_direct","info");
    channel.queueBind(queue,"logs_direct","error");
    channel.queueBind(queue,"logs_direct","waring");
    //获取消费的消息
    channel.basicConsume(queue,true,new DefaultConsumer(channel){
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("消费者2"+new String(body));
        }
    });
}

2.Routing 之订阅模型-Topic 

Topic类型的Exchange与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。

只不过Topic类型Exchange可以让队列在绑定Routing key的时候使用通配符!

这种模型Routingkey一般都是由一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert

统配符 

* (star) can substitute for exactly one word.    匹配不多不少恰好1个词
# (hash) can substitute for zero or more words.  匹配零个、一个或多个词

audit.#    匹配audit、audit.irs 、或者audit.irs.corporate等
audit.*   只能匹配 audit.irs

生产者

//获取连接对象
Connection connection = RabbltMQUtils.getConnection();
Channel channel = connection.createChannel();

//声明交换机以及交换机类型 topic
channel.exchangeDeclare("topics","topic");

//发布消息
String routekey = "user.save";

channel.basicPublish("topics",routekey,null,("这里是topic动态路由模型,routekey: ["+routekey+"]").getBytes());

//关闭资源
RabbltMQUtils.closeConnectionAndChanel(channel,connection);

消费者1

//获取连接
Connection connection = RabbltMQUtils.getConnection();
Channel channel = connection.createChannel();

//声明交换机以及交换机类型
channel.exchangeDeclare("topics","topic");
//创建一个临时队列
String queue = channel.queueDeclare().getQueue();
//绑定队列和交换机  动态统配符形式route key
channel.queueBind(queue,"topics","*.user.*");

//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.out.println("消费者1: "+ new String(body));
    }
});

消费者2

//获取连接
Connection connection = RabbltMQUtils.getConnection();
Channel channel = connection.createChannel();

//声明交换机以及交换机类型
channel.exchangeDeclare("topics","topic");
//创建一个临时队列
String queue = channel.queueDeclare().getQueue();
//绑定队列和交换机  动态统配符形式route key
channel.queueBind(queue,"topics","*.user.#");

//消费消息
channel.basicConsume(queue,true,new DefaultConsumer(channel){
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.out.println("消费者2: "+ new String(body));
    }
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值