RabbitMQ之发布确认

news2025/1/12 6:44:41

目录

8.1. 发布确认 springboot 版本

8.1.1. 确认机制方案

8.1.2. 代码架构图

8.1.3. 配置文件

8.1.4. 添加配置类

8.1.5. 消息生产者

8.1.6. 回调接口

8.1.7. 消息消费者

8.1.8. 结果分析

8.2. 回退消息

8.2.1. Mandatory 参数

8.2.2. 消息生产者代码

8.2.3. 回调接口

8.2.4. 结果分析

8.3. 备份交换机

8.3.1. 代码架构图

8.3.2. 修改配置类

8.3.3. 报警消费者

8.3.4. 测试注意事项

8.3.5. 结果分析


在生产环境中由于一些不明原因,导致 rabbitmq 重启,在 RabbitMQ 重启期间生产者消息投递失败,

导致消息丢失,需要手动处理和恢复。于是,我们开始思考,如何才能进行 RabbitMQ 的消息可靠投递呢?

特别是在这样比较极端的情况,RabbitMQ 集群不可用的时候,无法投递的消息该如何处理呢

8.1. 发布确认 springboot 版本

8.1.1. 确认机制方案

8.1.2. 代码架构图

8.1.3. 配置文件

在配置文件当中需要添加

spring.rabbitmq.publisher-confirm-type=correlated

⚫ NONE

禁用发布确认模式,是默认值

⚫ CORRELATED

发布消息成功到交换器后会触发回调方法

⚫ SIMPLE

经测试有两种效果,其一效果和 CORRELATED 值一样会触发回调方法,

其二在发布消息成功后使用 rabbitTemplate 调用 waitForConfirms 或 waitForConfirmsOrDie 方法

等待 broker 节点返回发送结果,根据返回结果来判定下一步的逻辑,要注意的点是

waitForConfirmsOrDie 方法如果返回 false 则会关闭 channel,则接下来无法发送消息到 broker

8.1.4. 添加配置类

package com.xingchen.mq.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

/**
 * @author xingchen
 * @version V1.0
 *  * 发布确认兜底方案  添加缓存测试
 * @Package com.xingchen.mq.config
 * @date 2022/12/7 11:52
 */
@Configuration
public class ConfirmConfig {

    //交换机
    private static final String CONFIRM_EXCHANGE_NAME = "confirm-exchange";
    //队列

    private static final String CONFIRM_QUEUE_NAME = "confirm-queue";
    //ROUTING_KEY

    private static final String CONFIRM_ROUTING_KEY = "confirm-key";

    /**
     * 备份交换机
     */
    private static final String BACKUP_EXCHANGE_NAME = "backup-exchange";
    /**
     * 备份队列
     */
    private static final String BACKUP_QUEUE_NAME = "backup-queue";
    /**
     * 报警队列
     */
    private static final String WARNING_QUEUE_NAME = "warning-queue";

    @Bean("confirmExchange")
    public DirectExchange directExchange() {
        /**确认交换机配置备份交换机 以确保宕机后将消息转发到备份交换机*/
        return ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME).durable(true)
                .withArgument("alternate-exchange", BACKUP_EXCHANGE_NAME).build();
    }

    @Bean("backupExchange")
    public FanoutExchange backupExchange() {
        return new FanoutExchange(BACKUP_EXCHANGE_NAME);
    }

    @Bean("confirmQueue")
    public Queue confirmQueue() {
        HashMap<String, Object> map = new HashMap<>(8);
        return new Queue(CONFIRM_QUEUE_NAME, false, false, false, map);
    }

    @Bean("backupQueue")
    public Queue backupQueue() {
        HashMap<String, Object> map = new HashMap<>(8);
        return new Queue(BACKUP_QUEUE_NAME, false, false, false, map);
    }

    @Bean("warningQueue")
    public Queue warningQueue() {
        HashMap<String, Object> map = new HashMap<>(8);
        return new Queue(WARNING_QUEUE_NAME, false, false, false, map);
    }

    @Bean
    public Binding queueConfirmBindingExchange(@Qualifier("confirmQueue") Queue queue,
                                               @Qualifier("confirmExchange") Exchange exchange) {

        return BindingBuilder.bind(queue).to(exchange).with(CONFIRM_ROUTING_KEY).noargs();
    }

    @Bean
    public Binding backupConfirmBindingExchange(@Qualifier("backupQueue") Queue queue,
                                                @Qualifier("backupExchange") FanoutExchange exchange) {

        return BindingBuilder.bind(queue).to(exchange);
    }

    @Bean
    public Binding warningConfirmBindingExchange(@Qualifier("warningQueue") Queue queue,
                                                 @Qualifier("backupExchange") FanoutExchange exchange) {

        return BindingBuilder.bind(queue).to(exchange);
    }
}

