消息中间件之ActiveMQ — 05

高级使用

queue browser

可以查看队列中的消息而不消费,没有订阅的功能

package com.zjw.activemq.browser;

import java.util.Enumeration;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.QueueBrowser;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;

/**
 * @author zjwblog <co.zjwblog@gmail.com>
 * @version 1.0
 * @date 2021/12/16 12:04 上午
 */
public class BrowserQueue {
  private static final String ACTIVEMQ_HOST = "failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?Randomize=false";
  private static final String USERNAME = null;
  private static final String PASSWORD = null;

  public static void main(String[] args) throws Exception {
    ConnectionFactory factory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, ACTIVEMQ_HOST);
    Connection conn = factory.createConnection();
    conn.start();
    Session session = conn.createSession(true, Session.CLIENT_ACKNOWLEDGE);

    QueueBrowser browser = session.createBrowser(new ActiveMQQueue("test"));

    Enumeration enumeration = browser.getEnumeration();

    while (enumeration.hasMoreElements()) {
      TextMessage textMessage = (TextMessage) enumeration.nextElement();
      System.out.println("textMessage:" + textMessage.getText());
    }
    
    conn.close();
  }
}

JMSCorrelationID

用于消息之间的关联,给人一种会话的感觉

http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html

示例

package com.zjw.activemq.correlation;

import com.zjw.activemq.delay.cron.Test;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * @author zjwblog <co.zjwblog@gmail.com>
 * @version 1.0
 * @date 2021/12/16 12:20 上午
 */
public class CorrelationIDQueueReceiver {

  public static void main(String[] args) throws Exception {
    ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(
        ActiveMQConnectionFactory.DEFAULT_USER,
        ActiveMQConnectionFactory.DEFAULT_PASSWORD,
        "failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?Randomize=false"
    );
    Connection conn = factory.createConnection();
    conn.start();
    Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
    Queue queue = session.createQueue("test");

    MessageConsumer consumer = session.createConsumer(queue);

    consumer.setMessageListener(message -> {
      if(message instanceof TextMessage) {
        TextMessage msg = (TextMessage) message;
        try {
          System.out.println(msg.getJMSCorrelationID());
        } catch (JMSException e) {
          e.printStackTrace();
        }
      }
    });
  }


}


package com.zjw.activemq.correlation;

import javax.jms.Connection;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * @author zjwblog <co.zjwblog@gmail.com>
 * @version 1.0
 * @date 2021/12/16 12:13 上午
 */
public class CorrelationIDQueueSender {

  public static void main(String[] args) throws Exception {
    ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(
        ActiveMQConnectionFactory.DEFAULT_USER,
        ActiveMQConnectionFactory.DEFAULT_PASSWORD,
        "failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?Randomize=false"
    );
    Connection conn = factory.createConnection();
    conn.start();
    Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);

    Queue queue = session.createQueue("test");

    MessageProducer producer = session.createProducer(queue);

    Message message = session.createTextMessage("Message from ServerA" );
    message.setJMSCorrelationID("movie");
    producer.send(message);

    // conn.close();
  }
}

JMSReplyTo

发送方可以接受到消息消费确认的地址,达到类似于同步的效果

发送消息中设置replyTo

message.setJMSReplyTo(queue);

在接收端可以获取到replyTo,然后发送确认信息到对应的位置

Destination replyTo = message.getJMSReplyTo();

后面的QueueRequestor就是使用replayTo实现的,只不过发送端client默认添加了一个监听在一个临时队列中,然后replayTo设置为该地址,接收端可以向该地址回复确认信息,达到同步调用效果

QueueRequestor同步消息

QueueRequestor发送同步消息,本质违背了mq的异步通讯原则。但是mq还是能够提供应用解耦、异构系统的特性因为使用QueueRequestor发送消息后,会等待接收端的回复,如果收不到回复就会造成死等现象!而且该方法没有设置超时等待的功能。

接收端示例:

package com.zjw.activemq.sync;

import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;

/**
 * @author zjwblog <co.zjwblog@gmail.com>
 * @version 1.0
 * @date 2021/12/15 9:13 上午
 */
public class QueueRequestorReceiver {
  private static final String ACTIVEMQ_HOST = "failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?Randomize=false";
  private static final String USERNAME = null;
  private static final String PASSWORD = null;
  public static void main(String[] args) throws Exception {
    ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, ACTIVEMQ_HOST);

