RabbitMQ安装配置,封装工具类,发送消息及监听
1. Get-Started
docker安装rabbitmq
- 拉取镜像
[root@heima ~]# docker pull rabbitmq:3.8-management
3.8-management: Pulling from library/rabbitmq
7b1a6ab2e44d: Pull complete
37f453d83d8f: Pull complete
e64e769bc4fd: Pull complete
c288a913222f: Pull complete
13adc5da62c6: Pull complete
bd67e639afcb: Pull complete
9a48b5ad2519: Pull complete
1cdfc59624be: Pull complete
8f5ad79f0ad6: Pull complete
Digest: sha256:543f7268600a27a39e2fdd532f8df479636fc0cf528aadde88d5fe718bed71e4
Status: Downloaded newer image for rabbitmq:3.8-management
docker.io/library/rabbitmq:3.8-management
- 创建目录
mkdir -p /home/apps/rabbitmq/data
- 运行容器
docker run \
-d \
--name rabbitmq \
--restart=always \
--privileged=true \
-p 5672:5672 \
-p 15672:15672 \
-v /home/apps/rabbitmq/data:/var/lib/rabbitmq \
-e RABBITMQ_DEFAULT_VHOST=vhost0 \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin123 \
rabbitmq:3.8-management
- 启用web界面管理插件
docker exec -it rabbitmq rabbitmq-plugins enable rabbitmq_management
或
-- 进入容器
docker exec -it rabbitmq /bin/bash
-- 安装插件
rabbitmq-plugins enable rabbitmq_management
- 浏览器访问 http://虚拟机ip:15672/出现以下界面说明安装成功。

