准备

1.引入客户端和配置文件依赖类

    <dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.4.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

2.properties文件配置

spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=liqiang
spring.rabbitmq.password=liqiang

3.Test父类

@TestPropertySource("classpath:application.properties")
public class BaseTest {
ConnectionFactory connectionFactory;
@Autowired
RabbitMQConfig rabbitMQConfig;
public BaseTest(){ }
public void initConnectionFactory(){
if(connectionFactory==null) {
connectionFactory = new ConnectionFactory();
connectionFactory.setUsername(rabbitMQConfig.getUsername());
connectionFactory.setPassword(rabbitMQConfig.getPassword());
connectionFactory.setHost(rabbitMQConfig.getHost());
connectionFactory.setPort(rabbitMQConfig.getPort());
}
}
public Connection newConnection() throws IOException, TimeoutException {
initConnectionFactory();
return connectionFactory.newConnection();
}
}

manadatory参数

说明

当次参数设置为true时 交换器无法根据自身类型和路由键找到符合条件的队列name将通过Basic.Retrun命令将消息返回给生产者 为false则直接丢弃

例子

String exchangeName = "test";
String queueName = "testQueue";
String routingKey = "testRoutingKey";
Connection connection = newConnection();
//声明一个channel一个连接可以监听多个channel 连接复用
Channel channel = connection.createChannel();
//声明一个名字为test 非自动删除的 direct类型的exchange 更多配置书37页
channel.exchangeDeclare(exchangeName, "direct", true);
//声明一个持久化,非排他,非自动删除的队列
channel.queueDeclare(queueName, true, false, false, null);
//将队列与交换器绑定
channel.queueBind(queueName, exchangeName, routingKey);
//mandatory设置为true 如果根据routing key找不到队列则会回调通知 false则直接丢弃(这里将routing key设置为""字符串)运行则会触发通知
channel.basicPublish(exchangeName, "", true, MessageProperties.PERSISTENT_TEXT_PLAIN, "你好呀".getBytes());
//未名字路由的回调
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("回调通知" + new String(body));
}
});

imanadatory参数

当immediate为true时如果队列没有消费者 则会通过Basic.Retrun返回 3.0已经移除

备份交换器

说明

路由不成功的时候 不是返回给生产者  而是存放到指定队列

demo

    public void backupsTest() throws IOException, TimeoutException {
String exchangeName = "test";
//备份exchange
String backupsExchange = "testBackup";
String queueName = "testQueue";
//备份队列名字
String backupsQueueName = "backupsTestQueue";
String routingKey = "testRoutingKey";
//设置参数
Map<String, Object> args = new HashMap<>();
args.put("alternate-exchange", backupsExchange);
Connection connection = newConnection();
//声明一个channel一个连接可以监听多个channel 连接复用
Channel channel = connection.createChannel();
//声明一个exchange并指定备份exchange 如果路由失败则路由到备份exchange
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT, true, false, args);
//声明备份exchage fanout类型 是因为 备份不需要路由key
channel.exchangeDeclare(backupsExchange, BuiltinExchangeType.FANOUT, true, false, null);
//声明一个持久化,非排他,非自动删除的队列
channel.queueDeclare(queueName, true, false, false, null);
//将队列与交换器绑定
channel.queueBind(queueName, exchangeName, routingKey);
//声明备份队列
channel.queueDeclare(backupsQueueName, true, false, false, null);
//备份队列交换器是fanout类型 所以不需要routingkey
channel.queueBind(backupsQueueName, backupsExchange, "");
channel.basicPublish(exchangeName, "", true, MessageProperties.PERSISTENT_TEXT_PLAIN, "你好呀".getBytes());
//不会触发回调通知
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("回调通知" + new String(body));
}
});
}

备份交换器几种情况

  • 如果设置备份交换器不存在 则消息会丢失 服务器不会报错
  • 如果备份交换器没有绑定任何队列客户端和rabbitMq则客户端和服务端都不会出现 消息会丢失
  • 如果备份交换器没有绑定任何队列客户端和rabbitMq都不会出现异常情况 消息会丢失
  • 如果备份交换器和manadatory一起使用 则manadatory无效

过期消息

