ActiveMQ详解一——连接、会话和消费者

本文深入解析ActiveMQ客户端的连接过程,包括连接配置、创建及启动,涉及TCPTransportFactory、WireFormatNegotiator等关键组件。同时,文章探讨了为何连接需调用start()方法才能接收消息,以及会话(Session)的创建和其内部机制,如TaskRunner的作用。
摘要由CSDN通过智能技术生成

本文摘自https://www.jianshu.com/p/d41f32ca22a5 如有问题,请在文章评论处留言

背景

ActiveMQ是非常受欢迎且功能强大的开源消息中间件。它具有速度快、支持多种语言和协议、附带易于使用的企业集成模式和诸多的高级功能,同时完全支持JMS1.1和J2EE1.4规范。在诸多系统系统中都有广泛的应用。并且社区活跃、文档丰富,是系统异步交互的不二选择。

作为一个消息中间件,有客户端和服务端两部分代码,本次主要针对客户端进行源码分析,分为建立连接、消息发送、消息消费三个部分。

文章分析的源码版本基于Activemq-5.15.4版本。历史版本大家可以去github上查看。

建立连接

通常来说,客户端使用MQ的API建立连接时,可以分为两个步骤:

  1. 连接的配置,比如服务器IP地址、用户名和密码等
  2. 建立连接并启动

客户端示例代码:

ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(username,password,url);  
ActiveMQConnection connection = connectionFactory.createConnection();  
connection.start();

可以看到主要的方法是ActiveMQConnectionFactory构造函数、createConnection()函数以及connection的start()函数。

配置

ActiveMQConnectionFactory中的createConnection

首先我们来看一下构造连接工厂都做了什么。

    public ActiveMQConnectionFactory() {
        this(DEFAULT_BROKER_URL);
    }

    public ActiveMQConnectionFactory(String brokerURL) {
        this(createURI(brokerURL));
    }

    public ActiveMQConnectionFactory(URI brokerURL) {
        setBrokerURL(brokerURL.toString());
    }

    public ActiveMQConnectionFactory(String userName, String password, URI brokerURL) {
        setUserName(userName);
        setPassword(password);
        setBrokerURL(brokerURL.toString());
    }

主要的构造器有四个,基本上就是设置用户名密码(非必须)和URI对象。

创建连接

它的createConnection方法指向了createActiveMQConnection方法,主要做了三件事情:

  1. 建立Transport并通过其建立Connection
  2. 配置Connection,建立好的Transport对象被放入其中
  3. 启动Transport
    /**
     * @return Returns the Connection.
     */
    protected ActiveMQConnection createActiveMQConnection(String userName, String password) throws JMSException {
        if (brokerURL == null) {
            throw new ConfigurationException("brokerURL not set.");
        }
        ActiveMQConnection connection = null;
        try {
            Transport transport = createTransport();
            connection = createActiveMQConnection(transport, factoryStats);

            connection.setUserName(userName);
            connection.setPassword(password);

            configureConnection(connection);

            transport.start();

            if (clientID != null) {
                connection.setDefaultClientID(clientID);
            }

            return connection;
        } catch (JMSException e) {
            // Clean up!
            try {
                connection.close();
            } catch (Throwable ignore) {
            }
            throw e;
        } catch (Exception e) {
            // Clean up!
            try {
                connection.close();
            } catch (Throwable ignore) {
            }
            throw JMSExceptionSupport.create("Could not connect to broker URL: " + brokerURL + ". Reason: " + e, e);
        }
    }

我们来详细看一下连接是如何创立的。 createTransport方法中包含了对客户端传入的url的初步校验,主要是验证URL的合法性,而后调用工厂类TransportFactory.connection(url)来进行连接的建立。

    /**
     * Creates a Transport based on this object's connection settings. Separated
     * from createActiveMQConnection to allow for subclasses to override.
     *
     * @return The newly created Transport.
     * @throws JMSException If unable to create trasnport.
     */
    protected Transport createTransport() throws JMSException {
        try {
            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"));
            }

            return TransportFactory.connect(connectBrokerUL);
        } catch (Exception e) {
            throw JMSExceptionSupport.create("Could not create Transport. Reason: " + e, e);
        }
    }

我们客户端在建立连接的时候,有可能有TCP、UDP等等协议,AMQ实现了简单工厂类FactoryFinder,在TransportFactory.connect(connectBrokerUL)方法中,先是通过FactoryFinder根据用户输入的url(比如tcp://192.168.0.1)来找到使用的协议工厂TcpTransportFactory,然后使用TcpTransportFactory中的类来进行连接的建立。这个过程从代码上来看有点曲折:

  1. TransportFactory的connect()调用findTransportFactory方法
  2. findTransportFactory调用FactoryFinder类的newInstance方法
  3. newInstance调用ObjectFactory类的create方法
  4. ObejctFactory是一个接口类,实现类是StandaloneObjectFactory,其中的create方法调用自身的loadClass方法
  5. loadClass方法中最终找到正确的类,返回至TransportFactory中
  6. 如果是tcp连接,最终得到的就是一个实例化的TcpTransportFactory类
public abstact class TransportFactory {
……
    private static final FactoryFinder TRANSPORT_FACTORY_FINDER = new FactoryFinder("META-INF/services/org/apache/activemq/transport/");

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

    public static TransportFactory findTransportFactory(URI location) throws IOException {
        String scheme = location.getScheme();
        if (scheme == null) {
            throw new IOException("Trans
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值