8.1.5. 消息生产者

package com.xingchen.mq.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
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;

import javax.annotation.Resource;

/**
 * @author xingchen
 * @version V1.0
 * @Package com.xingchen.mq.config
 * @date 2022/12/7 11:52
 */
@Slf4j
@RestController
@RequestMapping("/confirm")
public class ConfirmController {

    @Resource
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/sendConfirm/{msg}")
    public void sendConfirmMessage(@PathVariable("msg") String msg) {
        /**声明回调的形参*/

        CorrelationData correlationData = new CorrelationData("1");
        rabbitTemplate.convertAndSend("confirm-exchange", "confirm-key", msg, correlationData);
        log.info("发送信息为:" + msg);
    }
}

8.1.6. 回调接口

package com.xingchen.mq.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * @author xingchen
 * @version V1.0
 * @Package com.xingchen.mq.config
 * @date 2022/12/7 11:52
 */
@Component
@Slf4j
public class MyCallBack implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {

    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 该注解会在其他注解执行完毕之后,进行一个属性的注入,必须将该类注入到rabbitTemplate的内部类中
     * 内部类就是这个ConfirmCallback
     */
    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(this);
        /**同时需要注入队列回退接口*/
        rabbitTemplate.setReturnCallback(this);
    }

    /**
     * @param correlationData 包含了消息的ID和其他数据信息 这个需要在发送方创建,否则没有
     * @param ack             返回的一个交换机确认状态 true 为确认 false 为未确认
     * @param cause           未确认的一个原因,如果ack为true的话,此值为null
     */
    @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 为{}", cause, id);
        }
    }
    /**
     * 可以在消息传递过程中,如果交换机遇到不可路由的情况,会将消息返回给生产者
     *
     * @param message    消息
     * @param replyCode  回复状态码
     * @param replyText  退回原因
     * @param exchange   交换机
     * @param routingKey 路由Key
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.error("消息{},被交换机{}退回,路由Key是{},退回原因是{}", new String(message.getBody()), exchange
                , routingKey, replyText);
    }
}

8.1.7. 消息消费者

package com.xingchen.mq.consumer;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @author xingchen
 * @version V1.0
 * @Package com.xingchen.mq.config
 * @date 2022/12/7 11:52
 */
@Component
@Slf4j
public class ConfirmConsumer {
    @RabbitListener(queues = {"confirm-queue"})
    public void receiveMsg(Message message) {
        log.info("接收到的消息为: " + new String(message.getBody()));
    }
}

8.1.8. 结果分析

可以看到,发送了两条消息,第一条消息的 RoutingKey 为 "key1",第二条消息的 RoutingKey 为

"key2",两条消息都成功被交换机接收,也收到了交换机的确认回调,但消费者只收到了一条消息,因为

第二条消息的 RoutingKey 与队列的 BindingKey 不一致,也没有其它队列能接收这个消息,所有第二条

消息被直接丢弃了。

8.2. 回退消息

8.2.1. Mandatory 参数

在仅开启了生产者确认机制的情况下,交换机接收到消息后,会直接给消息生产者发送确认消息

果发现该消息不可路由,那么消息会被直接丢弃,此时生产者是不知道消息被丢弃这个事件的。那么如何

让无法被路由的消息帮我想办法处理一下?最起码通知我一声,我好自己处理啊。通过设置 mandatory 参

