RabbitMQ系列二 《RabbitMQ中的消息发布》

原生 Java 客户端进行消息通信

   

 

Direct 交换器

  DirectProducer:direct类型交换器的生产者

/**
 *类说明:direct类型交换器的生产者
 */
public class DirectProducer {

    public final static String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args)
            throws IOException, TimeoutException {
        //创建连接、连接到RabbitMQ
        ConnectionFactory connectionFactory= new ConnectionFactory();
        //设置下连接工厂的连接地址(使用默认端口5672)
        connectionFactory.setHost("192.168.85.130");
        //connectionFactory.setUsername("wong");
        //connectionFactory.setPassword("wong");

        //创建连接
        Connection connection =connectionFactory.newConnection();
        //创建信道
        Channel channel =connection.createChannel();
        //在信道中设置交换器
        channel.exchangeDeclare(EXCHANGE_NAME,BuiltinExchangeType.DIRECT);

        //申明队列(放在消费者中去做)
        //申明路由键 \消息体 (发的消息有king、mark、james)
        String[] routeKeys ={"king","mark","james"};
        for (int i=0;i<6;i++){
            String routeKey = routeKeys[i%3];
            String msg = "Hello,RabbitMQ"+(i+1);
            //发布消息
            channel.basicPublish(EXCHANGE_NAME, routeKey,null, msg.getBytes());
            System.out.println("Sent:"+ routeKey +":"+ msg);
        }

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

    }
}

 

NormalConsumer:普通的消费者

/**
 * 1、生产者和消费者一般用法
 */

/**
 *类说明:普通的消费者
 */
public class NormalConsumer {

    public static void main(String[] argv)
            throws IOException, TimeoutException {
        //创建连接、连接到RabbitMQ
        ConnectionFactory connectionFactory= new ConnectionFactory();
        //设置下连接工厂的连接地址(使用默认端口5672)
        connectionFactory.setHost("192.168.85.130");
        //connectionFactory.setUsername("wong");
        //connectionFactory.setPassword("wong");
        //创建连接
        Connection connection =connectionFactory.newConnection();
        //创建信道
        Channel channel =connection.createChannel();

        //在信道中设置交换器
        channel.exchangeDeclare(DirectProducer.EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        //申明队列(放在消费者中去做)
        String queueName="queue-k";
        channel.queueDeclare(queueName,false,false,false,null);

        //绑定:将队列(queuq-k)与交换器通过路由键绑定(k)
        String routeKey ="king";
        channel.queueBind(queueName, DirectProducer.EXCHANGE_NAME, routeKey);
        System.out.println("waiting for message ......");

        //申明一个消费者
        final Consumer consumer  = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String s, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
                String message = new String(bytes,"UTF-8");
                System.out.println("Received["+ envelope.getRoutingKey() +"]"+ message);
            }
        };

        //消息者正式开始在指定队列上消费。(queue-k)
        channel.basicConsume(queueName,true, consumer);

    }
}

 

MulitBindConsumer:队列绑定到交换器上时,是允许绑定多个路由键的,也就是多重绑定

/**
 *类说明:2、队列和交换器的多重绑定
 */
public class MultiBindConsumer {

    public static void main(String[] argv) throws IOException,
            InterruptedException, TimeoutException {
        //连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //连接rabbitMq的地址
        connectionFactory.setHost("192.168.85.130");
        //connectionFactory.setUsername("wong");
        //connectionFactory.setPassword("wong");

        // 打开连接和创建频道,与发送端一样
        Connection connection = connectionFactory.newConnection();
        //创建一个信道
        final Channel channel = connection.createChannel();
        //信道设置交换器类型(direct)
        channel.exchangeDeclare(DirectProducer.EXCHANGE_NAME,BuiltinExchangeType.DIRECT);

        //声明一个随机队列
        String queueName = channel.queueDeclare().getQueue();

        //TODO
        /*队列绑定到交换器上时,是允许绑定多个路由键的,也就是多重绑定*/
        String[] routekeys={"king","mark","james"};
        for(String routekey:routekeys){
            channel.queueBind(queueName,DirectProducer.EXCHANGE_NAME,
                    routekey);
        }
        System.out.println(" [*] Waiting for messages:");

        // 创建队列消费者
        final Consumer consumerA = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties
                                               properties,
                                       byte[] body)
                    throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println(" Received "
                        + envelope.getRoutingKey() + ":'" + message
                        + "'");
            }
        };
        channel.basicConsume(queueName, true, consumerA);
    }
}

 

MulitChannelConsumer:一个连接下允许有多个信道

