MateQ

java客户端接入

<dependency>
    <groupId>com.taobao.metaq.final</groupId>
    <artifactId>metaq-client</artifactId>
    <version>4.2.6.Final</version>
</dependency>

发布消息
日常环境不需要申请即可发送,Topic与Producer Group请保证唯一即可,但是如果需要在控制台里管理,则需要在控制台里申请对应的资源。

生产环境必须要到MetaQ Console申请才能发送,目前已经升级成自动审批,如有问题再联系metaq的开发。

对于非常重要的消息,例如订单消息,业务方需要有重发补偿的机制,例如MetaQ服务短暂不可用,此时发往MetaQ的消息将失败,等到MetaQ服务恢复后,业务方可以将之前发送失败的消息重新补偿发送

对于Message Size特别大的消息如何处理?例如1M,几百K的消息 不推荐应用发送超过16K的消息,如果消息确实比较大,发送消息客户端有个配置,默认超过4K的消息开始压缩,消息到达订阅方之前会自动解压,压缩过程对用户透明,但是如果压缩过以后消息仍然较大,我们推荐应用对消息进行拆分,这样做的原因如下

MetaQ通信层没有对大的请求做优化,采用的是典型的RPC方式,不适合大的请求传递,可能会导致网络层的Buffer异常。
MetaQ的服务器存储是一个典型的LRU CACHE系统,过大的消息会占用较多Cache,对于其他应用Cache命中率产生影响
MetaQ的磁盘资源通常比较紧张
MetaQ暂不解决大消息存储问题
发送消息时,如果将来需要查询消息,或者定位消息是否被接收,需要设置Message Key属性,例如设置为订单Id,商品Id等

发送消息时,如果订阅方有过滤需求,请在消息Tag属性上设置相关值,Tag的名称不需要申请,可自由设置,一条消息只允许设置一个Tag。

发送事务消息,出于运维角度考虑,淘宝用户请使用Notify。

订阅消息
日常、预发等非生产环境,订阅消息不需要申请。订阅生产环境的消息,必须要到MetaQ Console申请才能订阅,系统自动审批。

MetaQ支持服务器消息过滤,如果订阅某个Topic,但只关心其中一部分消息,可以使用表达式方式过滤。这样可以避免无用的消息传输到客户端,而且降低了应用与MetaQ服务器的负载。过滤表达式中的Message Tag是由发送方自由指定,MetaQ不做任何限制,当然不能传入非法字符,例如空白字符、|| 等

非顺序消息消费,耗时时间不做限制,但是应用应该尽可能保证耗时短,这样才能达到高性能,另外消费消息Hang住,会导致消息所在队列的消费动作暂停,直到Hang住的消息消费完。对其他队列不受影响

顺序消息消费,耗时时间有限制,要保证每条消息在30s内消费完,超过30s会有潜在的乱序问题。(原因是分布式锁超时问题,但概率极低)

消费方式
集群消费,一条消息只会被同一个group里一个消费端消费。不同group之间相互不影响。

广播消费,一条消息会被同一个group里每一个消费端消费。

消息重复性
MetaQ不能保证消息不重复,"Exactly Only Once"这个特性不支持,原因如下:

发送消息阶段,会存在分布式环境下典型的超时问题,即发送消息阶段不能保证消息不重复。
订阅消息阶段,由于涉及集群订阅,多个订阅者需要Rebalance方式订阅,在Rebalance短暂不一致情况下,会产生消息重复
订阅者意外宕机,消费进度未及时存储,也会产生消息重复
消息重复性问题如何解决?

应用方收到消息后,可通过Tair、DB等去重
应用方可通过主动拉的方式,可保证拉消息绝对不重复,但是分布式协调分配队列问题需要应用来控制
消息中间件团队也在思考如何有效去重,又对整个消息系统性能影响最低。
广播消息
MetaQ支持广播消息,但是广播消息的代价较高,投递比可能在1:100甚至1:1000,对于生产环境订阅广播消息,人工审核环节可能会拒绝,取决于订阅的消息量及消费者集群规模。

MetaQ的广播消息不支持失败重试,原因如下:

对于集群消费的消息支持失败重试,因为失败的维度是一个订阅组集群,而广播消息失败重试维护的则是订阅组集群中的每个订阅者,代价较高。
MetaQ的广播消息消费进度维护在消费者本地磁盘,每隔5s刷盘一次,如果本地磁盘损坏,消费进度如何恢复?

联系MetaQ运维人员,通过运维工具按照时间维度,例如回退一小时,新创建一份消费进度。(此功能开发中)
消息重试
非顺序消息消费失败重试,消费失败的消息发回服务器,应用可以指定这条失败消息下次到达Consumer的时间。消费失败重试次数有限制,通常线上为每个订阅组每条失败消息重试5次(每次消息都会定时重试,定时时间随着重试次数递增,此过程应用可干预)。超过重试次数,消息进入死信队列,并向用户报警。

消息重试对于服务器代价较高,如果某个应用消息量非常大,且失败率非常高,需要大量重试,则不建议使用MetaQ

顺序消息消费失败重试,某个队列正在消费的消息消费失败,会将当前队列挂起(挂起时间应用可通过API设置),其他队列仍然正常消费。

死信队列
消息一旦进入死信队列,则不再向应用投递,MetaQ监控系统会向应用报警 (报警功能开发中)

由于消息一旦进入死信队列,则不能再被订阅,建议应用在最后一次重试消费时,将失败消息保存到DB

消息堆积
MetaQ每台服务器提供大约亿级的消息堆积能力(多个业务方共用),超过堆积阀值,订阅消息吞吐量会下降。

