RabbitMQ(2)基础知识 (未整理)

客户端开发
1连接 RabbitMQ

ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost(virtualHost) ;
factory.setHost(IP ADDRESS);
factory . setPort(PORT) ;
Connection conn = factory.newConnection(); 
Channel channel = conn.createChannel(); 

在创建之后, Channel 可以用来发送或者接收消息了。

注意要点:
Connection 可以用来创建多个 Channel 实例,但是 Channel 实例不能在线程问共享,应用程序应该为每一个线程开辟一个 Channel.
以多线程问共享 Channel 实例是非线程安全的,

Channel 或者 Connection 中有个 isOpen 方法可以用来检测其是否己处于开启状态,。但并不推荐在生产环境的代码上使用isOpen 方法,这个方法有可能产生竞争。

如果在使用 Channel 的时候其己经处于关闭状态,那么程序会抛出一个
com.rabbitmq client.ShutdownSignalException ,我们只需捕获这个异常即可。

try (
channel.bas cQos(l);
} catch (ShutdownSignalException sse) { }
catch (IOException ioe) { }

2使用交换器和队列

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

上面创建了一个持久化的、非自动删除的、绑定类型为 direct 的交换器,同时也创建了一个非持久化的、排他的、自动删除的队列(此队列的名称由 RabbitMQ 自动生成)。这里的交换器和队列也都没有设置特殊的参数。

上面的代码也展示了如何使用路由键将队列和交换器绑定起来。上面声明的队列具备如下
特性 只对当前应用中同一个 Connection 层面可用,同一个 Connection 的不同 Channel
可共用,并且也会在应用连接断开时自动删除。

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

这里的队列被声明为持久化的 非排他的 非自动删除的,而且也被分配另 个确定的己知的名称。

注意 Channel API 方法都是可以重载的。
生产者和消费者都可以声明一个交换器或者队列。如果尝试声明一个已经存在的交换器或
者队列 只要声明的参数完全匹配现存的交换器或者队列, RabbitMQ 就可 以什么都不做 并成
功返回 如果声明的参数不匹配则会抛出异常。

        Exchange.DeclareOk exchangeDeclare(
                exchange,
                type,
                durable,
                autoDelete,
                internal,
                arguments)throws IOException;
    }
~ exchange 交换器的名称。

type 交换器的类型,常见的如 fanout, direct,topic
durable: 设置是否持久化 durable 设置为 true 表示持久化, 反之是非持久 。持久化就是将交换器存盘,在服务器重启 的时候不会丢失 关信息。

autoDelete 设置是否自动删除。autoDelete 设置为 true 表示自动删除

。自动删除的前提是至少有一个队列或者交换器与这个交换器绑定, 之后所有与这个交换器绑定的队列或者交换器都与 解绑。注意不能错误地 这个参数理解为 "当与此交换器
连接的客户端都断开时 RabbitMQ 会自动 除本交换器

~ internal 设置是否是内置的。如果设置为 true ,则表示是内置的交换器,客户端程序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式。

Exchange.DeclareOk exchangeDeclarePassive(String name) throws IOException

这个方法在实际应用过程中还是非常有用的,它主要用来检测相应的交换器是否存在。如果
存在则正常返回:如果不存在则抛出异常 404 channel exception ,同时 Channel 也会被关闭。

queueDeclare 方法详解

Queue.DeclareOk queueDec1are() throws IOException;

