当前位置: 首页 > 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…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

三体问题详解

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

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...