当前位置: 首页 > news >正文

自定义注解实现Redis分布式锁、手动控制事务和根据异常名字或内容限流的三合一的功能

自定义注解实现Redis分布式锁、手动控制事务和根据异常名字或内容限流的三合一的功能

文章目录

    • @[toc]
  • 1.依赖
  • 2.Redisson配置
    • 2.1单机模式配置
    • 2.2主从模式
    • 2.3集群模式
    • 2.4哨兵模式
  • 3.实现
    • 3.1 RedisConfig
    • 3.2 自定义注解IdempotentManualCtrlTransLimiterAnno
    • 3.3自定义切面IdempotentManualCtrlTransAspect
  • 4.测试验证
  • 5.总结

1.依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.3.9.RELEASE</version>
</dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.13.4</version>
</dependency>

2.Redisson配置

2.1单机模式配置

spring:redis:host: localhostport: 6379password: null
redisson:codec: org.redisson.codec.JsonJacksonCodecthreads: 4netty:threads: 4single-server-config:address: "redis://localhost:6379"password: null

2.2主从模式

spring:redis:sentinel:master: my-masternodes: localhost:26379,localhost:26389password: your_password
redisson:master-slave-config:master-address: "redis://localhost:6379"slave-addresses: "redis://localhost:6380,redis://localhost:6381"password: ${spring.redis.password}

2.3集群模式

spring:redis:cluster:nodes: localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384password: your_password
redisson:cluster-config:node-addresses: "redis://localhost:6379,redis://localhost:6380,redis://localhost:6381,redis://localhost:6382,redis://localhost:6383,redis://localhost:6384"password: ${spring.redis.password}

2.4哨兵模式

spring:redis:sentinel:master: my-masternodes: localhost:26379,localhost:26389password: your_password
redisson:sentinel-config:master-name: my-mastersentinel-addresses: "redis://localhost:26379,redis://localhost:26380,redis://localhost:26381"password: ${spring.redis.password}

Redission的集成还有很多种方式的,所以不局限于这种方式,条条大路通罗马,一万个读者就有一万个哈姆雷特。

3.实现

3.1 RedisConfig