不带任何参数的 queueDeclare 方法默认创建一个由 RabbitMQ 命名的(类似这种
amq.gen-LhQzlgv3GhDOv8PIDabOXA 名称,这种队列也称之为匿名队列〉、排他的、自动删除
的、非持久化的队列。

 Queue. DeclareOk queueDeclare (String queue , boolean durable , boolean exclusive ,boolean autoDelete , Map<Str ng Object> arguments) throws IOException; 
~queue 队列的名称。

durable: 设置是否持久化。为 true 则设置队列为持久化。持久化的队列会存盘,在服务器重启的时候可以保证不丢失相关信息。

exclusive 设置是否排他。为 true 则设置队列为排他的。如果一个队列被声明为排他队列,该队列仅对首次声明它的连接可见,并在连接断开时自动删除。这里需要注意三点:
排他队列是基于连接( Connection) 可见的,同一个连接的不同信道 (Channel)是可以同时访问同一连接创建的排他队列; "首次"是指如一个连接己经声明了排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同:即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除,这种队列适用于一个客户端同时发送和读取消息的应用场景。

autoDelete: 设置是否自动删除。为 true 则设置队列为自动删除。自动删除的前提是:
至少有一个消费者连接到这个队列,之后所有与这个队列连接的消费者都断开时,才会自动删除。不能把这个参数错误地理解为: “当连接到此队列的所有客户端断开时,这个队列自动删除”,因为生产者客户端创建这个队列,或者没有消费者客户端与这个队列连接时,都不会自动删除这个队列。

生产者和消费者都能够使用 queueDeclare 来声明一个队列,但是如果消费者在同一个信道上订阅了另一个队列,就无法再声明队列了。必须先取消订阅,然后将信道直为"传输"模式,之后才能声明队列。

队列删除:
(1) Queue . De1eteOk queueDe1ete(String queue) throws IOExcept on
(2) Queue . De1eteOk queueDe1ete(String queue , boo1ean ifUnused, boolean ifEmpty )throws IOExcept on;
(3 ) vo queueDe1eteNoWa t(Str ng queue , boo1ean fUnused boo1ean ifEmpty)throws IOException;

其中 queue 表示队列的名称 ifUnused 可以参考上一小节的交换器。 ifEmpty 设置为
true 表示在队列为空(队列里面没有任何消息堆积)的情况下才能够删除。
清空:

Queue.PurgeOk queuePurge(Str 工口 queue ) throws IOException; 

这个方
法用来清空队列中的内容,而不删除队列本身,

删除交换器的方法

(1) Exchange.DeleteOk exchangeDelete(String exchange) throws IOException ;
(2) void exchangeDeleteNoWait(String exchange , boolean ifUnused) throws
IOException ;
(3 ) Exchange . DeleteOk exchangeDelete(String exchange , boo1ean ifUnused) throws
IOException; 

ifUnused 用来设置是否在交换器没有被使用的情
况下删除 如果 isUnused 设置为 tru ,则只有在此交换器没有被使用的情况下才会被删除
如果设置 fa se ,则无论如何这个交换器都要被删除。

queueBind 方法详解

    AMQP.Queue.BindOk queueBind(String queue , String exchange , String routingKey);
(3) void queueBindNoWait(String queue , String exchange , String routingKey,
Map<String, Object> arguments) throws IOException ; 

~ queue: 队列名称:
~exchange: 交换器的名称:
~routingKey: 用来绑定队列和交换器的路由键
~argument: 定义绑定的一些参数。

,也可以将已经被绑定的队列和交换器进行解绑

(2) Queue. UnbindOk queueUnbind (String queue , String exchange , String routingKey,
Map<String , Object> arguments) throws IOException; 

exchangeBind 方法详解

我们不仅可以将交换器与队列绑定,也可以将交换器与交换器绑定,

(1) Exchange.BindOk exchangeBind(String destination , String source , String
routingKey) throws IOException;
(2) Exchange . BindOk exchangeBind(String destination, String source , String
routingKey, Map<String , Object> arguments) throws IOException ; 
(3 ) void exchangeBindNoWait(String destination, String sour ce , String routingKey,
Map<String, Object> arguments) throws IOException; 

如果要发送一个消息,可以使用 Channel 类的 basicPublish 方法,

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

为了更好地控制发送,可以使用 mandatory 这个参数 或者可以发送一些特定属性的信息

channe1.basicPub1ish(exchangeName , rout ngKey mandatory,
MessageProperties.PERSISTENT TEXT PLAIN ,
messageBodyBytes) ;

上面这行代码发送了一条消息,这条消息的投递模式 del very mode 设直为 ,即消息会
被持久化(即存入磁盘)在服务器中。同时这条消息的优先级 priority )设置为 ont
为" text/plain" 可以自己设定消息的属性:

