Spring Boot 整合Redis实现消息队列

news/2024/3/29 6:03:13/文章来源:https://blog.csdn.net/Alian_1223/article/details/129188701

目录

    • 一、简介
    • 二、maven依赖
    • 三、编码实现
      • 3.1、配置文件
      • 3.2、配置类
      • 3.3、监听器
      • 3.4、消费服务
      • 3.5、实体
    • 四、验证
    • 五、优化
      • 5.1、注册任务执行器
      • 5.2、配置任务执行器
      • 5.3、启用异步执行器

一、简介

  本篇文章主要来讲Spring Boot 整合Redis实现消息队列,实现redis用作消息队列有多种方式,比如:

  • 基于List rpush+lpop lpush+rpop
  • 基于List rpush+blpop lpush+brpop (阻塞式获取消息)
  • 基于Sorted Set 的优先级队列
  • Redis Stream (Redis5.0版本开始)
  • Pub/Sub 机制

  不过这里讲的是Pub/Sub 机制的,这种方式优缺点大致如下:

优点:

  • 一个消息可以发布到多个消费者
  • 消费者可以同时订阅多个信道,因此可以接收多种消息(处理时先根据信道判断)
  • 消息即时发送,消费者会自动接收到信道发布的消息

缺点:

  • 消息发布时,如果客户端不在线,则消息丢失
  • 消费者处理消息时出现了大量消息积压,则可能会断开通道,导致消息丢失
  • 消费者接收消息的时间不一定是一致的,可能会有差异(业务处理需要判重)

二、maven依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.alian</groupId><artifactId>redis-message-queue</artifactId><version>0.0.1-SNAPSHOT</version><name>redis-message-queue</name><description>redis-message-queue</description><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><project.package.directory>target</project.package.directory><java.version>1.8</java.version><!--com.fasterxml.jackson 版本--><jackson.version>2.9.10</jackson.version><!--lombok 版本--><lombok.version>1.16.14</lombok.version><!--阿里巴巴fastjson 版本--><fastjson.version>1.2.68</fastjson.version><!--junit 版本--><junit.version>4.12</junit.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--用于序列化--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>${jackson.version}</version></dependency><!--java 8时间序列化--><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>${jackson.version}</version></dependency><!--JSON--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><!--日志输出--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

三、编码实现

3.1、配置文件

application.properties

# 端口
server.port=8090
# 上下文路径
server.servlet.context-path=/redisQueue# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
#spring.redis.host=192.168.0.193
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=20
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=10
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=20000
# 读时间(毫秒)
spring.redis.timeout=10000
# 连接超时时间(毫秒)
spring.redis.connect-timeout=10000

3.2、配置类

RedisConfiguration.java

package com.alian.queue.config;import com.alian.queue.listener.RedisMessageListenerListener;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;@Slf4j
@Configuration
@EnableCaching
public class RedisConfiguration {/*** redis配置** @param redisConnectionFactory* @return*/@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {// 实例化redisTemplateRedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();//设置连接工厂redisTemplate.setConnectionFactory(redisConnectionFactory);// key采用String的序列化redisTemplate.setKeySerializer(keySerializer());// value采用jackson序列化redisTemplate.setValueSerializer(valueSerializer());// Hash key采用String的序列化redisTemplate.setHashKeySerializer(keySerializer());// Hash value采用jackson序列化redisTemplate.setHashValueSerializer(valueSerializer());//执行函数,初始化RedisTemplateredisTemplate.afterPropertiesSet();return redisTemplate;}@Beanpublic ChannelTopic channelTopic() {return new ChannelTopic("TOPIC_USER");}@Beanpublic RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, RedisMessageListenerListener redisMessageListenerListener) {RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);redisMessageListenerContainer.addMessageListener(redisMessageListenerListener, channelTopic());return redisMessageListenerContainer;}/*** key类型采用String序列化** @return*/private RedisSerializer<String> keySerializer() {return new StringRedisSerializer();}/*** value采用JSON序列化** @return*/private RedisSerializer<Object> valueSerializer() {//设置jackson序列化Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);//设置序列化对象jackson2JsonRedisSerializer.setObjectMapper(getMapper());return jackson2JsonRedisSerializer;}/*** 使用com.fasterxml.jackson.databind.ObjectMapper* 对数据进行处理包括java8里的时间** @return*/private ObjectMapper getMapper() {ObjectMapper mapper = new ObjectMapper();//设置可见性mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);//默认键入对象mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);//设置Java 8 时间序列化JavaTimeModule timeModule = new JavaTimeModule();timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));timeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));//禁用把时间转为时间戳mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);mapper.registerModule(timeModule);return mapper;}}