/**
* mq消息过期测试 书60页
* 通过直接给队列设置 则消息到了过期日期则自动移除
* 通过每条消息单独设置 消息过期不会马上移除,而是消费的时候判断是否过期 才移除
*/
//@Test
public void messageTTLTest() throws IOException, TimeoutException {
String exchangeName = "test";
String queueName = "testQueue";
String routingKey = "testRoutingKey";
Connection connection = newConnection();
//声明一个channel一个连接可以监听多个channel 连接复用
Channel channel = connection.createChannel();
//声明一个名字为test 非自动删除的 direct类型的exchange 更多配置书37页
channel.exchangeDeclare(exchangeName, "direct", true);
//设置参数
Map<String, Object> args = new HashMap<>();
//设置单位 毫秒 超时没消费则会被丢弃 如果设置为0 则如果有消费者直接投递 没有消费者则丢弃 可以替代imanadatory
        args.put("x-message-ttl", 6000);
//声明一个持久化,非排他,非自动删除的队列 并设置整个队列消息的过期时间
channel.queueDeclare(queueName, true, false, false, args);
//将队列与交换器绑定
channel.queueBind(queueName, exchangeName, routingKey);
//mandatory设置为true 如果根据routing key找不到队列 则会回调通知 false则直接丢弃
channel.basicPublish(exchangeName, routingKey, true, MessageProperties.PERSISTENT_TEXT_PLAIN, "你好呀".getBytes()); //针对单条消息过期时间设置
// AMQP.BasicProperties.Builder builder=new AMQP.BasicProperties.Builder();
// builder.deliveryMode(2);//持久化消息
// builder.expiration("6000");//设置ttl为6000
// AMQP.BasicProperties properties=builder.build();
// channel.basicPublish(exchangeName,routingKey,true,properties,"你好呀".getBytes());
}

死信队列(DLX)

说明

当一个消息由一个交换器变成死信后他会重新发送到另外一个交换器(称之为死信交换器)改交换器绑定的队列就是死信队列

消息变成死信的几种情况

  • 消息拒绝Basic/Reject 并设置requeue为false
  • 消息过期
  • 队列达到最大长度

demo

    /**
* 死信队列 DLX 书63页 通过ttl加死信 可以实现延迟消息 如果订单半小时没支付关闭
* 说明:死信队列本质也是队列,绑定死信交换器的队列叫死信队列
* 以下三种情况将发送到死信交换器
* 消息被拒绝 并设置 requeue为false
* 消息过期(这里以消息过期为例子)
* 队列达到最大长度
*/
//@Test
public void queueDLXTest() throws IOException, TimeoutException { String exchangeName = "test";
//死信交换器名字
String dlxExchangeName = "dlx_exchange";
String queueName = "testQueue";
//死信队列名字
String dlxQueueName = "dlxQueueName";
String routingKey = "testRoutingKey";
Connection connection = newConnection();
//声明一个channel一个连接可以监听多个channel 连接复用
Channel channel = connection.createChannel();
//声明一个名字为test 非自动删除的 direct类型的exchange 更多配置书37页
channel.exchangeDeclare(exchangeName, "direct", true);
//声明一个交换器 用于死信队列
channel.exchangeDeclare(dlxExchangeName, "direct", true); //为死信队列绑定一个队列
channel.queueDeclare(dlxQueueName, true, false, false, null);
//将队列与交换器绑定
channel.queueBind(dlxQueueName, dlxExchangeName, routingKey);
//设置参数
Map<String, Object> args = new HashMap<>();
//设置单位 毫秒 超时没消费则会被丢弃 如果设置为0 则如果有消费者直接投递 没有消费者则丢弃
args.put("x-message-ttl", 6000);
//指定对应的死信交换器
args.put("x-dead-letter-exchange", dlxExchangeName);
//可以为死信交换器指定路由key如果不指定 则默认使用原routingkey
//args.put("x-dead-letter-routing-key","dlx-routing-key");
//声明一个持久化,非排他,非自动删除的队列 并设置整个队列消息的过期时间
channel.queueDeclare(queueName, true, false, false, args); //将队列与交换器绑定
channel.queueBind(queueName, exchangeName, routingKey);
//mandatory设置为true 如果根据routing key找不到队列 则会回调通知 false则直接丢弃
channel.basicPublish(exchangeName, routingKey, true, MessageProperties.PERSISTENT_TEXT_PLAIN, "你好呀".getBytes()); }

队列过期

