RocketMQ(6)生产者核心配置和核心知识

目录

1、消息队列RocketMQ4.X核心配置

2、RocketMQ4.X消息发送状态

3、RocketMQ4.X生产和消费消息重试及处理

4、RocketMQ4.X异步发送消息和回调

5、RocketMQ OneWay发送消息及多种场景对比

6、RocketMQ延迟消息实战和电商系统中应用

7、RocketMQ生产者之MessageQueueSelector

8、顺序消息在电商和证券系统中应用场景

9、RocketMQ顺序消息

10、RocketMQ顺序消息生产者投递

11、RocketMQ顺序消息消息者消费


1、消息队列RocketMQ4.X核心配置

生产者常见核心配置
compressMsgBodyOverHowmuch :消息超过默认字节4096后进行压缩
retryTimesWhenSendFailed : 失败重发次数
maxMessageSize : 最大消息配置,默认128k
topicQueueNums : 主题下面的队列数量,默认是4
autoCreateTopicEnable : 是否自动创建主题Topic, 开发建议为true,生产要为false
defaultTopicQueueNums : 自动创建服务器不存在的topic,默认创建的队列数
autoCreateSubscriptionGroup: 是否允许 Broker 自动创建订阅组,建议线下开发开启,线上关闭
brokerClusterName : 集群名称
brokerId : 0表示Master主节点 大于0表示从节点
brokerIP1 : Broker服务地址
brokerRole : broker角色 ASYNC_MASTER/ SYNC_MASTER/ SLAVE
deleteWhen : 每天执行删除过期文件的时间,默认每天凌晨4点
flushDiskType :刷盘策略, 默认为 ASYNC_FLUSH(异步刷盘), 另外是SYNC_FLUSH(同步刷盘)
listenPort : Broker监听的端口号
mapedFileSizeCommitLog : 单个conmmitlog文件大小,默认是1GB
mapedFileSizeConsumeQueue:ConsumeQueue每个文件默认存30W条,可以根据项目调整
storePathRootDir : 存储消息以及一些配置信息的根目录 默认为用户的 ${HOME}/store
storePathCommitLog:commitlog存储目录默认为${storePathRootDir}/commitlog
storePathIndex: 消息索引存储路径
syncFlushTimeout : 同步刷盘超时时间
diskMaxUsedSpaceRatio : 检测可用的磁盘空间大小,超过后会写入报错


2、RocketMQ4.X消息发送状态

消息发送有同步和异步
     Broker消息投递状态讲解


 FLUSH_DISK_TIMEOUT

没有在规定时间内完成刷盘 (刷盘策略需要为SYNC_FLUSH 才会出这个错误)


FLUSH_SLAVE_TIMEOUT
主从模式下,broker是SYNC_MASTER, 没有在规定时间内完成主从同步


SLAVE_NOT_AVAILABLE

从模式下,broker是SYNC_MASTER, 但是没有找到被配置成Slave的Broker

SEND_OK
发送成功,没有发生上面的三种问题


3、RocketMQ4.X生产和消费消息重试及处理

生产者Producer重试(异步和SendOneWay下配置无效)
     消息重投(保证数据的高可靠性),本身内部支持重试,默认次数是2,
     如果网络情况比较差,或者跨集群则建改多几次(集群跨国)

消费端重试

     原因:消息处理异常、broker端到consumer端各种问题,如网络原因闪断,消费处理失败,ACK返回失败(消费成功后的返回)等等问题。

     注意:

     重试间隔时间配置 ,默认每条消息最多重试 16 次
     messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
     超过重试次数人工补偿
     消费端去重(全局表实现)
     一条消息无论重试多少次,这些重试消息的 Message ID,key 不会改变。
     消费重试只针对集群消费方式生效;

    广播方式不提供失败重试特性,即消费失败后,失败消息不再重试,继续消费新的消息,

模拟异常的情况

broker不会重试

消费者的模式一般都是不改变的,不能由于消息重试而改变消费模式


4、RocketMQ4.X异步发送消息和回调

官方文档:https://rocketmq.apache.org/docs/simple-example/

核心

producer.send(message, new SendCallback(){
    onSuccess(){}
    onException(){}
})

 