也可以发送 条带有 headers 的消息
Map<String , Object> headers = new HashMap<String, Object>() ;
headers.put( " loca1tion" , “here " );
headers . put( " time " , " today” );
channe1 .bas cPub1 sh(exchangeName rout ngKey
new AMQP.BasicPropert eS Bu 1der ()
.headers(headers)
.build()) ,
messageBodyBytes) ;

basicPublish
,有几个重载方法,参数意义:

(1) void basicPub1 sh (String exchange , String routingKey, BasicProperties props,
byte[) body) throws IOException ; 
(2) void basicPub1ish(String exchange , String routingKey, boo1ean mandatory, BasicProperties props, byte[] body) throws IOException; 
(3) void basicPublish(Stri 口 g exchange , String routingKey, boolean mandatory,
boolean immediate , BasicProperties props, byte[] body) throws IOException ; 

~ exchange: 交换器的名称,指明消息需要发送到哪个交换器中 如果设置为空字符串,
则消息会被发送到 bbitMQ 默认的交换器中。
~ routingKey 路由键,交换器根据路由键将消息存储到相应的队列之中

~ props 消息的基本属性集,其包含 14 个属性成员,分别有 contentType
content ncoding headers Map) deliveryMode priority
correlationld replyTo expiration messageld timestamp type userld
appld cluster 。其中常用的几种都在上面的示例中进行了演示
~byte [1 body 消息体 playload ,真正需要发送的消息

消费消息

分两种 推Push 模式和拉 Pull 模式 推模式采用 Basic.Consume
进行消费,而拉模式则是调用 Basic.Get 进行消费。

推模式
在推模式中,可以通过持续订阅的方式来消费消息,

接收消息一般通过实现 Consumer 接口或者继承 DefaultConsumer 类来实现。
不同的订阅采用不同的消费者标签 (co umerTag) 来区
分彼

        boolean autoAck = false;
        channel.basicQos(64);
        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();
                        // (process the message components here . .. )
                        channel.basicAck(deliveryTag, false);
                    }
                });

Channel 类中 basicConsume 方法有如下 种形式

(1) String basicConsume(String queue , Consumer callback) throws IOException ;
(2) String basicConsume(String ueue boolean autoAck, Consumer callback) throws
OExcept on
(3) String bas cConsume(String queue , boolean autoAck, Map<String, Object>
arguments, Consumer callback) throws IOException ;
(4) String basicConsume(String queue , bool ean autoAck, String consumerTag,
Consumer callback) throws IOException ; 
(5) String basicConsume(String queue , boolean autoAck, String consumerTag,
boolean noLocal , boolean exclusive , Map<Str ng Object> arguments, Consumer callback)
throws IOException ; 
参数意义
queue 队列的名称
autoAck 设置是否自动确认。建议设成 fa se ,即不自动确认:
~consumerTag: 消费者标签,用来区分多个消费者:
~noLocal 设置为 tru 则表示不能将同一个 Connectio口中生产者发送的消息传送给这个 Connection 中的消费者:
exclusive 设置是否排他
arguments 设置消费者的其他参数:
~callback 设置消费者的回调函数。用来处理 Rabb itM 推送过来的消息,比如
DefaultConsumer 使用时需要客户端重写 (override) 其中的方法。

对于消费者客户端来说重写 handleDelivery 方法是十分方便的。更复杂的消费者客户
端会重写更多的方法 具体如下

void handleConsumeOk(String consumerTag) ;
void handleCancelOk(String consumerTag);
void handleCancel(String consumerTag) throws IOException;
void handleShutdownSignal(String consumerTag , ShutdownSignalException sig) ;
void handleRecoverOk(String consumerTag);

比如 handleShutdownSignal 方法,当 Channel 或者 Connection 关闭的时候会调用。
再者, handleConsumeOk 方法会在其他方法之前调用,返回消费者标签。

