RabbitMq

好处:异步处理,应用解耦,流量消峰

1. 五种订阅模式

1.1 基本模式

img
在上图的模型中,有以下概念:

  • P:生产者,也就是要发送消息的程序
  • C:消费者:消息的接受者,会一直等待消息到来。
  • queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。
生产者:
 public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        //设置服务地址
        factory.setHost("127.0.0.1");
        //端口
        factory.setPort(5672);
        //设置账号信息,用户名、密码、vhost
//        factory.setVirtualHost("/guest");
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 通过工程获取连接
        Connection connection = null;
        try {
            connection = factory.newConnection();
            Channel channel = connection.createChannel();
            //队列名称,是否持久化,该队列是否只供一个消费者,是否自动删除,其他参数
            channel.queueDeclare("hello1", true, false, false, null);
            // 发送到那个交换机, 路由的key是那个,其他参数,发送消息的消息体
            channel.basicPublish("", "hello1", null, "hello world".getBytes());
            channel.close();
            connection.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }

    }

提取了工具类但有些不合理:

  public static Channel getConn(){
        ConnectionFactory factory = new ConnectionFactory();
        //设置服务地址
        factory.setHost("127.0.0.1");
        //端口
        factory.setPort(5672);
        //设置账号信息,用户名、密码、vhost
//        factory.setVirtualHost("/guest");
        factory.setUsername("guest");
        factory.setPassword("guest");
        try {
            Connection connection = factory.newConnection();
            return connection.createChannel();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        return null;
    }

消费者

 public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        //设置服务地址
        factory.setHost("127.0.0.1");
        //端口
        factory.setPort(5672);
        //设置账号信息,用户名、密码、vhost
//        factory.setVirtualHost("/guest");
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 通过工程获取连接
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
       /* DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
            public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                                       byte[] body) throws IOException {
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [x] received : " + msg + "!");
            }
        };*/
        DeliverCallback deliverCallback = (a, b)->{
            System.out.println(new String(b.getBody()));
        };
        CancelCallback callback = conn->{
            System.out.println("中断");
        };
        //回调- 消费那个队列,
        channel.basicConsume("hello1",true,deliverCallback,callback);
    }

1.2 work模型

在这里插入图片描述
角色:

  • P:生产者:任务的发布者
  • C1:消费者,领取任务并且完成任务,假设完成速度较慢
  • C2:消费者2:领取任务并完成任务,假设完成速度快

生产者

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.springframework.data.redis.connection.ConnectionUtils;

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

/**
 * @author: wz
 * @date: 2021/11/5 14:49
 * 多个消费者,消费一个服务  不重复消费
 */
public class WorkPro {
    public static void main(String[] args) throws IOException, InterruptedException, TimeoutException {
        Channel conn = Pro.getConn();
        conn.queueDeclare("work",false, false, false, null);
        for (int i = 0; i < 50; i++) {
            String content = "work start_" +i;
            conn.basicPublish("","work",null,content.getBytes());
            System.out.println("----send Ok!");
            Thread.sleep(2000);
        }
        conn.close();
    }
}

消费者

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.io.IOException;

/**
 * @author: wz
 * @date: 2021/11/5 14:49
 */
public class WorkCon {
    public static void main(String[] args) throws IOException {
        Channel channel = Pro.getConn();
        DeliverCallback deliverCallback = (a, b)->{
            System.out.println("内容:"+new String(b.getBody()));
        };
        CancelCallback callback = conn->{
            System.out.println("中断");
        };
        //回调- 消费那个队列,
        channel.basicConsume("work",true,deliverCallback,callback);
    }
}

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

import java.io.IOException;

/**
 * @author: wz
 * @date: 2021/11/5 14:49
 */
public class WorkCon1 {
    public static void main(String[] args) throws IOException {
        Channel channel = Pro.getConn();
        DeliverCallback deliverCallback = (a, b)->{
            System.out.println("内容:"+new String(b.getBody()));
        };
        CancelCallback callback = conn->{
            System.out.println("中断");
        };
        //回调- 消费那个队列,
        channel.basicConsume("work",true,deliverCallback,callback);
    }
}

1.3 订阅模式 -广播模式

img

  • P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
  • C:消费者,消息的接受者,会一直等待消息到来。
  • Queue:消息队列,接收消息、缓存消息。
  • Exchange:交换机,图中的X。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有以下3种类型:
    • Fanout:广播,将消息交给所有绑定到交换机的队列
    • Direct:定向,把消息交给符合指定routing key 的队列
    • Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列

生产者

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.Channel;

/**
 * @author: wz
 * @date: 2021/11/11 15:17
 *  广播模式--通过交换机 推送给各个队列  一条多个队列打印
 */
public class FanoutPro {
    private final static String EXCHANGE_NAME = "fanout_exchange_test";

    public static void main(String[] argv) throws Exception {
        // 获取通道
        Channel channel = Pro.getConn();

        // 声明exchange,指定类型为fanout
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        int i =1;
        while (true){
            // 消息内容
            String message = "Hello word --"+i;
            // 发布消息到Exchange
            channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
            System.out.println(" [生产者] Sent '"+i+""+ "次:"+ message + "'");
            i++;
            Thread.sleep(2000);
        }

    }
}

消费者

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author: wz
 * @date: 2021/11/5 14:49
 */
public class FanoutCon1 {
    private final static String QUEUE_NAME = "fanout_exchange_queue_1";

