RabbitMQ-客户端源码之ChannelN

ChannelN是整个RabbitMQ客户端最核心的一个类了,其包含的功能点甚多,这里需要分类阐述。
首先来看看ChannelN的成员变量:

private final Map _consumers = Collections.synchronizedMap(new HashMap());
private volatile Consumer defaultConsumer = null;
private final ConsumerDispatcher dispatcher;

private final Collection returnListeners = new CopyOnWriteArrayList();

private final Collection flowListeners = new CopyOnWriteArrayList();

private volatile CountDownLatch finishedShutdownFlag = null;

private final Collection confirmListeners = new CopyOnWriteArrayList();
private long nextPublishSeqNo = 0L;
private final SortedSet unconfirmedSet = Collections.synchronizedSortedSet(new TreeSet());
private volatile boolean onlyAcksReceived = true;

源代码中有关ChannelN的呈现顺序有所不同,这里博主为了区分开来,重新排了序。


processAsync(Command command)

在AMQChannel这个抽象类中唯一的抽象方法即为此方法,这个方法主要用来针对接受到broker的AMQCommand进行进一步的处理,至于怎么接受Socket,怎么封装成帧,怎么确定一个AMQComand已经封装完毕,都已在调用此方法前完成。此方法可以处理:Channel.Close, Basic.Deliver, Basic.Return, Channel.Flow, Basic.Ack, Basic.Nack, Basic.RecoverOk, Basic.Cancel, Channel.CloseOk等这些从broker端回传的AMQComand.
这个方法也比较长,下面也会涉及到这个方法内的内容。


Confirm.Select & Basic.Publish

在[RabbitMQ之消息确认机制(事务+Confirm)][RabbitMQ_Confirm]这篇文章中,博主就讲到RabbitMQ的producer端确认机制分为事务机制和Confirm机制,这里就来阐述下Confirm机制的内部实现。
和Confirm机制有关的成员变量有:

private final Collection confirmListeners = new CopyOnWriteArrayList();
private long nextPublishSeqNo = 0L;
private final SortedSet unconfirmedSet = Collections.synchronizedSortedSet(new TreeSet());
private volatile boolean onlyAcksReceived = true;

在使用Confirm机制的时候,首先要置Channel为Confirm模式,即向broker端发送Confirm.Select。
业务代码(DEMO实例):

channel.confirmSelect();
channel.addConfirmListener(new ConfirmListener() {
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        //TODO
    }
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        //TODO
    }
});
String message = "RabbitMQ Demo Test:" + System.currentTimeMillis();
channel.basicPublish(EXCHANGE_NAME, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
channel.waitForConfirms();

在创建完Channel之后调用channel.confirmSelect()方法即可,confirmSelect()代码如下:

public Confirm.SelectOk confirmSelect()
    throws IOException
{
    if (nextPublishSeqNo == 0) nextPublishSeqNo = 1;
    return (Confirm.SelectOk)
        exnWrappingRpc(new Confirm.Select(false)).getMethod();
}

这里的成员变量nextPublishSeqNo是用来为Confirm机制服务的,当Channel开启Confirm模式的时候,nextPublishSeqNo=1,标记第一条publish的序号,当Publish时:

public void basicPublish(String exchange, String routingKey,  boolean mandatory, boolean immediate, BasicProperties props, byte[] body) throws IOException
{
    if (nextPublishSeqNo > 0) {
        unconfirmedSet.add(getNextPublishSeqNo());
        nextPublishSeqNo++;
    }
    BasicProperties useProps = props;
    if (props == null) {
        useProps = MessageProperties.MINIMAL_BASIC;
    }
    transmit(new AMQCommand(new Basic.Publish.Builder()
                                .exchange(exchange)
                                .routingKey(routingKey)
                                .mandatory(mandatory)
                                .immediate(immediate)
                            .build(),
                            useProps, body));
}

client端向broker端Basic.Pubish发送消息并将当前的序号加入到unconfirmedSet中,并自加nextPublishSeqNo++等待下一个消息的发送。

有关Confirm.Select的详细用法可以参考:[RabbitMQ之消息确认机制(事务+Confirm)][RabbitMQ_Confirm]

之后等待broker的确认回复(Basic.Ack/.Nack):channel.waitForConfirms()

public boolean waitForConfirms(long timeout)
        throws InterruptedException, TimeoutException {
    if (nextPublishSeqNo == 0L)
        throw new IllegalStateException("Confirms not selected");
    long startTime = System.currentTimeMillis();
    synchronized (unconfirmedSet) {
        while (true) {
            if (getCloseReason() != null) {
                throw Utility.fixStackTrace(getCloseReason());
            }
            if (unconfirmedSet.isEmpty()) {
                boolean aux = onlyAcksReceived;
                onlyAcksReceived = true;
                return aux;
            }
            if (timeout == 0L) {
                unconfirmedSet.wait();
            } else {
                long elapsed = System.currentTimeMillis() - startTime;
                if (timeout > elapsed) {
                    unconfirmedSet.wait(timeout - elapsed);
                } else {
                    throw new TimeoutException();
                }
            }
        }
    }
}

可以看到waitForConfirms其实本质上是在等待unconfirmedSet变成empty,否则就线程wait()。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Java面试大全

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

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

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

打赏作者

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

抵扣说明:

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

余额充值