持久化和非持久化消息发送的策略

通过setDeliveMode设置持久跟非持久属性。
消息的同步发送,跟异步发送:

  1. 消息的同步发送跟异步发送是针对broker 而言。
    在默认情况下,非持久化的消息是异步发送的。
    非持久化消息且非事物模式下是同步发送的。
    在开启事务的情况下,消息都是异步发送的。
  2. 通过以下三种方式来设置异步发送:
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://127.0.0.1:61616?jms.useAsyncSend=true");
((ActiveMQConnectionFactory) connectionFactory).setUseAsyncSend(true);
((ActiveMQConnection)connection).setUseAsyncSend(true);

消息发送的流程图如下:

activemq发送byte activemq异步发送消息_activemq发送byte

  1. 源码入口ActiveMQMessageProducer#send()
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, AsyncCallback onComplete) throws JMSException {
        checkClosed(); //检查session的状态,如果session关闭则抛异常
        if (destination == null) {
            if (info.getDestination() == null) {
                throw new UnsupportedOperationException("A destination must be specified.");
            }
            throw new InvalidDestinationException("Don't understand null destinations");
        }
        //检查destination的类型,如果符合要求,就转变为ActiveMQDestination
        ActiveMQDestination dest;
        if (destination.equals(info.getDestination())) {
            dest = (ActiveMQDestination)destination;
        } else if (info.getDestination() == null) {
            dest = ActiveMQDestination.transform(destination);
        } else {
            throw new UnsupportedOperationException("This producer can only send messages to: " + this.info.getDestination().getPhysicalName());
        }
        if (dest == null) {
            throw new JMSException("No destination specified");
        }
 
        if (transformer != null) {
            Message transformedMessage = transformer.producerTransform(session, this, message);
            if (transformedMessage != null) {
                message = transformedMessage;
            }
        }
        //如果发送窗口大小不为空,则判断发送窗口的大小决定是否阻塞
        if (producerWindow != null) {
            try {
                producerWindow.waitForSpace();
            } catch (InterruptedException e) {
                throw new JMSException("Send aborted due to thread interrupt.");
            }
        }
        //发送消息到broker的topic
        this.session.send(this, dest, message, deliveryMode, priority, timeToLive, producerWindow, sendTimeout, onComplete);
 
        stats.onMessage();
    }
  1. ActiveMQSession#send()方法去做真正的发送
//。。。。这里省略一部分源码,主要做了对消息的封装,
                msg.setConnection(this.connection);
                msg.onSend();
                msg.setProducerId(msg.getMessageId().getProducerId());
				
				
	//消息异步发送的判断的条件: 回掉onComplete不为空、超时时间、不需要反馈、是否为异步,是否持久化
				 if (onComplete==null && sendTimeout <= 0 && !msg.isResponseRequired() && !connection.isAlwaysSyncSend() (!msg.isPersistent() || connection.isUseAsyncSend() || txid != null)) {
				                this.connection.asyncSendPacket(msg);
	            if (producerWindow != null) {
	                    int size = msg.getSize();//异步发送的情况下,需要设置producerWindow的大小
	                    producerWindow.increaseUsage(size);
	                }
	            } else {
	                if (sendTimeout > 0 && onComplete==null) {
	                    this.connection.syncSendPacket(msg,sendTimeout);
	                }else {
	                    this.connection.syncSendPacket(msg, onComplete);
	                }
	            }
  1. 首先我们看下异步发送
    异步发送的情况下,需要设置producerWindow的大小,producer每发送一个消息,统计一下发送的字节数,当发送的总字节数达到ProducerWindowSize峰值时,需要等待broker的确认。主要用来约束在异步发送时producer端允许积压的(尚未ACK)的消息的大小。每次发送消息之后,都将会导致memoryUsage大小增加(+msg.size),当broker返回producerAck时,memoryUsage尺寸减少(producerAck.size,此size表示先前发送消息的大小)。
    ProducerWindowSize值初始化的方式有2种
    在brokerUrl中设置: “tcp://127.0.0.1:61616?jms.producerWindowSize=10204”,这种设置将会对所有的producer生效。
    在destinationUri中设置: “myQueue?producer.windowSize=10204”,此参数只会对使用此Destination实例的producer生效,将会覆盖上面brokerUrl中的producerWindowSize值
  2. 接下来我们接着看ActiveMQConnection#asyncSendPacket方法

    紧接着我们分析下transport 对象是如何初始化的?通过ActiveMQConnection对象构造器可以看出,transport对象是通过初始化Connection链接的时候创建的。创建ActiveMQConnection对象代码如下:
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.161616");
Connection connection= connectionFactory.createConnection();