public class AsyncProducer {
    public static void main(String[] args) throws Exception {
        //Instantiate with a producer group name.
        DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
        // Specify name server addresses.
        producer.setNamesrvAddr("localhost:9876");
        //Launch the instance.
        producer.start();
        producer.setRetryTimesWhenSendAsyncFailed(0);
        for (int i = 0; i < 100; i++) {
                final int index = i;
                //Create a message instance, specifying topic, tag and message body.
                Message msg = new Message("TopicTest",
                    "TagA",
                    "OrderID188",
                    "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
                producer.send(msg, new SendCallback() {
                    @Override
                    public void onSuccess(SendResult sendResult) {
                        System.out.printf("%-10d OK %s %n", index,
                            sendResult.getMsgId());
                    }
                    @Override
                    public void onException(Throwable e) {
                        System.out.printf("%-10d Exception %s %n", index, e);
                        e.printStackTrace();
                    }
                });
        }
        //Shut down once the producer instance is not longer in use.
        producer.shutdown();
    }
}


注意:

官方例子:如果异步发送消息,调用producer.shutdown()后会失败(异步还没成功就把producer关掉了)
异步发送:不会重试,发送总次数等于1


5、RocketMQ OneWay发送消息及多种场景对比

SYNC :
应用场景:重要通知邮件、报名短信通知、营销短信系统等

ASYNC :异步
应用场景:对RT时间敏感,可以支持更高的并发,回调成功触发相对应的业务,

比如注册成功后通知积分系统发放优惠券(回调中做对应的业务逻辑)

ONEWAY : 无需要等待响应

官方文档:https://rocketmq.apache.org/docs/simple-example/

不管成不成功发过去就好了(可靠性要求不是很高)

public class OnewayProducer {
    public static void main(String[] args) throws Exception{
        //Instantiate with a producer group name.
        DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
        // Specify name server addresses.
        producer.setNamesrvAddr("localhost:9876");
        //Launch the instance.
        producer.start();
        for (int i = 0; i < 100; i++) {
            //Create a message instance, specifying topic, tag and message body.
            Message msg = new Message("TopicTest" /* Topic */,
                "TagA" /* Tag */,
                ("Hello RocketMQ " +
                    i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
            );
            //Call send message to deliver message to one of brokers.
            producer.sendOneway(msg);

        }
        //Shut down once the producer instance is not longer in use.
        producer.shutdown();
    }
}


使用场景:主要是日志收集,适用于某些耗时非常短,但对可靠性要求并不高的场景, 也就是LogServer, 只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答

例如淘宝中,我们每点击一个东西,点击收藏等   都会有一个记录,记录后会提交到淘宝的服务进行分析

(日志回传LogServer)双11就会用onway队列少量机器采集大量的消息,不用部署太多的机器

一般现在都有日志记录

 


6、RocketMQ延迟消息实战和电商系统中应用

什么是延迟消息:
Producer 将消息发送到消息队列 RocketMQ 服务端,(传到broker)但并不期望这条消息立马投递,而是推迟到在当前时间点之后的某一个时间投递到 Consumer 进行消费,该消息即定时消息,目前支持固定精度的消息
代码:rocketmq-store > MessageStoreConfig.java 属性 messageDelayLevel

"1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";

只能改时间,大部分的使用场景是足够使用的,一般不用更改

使用message.setDelayTimeLevel(xxx) //xxx是级别,1表示配置里面的第一个级别,2表示第二个级别

定时消息:目前rocketmq开源版本还不支持,商业版本则有,两者使用场景类似

使用场景
通过消息触发一些定时任务,比如在某一固定时间点向用户发送提醒消息(微信 QQ生日提醒)

前一天有个job轮询将明天生日的客户放到一个队列,第二天进行延时发送


消息生产和消费有时间窗口要求:比如在天猫电商交易中超时未支付关闭订单的场景,在订单创建时会发送一条 延时消息。

这条消息将会在 30 分钟以后投递给消费者,消费者收到此消息后需要判断对应的订单是否已完成支付。 如支付未完成,则关闭订单。如已完成支付则忽略


7、RocketMQ生产者之MessageQueueSelector

生产消息使用MessageQueueSelector投递到Topic下指定的queue

应用场景:顺序消息,分摊负载

同步的方式

arg是与具体的业务强耦合的,根据不同的需求投递


默认topic下的queue数量是4,可以配置    broker.config

//可以使用Jdk8的lambda表达式,只有一个方法需要被实现
producer.send(message, new MessageQueueSelector(){
			select(List<MessageQueue> mqs, Message msg, Object arg){
				Integer queueNum = (Integer)arg;
				return mqs.get(queueNum);
			}
	},0)

异步方式

lambda

支持同步,异步 发送指定的MessageQueue


选择的queue数量必须小于配置的,否则会出错


8、顺序消息在电商和证券系统中应用场景

顺序消息和对应可以使用的场景,订单系统

什么是顺序消息:消息的生产和消费顺序一致

   全局顺序:topic下面全部消息都要有序(少用)改成一个queue

(1)性能要求不高,所有的消息严格按照 FIFO 原则进行消息发布和消费的场景,并行度成为消息系统的瓶颈, 吞吐量不够.
(2)在证券处理中,以人民币兑换美元为例子,在价格相同的情况下,先出价者优先处理,则可以通过全局顺序的方式按照 FIFO 的方式进行发布和消费

