RabbitMQ系列03-rabbitmq快速入门案例

maven依赖

	<dependency>
      <groupId>com.rabbitmq</groupId>
      <artifactId>amqp-client</artifactId>
      <version>5.14.2</version>
    </dependency>

RabbitMQ系列03-rabbitmq快速入门案例_第1张图片

简单模式(simple)

在这里插入图片描述

  • producer 生产者
    1.创建连接工厂
    2.创建连接
    3.获取通道
    4.通过通道声明队列
    5.发布消息
    6.关闭通道/连接
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * @Description 生产者
 * @Author chanyu
 * @Date 2022/5/25 19:08
 * @Version 1.0
 **/
public class Producer {
    private final static String QUEUE_NAME = "测试队列";
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;
        try{
            // 2.创建连接
            connection = factory.newConnection("生产者");
            // 3.获取通道
            channel = connection.createChannel();
            /**
             * 队列名
             * 是否持久化
             * 是否排他,是否是独占独立
             * 是否自动删除,随最后一个消息消费完毕后是否自动删除队列
             * 携带其他参数
             */
            // 4.通过通道声明队列
            channel.queueDeclare(QUEUE_NAME,false,false,false,null);
            // 5.发布消息
            String msg = "一条消息";
            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes(StandardCharsets.UTF_8));
        }finally {
            // 6.关闭通道
            if (channel!=null && channel.isOpen()){
                channel.close();
            }
            // 7.关闭连接
            if (connection!=null && connection.isOpen()){
                connection.close();
            }
        }

    }
}

执行后在控制台可以找到相关队列和消息数。
RabbitMQ系列03-rabbitmq快速入门案例_第2张图片

  • consumer消费者
    1.创建连接工厂
    2.创建连接
    3.获取通道
    4.消费消息
    5.关闭通道/连接
import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @Description 消费者
 * @Author chanyu
 * @Date 2022/5/25 19:08
 * @Version 1.0
 **/
@Slf4j
public class Comsumer {
    private final static String QUEUE_NAME = "测试队列";
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try{
            // 2.创建连接
            connection = factory.newConnection("生产者");
            // 3.获取通道
            channel = connection.createChannel();
            // 4.消费消息
            channel.basicConsume(QUEUE_NAME, true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    log.info("consumerTag:{}", consumerTag);
                    log.info("消费消息:{}", new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                    log.info("consumerTag:{}", consumerTag);
                    log.info("消费消息失败");
                }
            });
        }finally {
            // 5.关闭通道
            if (channel!=null && channel.isOpen()){
                channel.close();
            }
            // 6.关闭连接
            if (connection!=null && connection.isOpen()){
                connection.close();
            }
        }
    }
}

Work queues

  • 确保消息不会丢失需要做两件事:我们需要将队列和消息都标记为持久的。
  • 关于消息持久性的注意事项:
    将消息标记为持久性并不能完全保证消息不会丢失。虽然它告诉 RabbitMQ 将消息保存到磁盘,但是当 RabbitMQ 接受消息并且还没有保存它时,仍然有很短的时间窗口。此外,RabbitMQ 不会对每条消息都执行fsync(2) ——它可能只是保存到缓存中而不是真正写入磁盘。持久性保证并不强,但对于我们简单的任务队列来说已经绰绰有余了。如果需要更强的保证,那么可以使用发布者确认(待处理)
  • 任务创建者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