  这里就是在整合redis的前提下(如果不懂可以参考:SpringBoot整合redis(redis支持单节点和集群)),然后新增了如下配置:

    @Beanpublic ChannelTopic channelTopic() {return new ChannelTopic("TOPIC_USER");}@Beanpublic RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, RedisMessageListenerListener redisMessageListenerListener) {RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);redisMessageListenerContainer.addMessageListener(redisMessageListenerListener, channelTopic());return redisMessageListenerContainer;}

  RedisMessageListenerContainer 是为Redis消息侦听器 MessageListener 提供异步行为的容器。处理侦听、转换和消息分派的低级别详细信息。它与低级别Redis(每个订阅一个连接)相反,容器只使用一个连接,该连接对所有注册的侦听器都是“多路复用”的,消息调度是通过任务执行器完成的。容器以惰性方式使用连接(仅当至少配置了一个侦听器时才使用连接),同时添加和删除侦听器具有未定义的结果,强烈建议对这些方法进行相应的同步/排序。

  • 创建一个Redis消息监听器容器
  • 设置 Redis 连接工厂
  • 将监听器和管道名想绑定
  • 配置管道名和推送消息时的管道名要一致,不然监听器监听不到消息

我这里使用的是主题订阅:ChannelTopic,你也可以使用模式匹配:PatternTopic,从而匹配多个信道。

3.3、监听器

package com.alian.queue.listener;import com.alian.queue.service.ConsumeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;@Slf4j
@Component
public class RedisMessageListenerListener implements MessageListener {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Autowiredprivate ConsumeService consumeService;/*** 消息处理** @param message* @param pattern*/@Overridepublic void onMessage(Message message, byte[] pattern) {String channel = new String(pattern);log.info("onMessage --> 消息通道是:{}", channel);try {RedisSerializer<?> valueSerializer = redisTemplate.getValueSerializer();Object deserialize = valueSerializer.deserialize(message.getBody());log.info("反序列化的结果:{}", deserialize);if (deserialize == null) {return;}String md5DigestAsHex = DigestUtils.md5DigestAsHex(deserialize.toString().getBytes(StandardCharsets.UTF_8));log.info("计算得到的key: {}", md5DigestAsHex);Boolean result = redisTemplate.opsForValue().setIfAbsent(md5DigestAsHex, "1", 20, TimeUnit.SECONDS);if (Boolean.TRUE.equals(result)) {// redis消息进行处理consumeService.processMessage(channel, deserialize.toString());log.info("处理redis消息完成");} else {log.info("其他服务处理中");}} catch (Exception e) {e.printStackTrace();log.error("处理redis消息异常:", e);}}}

  我们实现MessageListener 接口,就可以通过onMessage()方法接收到消息了,该方法有两个参数:

  • 参数 message getBody() 方法以二进制形式获取消息体, getChannel() 以二进制形式获取消息通道
  • 参数 pattern 二进制形式的消息通道(实际和 message.getChannel() 返回值相同)

3.4、消费服务

ConsumeService.java

package com.alian.queue.service;import com.alian.queue.dto.UserDto;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;@Slf4j
@Service
public class ConsumeService {public void processMessage(String channel,String message) {// 可以根据channel再继续映射到不同的实现UserDto userDto = JSONObject.parseObject(message, UserDto.class);log.info("接收的结果:{}", userDto);// 做业务...// 还可以分布式锁幂等处理}
}

