RabbitMQ初步到精通-第六章-RabbitMQ之死信队列

news2024/10/7 12:20:52

目录

第六章-RabbitMQ之死信队列

1. 死信概念

2. 死信架构

3. 死信来源

3.1 消息 TTL 过期

3.2 队列达到最大长度(队列满了,无法再添加数据到 mq 中)

3.3 消息被拒绝(basic.reject 或 basic.nack)并且 requeue=false(不再重新入队)

4. 验证代码

4.1 TTL

4.2 超过Length

4.3 拒绝

5. 总结


第六章-RabbitMQ之死信队列

1. 死信概念

未了解死信这块的内容的时候,经常听到别人提及 ‘死信’ - What? 信息死了? 按字面意思去看就是一个信息由于某些原因导致无法正常消费,造成了这个信息死亡的结果,造成的原因我们再单说。

死信- Dead Letter . 由于消息未正常消费,导致了消息的再次转移或被丢弃-成为死信。本文中涉及到的内容,是消息从原正常消费队列中又重定向路由到了另外一个预留的队列-死信队列。

应用场景:我们应用最多的应该是在消息防丢处理中使用,消费者消费的时候,出现了异常,我们可以使用死信队列的机制,将此条信息转移到一个预留的队列中,再有对应的消费者去处理这部分数据内容。-还是模糊-继续往下-

2. 死信架构

如上图所示,我们可以看出 黄色的部分内容 为 正常业务的交换机 Exchange,以及正常业务的对列1 Queue, 而绿色的部分为 另外一套 交换机与队列,他们的创建与正常的类型无异,姑且我们称他们为 死信交换机及死信队列2. 那现在我们就有两套交换机与队列

那最核心的内容是需要将死信交换机与正常业务的队列1做好绑定关系,这样就能实现当正常队列1无法消费或其他原因时,将消息重定向至死信的交换机中去,死信的交换机再将消息路由至队列2中。

总结一下:生产者生产了一条消息,将消息推送到Broker 中的 正常交换机Exchange,交换机将消息路由至队列1 ,这时由于队列1中的消息未能正常消费,过期了,导致队列1又将消息投递到了死信交换机中,死信交换机再将消息路由到队列2,从而是的消费者2正常消费到了队列2中的死信消息。

3. 死信来源

上文也提到了,消息过期了,导致消息未正常消费,那我们在这里总结一下所有死信的来源

3.1 消息 TTL 过期

TTL意思是 Time to live , 即消息的存活时间,那时间到期了,消息也就达到了他的生命的尽头成为了死信。那如何设置TTL呢?

3.1.1 设置消息的TTL

发送消息的时候设置属性,可以每条消息设置不同的ttl

AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("1000").build();

3.1.2 设置队列的TTL

声明消息队列的时候,这个是全局的,所有发到这个队列的消息的过期时间是一样的

deadLetterParams.put("x-message-ttl", 1000);

队列设置好TTL,则面板中会提示:

 假如你两种都设置了,以小的ttl为准

那这两者设置有啥区别呢?

区别:queue的全局ttl,消息过期立刻就会被删掉;如果是发送消息时设置的ttl,过期之后并不会立刻删掉,这时候消息是否过期是需要投递给消费者的时候判断的。【这个可以做个小实验-投递两条消息,第一条设置过期时间10s,第二条1s, 投递完成,都不进行消费理论第二条应该先过期,但由于第一条没消费,第二条也不会过期】

原因:queue的全局ttl,队列的有效期都一样,先入队列的队列头部,头部也是最早过期的消息,rabbitmq会有一个定时任务从队列的头部开始扫描是否有过期消息即可。而每条设置不同的ttl,只有遍历整个队列才可以筛选出来过期的消息,这样的效率实在是太低,而且如果消息量大了根本不可行,所以rabbitmq在等到消息投递给消费者的时候判断当前消息是否过期,虽然删除的不及时但是不影响功能。
 

3.2 队列达到最大长度(队列满了,无法再添加数据到 mq 中)

