RabbitMQ学习3.1:java操作RabbitMQ(原生java客户端amqp-client)

环境:

amqp-client:5.3.0

摘要说明:

本篇文章主要讲述java如何使用amqp-client对RabbitMQ进行操作;从连接、使用Exchanges和Queues、推送消息、消费消息等;

步骤:

1.引入依赖

引入依赖:

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

2.连接和渠道(Connections and Channels)

a.连接RabbitMQ

使用给定参数(主机名,端口号等)连接到RabbitMQ节点:

ConnectionFactory factory = new ConnectionFactory();
// “guest”/“guest”默认情况下,仅限于localhost连接
factory.setUsername("xxx");// 用户名
factory.setPassword("xxx");// 用户密码
factory.setVirtualHost("/xxx");// 虚拟主机
factory.setHost("xxx.xxx.xxx.xxx");// 主机名
factory.setPort(xxxx);// 端口号
Connection conn = factory.newConnection();

或者使用URI进行连接:

ConnectionFactory factory = new ConnectionFactory();
factory.setUri("amqp://userName:password@hostName:portNumber/virtualHost");
Connection conn = factory.newConnection();

b.打开渠道

要想发送和接受消息,必须先创建一个Channel即打开同道:

Channel channel = conn.createChannel();

c.断开连接

要断开连接,只需关闭同道和连接:

channel.close();
conn.close();

请注意,手动关闭可能被视为良好做法,但这里并不是必需的 - 无论如何,当底层连接关闭时,它将自动完成。

d.连接和渠道寿命

连接意味着长寿。底层协议是为长时间运行的连接而设计和优化的。这意味着每次操作打开一个新连接,例如发布的消息,是不必要的,并且强烈不鼓励,因为它会引入大量的网络往返和开销。

通道也意味着长寿,但由于许多可恢复的协议错误将导致通道关闭,因此通道寿命可能短于其连接的寿命。每次操作关闭和打开新通道通常是不必要的,但可能是合适的。如有疑问,请考虑重复使用频道。

通道级异常(例如尝试从不存在的队列中使用)将导致通道关闭。无法再使用已关闭的通道,也不会再从服务器接收任何事件(例如消息传递)。RabbitMQ将记录通道级异常,并将启动通道的关闭序列(见下文)。

3.使用Exchange和Queues(Exchanges and Queues)

a.创建声明Exchange和Queues

当只有一个客户端想要使用一个队列时:它不需要一个众所周知的名称,没有其他客户端可以使用它(独占)并且将自动清除(自动删除) 。这种典型的声明队列的方式如下:

channel.exchangeDeclare(exchangeName, "", true);
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, exchangeName, routingKey);

上述代码的意义是:

1、一种持久的,非自动交换的“direct”类型的Exchange;

2、具有生成名称的非持久,独占,自动删除队列;

3、将队列绑定到具有给定routingKey的Exchange;

如果多个客户端希望共享具有已知名称的队列,声明方式如下:

channel.exchangeDeclare(exchangeName,“direct”,true);
channel.queueDeclare(queueName,true,false,false,null);
channel.queueBind(queueName,exchangeName,routingKey);

上述代码的意义是:
1、一种持久的,非自动交换的“direct”类型的Exchange

2、具有已知名称的持久,非独占,非自动删除队列

4.发布消息(Publishing Messages)

使用Channel.basicPublish进行基本消息发布:

byte[] messageBodyBytes = "Hello, world!".getBytes();
channel.basicPublish(exchangeName, routingKey, null, messageBodyBytes);

可以加上强制标志和指定消息类型进行推送控制:

channel.basicPublish(exchangeName, routingKey, mandatory,
                     MessageProperties.PERSISTENT_TEXT_PLAIN,
                     messageBodyBytes);

使用new AMQP.BasicProperties.Builder()构建消息属性,如设置传递模式、优先级、内容类型:

channel.basicPublish(exchangeName, routingKey,
             new AMQP.BasicProperties.Builder()
               .contentType("text/plain")//消息类型
               .deliveryMode(2)//传递模式
               .priority(1)//优先级
               .userId("bob")//userId
               .build()),
               messageBodyBytes);

可推送带header的消息:

Map<String, Object> headers = new HashMap<String, Object>();
headers.put("latitude",  51.5252949);
headers.put("longitude", -0.0905493);

