01.ActiveMQ 源码解析之创建连接

1. 环境搭建

  • 代码已经上传至 https://github.com/masteryourself/activemq.git ,分支名称是 masteryourself-activemq-5.15.9

  • producer 是 activemq-example 工程,启动类是 QueueProducer

2. 源码解析

2.1 流程预览

image

// 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章