队列设置最大长度及存储

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-max-length",10);/∥设置queue的最大长度10
args.put("x-max-length-bytes",1024);//设置最大总字节数1KB
channel.queueDeclare("myqueue", false, false, false, args);

面板展示:

当消息超限会被丢弃或转向至死信队列中。

3.3 消息被拒绝(basic.reject 或 basic.nack)并且 requeue=false(不再重新入队)

消息拒绝方式:这两者的区别是 basic.nack 多一个参数,boolean multiple 支持批量拒绝。其余一致。

channel.basicReject(envelope.getDeliveryTag(), false);
channel.basicNack(envelope.getDeliveryTag(), false, false);

4. 验证代码

4.1 TTL

生产者:


/**
 * @author rabbit
 * @version 1.0.0
 * @Description 死信队列-
 * 当消息在一个队列中变为死信后,它被重新发送到另一个Exchange。
 * @createTime 2022/07/27 19:34:00
 */
public class DeadLetterTTLProducer {

    private static String NORMAL_EXCHANGE_NAME = "normal_exchange_ttl";

    //生产者
    public static void main(String[] args) throws Exception {
        //1、获取connection
        Connection connection = RabbitCommonConfig.getConnection();
        //2、创建channel
        Channel channel = connection.createChannel();
        for (int i = 0; i < 5; i++) {
            sendMsg(channel);
        }
        //4、关闭管道和连接
        channel.close();
        connection.close();
    }

    private static void sendMsg(Channel channel) throws IOException, InterruptedException {
        // 1. 设置消息 TTL 过期时间
        // 2. 设置 队列 TTL 过期时间
        AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("1000").build();
        String message = "info";
        channel.basicPublish(NORMAL_EXCHANGE_NAME, "normal-key", properties, message.getBytes());
        System.out.println("消息发送完成:" + message);
    }

}

消费者:


/**
 * @author rabbit
 * @version 1.0.0
 * @Description 正常队列消费者,除了正常的消费者 需要创建 队列、交换机,绑定关系外,
 * 还需要创建 死信的队列、交换机、绑定关系。
 * 最核心的一点还有,需要将 死信队列的交换机信息做为一个参数,绑定到正常的队列中去。
 * @createTime 2022/11/17 16:53:00
 */
public class DeadLetterTTLConsumer_Normal {

    private static String NORMAL_EXCHANGE_NAME = "normal_exchange_ttl";
    private static String NORMAL_QUEUE_NAME = "normal_queue_ttl";
    private static String DEAD_EXCHANGE_NAME = "dead_exchange";
    private static String DEAD_QUEUE_NAME = "dead-queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1、获取连对象、
        Connection connection = RabbitCommonConfig.getConnection();
        //2、创建channel
        Channel channel = connection.createChannel();
        //3. 创建死信队列与交换机及绑定关系
        handleQueueAndBinding(channel, DEAD_QUEUE_NAME, null, DEAD_EXCHANGE_NAME, "dead-letter-key");

        // 正常队列与死信交换机的绑定关系
        Map<String, Object> deadLetterParams = getNormalAndDeadParams();

        // 4.声明一个正常队列与交换机及绑定关系
        handleQueueAndBinding(channel, NORMAL_QUEUE_NAME, deadLetterParams, NORMAL_EXCHANGE_NAME, "normal-key");

        channel.basicQos(1);

        //5.开启监听Queue
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Normal消费者接收消息: " + new String(body, "UTF-8"));
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };

        channel.basicConsume(NORMAL_QUEUE_NAME, false, consumer);

        System.out.println("Normal消费者启动接收消息......");

        //5、键盘录入,让程序不结束!
        System.in.read();

        //6、释放资源
        channel.close();
        connection.close();

    }

    private static Map<String, Object> getNormalAndDeadParams() {
        Map<String, Object> deadLetterParams = new HashMap<>();
        deadLetterParams.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
        deadLetterParams.put("x-dead-letter-routing-key", "dead-letter-key");
        //队列过期时间限制
        //deadLetterParams.put("x-message-ttl", 1000);
        return deadLetterParams;
    }

    /**
     * 处理队列与绑定关系
     *
     * @param channel
     * @param deadQueueName
     * @param o
     * @param deadExchangeName
     * @param routingKey
     * @throws IOException
     */
    private static void handleQueueAndBinding(Channel channel, String deadQueueName, Map<String, Object> o, String deadExchangeName, String routingKey) throws IOException {
        // 声明一个队列
        channel.queueDeclare(deadQueueName, false, false, false, o);
        // 声明一个交换机
        channel.exchangeDeclare(deadExchangeName, BuiltinExchangeType.DIRECT);
        // 队列与交换机绑定
        channel.queueBind(deadQueueName, deadExchangeName, routingKey);
    }

}