    Connection conn = factory.createConnection();
    conn.start();

    Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);

    MessageConsumer consumer = session.createConsumer(new ActiveMQQueue("test"));

    consumer.setMessageListener(message -> {
      System.out.println("接到一条消息并发回确认信息.");
      try {
        Destination replyTo = message.getJMSReplyTo();
        System.out.println("replyTo:" + replyTo);
        MessageProducer producer = session.createProducer(replyTo);
        producer.send(session.createTextMessage(replyTo.toString()));

      } catch (JMSException e) {
        e.printStackTrace();
      }
    });

  }
}

发送端示例:


package com.zjw.activemq.sync;

import javax.jms.Queue;
import javax.jms.QueueRequestor;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * @author zjwblog <co.zjwblog@gmail.com>
 * @version 1.0
 * @date 2021/12/15 9:12 上午
 *
 * @description 发送端会被阻塞,等待服务端返回响应之后才会往后走,相当于同步方式请求
 */
public class QueueRequestorSender {
  private static final String ACTIVEMQ_HOST = "failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?Randomize=false";
  private static final String USERNAME = null;
  private static final String PASSWORD = null;
  public static void main(String[] args) throws Exception {
    ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, ACTIVEMQ_HOST);
    ActiveMQConnection conn = (ActiveMQConnection)factory.createConnection();
    // 这个不能少,因为要监听回来的消息,如果只是简单的发送消息可以写这个
    conn.start();

    QueueSession session = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

    Queue queue = session.createQueue("test");

    QueueRequestor requestor = new QueueRequestor(session, queue);

    TextMessage message = session.createTextMessage("Hello");
    System.out.println("发送同步请求");
    TextMessage respMsg = (TextMessage)requestor.request(message);
    System.out.println("收到请求响应");
    System.out.println("Response message content: [" + respMsg.getText() +"]");
  }
}

生产环境中影响性能的几个因素

OOM

activemq启动脚本中配置内存

ACTIVEMQ_OPTS=-Xms1G -Xmx1G

修改配置文件,以及配置文件中的百分比

<memoryUsage percentOfJvmHeap="70" />

SystemUsage配置设置了一些系统内存和硬盘容量,当系统消耗超过这些容量设置时,amq会“slow down producer”,还是很重要的。

持久化和非持久化
消息异步发送

建议使用默认,强制开启有可能丢失消息

异步发送丢失消息的场景是:生产者设置UseAsyncSend=true,使用producer.send(msg)持续发送消息。由于消息不阻塞,生产者会认为所有send的消息均被成功发送至MQ。如果服务端突然宕机,此时生产者端内存中尚未被发送至MQ的消息都会丢失。

一下方式可以设置开启:

new ActiveMQConnectionFactory("tcp://locahost:61616?jms.useAsyncSend=true");
((ActiveMQConnectionFactory)connectionFactory).setUseAsyncSend(true);
((ActiveMQConnection)connection).setUseAsyncSend(true)
批量确认

ActiveMQ缺省支持批量确认消息,批量确认可以提高系统性能

关闭方法

new ActiveMQConnectionFactory("tcp://locahost:61616?jms.optimizeAcknowledge=false");
((ActiveMQConnectionFactory)connectionFactory).setOptimizeAcknowledge(fase);
((ActiveMQConnection)connection).setOptimizeAcknowledge(true);

消费缓冲与消息积压prefetchSize

消费者端,一般来说消费的越快越好,broker的积压越小越好。

但是考虑到事务性和客户端确认的情况,如果一个消费者一次获取到了很多消息却都不确认,这会造成事务上下文变大,broker端这种“半消费状态”的数据变多,所以ActiveMQ有一个prefetchSize参数来控制未确认情况下,最多可以预获取多少条记录。

Pre-fetch默认值

consumer typedefault value
queue1000
queue browser500
topic32766
durable topic1000
可以通过3中方式设置prefetchSize

创建连接时整体设置

String ACTIVEMQ_HOST = "failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?Randomize=false&jms.prefetchPolicy.all=50";
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(null, null, ACTIVEMQ_HOST);

创建连接时对topic和queue单独设置

String ACTIVEMQ_HOST = "failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)?Randomize=false&jms.prefetchPolicy.queuePrefetch=1&jms.prefetchPolicy.topicPrefetch=1";
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(null, null, ACTIVEMQ_HOST);

针对destination单独设置

Destination topic = session.createTopic("test?consumer.prefetchSize=10");

注意:对destination设置prefetchsize后会覆盖连接时的设置值

消息到底是推还是拉?

发送消息时是推向broker

获取消息时:

  • 默认是一条一条的推

  • 当customer的prefetchSize满的时候停止推消息

  • 当customer的prefetchSize==0时拉取消息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值