指定时间没有被使用则自动移除

    /**
* 队列过期测试
* 队列指定时间没有被使用则移除
*/
//@Test
public void queueTTLTest() throws IOException, TimeoutException {
//设置参数
Map<String, Object> args = new HashMap<>();
//如果队列6秒没被使用则移除
args.put("x-expires", 6000);
String exchangeName = "test";
String queueName = "queueTTl";
String routingKey = "testRoutingKey";
Connection connection = newConnection();
//声明一个channel一个连接可以监听多个channel 连接复用
Channel channel = connection.createChannel();
//队列6秒没被使用则移除
channel.queueDeclare(queueName, true, false, false, args); }

延迟队列

说明

处理类似订单30分钟未支付自动关闭这种需求,或者延迟发短信 通过TTL+DLX实现

demo

/**
* 死信队列 DLX 书63页 通过ttl加死信 可以实现延迟消息 如果订单半小时没支付关闭
* 说明:死信队列本质也是队列,绑定死信交换器的队列叫死信队列
* 以下三种情况将发送到死信交换器
* 消息被拒绝 并设置 requeue为false
* 消息过期(这里以消息过期为例子)
* 队列达到最大长度
*/
//@Test
public void queueDLXTest() throws IOException, TimeoutException { String exchangeName = "test";
//死信交换器名字
String dlxExchangeName = "dlx_exchange";
String queueName = "testQueue";
//死信队列名字
String dlxQueueName = "dlxQueueName";
String routingKey = "testRoutingKey";
Connection connection = newConnection();
//声明一个channel一个连接可以监听多个channel 连接复用
Channel channel = connection.createChannel();
//声明一个名字为test 非自动删除的 direct类型的exchange 更多配置书37页
channel.exchangeDeclare(exchangeName, "direct", true);
//声明一个交换器 用于死信队列
channel.exchangeDeclare(dlxExchangeName, "direct", true); //为死信队列绑定一个队列
channel.queueDeclare(dlxQueueName, true, false, false, null);
//将队列与交换器绑定
channel.queueBind(dlxQueueName, dlxExchangeName, routingKey);
//设置参数
Map<String, Object> args = new HashMap<>();
//设置单位 毫秒 超时没消费则会被丢弃 如果设置为0 则如果有消费者直接投递 没有消费者则丢弃
args.put("x-message-ttl", 6000);
//指定对应的死信交换器
args.put("x-dead-letter-exchange", dlxExchangeName);
//可以为死信交换器指定路由key如果不指定 则默认使用原routingkey
//args.put("x-dead-letter-routing-key","dlx-routing-key");
//声明一个持久化,非排他,非自动删除的队列 并设置整个队列消息的过期时间
channel.queueDeclare(queueName, true, false, false, args); //将队列与交换器绑定
channel.queueBind(queueName, exchangeName, routingKey);
//mandatory设置为true 如果根据routing key找不到队列 则会回调通知 false则直接丢弃
channel.basicPublish(exchangeName, routingKey, true, MessageProperties.PERSISTENT_TEXT_PLAIN, "你好呀".getBytes()); }

优先级队列

说明

消息里面有阻塞情况 保证消息的 优先级高的先执行

demo

    /**
* 只针对队列里面有阻塞情况下 优先级 不然发送一条消费一条优先级就没有意义
* 可以在管理页面同时get10条看是否是有序的
*/
@Test
public void priorityTest() throws IOException, TimeoutException {
String exchangeName = "test";
String queueName = "testQueue";
String routingKey = "testRoutingKey";
Connection connection = newConnection();
//设置参数
Map<String, Object> args = new HashMap<>();
//优先级最大标识
args.put("x-max-priority", 10);
//声明一个channel一个连接可以监听多个channel 连接复用
Channel channel = connection.createChannel();
//声明一个名字为test 非自动删除的 direct类型的exchange 更多配置书37页
channel.exchangeDeclare(exchangeName, "direct", true);
//声明一个持久化,非排他,非自动删除的队列
channel.queueDeclare(queueName, true, false, false, args);
//将队列与交换器绑定
channel.queueBind(queueName, exchangeName, routingKey);
// for (int i = 0; i <= 10; i++) {
AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
int priority = new Random().nextInt(10);
builder.priority(priority);//用于优先级序列
AMQP.BasicProperties properties = builder.build();
//mandatory设置为true 如果根据routing key找不到队列 则会回调通知 false则直接丢弃
channel.basicPublish(exchangeName, routingKey, true, properties, ("你好呀" + priority).getBytes());
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("回调通知" + new String(body));
}
});
}
}