死信消费者:


/**
 * @author rabbit
 * @version 1.0.0
 * @Description 死信队列消费者
 * 与正常消费者一致 监听自己的队列消息即可
 * @createTime 2022/11/17 16:54:00
 */
public class DeadLetterConsumer_Dead {

    private static String DEAD_QUEUE_NAME = "dead-queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1、获取连对象、
        Connection connection = RabbitCommonConfig.getConnection();
        //2、创建channel
        Channel channel = connection.createChannel();
        //3.开启监听Queue
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("死信消费者接收消息: " + new String(body, "UTF-8"));
            }
        };

        channel.basicConsume(DEAD_QUEUE_NAME, true, consumer);
        System.out.println("死信消费者启动等待消费消息:");
        //5、键盘录入,让程序不结束!
        System.in.read();
        //6、释放资源
        channel.close();
        connection.close();
    }

}

结果:

生产者:

消息发送完成:info
消息发送完成:info
消息发送完成:info
消息发送完成:info
消息发送完成:info

消费者:

Normal消费者启动接收消息......
Normal消费者接收消息: info

死信消费者:

死信消费者启动等待消费消息:
死信消费者接收消息: info
死信消费者接收消息: info
死信消费者接收消息: info
死信消费者接收消息: info

4.2 超过Length

生产者:


/**
 * @author rabbit
 * @version 1.0.0
 * @Description 普通的生产者
 * @createTime 2022/11/17 16:51:00
 */
public class DeadLetterLengthProducer {

    private static String EXCHANGE_NAME = "normal_exchange";

    public static void main(String[] args) throws IOException {
        //1、获取连对象、
        Connection connection = RabbitCommonConfig.getConnection();
        //2、创建channel
        Channel channel = connection.createChannel();
        //3. 发送消息
        for (int i = 1; i <= 10; i++) {
            String message = "info" + i;
            channel.basicPublish(EXCHANGE_NAME, "normal-key", null, message.getBytes());
            System.out.println("生产者已发送消息:" + message);
        }
        System.out.println("消息发送完成");
    }
}

消费者:


/**
 * @author rabbit
 * @version 1.0.0
 * @Description 正常队列消费者,除了正常的消费者 需要创建 队列、交换机,绑定关系外,
 * 还需要创建 死信的队列、交换机、绑定关系。
 * 最核心的一点还有,需要将 死信队列的交换机信息做为一个参数,绑定到正常的队列中去。
 * @createTime 2022/11/17 16:53:00
 */
public class DeadLetterLengthConsumer_Normal {

    private static String NORMAL_EXCHANGE_NAME = "normal_exchange";
    private static String NORMAL_QUEUE_NAME = "normal-queue";
    private static String DEAD_EXCHANGE_NAME = "dead_exchange";
    private static String DEAD_QUEUE_NAME = "dead-queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1、获取连对象、
        Connection connection = RabbitCommonConfig.getConnection();
        //2、创建channel
        Channel channel = connection.createChannel();
        //3. 创建死信队列与交换机及绑定关系
        handleQueueAndBinding(channel, DEAD_QUEUE_NAME, null, DEAD_EXCHANGE_NAME, "dead-letter-key");

        // 正常队列与死信交换机的绑定关系
        Map<String, Object> deadLetterParams = getNormalAndDeadParams();

        // 4.声明一个正常队列与交换机及绑定关系
        handleQueueAndBinding(channel, NORMAL_QUEUE_NAME, deadLetterParams, NORMAL_EXCHANGE_NAME, "normal-key");

