ActiveMQ消息发送源码分析
我们可以从消息发送端的producer.send()出发,开始分析源码:
TextMessage message = session.createTextMessage("Hello ActiveMQ:" + i);
producer.send(message);
1.调用ActiveMQMessageProducerSupport类中的send方法
public void send(Message message) throws JMSException {
this.send(this.getDestination(), message, this.defaultDeliveryMode, this.defaultPriority, this.defaultTimeToLive);
}
1.1 调用ActiveMQMessageProducer类中的send方法
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException {
this.send(destination, message, deliveryMode, priority, timeToLive, (AsyncCallback)null);
}
1.2 在调用该类(ActiveMQMessageProducer类)下的具体send实现方法
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, AsyncCallback onComplete) throws JMSException {
//检查当前的session会话状态。如果session已经关闭,则抛出异常
this.checkClosed();
if (destination == null) {
if (this.info.getDestination() == null) {
throw new UnsupportedOperationException("A destination must be specified.");
} else {
throw new InvalidDestinationException("Don't understand null destinations");
}
} else {
ActiveMQDestination dest;
//检查destination的类型,如果符合要求,就转变为ActiveMQDestination
if (destination.equals(this.info.getDestination())) {
dest = (ActiveMQDestination)destination;
} else {
if (this.info.getDestination() != null) {
throw new UnsupportedOperationException("This producer can only send messages to: " + this.info.getDestination().getPhysicalName());
}
dest = ActiveMQDestination.transform(destination);
}
if (dest == null) {
throw new JMSException("No destination specified");
} else {
if (this.transformer != null) {
Message transformedMessage = this.transformer.producerTransform(this.session, this, message);
if (transformedMessage != null) {
message = transformedMessage;
}
}
//如果发送窗口大小不为空,则判断发送窗口的大小决定是否阻塞
if (this.producerWindow != null) {
try {
//根据当前窗口大小,来决定是否去阻塞
this.producerWindow.waitForSpace();
} catch (InterruptedException var10) {
throw new JMSException("Send aborted due to thread interrupt.");
}
}
//【跳转至步骤2】通过session.send(),发送消息到broker的topic
this.session.send(this, dest, message, deliveryMode, priority, timeToLive, this.producerWindow, this.sendTimeout, onComplete);
this.stats.onMessage();
}
}
}
2.调用ActiveMQSession类中的send方法,发送消息至Broker
protected void send(ActiveMQMessageProducer producer, ActiveMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive, MemoryUsage producerWindow, int sendTimeout, AsyncCallback onComplete) throws JMSException {
//检查当前的session会话状态
this.checkClosed();
if (destination.isTemporary() && this.connection.isDeleted(destination)) {
throw new InvalidDestinationException("Cannot publish to a deleted Destination: " + destination);
} else {
//互斥锁,如果一个session的多个producer发送消息到这里,会保证消息发送的有序性
synchronized(this.sendMutex) {
this.doStartTransaction();//告诉broker,开始一个新事务,只有事务性会话中才会开启
TransactionId txid = this.transactionContext.getTransactionId();
long sequenceNumber = producer.getMessageSequence();
message.setJMSDeliveryMode(deliveryMode);//在JMS协议头中,设置是否持久化标识
long expiration = 0L;//计算消息过期时间
if (!producer.getDisableMessageTimestamp()) {
long timeStamp = System.currentTimeMillis();
message.setJMSTimestamp(timeStamp);
if (timeToLive > 0L) {
expiration = timeToLive + timeStamp;
}
}
message.setJMSExpiration(expiration);//设置消息过期时间
message.setJMSPriority(priority);//设置消息的优先级
message.setJMSRedelivered(false);//设置消息为非重发
//将不同的消息格式,统一转化为ActiveMQMessage
ActiveMQMessage msg = ActiveMQMessageTransformation.transformMessage(message, this.connection);
msg.setDestination(destination);//设置消息的目的地
//生成并设置消息id
msg.setMessageId(new MessageId(producer.getProducerInfo().getProducerId(), sequenceNumber));
if (msg != message) {//如果消息式经过转化的,则更新原来的消息id和目的地
message.setJMSMessageID(msg.getMessageId().toString());
message.setJMSDestination(destination);
}
msg.setBrokerPath((BrokerId[])null);
msg.setTransactionId(txid);
if (this.connection.isCopyMessageOnSend()) {
msg = (ActiveMQMessage)msg.copy();
}
msg.setConnection(this.connection);
msg.onSend();//把消息属性和消息体都设置为只读,防止被修改
msg.setProducerId(msg.getMessageId().getProducerId());
if (LOG.isTraceEnabled()) {
LOG.trace(this.getSessionId() + " sending message: " + msg);
}
//如果onComplete有设置,或者发送超时时间小于0,或者消息需要反馈,或者连接器是同步发送模式,或者消息是持久化且连接器是异步发送模式
//或者不存在事务id的情况下,走异步发送,否则走同步发送
if (onComplete != null || sendTimeout > 0 || msg.isResponseRequired() || this.connection.isAlwaysSyncSend() || msg.isPersistent() && !this.connection.isUseAsyncSend() && txid == null) {
//走同步发送
if (sendTimeout > 0 && onComplete == null) {
this.connection.syncSendPacket(msg, sendTimeout);
} else {
this.connection.syncSendPacket(msg, onComplete);
}
} else {
//【跳转至步骤3】走异步发送
this.connection.asyncSendPacket(msg);
//判断producerWindow窗口是否为空
if (producerWindow != null) {
//获取producer发送消息的大小
int size = msg.getSize();
//增加producerWindow大小
producerWindow.increaseUsage((long)size);
}
}
}
}
}
3.分析异步发送过程(调用ActiveMQConnection类下的asyncSendPacket方法)
public void asyncSendPacket(Command command) throws JMSException {
if (this.isClosed()) {
throw new ConnectionClosedException();
} else {
//【跳转至步骤3.1】如果连接没有关闭,执行异步发送过程(command为当前的消息,经过组装后的结果)
this.doAsyncSendPacket(command);
}
}
3.1 执行同类(ActiveMQConnection类)下的doAsyncSendPacket方法
private void doAsyncSendPacket(Command command) throws JMSException {
try {
//【跳转至步骤3.2】此处即为传输层transport
this.transport.oneway(command);
//此处this.transport,在很多的中间件中都会看到,它并不是一个单纯的transport。我们在brokerURL中可以配置,我们
//如果配置"tcp://192.168.204.201:61616",此处传递一个tcp的话,意味着我们是使用TcpTransport.所以此处
//transport一定是一个多态实现的方式。因为transport是一个接口,所以我们在oneway中能够看到针对transport的很多
//实现这个时候它会调用哪个实现呢?我们发现在此处走不下去之后,我们可以去返回看一下transport是如何实例化的
} catch (IOException var3) {
throw JMSExceptionSupport.create(var3);
}
}
3.2 transport是如何实例化的
transport在创建connection的时候会去初始化 。因为当前transport是绑定在当前的activeMQConnection,按我们的猜想,这个transport一定是在构建这个连接的时候去传递的。
3.2.1 创建Connection连接
Connection connection = connectionFactory.createConnection();
3.2.2 进入ActiveMQConnectionFactory类下的createConnection方法
public Connection createConnection() throws JMSException {
return this.createActiveMQConnection();
}
//执行该类下的createActiveMQConnection()方法
protected ActiveMQConnection createActiveMQConnection() throws JMSException {
return this.createActiveMQConnection(this.userName, this.password);
}
3.2.3 进入ActiveMQConnectionFactory类下的createActiveMQConnection()方法的构造方法,开始创建连接
protected ActiveMQConnection createActiveMQConnection(String userName, String password) throws JMSException {
if (this.brokerURL == null) {
throw new ConfigurationException("brokerURL not set.");
} else {
ActiveMQConnection connection = null;
try {
//【跳转至步骤3.2.4】创建一个transport传输协议
Transport transport = this.createTransport();
connection = this.createActiveMQConnection(transport, this.factoryStats);
connection.setUserName(userName);
connection.setPassword(password);
this.configureConnection(connection);
//启动
transport.start();
if (this.clientID != null) {
connection.setDefaultClientID(this.clientID);
}
return connection;
} catch (JMSException var8) {
try {
connection.close();
} catch (Throwable var7) {
}
throw var8;
} catch (Exception var9) {
try {
connection.close();
} catch (Throwable var6) {
}
throw JMSExceptionSupport.create("Could not connect to broker URL: " + this.brokerURL + ". Reason: " + var9, var9);
}
}
}
3.2.4 如何构建transport(进入本类(ActiveMQConnectionFactory)下的createTransport()方法)
调用ActiveMQConnectionFactory.createTransport方法,去创建一个transport对象。
1.构建一个URI
2.根据URL去创建一个连接TransportFactory.connect
Ø 默认使用的是tcp的协议
protected Transport createTransport() throws JMSException {
try {
URI connectBrokerUL = this.brokerURL;
//获取brokerUrl的scheme
String scheme = this.brokerURL.getScheme();
//根据Scheme来判断
if (scheme == null) {
throw new IOException("Transport not scheme specified: [" + this.brokerURL + "]");
} else {
if (scheme.equals("auto")) {//Scheme默认为auto
connectBrokerUL = new URI(this.brokerURL.toString().replace("auto", "tcp"));//将auto替换为tcp
} else if (scheme.equals("auto+ssl")) {
connectBrokerUL = new URI(this.brokerURL.toString().replace("auto+ssl", "ssl"));
} else if (scheme.equals("auto+nio")) {
connectBrokerUL = new URI(this.brokerURL.toString().replace("auto+nio", "nio"));
} else if (scheme.equals("auto+nio+ssl")) {
connectBrokerUL = new URI(this.brokerURL.toString().replace("auto+nio+ssl", "nio+ssl"));
}
//【跳转至步骤3.2.5】将brokerUrl设置到TransportFactory中(这里是一个工厂设计模式)
return TransportFactory.connect(connectBrokerUL);
}
} catch (Exception var3) {
throw JMSExceptionSupport.create("Could not create Transport. Reason: " + var3, var3);
}
}
3.2.5 使用工厂模式来创建连接(调用TransportFactory类中的connect方法)
public static Transport connect(URI location) throws Exception {
//【跳转至步骤3.2.6】通过URI,来获取具体使用的Transport实例(通过步骤3.2.8可以知道该方法默认返回TcpTransportFactory类)
TransportFactory tf = findTransportFactory(location);
//【跳转至步骤3.2.9】
return tf.doConnect(location);
}
3.2.6 调用TransportFactory类中的findTransportFactory方法
具体步骤:
1.从TRANSPORT_FACTORYS这个Map集合中,根据Scheme去获得一个TransportFactory指定的实例对象;
2.如果Map集合中不存在,则通过TRANSPORT_FACTORY_FINDER去找一个并且构建实例
Ø 这个地方又有点类似于Java的SPI的思想。它会从META-INF/services/org/apache/activemq/transport/ 这个路径下,根据URI组装的scheme去找到匹配的class对象并且实例化,所以根据tcp为key去对应的路径下可以找到TcpTransportFactory
public static TransportFactory findTransportFactory(URI location) throws IOException {
//获取Scheme
String scheme = location.getScheme();
if (scheme == null) {
throw new IOException("Transport not scheme specified: [" + location + "]");
} else {
//此处类似于SPI思想
//通过Scheme,从ConcurrentHashMap中拿取一个TransportFactory实例
TransportFactory tf = (TransportFactory)TRANSPORT_FACTORYS.get(scheme);
//如果实例为空
if (tf == null) {
try {
//private static final FactoryFinder TRANSPORT_FACTORY_FINDER = new FactoryFinder("META-INF/services/org/apache/activemq/transport/");
//从TRANSPORT_FACTORY_FINDER去拿(此处又类似于SPI机制,但是和Java SPI机制又有点区别)
//【跳转至步骤3.2.7】
tf = (TransportFactory)TRANSPORT_FACTORY_FINDER.newInstance(scheme);
TRANSPORT_FACTORYS.put(scheme, tf);
} catch (Throwable var4) {
throw IOExceptionSupport.create("Transport scheme NOT recognized: [" + scheme + "]", var4);
}
}
return tf;
}
}
3.2.7 使用类似于Java SPI机制来newInstance新建实例
我们进入META-INF/services/org/apache/activemq/transport/目录下,会看到该目录下的文件,是以文件的名字作为key。默认的话,会读取到名字为tcp的文件,然后拿到文件定义的class=org.apache.activemq.transport.tcp.TcpTransportFactory的Factroy,然后再去newInstance来创建一个实例(相当于改造版的Java SPI机制)
3.2.8 步骤3.2.6中findTransportFactory方法返回的是个什么对象
在步骤3.2.6中,经过分析,我们知道该方法返回的是一个TransportFactory,默认的话返回的是一个TcpTransportFactory。
3.2.9 tf.doConnect(location);根据findTransportFactory返回的类,创建Transport的连接
在步骤3.2.5中,我们知道tf默认返回的是一个TcpTransportFactory。但是我们在doConnect()方法实现中,并没有找到TcpTransportFactory的实现类。我们通过搜索TcpTransportFactory类,会发现 public class TcpTransportFactory extends TransportFactory,该类继承了TransportFactory父类tory类,所以tf.doConnect(location)方法,实际执行的是父类TransportFactory类中的doConnect()方法,代码如下:
public Transport doConnect(URI location) throws Exception {
try {
Map<String, String> options = new HashMap(URISupport.parseParameters(location));
if (!options.containsKey("wireFormat.host")) {
options.put("wireFormat.host", location.getHost());
}
//此处为具体的TCP协议层实现 start
WireFormat wf = this.createWireFormat(options);
//此处为创建一个Transport(重要),此处是一个模版方法.默认情况下会进入TcpTransportFactory类下的createTransport()方法
//创建一个Transport,创建一个socket连接---->终于找到真相了
//【跳转至步骤3.2.10】
Transport transport = this.createTransport(location, wf);
//configure 【跳转至步骤3.2.12】 配置configure,这个里面是对Transport做的链路包装
Transport rc = this.configure(transport, wf, options);
//end
IntrospectionSupport.extractProperties(options, "auto.");
if (!options.isEmpty()) {
throw new IllegalArgumentException("Invalid connect parameters: " + options);
} else {
return rc;
}
} catch (URISyntaxException var6) {
throw IOExceptionSupport.create(var6);
}
}
3.2.10 开始TcpTransport创建过程
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);
}
}
}
//Tcp通信是基于Socket通信(NIO通信是基于原生NIO支持或者基于Netty的支持)(此处是重点)
SocketFactory socketFactory = this.createSocketFactory();
//【跳转至步骤3.2.11】创建TcpTransport
return this.createTcpTransport(wf, socketFactory, location, localLocation);
}
3.2.11 创建TcpTransport
public TcpTransport(WireFormat wireFormat, SocketFactory socketFactory, URI remoteLocation, URI localLocation) throws UnknownHostException, IOException {
this.connectionTimeout = 30000;
this.socketBufferSize = 65536;
this.ioBufferSize = 8192;
this.closeAsync = true;
this.buffOut = null;
this.trafficClass = 0;
this.trafficClassSet = false;
this.diffServChosen = false;
this.typeOfServiceChosen = false;
this.trace = false;
this.logWriterName = TransportLoggerSupport.defaultLogWriterName;
this.dynamicManagement = false;
this.startLogging = true;
this.jmxPort = 1099;
this.useLocalHost = false;
this.stoppedLatch = new AtomicReference();
this.soLinger = -2147483648;
this.wireFormat = wireFormat;
this.socketFactory = socketFactory;
try {
//这个地方,创建一个Socket(TcpTransport其实就是创建了一个连接)
//【跳转步骤3.2.9】,你就知道了Transport transport = this.createTransport(location, wf);返回的其实是一个连接
//【步骤3.2.9】中return 的是一个rc,并不是此处的transport,所以我们回到【跳转回步骤3.2.9】分析生成rc的configure方法
this.socket = socketFactory.createSocket();
} catch (SocketException var6) {
this.socket = null;
}
this.remoteLocation = remoteLocation;
this.localLocation = localLocation;
this.initBuffer = null;
this.setDaemon(false);
}
3.2.12 对createTransport()方法返回的连接进行链式包装(共做了3层包装)
public Transport configure(Transport transport, WireFormat wf, Map options) throws Exception {
//组装一个符合的transport,这里会包装两层,一个是InactivityMonitor,另一个是WireFormatNegotiator
transport = this.compositeConfigure(transport, wf, options);
//再做一层包装MutexTransport
Transport transport = new MutexTransport(transport);
//包装ResponseCorrelator
Transport transport = new ResponseCorrelator(transport);
return transport;
}
最终包装完毕后,这个transport实际上就是一个调用链了,它的链式结构如下:
ResponseCorrelator(MutexTransport(WireFormatNegotiator(InactivityMonitor(TcpTransport()))))
每一层包装表示的意思:
ResponseCorrelator:用于实现异步请求
MutexTransport:实现写锁,表示同一时间只允许发送一个请求
WireFormatNegotiator:实现客户端连接broker的时候先发送数据解析相关的协议信息,比如解析版本号,是否使用缓存等
InactivityMonitor:用于实现连接成功后的心跳检查机制,客户端每10s发送一次心跳信息。服务端每30s读取一次心跳信息。(心跳机制,用于维持当前连接的一个方式)
接下分析同步/异步发送具体操作
4. 异步发送分析
此时便可以【退回至步骤2】中,执行具体的同步/异步操作
4.1 异步发送源码分析
private void doAsyncSendPacket(Command command) throws JMSException {
try {
//此处即为传输层transport
this.transport.oneway(command);
//此处transport首先会去调用链路包装最外层的 【步骤4.2】中的 ResponseCorrelator类中的oneway方法
} catch (IOException var3) {
throw JMSExceptionSupport.create(var3);
}
}
4.2 组装ResponseCorrelator类(实际为组装Command信息,设置commondId 和 responseRequired)
public void oneway(Object o) throws IOException {
//强转
Command command = (Command)o;
//设置commondId
command.setCommandId(this.sequenceGenerator.getNextSequenceId());
//设置responseRequired
command.setResponseRequired(false);
//5.3 此处进行链路组装下一层(即:MutexTransport类)
this.next.oneway(command);
}
4.3 组装MutexTransport类,来完成加锁操作
public void oneway(Object command) throws IOException {
this.writeLock.lock();
try {
//【跳转至步骤4.4】此处进行链路组装下一层(即:WireFormatNegotiator类)
this.next.oneway(command);
} finally {
this.writeLock.unlock();
}
}
4.4 通过WireFormatNegotiator类,对内容进行解析(该类继承自TransportFilter父类)
public void oneway(Object command) throws IOException {
......//省略部分代码
//【跳转至步骤4.5】调用super.oneway,即 TransportFilter类中的oneway方法
super.oneway(command);
}
4.5 调用父类的oneway,发现父类中没做任何操作
public void oneway(Object command) throws IOException {
//【跳转至步骤4.6】此处进行链路组装下一层(即:InactivityMonitor类)
this.next.oneway(command);
}
4.6 发现next.oneway()的实现类没有InactivityMonitor类。
我们查找InactivityMonitor类可以发现他继承自AbstractInactivityMonitor类,所以【步骤4.5】中进入的链路组装下一层是AbstractInactivityMonitor父类
4.6.1 AbstractInactivityMonitor父类中的oneway方法
public void oneway(Object o) throws IOException {
this.sendLock.readLock().lock();
this.inSend.set(true);
try {
//【跳转至步骤4.6.2】调用doOnewaySend方法
this.doOnewaySend(o);
} finally {
this.commandSent.set(true);
this.inSend.set(false);
this.sendLock.readLock().unlock();
}
}
4.6.2 AbstractInactivityMonitor父类中的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);
}
}
//【跳转至步骤4.6.3】再次执行oneway()方法,最终调用至TcpTransport
this.next.oneway(command);
}
}
4.6.3 进入包装最内层---TcpTransport类
public void oneway(Object command) throws IOException {
this.checkStarted();
//Tcp,通过格式化等一系列操作,然后将数据发送至Broker
this.wireFormat.marshal(command, this.dataOut);
this.dataOut.flush();
}
5. 同步发送分析
【回退至步骤2】
5.1 调用步骤2中的 ActiveMQConnection类下的syncSendPacket方法 执行同步发送操作
public Response syncSendPacket(Command command, int timeout) throws JMSException {
if (this.isClosed()) {
throw new ConnectionClosedException();
} else {
try {
//【跳转至步骤5.2】调用this.transport.request()来获取返回response
Response response = (Response)((Response)(timeout > 0 ? this.transport.request(command, timeout) : this.transport.request(command)));
......省略部分代码
} catch (IOException var9) {
throw JMSExceptionSupport.create(var9);
}
}
}
5.2 调用包装类 ResponseCorrelator类中的request方法
public Object request(Object command, int timeout) throws IOException {
//你在这里会发现,可能还是一个异步操作,为什么呢?
//(会在response.getResult()方法中拿到异步请求的一个结果,只是这个阶段是阻塞的)
FutureResponse response = this.asyncRequest(command, (ResponseCallback)null);
//【跳转至步骤5.3】此处有response.getResult
return response.getResult(timeout);
}
5.3 调用response.getResult()方法
public Response getResult(int timeout) throws IOException {
boolean wasInterrupted = Thread.interrupted();
Response var4;
try {
//有一个responseSlot.poll()操作
Response result = (Response)this.responseSlot.poll((long)timeout, TimeUnit.MILLISECONDS);
......省略部分代码
}
5.4 调用responseSlot.poll()操作
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0)
return null;
//此处有一个等待操作()
nanos = notEmpty.awaitNanos(nanos);
}
return dequeue();
} finally {
lock.unlock();
}
}
至此,你应该知道了同步和异步的差别在哪里了吧?
一句话总结:同步就是不阻塞发送,阻塞获取结果。
同步的发送,说到底还是异步发送,只是说从异步请求中拿到一个返回的结果,但是这个过程是阻塞的而已.所以它就实现了一个阻塞的同步发送。