数可以在当消息传递过程中不可达目的地时将消息返回给生产者。

8.2.2. 消息生产者代码

package com.xingchen.mq.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * @author xingchen
 * @version V1.0
 * @Package com.xingchen.mq.config
 * @date 2022/12/7 11:52
 */
@Component
@Slf4j
public class MyCallBack implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {

    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 该注解会在其他注解执行完毕之后,进行一个属性的注入,必须将该类注入到rabbitTemplate的内部类中
     * 内部类就是这个ConfirmCallback
     */
    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(this);
        /**同时需要注入队列回退接口*/
        rabbitTemplate.setReturnCallback(this);
    }

    /**
     * @param correlationData 包含了消息的ID和其他数据信息 这个需要在发送方创建,否则没有
     * @param ack             返回的一个交换机确认状态 true 为确认 false 为未确认
     * @param cause           未确认的一个原因,如果ack为true的话,此值为null
     */
    @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 为{}", cause, id);
        }
    }

    /**
     * 可以在消息传递过程中,如果交换机遇到不可路由的情况,会将消息返回给生产者
     *
     * @param message    消息
     * @param replyCode  回复状态码
     * @param replyText  退回原因
     * @param exchange   交换机
     * @param routingKey 路由Key
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.error("消息{},被交换机{}退回,路由Key是{},退回原因是{}", new String(message.getBody()), exchange
                , routingKey, replyText);
    }
}

8.2.3. 回调接口

package com.xingchen.mq.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * @author xingchen
 * @version V1.0
 * @Package com.xingchen.mq.config
 * @date 2022/12/7 11:52
 */
@Component
@Slf4j
public class MyCallBack implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {

    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 该注解会在其他注解执行完毕之后,进行一个属性的注入,必须将该类注入到rabbitTemplate的内部类中
     * 内部类就是这个ConfirmCallback
     */
    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(this);
        /**同时需要注入队列回退接口*/
        rabbitTemplate.setReturnCallback(this);
    }

    /**
     * @param correlationData 包含了消息的ID和其他数据信息 这个需要在发送方创建,否则没有
     * @param ack             返回的一个交换机确认状态 true 为确认 false 为未确认
     * @param cause           未确认的一个原因,如果ack为true的话,此值为null
     */
    @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 为{}", cause, id);
        }
    }

    /**
     * 可以在消息传递过程中,如果交换机遇到不可路由的情况,会将消息返回给生产者
     *
     * @param message    消息
     * @param replyCode  回复状态码
     * @param replyText  退回原因
     * @param exchange   交换机
     * @param routingKey 路由Key
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.error("消息{},被交换机{}退回,路由Key是{},退回原因是{}", new String(message.getBody()), exchange
                , routingKey, replyText);
    }
}

8.2.4. 结果分析

8.3. 备份交换机

有了 mandatory 参数和回退消息,我们获得了对无法投递消息的感知能力,有机会在生产者的消息

无法被投递时发现并处理。但有时候,我们并不知道该如何处理这些无法路由的消息,最多打个日志,然

后触发报警,再来手动处理。而通过日志来处理这些无法路由的消息是很不优雅的做法,特别是当生产者

所在的服务有多台机器的时候,手动复制日志会更加麻烦而且容易出错。而且设置 mandatory 参数会增

加生产者的复杂性,需要添加处理这些被退回的消息的逻辑。如果既不想丢失消息,又不想增加生产者的

复杂性,该怎么做呢?前面在设置死信队列的文章中,我们提到,可以为队列设置死信交换机来存储那些

处理失败的消息,可是这些不可路由消息根本没有机会进入到队列,因此无法使用死信队列来保存消息。

在 RabbitMQ 中,有一种备份交换机的机制存在,可以很好的应对这个问题。什么是备份交换机呢?备份

交换机可以理解为 RabbitMQ 中交换机的“备胎”,当我们为某一个交换机声明一个对应的备份交换机时,就

是为它创建一个备胎,当交换机接收到一条不可路由消息时,将会把这条消息转发到备份交换机中,由备

份交换机来进行转发和处理,通常备份交换机的类型为 Fanout ,这样就能把所有消息都投递到与其绑定

的队列中,然后我们在备份交换机下绑定一个队列,这样所有那些原交换机无法被路由的消息,就会都进

入这个队列了。当然,我们还可以建立一个报警队列,用独立的消费者来进行监测和报警。

8.3.1. 代码架构图

8.3.2. 修改配置类

package com.xingchen.mq.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

/**
 * @author xingchen
 * @version V1.0
 *  * 发布确认兜底方案  添加缓存测试
 * @Package com.xingchen.mq.config
 * @date 2022/12/7 11:52
 */
