练习
朋友说每天耗费这么多时间学算法 技术学全了没,我想了想,雀氏啊
以后每天坚持练习,但是随缘 先技术后项目,算法题其次
矩阵中的局部最大值
给你一个大小为 n x n 的整数矩阵 grid 。
生成一个大小为 (n - 2) x (n - 2) 的整数矩阵 maxLocal ,并满足:
maxLocal[i][j] 等于 grid 中以 i + 1 行和 j + 1 列为中心的 3 x 3 矩阵中的 最大值 。
换句话说,我们希望找出 grid 中每个 3 x 3 矩阵中的最大值。
返回生成的矩阵。
示例 1:
输入:grid = [[9,9,8,1],[5,6,2,6],[8,2,6,4],[6,2,2,2]]
输出:[[9,9],[8,6]]
解释:原矩阵和生成的矩阵如上图所示。
注意,生成的矩阵中,每个值都对应 grid 中一个相接的 3 x 3 矩阵的最大值。
示例 2:
输入:grid = [[1,1,1,1,1],[1,1,1,1,1],[1,1,2,1,1],[1,1,1,1,1],[1,1,1,1,1]]
输出:[[2,2,2],[2,2,2],[2,2,2]]
解释:注意,2 包含在 grid 中每个 3 x 3 的矩阵中。
提示:
n == grid.length == grid[i].length
3 <= n <= 100
1 <= grid[i][j] <= 100
class Solution {
public int[][] largestLocal(int[][] grid) {
int n=grid.length;
int[][] maxLocal=new int[n-2][n-2];
for (int i = 1; i <n-1; i++) {
for (int j = 1; j < n-1; j++) {
int max=grid[i][j];
for (int k = i-1; k <i+2; k++) {
for (int m = j-1; m <j+2; m++) {
maxLocal[i-1][j-1]=Math.max(maxLocal[i-1][j-1],grid[k][m]);
}
}
}
}
return maxLocal;
}
}
没什么说的
枚举 一个一个的遍历
写完才发现 其实可以不让i=1的 因为我一开始想是从中心开始 反正题的意思的3x3 写完发现
还是得遍历3X3
八股
在 Queue 中 poll()和 remove()有什么区别
poll()和remove()都是从队列中取出一个元素
但是前者取出失败会返回null 后者取出失败会抛出异常
Array 和 ArrayList 有何区别?
arraylist比array的功能更多 array是数组 arraylist是数组链表
array可以存基本类型和对象 而arraylist只能存对象
array是指定大小 arraylist大小固定
ArrayList 和 Vector 的区别是什么
vector比arraylist多了个线程同步 更安全
arraylist可以超载,比vector效率更快 不安全 更多人用
如何实现数组和 List 之间的转换?
list转数组 arraylist.toarray()
数组转list array.aslist()
rabbitmq第五天
延迟队列
延迟队列优化
由生产者发消息来指定QC的延迟时间
这样的通用队列来适应不同的需求
配置
TtlQueueConfig
//通用普通队列名称
public static final String QUEUE_C = "QC";
.........
//声明普通队列 C TTL 并绑定到对应的死信交换机
@Bean("queueC")
public Queue queueC(){
Map<String, Object> args = new HashMap<>(3);
//声明当前队列绑定的死信交换机
args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
//声明当前队列的死信路由 routing key
args.put("x-dead-letter-routing-key", "YD");
//声明队列的 TTL 单位ms
return QueueBuilder.durable(QUEUE_C).withArguments(args).build();
}
//声明队列 C 绑定 X 交换机
@Bean
public Binding queueCBindingX(@Qualifier("queueC") Queue queueC, @Qualifier("xExchange") DirectExchange xExchange){
return BindingBuilder.bind(queueC).to(xExchange).with("XC");
}
生产者
SendMsgController
//开始发消息 消息TTL
@GetMapping("sendExpirationMsg/{message}/{ttlTime}")
public void sendMsg(@PathVariable String message,@PathVariable String ttlTime) {
//转换并发送(交换机 routingkey 信息 信息的相关数据(就是信息别名))
rabbitTemplate.convertAndSend("X", "XC", message, correlationData ->{
//发送消息的时候 延迟时长
correlationData.getMessageProperties().setExpiration(ttlTime);
return correlationData;
});
//打印信息
log.info("当前时间:{},发送一条时长{}毫秒 TTL 信息给队列QC:{}", new Date(),ttlTime, message);
}
使用地址测试 20s 2s
http://localhost:8080/ttl/sendExpirationMsg/你好 1/20000
http://localhost:8080/ttl/sendExpirationMsg/你好 2/2000
先入先出FIFO特性
因为 RabbitMQ 只会检查第一个消息是否过期,如果过期则丢到死信队列,
如果第一个消息的延时时长很长,而第二个消息的延时时长很短,第二个消息并不会优先得到执行
基于插件的延迟队列
安装
找对应rabbitmq的版本
https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases
md慢死 请改hosts
感谢大神
找到后
用xftp传到linux
用docker 命令
docker cp rabbitmq_delayed_message_exchange-3.9.0.ez rabbit:/plugins
后面的rabbit是你给自己容器起的名字 不是版本号
进容器
docker exec -it 91bdcca9ab45 /bin/bash
然后
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
版本不对下对应的版本的
通过命令查看已安装的插件
rabbitmq-plugins list
退出容器后,重启 rabbitmq 容器
exit
docker restart rabbit
图例
配置者
DelauedQueueConfig
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
*延迟配置
*/
@Configuration
public class DelauedQueueConfig {
//队列
public static final String DELAYED_QUEUE_NAME = "delayed.queue";
//交换机
public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
//routingkey
public static final String DELAYED_ROUTING_KEY = "delayed.routingkey";
//声明交换队列
@Bean
public Queue delayedQueue() {
return new Queue(DELAYED_QUEUE_NAME);
}
//自定义交换机 我们在这里定义的是一个延迟交换机
@Bean
public CustomExchange delayedExchange() {
Map<String, Object> args = new HashMap<>();
//自定义交换机的类型 直接类型 延迟交换机
args.put("x-delayed-type", "direct");
//交换机名称 交换机类型 持久化 是否自动删除 其他参数
return new CustomExchange(DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false,
args);
}
//绑定延迟队列
//noargs构建方法
@Bean
public Binding bindingDelayedQueue(@Qualifier("delayedQueue") Queue queue, @Qualifier("delayedExchange") CustomExchange delayedExchange) {
return BindingBuilder.bind(queue).to(delayedExchange).with(DELAYED_ROUTING_KEY).noargs();
}
}
生产者
SendMsgController
//开始发消息 基于插件的 信息 及 延迟时间
@GetMapping("sendDelayMsg/{message}/{delayTime}")
public void sendMsg(@PathVariable String message,@PathVariable Integer delayTime) {
rabbitTemplate.convertAndSend(DelauedQueueConfig.DELAYED_EXCHANGE_NAME, DelauedQueueConfig.DELAYED_ROUTING_KEY, message, msg ->{
//发送消息 延迟时长 ms
msg.getMessageProperties().setDelay(delayTime);
return msg;
});
log.info(" 当 前 时 间 : {}, 发送一条延迟 {} 毫秒的信息给队列 delayed.queue:{}", new
Date(),delayTime, message);
}
消费者
DelayQueueConsumer
import com.atguigu.rabbitmq.springbootrabbitmq.config.DelauedQueueConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
*消费者 基于插件的延迟消息
*/
@Slf4j
@Component
public class DelayQueueConsumer {
//监听消息
@RabbitListener(queues = DelauedQueueConfig.DELAYED_QUEUE_NAME)
public void receiveDelayedQueue(Message message){
String msg = new String(message.getBody());
log.info("当前时间:{},收到延时队列的消息:{}", new Date().toString(), msg);
}
}
发起请求:
http://localhost:8080/ttl/sendDelayMsg/come on baby1/20000
http://localhost:8080/ttl/sendDelayMsg/come on baby2/2000
第二个消息被先消费掉了,符合预期
总结
延时队列在需要延时处理的场景下非常有用,使用 RabbitMQ 来实现延时队列可以很好的利用
RabbitMQ 的特性,如:消息可靠发送、消息可靠投递、死信队列来保障消息至少被消费一次以及未被正
确处理的消息不会被丢弃。另外,通过 RabbitMQ 集群的特性,可以很好的解决单点故障问题,不会因为
单个节点挂掉导致延时队列不可用或者消息丢失。
当然,延时队列还有很多其它选择,比如利用 Java 的 DelayQueue,利用 Redis 的 zset,利用 Quartz
或者利用 kafka 的时间轮,这些方式各有特点,看需要适用的场景
发布确定--高级内容
在生产环境中由于一些不明原因,导致 rabbitmq 重启,在 RabbitMQ 重启期间生产者消息投递失败,
导致消息丢失,需要手动处理和恢复。于是,我们开始思考,如何才能进行 RabbitMQ 的消息可靠投递呢?
特别是在这样比较极端的情况,RabbitMQ 集群不可用的时候,无法投递的消息该如何处理呢
(就是服务器宕机了)
发布确定springboot版本
在配置文件当中需要添加
spring.rabbitmq.publisher-confirm-type=correlated
CORRELATED
发布消息成功到交换器后会触发回调方法
配置类
ConfirmConfig
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*配置类 发布确定--高级
*/
@Configuration
public class ConfirmConfig {
//交换机
public static final String CONFIRM_EXCHANGE_NAME = "confirm.exchange";
//队列
public static final String CONFIRM_QUEUE_NAME = "confirm.queue";
//队列 RoutingKey
public static final String CONFIRM_ROUTING_KEY = "key1";
//声明交换机 Exchange
@Bean("confirmExchange")
public DirectExchange confirmExchange(){
return new DirectExchange(CONFIRM_EXCHANGE_NAME);
}
// 声明确认队列
@Bean("confirmQueue")
public Queue confirmQueue(){
return QueueBuilder.durable(CONFIRM_QUEUE_NAME).build();
}
// 声明确认队列绑定关系
@Bean
public Binding queueBinding(@Qualifier("confirmQueue") Queue queue, @Qualifier("confirmExchange") DirectExchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(CONFIRM_ROUTING_KEY);
}
}
消费者
Consumer
import com.atguigu.rabbitmq.springbootrabbitmq.config.ConfirmConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
*接收消息
*/
@Slf4j
@Component
public class Consumer {
@RabbitListener(queues= ConfirmConfig.CONFIRM_QUEUE_NAME)
public void receiveConfirmMessage(Message message){
String msg = new String(message.getBody());
log.info("接受到队列 confirm.queue 消息:{}",msg);
}
}
生产者
Producer
import com.atguigu.rabbitmq.springbootrabbitmq.config.ConfirmConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*开始发消息 测试确认
*/
@RestController
@RequestMapping("/confirm")
@Slf4j
public class Producer {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("sendMessage/{message}")
public void sendMessage(@PathVariable String message){
//发消息
rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,ConfirmConfig.CONFIRM_ROUTING_KEY,message);
log.info("发送消息内容:{}",message);
}
}
测试
http://localhost:8080/confirm/sendMessage/大家好1
回调接口
MyCallBack
@Component
@Slf4j
public class MyCallBack implements
RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
//Post Construct 后构造 最后构造
@PostConstruct
public void init(){
//注入
rabbitTemplate.setConfirmCallback(this);
}
/**
* 交换机不管是否收到消息的一个回调方法(确认回调方法)
* CorrelationData
* 消息相关数据
* ack
* 交换机是否收到消息
*
* 1.发消息 交换机接收到了 回调
* 1.1CorrelationData 保存回调消息的ID及相关信息
* 1.2交换机收到消息 ack=true
* 1.3cause null
* 2.发消息 交换机接收失败了 回调
* 2.1CorrelationData 保存回调消息的ID及相关信息
* 2.2交换机收到消息 ack=false
* 2.3cause 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
//三元运算
String id=correlationData!=null?correlationData.getId():"";
if(ack){
log.info("交换机已经收到 id 为:{}的消息",id);
}else{
log.info("交换机还未收到 id 为:{}消息,由于原因:{}",id,cause);
}
}
//当消息无法路由的时候的回调方法
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String
exchange, String routingKey) {
log.error(" 消 息 {}, 被交换机 {} 退回,退回原因 :{}, 路 由 key:{}",new
String(message.getBody()),exchange,replyText,routingKey);
}
}
/**
*开始发消息 测试确认
*/
@RestController
@RequestMapping("/confirm")
@Slf4j
public class Producer {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("sendMessage/{message}")
public void sendMessage(@PathVariable String message){
//相关数据 自己创建
CorrelationData correlationData1 = new CorrelationData("1");
//发消息
rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME+"123",ConfirmConfig.CONFIRM_ROUTING_KEY,message+"key1",correlationData1);
log.info("发送消息内容:{}",message+"key1");
//相关数据 自己创建
CorrelationData correlationData2 = new CorrelationData("2");
//发消息
rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,ConfirmConfig.CONFIRM_ROUTING_KEY+"2",message+"key1",correlationData2);
log.info("发送消息内容:{}",message+"key12");
}
}
一个是失败交换 直接回调失败原因
一个是队列没有收到
回退消息
在仅开启了生产者确认机制的情况下,交换机接收到消息后,会直接给消息生产者发送确认消息,如
果发现该消息不可路由,那么消息会被直接丢弃,此时生产者是不知道消息被丢弃这个事件的。那么如何
让无法被路由的消息帮我想办法处理一下?最起码通知我一声,我好自己处理啊。通过设置 mandatory 参
数可以在当消息传递过程中不可达目的地时将消息返回给生产者。
//配置文件里面写 这个回退接口
spring. rabbitmq. publisher-returns=true
生产者
//相关数据 自己创建
CorrelationData correlationData1 = new CorrelationData("1");
//发消息
rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,ConfirmConfig.CONFIRM_ROUTING_KEY,message+"key1",correlationData1);
log.info("发送消息内容:{}",message+"key1");
//相关数据 自己创建
CorrelationData correlationData2 = new CorrelationData("2");
//发消息
rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,ConfirmConfig.CONFIRM_ROUTING_KEY+"2",message+"key1",correlationData2);
log.info("发送消息内容:{}",message+"key12");
回调接口
//只有传递消息不到时返回生产者
//当消息无法路由的时候的回调方法
//消息 失败码 失败原因 交换机 交换码
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String
exchange, String routingKey) {
log.error(" 消 息 {}, 被交换机 {} 退回,退回原因 :{}, 路 由 key:{}",new
String(message.getBody()),exchange,replyText,routingKey);
}
效果如上 出现回退原因
备份交换机
图
配置类
ConfirmConfig
//备份交换机
public static final String BACKUP_EXCHANGE_NAME = "backup.exchange";
//备份队列
public static final String BACKUP_QUEUE_NAME = "backup.queue";
//报警队列
public static final String WARNING_QUEUE_NAME = "warning.queue";
。。
。。
。。
。。
//声明交换机 Exchange
@Bean("confirmExchange")
public DirectExchange confirmExchange(){
//alternare 备份
return ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME).durable(true).withArgument("alternare-exchange",BACKUP_EXCHANGE_NAME).build();
}
//备份交换机 删除类型
@Bean("backupExchange")
public FanoutExchange backupExchange(){
return new FanoutExchange(BACKUP_EXCHANGE_NAME);
}
// 声明备份队列
@Bean("backQueue")
public Queue backQueue(){
return QueueBuilder.durable(BACKUP_QUEUE_NAME).build();
}
// 声明警告队列
@Bean("warningQueue")
public Queue warningQueue(){
return QueueBuilder.durable(WARNING_QUEUE_NAME).build();
}
// 声明备份队列绑定关系
@Bean
public Binding backupBinding(@Qualifier("backQueue") Queue queue, @Qualifier("backupExchange") FanoutExchange backupExchange){
return BindingBuilder.bind(queue).to(backupExchange);
}
// 声明报警队列绑定关系
@Bean
public Binding warningBinding(@Qualifier("warningQueue") Queue queue, @Qualifier("backupExchange") FanoutExchange
backupExchange){
return BindingBuilder.bind(queue).to(backupExchange);
}
报警消费者
import com.atguigu.rabbitmq.springbootrabbitmq.config.ConfirmConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
*报警消费者
*/
@Slf4j
@Component
public class WarningConsumer {
//接收报警消息
@RabbitListener(queues = ConfirmConfig.WARNING_QUEUE_NAME)
public void receiveWarningMsg(Message message) {
String msg = new String(message.getBody());
log.error("报警发现不可路由消息:{}", msg);
}
}
mandatory (强制)参数与备份交换机可以一起使用的时候,如果两者同时开启,消息究竟何去何从?谁优先(我的理解是回退和备份一起用 备份优先)
级高,经过上面结果显示答案是备份交换机优先级高。