public class NewTask {
    private static final String TASK_QUEUE_NAME = "task_queue";
    private static final String[] msg = new String[]{"jdkssd.dds", "d23d.2w.ttt", "2332.uyyt", "eewe.764", "dsada.55"};

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);

            String message = String.join(" ", msg);

            channel.basicPublish("", TASK_QUEUE_NAME,
                    MessageProperties.PERSISTENT_TEXT_PLAIN,
                    message.getBytes("UTF-8"));
            System.out.println(" [x] Sent '" + message + "'");
        }
    }

}
  • 任务执行者

    • 循环调度:默认情况下,消息是按顺序循环发放的,不论消费端是否有能力完成任务,这种方式等于是平均分配任务。这种调度方式下,如果某些消费任务很重,导致这些消费端很忙,而可能其他消费端很空闲,这会让消费端得不到充分合理的使用。
    • 公平调度:为了解决这个问题,我们可以使用带有prefetchCount = 1设置的basicQos方法 。这告诉 RabbitMQ 一次不要给一个 worker 多条消息。或者说,在消费端处理并确认之前的消息之前,不向忙的消费端发送新消息,而是把它分派给下一个空闲的消费端。
  • 消息确认
    完成一项任务可能需要几秒钟。一旦 RabbitMQ 将消息传递给消费者,它会立即将其标记为删除。在这种情况下,如果消费者出了问题。将丢失所有发送给该消费者但尚未处理的消息。
    但是我们不想丢失任何任务。我们希望将任务交付给另一个消费者。
    为了确保消息永远不会丢失,RabbitMQ 支持消息确认。消费者发回一个确认,告诉 RabbitMQ 一个特定的消息已经被接收、处理并且 RabbitMQ 可以自由地删除它。
    如果消费者在没有发送 ack 的情况下死亡(其通道关闭、连接关闭或 TCP 连接丢失),RabbitMQ 将理解消息未完全处理并将重新排队。如果同时有其他消费者在线,它会迅速将其重新发送给另一个消费者。这样,即使消费者偶尔异常,也可以确保不会丢失任何消息。
    对消费者交付确认强制执行超时(默认为 30 分钟)。

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

public class Worker {

    private static final String TASK_QUEUE_NAME = "task_queue";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        final Connection connection = factory.newConnection();
        final Channel channel = connection.createChannel();

        channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);


        // 公平调度,消费完(响应)完才继续接受任务。不设置该项则是默认轮询调度
        channel.basicQos(1);

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");

            System.out.println(" [x] Received '" + message + "'");
            try {
                doWork(message);
            } finally {
                System.out.println(" [x] Done");
                // 消息确认
                // 确认收到一个或多个消息
                channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
            }
        };
        channel.basicConsume(TASK_QUEUE_NAME, false, deliverCallback, consumerTag -> {
        });
    }

    private static void doWork(String task) {
        for (char ch : task.toCharArray()) {
            if (ch == '.') {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException _ignored) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
}

Publish/Subscribe(fanout)

fanout交换机,只要绑定了该交换机的队列都会收到消息,与routingkey无关。

  • 生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * @Description 生产者
 * @Author chanyu
 * @Date 2022/5/25 19:08
 * @Version 1.0
 **/
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = null;
        Channel channel = null;
        try{
            // 2.创建连接
            connection = factory.newConnection("生产者");
            // 3.获取通道
            channel = connection.createChannel();

            /**
             * 队列名
             * 是否持久化(非持久化队列会随服务器重启取消,消息也一样)
             * 是否排他,是否是独占独立
             * 是否自动删除,随最后一个消息消费完毕后是否自动删除队列
             * 携带其他参数
             */
            // 通过通道声明队列
            channel.queueDeclare("fanoutqueue1",true,false,false,null);
            channel.queueDeclare("fanoutqueue2",true,false,false,null);

            // 4.发布消息
            String msg = "一条消息";

            // 5.准备交换机 交换机类型
            String exchangeName = "fanoutExchange";
            String type = "fanout";
            // fanout 模式下 routekey无效,会发布给所有绑定了该交换机的队列
            channel.exchangeDeclare(exchangeName,type,true,false,null);
            String routeKey = "";
            // 队列绑定交换机
            channel.queueBind("fanoutqueue1",exchangeName,routeKey);
            channel.queueBind("fanoutqueue2",exchangeName,routeKey);

            channel.basicPublish(exchangeName,routeKey,null,msg.getBytes(StandardCharsets.UTF_8));
        }finally {
            // 6.关闭通道
            if (channel!=null && channel.isOpen()){
                channel.close();
            }
            // 7.关闭连接
            if (connection!=null && connection.isOpen()){
                connection.close();
            }
        }
    }
}
  • 消费者
import com.rabbitmq.client.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @Description 消费者
 * @Author chanyu
 * @Date 2022/5/25 19:08
 * @Version 1.0
 **/
