rocketmq面试题

rocketmq面试题

一、基本概念

1.消息(message)

消息是指,消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主
题。

2.主题(topic)

Topic表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行
消息订阅的基本单位。

一个生产者可以同时发送多种Topic的消息;而一个消费者只对某种特定的Topic感兴趣,即只可以订阅
和消费一种Topic的消息。

3.标签(Tag)在这里插入图片描述

在这里插入图片描述

4.队列(Queue)

在这里插入图片描述

5.消息标识(MessageId/Key)

在这里插入图片描述

6.分片

分片不同于分区。在RocketMQ中,分片指的是存放相应Topic的Broker。每个分片中会创建出相应数量的分区,即Queue,每个
Queue的大小都是相同的。
在这里插入图片描述

二、系统架构

在这里插入图片描述

1.Producer

在这里插入图片描述

2.Consumer

在这里插入图片描述
在这里插入图片描述

3.Name Server

功能介绍

在这里插入图片描述
在这里插入图片描述

路由注册

在这里插入图片描述

路由剔除

在这里插入图片描述

路由发现

在这里插入图片描述

4.Broker

功能介绍

在这里插入图片描述

模块构成

在这里插入图片描述

集群部署

在这里插入图片描述
在这里插入图片描述

5.工作流程

具体流程

在这里插入图片描述

Topic的创建模式

在这里插入图片描述

读/写队列

在这里插入图片描述
在这里插入图片描述

三、集群搭建理论

1.数据复制与刷盘策略

在这里插入图片描述

2.Broker集群模式

单Master

在这里插入图片描述

多Master

在这里插入图片描述

多Master多Slave模式-异步复制

在这里插入图片描述

多Master多Slave模式-同步双写

在这里插入图片描述
在这里插入图片描述

最佳实践

在这里插入图片描述

四、消息分类

1.普通消息

消息发送分类
同步发送消息

在这里插入图片描述

异步发送消息

在这里插入图片描述
在这里插入图片描述

单向发送消息

在这里插入图片描述

代码举例
定义同步消息发送生产者
public class SyncProducer {
	public static void main(String[] args) throws Exception {
		// 创建一个producer,参数为Producer Group名称
		DefaultMQProducer producer = new DefaultMQProducer("pg");
		// 指定nameServer地址
		producer.setNamesrvAddr("rocketmqOS:9876");
		// 设置当发送失败时重试发送的次数,默认为2次
		producer.setRetryTimesWhenSendFailed(3);
		// 设置发送超时时限为5s,默认3s
		producer.setSendMsgTimeout(5000);
		// 开启生产者
		producer.start();
		// 生产并发送100条消息
		for (int i = 0; i < 100; i++) {
			byte[] body = ("Hi," + i).getBytes();
			Message msg = new Message("someTopic", "someTag", body);
			// 为消息指定key
			msg.setKeys("key-" + i);
			// 发送消息
			SendResult sendResult = producer.send(msg);
			System.out.println(sendResult);
			}
			// 关闭producer
			producer.shutdown();
		}
	}
