第六天..

练习

朋友说每天耗费这么多时间学算法 技术学全了没,我想了想,雀氏啊

以后每天坚持练习,但是随缘 先技术后项目,算法题其次

矩阵中的局部最大值

给你一个大小为 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

感谢大神

http://t.csdn.cn/RaSGA

找到后

用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 (强制)参数与备份交换机可以一起使用的时候,如果两者同时开启,消息究竟何去何从?谁优先(我的理解是回退和备份一起用 备份优先)

级高,经过上面结果显示答案是备份交换机优先级高

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值