@Configuration
public class ConfirmConfig {

    //交换机
    private static final String CONFIRM_EXCHANGE_NAME = "confirm-exchange";
    //队列

    private static final String CONFIRM_QUEUE_NAME = "confirm-queue";
    //ROUTING_KEY

    private static final String CONFIRM_ROUTING_KEY = "confirm-key";

    /**
     * 备份交换机
     */
    private static final String BACKUP_EXCHANGE_NAME = "backup-exchange";
    /**
     * 备份队列
     */
    private static final String BACKUP_QUEUE_NAME = "backup-queue";
    /**
     * 报警队列
     */
    private static final String WARNING_QUEUE_NAME = "warning-queue";

    @Bean("confirmExchange")
    public DirectExchange directExchange() {
        /**确认交换机配置备份交换机 以确保宕机后将消息转发到备份交换机*/
        return ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME).durable(true)
                .withArgument("alternate-exchange", BACKUP_EXCHANGE_NAME).build();
    }

    @Bean("backupExchange")
    public FanoutExchange backupExchange() {
        return new FanoutExchange(BACKUP_EXCHANGE_NAME);
    }

    @Bean("confirmQueue")
    public Queue confirmQueue() {
        HashMap<String, Object> map = new HashMap<>(8);
        return new Queue(CONFIRM_QUEUE_NAME, false, false, false, map);
    }

    @Bean("backupQueue")
    public Queue backupQueue() {
        HashMap<String, Object> map = new HashMap<>(8);
        return new Queue(BACKUP_QUEUE_NAME, false, false, false, map);
    }

    @Bean("warningQueue")
    public Queue warningQueue() {
        HashMap<String, Object> map = new HashMap<>(8);
        return new Queue(WARNING_QUEUE_NAME, false, false, false, map);
    }

    @Bean
    public Binding queueConfirmBindingExchange(@Qualifier("confirmQueue") Queue queue,
                                               @Qualifier("confirmExchange") Exchange exchange) {

        return BindingBuilder.bind(queue).to(exchange).with(CONFIRM_ROUTING_KEY).noargs();
    }

    @Bean
    public Binding backupConfirmBindingExchange(@Qualifier("backupQueue") Queue queue,
                                                @Qualifier("backupExchange") FanoutExchange exchange) {

        return BindingBuilder.bind(queue).to(exchange);
    }

    @Bean
    public Binding warningConfirmBindingExchange(@Qualifier("warningQueue") Queue queue,
                                                 @Qualifier("backupExchange") FanoutExchange exchange) {

        return BindingBuilder.bind(queue).to(exchange);
    }
}

8.3.3. 报警消费者

package com.xingchen.mq.consumer;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @author xingchen
 * @version V1.0
 * @Package com.xingchen.mq.config
 * @date 2022/12/7 11:52
 */
@Component
@Slf4j
public class WarningConsumer {
    @RabbitListener(queues = {"warning-queue"})
    public void receiveWarningMsg(Message message) {
        log.warn("出现不可路由消息:", message);
    }
}

8.3.4. 测试注意事项

重新启动项目的时候需要把原来的confirm.exchange 删除因为我们修改了其绑定属性,不然报以下错:

8.3.5. 结果分析

mandatory 参数与备份交换机可以一起使用的时候,如果两者同时开启,消息究竟何去何从?谁优先

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

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

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

相关文章

全国青少年软件编程(Scratch)等级考试二级考试真题2022年3月——持续更新.....