@Slf4j
public class Consumer implements Runnable{
    @SneakyThrows
    @Override
    public void run() {
        // 1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try{
            // 2.创建连接
            connection = factory.newConnection("生产者");
            // 3.获取通道
            channel = connection.createChannel();

            String queue_name = Thread.currentThread().getName();

            // 4.消费消息
            channel.basicConsume(queue_name, true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    log.info("{}消费消息:{}",queue_name, new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                    log.info("消费消息失败");
                }
            });
            System.in.read();
        }finally {
            // 5.关闭通道
            if (channel!=null && channel.isOpen()){
                channel.close();
            }
            // 6.关闭连接
            if (connection!=null && connection.isOpen()){
                connection.close();
            }
        }
    }


    public static void main(String[] args) throws IOException, TimeoutException {
        new Thread(new Consumer(),"fanoutqueue1").start();
        new Thread(new Consumer(),"fanoutqueue2").start();
    }
}

Routing(direct)

在fanout的基础上,通过routingkey来筛选发送消息的队列。

  • 生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * @Description 生产者
 * @Author chanyu
 * @Date 2022/5/25 19:08
 * @Version 1.0
 **/
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = null;
        Channel channel = null;
        try{
            // 2.创建连接
            connection = factory.newConnection("生产者");
            // 3.获取通道
            channel = connection.createChannel();

            /**
             * 队列名
             * 是否持久化(非持久化队列会随服务器重启取消,消息也一样)
             * 是否排他,是否是独占独立
             * 是否自动删除,随最后一个消息消费完毕后是否自动删除队列
             * 携带其他参数
             */
            // 通过通道声明队列
            channel.queueDeclare("directqueue1",true,false,false,null);
            channel.queueDeclare("directqueue2",true,false,false,null);
            // 4.发布消息
            String msg = "一条消息";

            // 5.准备交换机 交换机类型
            String exchangeName = "directExchange";
            String type = "direct";
            // direct 模式下 routekey有效,会发布给所有绑定了该交换机的队列
            channel.exchangeDeclare(exchangeName,type,true,false,null);
            // 队列绑定交换机
            channel.queueBind("directqueue1",exchangeName,"email");
            channel.queueBind("directqueue2",exchangeName,"sms");

            // 发布给路由为email的队列
            channel.basicPublish(exchangeName,"email",null,msg.getBytes(StandardCharsets.UTF_8));
        }finally {
            // 6.关闭通道
            if (channel!=null && channel.isOpen()){
                channel.close();
            }
            // 7.关闭连接
            if (connection!=null && connection.isOpen()){
                connection.close();
            }
        }
    }
}


  • 消费者
import com.rabbitmq.client.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @Description 消费者
 * @Author chanyu
 * @Date 2022/5/25 19:08
 * @Version 1.0
 **/
@Slf4j
public class Consumer implements Runnable{
    @SneakyThrows
    @Override
    public void run() {
        // 1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setUsername("admin");
        factory.setPassword("admin");
        factory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try{
            // 2.创建连接
            connection = factory.newConnection("生产者");
            // 3.获取通道
            channel = connection.createChannel();

            String queue_name = Thread.currentThread().getName();

            // 4.消费消息
            channel.basicConsume(queue_name, true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    log.info("{}消费消息:{}",queue_name, new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                    log.info("消费消息失败");
                }
            });
            System.in.read();
        }finally {
            // 5.关闭通道
            if (channel!=null && channel.isOpen()){
                channel.close();
            }
            // 6.关闭连接
            if (connection!=null && connection.isOpen()){
                connection.close();
            }
        }
    }

    // 结果是只有directqueue1 email路由的队列才有消费信息
    public static void main(String[] args) throws IOException, TimeoutException {
        new Thread(new Consumer(),"directqueue1").start();
        new Thread(new Consumer(),"directqueue2").start();
    }
}

Topics

