ActiveMQ介绍(二)

来源:互联网 发布:鞍山北国知春房价 编辑:程序博客网 时间:2024/05/16 17:52

3 ActiveMQ

3.1 MessageProducer

异步/同步发送

ActiveMQ支持以同步(sync)方式或者异步(async)方式向broker发送消息。使用何种方式对send方法的延迟有巨大的影响。对于生产者来说,既然延迟是决定吞吐量的重要因素,那么使用异步发送方式会极大地提高系统的性能。

ActiveMQ缺省使用异步传输方式。按照JMS规范,当在事务外发送持久化消息的时候强制使用同步发送方式。在这种情况下,每一次发送都是同步的,而且阻塞到收到broker的应答。这个应答保证了broker已经成功地将消息持久化,而且不会丢失。但是这样做也严重影响了性能。

如果你对系统可以容忍少量的消息丢失,那么可以在事务外发送持久消息的时候,选择使用异步方式。以下是几种不同的配置方式:

方法一,通过建立ActiveMQConnectionFactory时的URL配置,示例如下:

cf = new ActiveMQConnectionFactory("tcp://locahost:61616?jms.useAsyncSend=true");
方法二,通过ActiveMQConnectionFactory的方法调用,示例:

((ActiveMQConnectionFactory) connectionFactory).setUseAsyncSend(true);
方法三,通过connection的方法调用,示例如下:

((ActiveMQConnection)connection).setUseAsyncSend(true);
同步发送消息的Producer会自动使用producer flow control;对于异步发送消息的producer,要使用producerFlowControl,先要为connection配置一个ProducerWindowSize参数,如下:

((ActiveMQConnectionFactory)cf).setProducerWindowSize(1024000);
ProducerWindowSize是producer在发送消息的过程中,收到broker对于之前发送消息的确认之前,能够发送消息的最大字节数。

设置了ProducerFlowControl属性时,但broker端的内存使用或者单个Destination的内存使用达到上限时,会对Producer端进行限流。


3.2 MessageConsumer

在ActiveMQ中,异步消费要比同步消费的时延要小,相应更快。这是因为同步消费会把消息放入broker内部的消费队列所致。所以最好采用异步消费,在Consumer上实现MessageListener接口,而不是采用MessageConsumer.receive()方法。


3.3 Transport

ActiveMQ目前支持的transport有:VM  Transport、TCP Transport、SSL Transport、Peer Transport、UDP Transport、Multicat Transport、HTTP and HTTPs Transport、Failover Transport、Fanout Transport等。

TCP Transport允许客户端通过TCP socket连接到broker。以下是配置语法:

tcp://hostname:port?transportOptions

这是broker默认使用的transport协议。适用于大部分场合,采用阻塞I/O操作,降低网络延时。

例如:tcp://localhost:61616?trance=false

ActiveMQ同时提供了NIO协议的transport,它的底层也是通过TCP协议实现的。与TCP协议不同的是,它通过SELECT机制来管理多路非阻塞式连接,降低了系统的线程数。提供很好的垂直扩展性。如果一个broker上面的连接过多时,应该采用这种方式来代替TCP Transport协议。配置语法与TCP相同:

nio://hostname:port?transportOptions


说了那么多,下面来一个Java实例:

Producer.java