消息持久化

说明

  • 交换器持久化(未设置持久化重启 交换器会消息 会路由不到数据不会影响队列和消息)
  • 队列持久化(未设置持久化话,重启队列会消失 就算消息设置了持久化)
  • 消息持久化(如果队列是持久化 消息也是持久化重启后消息不会消失)

注意:如果将交换器/队列/消息都设置持久化 会降低消息的吞吐量  需要在可靠性和吞吐量做权衡

   场景1:autoack 消费者受到消息还没来得及处理就挂掉了
场景2:服务发送方发送消息到mq 消息还没来得及写入磁盘就挂了(写入磁盘和写入内存是异步的)
解决方案
1:解决此功能是引入MQ的镜像队列 如果master挂了 快速且切到从(并不能完全保证 但是可靠性会高很多 生产环境一般都是镜像队列)
2.通过事物消息 但是事物消息性能很低因为发送消息比普通发送消息多了几个步骤(书:75)
3.通过发送方ack确认的方式(需要保证消费端幂等性 因为网络原因可能未能正确收到ack)
4.ack有2种 一种是同步一种是异步 异步性能优于同步(队列和消息设置了持久化 name将在成功落盘后才能收到ack)

事物消息

可以保证消息不会丢失但是性能很低

public Channel  createChannel() throws IOException, TimeoutException {
String exchangeName = "test";
String queueName = "testQueue";
String routingKey = "testRoutingKey";
Connection connection = newConnection();
//声明一个channel一个连接可以监听多个channel 连接复用
Channel channel = connection.createChannel();
//声明一个名字为test 非自动删除的 direct类型的exchange 更多配置书37页
channel.exchangeDeclare(exchangeName, "direct", true);
//声明一个持久化,非排他,非自动删除的队列
channel.queueDeclare(queueName, true, false, false, null);
//将队列与交换器绑定
channel.queueBind(queueName, exchangeName, routingKey);
return channel;
}
    public void transactionCommit() throws IOException, TimeoutException {
Channel channel = createChannel();
try {
//向broker发送tx.select指令 将信道设置为事物模式 broker响应tx.select-ok表示设置成功
channel.txSelect();
//在事物信道执行发送消息指令 可以多个
channel.basicPublish("test","testRoutingKey",MessageProperties.PERSISTENT_TEXT_PLAIN,"滴滴".getBytes());
//向broker发送tx.commit执行 broker响应tx.commit-OK表示成功成功才会罗盘
channel.txCommit();
} catch (Exception e) {
e.printStackTrace();
//向broker发送tx.rollback broker响应tx.rollback-ok表示成功
channel.txRollback();
}
}

生产者ACK

原理

  将信道设置为ack模式 所有在此信道上面发送的消息都会分配一个唯一id 当消息投递到指定队列后 将会在回传的deliveryTag包含此消息
channel.basicAck(消费端ack)的 multiple参数表示这个序号之前的都已经确认进行批量确认
如果设置了持久化 将在写入磁盘后ack通知

同步ack

    /**
* 同步ack
* 注意事物信道和confirm信道不能共存
*/
//@Test
public void synchroAck() throws IOException, TimeoutException, InterruptedException {
Channel channel = createChannel();
//发送tx.configSelect 将信道设置为publisher confirm模式
channel.confirmSelect();
//在confirm信道发送消息指令 如果多个 则将channel.basicPublish channel.waitForConfirms包在循环里面
channel.basicPublish("test","testRoutingKey",MessageProperties.PERSISTENT_TEXT_PLAIN,"滴滴".getBytes());
//同步等待ack如果非confirm模式 调用此方法会报错 4个重载 书79页
if(channel.waitForConfirms()){
System.out.println("发送消息成功");
} }

批量发送

    /**
* 批量发送
* 性能优于上面一种方式
* @throws IOException
* @throws TimeoutException
*/
//@Test
public void batchSynchroAck() throws IOException, TimeoutException {
Channel channel=createChannel();
AMQP.Confirm.SelectOk selectOk=channel.confirmSelect();
try{
for(int i=0;i<10;i++){
channel.basicPublish("test","testRoutingKey",MessageProperties.PERSISTENT_TEXT_PLAIN,("滴滴"+i).getBytes());
}
/**
* 发送一条消息会生成一个ID(从1开始) mq会回传ack或者nack 客户端里面做了处理 basicpublish内部维护一个SortedSet 回传一个ack则移除一个 实现批量监听ack消息
* 其中有一条未确认就会抛异常
* 缺点是其中一条失败 则全部要重发,所以批次不能太大
*/
if(channel.waitForConfirms()){
System.out.println("发送消息成功");
}
}catch (InterruptedException e){
/**
* 重发逻辑
*/
} }