在创建connection 方法中我们发现transport创建的代码如下:

activemq发送byte activemq异步发送消息_java_02


createTransport();来创建Transport对象

activemq发送byte activemq异步发送消息_原理分析_03


TransportFactory#connect(java.net.URI)

public static Transport connect(URI location) throws Exception {
        TransportFactory tf = findTransportFactory(location);
        return tf.doConnect(location);
    }

1.创建上一步传入的URL 创建对应的 TransportFactory,调用TransportFactory#findTransportFactory方法创建工厂对象:

activemq发送byte activemq异步发送消息_activemq发送byte_04


其中TRANSPORT_FACTORY_FINDER定义如下

FactoryFinder TRANSPORT_FACTORY_FINDER = new FactoryFinder("META-INF/services/org/apache/activemq/transport/");

这点类似与之前dubbo 源码分析中的SPI思想,将需要加载的类写入固定文件夹下,通过解析加载配置文件来初始化对应的对象。

activemq发送byte activemq异步发送消息_activemq发送byte_05


tcp 文件中的定义如下

activemq发送byte activemq异步发送消息_ActiveMQ_06


因此 findTransportFactory创建的工厂对象为TcpTransportFactory2.调用TransportFactory#doConnect(java.net.URI)创建Transport 对象

activemq发送byte activemq异步发送消息_JMS_07


TransportFactory#createTransport方法,这里的TransportFactory为TcpTransportFactory

protected Transport createTransport(URI location, WireFormat wf) throws UnknownHostException, IOException {
        URI localLocation = null;
        String path = location.getPath();
        if(path != null && path.length() > 0) {
            int localPortIndex = path.indexOf(58);

            try {
                Integer.parseInt(path.substring(localPortIndex + 1, path.length()));
                String localString = location.getScheme() + ":/" + path;
                localLocation = new URI(localString);
            } catch (Exception var7) {
                LOG.warn("path isn't a valid local location for TcpTransport to use", var7.getMessage());
                if(LOG.isDebugEnabled()) {
                    LOG.debug("Failure detail", var7);
                }
            }
        }

        SocketFactory socketFactory = this.createSocketFactory(); //因为目前是Tcp 传输的
        return this.createTcpTransport(wf, socketFactory, location, localLocation);//因此得到的是tcptransport
    }

从代码上看返回的是一个tcptransport 对象再往上看返回的是rc 对象。通过TransportFactory#configure对创建好的tcptransport 进行包装代码如下:

public Transport configure(Transport transport, WireFormat wf, Map options) throws Exception {
//组装一个复合的transport,这里会包装两层,一个是IactivityMonitor.另一个是WireFormatNegotiator
        transport = compositeConfigure(transport, wf, options);
        //再做一层包装,MutexTransport
        transport = new MutexTransport(transport);
        //包装ResponseCorrelator
        transport = new ResponseCorrelator(transport);
        return transport;
    }