// 消息发送的状态
public enum SendStatus {
	SEND_OK, // 发送成功
	FLUSH_DISK_TIMEOUT, // 刷盘超时。当Broker设置的刷盘策略为同步刷盘时才可能出
	现这种异常状态。异步刷盘不会出现
	FLUSH_SLAVE_TIMEOUT, // Slave同步超时。当Broker集群设置的Master-Slave的复
	制方式为同步复制时才可能出现这种异常状态。异步复制不会出现
	SLAVE_NOT_AVAILABLE, // 没有可用的Slave。当Broker集群设置为Master-Slave的
	复制方式为同步复制时才可能出现这种异常状态。异步复制不会出现
}
定义异步消息发送生产者
public class AsyncProducer {
	public static void main(String[] args) throws Exception {
		DefaultMQProducer producer = new DefaultMQProducer("pg");
		producer.setNamesrvAddr("rocketmqOS:9876");
		// 指定异步发送失败后不进行重试发送
		producer.setRetryTimesWhenSendAsyncFailed(0);
		// 指定新创建的Topic的Queue数量为2,默认为4
		producer.setDefaultTopicQueueNums(2);
		producer.start();
		for (int i = 0; i < 100; i++) {
			byte[] body = ("Hi," + i).getBytes();
			try {
				Message msg = new Message("myTopicA", "myTag", body);
				// 异步发送。指定回调
				producer.send(msg, new SendCallback() {
					// 当producer接收到MQ发送来的ACK后就会触发该回调方法的执行
					@Override
					public void onSuccess(SendResult sendResult) {
					System.out.println(sendResult);
					}
					@Override
					public void onException(Throwable e) {
						e.printStackTrace();
					} catch (Exception e) {
						e.printStackTrace();
					}
					} // end-for
					// sleep一会儿
					// 由于采用的是异步发送,所以若这里不sleep,
					// 则消息还未发送就会将producer给关闭,报错
					TimeUnit.SECONDS.sleep(3);
					producer.shutdown();
					}
			}
	}
});
定义单向消息发送生产者
public class OnewayProducer {
	public static void main(String[] args) throws Exception{
		DefaultMQProducer producer = new DefaultMQProducer("pg");
		producer.setNamesrvAddr("rocketmqOS:9876");
		producer.start();
		for (int i = 0; i < 10; i++) {
		byte[] body = ("Hi," + i).getBytes();
		Message msg = new Message("single", "someTag", body);
		// 单向发送
		producer.sendOneway(msg);
		}
		producer.shutdown();
		System.out.println("producer shutdown");
	}
}
定义消息消费者
public class SomeConsumer {
	public static void main(String[] args) throws MQClientException {
		// 定义一个pull消费者
		// DefaultLitePullConsumer consumer = new
		DefaultLitePullConsumer("cg");
		// 定义一个push消费者
		DefaultMQPushConsumer consumer = new
		DefaultMQPushConsumer("cg");
		// 指定nameServer
		consumer.setNamesrvAddr("rocketmqOS:9876");
		// 指定从第一条消息开始消费
		consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET
		);
		// 指定消费topic与tag
		consumer.subscribe("someTopic", "*");
		// 指定采用“广播模式”进行消费,默认为“集群模式”
		// consumer.setMessageModel(MessageModel.BROADCASTING);
		// 注册消息监听器
		consumer.registerMessageListener(new
		MessageListenerConcurrently() {
			// 一旦broker中有了其订阅的消息就会触发该方法的执行,
			// 其返回值为当前consumer消费的状态
			@Override
			public ConsumeConcurrentlyStatus
			consumeMessage(List<MessageExt> msgs,
			ConsumeConcurrentlyContext context) {
				// 逐条消费消息
				for (MessageExt msg : msgs) {
					System.out.println(msg);
				}
				// 返回消费状态:消费成功
				return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
			}
		});
		// 开启消费者消费
		consumer.start();
		System.out.println("Consumer Started");
		}
}

2.顺序消息

什么是顺序消息

在这里插入图片描述

为什么需要顺序消息

在这里插入图片描述
在这里插入图片描述

有序性分类

根据有序范围的不同,RocketMQ可以严格地保证两种消息的有序性:分区有序与全局有序。
在这里插入图片描述
在这里插入图片描述

代码举例
public class OrderedProducer {
	public static void main(String[] args) throws Exception {
		DefaultMQProducer producer = new DefaultMQProducer("pg");
		producer.setNamesrvAddr("rocketmqOS:9876");
		producer.start();
			for (int i = 0; i < 100; i++) {
				Integer orderId = i;
				byte[] body = ("Hi," + i).getBytes();
				Message msg = new Message("TopicA", "TagA", body);
				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.println(sendResult);
			}
		producer.shutdown();
	}
}

3.延时消息

什么是延时消息

在这里插入图片描述

延时等级

在这里插入图片描述

延时消息实现原理

在这里插入图片描述
在这里插入图片描述

代码举例

定义DelayProducer类

public class DelayProducer {
	public static void main(String[] args) throws Exception {
		DefaultMQProducer producer = new DefaultMQProducer("pg");
		producer.setNamesrvAddr("rocketmqOS:9876");
		producer.start();
		for (int i = 0; i < 10; i++) {
			byte[] body = ("Hi," + i).getBytes();
			Message msg = new Message("TopicB", "someTag", body);
			// 指定消息延迟等级为3级,即延迟10s
			// msg.setDelayTimeLevel(3);
			SendResult sendResult = producer.send(msg);
			// 输出消息被发送的时间
			System.out.print(new SimpleDateFormat("mm:ss").format(new
			Date()));
			System.out.println(" ," + sendResult);
		}
		producer.shutdown();
	}
}

定义OtherConsumer类

public class OtherConsumer {
	public static void main(String[] args) throws MQClientException {
		DefaultMQPushConsumer consumer = new
		DefaultMQPushConsumer("cg");
		consumer.setNamesrvAddr("rocketmqOS:9876");
		consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET
		);
		consumer.subscribe("TopicB", "*");
		consumer.registerMessageListener(new
		MessageListenerConcurrently() {
			@Override
			public ConsumeConcurrentlyStatus
			consumeMessage(List<MessageExt> msgs,
			ConsumeConcurrentlyContext context) {
				for (MessageExt msg : msgs) {
					// 输出消息被消费的时间
					System.out.print(new
					SimpleDateFormat("mm:ss").format(new Date()));
					System.out.println(" ," + msg);
				}
	return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
	}
	});
	consumer.start();
	System.out.println("Consumer Started");
	}
}

五、事务消息

1.问题引入

在这里插入图片描述
在这里插入图片描述

2.解决思路

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.基础

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.XA模式三剑客

在这里插入图片描述

5.XA模式架构

在这里插入图片描述

6.注意