电子学会202203Scratch二级真题及参考答案 1.红框中加入哪个选项积木,不能阻止气球下落?( ) A. B. C. D. 正确答案:D 答案解析: 2.运行下列程序,会画出?( ) A.一条横的实线<

flex布局父项属性

flex布局父项属性 1、flex-direction设置主轴方向 源代码 主轴与侧轴在flex布局中&#xff0c;是分为主轴和侧轴两个方向&#xff0c;同样的叫法有&#xff1a;行和列、x轴和y轴 默认主轴方向就是x轴方向&#xff0c;水平向右 默认侧轴方向就是y轴方向&#xff0c;水平向下 属…

字节京东互联网大厂为什么使用 clickhouse

ClickHouse 开源于 2016 年&#xff0c;是一款开源列式存储的分析型数据库&#xff0c;相较业界 OLAP 数据库系统&#xff0c;其最核心优势就是极致的查询性能。它实现了向量化执行和 SIMD 指令&#xff0c;对内存中的列式数据&#xff0c;一个 batch 调用一次 SIMD 指令&#…

Android-实现View模糊效果

首先&#xff0c;关于在Android上绘图的一句话 在最基本的层面上&#xff0c;Android 上的可视元素&#xff08;如按钮、文本和其他 UI 或自定义元素&#xff09;通常是通过调用 Canvas API 绘制的&#xff0c;如 drawLine()&#xff08;&#xff09;、drawText&#xff08;&a…

从《100道光芒》到《可爱冠军的诞生》:湖南广电打响“参与式”电商突围战

每年的双11&#xff0c;都是各电商平台秀肌肉的比拼时刻。 在这场看不见硝烟的战场上&#xff0c;已经逐步分化成两大阵营&#xff1a;阿里、京东为首的货架电商&#xff0c;抖音为主的兴趣电商。在“人货场”偏向“人”的形势下&#xff0c;它们无一例外都在全力布局吸引“人…

Apache APISIX 玩转 Tongsuo 国密插件

文&#xff5c;罗泽轩Apache APISIX PMC本文通过解读国密的相关内容与标准&#xff0c;呈现了当下国内技术环境中对于国密功能支持的现状。并从 API 网关 Apache APISIX 的角度&#xff0c;带来有关国密的探索与功能呈现。本文 3446 字 阅读 11 分钟1. 什么是国密 顾名思义&…

元素的显示与隐藏

元素的显示与隐藏 本质&#xff1a;让一个元素在页面中隐藏或者显示出来 1、display属性 源代码 display属性用于设置一个元素应如何显示 display: none; 隐藏对象 display: block; block除了转换为块元素之外&#xff0c…

软件测试基础理论体系学习5-静态测试的理解

5-静态测试的理解1 介绍2 静态测试技术2.1 代码检查2.1.1 代码走查2.1.2 编码风格与规范2.1.3 审查2.1.3.1 代码审查和代码走查2.1.3.2 代码审查清单2.2 静态结构分析2.3 代码质量度量1 介绍 静态测试包括包括代码检查、静态结构分析、代码质量度量等。它可以由人工进行&#…

友芝友生物冲刺港交所上市:极其依赖单一供应商,周鹏飞为董事长

12月9日&#xff0c;Wuhan YZY Biopharma Co.,LTD.&#xff08;武汉友芝友生物制药股份有限公司&#xff0c;下称“友芝友生物”&#xff09;向港交所提交上市申请材料&#xff0c;计划在港交所主板上市&#xff0c;中信建投国际为其独家保荐人。 此前&#xff0c;贝多财经曾在…

Spark零基础入门实战(六)使用IntelliJ IDEA创建Scala项目

IntelliJ IDEA(简称IDEA)是一款支持Java、Scala和Groovy等语言的开发工具,主要用于企业应用、移动应用和Web应用的开发。IDEA在业界被公认为是很好的Java开发工具,尤其是智能代码助手、代码自动提示、重构、J2EE支持等功能非常强大。 在IDEA中安装Scala插件 在IDEA中安装…