        channel.basicQos(1);

        //5.开启监听Queue
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Normal消费者接收消息: " + new String(body, "UTF-8"));
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };

        channel.basicConsume(NORMAL_QUEUE_NAME, false, consumer);

        System.out.println("Normal消费者启动接收消息......");

        //5、键盘录入,让程序不结束!
        System.in.read();

        //6、释放资源
        channel.close();
        connection.close();

    }

    private static Map<String, Object> getNormalAndDeadParams() {
        Map<String, Object> deadLetterParams = new HashMap<>();
        deadLetterParams.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
        deadLetterParams.put("x-dead-letter-routing-key", "dead-letter-key");
        deadLetterParams.put("x-max-length", 6);
        return deadLetterParams;
    }

    /**
     * 处理队列与绑定关系
     *
     * @param channel
     * @param deadQueueName
     * @param o
     * @param deadExchangeName
     * @param routingKey
     * @throws IOException
     */
    private static void handleQueueAndBinding(Channel channel, String deadQueueName, Map<String, Object> o, String deadExchangeName, String routingKey) throws IOException {
        // 声明一个队列
        channel.queueDeclare(deadQueueName, false, false, false, o);
        // 声明一个交换机
        channel.exchangeDeclare(deadExchangeName, BuiltinExchangeType.DIRECT);
        // 队列与交换机绑定
        channel.queueBind(deadQueueName, deadExchangeName, routingKey);
    }

}

死信消费者:同上

结果:

生产者:

生产者已发送消息:info1
生产者已发送消息:info2
生产者已发送消息:info3
生产者已发送消息:info4
生产者已发送消息:info5
生产者已发送消息:info6
生产者已发送消息:info7
生产者已发送消息:info8
生产者已发送消息:info9
生产者已发送消息:info10
消息发送完成

消费者:

Normal消费者启动接收消息......
Normal消费者接收消息: info1
Normal消费者接收消息: info5
Normal消费者接收消息: info6
Normal消费者接收消息: info7
Normal消费者接收消息: info8
Normal消费者接收消息: info9
Normal消费者接收消息: info10

死信消费者:

死信消费者启动等待消费消息:
死信消费者接收消息: info2
死信消费者接收消息: info3
死信消费者接收消息: info4

4.3 拒绝

生产者:


/**
 * @author rabbit
 * @version 1.0.0
 * @Description 普通的生产者
 * @createTime 2022/11/17 16:51:00
 */
public class DeadLetterRejectProducer {

    private static String EXCHANGE_NAME = "normal_exchange_reject";

    public static void main(String[] args) throws IOException {
        //1、获取连对象、
        Connection connection = RabbitCommonConfig.getConnection();
        //2、创建channel
        Channel channel = connection.createChannel();
        //3. 发送消息
        for (int i = 1; i <= 10; i++) {
            String message = "info" + i;
            channel.basicPublish(EXCHANGE_NAME, "normal-key", null, message.getBytes());
            System.out.println("生产者已发送消息:" + message);
        }
        System.out.println("消息发送完成");
    }
}

消费者:


/**
 * @author rabbit
 * @version 1.0.0
 * @Description 正常队列消费者,除了正常的消费者 需要创建 队列、交换机,绑定关系外,
 * 还需要创建 死信的队列、交换机、绑定关系。
 * 最核心的一点还有,需要将 死信队列的交换机信息做为一个参数,绑定到正常的队列中去。
 * @createTime 2022/11/17 16:53:00
 */
public class DeadLetterRejectConsumer_Normal {

    private static String NORMAL_EXCHANGE_NAME = "normal_exchange_reject";
    private static String NORMAL_QUEUE_NAME = "normal_queue_reject";
    private static String DEAD_EXCHANGE_NAME = "dead_exchange";
    private static String DEAD_QUEUE_NAME = "dead-queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //1、获取连对象、
        Connection connection = RabbitCommonConfig.getConnection();
        //2、创建channel
        Channel channel = connection.createChannel();
        //3. 创建死信队列与交换机及绑定关系
        handleQueueAndBinding(channel, DEAD_QUEUE_NAME, null, DEAD_EXCHANGE_NAME, "dead-letter-key");