主题模式,路由模糊匹配,.# 匹配零级或多级,.*匹配1级。

  • 生产者

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * @Description 生产者
 * @Author chanyu
 * @Date 2022/5/25 19:08
 * @Version 1.0
 **/
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = null;
        Channel channel = null;
        try{
            // 2.创建连接
            connection = factory.newConnection("生产者");
            // 3.获取通道
            channel = connection.createChannel();

            /**
             * 队列名
             * 是否持久化(非持久化队列会随服务器重启取消,消息也一样)
             * 是否排他,是否是独占独立
             * 是否自动删除,随最后一个消息消费完毕后是否自动删除队列
             * 携带其他参数
             */
            // 通过通道声明队列
            channel.queueDeclare("topicqueue1",true,false,false,null);
            channel.queueDeclare("topicqueue2",true,false,false,null);
            channel.queueDeclare("topicqueue3",true,false,false,null);
            channel.queueDeclare("topicqueue4",true,false,false,null);
            // 4.发布消息
            String msg = "一条消息";

            // 5.准备交换机 交换机类型
            String exchangeName = "topicExchange";
            String type = "topic";
            // direct 模式下 routekey有效,会发布给所有绑定了该交换机的队列
            channel.exchangeDeclare(exchangeName,type,true,false,null);

            // .*是至少有1级 .#是有0--n级
            // 队列绑定交换机
            channel.queueBind("topicqueue1",exchangeName,"com.#");
            channel.queueBind("topicqueue2",exchangeName,"*.test.*");
            channel.queueBind("topicqueue3",exchangeName,"#.cy.#");
            channel.queueBind("topicqueue4",exchangeName,"#.user.*");

            // 发布给路由为email的队列
            String routingKey = "com.test.cy.user.xxx";
            channel.basicPublish(exchangeName,routingKey,null,msg.getBytes(StandardCharsets.UTF_8));
        }finally {
            // 6.关闭通道
            if (channel!=null && channel.isOpen()){
                channel.close();
            }
            // 7.关闭连接
            if (connection!=null && connection.isOpen()){
                connection.close();
            }
        }
    }
}


  • 消费者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * @Description 生产者
 * @Author chanyu
 * @Date 2022/5/25 19:08
 * @Version 1.0
 **/
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = null;
        Channel channel = null;
        try{
            // 2.创建连接
            connection = factory.newConnection("生产者");
            // 3.获取通道
            channel = connection.createChannel();

            /**
             * 队列名
             * 是否持久化(非持久化队列会随服务器重启取消,消息也一样)
             * 是否排他,是否是独占独立
             * 是否自动删除,随最后一个消息消费完毕后是否自动删除队列
             * 携带其他参数
             */
            // 通过通道声明队列
            channel.queueDeclare("topicqueue1",true,false,false,null);
            channel.queueDeclare("topicqueue2",true,false,false,null);
            channel.queueDeclare("topicqueue3",true,false,false,null);
            channel.queueDeclare("topicqueue4",true,false,false,null);
            // 4.发布消息
            String msg = "一条消息";

            // 5.准备交换机 交换机类型
            String exchangeName = "topicExchange";
            String type = "topic";
            // direct 模式下 routekey有效,会发布给所有绑定了该交换机的队列
            channel.exchangeDeclare(exchangeName,type,true,false,null);

            // .*是有且仅有一个(一级) .#是有零个(零级)或多个(多级)
            // 队列绑定交换机
            channel.queueBind("topicqueue1",exchangeName,"com.#");
            channel.queueBind("topicqueue2",exchangeName,"*.test.*");
            channel.queueBind("topicqueue3",exchangeName,"#.cy.#");
            channel.queueBind("topicqueue4",exchangeName,"#.user.*");

            // 发布给路由为email的队列
            String routingKey = "com.test.cy.user.xxx";
            channel.basicPublish(exchangeName,routingKey,null,msg.getBytes(StandardCharsets.UTF_8));
        }finally {
            // 6.关闭通道
            if (channel!=null && channel.isOpen()){
                channel.close();
            }
            // 7.关闭连接
            if (connection!=null && connection.isOpen()){
                connection.close();
            }
        }
    }
}

执行消费结果是:
在这里插入图片描述
队列2test前后各一级,不满足。

其他

未绑定交换机的队列,都是绑定默认交换机。
消息都是通过交换机给队列。
管理平台中,队列中获取消息要使用nack(不消费)方式获取查看消息。
如果使用ack方式会消费掉消息。

RabbitMQ系列03-rabbitmq快速入门案例_第3张图片

交换机类型:
fanout:发布订阅,所有绑定该交换机的都会收到消息,与路由key无关。
direct :处理路由键,带路由发送消息。
topic :模糊匹配的路由key。匹配规则:# 代表0–n级,*代表必须有一级。
Headers :根据参数匹配(发布时带入参数,消费端匹配)。

你可能感兴趣的:(rabibitmq,rabbitmq,java)