在这里插入图片描述

7.代码举例

定义工行事务监听器

public class ICBCTransactionListener implements TransactionListener {
	// 回调操作方法
	// 消息预提交成功就会触发该方法的执行,用于完成本地事务
	@Override
	public LocalTransactionState executeLocalTransaction(Message msg,
	Object arg) {
		System.out.println("预提交消息成功:" + msg);
		// 假设接收到TAGA的消息就表示扣款操作成功,TAGB的消息表示扣款失败,
		// TAGC表示扣款结果不清楚,需要执行消息回查
		if (StringUtils.equals("TAGA", msg.getTags())) {
			return LocalTransactionState.COMMIT_MESSAGE;
		} else if (StringUtils.equals("TAGB", msg.getTags())) {
			return LocalTransactionState.ROLLBACK_MESSAGE;
		} else if (StringUtils.equals("TAGC", msg.getTags())) {
			return LocalTransactionState.UNKNOW;
		}
		return LocalTransactionState.UNKNOW;
	}
	// 消息回查方法
	// 引发消息回查的原因最常见的有两个:
	// 1)回调操作返回UNKNWON
	// 2)TC没有接收到TM的最终全局事务确认指令
	@Override
	public LocalTransactionState checkLocalTransaction(MessageExt msg) {
		System.out.println("执行消息回查" + msg.getTags());
		return LocalTransactionState.COMMIT_MESSAGE;
	}
}

定义事物消息生产者

public class TransactionProducer {
	public static void main(String[] args) throws Exception {
		TransactionMQProducer producer = new
		TransactionMQProducer("tpg");
		producer.setNamesrvAddr("rocketmqOS:9876");
		/**
		* 定义一个线程池
		* @param corePoolSize 线程池中核心线程数量
		* @param maximumPoolSize 线程池中最多线程数
		* @param keepAliveTime 这是一个时间。当线程池中线程数量大于核心线程数量
		是,
		* 多余空闲线程的存活时长
	* @param unit 时间单位
	* @param workQueue 临时存放任务的队列,其参数就是队列的长度
	* @param threadFactory 线程工厂
	*/
	ExecutorService executorService = new ThreadPoolExecutor(2, 5,
	100, TimeUnit.SECONDS,
	new ArrayBlockingQueue<Runnable>(2000), new
	ThreadFactory() {
		@Override
		public Thread newThread(Runnable r) {
			Thread thread = new Thread(r);
			thread.setName("client-transaction-msg-check-thread");
			return thread;
			}
			});
			// 为生产者指定一个线程池
			producer.setExecutorService(executorService);
			// 为生产者添加事务监听器
			producer.setTransactionListener(new ICBCTransactionListener());
			producer.start();
			String[] tags = {"TAGA","TAGB","TAGC"};
			for (int i = 0; i < 3; i++) {
				byte[] body = ("Hi," + i).getBytes();
				Message msg = new Message("TTopic", tags[i], body);
				// 发送事务消息
				// 第二个参数用于指定在执行本地事务时要使用的业务参数
				SendResult sendResult =
				producer.sendMessageInTransaction(msg,null);
				System.out.println("发送结果为:" +
				sendResult.getSendStatus());
		}
	}
}

定义消费者
直接使用普通消息的SomeConsumer作为消费者即可。

public class SomeConsumer {
	public static void main(String[] args) throws MQClientException {
		// 定义一个pull消费者
		// DefaultLitePullConsumer consumer = new
		DefaultLitePullConsumer("cg");
		// 定义一个push消费者
		DefaultMQPushConsumer consumer = new
		DefaultMQPushConsumer("cg");
		// 指定nameServer
		consumer.setNamesrvAddr("rocketmqOS:9876");
		// 指定从第一条消息开始消费
		consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET
		);
		// 指定消费topic与tag
		consumer.subscribe("TTopic", "*");
		// 指定采用“广播模式”进行消费,默认为“集群模式”
		// consumer.setMessageModel(MessageModel.BROADCASTING);
		// 注册消息监听器
		consumer.registerMessageListener(new
		MessageListenerConcurrently() {
			// 一旦broker中有了其订阅的消息就会触发该方法的执行,
			// 其返回值为当前consumer消费的状态
			@Override
			public ConsumeConcurrentlyStatus
			consumeMessage(List<MessageExt> msgs,
			ConsumeConcurrentlyContext context) {
				// 逐条消费消息
				for (MessageExt msg : msgs) {
				System.out.println(msg);
				}
				// 返回消费状态:消费成功
				return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
			}
		});
		// 开启消费者消费
		consumer.start();
		System.out.println("Consumer Started");
		}
}

六、消息幂等

1. 什么是消费幂等

在这里插入图片描述

2.消息重复的场景分析

在这里插入图片描述

3.通用解决方案

在这里插入图片描述
在这里插入图片描述

4.消费幂等的实现

在这里插入图片描述
在这里插入图片描述

七、不同mq对比

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值