        // 正常队列与死信交换机的绑定关系
        Map<String, Object> deadLetterParams = getNormalAndDeadParams();

        // 4.声明一个正常队列与交换机及绑定关系
        handleQueueAndBinding(channel, NORMAL_QUEUE_NAME, deadLetterParams, NORMAL_EXCHANGE_NAME, "normal-key");

        channel.basicQos(1);

        //5.开启监听Queue
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if ("info5".equals(new String(body, "UTF-8"))) {
                    System.out.println("Normal消费者接收消息:" + new String(body, "UTF-8") + "并且拒绝签收了");
                    // 禁止重新入队
                    //channel.basicReject(envelope.getDeliveryTag(), false);
                    channel.basicNack(envelope.getDeliveryTag(), false, false);
                } else {
                    System.out.println("Normal消费者接收消息:" + new String(body, "UTF-8"));
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };

        channel.basicConsume(NORMAL_QUEUE_NAME, false, consumer);

        System.out.println("Normal消费者启动接收消息......");

        //5、键盘录入,让程序不结束!
        System.in.read();

        //6、释放资源
        channel.close();
        connection.close();

    }

    private static Map<String, Object> getNormalAndDeadParams() {
        Map<String, Object> deadLetterParams = new HashMap<>();
        deadLetterParams.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
        deadLetterParams.put("x-dead-letter-routing-key", "dead-letter-key");
        return deadLetterParams;
    }

    /**
     * 处理队列与绑定关系
     *
     * @param channel
     * @param deadQueueName
     * @param o
     * @param deadExchangeName
     * @param routingKey
     * @throws IOException
     */
    private static void handleQueueAndBinding(Channel channel, String deadQueueName, Map<String, Object> o, String deadExchangeName, String routingKey) throws IOException {
        // 声明一个队列
        channel.queueDeclare(deadQueueName, false, false, false, o);
        // 声明一个交换机
        channel.exchangeDeclare(deadExchangeName, BuiltinExchangeType.DIRECT);
        // 队列与交换机绑定
        channel.queueBind(deadQueueName, deadExchangeName, routingKey);
    }

}

死信消费者:同上

结果:

生产者:

生产者已发送消息:info1
生产者已发送消息:info2
生产者已发送消息:info3
生产者已发送消息:info4
生产者已发送消息:info5
生产者已发送消息:info6
生产者已发送消息:info7
生产者已发送消息:info8
生产者已发送消息:info9
生产者已发送消息:info10
消息发送完成

消费者:

Normal消费者启动接收消息......
Normal消费者接收消息:info1
Normal消费者接收消息:info2
Normal消费者接收消息:info3
Normal消费者接收消息:info4
Normal消费者接收消息:info5并且拒绝签收了
Normal消费者接收消息:info6
Normal消费者接收消息:info7
Normal消费者接收消息:info8
Normal消费者接收消息:info9
Normal消费者接收消息:info10


死信消费者:

死信消费者启动等待消费消息:
死信消费者接收消息: info5

5. 总结

其实死信队列没什么神秘的内容,只是:

1. 在原来的基础上增加了一套死信Exchange和死信Queue

2. 与原来的Queue 和死信Exchange做好了绑定关系。

绑定关系依靠参数设置在业务正常queue声明的时候进行传入:

 Map<String, Object> deadLetterParams = new HashMap<>();
 deadLetterParams.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
 deadLetterParams.put("x-dead-letter-routing-key", "dead-letter-key");

channel.queueDeclare(deadQueueName, false, false, false, deadLetterParams );

与业务Queue绑定好关系后,看下面板的体现:

 这里的 DLX - dead-letter-exchange  = 业务中的参数 :"x-dead-letter-exchange" 指向的 死信Exchange

这里的DLK -dead-letter-routing-key" = 业务中的参数:"x-dead-letter-routing-key" 指向的是 死信的 路由键

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/19923.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于stm32单片机的智能恒温箱游泳池

