RabbitMQ学习(三)—— 四种交换器

建立连接与关闭

  我们要启动rabbitmq服务,让后建立连接与关闭。

public static Connection conn() {
        ConnectionFactory cf = new ConnectionFactory();
        cf.setHost("127.0.0.1");
        cf.setPort(5672);
        cf.setUsername("guest");
        cf.setPassword("guest");
        cf.setVirtualHost("/");
        //cf.setUri("amqp://guest:guest@127.0.0.1:5672/");
        try {
            Connection connection = cf.newConnection();
            System.out.println("连接RabbitMQ成功");
            return connection;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
public class RabbitmqUtil {
    
    public static Connection conn() {
        ConnectionFactory cf = new ConnectionFactory();
        cf.setHost("127.0.0.1");
        cf.setPort(5672);
        cf.setUsername("guest");
        cf.setPassword("guest");
        cf.setVirtualHost("/");
        //cf.setUri("amqp://guest:guest@127.0.0.1:5672/");
        try {
            Connection connection = cf.newConnection();
            System.out.println("连接RabbitMQ成功");
            return connection;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void close(Connection coon) {
        if (coon != null) {
            try {
                coon.close();
                System.out.println("断开连接");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

建立连接时要指定必要的信息,也可以用过setUri()建立连接,这里说下setVirtualHost()是设置虚拟主机,它的意思有点像数据库。不同的信道或队列等存放到不同的虚拟主机,不同的虚拟主机之间没有关联。

点击虚拟主机名称可以去设置用户以及其它的。当然你也可以通过命令行去创建。

fanout

  该类型的交换器会把所有的消息路由到与其绑定的队列中。无论路由键与绑定键是否比配。
发布消息:

public class Producer {
    Connection connection = RabbitmqUtil.conn();

    public void send(String msg) {
        assert connection != null;
        try (Channel channel = connection.createChannel()) {
            /**
             * 声明一个交换器,其函数有多个,至少必须设置exchange,BuiltinExchangeType参数
             * exchange: 交换器名称
             * BuiltinExchangeType:交换器类型(枚举类)
             * durable:是否持久化,默认false,默认值可以看源码
             * autoDelete:自动删除,当没有队列与其绑定则自动删除,一般我们对删除操作比较谨慎,so你懂得,默认false
             * internal:内置交换器,客户段无法直接发消息,需要交换器路由到交换器
             * args:是个map,存放其它参数
             */
            channel.exchangeDeclare("ex1", BuiltinExchangeType.FANOUT, true, false, false, null);
            /**
             * 声明队列,可以不加参数则表示直接生成一个队列,通过getQueue()获取其名称,但其它参数都是默认,建议使用参数声明
             * queue: 队列名
             * durable:是否持久化
             * exclusive:是否排它,设置为true,则该队列只对本次连接有用,同一连接的不同信道可以访问,不同的连接无法访问
             * autoDelete:自动删除,至少有一个消费者连接了该队列,之后所有消费者断开连接则删除
             * args:是个map,存放其它参数
             */
            channel.queueDeclare("q1", true, false, false, null);
            //绑定队列,指明队列、交换器、绑定键、其它参数
            channel.queueBind("q1", "ex1", "rk2", null);
            /**
             * 发布消息
             * exchange:发布的交换器名称
             * routing key:路由键
             * mandatory:后面有单独出一篇,不是本篇重点
             * immediate:后面有单独出一篇,不是本篇重点
             * BasicProperties:设置属性,比如消息类型,持久化等等,可以new AMQP.BasicProperties().builder()自定义建造也可以使用提供的。
             * body:消息,是个字节数组
             */
            channel.basicPublish("ex1", "rk1", false, false, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            RabbitmqUtil.close(connection);
        }
    }
}

消费消息:

public class Consumer {
    public void recieve(String consumerTag){
        Connection conn = RabbitmqUtil.conn();
        assert conn != null;
        try (Channel channel=conn.createChannel()){
            /**
             * 拉模式:主动去获取数据
             * queue:获取哪个队列消息的队列名称
             * autoAck:是否自动确认,设置为true,消费者收到消息就发送确认,为防止消息丢失设置false,当我们确定已经消费了消息手动确认
             */
            /*GetResponse response = channel.basicGet("q1", false);
            if (response!=null){
                System.out.println(new String(response.getBody()));
                //向rabbitmq发送确认接收信息
                channel.basicAck(response.getEnvelope().getDeliveryTag(),true);
            }else {
                System.out.println("not have response");
            }*/
            /**
             * 推模式(消费者订阅),当订阅的队列中有消息时则推送到消费者
             * queue:消费的队列名
             * autoAck:自动确认消息
             * consumerTag: 消费者标签,区分不同的消费者
             * nolocal:为true则同个连接中的消费者不能消费生产者的消息
             * exclusive: 是否排它
             * args:其它参数
             * callback: 消费回调
             */
            channel.basicConsume("q1", false, consumerTag, false, false, null, new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    System.out.println(consumerTag);
                    System.out.println(envelope.toString());
                    System.out.println(new String(body));
                    /**
                     * 确认消息
                     * deliverTag:消息投递标记,它表示消费的编号
                     * multiple: 确认该编号之前的所有消息
                     */
                    channel.basicAck(envelope.getDeliveryTag(), true);
                    /**
                     * 拒绝消息
                     * deliverTag: 投递标记
                     * requeue: 重入队列,true表示该消息会重新存入队列,已到达再次消费
                     */
                    //channel.basicReject(envelope.getDeliveryTag(),false);
                    /**
                     * 批量拒绝消息
                     * deliverTag: 投递标记
                     * multiple: 拒绝该编号之前的所有消息
                     * requeue: 重入队列,true表示该消息会重新存入队列,已到达再次消费
                     */
                    //channel.basicNack(envelope.getDeliveryTag(),true,false);
                }
            });
            Thread.sleep(10*1000);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            RabbitmqUtil.close(conn);
        }
    }
}

运行

public class RabbitmqTest {

    @Test
    public void test(){
        new Producer().send("hello world!");
        new Consumer().recieve("consumer1");
    }
}

结果:
在这里插入图片描述

因为前面没有讲过代码实现,所以在这里讲的详细点,各行代码以及方法和参数我都有解释。上述消费者拉模式及推模式以及消息的确认与拒绝我都有写出来。还要注意不能用循环或定时使用拉模式代替推模式,会降低性能,还会增加复杂度。
现在说先说下fanout类型。我们发现上述的路由键是rk1,但绑定键是rk2,消费者依旧消费了消息,则证明了会把所有的消息路由到与其绑定的队列中。

direct

  direct类型则是当路由键和绑定键完全匹配时才会存消息到队列,比较简单,可以把上边的代码稍微改下就行,不演示了。

topic

  我们发现direct类型的交换器必须路由键和绑定键完全匹配,这种匹配方式的局限性太大了。所以出现topic这种模糊匹配的交换器类型。

  • 路由键与绑定键单词之间用"."分割比如(aaa.bbb.ccc)
  • 绑定键的使用"#"匹配多个单词(可以是0个)比如(aaa.#可以匹配路由键是aaa.bbb.ccc和aaa的)
  • 绑定键的使用"*"匹配一个单词比如(aaa.*匹配路由键是aaa.bbb、aaa.ccc或aaa但是不匹配aaa.bbb.ccc)。

headers

  headers类型不需要关心路由键和绑定键的规则,我们需要在生产者发布消息的时候通过AMQP.BasicProperties.Builder()设置headers属性,headers是一个map键值对。然后当绑定队列时设置第四个参数,其也是一个map,当键值对匹配时则存消息到queue。我们可以在map中存放key为x-match,值为all则表示全部匹配正确,any表示有一个匹配正确即可。当我们不设置x-match时是表示全部匹配。

public class HeaderProducer {
    Connection connection = RabbitmqUtil.conn();

    public void send(String msg) {
        assert connection != null;
        try (Channel channel = connection.createChannel()) {
            channel.exchangeDeclare("ex3", BuiltinExchangeType.HEADERS, true, false, false, null);
            channel.queueDeclare("q3", true, false, false, null);
            Map<String,Object> args=new HashMap<>();
            args.put("x-match","all");
            args.put("client","app");
            args.put("role","admin");
            channel.queueBind("q3", "ex3", "", args);
            Map<String,Object> headers=new HashMap<>();
            headers.put("client","app");
            headers.put("role","admin");
            AMQP.BasicProperties basicProperties = new AMQP.BasicProperties.Builder().headers(headers).build();
            channel.basicPublish("ex3", "", false, false, basicProperties, msg.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            RabbitmqUtil.close(connection);
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈大侠在江湖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值