消息中间件activeMQ(3)

消息总是从生产者发送到中间件再有中间件发送给消费者。

发送端特性分析:

producer默认是异步发送消息。在没有开启事务的情况下,producer发送持久化消息是同步的,调用send会阻塞直到broker把消息保存到磁盘并返回确认。

消息设置为持久:

MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);

消息设置为非持久:

MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

 

官方提示异步发送有三种配置方式:

Configuring Async Send using a Connection URI
You can use the Connection Configuration URI to configure async sends as follows

cf = new ActiveMQConnectionFactory("tcp://locahost:61616?jms.useAsyncSend=true");
Configuring Async Send at the ConnectionFactory Level
You can enable this feature on the ActiveMQConnectionFactory object using the property.

((ActiveMQConnectionFactory)connectionFactory).setUseAsyncSend(true);
Configuring Async Send at the Connection Level
Configuring the dispatchAsync setting at this level overrides the settings at the connection factory level.

You can enable this feature on the ActiveMQConnection object using the property.

((ActiveMQConnection)connection).setUseAsyncSend(true);

 


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


alwaysSyncSend/useAsyncSend

    用来设定producer(或者session)发送消息的方式:同步/异步。

    alwaysSyncSend表示消息将总会被同步发送(request<->response),即消息通过底层transport链接发送之后,将会阻塞直到收到broker端的ProducerAck或者sentTimeout。同步发送,是一种担保能力强但发送效率较低的方式,如果消息发送失败,client端可以立即获得异常。如果alwaysSyncSend值为true,则producer将使用同步发送所有消息(同时忽略useAsyncSend选项)

    useAsyncSend表示消息可以被异步发送(oneway),异步发送并不是消息在client缓存起来,而是消息也将立即被transport发送给broker,只是不会阻塞等待broker的ACK。消息发送给broker之后,producer.send方法将会立即返回。异步发送,通常配合broker端“Flow Control”手段使用,它可以最大化produer端的发送效率。我们通常在消息量比较密集的情况下使用异步发送,它可以很大的提升Producer性能;不过这也带来了额外的问题,就是需要消耗较多的Client端内存,同时也会导致broker端性能消耗增加;此外它不能有效的确保消息的发送成功。在useAsyncSend的情况下,客户端需要容忍消息丢失的可能。

在不考虑事务的情况下:

producer发送持久化消息是同步发送,发送是阻塞的,直到收到确认。同步发送肯定是有流量控制的。

producer默认是异步发送,异步发送不会等待broker的确认, 所以就需要考虑流量控制了:

ActiveMQConnectionFactory.setProducerWindowSize(int producerWindowSize)

ProducerWindowSize的含义:producer每发送一个消息,统计一下发送的字节数,当字节数达到ProducerWindowSize值时,需要等待broker的确认,才能继续发送。

windowSize(异步发送)

    窗口尺寸,用来约束在异步发送时producer端允许积压的(尚未ACK)的消息的尺寸,且只对异步发送有意义。每次发送消息之后,都将会导致memoryUsage尺寸增加(+message.size),当broker返回producerAck时,memoryUsage尺寸减少(producerAck.size,此size表示先前发送消息的大小)。可以通过如下2种方式设置:

  • 在brokerUrl中设置: "tcp://localhost:61616?jms.producerWindowSize=1048576",这种设置将会对所有的producer生效。

  • 在destinationUri中设置: "test-queue?producer.windowSize=1048576",此参数只会对使用此Destination实例的producer失效,将会覆盖brokerUrl中的producerWindowSize值。

     此值越大,意味着消耗Client端的内存就越大。

    至此我们已经清楚,任何发送到broker端的消息,broker总会在存储成功后回传ProducerAck信息,并且在ACK中包含消息的size。当producer发送消息时,会首先检测memoryUsage中是否有足够的空间(根据message.size判断),如果空间足够,则消息正常发送;否则将会阻塞,直到收到producerACK且memoryUsage空间释放足够多。(注意,对于持久化消息,只要broker存储消息成功即立即发送ProcuerAck)

sendTimeout

    消息发送最大阻塞(等到ACK)时间。这个参数和syncSend方式配合使用,在同步发送方式中,send方法将会阻塞直到broker反馈ACK,不过这种阻塞的时长无法预期,我们需要使用sendTimeout来设定最大的阻塞时间,如果阻塞超时,将会抛出RequestTimedOutIOException。此值默认为0,表示永不超时。

  • 在brokerUrl中设定

//在brokerUrl中指定,单位:毫秒
jms.sendTimeout=30000
  • connection.setSendTimeout(long) 

    此外需要注意,如果指定了sendTimeout值 >0,无论何时,所有的消息都将同步发送。(忽略alwaysSyncSend、useAsyncSend、消息持久化方式等)。如果sendTimeout <0,则效果和=0一致。

    这是一个非常重要而且有效的调优参数之一;它在producer端消息发送比较密集时,或者producer发送速率与consumer消费速度失衡严重时,对维护broker实例性能稳定具有重要意义。比如,当producer短时间内大批量发送消息,而consumer消费速度相对较低时,会对broker端的存储造成巨大冲击,在极端情况下,可能导致OOM或者broker宕机。所以,我们通常开启此参数,且指定相对合理的值。

    仅仅在producer指定windowSize,可以在Client端避免这些问题,不过这只是一个方面;但是从全局来看,还不够全面,我们还需要在broker端使用“Flow Control”策略来避免上述情况的发生。不过对于"持久化"类型的消息,当broker存储成功之后,总是立即发送ack,所以我们不需要担心会带来潜在的危险。因为“windowSize”、“Flow Control”通常是针对“非持久化”消息。

    “Flow Control”通常称为“流量控制”,它的核心理念就是“按需交付”。如果每个Producer指定的windowSize为64M,但是我们有10个这样的producer,那么意味着broker端在最坏的情况下,需要承载640M数据,不过很不幸的是broker上可供cache的空间只有512M,那么此后broker可能因为内存不足而强制挂起Producer的物理连接(connection),从而导致在connection上无法继续发送任何消息。这会引入另外一个问题,当connection上还有当前通道(Queue或者Topic)的consumer时,会带来死锁;这导致consumer的ACK无法发送-->进而导致消息继续消费,以致于形成“消息无法消费”“也无法发送”的死锁情况。所以通常情况下,我们不能让Consumer和Producer共享一个物理Connection。

  “Flow Control”所能做的就是:当broker内存不足时,让producer暂停一下,直到消息消费之后腾出足够的空间。可以让消息密集发送时,broker能够协调Producer发送的“频率”,同时确保自己的性能处于可控范围

参考文献:

http://shift-alt-ctrl.iteye.com/blog/2034440

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值