channel.basicPublish(exchangeName, routingKey,
             new AMQP.BasicProperties.Builder()
               .headers(headers)//头部消息
               .build()),
               messageBodyBytes);

可推送消息有效期的消息:

channel.basicPublish(exchangeName, routingKey,
             new AMQP.BasicProperties.Builder()
               .expiration("60000")//有效期
               .build()),
               messageBodyBytes);

推送方式如下:

a.direct类型Exchange

 

direct:把消息投递到那些binding key与routing key完全匹配的队列中。

首先我们知道exchange和queue进行绑定时会设置routingkey;

然后我们在将消息发送到exchange时会设置对应的routingkey;

示例如下

String exchangeName = "exchangeName_direct";// 交换机名称
String routingKey = "routingKey_direct";// 路由key
String queueName = "queueName_direct";// 队列名称
String message1 = "Hello RabbitMQ";// 消息内容
channel.exchangeDeclare(exchangeName, "direct", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
channel.basicPublish(exchangeName, routingKey, null, message1.getBytes("UTF-8"));//注意此时routingKey要与channel.queueBind一致

使用空字符串也是direct类型的exchange,则此routingKey需为队列名称;因为队列会默认跟空字符串exchange进行绑定且routingkey为队列名称:

String queueName = "rabbitMQ.test";// 队列名称
channel.queueDeclare(queueName, false, false, false, null);// 声明一个队列
String message = "Hello RabbitMQ";// 消息内容
channel.basicPublish("", queueName, null, message.getBytes("UTF-8"));// 发送消息到队列中

实战 :

Direct类型交换机消息生产类:

package cn.cc.basic.exchange.direct;

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

/**
 * @模块名:basic
 * @包名:cn.cc.basic.exchange.direct
 * @类名称: DirectProducer
 * @类描述:【类描述】 Direct类型交换机消息生产类
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年12月12日上午10:16:02
 */

public class DirectProducer {
    // 连接信息
    public final static String USER_NAME = "test";

    public final static String PASSWORD = "123456";

    public final static String VHOST = "/test";

    public final static String HOST = "47.99.159.236";

    public final static Integer PORT = 5672;

    // 队列信息
    // 交换机名称
    public final static String EXCHANGE_NAME = "test-exchangeName_direct_1";

    // 路由key
    public final static String[] ROUTINGKEYS = { "test-routingKey_direct_1", "test-routingKey_direct_2", "test-routingKey_direct_3" };

    // 队列名称
    public final static String QUEUENAME = "test-queueName_direct_1";

    public static void main(String[] args) {

        try {
            ConnectionFactory factory = new ConnectionFactory();
            // “guest”/“guest”默认情况下,仅限于localhost连接
            factory.setUsername(USER_NAME);// 用户名
            factory.setPassword(PASSWORD);// 用户密码
            factory.setVirtualHost(VHOST);// 虚拟主机
            factory.setHost(HOST);// 主机名
            factory.setPort(PORT);// 端口号
            // factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT +"/"+ VHOST);
            Connection conn = factory.newConnection();
            /**
             * 创建信道
             */
            Channel channel = conn.createChannel();

            /**
             * 创建交换器
             */
            // 指明交换器名称+类型+是否持久化
            // BuiltinExchangeType.DIRECT
            channel.exchangeDeclare(EXCHANGE_NAME, "direct", false);

            /**
             * 推送消息
             */
            for (int i = 0; i < 9; i++) {
                // 指明推送的路由键
                String routingkey = ROUTINGKEYS[i % 3];
                // 推送消息
                String msg = "Hellol,RabbitMq_" + routingkey;
                // 发布消息,需要参数:交换器,路由键,
                channel.basicPublish(EXCHANGE_NAME, routingkey, null, msg.getBytes());
                System.out.println("Sent " + EXCHANGE_NAME + ":" + routingkey + ":" + msg);
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

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

/**
 * @模块名:basic
 * @包名:cn.cc.basic.exchange.direct
 * @描述:MulitConsumerOneQueue.java
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年12月12日下午3:49:17
 */

package cn.cc.basic.exchange.direct;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

/**
 * @模块名:basic
 * @包名:cn.cc.basic.exchange.direct
 * @类名称: MulitConsumerOneQueue
 * @类描述:【类描述】一个队列多个消费者,则会表现出消息在消费者之间的轮询发送。
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年12月12日下午3:49:17
 */

public class MulitConsumerOneQueue {
    // 连接信息
    public final static String USER_NAME = "test";

    public final static String PASSWORD = "123456";

    public final static String VHOST = "%2Ftest";

    public final static String HOST = "47.99.159.236";

    public final static Integer PORT = 5672;

    // 队列信息
    // 交换机名称
    public final static String EXCHANGE_NAME = "test-exchangeName_direct_1";

    // 路由key
    public final static String[] ROUTINGKEYS = { "test-routingKey_direct_1", "test-routingKey_direct_2",
            "test-routingKey_direct_3" };

    // 队列名称
    public final static String QUEUENAME = "test-queueName_direct_1";

    /**
     * 
     * @模块名:basic
     * @包名:cn.cc.basic.exchange.direct
     * @类名称: ConsumerWorker
     * @类描述:【类描述】创建消费者
     * @版本:1.0
     * @创建人:cc
     * @创建时间:2018年12月12日下午3:50:21
     */
    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();
                channel.exchangeDeclare(EXCHANGE_NAME, "direct");

                /* 声明一个队列,rabbitmq,如果队列已存在,不会重复创建 */
                channel.queueDeclare(queueName, false, false, false, null);

                // 消费者名字,打印输出用
                final String consumerName = Thread.currentThread().getName();
                for (String routingkey : ROUTINGKEYS) {
                    // (多重绑定)
                    channel.queueBind(queueName, DirectProducer.EXCHANGE_NAME, routingkey);
                }
                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) {
        try {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
            // 打开连接和创建频道,与发送端一样
            Connection connection = factory.newConnection();

            // 3个线程,线程之间共享队列,一个队列多个消费者
            for (int i = 0; i < 3; i++) {
                /* 将队列名作为参数,传递给每个线程 */
                Thread worker = new Thread(new ConsumerWorker(connection, EXCHANGE_NAME));
                worker.start();
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }

    }
}

 通用rabbitMQ消费者:
 

package cn.cc.basic.exchange.direct;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

/**
 * @模块名:basic
 * @包名:cn.cc.basic.exchange.direct
 * @类名称: NormalConsumer
 * @类描述:【类描述】通用rabbit消费者
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年12月12日上午11:23:44
 */

public class NormalConsumer {

    // 连接信息
    public final static String USER_NAME = "test";

    public final static String PASSWORD = "123456";

    public final static String VHOST = "%2Ftest";

    public final static String HOST = "47.99.159.236";

    public final static Integer PORT = 5672;

    // 队列信息
    // 交换机名称
    public final static String EXCHANGE_NAME = "test-exchangeName_direct_1";

    // 路由key
    public final static String[] ROUTINGKEYS = { "test-routingKey_direct_1", "test-routingKey_direct_2",
            "test-routingKey_direct_3" };

    // 队列名称
    public final static String QUEUENAME = "test-queueName_direct_1";

    public static void main(String[] args) {

        try {
            /**
             * mq连接
             */
            ConnectionFactory factory = new ConnectionFactory();
            // “guest”/“guest”默认情况下,仅限于localhost连接
            factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
            Connection conn = factory.newConnection();
            /**
             * 创建信道
             */
            Channel channel = conn.createChannel();

            /**
             * 创建交换器
             */
            // 指明交换器名称+类型+是否持久化
            // BuiltinExchangeType.DIRECT
            channel.exchangeDeclare(EXCHANGE_NAME, "direct", false);

            /**
             * 声明一个队列
             */
            // 指定队列名称+是否持久化+是否独占+非自动删除
            channel.queueDeclare(QUEUENAME, false, false, false, null);

            /**
             * 绑定,将队列和交换器通过路由键进行绑定
             */
            channel.queueBind(QUEUENAME, EXCHANGE_NAME, ROUTINGKEYS[0]);
            // channel.queueBind(QUEUENAME, EXCHANGE_NAME, ROUTINGKEYS[2]);
            System.out.println("waiting for message........");

            /**
             * 声明了一个消费者
             */
            final Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                        byte[] body) {
                    String message;
                    try {
                        message = new String(body, "UTF-8");
                        System.out.println("Received[" + envelope.getRoutingKey() + "]" + message);
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            /**
             * 消费者正式开始消费指定队列上消息
             */
            channel.basicConsume(QUEUENAME, true, consumer);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

}

b.fanout类型Exchange

fanout:把所有发送到该Exchange的消息投递到所有与它绑定的队列中。

即无需进行routingKey匹配只要进行exchange绑定:

String exchangeName = "exchangeName_fanout";// 交换机名称
String routingKey = "routingKey_fanout";// 路由key
String queueName = "queueName_fanout";// 队列名称
String message = "Hello RabbitMQ";// 消息内容
channel.exchangeDeclare(exchangeName, "fanout", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
channel.basicPublish(exchangeName, "", null, message.getBytes("UTF-8"));//routingKey为空即可

实战 :

 

Fanout类型交换机消息生产类:


package cn.cc.basic.exchange.fanout;

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

/**
 * @模块名:basic
 * @包名:cn.cc.basic.exchange.fanout
 * @类名称: FanoutProducer
 * @类描述:【类描述】 Fanout类型交换机消息生产类
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年12月12日下午3:59:57
 */

public class FanoutProducer {

    // 连接信息
    public final static String USER_NAME = "test";

    public final static String PASSWORD = "123456";

    public final static String VHOST = "/test";

    public final static String HOST = "47.99.159.236";

    public final static Integer PORT = 5672;

    // 队列信息
    // 交换机名称
    public final static String EXCHANGE_NAME = "test-exchangeName_fanout_1";

    // 路由key
    public final static String[] ROUTINGKEYS = { "test-routingKey_fanout_1", "test-routingKey_fanout_2",
            "test-routingKey_fanout_3" };

    // 队列名称
    public final static String QUEUENAME = "test-queueName_fanout_1";

    public static void main(String[] args) {

        try {
            ConnectionFactory factory = new ConnectionFactory();
            // “guest”/“guest”默认情况下,仅限于localhost连接
            factory.setUsername(USER_NAME);// 用户名
            factory.setPassword(PASSWORD);// 用户密码
            factory.setVirtualHost(VHOST);// 虚拟主机
            factory.setHost(HOST);// 主机名
            factory.setPort(PORT);// 端口号
            // factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT +"/"+ VHOST);
            Connection conn = factory.newConnection();
            /**
             * 创建信道
             */
            Channel channel = conn.createChannel();

            /**
             * 创建交换器
             */
            // 指明交换器名称+类型+是否持久化
            // BuiltinExchangeType.fanout
            channel.exchangeDeclare(EXCHANGE_NAME, "fanout", false);

            /**
             * 推送消息
             */
            for (int i = 0; i < 9; i++) {
                // 指明推送的路由键
                String routingkey = ROUTINGKEYS[i % 3];
                // 推送消息
                String msg = "Hellol,RabbitMq_" + routingkey;
                // 发布消息,需要参数:交换器,路由键,
                channel.basicPublish(EXCHANGE_NAME, routingkey, null, msg.getBytes());
                System.out.println("Sent " + EXCHANGE_NAME + ":" + routingkey + ":" + msg);
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

}

单个绑定: 


package cn.cc.basic.exchange.fanout;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

/**
 * @模块名:basic
 * @包名:cn.cc.basic.exchange.fanout
 * @类名称: Customer1
 * @类描述:【类描述】单个绑定
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年12月12日下午4:01:29
 */

public class Customer1 {

    // 连接信息
    public final static String USER_NAME = "test";

    public final static String PASSWORD = "123456";

    public final static String VHOST = "%2Ftest";

    public final static String HOST = "47.99.159.236";

    public final static Integer PORT = 5672;

    // 队列信息
    // 交换机名称
    public final static String EXCHANGE_NAME = "test-exchangeName_fanout_1";

    // 路由key
    public final static String[] ROUTINGKEYS = { "test-routingKey_fanout_1", "test-routingKey_fanout_2",
            "test-routingKey_fanout_3" };

    // 队列名称
    public final static String QUEUENAME = "test-queueName_fanout_1";

    public static void main(String[] args) {

        try {
            /**
             * mq连接
             */
            ConnectionFactory factory = new ConnectionFactory();
            // “guest”/“guest”默认情况下,仅限于localhost连接
            factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
            Connection conn = factory.newConnection();
            /**
             * 创建信道
             */
            Channel channel = conn.createChannel();

            /**
             * 创建交换器
             */
            // 指明交换器名称+类型+是否持久化
            // BuiltinExchangeType.fanout
            channel.exchangeDeclare(EXCHANGE_NAME, "fanout", false);

            /**
             * 声明一个队列
             */
            // 指定队列名称+是否持久化+是否独占+非自动删除
            channel.queueDeclare(QUEUENAME, false, false, false, null);

            /**
             * 绑定,将队列和交换器通过路由键进行绑定
             */
            channel.queueBind(QUEUENAME, EXCHANGE_NAME, ROUTINGKEYS[0]);
            System.out.println("waiting for message........");

            /**
             * 声明了一个消费者
             */
            final Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                        byte[] body) {
                    String message;
                    try {
                        message = new String(body, "UTF-8");
                        System.out.println("Received[" + envelope.getRoutingKey() + "]" + message);
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            /**
             * 消费者正式开始消费指定队列上消息
             */
            channel.basicConsume(QUEUENAME, true, consumer);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

}

多重绑定: 


package cn.cc.basic.exchange.fanout;

import cn.cc.basic.exchange.direct.DirectProducer;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

/**
 * @模块名:basic
 * @包名:cn.cc.basic.exchange.fanout
 * @类名称: Customer2
 * @类描述:【类描述】多重绑定
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年12月12日下午4:01:44
 */

public class Customer2 {

    // 连接信息
    public final static String USER_NAME = "test";

    public final static String PASSWORD = "123456";

    public final static String VHOST = "%2Ftest";

    public final static String HOST = "47.99.159.236";

    public final static Integer PORT = 5672;

    // 队列信息
    // 交换机名称
    public final static String EXCHANGE_NAME = "test-exchangeName_fanout_1";

    // 路由key
    public final static String[] ROUTINGKEYS = { "test-routingKey_fanout_1", "test-routingKey_fanout_2",
            "test-routingKey_fanout_3" };

    // 队列名称
    public final static String QUEUENAME = "test-queueName_fanout_1";

    public static void main(String[] args) {

        try {
            /**
             * mq连接
             */
            ConnectionFactory factory = new ConnectionFactory();
            // “guest”/“guest”默认情况下,仅限于localhost连接
            factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
            Connection conn = factory.newConnection();
            /**
             * 创建信道
             */
            Channel channel = conn.createChannel();

            /**
             * 创建交换器
             */
            // 指明交换器名称+类型+是否持久化
            // BuiltinExchangeType.fanout
            channel.exchangeDeclare(EXCHANGE_NAME, "fanout", false);

            /**
             * 声明一个队列
             */
            // 指定队列名称+是否持久化+是否独占+非自动删除
            channel.queueDeclare(QUEUENAME, false, false, false, null);

            /**
             * 绑定,将队列和交换器通过路由键进行绑定
             */
            for (String routingkey : ROUTINGKEYS) {
                // (多重绑定)
                channel.queueBind(QUEUENAME, DirectProducer.EXCHANGE_NAME, routingkey);
            }
            System.out.println("waiting for message........");

            /**
             * 声明了一个消费者
             */
            final Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                        byte[] body) {
                    String message;
                    try {
                        message = new String(body, "UTF-8");
                        System.out.println("Received[" + envelope.getRoutingKey() + "]" + message);
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            /**
             * 消费者正式开始消费指定队列上消息
             */
            channel.basicConsume(QUEUENAME, true, consumer);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

}

c.topic类型Exchange

topic:将消息路由到binding key与routing key模式匹配的队列中。

即无需进行routingKey匹配只要进行exchange绑定:

String exchangeName = "exchangeName_topic";// 交换机名称
String queueName = "queueName_topic";// 队列名称
String message = "Hello RabbitMQ";// 消息内容
String bindKey = "bindKey.#";// 绑定key
String routingKey = "bindKey.topic";// 路由key
channel.exchangeDeclare(exchangeName, "topic", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, bindKey);
channel.basicPublish(exchangeName, routingKey, null, message.getBytes("UTF-8"));// routingKey与bindKey路由批跑

实战: 

topic类型交换机消息生产类:


package cn.cc.basic.exchange.topic;

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

/**
 * @模块名:basic
 * @包名:cn.cc.basic.exchange.topic
 * @类名称: topicProducer
 * @类描述:【类描述】 topic类型交换机消息生产类
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年12月12日下午3:59:57
 */

public class TopicProducer {

    // 连接信息
    public final static String USER_NAME = "test";

    public final static String PASSWORD = "123456";

    public final static String VHOST = "/test";

    public final static String HOST = "47.99.159.236";

    public final static Integer PORT = 5672;

    // 队列信息
    // 交换机名称
    public final static String EXCHANGE_NAME = "test-exchangeName_topic_1";

    // 路由key
    public final static String[] ROUTINGKEYS1 = { "test-A", "test-B", "test-C" };

    public final static String[] ROUTINGKEYS2 = { "test-a", "test-b", "test-c" };

    public final static String[] ROUTINGKEYS3 = { "test-1", "test-2", "test-3" };

    // 队列名称
    public final static String QUEUENAME = "test-queueName_topic_1";

    public static void main(String[] args) {

        try {
            ConnectionFactory factory = new ConnectionFactory();
            // “guest”/“guest”默认情况下,仅限于localhost连接
            factory.setUsername(USER_NAME);// 用户名
            factory.setPassword(PASSWORD);// 用户密码
            factory.setVirtualHost(VHOST);// 虚拟主机
            factory.setHost(HOST);// 主机名
            factory.setPort(PORT);// 端口号
            // factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT +"/"+ VHOST);
            Connection conn = factory.newConnection();
            /**
             * 创建信道
             */
            Channel channel = conn.createChannel();

            /**
             * 创建交换器
             */
            // 指明交换器名称+类型+是否持久化
            // BuiltinExchangeType.topic
            channel.exchangeDeclare(EXCHANGE_NAME, "topic", false);

            /**
             * 推送消息
             */
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    for (int k = 0; k < 3; k++) {
                        // 发送的消息
                       
                        String routeKey = ROUTINGKEYS1[i % 3] + "." + ROUTINGKEYS2[j % 3] + "." + ROUTINGKEYS3[k % 3];
                        String message = "Hello Topic_[" + i + "," + j + "," + k + "]";
                        channel.basicPublish(EXCHANGE_NAME, routeKey, null, message.getBytes());
                        System.out.println(" [x] Sent '"+routeKey+":" + message + "'");
                    }
                }

            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

}

topic类型消费者: 


package cn.cc.basic.exchange.topic;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

/**
 * @模块名:basic
 * @包名:cn.cc.basic.exchange.topic
 * @类名称: TopicCutomer
 * @类描述:【类描述】
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2018年12月12日下午4:16:05
 */

public class TopicCustomer {

    // 连接信息
    public final static String USER_NAME = "test";

    public final static String PASSWORD = "123456";

    public final static String VHOST = "%2Ftest";

    public final static String HOST = "47.99.159.236";

    public final static Integer PORT = 5672;

    // 队列信息
    // 交换机名称
    public final static String EXCHANGE_NAME = "test-exchangeName_topic_1";

    // 队列名称
    public final static String QUEUENAME = "test-queueName_topic_1";

    public static void main(String[] args) {

        try {
            /**
             * mq连接
             */
            ConnectionFactory factory = new ConnectionFactory();
            // “guest”/“guest”默认情况下,仅限于localhost连接
            factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
            Connection conn = factory.newConnection();
            /**
             * 创建信道
             */
            Channel channel = conn.createChannel();

            /**
             * 创建交换器
             */
            // 指明交换器名称+类型+是否持久化
            // BuiltinExchangeType.topic
            channel.exchangeDeclare(EXCHANGE_NAME, "topic", false);

            // 声明一个随机队列
            // String queueName = channel.queueDeclare().getQueue();
            // channel.queueDeclare(QUEUENAME, false, false, false, null);
            /**
             * 声明一个队列
             */
            // 指定队列名称+是否持久化+是否独占+非自动删除
            channel.queueDeclare(QUEUENAME, false, false, false, null);

            /**
             * 绑定,将队列和交换器通过路由键进行绑定
             * 
             * 注意多重绑定后是不会覆盖前面的
             */
            // #接收所有路由键信息
            // channel.queueBind(QUEUENAME, EXCHANGE_NAME, "#");
            // 若只匹配前后用#,其他用*
            channel.queueUnbind(QUEUENAME, EXCHANGE_NAME, "#");
            channel.queueBind(QUEUENAME, EXCHANGE_NAME, "test-A.#");
            // channel.queueBind(QUEUENAME, EXCHANGE_NAME, "#.test-1");
            // channel.queueBind(QUEUENAME, EXCHANGE_NAME, "*.test-a.*");

            System.out.println("waiting for message........");

            /**
             * 声明了一个消费者
             */
            final Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                        byte[] body) {
                    String message;
                    try {
                        message = new String(body, "UTF-8");
                        System.out.println("Received[" + envelope.getRoutingKey() + "]" + message);
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            /**
             * 消费者正式开始消费指定队列上消息
             */
            channel.basicConsume(QUEUENAME, true, consumer);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

}

d.headers类型Exchange

headers:  Headers Exchange不同于上面三种Exchange,它是根据Message的一些头部信息来分发过滤Message,忽略routing key的属性,如果Headers信息和message消息的头信息相匹配,那么这条消息就匹配上了。

此类型不常用,第一步需在绑定时配置参数信息即Headers信息,第二步需在推送时构建BasicProperties,且BasicProperties.headers需与绑定一致:

import com.rabbitmq.client.AMQP.BasicProperties.Builder;
String exchangeName = "exchangeName_headers";// 交换机名称
String queueName = "queueName_headers";// 队列名称
String message = "Hello RabbitMQ";// 消息内容
Map<String, Object> headers = new HashMap<String, Object>();//构建headers
headers.put("param1", "param1");
headers.put("param2", "param2");
headers.put("param3", "param3");
headers.put("param4", "param4");
channel.exchangeDeclare(exchangeName, "headers", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, "",headers);
Builder builder = new Builder();
builder.headers(headers);//构建headers
channel.basicPublish(exchangeName, "", builder.build(), message.getBytes("UTF-8"));// routingKey与bindKey路由批跑

5.订阅消息消费(Receiving Messages by Subscription ("Push API")

使用channel.basicConsume进行订阅消息然后消费;

在这里强烈建议使用自定义消费者标签,且消费者标签需不同;同时建议手动回调,保障业务安全;

上面发布的消息都可以用下面的方式进行消费:

import com.rabbitmq.client.DefaultConsumer;
boolean autoAck = false;// 标记手动回调
String myConsumerTag = "myConsumerTag";//消费者标签
channel.basicConsume(queueName, autoAck, myConsumerTag, new DefaultConsumer(channel) {
	@Override
	public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
       byte[] body) throws IOException {
		String routingKey = envelope.getRoutingKey();
		String contentType = properties.getContentType();
		long deliveryTag = envelope.getDeliveryTag();
		String message = new String(body, "UTF-8");// 获取消息体
		System.out.println(routingKey + ":" + contentType + ":" + deliveryTag + ":" + message);
		channel.basicAck(deliveryTag, false);// 手动回调
	}
});

:消费者的回调在与实例化其Channel的线程分开的线程池中调度。这意味着Consumer可以安全地在Connection或Channel上调用阻塞方法,例如  Channel#queueDeclare或 Channel#basicCancel。
每个Channel都有自己的调度线程。对于每个渠道一个消费者的最常见用例,这意味着消费者不会占用其他消费者。如果每个Channel有多个消费者,请注意长时间运行的消费者可能会阻止向该Channel上的其他消费者发送回叫 。

6.检索单个消息消费(Retrieving Individual Messages ("Pull API")

使用channel.basicGet获取单个消息进行消费;

建议如上手动回调:

boolean autoAck = false;// 标记手动回调
GetResponse response = channel.basicGet(queueName, autoAck);// 手动获取单个消息
if (response == null) {
	// No message retrieved.
} else {
	AMQP.BasicProperties props = response.getProps();// 获取BasicProperties
	byte[] body = response.getBody();// 获取消息体
	long deliveryTag = response.getEnvelope().getDeliveryTag();// 获取消息id
	System.out.println(deliveryTag + ":" + new String(body, "UTF-8") + ":" + props.getHeaders());
	channel.basicAck(deliveryTag, false); // 手动回调
}

更多可参考官网:http://www.rabbitmq.com/api-guide.html

7.源码地址

https://github.com/cc6688211/rabbitmq_study

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值