1. 环境搭建
-
代码已经上传至 https://github.com/masteryourself/activemq.git ,分支名称是
masteryourself-activemq-5.15.9
-
producer 是
activemq-example
工程,启动类是QueueProducer
2. 源码解析
2.1 流程预览
// 1. 创建 ActiveMQConnection
org.apache.activemq.ActiveMQConnectionFactory#createConnection ->
org.apache.activemq.ActiveMQConnectionFactory#createActiveMQConnection
// 1.1(*) 创建 transport,用于与 server 端进行交互
org.apache.activemq.ActiveMQConnectionFactory#createTransport ->
org.apache.activemq.transport.TransportFactory#connect ->
// 1.1.1(*) 这里是 spi 扩展,和 dubbo 类似
// 从 META-INF/services/org/apache/activemq/transport/tcp 文件中找到实现类【TcpTransportFactory】
org.apache.activemq.transport.TransportFactory#findTransportFactory
// 1.1.2(*) 调用 doConnect 方法,返回 Transport 对象
org.apache.activemq.transport.TransportFactory#doConnect
// 1.1.2.1 创建 Transport
org.apache.activemq.transport.tcp.TcpTransportFactory#createTransport->
// tcpTransport 里持有 socketFactory 对象,socketFactory 会创建一个 socket,所以 tcpTransport 就是操作 socket
org.apache.activemq.transport.tcp.TcpTransportFactory#createTcpTransport
// 1.1.2.2(*) 配置 configure,这个里面是对 Transport 做链路包装
org.apache.activemq.transport.TransportFactory#configure
// 1.1.2.2.1 组装一个复合的transport,这里会包装两层,一个是 IactivityMonitor.另一个是 WireFormatNegotiator
// WireFormatNegotiator 实现了客户端连接 broker 的时候先发送数据解析相关的协议信息,比如解析版本号,是否使用缓存等
// InactivityMonitor 用于实现连接成功成功后的心跳检查机制,客户端每 10s 发送一次心跳信息。服务端每 30s 读取一次心跳信息
org.apache.activemq.transport.tcp.TcpTransportFactory#compositeConfigure
// 1.1.2.2.2 用 MutexTransport 包装,实现写锁,表示同一时间只允许发送一个请求
org.apache.activemq.transport.MutexTransport#<init>
// 1.1.2.2.3 用 ResponseCorrelator 包装,用于实现异步请求
org.apache.activemq.transport.ResponseCorrelator#<init>
// 1.2(*) 设置 ActiveMQConnection 的属性,如 prefetchPolicy、optimizedMessageDispatch 等属性
org.apache.activemq.ActiveMQConnectionFactory#configureConnection
// 1.3(*) 最终会启动 TransportThreadSupport 的
org.apache.activemq.transport.TransportFilter#start ->
// 这里省略了一些一些包装类,最终会调用 ServiceSupport 中的 start 方法
org.apache.activemq.util.ServiceSupport#start ->
// 1.3.1 利用 socketFactory 创建 socket 对象(连接 activeMQ server 端)
org.apache.activemq.transport.tcp.TcpTransport#connect
// 1.3.2 调用 TcpTransport 中的 doStart 方法
org.apache.activemq.transport.tcp.TcpTransport#doStart ->
// 启动线程,runnable 对象是 【TcpTransport】,所以在 【TcpTransport】 的 run 方法中等待结果
org.apache.activemq.transport.TransportThreadSupport#doStart
// 2. 【TcpTransport】 中的 run 方法,空壳方法,调用 doRun
org.apache.activemq.transport.tcp.TcpTransport#run ->
// 调用 doConsume 处理
org.apache.activemq.transport.tcp.TcpTransport#doRun
// 2.1 从 socket 读数据,封装成 command
org.apache.activemq.transport.tcp.TcpTransport#readCommand
// 2.2 Transport 的包装类处理逻辑
org.apache.activemq.transport.TransportSupport#doConsume ->
// Transport 的包装类处理逻辑
org.apache.activemq.transport.AbstractInactivityMonitor#onCommand ->
// Transport 的包装类处理逻辑
org.apache.activemq.transport.WireFormatNegotiator#onCommand ->
// Transport 的包装类处理逻辑
org.apache.activemq.transport.MutexTransport#onCommand ->
// Transport 的包装类处理逻辑
org.apache.activemq.transport.ResponseCorrelator#onCommand ->
// ActiveMQ 在这里处理来自于 server 端的 command 命令
org.apache.activemq.ActiveMQConnection#onCommand
2.2 流程详解
2.2.1 ActiveMQConnectionFactory#createTransport(1.1)
org.apache.activemq.ActiveMQConnectionFactory
protected Transport createTransport() throws JMSException {
try {
// 即 tcp://192.168.89.210:61616
URI connectBrokerUL = brokerURL;
String scheme = brokerURL.getScheme();
if (scheme == null) {
throw new IOException("Transport not scheme specified: [" + brokerURL + "]");
}
if (scheme.equals("auto")) {
connectBrokerUL = new URI(brokerURL.toString().replace("auto", "tcp"));
} else if (scheme.equals("auto+ssl")) {
connectBrokerUL = new URI(brokerURL.toString().replace("auto+ssl", "ssl"));
} else if (scheme.equals("auto+nio")) {
connectBrokerUL = new URI(brokerURL.toString().replace("auto+nio", "nio"));
} else if (scheme.equals("auto+nio+ssl")) {
connectBrokerUL = new URI(brokerURL.toString().replace("auto+nio+ssl", "nio+ssl"));
}
// 根据 scheme 动态构建一个 Transport
return TransportFactory.connect(connectBrokerUL);
} catch (Exception e) {
throw JMSExceptionSupport.create("Could not create Transport. Reason: " + e, e);
}
}
2.2.2 TransportFactory#findTransportFactory(1.1.1)
org.apache.activemq.transport.TransportFactory
public static TransportFactory findTransportFactory(URI location) throws IOException {
String scheme = location.getScheme();
if (scheme == null) {
throw new IOException("Transport not scheme specified: [" + location + "]");
}
TransportFactory tf = TRANSPORT_FACTORYS.get(scheme);
if (tf == null) {
// Try to load if from a META-INF property.
try {
// 这里是 spi 扩展,和 dubbo 类似
// 从 META-INF/services/org/apache/activemq/transport/tcp 文件中找到实现类【TcpTransportFactory】
tf = (TransportFactory)TRANSPORT_FACTORY_FINDER.newInstance(scheme);
// 放置到缓存中
TRANSPORT_FACTORYS.put(scheme, tf);
} catch (Throwable e) {
throw IOExceptionSupport.create("Transport scheme NOT recognized: [" + scheme + "]", e);
}
}
return tf;
}
2.2.3 TransportFactory#doConnect(1.1.2)
org.apache.activemq.transport.TransportFactory
public Transport doConnect(URI location) throws Exception {
try {
Map<String, String> options = new HashMap<String, String>(URISupport.parseParameters(location));
if( !options.containsKey("wireFormat.host") ) {
options.put("wireFormat.host", location.getHost());
}
WireFormat wf = createWireFormat(options);
// 创建一个Transport,就是创建一个 socket连接
Transport transport = createTransport(location, wf);
// 配置 configure,这个里面是对 Transport 做链路包装
Transport rc = configure(transport, wf, options);
//remove auto
IntrospectionSupport.extractProperties(options, "auto.");
if (!options.isEmpty()) {
throw new IllegalArgumentException("Invalid connect parameters: " + options);
}
return rc;
} catch (URISyntaxException e) {
throw IOExceptionSupport.create(e);
}
}
2.2.4 TransportFactory#configure(1.1.2.2)
org.apache.activemq.transport.TransportFactory
public Transport configure(Transport transport, WireFormat wf, Map options) throws Exception {
// 组装一个复合的transport,这里会包装两层,一个是 IactivityMonitor.另一个是 WireFormatNegotiator
// WireFormatNegotiator 实现了客户端连接 broker 的时候先发送数据解析相关的协议信息,比如解析版本号,是否使用缓存等
// InactivityMonitor 用于实现连接成功成功后的心跳检查机制,客户端每 10s 发送一次心跳信息。服务端每 30s 读取一次心跳信息
transport = compositeConfigure(transport, wf, options);
// 再做一层包装 MutexTransport,实现写锁,表示同一时间只允许发送一个请求
transport = new MutexTransport(transport);
// 包装 ResponseCorrelator,用于实现异步请求
transport = new ResponseCorrelator(transport);
return transport;
}