资料编号&#xff1a;104 下面是相关功能视频演示&#xff1a; 104-基于stm32单片机的智能恒温箱游泳池控制系统Proteus仿真&#xff08;源码仿真全套资料&#xff09;功能介绍&#xff1a; 采用stm32单片机&#xff0c;程序可以设置最高值和最低值&#xff0c;当温度超过最高…

[笔记]vue从入门到入坟《五》vue-cli构建vue webpack项目

参考&#xff1a; 用vue-cli搭建vue项目 vue-cli 项目结构目录简介 文章目录前言一、Vue-cli介绍二、开始2.0 下载安装npm 以及gitnpm安装git安装2.1 全局安装 vue-cli2.2 创建项目目录介绍三、常见问题总结前言 一、Vue-cli介绍 官网 简单地说就是构建vue项目的工具包&#…

SpringBoot SpringBoot 开发实用篇 5 整合第三方技术 5.1 缓存的作用

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇5 整合第三方技术5.1 缓存的作用5.1.1 缓存介绍5.1.2 小结5 整合第三…

【计算机组成原理Note】5.3 CPU数据通路(CPU内部总线+专用数据通路)

5.3.1 数据通路-CPU内部单总线方式 这是第一节的图&#xff0c;将其部件换到一边&#xff1a; 5.3.1.1 CPU内部单总线方式 内部总线是指同一部件&#xff0c;如CPU内部连接各寄存器及运算部件之间的总线; 系统总线是指同一台计算机系统的各部件&#xff0c;如CPU、内存、通道…

C++类型转换+特殊类的设计+单例模式+IO流+空间配置器

索引类型转换(1).C语言的类型转换(2).C四种类型转换a.static_castb.reinterpret_castc.const_castd.dynamic_castvolatile关键字(了解即可)RTTL特殊类的设计(1).设计一个类不能支持拷贝(2).设计一个类&#xff0c;只能在堆上创建对象(3).设计一个类&#xff0c;只能在栈上创建对…

[C++数据结构](33)图,图的遍历,最小生成树,最短路径算法详解

文章目录图的基本概念图的存储结构邻接矩阵邻接表实现图的遍历广度优先遍历深度优先遍历最小生成树Kruskal 算法Prim 算法最短路径Dijkstra 算法Bellman-Ford 算法Bellman-Ford 算法的队列优化Floyd 算法图的基本概念 图(Graph)是由顶点集合及顶点间的关系组成的一种数据结构&…

uni-app入门:常用事件绑定与数据同步

1.常见事件与事件绑定 1.1点击事件bindtap 1.2 文本输入事件bindinput 1.3 切换事件bindtouchend 2.数据同步 2.1事件回调 2.2逻辑层中page对象中的中数据如何进行改变 2.3页面触发事件如何传参到page中数据 …

Android 基础知识1-开发环境搭建

1.1 Android开发准备工作 配置Android开发环境之前&#xff0c;首先需要了解Android对操作系统的要求。它可以使用Windows XP 已经以上版本、Mac OS 、Linux等操作系统&#xff0c;我是以Windows系统为例学习以及开发的。 1.2 开发包以及其工具的安装和配置 Android以Java作为…

内蒙古工程学院无线网络设计与规划

摘 要 I ABSTRACT II 第1章 绪论 1 1.1选题背景及意义 1 1.1.1选题背景 1 1.1.2选题意义 1 1.1.3国内外研究现状 2 1.2需求分析 3 1.2.1建设背景 3 1.2.2总体建设目标 4 1.2.4具体实施目标 5 1.3校园无线网在教育中的发展 6 第2章 无线网络的应用 7 2.1 无线网络的概述 7 2.1.1…

【毕业设计】60-基于ZigBee无线智能消防\烟雾报警逃生系统设计(原理图工程、源代码、低重复率参考文档、实物图)

【毕业设计】60-基于ZigBee无线智能消防\烟雾报警逃生系统设计&#xff08;原理图工程、源代码、低重复率参考文档、实物图&#xff09; 文章目录【毕业设计】60-基于ZigBee无线智能消防\烟雾报警逃生系统设计&#xff08;原理图工程、源代码、低重复率参考文档、实物图&#x…