channel . basicCancel(consumerTag) ;
注意上面这行代码会首先触发 handleConsumerOk 方法,之后触发 handleDelivery
方法,最后才触发 handleCancelOk 方法。

消费者客户端同样需要考虑线程安全的问题。消费者客户端的这些 callback
会被分配到与 Channel 不同的线程池上 这意味着消费者客户端可以安全地调用这些阻塞方
法,比如 channel queueDeclare channel basicCancel 等。

每个 Channel 都拥有自己独立的线程。最常用的做法是一 Channel 对应一个消费者,
也就是意味着消费者彼此之间没有任何关联。当然也可以在一个 Channel 中维持多个消费者,
但是要注意 个问题,如果 Channel 中的 个消费者 直在运行,那么其他消费者的 callback
会被"耽搁"

拉模式

通过 channel basicGet 方法可以单条地获取消息,其返回值是 GetResponeo Channel 类的 basicGet 方法没有其他重载方法,只有
GetResponse basicGet(String queue , boolean autoAck) throws IOException;

GetResponse response = channel.basicGet(QUEUE NAME , false) ;
System.out.println(new String(response.getBody()));
channel .basicAck(response . getEnvelope() . getDeliveryTag() , false); 

Basic Consume 将信道 (Channel) 直为接收模式,直到取消队列的订阅为止。在接收
模式期间, RabbitMQ 会不断地推送消息给消费者,当然推送消息的个数还是会受到 Basic.Qos
的限制.如果只想从队列获得单条消息而不是持续订阅,建议还是使用 Basic.Get 进行消费.但
是不能将 Basic.Get 放在一个循环里来代替 Basic.Consume ,这样做会严重影响 RabbitMQ
的性能.如果要实现高吞吐量,消费者理应使用 Basic.Consume 方法。

消费端的确认与拒绝:

采用消息确认机制后,只要设置 autoAck 参数为 false ,消费者就有足够的时间处理消息
(任务) ,不用担心处理消息过程中消费者进程挂掉后消息丢失的问题 因为 RabbitMQ 会一直等待持有消息直到消费者显式调用 Basic.Ack 命令为止。

RabbitMQ 不会为未确认的消息设置过期时间,它判断此消息是否需要重新投递给消费者的依据是消费该消息的消费者连接是否己经断开,
在这里插入图片描述
Ready" 待投递给消费者的消息数
“Unacknowledged” 己经投递给消费者但是未收到确认信号的消息数。

拒绝当前的消息

void basicReject(long deliveryTag, boolean requeue) throws IOException; 

deliveryTag 可以看作消息的编号 ,它是 一个 64 位的长整型值,最大值是
9223372036854775807
如果 requeue 参数设置为 true ,则 RabbitMQ 会重新将这条消息存入
队列,以便可以发送给下 个订阅的消费者;如果 requeue 参数设置为 false ,则 RabbitMQ
立即会把消息从队列中移除,而不会把它发送给新的消费者。

Basic.Reject 命令一次只能拒绝一条消息 ,如果想要批量拒绝消息 ,则可以使用
Basic.Nack 这个命令 消费者客户端可以调用 channel.basicNack 方法来实现,

void basicNack(long deliveryTag, boolean multiple , boolean requeue) throws IOException; 

multiple 参数设置为 false 则表示拒绝编号为 deliveryTag的这 条消息,这时候 basicNack
basicReject 方法一样; multiple 参数设置为 true 则表示拒绝 deliveryTag 编号之前所有未被当前消费者确认的消息。

注意要点:
channel.basicReject 或者 channel.basicNack 中的 requeue 设直为 false ,可以启用"死信队列"的功能。死信队列可以通过检测被拒绝或者未送达的消息来追踪问题。

在应用程序使用完之后,需要关闭连接,释放资源:
channel.close();
conn.close() ;
在 Conection 关闭的时候,Channel 也会自动关闭。
Connection 和Channel 最终都是会成为 Closed 的状态,不论是程序正常调用的关闭方法,或者是客户端的异常,再或者是发生了网络异常。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值