自定义注解实现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…...
python获取时间戳
使用 datetime 库获取时间。 获取当前时间: import datetime print(datetime.datetime.now()) . 后面的是微秒,也是一个时间单位,1秒1000000微秒。 转为时间戳: import datetimedate datetime.datetime.now() timestamp date…...
2023年山东省安全员C证证考试题库及山东省安全员C证试题解析
题库来源:安全生产模拟考试一点通公众号小程序 2023年山东省安全员C证证考试题库及山东省安全员C证试题解析是安全生产模拟考试一点通结合(安监局)特种作业人员操作证考试大纲和(质检局)特种设备作业人员上岗证考试大…...
Java中的Unicode字符编码与占用比特位解析
本文将详细介绍Java中Unicode字符编码与占用比特位的相关知识。我们将首先介绍Unicode字符集的基本概念,然后深入探讨Java中Unicode字符的编码方式以及占用比特位的特点。最后,我们将讨论一些特殊字符的编码情况,并给出一些在Java中处理Unico…...
分布式事务-TCC案例分析流程图
防止cancel方法在最后执行出现问题,用户收到提示已经退款成功但是由于cancel过慢或者出现问题(虽然最后会重试成功但是用户体验很差),可以做以下的业务sql模型优化(增加一个冻结金额)。...
究竟是什么样的讲解数组算法的博客让我写了三小时???
版本说明 当前版本号[20231004]。 版本修改说明20231004初版 目录 文章目录 版本说明目录二. 基础数据结构2.1 数组1) 概述2) 动态数组1)插入addlast 方法测试: addlast 方法 add 方法测试:add方法 addlast 方法与 add 方法合并版get 方法测试&#x…...
Day-05 CentOS7.5 安装docker
参考 : Install Docker Engine on CentOS | Docker DocsLearn how to install Docker Engine on CentOS. These instructions cover the different installation methods, how to uninstall, and next steps.https://docs.docker.com/engine/install/centos/ Doc…...
Makefile
目录 Makefile Makefile格式 Makefile函数:foreach和wildcard $(foreach var,list,text) $(wildcard pattern) 完善Makefile Makefile 在Linux中使用make工具来编译程序(特别是大程序),而make工具所执行的动作依赖于Makefil…...
c语言练习77:公因⼦的数⽬
公因⼦的数⽬ 题⽬描述: 给你两个正整数 a 和 b ,返回 a 和 b 的公因⼦的数⽬。 如果 x 可以同时整除 a 和 b ,则认为 x 是 a 和 b 的⼀个公因⼦ 。 • ⽰例 1: 输⼊:a 12, b 6 输出:4 解释&#…...
【C++】C++11——右值引用和移动语义、左值引用和右值引用、右值引用使用场景和意义、完美转发、新的类功能
文章目录 C115.右值引用和移动语义5.1左值引用和右值引用5.2左值引用与右值引用比较5.3右值引用使用场景和意义5.4右值引用引用左值及其一些更深入的使用场景分析5.5完美转发 6.新的类功能 C11 5.右值引用和移动语义 右值引用是C11引入的一个新特性,用于支持移动语义…...
Spring Boot的创建和使用(JavaEE进阶系列2)
目录 前言: 1.什么是Spring Boot?为什么要学习Spring Boot? 2.Spring Boot优点 3.创建Spring Boot项目 3.1准备工作 3.2Spring Boot创建 3.2.1通过idea的方式创建 3.2.2通过网页创建 4.Spring Boot中的配置文件 4.1Spring Boot配置…...
php可以开发动态网站/百度网盘网页版入口
[]查看原图 你不可不知的50个艺术知识 抽象画很难被人理解,要把生僻的作品形象化,比如涉及波洛克的绘画,就可以讲述他富有传奇色彩的经历。 【一团乱麻】 作品简介:1948年1月,波洛克首次展出了他的行动绘画。他极端创新…...
厦门外贸网站找谁/网站平台搭建
htmlxamlfont标签存在单独font标签只是其他标签属性字体加粗<b></b>FontWeight"Bold"背景bgcolor"aliceblue"Background"AliceBlue"对齐align"center" HorizontalAlignment"Center" VerticalAlignment"Cen…...
大宗商品交易平台上市公司/seo是什么意思广东话
引言 前面已经学习了celeryredis的异步和定时任务,下面介绍如何结合django来使用。 环境配置 在动手之前,一定要准备好的是环境,celery版本有很多,在使用过程中如何版本与django和redis版本不配套,将会很麻烦。 我这里…...
如何做网站方案/成人本科
简介: 主要逻辑在java实现,h5只有一个a标签调用接口。 后台分两个接口: 接口1:登录前缀接口,h5负责调用唤醒QQ授权。 接口2:需要在腾讯开放平台配置回调地址的接口,就是授权成功后回调。 j…...
电子商务网站建设的结论/淘宝指数查询官网
1、按本站或各区域城市组织各位妈妈们在网站以宝宝照片经验交流形式SHOW自己可爱的宝宝,可按月、季、年设擂台,当然各擂主要设奖品:),奖品以后可由各赞助商提供; 2、宝宝SHOW分类: 2.1、【Bab…...
网站开发学哪种语言/今日最新消息新闻报道
用CSS的expression判断表达式设置input样式,简单,轻量级。缺点在于expression判断表达式FireFox是不支持的。致命的是只能区分出一个(例如例子中就只能区分出text文本框),不要试图设置多个…代码:复制代码代码如下:www.52CSS.comi…...