    局部顺序:只要保证一组消息被顺序消费即可(RocketMQ使用)生产者和消费者一起配合才能实现

性能要求高
(1)电商的订单创建,同一个订单相关的创建订单消息、订单支付消息、订单退款消息、订单物流消息、订单交易成功消息 都会按照先后顺序来发布和消费
(阿里巴巴集团内部电商系统均使用局部顺序消息,既保证业务的顺序,同时又能保证业务的高性能)

顺序发布:对于指定的一个 Topic,客户端将按照一定的先后顺序发送消息
顺序消费:对于指定的一个 Topic,按照一定的先后顺序接收消息,即先发送的消息一定会先被客户端接收到。

注意:
顺序消息暂不支持广播模式(a机器快b机器慢不能保证哪个先到)
顺序消息不支持异步发送方式,否则将无法严格保证顺序(不能指定消息的顺序)


9、RocketMQ顺序消息

RocketMQ顺序消息的使用

  • 生产端保证发送消息有序,且发送到同一个Topic的同个queue里面,RocketMQ的确是能保证FIFO的

例子:订单的顺序流程是:创建、付款、物流、完成,订单号相同的消息会被先后发送到同一个队列中,
根据MessageQueueSelector里面自定义策略,根据同个业务id放置到同个queue里面,

如订单号取模运算再放到selector中(arg参数),同一个模的值都会投递到同一条queue

public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
		//如果是订单号是字符串,则进行hash,得到一个hash值
	  Long id = (Long) arg;
      long index = id % mqs.size();//取模
      return mqs.get((int)index);
    }
  • 消费端要在保证消费同个topic里的同个队列,不应该用MessageListenerConcurrently,(并发的消费)

应该使用MessageListenerOrderly,自带单线程消费消息,不能再Consumer端再使用多线程去消费,消费端分配到的queue数量是固定的,集群消会锁住当前正在消费的队列集合的消息,所以会保证顺序消费。

官方例子 https://rocketmq.apache.org/docs/order-example/

public class OrderedProducer {
    public static void main(String[] args) throws Exception {
        //Instantiate with a producer group name.
        MQProducer producer = new DefaultMQProducer("example_group_name");
        //Launch the instance.
        producer.start();
        String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"};
        for (int i = 0; i < 100; i++) {
            int orderId = i % 10;
            //Create a message instance, specifying topic, tag and message body.
            Message msg = new Message("TopicTestjjj", tags[i % tags.length], "KEY" + i,
                    ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                Integer id = (Integer) arg;
                int index = id % mqs.size();
                return mqs.get(index);
            }
            }, orderId);

            System.out.printf("%s%n", sendResult);
        }
        //server shutdown
        producer.shutdown();
    }
}
public class OrderedConsumer {
    public static void main(String[] args) throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group_name");

        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

        consumer.subscribe("TopicTest", "TagA || TagC || TagD");

        consumer.registerMessageListener(new MessageListenerOrderly() {

            AtomicLong consumeTimes = new AtomicLong(0);
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
                                                       ConsumeOrderlyContext context) {
                context.setAutoCommit(false);
                System.out.printf(Thread.currentThread().getName() + " Receive New Messages: " + msgs + "%n");
                this.consumeTimes.incrementAndGet();
                if ((this.consumeTimes.get() % 2) == 0) {
                    return ConsumeOrderlyStatus.SUCCESS;
                } else if ((this.consumeTimes.get() % 3) == 0) {
                    return ConsumeOrderlyStatus.ROLLBACK;
                } else if ((this.consumeTimes.get() % 4) == 0) {
                    return ConsumeOrderlyStatus.COMMIT;
                } else if ((this.consumeTimes.get() % 5) == 0) {
                    context.setSuspendCurrentQueueTimeMillis(3000);
                    return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
                }
                return ConsumeOrderlyStatus.SUCCESS;

            }
        });

        consumer.start();

        System.out.printf("Consumer Started.%n");
    }
}

10、RocketMQ顺序消息生产者投递


11、RocketMQ顺序消息消息者消费

RocketMQ顺序消息消费实战

MessageListenerConcurrently(多线程并发执行)
MessageListenerOrderly(单线程顺序执行)

现在是一个produce和一个consumer,如果有多个consumer怎么办??

consumer启动的时候,会到nameserver中拿取Topic,拿取对应的queue,一个Topic中有多个queue,Consumer会平均分配queue的数量

用IDE启动两个节点,

节点1                                                                                               |节点2

Consumer会平均分配queue的数量(consumer数量<queue的数量)
并不是简单禁止并发处理,而是为每个Consumer Quene加个锁,消费每个消息前,需要获得这个消息所在的Queue的锁,这样同个时间,同个Queue的消息不被并发消费,但是不同Queue的消息可以并发处理(局部锁)


 
扩展思维:为什么高并发情况下ConcurrentHashMap比HashTable和HashMap更高效且线程安全?
提示:分段锁Segment

ConcurrentHashMap:分段锁

HashTable:整个map加锁

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智达教育‍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值