    private final static String EXCHANGE_NAME = "fanout_exchange_test";

    public static void main(String[] argv) throws Exception {

        // 获取通道
        Channel channel = Pro.getConn();
        //交换机绑定
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");


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

        // 监听队列,自动返回完成
        channel.basicConsume(QUEUE_NAME, true, defaultConsumer);
    }
}

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author: wz
 * @date: 2021/11/5 14:49
 */
public class FanoutCon2 {
    private final static String QUEUE_NAME = "fanout_exchange_queue_2";

    private final static String EXCHANGE_NAME = "fanout_exchange_test";

    public static void main(String[] argv) throws Exception {

        // 获取通道
        Channel channel = Pro.getConn();
        //交换机绑定
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");


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

        // 监听队列,自动返回完成
        channel.basicConsume(QUEUE_NAME, true, defaultConsumer);
    }
}

1.4 订阅模型-Direct 路由模式

在这里插入图片描述

  • P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。
  • X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列
  • C1:消费者,其所在队列指定了需要routing key 为 error 的消息
  • C2:消费者,其所在队列指定了需要routing key 为 info、error、warning 的消息

生产者

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.Channel;

/**
 * @author: wz
 * @date: 2021/11/11 15:38
 *  路由模式: 发送消息时 带个表标识, 标识相同的都会执行,不同的则不会执行
 *
 */
public class DirectPro {
    private final static String EXCHANGE_NAME = "direct_exchange_test";

    public static void main(String[] argv) throws Exception {
        // 获取通道
        Channel channel = Pro.getConn();
        // 声明exchange,指定类型为direct
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        int i = 1;
        while (true) {
            // 消息内容
            String message = "mq推送了---"+ (i>3 ?"insert":"update")+"模式";
            // 发送消息,并且指定routing key 为:insert ,代表新增商品
            channel.basicPublish(EXCHANGE_NAME, i > 3 ? "insert" : "update", null, message.getBytes());
            System.out.println(" [生产者服务:] Sent '" + message + "'");
            i++;
            Thread.sleep(2000);
        }

    }
}

消费者

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * @author: wz
 * @date: 2021/11/11 15:39
 */
public class DirectCon1 {
    private final static String QUEUE_NAME = "direct_exchange_queue_1";
    private final static String EXCHANGE_NAME = "direct_exchange_test";

    public static void main(String[] argv) throws Exception {
        // 获取通道
        Channel channel = Pro.getConn();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机,同时指定需要订阅的routing key。假设此处需要update和delete消息
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "update");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "delete");

        // 定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [消费者1] received : " + msg + "!");
            }

        };
        // 监听队列,自动ACK
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * @author: wz
 * @date: 2021/11/11 15:39
 */
public class DirectCon2 {
    private final static String QUEUE_NAME = "direct_exchange_queue_2";
    private final static String EXCHANGE_NAME = "direct_exchange_test";

    public static void main(String[] argv) throws Exception {
        // 获取通道
        Channel channel = Pro.getConn();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机,同时指定需要订阅的routing key。假设此处需要update和delete消息
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "update");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "delete");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "insert");

        // 定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用

            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [消费者1] received : " + msg + "!");
            }

        };
        // 监听队列,自动ACK
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

1.5 订阅模型-Topic

img

生产者

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.Channel;

/**
 * @author: wz
 * @date: 2021/11/11 16:00
 * 主题模式:
 */
public class TopicPro {
    private final static String EXCHANGE_NAME = "topic_exchange_test";

    public static void main(String[] argv) throws Exception {
        // 获取通道
        Channel channel = Pro.getConn();
        // 声明exchange,指定类型为topic
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        int i = 0;
        while (i<20) {
            // 消息内容
            String message = "新增商品 : id = 1001";
            // 发送消息,并且指定routing key 为:insert ,代表新增商品
            channel.basicPublish(EXCHANGE_NAME, "item.update", null, message.getBytes());
            System.out.println(" [商品服务:] Sent '" + message + "'");
            i++;
            Thread.sleep(2000);
        }

    }
}

消费者

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * @author: wz
 * @date: 2021/11/11 16:03
 */
public class TopicCon1 {
    private final static String QUEUE_NAME = "topic_exchange_queue_1";
    private final static String EXCHANGE_NAME = "topic_exchange_test";

    public static void main(String[] argv) throws Exception {
        // 获取通道
        Channel channel = Pro.getConn();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机,同时指定需要订阅的routing key。需要 update、delete
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.update");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.delete");

        // 定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [消费者1] received : " + msg + "!");
            }
        };
        // 监听队列,自动ACK
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

package com.mine.minefile.utils.rabbitMq;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * @author: wz
 * @date: 2021/11/11 16:03
 */
public class TopicCon2 {
    private final static String QUEUE_NAME = "topic_exchange_queue_2";
    private final static String EXCHANGE_NAME = "topic_exchange_test";

    public static void main(String[] argv) throws Exception {
        // 获取通道
        Channel channel = Pro.getConn();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机,同时指定需要订阅的routing key。需要 update、delete
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.*");

        // 定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [消费者2] received : " + msg + "!");
            }
        };
        // 监听队列,自动ACK
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

2.Spring AMQP

2.1 依赖

 <!-- amqp-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

2.2配置

spring:
  rabbitmq:
    username: guest
    password: guest
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值