RabbitMQ-rabbitMq各个特性的使用(三)
准备
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各个特性的使用(三)的更多相关文章
- IdentityServer4 + SignalR Core +RabbitMQ 构建web即时通讯(三)
IdentityServer4 + SignalR Core +RabbitMQ 构建web即时通讯(三) 后台服务用户与认证 新建一个空的.net core web项目Demo.Chat,端口配置为 ...
- Rabbitmq之高级特性——百分百投递消息&消息确认模式&消息返回模式实现
rabbitmq的高级特性: 如何保障消息的百分之百成功? 要满足4个条件:生产方发送出去,消费方接受到消息,发送方接收到消费者的确认信息,完善的消费补偿机制 解决方案,1)消息落库,进行消息状态打标 ...
- 【详细】【转】C#中理解委托和事件 事件的本质其实就是委托 RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门
[详细][转]C#中理解委托和事件 文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之! 1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托 ...
- .NET 环境中使用RabbitMQ RabbitMQ与Redis队列对比 RabbitMQ入门与使用篇
.NET 环境中使用RabbitMQ 在企业应用系统领域,会面对不同系统之间的通信.集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越发重要.其次,系统中一般会有很多对实时性要求不高的 ...
- error in config file "/etc/rabbitmq/rabbitmq.config"
记录一次RabbitMQ配置文件配置错误 error信息: dill@ubuntu-vm:/usr/share/doc/rabbitmq-server$ sudo /usr/lib/rabbitmq/ ...
- rabbitmq页面出现/etc/rabbitmq/rabbitmq.config(not found)解决方法
如果出现页面出现/etc/rabbitmq/rabbitmq.config(not found) 解决如下:find / -name "rabbitmq.config.example&quo ...
- 【MQ中间件】RabbitMQ -- RabbitMQ死信队列及内存监控(4)
1.RabbitMQ TTL及死信队列 1.1.TTL概述 过期时间TTL表示可以对消息设置预期的时间,在这个时间内都可以被消费者接收获取:过了之后消息将自动被删除.RabbitMQ可以对消息和队列设 ...
- RabbitMQ概念及环境搭建(三)RabbitMQ cluster
测试环境:VMS00781 VMS00782 VMS00386 (centos5.8) 1.先在三台机器上分别安装RabbitMQ Server 2.读取其中一个节点的cookie,并复制到其他节点( ...
- RabbitMQ的高级特性概念理解
1.RabbitMQ中的消息如何保障百分之百的投递成功? 答:百分之百的投递成功,方案可以参考下面的2.3. 2.什么是生产者端的可靠性投递? 答:第一步,生产者保障消息的成功发出.第二步,保障Rab ...
- RabbitMQ从入门到精通(三)
目录 1. 自定义消费者使用 自定义消费端演示 2.消费端的限流策略 2.1 限流的场景与机制 2.2 限流相关API 2.3 限流演示 3. 消费端ACK与重回队列机制 3.1 ACK与NACK 3 ...
随机推荐
- chrome 跨域设置-(完善博客内容)
目的完善自己的一套 ajax前端开发流程,在网上扒了一份成功的案例. 出于一些原因往往需要将浏览器设置成支持跨域的模式,好在chrome浏览器就是支持可跨域的设置,网上也有很多chrome跨域设置教程 ...
- bzoj 4756: [Usaco2017 Jan]Promotion Counting【dfs+树状数组】
思路还是挺好玩的 首先简单粗暴的想法是dfs然后用离散化权值树状数组维护,但是这样有个问题就是这个全局的权值树状数组里并不一定都是当前点子树里的 第一反应是改树状数组,但是显然不太现实,但是可以这样想 ...
- 流程图软件draw.io
工作中经常需要梳理一些流程图,时序图.以前用微软Visio绘制流程图(当然不是正版Visio).后来为了响应国家号召,改用processon(proceson.com)进行绘制流程图.Processo ...
- 小记 vue 打包(build)需要注意的一些事
记录 vue 项目打包的一些事情 首先声明项目都是由 vue-cli 生成; vue 项目从 dev 切换到 prod 时有很多地方需要注意; 首先是大家最需要注意的 ajax 切换环节 以前一开始用 ...
- js 事件循环机制 EventLoop
js 的非阻塞I/O 就是由事件循环机制实现的 众所周知 js是单线程的 也就是上一个任务完成后才能开始新的任务 那js碰到ajxa和定时器.promise这些异步任务怎么办那?这时候就出现了事件 ...
- [Usaco2005 oct]Flying Right 飞行航班
Description 为了表示不能输给人类,农场的奶牛们决定成立一家航空公司.她们计划每天早晨,从密歇根湖湖岸的最北端飞向最南端,晚上从最南端飞往最北端.在旅途中,航空公司可以安排飞机停在某些机场. ...
- 全面学习ORACLE Scheduler特性(10)管理Chains
5.2 管理Chains 5.2.1 修改Chains属性 基本上碰到修改CHAIN属性的机率不会太大,因此确实没啥可修改的,对于CHAIN对象来说,能够修改的属性只有两个:evaluation_ ...
- MySQL 四种事务隔离级别详解及对比--转
http://www.jb51.net/article/100183.htm 接的隔离级别.它的语法如下: ? 1 SET [SESSION | GLOBAL] TRANSACTION ISOLATI ...
- Echarts修改legend样式
legend: { icon: 'rect', itemWidth: 20, itemHeight: 10, itemGap: 10}
- go 语言开发环境的安装与配置
go 语言开发环境的安装与配置 编辑器选择 一直以来都是用sublime,但是听说sublime对于golang的插件支持并不是特别完善,并且VS Code只要在自身所带的扩展商店里安装go插件就可以 ...