  这个就是消息处理了,可以根据channel再继续映射到不同的实现,然后业务也可以继续使用分布式锁进行逻辑判断处理,这里就不具体去操作了。

3.5、实体

UserDto.java

package com.alian.queue.dto;import lombok.Data;import java.io.Serializable;
import java.time.LocalDateTime;@Data
public class UserDto implements Serializable {private String id;//员工IDprivate String name;//员工姓名private int age;//员工年龄private String department;//部门private double salary;//工资private LocalDateTime hireDate;//入职时间public UserDto() {}/**  简单的构造方法用于测试*/public UserDto(String id, String name, int age, String department, double salary, LocalDateTime hireDate) {this.id = id;this.name = name;this.age = age;this.department = department;this.salary = salary;this.hireDate = hireDate;}
}

四、验证

  我们把上面的服务启动多个实例,这里就用端口区别,分别是 8090 8091,然后发送消息到 Redis,下面使我们的测试类:

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class RedisMessageQueueTest {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Testpublic void sendMessage() {UserDto userDto = new UserDto("BAT002", "包雅馨", 25, "财务部", 8800.0, LocalDateTime.of(2016, 11, 10, 8, 30, 0));// 注意这里的通道名【TOPIC_USER】要和RedisMessageListenerContainer里面配置的一致redisTemplate.convertAndSend("TOPIC_USER", JSON.toJSONString(userDto));log.info("发送成功");}}

  注意这里的通道名要和 RedisMessageListenerContainer 里配置的一致,不然消息发送不出去的。

8090的日志

onMessage --> 消息通道是:TOPIC_USER
反序列化的结果:{"age":25,"department":"财务部","hireDate":"2016-11-10T08:30:00","id":"BAT002","name":"包雅馨","salary":8800.0}
计算得到的key: c69dc1563cc892718bf3ee0c5b90320b
接收的结果:UserDto(id=BAT002, name=包雅馨, age=25, department=财务部, salary=8800.0, hireDate=2016-11-10T08:30)
处理redis消息完成

8091的日志

onMessage --> 消息通道是:TOPIC_USER
反序列化的结果:{"age":25,"department":"财务部","hireDate":"2016-11-10T08:30:00","id":"BAT002","name":"包雅馨","salary":8800.0}
计算得到的key: c69dc1563cc892718bf3ee0c5b90320b
其他服务处理中

  从结果上可以看到,分布式服务都可以接收到消息,但是最终只有一台服务会真正进行业务的处理,因为我这里使用了最简单的分布式锁来控制了,实际上 redisTemplate.opsForValue().setIfAbsent() 并不是最优解,尤其是在集群模式,我这里只是为了演示要有这么一个操作,推荐还是使用 Redisson 去做分布式锁更可靠。如果有不懂的可以参考我之前的文章:SpringBoot基于Redisson实现分布式锁并分析其原理

五、优化

  其实我们还可以继续去优化我们的配置,消息的接收都是在频繁的创建线程,从而占用系统资源,我们可以通过线程池的方式去优化,RedisMessageListenerContainer 类中有一个方法setTaskExecutor(Executor taskExecutor)可以为监听容器配置线程池。配置线程池以后,所有的线程都会由该线程池产生,因此我们可以通过调节线程池来控制队列监听的速率。修改步骤大致如下:

5.1、注册任务执行器

  RedisConfiguration 新注册Bean:Executor

    @Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//设置核心线程数executor.setCorePoolSize(5);//设置最大线程数executor.setMaxPoolSize(10);//设置任务队列容量executor.setQueueCapacity(10);//设置线程活跃时间(秒)executor.setKeepAliveSeconds(60);//设置默认线程名称(线程前缀名称,有助于区分不同线程池之间的线程比如:taskExecutor-)executor.setThreadNamePrefix("taskExecutor-");//设置拒绝策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//设置允许核心线程超时executor.setAllowCoreThreadTimeOut(true);return executor;}

5.2、配置任务执行器

  redisMessageListenerContainer.setTaskExecutor(taskExecutor())

	@Beanpublic RedisMessageListenerContainer getRedisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, RedisMessageListenerListener redisMessageListenerListener) {RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();// 设置连接工厂redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);// 绑定监听器和信道redisMessageListenerContainer.addMessageListener(redisMessageListenerListener, channelTopic());// 配置任务执行器redisMessageListenerContainer.setTaskExecutor(taskExecutor());return redisMessageListenerContainer;}

5.3、启用异步执行器

  RedisConfiguration 增加注解@EnableAsync,为了其他操作也能用到异步操作处理。

@Slf4j
@EnableAsync
@Configuration
@EnableCaching
public class RedisConfiguration {//...
}

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LLaMA-META发布单卡就能跑的大模型

2023年2月25日&#xff0c;Meta使用2048张A100 GPU&#xff0c;花费21天训练的Transformer大模型LLaMA开源了。 1.4T tokenstakes approximately 21 days 以下是觉得论文中重要的一些要点 1&#xff09;相对较小的模型也可以获得不错的性能 研究者发现在给定计算能力限制的情…

《高性能MySQL》读书笔记(下)

目录 Mysql查询性能的优化 慢查询基础 优化数据访问 是否向数据库请求了不需要的数据 查询了不需要的记录 多表联查中返回全部列 MySQL是否在扫描额外的记录 重写查询的方式 切分查询&#xff08;重点&#xff09; 分解连接查询&#xff08;重点&#xff09; MySQL如…

史上最全的大数据开发八股文【自己的吐血总结】

自我介绍 我本硕都是双非计算机专业&#xff0c;从研一下开始学习大数据开发的相关知识&#xff0c;从找实习到秋招&#xff0c;我投递过100公司&#xff0c;拿到过10的offer&#xff0c;包括滴滴、字节、蚂蚁、携程、蔚来、去哪儿等大厂&#xff08;岗位都是大数据开发&#…

算法练习(七)数据分类处理

一、数据分类处理 1、题目描述&#xff1a; 信息社会&#xff0c;有海量的数据需要分析处理&#xff0c;比如公安局分析身份证号码、 QQ 用户、手机号码、银行帐号等信息及活动记录。采集输入大数据和分类规则&#xff0c;通过大数据分类处理程序&#xff0c;将大数据分类输出…

喜讯!华秋电子荣获第六届“蓝点奖”十佳分销商奖

2 月 25 日&#xff0c;由深圳市电子商会主办的2023 中国电子信息产业创新发展交流大会暨第六届蓝点奖颁奖盛典在深圳隆重举行。 图&#xff1a;华秋商城渠道总监杨阳&#xff08;右三&#xff09; 深圳市电子商会连续六年举办“蓝点奖”评选活动&#xff0c;旨在表彰对电子信…

高端电器新十年,求解「竞速突围」

竞争激烈的高端电器品牌们&#xff0c;平时王不见王&#xff0c;但也有例外。海尔、博西、海信、创维、方太、老板等等近乎中国电器行业所有一线品牌副总裁级别以上高层&#xff0c;2月22日都现身于上海&#xff0c;来参加一场由红星美凯龙攒起来的高端电器局&#xff0c;2023中…

能在软路由docker给部署搭建teamsperk服务器么?并且设置好ddns

参考链接(4条消息) 【个人学习总结】使用docker搭建Teamspeak服务器_blcurtain的博客-CSDN博客_teamspeak3 docker(⊙﹏⊙)哎呀&#xff0c;崩溃啦&#xff01; (tdeh.top)TeamSpeak服务器搭建与使用 - 缘梦の镇 (cmsboy.cn)Openwrt X86 docker运行甜糖-软路由,x86系统,openwrt…

虚拟数字人直播带货相比人工有哪些优势?

新经济时代的到来&#xff0c;彻底改变了传统的消费方式。虚拟数字人的出现&#xff0c;标志着新一波的消费升级到来。虚拟数字人直播带货&#xff0c;不仅降低了商家的带货成本&#xff0c;拉近了商家与消费者的距离&#xff0c;也给消费者带来全新的消费方式。 花西子虚拟形象…

如何查看Spring Boot各版本的变化

目录 1.版本 2.基础特性和使用 3.新增特性和Bug修复 1.版本 打开Spring官网&#xff0c;点进Spring Boot项目我们会发现在不同版本后面会跟着不同的标签&#xff1a; 这些标签对应不同的版本&#xff0c;其意思如下&#xff1a; GA正式版本&#xff0c;通常意味着该版本已…

k8s学习之路 | Day16 k8s 中的容器初探

文章目录容器镜像镜像名称镜像拉取策略私有仓库的拉取策略容器的环境变量和启动命令容器的环境变量容器的启动命令容器的生命周期钩子postStartpreStop容器的探针startupProbelivenessProbereadinessProbek8s 集群中最小的管理单元就是一个Pod&#xff0c;而Pod里面才是容器&am…

利用GPT-3 Fine-tunes训练专属语言模型

利用GPT-3 Fine-tunes训练专属语言模型 文章目录什么是模型微调&#xff08;fine-tuning&#xff09;&#xff1f;为什么需要模型微调&#xff1f;微调 vs 重新训练微调 vs 提示设计训练专属模型数据准备清洗数据构建模型微调模型评估模型部署模型总结什么是模型微调&#xff0…

JavaScript split()方法

JavaScript split()方法 目录JavaScript split()方法一、定义和用法二、语法三、参数值四、返回值五、更多实例5.1 省略分割参数5.2 使用limit参数5.3 使用一个字符作为分割符一、定义和用法 split() 方法用于把一个字符串分割成字符串数组。 二、语法 string.split(separat…

NCRE计算机等级考试Python真题(四)

第四套试题1、以下选项中&#xff0c;不属于需求分析阶段的任务是&#xff1a;A.需求规格说明书评审B.确定软件系统的性能需求C.确定软件系统的功能需求D.制定软件集成测试计划正确答案&#xff1a; D2、关于数据流图&#xff08;DFD&#xff09;的描述&#xff0c;以下选项中正…

RTMP的工作原理及优缺点

一.什么是RTMP&#xff1f;RTMP&#xff08;Real-Time Messaging Protocol&#xff0c;实时消息传输协议&#xff09;是一种用于低延迟、实时音视频和数据传输的双向互联网通信协议&#xff0c;由Macromedia&#xff08;后被Adobe收购&#xff09;开发。RTMP的工作原理是&#…

IP-GUARD控制台账户输入多次错误密码锁定后该如何解锁?

其他管理员账户给锁定了,暂时只能等其锁定时间到了才可以再次输入,默认是设置是锁定30min; 1、如果急需此账户查看,可以使用admin系统管理员账户登录控制台,在工具-账户中清除这个账户的密码,重新登录设置密码。

NIO与零拷贝

目录 一、零拷贝的基本介绍 二、传统IO数据读写的劣势 三、mmap优化 四、sendFile优化 五、 mmap 和 sendFile 的区别 六、零拷贝实战 6.1 传统IO 6.2 NIO中的零拷贝 6.3 运行结果 一、零拷贝的基本介绍 零拷贝是网络编程的关键&#xff0c;很多性能优化都离不开。 在…

【云原生kubernetes】k8s 常用调度策略使用详解

一、前言 通过之前的学习&#xff0c;我们了解到k8s集群中最小工作单位是pod&#xff0c;对于k8s集群来说&#xff0c;一个pod的完整生命周期是由一系列调度策略来控制&#xff0c;这些调度策略具体是怎么工作的呢&#xff1f;本文将详细讨论下这个问题。 二、k8s调度策略简介…

【多目标优化算法】多目标蚱蜢优化算法(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…

APP测试的7大注意点。

1. 运行 1&#xff09; App安装完成后的试运行&#xff0c;可正常打开软件。 2&#xff09; App打开测试&#xff0c;是否有加载状态进度提示。 3&#xff09; App⻚面间的切换是否流畅&#xff0c;逻辑是否正确。 4&#xff09; 注册 同表单编辑⻚面 用户名密码⻓度 …

快手电商新增商品信息诊断规则,对商家有何影响?

1、2022年快手短剧日活跃用户达2.6亿 新榜讯 近日&#xff0c;快手数据显示&#xff0c;2022年快手短剧日活跃用户达2.6亿&#xff0c;现在的付费用户数对比2022年4月增长超过480%&#xff0c;快手已经是最大的短剧消费市场。此外&#xff0c;2023年快手小游戏日活跃用户峰值超…