异步ack

demo异步方式优于前面2种方式

public void AsynchroAck() throws IOException, TimeoutException, InterruptedException {
Channel channel=createChannel(); channel.confirmSelect();
SortedSet confirmSet=new TreeSet();
/**
* 注意 比如你发10条 不一定回调10次 因为id从1开始 如果回调一次10 表示签名都被确认
*/
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("已确认"+deliveryTag);
//为true表示批量确认
if(multiple){
//小于e 之前的元素不包括e
SortedSet ackSet=confirmSet.headSet(deliveryTag+1);
ackSet.clear();
}else{
//删除deliveryTag对象
confirmSet.remove(deliveryTag);
}
} @Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
//为true表示批量NACK
if(multiple){
//清除之前的
/**
* confirmSet.headSet(deliveryTag+1)重发只需要获得当前元素和之前的就行了
*/
//小于e 之前的元素不包括e
SortedSet ackSet=confirmSet.headSet(deliveryTag+1);
ackSet.clear();
}else{
confirmSet.remove(deliveryTag);
}
//处理消息重发逻辑
}
});
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("returnListener"+replyCode);
}
});
for(int i=0;i<10;i++){
System.out.println("消息"+i+"正在发送");
channel.basicPublish("test","testRoutingKey",MessageProperties.PERSISTENT_BASIC,("滴滴"+i).getBytes());
confirmSet.add(channel.getNextPublishSeqNo());//获得生成的序号
}
Thread.sleep(10000);
System.out.println("未确认"+confirmSet.size()); }

消息分发

    /**
* 消息分发
* 当一个队列拥有多个消费者的时候.模式是通过轮询的方式分发(有消费者n 当前第m条消息发送给 m%n的方式确认消费者)
* 但是有个缺点,当某个消费者任务繁重来不及消费消息 则uack消息会堆叠再那里 导致整体消息处理吞吐量下降
*可以通过设置Qos 当uack消息到达一定限量后将不再给当前消息发送消息 每次ack后-1 才继续发
* 此参数对拉模式的消费模式无效
*/
// @Test
public void basicQos() throws IOException, TimeoutException, InterruptedException {
Channel channel=createChannel();
//内部会维护一个计数每推送一条消息+1 ack后-1 到达上限后将不推送
/**
* 一个 channel可以定义多个消费者 重载可以通过global来确认是否是用于整个信道
* channel.basicQos(3,true);
* channel.basicQos(5,false);
* 如果设置了true和false呢 那么表示多个消费者最多收到3个 +起来不超过5
*/
channel.basicQos(5);//默认0表示不限量
channel.basicConsume("testQueue",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
System.out.println(new String(body)+"已消费,deliveryTag:"+envelope.getDeliveryTag());
//channel.basicAck(envelope.getDeliveryTag(),false);
}
});
//可以通过管理页面发现unack是5 就没有再接收消息了
Thread.sleep(5000); }