/**
 *类说明:3、一个连接多个信道
 */
public class MultiChannelConsumer {

    private static class ConsumerWorker implements Runnable{
        final Connection connection;

        public ConsumerWorker(Connection connection) {
            this.connection = connection;
        }

        public void run() {
            try {
                /*创建一个信道,意味着每个线程单独一个信道*/
                Channel channel = connection.createChannel();
                //信道设置交换器类型(direct)
                channel.exchangeDeclare(DirectProducer.EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
                // 声明一个随机队列
                 String queueName = channel.queueDeclare().getQueue();
                //String queueName = "queue-k";      // 同一个队列

                //消费者名字,打印输出用
                final String consumerName =  Thread.currentThread().getName()+"-all";

                /*队列绑定到交换器上时,是允许绑定多个路由键的,也就是多重绑定*/
                String[] routekeys={"king","mark","james"};
                for(String routekey : routekeys){
                    channel.queueBind(queueName,DirectProducer.EXCHANGE_NAME,
                            routekey);
                }
                System.out.println("["+consumerName+"] Waiting for messages:");

                // 创建队列消费者
                final Consumer consumerA = new DefaultConsumer(channel) {
                    @Override
                    public void handleDelivery(String consumerTag,
                                               Envelope envelope,
                                             AMQP.BasicProperties
                                                       properties,
                                               byte[] body)
                            throws IOException {
                        String message =
                                new String(body, "UTF-8");
                        System.out.println(consumerName
                                +" Received "  + envelope.getRoutingKey()
                                + ":'" + message + "'");
                    }
                };
                channel.basicConsume(queueName, true, consumerA);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] argv) throws IOException,
            InterruptedException, TimeoutException {
        //连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //连接rabbitMq的地址
        factory.setHost("192.168.85.130");
        //打开连接和创建信道,与发送端一样
        Connection connection = factory.newConnection();
        //一个连接多个信道
        for(int i=0;i<2;i++){
            /*将连接作为参数,传递给每个线程*/
            Thread worker =new Thread(new ConsumerWorker(connection));
            worker.start();
        }
    }
}

 

MulitConsumerOneQueue:一个队列多个消费者,则会表现出消息在消费者之间的轮询发送。

/**
 *类说明:4、一个队列多个消费者,则会表现出消息在消费者之间的轮询发送。
 */
public class MultiConsumerOneQueue {

    private static class ConsumerWorker implements Runnable{
        final Connection connection;
        final String queueName;

        public ConsumerWorker(Connection connection,String queueName) {
            this.connection = connection;
            this.queueName = queueName;
        }

        public void run() {
            try {
                /*创建一个信道,意味着每个线程单独一个信道*/
                final Channel channel = connection.createChannel();
                //信道设置交换器类型(direct)
                channel.exchangeDeclare(DirectProducer.EXCHANGE_NAME,BuiltinExchangeType.DIRECT);
                /*声明一个队列,rabbitmq,如果队列已存在,不会重复创建*/
                channel.queueDeclare(queueName, false, false, false,null);
                //消费者名字,打印输出用
                final String consumerName =  Thread.currentThread().getName();

                /*队列绑定到交换器上时,是允许绑定多个路由键的,也就是多重绑定*/
                String[] routekeys={"king","mark","james"};
                for(String routekey:routekeys){
                    channel.queueBind(queueName,DirectProducer.EXCHANGE_NAME,
                            routekey);
                }
                System.out.println(" ["+consumerName+"] Waiting for messages:");

                // 创建队列消费者
                final Consumer consumerA = new DefaultConsumer(channel) {
                    @Override
                    public void handleDelivery(String consumerTag,
                                               Envelope envelope,
                                               AMQP.BasicProperties
                                                       properties,
                                               byte[] body)
                            throws IOException {
                        String message =
                                new String(body, "UTF-8");
                        System.out.println(consumerName
                                +" Received "  + envelope.getRoutingKey()
                                + ":'" + message + "'");
                    }
                };
                channel.basicConsume(queueName, true, consumerA);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] argv) throws IOException,
            InterruptedException, TimeoutException {
        //连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //连接rabbitMq的地址
        factory.setHost("192.168.85.130");

        // 打开连接和创建频道,与发送端一样
        Connection connection = factory.newConnection();
        //TODO
        //3个线程,线程之间共享队列,一个队列多个消费者
        String queueName = "focusAll";
        for(int i=0;i<3;i++){
            /*将队列名作为参数,传递给每个线程*/
            Thread worker =new Thread(new ConsumerWorker(connection, queueName));
            worker.start();
        }
    }
}

 

生产者和消费者一般用法    

DirectProducer:direct类型交换器的生产者

NormalConsumer:普通的消费者

使用 DirectProducer 作为生产者,NormalConsumer 作为消费者,消费者绑定一个队列

 

 

队列和交换器的多重绑定

DirectProducer:direct类型交换器的生产者

NormalConsumer:普通的消费者

MultiBindConsumer:队列绑定到交换器上时,是允许绑定多个路由键的,也就是多重绑定

对比:单个绑定的消费者只能收到指定的消息,多重绑定的的消费者可以收到所有路由键的消息。

 

 

一个连接多个信道

DirectProducer:direct类型交换器的生产者

MultiChannelConsumer:一个连接下允许有多个信道

一个连接,我们可以使用多线程的方式模拟多个信道进行通讯。这样可以做到多路复用。

 

一个队列多个消费者

MultiConsumerOneQueue:一个队列多个消费者,则会表现出消息在消费者之间的轮询发送。

 

 

 

Fanout

消息广播到绑定的队列

通过测试表明,不管我们如何调整生产者和消费者的路由键,都对消息的接受没有影响。

/**
 *类说明:fanout生产者
 */
public class FanoutProducer {