Mybatis的一级缓存

目录前置生效场景一场景二失效场景一场景二场景三场景四场景五前置 什么是一级缓存: mybatis 默认开启一级缓存, SQLSession会话缓存, 每个SQLSession都会有各自的缓存 以下会演示一级缓存生效/失效的场景 项目地址: https://gitee.com/xmaxm/test-code/blob/master/chaim-cac…

初识golang微服务框架kratos

前言 今天给大家介绍一下Kratos&#xff0c;Kratos 一套轻量级 Go 微服务框架&#xff0c;包含大量微服务相关框架及工具,使用Kratos的原因主要是感觉原来使用的go-kit工具并不是很方便&#xff0c;期望用上kratos后开发会更快捷一些。 Kratos名字根据官方的説法是来源于:《战…

灵界的科学丨六、星际通信新科技──寻找外星人

摘自李嗣涔教授《灵界的科学》 外星先进文明科技领先地球的关键&#xff0c; 是外星人掌握了意识的物理&#xff0c; 能够制造仿照天眼的仪器&#xff0c; 自由进出虚数空间遨游宇宙&#xff0c;同时创造出瞬间科技。 人类未来学习的典范&#xff0c;就在天上无数的外星先进…

数据结构--线性表之顺序表

1.线性表定义 线性表&#xff08;List&#xff09;&#xff1a;零个或多个数据元素的有限序列。 线性表的数据集合为{a1,a2,…,an}&#xff0c;假设每个元素的类型均为DataType。其中&#xff0c;除第一个元素a1外&#xff0c;每一个元素有且只有一个直接前驱元素&#xff0c…

第三十三篇 transition-group 列表过渡

上一篇内容讲到的是transiotion&#xff0c;其中还记得有一个报错吗&#xff1f;如下&#xff1a; 先来回顾一下&#xff0c;<transition> 只能用于单个元素&#xff0c;如果在<transition>单中并列两个<p>标签&#xff0c;那么这样一来就会报以上这个错误&a…

六、表空间管理

六、表空间管理 1、查看表空间 使用DM Manager工具&#xff1a; 代码&#xff1a; -- 1、查看表空间名 select tablespace_name FROM SYS.DBA_TABLESPACES;-- 2、查看表空间名、表空间对应的数据文件地址、状态 select tablespace_name,file_name,status FROM dba_data_files;…

软件定义汽车产业生态创新白皮书

1 什么是软件定义汽车 1.1 驱动因素 汽车“新四化”的发展需要软件的加持 据大众汽车公开披露信息&#xff0c;未来平均每辆普通汽车软件代码量超 1 亿行。在电动化、智能化和网联化等的发展推动下&#xff0c;汽车将加速向高度数字化、信息化、智能化的移动终端发展。座舱娱…

CM311-1_YST_S905L3(B)_安卓9.0_设置无密码_默认打开adb_完美AI语音_线刷固件包

CM311-1_YST_S905L3(B)_安卓9.0_设置无密码_默认打开adb_完美AI语音_线刷固件包 固件特点&#xff1a; 1、修改dns&#xff0c;三网通用&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、无开机广告&#xff0c;无系统更新&#xff0c;不在被强制升…

LX12864P1屏幕使用介绍(ST7567驱动),显示横线、字符、图形

LX12864P1屏幕显示&#xff08;ST7567驱动&#xff09; 可编辑12864液晶模组&#xff0c;也就是液晶显示屏是有128*64个点阵组成。12864是一种图形点阵液晶显示器&#xff0c;它主要采用动态驱动原理由行驱动—控制器和列驱动器两部分组成了128(列)64(行)的全点阵液晶显示此显…

全国地级市城镇化和协调发展指数测算数据(2005-2019)六份数据

全国地级市城镇化和协调发展指数测算数据&#xff08;2005-2019&#xff09;六份数据 1、范围&#xff1a;受数据限制&#xff0c;剔除了新疆和西藏的城市共包括287个地级市 2、时间区间为&#xff1a;2005-2019年 3、六分数据包括&#xff1a; 地级市城镇化发展水平&#…