package xxxxx.config;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 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.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Bean@SuppressWarnings("all")public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {// 定义泛型为 <String, Object> 的 RedisTemplateRedisTemplate<String, Object> template = new RedisTemplate<String, Object>();// 设置连接工厂template.setConnectionFactory(factory);// 定义 Json 序列化Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);// Json 转换工具ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);//方法二:解决jackson2无法反序列化LocalDateTime的问题om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);om.registerModule(new JavaTimeModule());jackson2JsonRedisSerializer.setObjectMapper(om);// 定义 String 序列化StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);// hash的key也采用String的序列化方式template.setHashKeySerializer(stringRedisSerializer);// value序列化方式采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的value序列化方式采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}@BeanRedisTemplate<String, Long> redisTemplateLimit(RedisConnectionFactory factory) {final RedisTemplate<String, Long> template = new RedisTemplate<>();template.setConnectionFactory(factory);template.setKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(new GenericToStringSerializer<>(Long.class));template.setValueSerializer(new GenericToStringSerializer<>(Long.class));return template;}}

3.2 自定义注解IdempotentManualCtrlTransLimiterAnno

package xxxxx.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;/*** @author zlf*/
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IdempotentManualCtrlTransLimiterAnno {/*** 是否开启RedissonLock* 1:开启* 0:不开启** @return*/boolean isOpenRedissonLock() default false;/*** 是否开启手动控制事务提交* 1:开启* 0:不开启** @return*/boolean isOpenManualCtrlTrans() default false;/*** 分布式锁key格式:* keyFormat a:b:%s** @return*/String keyFormat() default "";/*** 锁定时间* 默认 3s** @return*/long lockTime() default 3l;/*** 锁定时间单位* TimeUnit.MILLISECONDS 毫秒* TimeUnit.SECONDS 秒* TimeUnit.MINUTES 分* TimeUnit.HOURS 小时* TimeUnit.DAYS 天** @return*/TimeUnit lockTimeUnit() default TimeUnit.SECONDS;/*** 是否开启限流** @return*/boolean isOpenLimit() default false;/*** 限流redis失败次数统计key* public方法第一个string参数就是%s** @return*/String limitRedisKeyPrefix() default "limit:redis:%s";/*** 限流redisKey统计的key的过期时间* 默认10分钟后过期** @return*/long limitRedisKeyExpireTime() default 10l;/*** 锁过期单位* TimeUnit.MILLISECONDS 毫秒* TimeUnit.SECONDS 秒* TimeUnit.MINUTES 分* TimeUnit.HOURS 小时* TimeUnit.DAYS 天** @return*/TimeUnit limitRedisKeyTimeUnit() default TimeUnit.MINUTES;/*** 默认限流策略:* 异常计数器限流:可以根据异常名称和异常内容来计数限制* 根据异常次数,当异常次数达到多少次后,限制访问(异常类型为RuntimeException类型) (实现)* RedisTemplate的配置文件中需要有这个类型的bean** @return* @Bean RedisTemplate<String, Long> redisTemplateLimit(RedisConnectionFactory factory) {* final RedisTemplate<String, Long> template = new RedisTemplate<>();* template.setConnectionFactory(factory);* template.setKeySerializer(new StringRedisSerializer());* template.setHashValueSerializer(new GenericToStringSerializer<>(Long.class));* template.setValueSerializer(new GenericToStringSerializer<>(Long.class));* return template;* }* 滑动窗口限流 (未实现)* 令牌桶 (未实现)* ip限流 (未实现)* Redisson方式限流 (未实现)* <p>* limitTye() 异常计数器限流 可以写Exception的子类* 这个和下面的expContent()互斥,二选一配置即可*/String limitTye() default "";/*** 异常信息内容匹配统计** @return*/String expContent() default "";/*** 异常统计次数上线默认为10次** @return*/int limitMaxErrorCount() default 10;}

3.3自定义切面IdempotentManualCtrlTransAspect

package xxxxxx.annotation;import cn.hutool.core.lang.Tuple;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;@Slf4j
@Aspect
@Component
public class IdempotentManualCtrlTransAspect {@Autowiredprivate TransactionDefinition transactionDefinition;@Autowiredprivate DataSourceTransactionManager transactionManager;@Autowiredprivate RedissonClient redissonClient;@Autowiredprivate RedisTemplate<String, Long> redisTemplateLimit;private static final List<String> KEY_FORMAT_MATCHS = new ArrayList<>();static {KEY_FORMAT_MATCHS.add("%s");}@Pointcut("@annotation(com.dy.member.annotation.IdempotentManualCtrlTransLimiterAnno)")public void idempotentManualCtrlTransPoint() {}@Around("idempotentManualCtrlTransPoint()")public Object deal(ProceedingJoinPoint pjp) throws Throwable {//当前线程名String threadName = Thread.currentThread().getName();log.info("-------------IdempotentManualCtrlTransLimiterAnno开始执行-----线程{}-----------", threadName);//获取参数列表Object[] objs = pjp.getArgs();String key = null;String redisLimitKey = null;String message = "";IdempotentManualCtrlTransLimiterAnno annotation = null;try {//注解加上的public方法的第一个参数就是key,只支持改参数为String类型key = (String) objs[0];if (Objects.isNull(key)) {return pjp.proceed();}//获取该注解的实例对象annotation = ((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(IdempotentManualCtrlTransLimiterAnno.class);//是否开启RedissonLockboolean openRedissonLock = annotation.isOpenRedissonLock();boolean openManualCtrlTrans = annotation.isOpenManualCtrlTrans();boolean openLimit = annotation.isOpenLimit();boolean bothFlag = openRedissonLock && openManualCtrlTrans;if (openLimit) {int errorCount = annotation.limitMaxErrorCount();String limitRedisKey = annotation.limitRedisKeyPrefix();redisLimitKey = String.format(limitRedisKey, key);TimeUnit timeUnit = annotation.limitRedisKeyTimeUnit();this.checkFailCount(redisLimitKey, errorCount, timeUnit);if (!openRedissonLock && !openManualCtrlTrans) {return pjp.proceed();}}if (!bothFlag) {if (openRedissonLock) {key = checkKeyFormatMatch(annotation, key);RLock lock = redissonClient.getLock(key);try {Tuple lockAnnoParamsTuple = this.getLockAnnoParams(annotation);long t = lockAnnoParamsTuple.get(0);TimeUnit uint = lockAnnoParamsTuple.get(1);if (lock.tryLock(t, uint)) {return pjp.proceed();}} catch (Exception e) {log.error("-------------IdempotentManualCtrlTransLimiterAnno锁异常ex:{}-----线程{}-----------", ExceptionUtils.getMessage(e), threadName);throw new RuntimeException(ExceptionUtils.getMessage(e));} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();log.info("-------------IdempotentManualCtrlTransLimiterAnno释放锁成功-----线程{}-----------", threadName);}}}if (openManualCtrlTrans) {TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);try {Object proceed = pjp.proceed();transactionManager.commit(transactionStatus);return proceed;} catch (Exception e) {transactionManager.rollback(transactionStatus);log.info("-------------IdempotentManualCtrlTransLimiterAnno执行异常事务回滚1-----线程{}-----------", threadName);throw new RuntimeException(ExceptionUtils.getMessage(e));}}}if (bothFlag) {key = checkKeyFormatMatch(annotation, key);RLock lock = redissonClient.getLock(key);TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);try {Tuple lockAnnoParamsTuple = this.getLockAnnoParams(annotation);long t = lockAnnoParamsTuple.get(0);TimeUnit uint = lockAnnoParamsTuple.get(1);if (lock.tryLock(t, uint)) {Object proceed = pjp.proceed();transactionManager.commit(transactionStatus);return proceed;}} catch (Exception e) {log.error("-------------IdempotentManualCtrlTransLimiterAnno处理异常ex:{}-----线程{}-----------", ExceptionUtils.getMessage(e), threadName);transactionManager.rollback(transactionStatus);log.info("-------------IdempotentManualCtrlTransLimiterAnno执行异常事务回滚2-----线程{}-----------", threadName);throw new RuntimeException(ExceptionUtils.getMessage(e));} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();log.info("-------------IdempotentManualCtrlTransLimiterAnno释放锁成功2-----线程{}-----------", threadName);}}}} catch (Exception e) {message = ExceptionUtils.getMessage(e);String stackTrace = ExceptionUtils.getStackTrace(e);String limitExType = annotation.limitTye();log.error("------------IdempotentManualCtrlTransLimiterAnno-------msg:{},stackTrace:{},limitExType:{}-------", message, stackTrace, limitExType);boolean openLimit = annotation.isOpenLimit();TimeUnit timeUnit = annotation.limitRedisKeyTimeUnit();long limitRedisKeyExpireTime = annotation.limitRedisKeyExpireTime();String expContent = annotation.expContent();if (openLimit) {if (StringUtils.isNotBlank(message) && StringUtils.isNotBlank(expContent) && message.indexOf(expContent) != -1) {log.error("------------IdempotentManualCtrlTransLimiterAnno-------openLimit:{},message:{},expContent:{}-------", openLimit, message, expContent);if (!redisTemplateLimit.hasKey(redisLimitKey)) {redisTemplateLimit.opsForValue().set(redisLimitKey, 1L, limitRedisKeyExpireTime, timeUnit);} else {redisTemplateLimit.opsForValue().increment(redisLimitKey);}} else if (stackTrace.indexOf(limitExType) != -1 && StringUtils.isBlank(expContent)) {log.error("------------IdempotentManualCtrlTransLimiterAnno-------openLimit:{},stackTrace:{},expContent:{}-------", openLimit, stackTrace, limitExType);if (!redisTemplateLimit.hasKey(redisLimitKey)) {redisTemplateLimit.opsForValue().set(redisLimitKey, 1L, limitRedisKeyExpireTime, timeUnit);} else {redisTemplateLimit.opsForValue().increment(redisLimitKey);}} else {if (!redisTemplateLimit.hasKey(redisLimitKey)) {redisTemplateLimit.opsForValue().set(redisLimitKey, 1L, limitRedisKeyExpireTime, timeUnit);} else {redisTemplateLimit.opsForValue().increment(redisLimitKey);}}}log.error("-------------IdempotentManualCtrlTransLimiterAnno开始异常ex:{}-----线程{}-----------", ExceptionUtils.getMessage(e), threadName);}throw new RuntimeException(message.replaceAll("RuntimeException", "").replaceAll("Exception", "").replaceAll(":", "").replaceAll(" ", ""));}private void checkFailCount(String key, long errorCount, TimeUnit timeUnit) {boolean isExistKey = redisTemplateLimit.hasKey(key);if (isExistKey) {Long count = (Long) redisTemplateLimit.opsForValue().get(key);log.info("=========IdempotentManualCtrlTransLimiterAnno=====key:{}=======failCount:{}=========", key, count);if (Objects.nonNull(count) && count > errorCount) {Long expire = redisTemplateLimit.getExpire(key, timeUnit);String unitStr = "";if (timeUnit.equals(TimeUnit.DAYS)) {unitStr = "天";} else if (timeUnit.equals(TimeUnit.HOURS)) {unitStr = "小时";} else if (timeUnit.equals(TimeUnit.MINUTES)) {unitStr = "分钟";} else if (timeUnit.equals(TimeUnit.SECONDS)) {unitStr = "秒钟";} else if (timeUnit.equals(TimeUnit.MILLISECONDS)) {unitStr = "毫秒";}log.error("IdempotentManualCtrlTransLimiterAnno异常次数限制,错误次数:{}", errorCount);throw new RuntimeException("请求异常,请" + expire + unitStr + "后重试!");}}}private Tuple getLockAnnoParams(IdempotentManualCtrlTransLimiterAnno annotation) {long t = annotation.lockTime();TimeUnit unit = annotation.lockTimeUnit();return new Tuple(t, unit);}private String checkKeyFormatMatch(IdempotentManualCtrlTransLimiterAnno annotation, String key) {String keyFormat = annotation.keyFormat();if (StringUtils.isNotBlank(keyFormat)) {if (!KEY_FORMAT_MATCHS.contains(keyFormat)) {throw new RuntimeException("注解key格式匹配有误!");}key = String.format(keyFormat, key);}return key;}}

4.测试验证

在controller层新建TestController1

package com.xxxx.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
@Slf4j
@RequestMapping("/test")
public class TestController1 {@Autowiredprivate xxxxRecordService xxxRecordService;@PostMapping("/hellWorld")@IdempotentManualCtrlTransLimiterAnno(isOpenRedissonLock = true, isOpenManualCtrlTrans = true, isOpenLimit = true, limitRedisKeyExpireTime = 10, expContent = "我是异常")public RestResponse hellWorld(@RequestParam String key) {xxxRecord pm = new xxxRecord();pm.setOrderNo(key);xxxRecordService.save(pm);if (key.equals("2")) {//int i = 10;//i = i / 0;throw new RuntimeException("我是异常");}return RestResponse.success(key);}
}

使用postMan请求接口,在自定定义的aspect的类中的deal方法内打上断点就可以调试了:

请添加图片描述

使用这个方法才可以调试,如果使用的是springBoot的单元测试,不会进断点,这个也是奇怪的很,切面表达式可以切一个注解,也可以切指定包下的某些方法,比如可以切所有controller下的xx类的xx方法的xxx参数,这个可以参考网上的教程实现,也可以把SPEL表达式的解析实现进去,key的解析通过SPEL表达式解析配偶到第一个参数中对应的String参数或者是Json参数匹配的字段上,这种灵活性就好一点了,本文的这个约定的也是好使的,一点也不影响,复杂度没有那么高,本文约定的是公共方法的第一个String的参数为key,可以读上面的代码实现,都有注释说明的。

5.总结

在日常开发当中,进场会遇到这三个场景,所以需要写一些重复性的代码,用一次写一次,CV哥就是这样养成的,所以经过我的思考,突发了这个灵感,搞个注解全部搞定,这个多方便,要那个功能开哪个功能,而且还可以组合使用,优雅永不过时,本次分享就到这里,希望我的分享对你有所帮助,请一键三连,么么么哒!

相关文章:

自定义注解实现Redis分布式锁、手动控制事务和根据异常名字或内容限流的三合一的功能

自定义注解实现Redis分布式锁、手动控制事务和根据异常名字或内容限流的三合一的功能 文章目录 [toc] 1.依赖2.Redisson配置2.1单机模式配置2.2主从模式2.3集群模式2.4哨兵模式 3.实现3.1 RedisConfig3.2 自定义注解IdempotentManualCtrlTransLimiterAnno3.3自定义切面Idempote…...

Linux:minishell

目录 1.实现逻辑 2.代码及效果展示 1.打印字符串提示用户输入指令 2.父进程拆解指令 3.子进程执行指令,父进程等待结果 4.效果 3.实现过程中遇到的问题 1.打印字符串的时候不显示 2.多换了一行 3.cd路径无效 4.优化 1.ll指令 2.给文件或目录加上颜色 代码链接 模…...

STM32驱动步进电机

前言 &#xff08;1&#xff09;本章介绍用stm32驱动42步进电机&#xff0c;将介绍需要准备的硬件器材、所需芯片资源以及怎么编程及源代码等等。 &#xff08;2&#xff09;实验效果&#xff1a;按下按键&#xff0c;步进电机顺时针或逆时针旋转90度。 &#xff08;3&#xff…...

计算机视觉——飞桨深度学习实战-深度学习网络模型

深度学习网络模型的整体架构主要数据集、模型组网以及学习优化过程三部分&#xff0c;本章主要围绕着深度学习网络模型的算法架构、常见模型展开了详细介绍&#xff0c;从经典的深度学习网络模型以CNN、RNN为代表&#xff0c;到为了解决显存不足、实时性不够等问题的轻量化网络…...

用c动态数组(不用c++vector)实现手撸神经网咯230901

用c语言动态数组(不用c++的vector)实现:输入数据inputs = { {1, 1}, {0,0},{1, 0},{0,1} };目标数据targets={0,0,1,1}; 测试数据 inputs22 = { {1, 0}, {1,1},{0,1} }; 构建神经网络,例如:NeuralNetwork nn({ 2, 4,3,1 }); 则网络有四层、输入层2个nodes、输出层1个节点、第…...

视频讲解|基于DistFlow潮流的配电网故障重构代码

目录 1 主要内容 2 视频链接 1 主要内容 该视频为基于DistFlow潮流的配电网故障重构代码讲解内容&#xff0c;对应的资源下载链接为基于DistFlow潮流的配电网故障重构(输入任意线路)&#xff0c;对该程序进行了详尽的讲解&#xff0c;基本做到句句分析和讲解&#xff08;讲解…...

Ultralytics(YoloV8)开发环境配置,训练,模型转换,部署全流程测试记录

关键词&#xff1a;windows docker tensorRT Ultralytics YoloV8 配置开发环境的方法&#xff1a; 1.Windows的虚拟机上配置&#xff1a; Python3.10 使用Ultralytics 可以得到pt onnx&#xff0c;但无法转为engine&#xff0c;找不到GPU&#xff0c;手动转也不行&#xff0…...

springboot之@ImportResource:导入Spring配置文件~

ImportResource的作用是允许在Spring配置文件中导入其他的配置文件。通过使用ImportResource注解&#xff0c;可以将其他配置文件中定义的Bean定义导入到当前的配置文件中&#xff0c;从而实现配置文件的模块化和复用。这样可以方便地将不同的配置文件进行组合&#xff0c;提高…...

阿里云服务器免费申请入口_注册阿里云免费领4台服务器

注册阿里云账号&#xff0c;免费领云服务器&#xff0c;最高领取4台云服务器&#xff0c;每月750小时&#xff0c;3个月免费试用时长&#xff0c;可快速搭建网站/小程序&#xff0c;部署开发环境&#xff0c;开发多种企业应用。阿里云百科分享阿里云服务器免费领取入口、免费云…...

ES6中的async、await函数

async是为了解决异步操作&#xff0c;其实是一个语法糖&#xff0c;使代码书写更加简洁。 1. async介绍 async放在一个函数的前面&#xff0c;await则放在异步操作前面。async代表这个函数中有异步操作需要等待结果&#xff0c;在一个async函数中可以存在多个await&#xff0…...

代码随想录算法训练营第五十六天 | 动态规划 part 14 | 1143.最长公共子序列、1035.不相交的线、53. 最大子序和(dp)

目录 1143.最长公共子序列思路代码 1035.不相交的线思路代码 53. 最大子序和&#xff08;dp&#xff09;思路代码 1143.最长公共子序列 Leetcode 思路 本题和718. 最长重复子数组 区别在于这里不要求是连续的了&#xff0c;但要有相对顺序&#xff0c;即&#xff1a;“ace” …...

【数据挖掘】2021年 Quiz 1-3 整理 带答案

目录 Quiz 1Quiz 2Quiz 3Quiz 1 Problem 1 (30%). Consider the training data shown below. Here, A A A and B B B</...

【软件设计师-中级——刷题记录6(纯干货)】

目录 管道——过滤器软件体系结构风格优点&#xff1a;计算机英语重点词汇&#xff1a;单元测试主要检查模块的以下5个特征&#xff1a;数据库之并发控制中的事务&#xff1a;并发产生的问题解决方案:封锁协议原型化开发方法&#xff1a; 每日一言&#xff1a;持续更新中... 个…...

微信小程序点单左右联动的效果实现

微信小程序点单左右联动的效果实现 原理解析&#xff1a;   点击左边标签会跳到右边相应位置&#xff1a;点击改变rightCur值&#xff0c;转跳相应位置滑动右边&#xff0c;左边标签会跳到相应的位置&#xff1a;监听并且设置每个右边元素的top和bottom&#xff0c;再判断当…...

Socket通信

优质博文IT-BLOG-CN 一、简介 Socket套接字&#xff1a;描述了计算机的IP地址和端口&#xff0c;运行在计算机中的程序之间采用socket进行数据通信。通信的两端都有socket&#xff0c;它是一个通道&#xff0c;数据在两个socket之间进行传输。socket把复杂的TCP/IP协议族隐藏在…...

TCP 如何保证有效传输及拥塞控制

TCP&#xff08;传输控制协议&#xff09;可以通过以下机制保证有效传输和拥塞控制&#xff1a; 确认机制&#xff1a;TCP使用确认机制来保证数据的有效传输。发送方在发送数据的同时还会发送一个确认请求&#xff0c;接收方收到数据后会回复确认响应。如果发送方没有收到确认响…...

PyQt5+Qt设计师初探

在上一篇文章中我们搭建好了PyQt5的开发环境&#xff0c;打铁到趁热我们基于搭建好的环境来简单实战一把 一&#xff1a;PyQt5包模块简介 PyQt5包括的主要模块如下。 QtCore模块——涵盖了包的核心的非GUI功能&#xff0c;此模块被用于处理程序中涉及的时间、文件、目录、数…...

rust cargo

一、cargo是什么 Cargo是Rust的构建工具和包管理器。 Cargo除了创建工程、构建工程、运行工程等功能&#xff0c;还具有下载依赖库、编译依赖等功能。 真正编写程序时&#xff0c;我们不直接用rustc&#xff0c;而是用cargo。 二、使用cargo &#xff08;一&#xff09;使用…...

CANoe.Diva生成测试用例

Diva目录 一、CANoe.Diva打开CDD文件二、导入CDD文件三、ECU Information四、时间参数设置五、选择是否测试功能寻址六、勾选需要测试服务项七、生成测试用例 一、CANoe.Diva打开CDD文件 CANoe.Diva可以通过导入cdd或odx文件&#xff0c;自动生成全面的测试用例。再在CANoe中导…...

openGauss学习笔记-89 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用查询原生编译

文章目录 openGauss学习笔记-89 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用查询原生编译89.1 查询编译&#xff1a;PREPARE语句89.2 运行命令89.3 轻量执行支持的查询89.4 轻量执行不支持的查询89.5 JIT存储过程89.6 MOT JIT诊断89.6.1 mot_jit_detai…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...