1. ActiveMQ 协议
官网地址:http://activemq.apache.org/configuring-version-5-transports 从
5.13.0 开始,ActiveMQ 支持通过 TCP、SSL、NIO 和 NIO SSL 自动检测有线协议。支持 OpenWire、STOMP、AMQP 和 MQTT。有关详细信息,请参阅AUTO传输参考。
activemq连接支持多种协议,常用的是openwire,默认是tcp协议,使用的是BIO。
可以通过配置,修改为NIO。NIO也是基于tcp协议的。
这些是配置在activemq.xml文件中的Broker标签下的:
<transportConnectors>
<!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB -->
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
<transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
<transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
<transportConnector name="mqtt" uri="mqtt://0.0.0.0:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
<transportConnector name="ws" uri="ws://0.0.0.0:61614?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
1.1 修改tcp为nio
修改配置
<transportConnector name="nio" uri="nio://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
修改连接
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"nio://localhost:61616?jms.producerWindowSize=1048576"
);
1.2 自动适应协议
修改配置
<transportConnector name="auto+nio" uri="auto+nio://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
修改连接
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"auto+nio://localhost:61616?jms.producerWindowSize=1048576"
);
1.3 openwire(tcp)连接的配置项
在创建ActiveMQConnectionFactory 时,可以在连接后面加很多的配置
很多配置可以加载brokerURL参数的后面用?隔开。
一下是详细的配置项。不需要记住,知道就行。
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?wireFormat.maxInactivityDuration=30000&wireFormat.maxInactivityDurationInitalDelay=10000"/>
</transportConnectors>
Option | Default | Description |
---|---|---|
cacheEnabled | true | Should commonly repeated values be cached so that less marshaling occurs? |
cacheSize | 1024 | When cacheEnabled=true then this parameter is used to specify the number of values to be cached. |
maxInactivityDuration | 30000 | The maximum inactivity duration (before which the socket is considered dead) in milliseconds. On some platforms it can take a long time for a socket to die. Therefore allow the broker to kill connections when they have been inactive for the configured period of time. Used by some transports to enable a keep alive heart beat feature. Inactivity monitoring is disabled when set to a value <= 0 . |
maxInactivityDurationInitalDelay | 10000 | The initial delay before starting inactivity checks. Yes, the word 'Inital' is supposed to be misspelled like that. |
maxFrameSize | MAX_LONG | Maximum allowed frame size. Can help help prevent OOM DOS attacks. |
sizePrefixDisabled | false | Should the size of the packet be prefixed before each packet is marshaled? |
stackTraceEnabled | true | Should the stack trace of exception that occur on the broker be sent to the client? |
tcpNoDelayEnabled | true | Does not affect the wire format, but provides a hint to the peer that TCP_NODELAY should be enabled on the communications Socket. |
tightEncodingEnabled | true | Should wire size be optimized over CPU usage? |
Transport 可用配置选项
Option Name | Default Value | Description |
---|---|---|
backlog | 5000 | Specifies the maximum number of connections waiting to be accepted by the transport server socket. |
closeAsync | true | If true the socket close call happens asynchronously. This parameter should be set to false for protocols like STOMP, that are commonly used in situations where a new connection is created for each read or write. Doing so ensures the socket close call happens synchronously. A synchronous close prevents the broker from running out of available sockets owing to the rapid cycling of connections. |
connectionTimeout | 30000 | If >=1 the value sets the connection timeout in milliseconds. A value of 0 denotes no timeout. Negative values are ignored. |
daemon | false | If true the transport thread will run in daemon mode. Set this parameter to true when embedding the broker in a Spring container or a web container to allow the container to shut down correctly. |
dynamicManagement | false | If true the TransportLogger can be managed by JMX. |
ioBufferSize | 8 * 1024 | Specifies the size of the buffer to be used between the TCP layer and the OpenWire layer where wireFormat based marshaling occurs. |
jmxPort | 1099 | (Client Only) Specifies the port that will be used by the JMX server to manage the TransportLoggers . This should only be set, via URI, by either a client producer or consumer as the broker creates its own JMX server. Specifying an alternate JMX port is useful for developers that test a broker and client on the same machine and need to control both via JMX. |
keepAlive | false | If true , enables TCP KeepAlive on the broker connection to prevent connections from timing out at the TCP level. This should not be confused with KeepAliveInfo messages as used by the InactivityMonitor . |
logWriterName | default | Sets the name of the org.apache.activemq.transport.LogWriter implementation to use. Names are mapped to classes in the resources/META-INF/services/org/apache/activemq/transport/logwriters directory. |
maximumConnections | Integer.MAX_VALUE | The maximum number of sockets allowed for this broker. |
minmumWireFormatVersion | 0 | The minimum remote wireFormat version that will be accepted (note the misspelling). Note: when the remote wireFormat version is lower than the configured minimum acceptable version an exception will be thrown and the connection attempt will be refused. A value of 0 denotes no checking of the remote wireFormat version. |
socketBufferSize | 64 * 1024 | Sets the size, in bytes, for the accepted socket’s read and write buffers. |
soLinger | Integer.MIN_VALUE | Sets the socket’s option soLinger when the value is > -1 . When set to -1 the soLinger socket option is disabled. |
soTimeout | 0 | Sets the socket’s read timeout in milliseconds. A value of 0 denotes no timeout. |
soWriteTimeout | 0 | Sets the socket’s write timeout in milliseconds. If the socket write operation does not complete before the specified timeout, the socket will be closed. A value of 0 denotes no timeout. |
stackSize | 0 | Set the stack size of the transport’s background reading thread. Must be specified in multiples of 128K . A value of 0 indicates that this parameter is ignored. |
startLogging | true | If true the TransportLogger object of the Transport stack will initially write messages to the log. This parameter is ignored unless trace=true . |
tcpNoDelay | false | If true the socket’s option TCP_NODELAY is set. This disables Nagle’s algorithm for small packet transmission. |
threadName | N/A | When this parameter is specified the name of the thread is modified during the invocation of a transport. The remote address is appended so that a call stuck in a transport method will have the destination information in the thread name. This is extremely useful when using thread dumps for degugging. |
trace | false | Causes all commands that are sent over the transport to be logged. To view the logged output define the Log4j logger: log4j.logger.org.apache.activemq.transport.TransportLogger=DEBUG . |
trafficClass | 0 | The Traffic Class to be set on the socket. |
diffServ | 0 | (Client only) The preferred Differentiated Services traffic class to be set on outgoing packets, as described in RFC 2475. Valid integer values: [0,64] . Valid string values: EF , AF[1-3][1-4] or CS[0-7] . With JDK 6, only works when the JVM uses the IPv4 stack. To use the IPv4 stack set the system property java.net.preferIPv4Stack=true . Note: it’s invalid to specify both ‘diffServ and typeOfService’ at the same time as they share the same position in the TCP/IP packet headers |
typeOfService | 0 | (Client only) The preferred Type of Service value to be set on outgoing packets. Valid integer values: [0,256] . With JDK 6, only works when the JVM is configured to use the IPv4 stack. To use the IPv4 stack set the system property java.net.preferIPv4Stack=true . Note: it’s invalid to specify both ‘diffServ and typeOfService’ at the same time as they share the same position in the TCP/IP packet headers. |
useInactivityMonitor | true | When false the InactivityMonitor is disabled and connections will never time out. |
useKeepAlive | true | When true KeepAliveInfo messages are sent on an idle connection to prevent it from timing out. If this parameter is false connections will still timeout if no data was received on the connection for the specified amount of time. |
useLocalHost | false | When true local connections will be made using the value localhost instead of the actual local host name. On some operating systems, such as OS X , it’s not possible to connect as the local host name so localhost is better. |
useQueueForAccept | true | When true accepted sockets are placed onto a queue for asynchronous processing using a separate thread. |
wireFormat | default | The name of the wireFormat factory to use. |
wireFormat.* | N/A | Properties with this prefix are used to configure the wireFormat . |
2. ActiveMQ服务监控Hawtio
官方网站:https://hawt.io/
可以下载两种方式,jar 或者 war
2.1 单独jar运行
2.2 war包嵌入ActiveMQ
- 下载war包,将 war包复制到ActiveMQ的webapps目录下
- 在jetty.xml文件中 bean 标签下
<bean class="org.eclipse.jetty.webapp.WebAppContext">
<property name="contextPath" value="/hawtio" />
<property name="war" value="${activemq.home}/webapps/hawtio.war" />
<property name="logUrlOnStart" value="true" />
</bean>
- 在ActiveMQ.bat 文件下添加
if “%ACTIVEMQ_OPTS%” == “” set ACTIVEMQ_OPTS=-Xms1G -Xmx1G -Dhawtio.realm=activemq -Dhawtio.role=admins -Dhawtio.rolePrincipalClasses=org.apache.activemq.jaas.GroupPrincipal -Djava.util.logging.config.file=logging.properties -Djava.security.auth.login.config="%ACTIVEMQ_CONF%\login.config"
3. JMS消息结构
Message主要由三部分组成,分别是Header,Properties,Body
Header | 消息头,所有类型的这部分格式都是一样的 |
---|---|
Properties | 属性,按类型可以分为应用设置的属性,标准属性和消息中间件定义的属性 |
Body | 消息正文,指我们具体需要消息传输的内容。 |
消息头
public interface Message {
String getJMSMessageID() throws JMSException;
void setJMSMessageID(String id) throws JMSException;
long getJMSTimestamp() throws JMSException;
void setJMSTimestamp(long timestamp) throws JMSException;
byte[] getJMSCorrelationIDAsBytes() throws JMSException;
void setJMSCorrelationIDAsBytes(byte[] correlationID) throws JMSException;
void setJMSCorrelationID(String correlationID) throws JMSException;
String getJMSCorrelationID() throws JMSException;
Destination getJMSReplyTo() throws JMSException;
void setJMSReplyTo(Destination replyTo) throws JMSException;
Destination getJMSDestination() throws JMSException;
void setJMSDestination(Destination destination) throws JMSException;
int getJMSDeliveryMode() throws JMSException;
void setJMSDeliveryMode(int deliveryMode) throws JMSException;
boolean getJMSRedelivered() throws JMSException;
void setJMSRedelivered(boolean redelivered) throws JMSException;
String getJMSType() throws JMSException;
void setJMSType(String type) throws JMSException;
long getJMSExpiration() throws JMSException;
void setJMSExpiration(long expiration) throws JMSException;
int getJMSPriority() throws JMSException;
void setJMSPriority(int priority) throws JMSException;
}
消息头分为自动设置 和手动设置
3.1 自动设置
,会有一个默认值。当然也可以自定义指定。
JMSDeliveryMode | 消息的发送模式,分为NON_PERSISTENT和PERSISTENT,即非持久性模式的和持久性模式。默认设置为PERSISTENT(持久性)。一条持久性消息应该被传送一次(就一次),这就意味着如果JMS提供者出现故障,该消息并不会丢失; 它会在服务器恢复正常之后再次传送。一条非持久性消息最多只会传送一次,这意味着如果JMS提供者出现故障,该消息可能会永久丢失。在持久性和非持久性这两种传送模式中,消息服务器都不会将一条消息向同一消息者发送一次以上(成功算一次)。 |
---|---|
JMSMessageID | 消息ID,需要以ID:开头,用于唯一地标识了一条消息 |
JMSTimestamp | 消息发送时的时间。这条消息头用于确定发送消息和它被消费者实际接收的时间间隔。时间戳是一个以毫秒来计算的Long类型时间值(自1970年1月1日算起)。 |
JMSExpiration | 消息的过期时间,以毫秒为单位,用来防止把过期的消息传送给消费者。任何直接通过编程方式来调用setJMSExpiration()方法都会被忽略。 |
JMSRedelivered | 消息是否重复发送过,如果该消息之前发送过,那么这个属性的值需要被设置为true, 客户端可以根据这个属性的值来确认这个消息是否重复发送过,以避免重复处理。 |
JMSPriority | 消息的优先级,0-4为普通的优化级,而5-9为高优先级,通常情况下,高优化级的消息需要优先发送。任何直接通过编程方式调用setJMSPriority()方法都将被忽略。 |
JMSDestination | 消息发送的目的地,是一个Topic或Queue |
当发送消息时,不自定义设置,这些些值都会有默认值。
当然可以自定已进行设置。
MessageProducer producer = session.createProducer(mqqueue1);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
producer.setTimeToLive(10000);
producer.setPriority(9);
3.2 手动设置
根据以上图,发现这几个都没有值,默认为空。可以进行手动设置
JMSReplyTo | 消息回复的目的地,其值为一个Topic或Queue, 这个由发送者设置,但是接收者可以决定是否响应 |
---|---|
JMSType | 由消息发送者设置的消息类型,代表消息的结构,有的消息中间件可能会用到这个,但这个并不是是批消息的种类,比如TextMessage之类的 |
JMSCorrelationID | 关联的消息ID,这个通常用在需要回传消息的时候 |
3.2.1. JMSReplyTo使用
指定响应目的地,Destination
Provider端代码。
- 发送消息,消息中设置JMSRepalyTo目的地
- 设置consumer,监听JMSRepalyTo目的地
//provider发送消息
textMessage.setText("hello world!");
ActiveMQTempQueue testResponse = new ActiveMQTempQueue("testResponse");
textMessage.setJMSReplyTo(testResponse);
producer.send(textMessage);
System.out.println("发送成功。。。");
//创建consumer,用来监听JMSReplyTo 指定的目的地Destination
MessageConsumer consumer = session.createConsumer(testResponse);
//设置监听
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if(message instanceof TextMessage){
try {
System.out.println("接收消息 = [" + ((TextMessage) message).getText() + "]");
} catch (JMSException e) {
e.printStackTrace();
}
}
if(message instanceof BytesMessage){
BytesMessage bytesMessage = (BytesMessage) message;
byte[] bytes = new byte[1024];
try {
int i = bytesMessage.readBytes(bytes);
System.out.println(i+"-------");
System.out.println(new String(bytes,0,i));
} catch (JMSException e) {
e.printStackTrace();
}
}
if(message instanceof ObjectMessage){
ObjectMessage objectMessage = (ObjectMessage) message;
try {
Serializable object = objectMessage.getObject();
Boy boy = (Boy) object;
System.out.println(boy.toString());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
Consumer端代码
- 监听provider端发送的目的地
- 获取到消息或,创建provider向JMSRelayTo设置的目的中放入响应消息
MessageConsumer consumer = session.createConsumer(mqqueue1);
//设置监听
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if(message instanceof TextMessage){
try {
System.out.println("接收消息 = [" + ((TextMessage) message).getText() + "]");
Destination jmsReplyTo = message.getJMSReplyTo();
MessageProducer producer = session.createProducer(jmsReplyTo);
TextMessage testResponse = session.createTextMessage("收到消息");
producer.send(testResponse);
} catch (JMSException e) {
e.printStackTrace();
}
}
if(message instanceof BytesMessage){
BytesMessage bytesMessage = (BytesMessage) message;
byte[] bytes = new byte[1024];
try {
int i = bytesMessage.readBytes(bytes);
System.out.println(i+"-------");
System.out.println(new String(bytes,0,i));
} catch (JMSException e) {
e.printStackTrace();
}
}
if(message instanceof ObjectMessage){
ObjectMessage objectMessage = (ObjectMessage) message;
try {
Serializable object = objectMessage.getObject();
Boy boy = (Boy) object;
System.out.println(boy.toString());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
3.2.2. JMSCorrelationID 的使用
用于消息之间的关联,给人一种会话的感觉,在provider端和consumer端,将JMSCorrelationID不停的传递,provider端 —>broker----->consumer端 -------->broker------->provider端。整个是一个会话。
provider端
//provider发送消息,设置JMSCorrelationID,设置标志
textMessage.setText("hello world!");
String uuid = String.valueOf(UUID.randomUUID());
textMessage.setJMSCorrelationID(uuid);
textMessage.setStringProperty("flag","0");
producer.send(textMessage);
System.out.println("发送成功。。。");
//Consumer接收消息,利用JMSCorrelationID 和 标记进行筛选。
MessageConsumer consumer = session.createConsumer(mqqueue1,"JMSCorrelationID='"+uuid+"'and flag='1'" );
//设置监听
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if(message instanceof TextMessage){
try {
System.out.println("接收消息 = [" + ((TextMessage) message).getText() + "]");
} catch (JMSException e) {
e.printStackTrace();
}
}
if(message instanceof BytesMessage){
BytesMessage bytesMessage = (BytesMessage) message;
byte[] bytes = new byte[1024];
try {
int i = bytesMessage.readBytes(bytes);
System.out.println(i+"-------");
System.out.println(new String(bytes,0,i));
} catch (JMSException e) {
e.printStackTrace();
}
}
if(message instanceof ObjectMessage){
ObjectMessage objectMessage = (ObjectMessage) message;
try {
Serializable object = objectMessage.getObject();
Boy boy = (Boy) object;
System.out.println(boy.toString());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
consumer端
final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
final Queue mqqueue1 = session.createQueue("testqueue");
//接收消息,利用筛选器,选择标记为0的进行消费
MessageConsumer consumer = session.createConsumer(mqqueue1,"flag='0'");
//设置监听
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if(message instanceof TextMessage){
try {
System.out.println("接收消息 = [" + ((TextMessage) message).getText() + "]");
//接收到消息后发送响应,设置uuid,设置标记为1
String jmsCorrelationID = message.getJMSCorrelationID();
MessageProducer producer = session.createProducer(mqqueue1);
TextMessage testResponse = session.createTextMessage("收到消息");
testResponse.setStringProperty("flag","1");
testResponse.setJMSCorrelationID(jmsCorrelationID);
producer.send(testResponse);
} catch (JMSException e) {
e.printStackTrace();
}
}
if(message instanceof BytesMessage){
BytesMessage bytesMessage = (BytesMessage) message;
byte[] bytes = new byte[1024];
try {
int i = bytesMessage.readBytes(bytes);
System.out.println(i+"-------");
System.out.println(new String(bytes,0,i));
} catch (JMSException e) {
e.printStackTrace();
}
}
if(message instanceof ObjectMessage){
ObjectMessage objectMessage = (ObjectMessage) message;
try {
Serializable object = objectMessage.getObject();
Boy boy = (Boy) object;
System.out.println(boy.toString());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
4. 高级使用
4.1 queue browser
可以查看队列中的消息而不消费,没有订阅的功能
只浏览消息,不会消费消息
Browse进行消息浏览
public static void main(String[] args) throws Exception{
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"tcp://localhost:61616?jms.producerWindowSize=1048576"
);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue mqqueue1 = session.createQueue("testqueue");
//浏览消息
QueueBrowser browser = session.createBrowser(mqqueue1);
Enumeration enumeration = browser.getEnumeration();
while(enumeration.hasMoreElements()){
TextMessage textMessage = (TextMessage)enumeration.nextElement();
String text = textMessage.getText();
System.out.println("浏览消息:"+text);
}
}
4.2 QueueRequestor同步消息
可以发送同步消息
本质违背了mq的异步通讯原则
但是mq还是能够提供应用解耦、异构系统的特性
因为使用QueueRequestor发送消息后,会等待接收端的回复,如果收不到回复就会造成死等现象!
而且该方法没有设置超时等待的功能
流程:
- 发送端利用Message response = queueRequestor.request(textMessage); 进行发送,且发送的消息中会默认增加一个reply的值,临时queue,用来存放响应。调用该方法后,会阻塞等待。
- 接收端接收到消息后,需要放入一个响应消息到临时queue中。
- 此时发送端接收到临时queue中的响应消息,继续执行下面的流程。
发送端:
public static void main(String[] args) throws Exception{
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"nio://localhost:61616?jms.producerWindowSize=1048576"
);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
QueueSession session = (QueueSession)connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue mqqueue1 = session.createQueue("testqueue?producer.windowSize=1048576");
TextMessage textMessage = session.createTextMessage();
QueueRequestor queueRequestor = new QueueRequestor(session,mqqueue1);
//provider发送消息
textMessage.setText("hello world!");
Message response = queueRequestor.request(textMessage);
System.out.println(response);
System.out.println("发送成功。。。");
queueRequestor.close();
session.close();
connection.close();
}
接收端
public static void main(String[] args) throws Exception{
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
"tcp://localhost:61616?jms.producerWindowSize=1048576"
);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue mqqueue1 = session.createQueue("testqueue");
MessageConsumer consumer = session.createConsumer(mqqueue1);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if(message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
String text = textMessage.getText();
System.out.println(text);
Destination jmsReplyTo = textMessage.getJMSReplyTo();
MessageProducer producer = session.createProducer(jmsReplyTo);
TextMessage messageSend = session.createTextMessage("响应消息");
producer.send(messageSend);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
}
5. 消息异步发送
默认情况下,开启事务的持久化发送是发送异步的。不开启事务持久化是发送同步的。
当然也可以强制设置异步发送。但是建议用默认的。否则可能会丢消息。
异步发送丢失消息的场景是:生产者设置UseAsyncSend=true,使用producer.send(msg)持续发送消息。由于消息不阻塞,生产者会认为所有send的消息均被成功发送至MQ。如果服务端突然宕机,此时生产者端内存中尚未被发送至MQ的消息都会丢失。
强制设置异步方式: new
ActiveMQConnectionFactory(“tcp://locahost:61616?jms.useAsyncSend=true”);
((ActiveMQConnectionFactory)connectionFactory).setUseAsyncSend(true);
((ActiveMQConnection)connection).setUseAsyncSend(true)
6. 批量确认消息
ActiveMQ 默认是支持批量确认的,批量确认可以提高性能。
如果不想批量确认,可以设置OptimizeAcknowledge=false;
new ActiveMQConnectionFactory(“tcp://locahost:61616?jms.optimizeAcknowledge=false”);
((ActiveMQConnectionFactory)connectionFactory).setOptimizeAcknowledge(false);
((ActiveMQConnection)connection).setOptimizeAcknowledge(true);