【Keras计算机视觉OCR】文字识别算法中DenseNet、LSTM、CTC、Attention的讲解(图文解释 超详细)

觉得有帮助麻烦点赞关注收藏~~~ 一、OCR文字识别的概念 利用计算机自动识别字符的技术&#xff0c;是模式识别应用的一个重要领域。人们在生产和生活中&#xff0c;要处理大量的文字、报表和文本。为了减轻人们的劳动&#xff0c;提高处理效率&#xff0c;从上世纪50年代起就开…

GPR学术报告学习-刘四新 主持

钱荣毅 道路空洞 1 设备 天线矩阵 不能完全覆盖调查区域&#xff0c; 存在差异&#xff1a; 难以发现动态发育特征&#xff0c; 数据采集难以实现3D 全覆盖&#xff0c; 高频上探测深度浅&#xff0c; 通道间存在差异。 数据需要校准。。。主要A-SCAN的地面抖动矫正。 异常区…

【EmonCMS】开源数据处理平台安装教程

EmonCMS 安装教程1.EmonCMS平台介绍&#xff1a;2. EmonCMS安装2.1在 Ubuntu中安装2.2.在树莓派中安装3.安装完毕Emoncms是作为该项目的一部分开发的开源web应用程序&#xff0c;用于处理、记录和可视化能源、温度和其他环境数据。Emoncms从OpenEnergyMonitor监控硬件接收数据&…

VS Code 上已有200万+ Java 开发者!

大家好&#xff0c;我们很高兴与大家分享一个好消息&#xff0c;现在 Visual Studio Code 上已有超过200万 Java 开发者&#xff0c;这要来自于长期社区以及用户的支持&#xff0c;所以谢谢你们&#xff01; 对于11月的更新&#xff0c;我们为您带来了全新的代码编辑功能&…

运行 Jmeter 文件生成 HTML 测试报告,我选择 ANT 工具

概述 ant 是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具&#xff0c;大多用于 Java 环境中的软件开发。 在与 Jmeter 生成的 jmx 文件配合使用中&#xff0c;ant 会完成jmx计划的执行和生成jtl文件&#xff0c;并将jtl文件转化为html页面进行查看。 还可…

类加载器和反射

1.通过全限定名&#xff1a;包名类名&#xff08;类名可能会重复&#xff0c;所以要加包名&#xff09;&#xff0c;然后用流拷贝到内存中&#xff0c;并在内存中创建一个class对象用来存储这个class文件的成员信息 类加载过程http://t.csdn.cn/FDnaa 在验证之前会把该类中引…

CRM的开发

开发步骤1.首页的开发2.由首页跳转到登陆页面3.用户登录4.记住密码实现5.安全退出6.登录验证7.创建市场活动8.分页查询市场活动9.删除市场活动10.修改市场活动11.批量导出市场活动12.导入市场活动13.查看市场活动明细14.添加市场活动备注15.删除市场活动备注16.修改市场活动备注…

分分钟搭建出nginx的https服务

nginx添加https协议一、https是什么&#xff1f;二、创建步骤1.安装nginx2.创建证书三、验证四、浏览器访问在windows安装证书总结nginx 192.168.122.50一、https是什么&#xff1f; HTTPS &#xff08;全称&#xff1a;Hypertext Transfer Protocol Secure &#xff09;&…

分布式文件存储系统Minio,tuling,fox,p3 5:27

MinIO | 高性能&#xff0c;对Kubernetes友好的对象存储 一 分布式文件系统应用场景 fastDFS 缺点&#xff1a; 部署麻烦。因为文件名是自动生成的&#xff0c;所以如果要处理文件的话&#xff0c;还要经过额外的编码。Minio介绍 应用场景 互联网海量非结构化数据的存储需求…

Spring常见注解

普通CURD框架结构 1、controller 控制器&#xff08;注入服务&#xff09;用于标注控制层&#xff0c;相当于struts中的action层 2、service 服务&#xff08;注入dao&#xff09;用于标注服务层&#xff0c;主要用来进行业务的逻辑处理 3、repository/Mapper&#xff08;实…