输入上面在初始化Rabbitmq容器时我们自己指定了默认账号和密码:admin/admin123,如果没有指定的话那么rabbitmq的默认账号密码是:guest/guest
扩展 :安装延迟队列
- 去官网下载插件(v3.8.17)
- 地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases
# 将下载好的插件复制到mq容器内部
docker cp rabbitmq_delayed_message_exchange-3.8.17.8f537ac.ez rabbitmq:/plugins# 进入mq容器
docker exec -it rabbitmq /bin/bash# 开启插件支持
rabbitmq-plugins enable rabbitmq_delayed_message_exchange# 查看插件列表
rabbitmq-plugins list
2. 生产者模块
2.1 项目引入依赖:
rabbitmq依赖包,使用RabbitMq这2个依赖就够了。
<dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-amqp</artifactId><scope>provided</scope>
</dependency>
<dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-rabbit</artifactId><scope>provided</scope>
</dependency>
封装工具类用到的包
<!--hutool工具包-->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.17</version>
</dependency>
2.2 工具类
- RabbitMqHelper
import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
public class RabbitMqHelper {public static final String REQUEST_ID_HEADER = "requestId";private final RabbitTemplate rabbitTemplate;private final MessagePostProcessor processor = new BasicIdMessageProcessor();private final ThreadPoolTaskExecutor executor;public RabbitMqHelper(RabbitTemplate rabbitTemplate) {this.rabbitTemplate = rabbitTemplate;executor = new ThreadPoolTaskExecutor();//配置核心线程数executor.setCorePoolSize(10);//配置最大线程数executor.setMaxPoolSize(15);//配置队列大小executor.setQueueCapacity(99999);//配置线程池中的线程的名称前缀executor.setThreadNamePrefix("mq-async-send-handler");// 设置拒绝策略:当pool已经达到max size的时候,如何处理新任务// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//执行初始化executor.initialize();}/*** 根据exchange和routingKey发送消息*/public <T> void send(String exchange, String routingKey, T t) {log.debug("准备发送消息,exchange:{}, RoutingKey:{}, message:{}", exchange, routingKey, t);// 1.设置消息标示,用于消息确认,消息发送失败直接抛出异常,交给调用者处理String id = UUID.randomUUID().toString(true);CorrelationData correlationData = new CorrelationData(id);// 2.设置发送超时时间为500毫秒rabbitTemplate.setReplyTimeout(500);// 3.发送消息,同时设置消息idrabbitTemplate.convertAndSend(exchange, routingKey, t, processor, correlationData);}/*** 根据exchange和routingKey发送消息,并且可以设置延迟时间*/public <T> void sendDelayMessage(String exchange, String routingKey, T t, Duration delay) {// 1.设置消息标示,用于消息确认,消息发送失败直接抛出异常,交给调用者处理String id = UUID.randomUUID().toString(true);CorrelationData correlationData = new CorrelationData(id);// 2.设置发送超时时间为500毫秒rabbitTemplate.setReplyTimeout(500);// 3.发送消息,同时设置消息idrabbitTemplate.convertAndSend(exchange, routingKey, t, new DelayedMessageProcessor(delay), correlationData);}/*** 根据exchange和routingKey 异步发送消息,并指定一个延迟时间** @param exchange 交换机* @param routingKey 路由KEY* @param t 数据* @param <T> 数据类型*/public <T> void sendAsync(String exchange, String routingKey, T t, Long time) {String requestId = MDC.get(REQUEST_ID_HEADER);CompletableFuture.runAsync(() -> {try {MDC.put(REQUEST_ID_HEADER, requestId);// 发送延迟消息if (time != null && time > 0) {sendDelayMessage(exchange, routingKey, t, Duration.ofMillis(time));} else {send(exchange, routingKey, t);}} catch (Exception e) {log.error("推送消息异常,t:{},", t, e);}}, executor);}/*** 根据exchange和routingKey 异步发送消息** @param exchange 交换机* @param routingKey 路由KEY* @param t 数据* @param <T> 数据类型*/public <T> void sendAsync(String exchange, String routingKey, T t) {sendAsync(exchange, routingKey, t, null);}
}
- MqConfig
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.MDC;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.ContainerCustomizer;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;@Configuration
@ConditionalOnClass(value = {MessageConverter.class, AmqpTemplate.class})
public class MqConfig implements EnvironmentAware{public static final String REQUEST_ID_HEADER = "requestId";public static final String ERROR_EXCHANGE = "error.topic";public static final String ERROR_KEY_PREFIX = "error.";public static final String ERROR_QUEUE_TEMPLATE = "error.{}.queue";private String defaultErrorRoutingKey;private String defaultErrorQueue;@Bean(name = "rabbitListenerContainerFactory")@ConditionalOnProperty(prefix = "spring.rabbitmq.listener", name = "type", havingValue = "simple",matchIfMissing = true)SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory,ObjectProvider<ContainerCustomizer<SimpleMessageListenerContainer>> simpleContainerCustomizer) {SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();configurer.configure(factory, connectionFactory);simpleContainerCustomizer.ifUnique(factory::setContainerCustomizer);factory.setAfterReceivePostProcessors(message -> {Object header = message.getMessageProperties().getHeader(REQUEST_ID_HEADER);if(header != null) {MDC.put(REQUEST_ID_HEADER, header.toString());}return message;});return factory;}@Beanpublic MessageConverter messageConverter(ObjectMapper mapper){// 1.定义消息转换器Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(mapper);// 2.配置自动创建消息id,用于识别不同消息jackson2JsonMessageConverter.setCreateMessageIds(true);return jackson2JsonMessageConverter;}/*** <h1>消息处理失败的重试策略</h1>* 本地重试失败后,消息投递到专门的失败交换机和失败消息队列:error.queue*/@Bean@ConditionalOnClass(MessageRecoverer.class)@ConditionalOnMissingBeanpublic MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){// 消息处理失败后,发送到错误交换机:error.direct,RoutingKey默认是error.微服务名称return new RepublishMessageRecoverer(rabbitTemplate, ERROR_EXCHANGE, defaultErrorRoutingKey);}/*** rabbitmq发送工具**/@Bean@ConditionalOnMissingBean@ConditionalOnClass(RabbitTemplate.class)public RabbitMqHelper rabbitMqHelper(RabbitTemplate rabbitTemplate){return new RabbitMqHelper(rabbitTemplate);}/*** 专门接收处理失败的消息*/@Beanpublic DirectExchange errorMessageExchange(){return new DirectExchange(ERROR_EXCHANGE);}@Beanpublic Queue errorQueue(){return new Queue(defaultErrorQueue, true);}@Beanpublic Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with(defaultErrorRoutingKey);}@Overridepublic void setEnvironment(Environment environment) {String appName = environment.getProperty("spring.application.name");this.defaultErrorRoutingKey = ERROR_KEY_PREFIX + appName;this.defaultErrorQueue = StrUtil.format(ERROR_QUEUE_TEMPLATE, appName);}
}
- Processor
import cn.hutool.core.lang.UUID;
import org.slf4j.MDC;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;public class BasicIdMessageProcessor implements MessagePostProcessor {public static final String REQUEST_ID_HEADER = "requestId";@Overridepublic Message postProcessMessage(Message message) throws AmqpException {String requestId = MDC.get(REQUEST_ID_HEADER);if (requestId == null) {requestId = UUID.randomUUID().toString(true);}// 写入RequestID标示message.getMessageProperties().setHeader(REQUEST_ID_HEADER, requestId);return message;}
}----------------------------------------------------------------------import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import java.time.Duration;public class DelayedMessageProcessor extends BasicIdMessageProcessor {private final long delay;public DelayedMessageProcessor(Duration delay) {this.delay = delay.toMillis();}@Overridepublic Message postProcessMessage(Message message) throws AmqpException {// 1.添加消息idsuper.postProcessMessage(message);// 2.添加延迟时间message.getMessageProperties().setHeader("x-delay", delay);return message;}
}
2.3 配置
通常mq是放在common模块中,别的模块需要mq时就引入该模块,因此需要把MqConfig放在IOC容器里加载。
- resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.tianji.common.autoconfigure.mq.MqConfig
配置rabbitmq的地址等信息
- boostrap.yml
spring:rabbitmq:host: ${mydemo.mq.host:192.168.150.101}port: ${mydemo.mq.port:5672}virtual-host: ${mydemo.mq.vhost:/vhost0}username: ${mydemo.mq.username:admin}password: ${mydemo.mq.password:admin123}listener:simple:retry:enabled: ${mydemo.mq.listener.retry.enable:true} # 开启消费者失败重试initial-interval: ${mydemo.mq.listener.retry.interval:1000ms} # 初始的失败等待时长为1秒multiplier: ${mydemo.mq.listener.retry.multiplier:2} # 失败的等待时长倍数,下次等待时长 = multiplier * last-intervalmax-attempts: ${mydemo.mq.listener.retry.max-attempts:3} # 最大重试次数stateless: ${mydemo.mq.listener.retry.stateless:true} # true无状态;false有状态。如果业务中包含事务,这里改为false
2.4 测试文件
TestController
@RestController
public class TestController {@AutowiredRabbitMqHelper rabbitMqHelper;@GetMapping("/hello")public String hello(){return "hello";}@GetMapping("/sendMsg")public String sendMsg(){rabbitMqHelper.send("order.topic", // "order.topic""order.pay", // "order.pay"UserDto.builder().id(10001).name("gz").age(23).phone("15500000001").email("123@qq.com").build());return "success";}
}
UserDto
import lombok.Builder;
import lombok.Data;@Data
@Builder
public class UserDto {private Integer id;private String name;private String phone;private Integer age;private String email;
}
2.5 生产者测试
生产者模块目录结构

启动生产者服务,访问http://127.0.0.1:8080/sendMsg
报错了,原因是没有消费者消费消息
但是发现创建了新的错误消息exchange和queue

因此,接下来创建消费者监听消息。
3. 消费者
3.1 引入通用模块
由于消费者也需要使用刚刚的工具类和UserDto用来接收消息。

而这些工具类和UserDto都在模块rabbitmq-demo中。因此,在消费者模块中引入该生产者模块(如果这些通用的配置和实体类dto都在一个通用的模块common中,哪些模块需要发送消息,哪些模块需要监听消息,就都引入common模块就行了)
<groupId>com.gzdemo</groupId><artifactId>rabbitmq-demo</artifactId><version>1.0-SNAPSHOT</version>
因为rabbitmq-demo模块中已经引入了RabbitMq相关依赖,因此消费者模块不需要重复引入RabbitMq相关的依赖。只需要把依赖包添加到classpath下,再import就行了


3.2 配置yml
与生产者的配置相同,通常只有消费者才需要配置listener,生产者不需要。

3.3 编写listener
import com.gzdemo.rabbitmq.pojos.UserDto;import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;@Component
public class UserListener {/*** 监听消息*/@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "learning.lesson.pay.queue", durable = "true"),exchange = @Exchange(name = "order.topic", type = ExchangeTypes.TOPIC),key = "order.pay"))public void listenLessonPay(UserDto userDto){System.out.println(userDto);}
}
3.4 消费者目录结构
这里是随便找了个同工程中的子模块作为消费者测试,在UserListener类中监听消息

4 测试消息发送、监听
启动这两个服务,浏览器访问http://127.0.0.1:8080/sendMsg,多访问几次后发现消费者接收到了消息
生产者console
2024-06-14 05:46:39.187 INFO 106168 --- [nio-8080-exec-1] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [192.168.150.101:5672]
2024-06-14 05:46:39.210 INFO 106168 --- [nio-8080-exec-1] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#399d82:0/SimpleConnection@1673b17 [delegate=amqp://admin@192.168.150.101:5672/vhost0, localPort= 61317]
相关文章:
RabbitMQ安装配置,封装工具类,发送消息及监听
1. Get-Started docker安装rabbitmq 拉取镜像 [rootheima ~]# docker pull rabbitmq:3.8-management 3.8-management: Pulling from library/rabbitmq 7b1a6ab2e44d: Pull complete 37f453d83d8f: Pull complete e64e769bc4fd: Pull complete c288a913222f: Pull complet…...
iOS接入Flutter
在现有的iOS项目上接入Flutter,参考链接 第一步:创建flutter项目,即 创建 Flutter module flutter create --template module my_flutter第二步:创建framework,这里选择的是B方式,即 选项 B - 在 Xcode 中…...
【ubuntu】用户添加root权限
添加root用户添加新用户并赋予权限 文件只读,无法更改 rootubuntu-server:/home/ubuntu# vi /etc/sudoers rootubuntu-server:/home/ubuntu# vi /etc/sudoers rootubuntu-server:/home/ubuntu# chmod -R 777 /etc/sudoers rootubuntu-server:/home/ubuntu# vi /et…...
设计通用灵活的LabVIEW自动测试系统
为了在不同客户案例中灵活使用不同设备(如采集卡、Modbus模块)且保持功能一致的LabVIEW自动测试系统,需要采用模块化的软件架构、配置文件管理、标准化接口和良好的升级维护策略。本文从软件架构、模块化设计、配置管理、升级维护、代码管理和…...
C# WinForm —— 35 StatusStrip 介绍
1. 简介 状态栏 StatusStrip,默认在软件的最下方,用于显示系统时间、版本、进度条、账号、角色信息、操作位置信息等 可以在状态栏中添加的控件类型有:StatusLabel、ProgressBar、DropDownButton、SplitButton 2. 属性 属性解释(Name)控…...
如何应对生活中的不确定性:仁者安仁,知者利仁。
有较高自尊水平的人,接近于孔子说的:仁者。 ——— 有着稳定的高自尊,无论外在环境如何变化,对其影响都不大,他能够愉快地生活。 相反:一个人处于低自尊状态,就会活得很痛苦,对自己…...
C#面:请解释C#接口的显式实现有什么意义
C#接口的显式实现是指在实现接口成员时,使用接口名称进行限定的方式。这种方式可以在一个类中实现多个接口,并且可以避免接口成员之间的命名冲突。显式实现接口的成员只能通过接口类型来访问,而不能通过类的实例来访问。 显式实现接口的主要…...
STM32项目分享:智能窗帘系统
目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板打样焊接图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片: 哔哩哔哩视频链接: https://www.bilibili.c…...
【算法-力扣】72. 编辑距离(动态规划)
目录 一、题目描述 二、解题思路 三、参考答案 一、题目描述 编辑距离 给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作: 插入一个字符 删除一个字符 替换一个字符 示例 1&#…...
Spring 系统架构图
Spring 系统架构图 Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基。 Spring Framework的发展也经历了很多版本的变更,每个版本都有相应的调整 Spring Framework的5版本目前没有最新的架构图,而最新的是4版本,…...
同三维T80005EHS-4K60 4K60 HDMI/SDI编码器
1路4K60 HDMI或12G SDI输入,2路3.5MM音频输入,对应HDMI或SDI,1个USB口和1个SD卡槽,可录像到U盘/移动硬盘/SSD硬盘/TF卡 产品简介: 同三维T80005EHS-4K60 4K60HDMI/SDI H.265编码器采用最新高效H.265高清数字视频压缩…...
React state(及组件) 的保留与重置
当在树中相同的位置渲染相同的组件时,React 会一直保留着组件的 state return (<div><Counter />{showB && <Counter />} </div> ) // 当 showB 为 false, 第二个计数器停止渲染,它的 state 完全消失了。这是因为 React…...
flask返回的数据怎么是转义后的字符串啊
Flask在返回JSON数据时,默认情况下会对特殊字符进行转义,以确保数据能安全地在HTML页面中展示,避免XSS(跨站脚本攻击)等安全问题。如果不希望Flask对JSON响应中的字符串自动转义,通常是因为你希望在前端直接使用这些数据(例如作为JavaScript的一部分),那么需要确保数据…...
C++17并行算法与HIPSTDPAR
C17 parallel algorithms and HIPSTDPAR — ROCm Blogs (amd.com) C17标准在原有的C标准库中引入了并行算法的概念。像std::transform这样的并行版本算法保持了与常规串行版本相同的签名,只是增加了一个额外的参数来指定使用的执行策略。这种灵活性使得已经使用C标准…...
【什么是几度cms,主要功能有什么】
几度CMS内容管理框架是基于 PHP 语言采用最新 Thinkphp 作为开发框架生产的网站 内容管理框架,提供“电脑网站 手机网站 多终端 APP 接口”一体化网站技术解 决方案。她拥有强大稳定底层框架,以灵活扩展为主的开发理念,二次开发方便且…...
组合和外观模式
文章目录 组合模式1.引出组合模式1.院系展示需求2.组合模式基本介绍3.组合模式原理类图4.解决的问题 2.组合模式解决院系展示1.类图2.代码实现1.AbsOrganizationComponent.java 总体抽象类用于存储信息和定义方法2.University.java 第一层,University 可以管理 Coll…...
设置服务器禁止和ip通信
要禁止服务器与特定 IP 地址的通信,可以使用防火墙来设置规则。在 Ubuntu 上,iptables 是一个常用的防火墙工具。以下是使用 iptables 设置禁止与特定 IP 通信的步骤: 阻止所有进出的通信 如果你想阻止服务器与特定 IP 地址的所有通信&…...
中文技术文档的写作规范(搬运)
阮一峰老师的《中文技术文档的写作规范》搬运。 链接指路: https://github.com/ruanyf/document-style-guide/tree/master 内容:对中文技术文档从标题、文本、段落、数值、标点符号、文档体系、参考链接等七大方面进行了简明扼要的介绍。...
「实战应用」如何用DHTMLX将上下文菜单集成到JavaScript甘特图中(一)
DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的所有需求,是最完善的甘特图图表库。 DHTMLX Gantt是一个高度可定制的工具,可以与项目管理应用程序所需的其他功能相补充。在本文中您将学习如何使用自定义上…...
Python使用策略模式生成TCP数据包
使用策略模式(Strategy Pattern)来灵活地生成不同类型的TCP数据包。 包括三次握手、数据传输和四次挥手。 from scapy.all import * from scapy.all import Ether, IP, TCP, UDP, wrpcap from abc import ABC, abstractmethodclass TcpPacketStrategy(A…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