    public final static String EXCHANGE_NAME = "fanout_logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        /**
         * 创建连接连接到MabbitMQ
         */
        ConnectionFactory factory = new ConnectionFactory();
        // 设置MabbitMQ所在主机ip或者主机名
        factory.setHost("192.168.85.130");

        // 创建一个连接
        Connection connection = factory.newConnection();

        // 创建一个信道
        Channel channel = connection.createChannel();
        //TODO
        // 指定转发
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        /*日志消息级别,作为路由键使用*/
        String[] routekeys = {"king","mark","james"};
        for(int i=0;i<3;i++){
            String routekey = routekeys[i%3];//每一次发送一条消息
            // 发送的消息
            String message = "Hello World_"+(i+1);
            //参数1:exchange name
            //参数2:routing key
            channel.basicPublish(EXCHANGE_NAME, routekey,
                    null, message.getBytes());
            System.out.println(" [x] Sent '" + routekey +"':'"
                    + message + "'");
        }
        // 关闭频道和连接
        channel.close();
        connection.close();
    }
}
/**
 *类说明:fanout消费者 -- 绑定一个不存在的路由键,消费者会全部收到进行消费,路由键无影响
 */
public class Consumer2 {

    public static void main(String[] argv) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.85.130");


        // 打开连接和创建频道,与发送端一样
        Connection connection = factory.newConnection();
        final Channel channel = connection.createChannel();

        channel.exchangeDeclare(FanoutProducer.EXCHANGE_NAME,
                BuiltinExchangeType.FANOUT);
        // 声明一个随机队列
        String queueName = channel.queueDeclare().getQueue();

        //设置一个不存在的路由键
        String routekey="xxx";
        channel.queueBind(queueName, FanoutProducer.EXCHANGE_NAME, routekey);

        System.out.println(" [*] Waiting for messages......");

        // 创建队列消费者
        final Consumer consumerB = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                    throws IOException {
                String message = new String(body, "UTF-8");

                System.out.println( "Received ["+ envelope.getRoutingKey()
                        + "] "+message);
            }
        };

        channel.basicConsume(queueName, true, consumerB);
    }
}

 

 

Topic

通过使用“*”和“#”,使来自不同源头的消息到达同一个队列,”.”将路由键分为了几个标识符,“*”匹配 1 个,“#”匹配一个或多个。例子如下:

假设有交换器 topic_course, 人员有 king,mark,james, 技术专题有 kafka,jvm,redis, 章节有 A、B、C,路由键的规则为 人员+“.”+技术专题+“.”+章节,如:king.kafka.A。

*#的区别:如果我们发送的路由键变成 king.kafka.A 那么队列中如果绑定了 king.* 不能匹配队列中如果绑定了 king.# 能够匹配

/**
 *类说明:Topic类型的生产者
 * 假设有交换器 topic_course,
 * 人员有king,mark,james,
 * 技术专题有kafka,jvm,redis,
 * 章节有 A、B、C,
 * 路由键的规则为 人员+“.”+技术专题+“.”+章节,如:king.kafka.A。
 * 生产者--生产全部的消息3*3*3=27条消息
 */
public class TopicProducer {

    public final static String EXCHANGE_NAME = "topic_course";