RabbitMQ-rabbitMq各个特性的使用(三)的更多相关文章

  1. IdentityServer4 + SignalR Core +RabbitMQ 构建web即时通讯(三)

    IdentityServer4 + SignalR Core +RabbitMQ 构建web即时通讯(三) 后台服务用户与认证 新建一个空的.net core web项目Demo.Chat,端口配置为 ...

  2. Rabbitmq之高级特性——百分百投递消息&消息确认模式&消息返回模式实现

    rabbitmq的高级特性: 如何保障消息的百分之百成功? 要满足4个条件:生产方发送出去,消费方接受到消息,发送方接收到消费者的确认信息,完善的消费补偿机制 解决方案,1)消息落库,进行消息状态打标 ...

  3. 【详细】【转】C#中理解委托和事件 事件的本质其实就是委托 RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门

    [详细][转]C#中理解委托和事件   文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之! 1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托 ...

  4. .NET 环境中使用RabbitMQ RabbitMQ与Redis队列对比 RabbitMQ入门与使用篇

    .NET 环境中使用RabbitMQ   在企业应用系统领域,会面对不同系统之间的通信.集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越发重要.其次,系统中一般会有很多对实时性要求不高的 ...

  5. error in config file "/etc/rabbitmq/rabbitmq.config"

    记录一次RabbitMQ配置文件配置错误 error信息: dill@ubuntu-vm:/usr/share/doc/rabbitmq-server$ sudo /usr/lib/rabbitmq/ ...

  6. rabbitmq页面出现/etc/rabbitmq/rabbitmq.config(not found)解决方法

    如果出现页面出现/etc/rabbitmq/rabbitmq.config(not found) 解决如下:find / -name "rabbitmq.config.example&quo ...

  7. 【MQ中间件】RabbitMQ -- RabbitMQ死信队列及内存监控(4)

    1.RabbitMQ TTL及死信队列 1.1.TTL概述 过期时间TTL表示可以对消息设置预期的时间,在这个时间内都可以被消费者接收获取:过了之后消息将自动被删除.RabbitMQ可以对消息和队列设 ...

  8. RabbitMQ概念及环境搭建(三)RabbitMQ cluster

    测试环境:VMS00781 VMS00782 VMS00386 (centos5.8) 1.先在三台机器上分别安装RabbitMQ Server 2.读取其中一个节点的cookie,并复制到其他节点( ...

  9. RabbitMQ的高级特性概念理解

    1.RabbitMQ中的消息如何保障百分之百的投递成功? 答:百分之百的投递成功,方案可以参考下面的2.3. 2.什么是生产者端的可靠性投递? 答:第一步,生产者保障消息的成功发出.第二步,保障Rab ...

  10. RabbitMQ从入门到精通(三)

    目录 1. 自定义消费者使用 自定义消费端演示 2.消费端的限流策略 2.1 限流的场景与机制 2.2 限流相关API 2.3 限流演示 3. 消费端ACK与重回队列机制 3.1 ACK与NACK 3 ...

随机推荐

  1. Spark之RDD的定义及五大特性

    RDD是分布式内存的一个抽象概念,是一种高度受限的共享内存模型,即RDD是只读的记录分区的集合,能横跨集群所有节点并行计算,是一种基于工作集的应用抽象. RDD底层存储原理:其数据分布存储于多台机器上 ...

  2. Springboot拦截器线上代码失效

    今天想测试下线上代码,能否正常的执行未登录的拦截.所以把拦截器的代码给开放出来,但是没想到线上代码addInerceptors(InterceptorRegistry registry) 这个方法一直 ...

  3. git 详细部署及其应用

    第1章 版本控制系统 自动生成备份.随时回滚.知道改动的地方. 1.1 svn和git的区别 1.1.1 svn 集中式的版本控制系统,只有一个中央数据仓库,如果中央数据库仓库挂了或者不可访问,所有的 ...

  4. Asp.NET 知识点总结(二)

    1.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对? 答:不对,有相同的 hash code 编码格式. 2.swtich是否能作用在byte ...

  5. Android 性能优化(14)网络优化( 10)Determining and Monitoring the Connectivity Status

    Determining and Monitoring the Connectivity Status This lesson teaches you to Determine if you Have ...

  6. js实现水波纹背景

    <!DOCTYPE html> <html> <head> <title>水波背景</title> <meta charset=&qu ...

  7. 409 Longest Palindrome 最长回文串

    给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串.在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串.注意:假设字符串的长度不会超过 ...

  8. Objective-C设计模式——生成器Builder(对象创建)

    生成器 生成器,也成为建造者模式,同样是创建对象时的设计模式.该模式下有一个Director(指挥者),客户端知道该类引用用来创建产品.还有一个Builder(建造者),建造者知道具体创建对象的细节. ...

  9. Java易忘知识点统计

    缺少 内容 替代措施 幂运算 借助Math类的pow方法 注意 内容 备注 const Java保留关键字,未使用 其他 强制类型转换时,若要舍入得到最接近的整数,可以使用Math.round方法 J ...

  10. XAMPP--Apache服务无法启动问题定位及处理

    一.问题简述: XAMPP 在使用一段时间后,Apache服务无法启动. 二.详细描述: 安装Xampp服务器套件之后,部署使用正常.一段时间未使用,再次打开时,Apache服务无法启动.错误提示如下 ...