自定义注解实现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.总结
文章目录
- @[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驱动步进电机
前言 (1)本章介绍用stm32驱动42步进电机,将介绍需要准备的硬件器材、所需芯片资源以及怎么编程及源代码等等。 (2)实验效果:按下按键,步进电机顺时针或逆时针旋转90度。 (3ÿ…...

计算机视觉——飞桨深度学习实战-深度学习网络模型
深度学习网络模型的整体架构主要数据集、模型组网以及学习优化过程三部分,本章主要围绕着深度学习网络模型的算法架构、常见模型展开了详细介绍,从经典的深度学习网络模型以CNN、RNN为代表,到为了解决显存不足、实时性不够等问题的轻量化网络…...
用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潮流的配电网故障重构代码讲解内容,对应的资源下载链接为基于DistFlow潮流的配电网故障重构(输入任意线路),对该程序进行了详尽的讲解,基本做到句句分析和讲解(讲解…...

Ultralytics(YoloV8)开发环境配置,训练,模型转换,部署全流程测试记录
关键词:windows docker tensorRT Ultralytics YoloV8 配置开发环境的方法: 1.Windows的虚拟机上配置: Python3.10 使用Ultralytics 可以得到pt onnx,但无法转为engine,找不到GPU,手动转也不行࿰…...
springboot之@ImportResource:导入Spring配置文件~
ImportResource的作用是允许在Spring配置文件中导入其他的配置文件。通过使用ImportResource注解,可以将其他配置文件中定义的Bean定义导入到当前的配置文件中,从而实现配置文件的模块化和复用。这样可以方便地将不同的配置文件进行组合,提高…...

阿里云服务器免费申请入口_注册阿里云免费领4台服务器
注册阿里云账号,免费领云服务器,最高领取4台云服务器,每月750小时,3个月免费试用时长,可快速搭建网站/小程序,部署开发环境,开发多种企业应用。阿里云百科分享阿里云服务器免费领取入口、免费云…...
ES6中的async、await函数
async是为了解决异步操作,其实是一个语法糖,使代码书写更加简洁。 1. async介绍 async放在一个函数的前面,await则放在异步操作前面。async代表这个函数中有异步操作需要等待结果,在一个async函数中可以存在多个await࿰…...

代码随想录算法训练营第五十六天 | 动态规划 part 14 | 1143.最长公共子序列、1035.不相交的线、53. 最大子序和(dp)
目录 1143.最长公共子序列思路代码 1035.不相交的线思路代码 53. 最大子序和(dp)思路代码 1143.最长公共子序列 Leetcode 思路 本题和718. 最长重复子数组 区别在于这里不要求是连续的了,但要有相对顺序,即:“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(纯干货)】
目录 管道——过滤器软件体系结构风格优点:计算机英语重点词汇:单元测试主要检查模块的以下5个特征:数据库之并发控制中的事务:并发产生的问题解决方案:封锁协议原型化开发方法: 每日一言:持续更新中... 个…...

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

Socket通信
优质博文IT-BLOG-CN 一、简介 Socket套接字:描述了计算机的IP地址和端口,运行在计算机中的程序之间采用socket进行数据通信。通信的两端都有socket,它是一个通道,数据在两个socket之间进行传输。socket把复杂的TCP/IP协议族隐藏在…...
TCP 如何保证有效传输及拥塞控制
TCP(传输控制协议)可以通过以下机制保证有效传输和拥塞控制: 确认机制:TCP使用确认机制来保证数据的有效传输。发送方在发送数据的同时还会发送一个确认请求,接收方收到数据后会回复确认响应。如果发送方没有收到确认响…...

PyQt5+Qt设计师初探
在上一篇文章中我们搭建好了PyQt5的开发环境,打铁到趁热我们基于搭建好的环境来简单实战一把 一:PyQt5包模块简介 PyQt5包括的主要模块如下。 QtCore模块——涵盖了包的核心的非GUI功能,此模块被用于处理程序中涉及的时间、文件、目录、数…...
rust cargo
一、cargo是什么 Cargo是Rust的构建工具和包管理器。 Cargo除了创建工程、构建工程、运行工程等功能,还具有下载依赖库、编译依赖等功能。 真正编写程序时,我们不直接用rustc,而是用cargo。 二、使用cargo (一)使用…...

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

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

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...

Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...