    public static void main(String[] args)
            throws IOException, TimeoutException {
        /**
         * 创建连接连接到RabbitMQ
         */
        ConnectionFactory factory = new ConnectionFactory();
        // 设置MabbitMQ所在主机ip或者主机名
        factory.setHost("192.168.85.130");

        // 创建一个连接
        Connection connection = factory.newConnection();

        // 创建一个信道
        Channel channel = connection.createChannel();

        //TODO
        // 指定转发
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

        /*我们的课程,路由键最终格式类似于:king.kafka.A  king.kafka.B*/
        String[] techers={"king","mark","james"};
        for(int i=0;i<3;i++){
            String[]  modules={"kafka","jvm","redis"};
            for(int j=0;j<3;j++){
                String[]  servers={"A","B","C"};
                for(int k=0;k<3;k++){
                    // 发送的消息
                    String message = "Hello Topic_["+i+","+j+","+k+"]";
                    String routeKey = techers[i%3]+ "." + modules[j%3] +"."+ servers[k%3];
                    channel.basicPublish(EXCHANGE_NAME,routeKey,
                            null, message.getBytes());
                    System.out.println(" [x] Sent '" + routeKey +":'"
                            + message + "'");
                }
            }

        }
        // 关闭频道和连接
        channel.close();
        connection.close();
    }
}

1、要关注所有的课程,怎么做?

声明一个队列并绑定到交换器上:channel.queueBind(queueName,TopicProducer.EXCHANGE_NAME, "#");

/**
 * 1、要关注所有的课程,怎么做? #
 *类说明:所有的课程内容
 */
public class AllConsumer {

    public static void main(String[] argv) throws IOException,
            InterruptedException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.85.130");

        // 打开连接和创建频道,与发送端一样
        Connection connection = factory.newConnection();
        final Channel channel = connection.createChannel();

        channel.exchangeDeclare(TopicProducer.EXCHANGE_NAME,
                BuiltinExchangeType.TOPIC);
        // 声明一个随机队列
        String queueName = channel.queueDeclare().getQueue();
        //TODO  #
        channel.queueBind(queueName,TopicProducer.EXCHANGE_NAME, "#");

        System.out.println(" [*] Waiting for messages:");

        // 创建队列消费者
        final Consumer consumerA = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                    throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println(" AllConsumer Received "
                        + envelope.getRoutingKey()
                        + "':'" + message + "'");
            }
        };
        channel.basicConsume(queueName, true, consumerA);
    }
}

2、关注king的所有课程,怎么办?

声明一个队列并绑定到交换器上:channel.queueBind(queueName,TopicProducer.EXCHANGE_NAME, "king.#");

注意:如果这里改为 king.* 的话,则不会出现任何信息,因为 “*”匹配 1 个(使用.分割的标识的个数)

/**
 2、关注king的所有课程,怎么办?  king.#
 *类说明:
 */
public class King_AllConsumer {

    public static void main(String[] argv) throws IOException,
            InterruptedException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.85.130");

        // 打开连接和创建频道,与发送端一样
        Connection connection = factory.newConnection();
        final Channel channel = connection.createChannel();

        channel.exchangeDeclare(TopicProducer.EXCHANGE_NAME,
                BuiltinExchangeType.TOPIC);
        // 声明一个随机队列
        String queueName = channel.queueDeclare().getQueue();
        //TODO
        channel.queueBind(queueName,TopicProducer.EXCHANGE_NAME, "king.#");

        System.out.println(" [*] Waiting for messages:");

        // 创建队列消费者
        final Consumer consumerA = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                    throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println(" AllConsumer Received "
                        + envelope.getRoutingKey()
                        + "':'" + message + "'");
            }
        };
        channel.basicConsume(queueName, true, consumerA);
    }
}

3、关注king 所有的A 章节,怎么办?

声明一个队列并绑定到交换器上:channel.queueBind(queueName,TopicProducer.EXCHANGE_NAME, "king.#.A");

或者声明一个队列并绑定到交换器上:channel.queueBind(queueName,TopicProducer.EXCHANGE_NAME, "king.*.A");

4、关注kafka 所有的课程,怎么办?

声明一个队列并绑定到交换器上:channel.queueBind(queueName,TopicProducer.EXCHANGE_NAME, "#.kafka.#");

5、关注所有的B章节,怎么办?

声明一个队列并绑定到交换器上:channel.queueBind(queueName,TopicProducer.EXCHANGE_NAME, "#.B");

6、关注king的kafka A章节,怎么办?

声明一个队列并绑定到交换器上:channel.queueBind(queueName,TopicProducer.EXCHANGE_NAME, "king.kafka.A");

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值