消息实时性
MetaQ采用了长轮询方式从Broker拉消息,实时性同Push方式一致,消息的延迟时间大约几毫秒左右。

发送普通消息

ProducerGroup是否需要申请
不需要,ProducerGroup用户可以自己随意定义

/**
 * Copyright (C) 2010-2013 Alibaba Group Holding Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.taobao.metaq.example.simple;

import com.alibaba.rocketmq.client.exception.MQClientException;

import com.alibaba.rocketmq.client.producer.SendResult;

import com.alibaba.rocketmq.common.message.Message;

import com.taobao.metaq.client.MetaProducer;

public class Producer {

    public static void main(String[] args) throws MQClientException, InterruptedException {

        /**
         * 一个应用创建一个Producer,由应用来维护此对象,可以设置为全局对象或者单例<br>
         * 注意:ProducerGroupName需要由应用来保证唯一<br>
         * ProducerGroup这个概念发送普通的消息时,作用不大,但是发送分布式事务消息时,比较关键,
         * 因为服务器会回查这个Group下的任意一个Producer
         */

        MetaProducer producer = new MetaProducer("manhongTestPubGroup");

        /**
         * Producer对象在使用之前必须要调用start初始化,初始化一次即可<br>
         * 注意:切记不可以在每次发送消息时,都调用start方法
         */
        producer.start();

        /**
         * 下面这段代码表明一个Producer对象可以发送多个topic,多个tag的消息。
         * 注意:send方法是同步调用,只要不抛异常就标识成功。但是发送成功也可会有多种状态,<br>
         * 例如消息写入Master成功,但是Slave不成功,这种情况消息属于成功,但是对于个别应用如果对消息可靠性要求极高,<br>
         * 需要对这种情况做处理。另外,消息可能会存在发送失败的情况,失败重试由应用来处理。
         */
        try {

            for (int i = 0; i < 20; i++) {
                {
                    Message msg = new Message("Jodie_topic_1023",// topic
                            "TagA",// tag
                            "OrderID001",// key,消息的Key字段是为了唯一标识消息的,方便运维排查问题。如果不设置Key,则无法定位消息丢失原因。
                            ("Hello MetaQ").getBytes());// body
                    SendResult sendResult = producer.send(msg);
                    System.out.println(sendResult);
                }

                {
                    Message msg = new Message("TopicTest2",// topic
                            "TagB",// tag
                            "OrderID0034",// key
                            ("Hello MetaQ").getBytes());// body
                    SendResult sendResult = producer.send(msg);
                    System.out.println(sendResult);
                }

                {
                    Message msg = new Message("TopicTest3",// topic
                            "TagC",// tag
                            "OrderID061",// key
                            ("Hello MetaQ").getBytes());// body
                    SendResult sendResult = producer.send(msg);
                    System.out.println(sendResult);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        /**
         * 应用退出时,要调用shutdown来清理资源,关闭网络连接,从MetaQ服务器上注销自己
         * 注意:我们建议应用在JBOSS、Tomcat等容器的退出钩子里调用shutdown方法
         */
        producer.shutdown();
    }
}

订阅普通消息

ConsumerId和consumerGroup的区别
没有区别,叫法不同而已

/**
 * Copyright (C) 2010-2013 Alibaba Group Holding Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.taobao.metaq.example.simple;

import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import com.alibaba.rocketmq.client.exception.MQClientException;
import com.alibaba.rocketmq.common.message.MessageExt;
import com.taobao.metaq.client.MetaPushConsumer;

import java.util.List;


public class PushConsumer {

    /**
     * 当前例子是PushConsumer用法,使用方式给用户感觉是消息从MetaQ服务器推到了应用客户端。<br>
     * 但是实际PushConsumer内部是使用长轮询Pull方式从MetaQ服务器拉消息,然后再回调用户Listener方法<br>
     */
    public static void main(String[] args) throws InterruptedException, MQClientException {
        /**
         * 一个应用创建一个Consumer,由应用来维护此对象,可以设置为全局对象或者单例<br>
         * 注意:ConsumerGroupName需要由应用来保证唯一<br>
         * ConsumerGroupName在生产环境需要申请,非生产环境不需要
         */
        MetaPushConsumer consumer = new MetaPushConsumer("RebalanceTest_Consumer_Group");

        /**
         * 订阅指定topic下tags分别等于TagA或TagC或TagD
         */
        consumer.subscribe("TopicTest1", "TagA || TagC || TagD");
        consumer.setConsumeMessageBatchMaxSize(3);
        /**
         * 订阅指定topic下所有消息<br>
         * 注意:一个consumer对象可以订阅多个topic
         */
        consumer.subscribe("TopicTest2", "*");

        consumer.registerMessageListener(new MessageListenerConcurrently() {

            /**
             * 1、默认msgs里只有一条消息,可以通过设置consumeMessageBatchMaxSize参数来批量接收消息<br>
             * 2、如果设置为批量消费方式,要么都成功,要么都失败。<br>
             * 3、此方法由MetaQ客户端多个线程回调,需要应用来处理并发安全问题<br>
             * 4、抛异常与返回ConsumeConcurrentlyStatus.RECONSUME_LATER等价<br>
             * 5、每条消息失败后,会尝试重试,重试16次都失败,则丢弃<br>
             */
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                                                            ConsumeConcurrentlyContext context) {
                System.out.println(Thread.currentThread().getName() + " Receive New Messages: " + msgs);
                // for (MessageExt msg : msgs) {
                // if (msg.getTags().equals("TagA")) {
                // return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                // }
                // }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });

        /**
         * Consumer对象在使用之前必须要调用start初始化,初始化一次即可<br>
         */
        consumer.start();

        System.out.println("Consumer Started.");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值