import org.apache.activemq.ActiveMQConnectionFactory;import javax.jms.*;import javax.xml.soap.Text;import java.net.ConnectException;/** * Created by niuliguo on 2017/1/12. */public class Producer {    private static String uri = "tcp://ip:61616";    private static String user = "xxxx";    private static String password = "xxxx";    private static String subject = "xxxx";    private static boolean transacted = false;    private static Connection connection;    private static final String message = "bizid=jy_cache_test&role=cache";    public static void main(String[] args){        Destination destination = null;        try {            ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(                    user,password,uri);            connection = connectionFactory.createConnection();            connection.start();            if ( null == connection ){                System.exit(-1);            }            Session session = connection.createSession(transacted,Session.AUTO_ACKNOWLEDGE);            destination = session.createTopic(subject);            MessageProducer producer = session.createProducer(destination);            producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);            TextMessage textMessage = session.createTextMessage(message);            int i = 5;            while ( (i--)!=0 ) {                producer.send(textMessage);                try {                    Thread.sleep(5000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        } catch (JMSException e){            e.printStackTrace();        } finally {            try {                if ( null != connection ){                    connection.close();                }            } catch (JMSException e){                e.printStackTrace();            }        }    }}
Consumer.java

import org.apache.activemq.ActiveMQConnectionFactory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.jms.*;/** * Created by niuliguo on 2017/1/12. */public class Consumer implements MessageListener,Runnable{    private final Logger logger = LoggerFactory.getLogger(Consumer.class);    private Session session;    private String uri = "tcp://ip:61616";    private String user = "xxxx";    private String password = "xxxxxx";    private String subject = "xxxxxx";    private boolean transacted = false;    public void onMessage(Message message) {        TextMessage textMessage = (TextMessage)message;        try {            String msg = textMessage.getText();            System.out.println(msg);        } catch (JMSException e){            e.printStackTrace();        }    }    public void run() {        Connection connection = null;        Destination destination = null;        MessageConsumer consumer = null;        try {            ActiveMQConnectionFactory  connectionFactory = new ActiveMQConnectionFactory(user,password,uri);            connection = connectionFactory.createConnection();            connection.start();            session = connection.createSession(transacted,Session.AUTO_ACKNOWLEDGE);            destination = session.createTopic(subject);            consumer = session.createConsumer(destination);            consumer.setMessageListener(this);        } catch ( JMSException e) {            e.printStackTrace();        }    }    public static void main(String[] args){        Consumer consumer = new Consumer();        consumer.run();    }}


Tips

1.生产者和消费者不要和Broker部署在不同的数据中心,避免网络延迟和不稳定对系统带来的影响。

2.请不要将wireFormat.maxInactivityDuration的设置修改成为小于默认值30000ms,易受网络抖动影响。

3.客户端代码中需要复用connect以及session对象,避免每次生产和消费时都重建对象造成对服务器和客户端的压力。

4.生产者发送速率调优:

>对于数据可靠性要求较低,容忍数据在极端情况下丢失的场景中,使用NON_PERSISTENT

>配置alwaysSyncSend=false,对于"NON_PERSISTENT"(非持久化)消息将使用"异步发送";并可以通过windowSize来调整在异步发送时producer端允许积压的(尚未ACK)的消息的尺寸,该值需要根据客户端的内存进行配置。

>尽量少用消息属性

5.消费者消费速率调优:

>optimizeAcknowledge:consumer批量进行ACK,如果无法接受重复收到消息的情况,不要将其设置为TRUE

>asyncDispatch:异步发送,如果设置为true,broker发送消息不会阻塞,但是如果Queue/Topic中的consumer客户端接收和消费消息的速度很快,可以关闭异步转发来避免线程切换。

6.使用VirtualTopic:

>同一应用内consumer端负载均衡的问题:在一个应用上的持久订阅不能使用多个consumer来共同承担消息处理功能。因为美国都会获取所有消息。queue模式可以解决这个问题,broker端又不能将消息发送到多个应用端。所以,既要发布订阅,又要让消费者分组,,这个功能jms规范本身是没有的。

>统一应用内consumer端failover的问题:由于只能使用单个的持久订阅者,如果这个订阅者出错,则应用就无法处理消息了,系统的健壮性不高

解决方法:

>生产者:对于消息发布者来说,就是一个正常的Topic,名称以VirtualTopic.开头。例如:VirtualTopic.TEST。

>消费者:对于消费接收端来说,是个队列,不同应用里使用不同的前缀作为队列的名称,即可表明自己的身份可实现消费端应用分组。例如Consumer.A.VirtualTopic.TEST,说明它是名称为A的消费端,同理Consumer.B.VitualTopic.TEST说明是一个名称为B的客户端。可以在同一个应用里使用多个consumer消费此queue,则可以实现上面两个功能。又因为不同应用使用的queue名称不同(前缀不同),所以不同的应用中都可以接收到全部的消息。每个客户端相当于一个持久订阅者,而且这个客户端可以使用多个消费者共同来承担消费任务。


向Topic发送消息,从Queue消费消息(消费者集群)。Queue:是Topic的持久订阅者。

创建VirtualTopic.Queue:可以先创建Queue,定义该Queue name为Consumer.<consumer name>.VirtualTopic.<topic name>。

7.ActiveMQ服务器跨机房会存在延时问题,建议使用和客户端在同DC的服务器。

8.非持久性消息:消息全部存在内存中,收发效率会更高,但一旦重启MQ服务,内存中的消息会全部消失,不可以找回。持久性消息,消息首先完成持久化过程(磁盘),会比较耗时,但可以确保消息不丢失。手法效率不如非持久性消息。


Author:忆之独秀

Email:leaguenew@qq.com

注明出处:http://blog.csdn.net/lavorange/article/details/65633981




0 0
原创粉丝点击