到目前为止,tcptransport 是一系列的包装 ResponseCorrelator(MutexTransport(WireFormatNegotiator(IactivityMonitor(TcpTransport()))
ResponseCorrelator 异步请求包装。
MutexTransport 加锁包装。
WireFormatNegotiator发送数据解析相关协议包装。
IactivityMonitor 心跳机制包装。

至此transport 对象就创建完毕。我们回到之前的消息异步发送.syncSendPacket(msg)方法

private void doAsyncSendPacket(Command command) throws JMSException {
        try {
            this.transport.oneway(command);
        } catch (IOException e) {
            throw JMSExceptionSupport.create(e);
        }
    }

这里的transport对象指的是ResponseCorrelator,onway 设置了Command

public void oneway(Object o) throws IOException {
        Command command = (Command)o;
        command.setCommandId(this.sequenceGenerator.getNextSequenceId());
        command.setResponseRequired(false);
        this.next.oneway(command);
    }

next 指的是MutexTransport ,主要是加锁,

public void oneway(Object command) throws IOException {
        this.writeLock.lock();

        try {
            this.next.oneway(command);
        } finally {
            this.writeLock.unlock();
        }

    }

next 指的的WireFormatNegotiator#oneway 解析我们发送的内容

public void oneway(Object command) throws IOException {
        boolean wasInterrupted = Thread.interrupted();

        try {
            if(this.readyCountDownLatch.getCount() > 0L && !this.readyCountDownLatch.await(this.negotiateTimeout, TimeUnit.MILLISECONDS)) {
                throw new IOException("Wire format negotiation timeout: peer did not send his wire format.");
            }
        } catch (InterruptedException var14) {
            InterruptedIOException interruptedIOException = new InterruptedIOException("Interrupted waiting for wire format negotiation");
            interruptedIOException.initCause(var14);

            try {
                this.onException(interruptedIOException);
            } finally {
                Thread.currentThread().interrupt();
                wasInterrupted = false;
            }

            throw interruptedIOException;
        } finally {
            if(wasInterrupted) {
                Thread.currentThread().interrupt();
            }

        }

        super.oneway(command);
    }

这个里面调用了父类的 oneway ,父类是 TransportFilter 类

public void oneway(Object command) throws IOException {
        this.next.oneway(command);
    }

这里的next 是InactivityMonitor,我们发现并没有对此方法进行实现,我们去看他的父类。

public void oneway(Object o) throws IOException {
        this.sendLock.readLock().lock();
        this.inSend.set(true);

        try {
            this.doOnewaySend(o);
        } finally {
            this.commandSent.set(true);
            this.inSend.set(false);
            this.sendLock.readLock().unlock();
        }

    }

doOnewaySend()通过模板模式调用子类的

private void doOnewaySend(Object command) throws IOException {
        if(this.failed.get()) {
            throw new InactivityIOException("Cannot send, channel has already failed: " + this.next.getRemoteAddress());
        } else {
            if(command.getClass() == WireFormatInfo.class) {
                synchronized(this) {
                    this.processOutboundWireFormatInfo((WireFormatInfo)command);
                }
            }

            this.next.oneway(command);
        }
    }

这里的next 其实就是我们最总的transport 对象

public void oneway(Object command) throws IOException {
        this.checkStarted();
        this.wireFormat.marshal(command, this.dataOut);
        this.dataOut.flush();
    }

通过wireFormat对数据进行格式化,然后通过sokect 对数据进行传输。至此异步发送到此结束。

8 . 同步发送过程如下

activemq发送byte activemq异步发送消息_原理分析_08


调用request 方法,这里的transport 对象跟异步的一样。

activemq发送byte activemq异步发送消息_JMS_09


发送的过程还是异步,它与上面的异步方式的区别:

通过阻塞的方式从FutureResponse 异步拿到一个结果。拿到的过程是阻塞的,所以就实现了一个阻塞的同步同步发送。

activemq发送byte activemq异步发送消息_activemq发送byte_10


同步等等过程。

activemq发送byte activemq异步发送消息_java_11


自此同步就发送完了。