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

Redis+Caffeine 实现两级缓存实战

Redis+Caffeine 实现两级缓存

背景

​ 事情的开始是这样的,前段时间接了个需求,给公司的商城官网提供一个查询预计送达时间的接口。接口很简单,根据请求传的城市+仓库+发货时间查询快递的预计送达时间。因为商城下单就会调用这个接口,所以对接口的性能要求还是挺高的,据老员工的说法是特别是大促的时候,访问量还是比较大的。

​ 因为数据量不是很大,每天会全量推今天和明天的预计送达时间到MySQL,总数据量大约7k+。每次推完数据后会把数据全量写入到redis中,做一个缓存预热,然后设置过期时间为1天。

​ 鉴于之前Redis集群出现过压力过大查询缓慢的情况,进一步保证接口的高性能和高可用,防止redis出现压力大,查询慢,缓存雪崩,缓存穿透等问题,我们最终采用了Reids + Caffeine两级缓存的策略。

本地缓存优缺点

优点:

  1. 本地缓存,基于本地内存,查询速度是很快的。适用于:实时性要求不高,更新频率不高等场景。(我们的数据每天凌晨更新一次,总量7k左右)
  2. 查询本地缓存与查询远程缓存相比可以减少网络的I/O,降低网络上的一些消耗。(我们的redis之前出现过查询缓慢的情况)

缺点:

  1. Caffeine既然是本地缓存,在分布式环境的情况下就要考虑各个节点之间缓存的一致性问题,一个节点的本地缓存更新了,怎么可以同步到其他的节点。
  2. Caffeine不支持持久化的存储。
  3. Caffeine使用本地内存,需要合理设置大小,避免内存溢出。

流程图

在这里插入图片描述

代码实现

MySQL表

CREATE TABLE `t_estimated_arrival_date`  (`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键id',`warehouse_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '货仓id',`warehouse` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '发货仓',`city` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '签收城市',`delivery_date` date NULL DEFAULT NULL COMMENT '发货时间',`estimated_arrival_date` date NULL DEFAULT NULL COMMENT '预计到货日期',PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `uk_warehouse_id_city_delivery_date`(`warehouse_id`, `city`, `delivery_date`) USING BTREE
) ENGINE = InnoDB  COMMENT = '预计到货时间表(具体到day:T, T+1,近90天到货时间众数)' ROW_FORMAT = Dynamic;INSERT INTO `t_estimated_arrival_date` VALUES (9, '6', '湖熟正常仓', '兰州市', '2024-07-08', '2024-07-10');
INSERT INTO `t_estimated_arrival_date` VALUES (10, '6', '湖熟正常仓', '兰州市', '2024-07-09', '2024-07-11');
INSERT INTO `t_estimated_arrival_date` VALUES (11, '6', '湖熟正常仓', '兴安盟', '2024-07-08', '2024-07-11');
INSERT INTO `t_estimated_arrival_date` VALUES (12, '6', '湖熟正常仓', '兴安盟', '2024-07-09', '2024-07-12');
INSERT INTO `t_estimated_arrival_date` VALUES (13, '6', '湖熟正常仓', '其他', '2024-07-08', '2024-07-19');
INSERT INTO `t_estimated_arrival_date` VALUES (14, '6', '湖熟正常仓', '其他', '2024-07-09', '2024-07-20');
INSERT INTO `t_estimated_arrival_date` VALUES (15, '6', '湖熟正常仓', '内江市', '2024-07-08', '2024-07-10');
INSERT INTO `t_estimated_arrival_date` VALUES (16, '6', '湖熟正常仓', '内江市', '2024-07-09', '2024-07-11');
INSERT INTO `t_estimated_arrival_date` VALUES (17, '6', '湖熟正常仓', '凉山彝族自治州', '2024-07-08', '2024-07-11');
INSERT INTO `t_estimated_arrival_date` VALUES (18, '6', '湖熟正常仓', '凉山彝族自治州', '2024-07-09', '2024-07-12');
INSERT INTO `t_estimated_arrival_date` VALUES (19, '6', '湖熟正常仓', '包头市', '2024-07-08', '2024-07-11');
INSERT INTO `t_estimated_arrival_date` VALUES (20, '6', '湖熟正常仓', '包头市', '2024-07-09', '2024-07-12');
INSERT INTO `t_estimated_arrival_date` VALUES (21, '6', '湖熟正常仓', '北京城区', '2024-07-08', '2024-07-10');
INSERT INTO `t_estimated_arrival_date` VALUES (22, '6', '湖熟正常仓', '北京城区', '2024-07-09', '2024-07-11');

pom.xm

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--redis连接池--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.9.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.1</version></dependency>

application.yml

server:port: 9001
spring:application:name: springboot-redisdatasource:name: demourl: jdbc:mysql://localhost:3306/test?userUnicode=true&&characterEncoding=utf8&allowMultiQueries=true&useSSL=falsedriver-class-name: com.mysql.cj.jdbc.Driverusername: password: # mybatis相关配置mybatis-plus:mapper-locations: classpath:mapper/*.xmlconfiguration:cache-enabled: trueuse-generated-keys: truedefault-executor-type: REUSEuse-actual-param-name: true# 打印日志#    log-impl: org.apache.ibatis.logging.stdout.StdOutImplredis:host: 192.168.117.73port: 6379password: root
#  redis:
#    lettuce:
#      cluster:
#        refresh:
#          adaptive: true
#          period: 10S
#      pool:
#        max-idle: 50
#        min-idle: 8
#        max-active: 100
#        max-wait: -1
#    timeout: 100000
#    cluster:
#      nodes:
#        - 192.168.117.73:6379
logging:level:com.itender.redis.mapper: debug

配置类

  • RedisConfig
/*** @author yuanhewei* @date 2024/5/31 16:18* @description*/
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(connectionFactory);Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper mapper = new ObjectMapper();mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);serializer.setObjectMapper(mapper);// 如果不序列化在key value 使用redis客户端工具 直连redis服务器 查看数据时 前面会有一个 \xac\xed\x00\x05t\x00\x05 字符串// StringRedisSerializer 来序列化和反序列化 String 类型 redis 的 key valueredisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(serializer);// StringRedisSerializer 来序列化和反序列化 hash 类型 redis 的 key valueredisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(serializer);redisTemplate.afterPropertiesSet();return redisTemplate;}
}
  • CaffeineConfig
/*** @author yuanhewei* @date 2024/7/9 14:16* @description*/
@Configuration
public class CaffeineConfig {/*** Caffeine 配置类*  initialCapacity:初始缓存空间大小*  maximumSize:缓存的最大数量,设置这个值避免内存溢出*  expireAfterWrite:指定缓存的过期时间,是最后一次写操作的一个时间*  容量的大小要根据自己的实际应用场景设置** @return*/@Beanpublic Cache<String, Object> caffeineCache() {return Caffeine.newBuilder()// 初始大小.initialCapacity(128)//最大数量.maximumSize(1024)//过期时间.expireAfterWrite(60, TimeUnit.SECONDS).build();}@Beanpublic CacheManager cacheManager(){CaffeineCacheManager cacheManager=new CaffeineCacheManager();cacheManager.setCaffeine(Caffeine.newBuilder().initialCapacity(128).maximumSize(1024).expireAfterWrite(60, TimeUnit.SECONDS));return cacheManager;}
}

Mapper

这里采用了Mybatis Plus

/*** @author yuanhewei* @date 2024/7/9 18:11* @description*/
@Mapper
public interface EstimatedArrivalDateMapper extends BaseMapper<EstimatedArrivalDateEntity> {} 

Service

/*** @author yuanhewei* @date 2024/7/9 14:25* @description*/
public interface DoubleCacheService {/*** 查询一级送达时间-常规方式** @param request* @return*/EstimatedArrivalDateEntity getEstimatedArrivalDateCommon(EstimatedArrivalDateEntity request);/*** 查询一级送达时间-注解方式** @param request* @return*/EstimatedArrivalDateEntity getEstimatedArrivalDate(EstimatedArrivalDateEntity request);
}

实现类

/*** @author yuanhewei* @date 2024/7/9 14:26* @description*/
@Slf4j
@Service
public class DoubleCacheServiceImpl implements DoubleCacheService {@Resourceprivate Cache<String, Object> caffeineCache;@Resourceprivate RedisTemplate<String, Object> redisTemplate;@Resourceprivate EstimatedArrivalDateMapper estimatedArrivalDateMapper;@Overridepublic EstimatedArrivalDateEntity getEstimatedArrivalDateCommon(EstimatedArrivalDateEntity request) {String key = request.getDeliveryDate() + RedisConstants.COLON + request.getWarehouseId() + RedisConstants.COLON + request.getCity();log.info("Cache key: {}", key);Object value = caffeineCache.getIfPresent(key);if (Objects.nonNull(value)) {log.info("get from caffeine");return EstimatedArrivalDateEntity.builder().estimatedArrivalDate(value.toString()).build();}value = redisTemplate.opsForValue().get(key);if (Objects.nonNull(value)) {log.info("get from redis");caffeineCache.put(key, value);return EstimatedArrivalDateEntity.builder().estimatedArrivalDate(value.toString()).build();}log.info("get from mysql");DateTime deliveryDate = DateUtil.parse(request.getDeliveryDate(), "yyyy-MM-dd");EstimatedArrivalDateEntity estimatedArrivalDateEntity = estimatedArrivalDateMapper.selectOne(new QueryWrapper<EstimatedArrivalDateEntity>().eq("delivery_date", deliveryDate).eq("warehouse_id", request.getWarehouseId()).eq("city", request.getCity()));redisTemplate.opsForValue().set(key, estimatedArrivalDateEntity.getEstimatedArrivalDate(), 120, TimeUnit.SECONDS);caffeineCache.put(key, estimatedArrivalDateEntity.getEstimatedArrivalDate());return EstimatedArrivalDateEntity.builder().estimatedArrivalDate(estimatedArrivalDateEntity.getEstimatedArrivalDate()).build();}@DoubleCache(cacheName = "estimatedArrivalDate", key = {"#request.deliveryDate", "#request.warehouseId", "#request.city"},type = DoubleCache.CacheType.FULL)@Overridepublic EstimatedArrivalDateEntity getEstimatedArrivalDate(EstimatedArrivalDateEntity request) {DateTime deliveryDate = DateUtil.parse(request.getDeliveryDate(), "yyyy-MM-dd");EstimatedArrivalDateEntity estimatedArrivalDateEntity = estimatedArrivalDateMapper.selectOne(new QueryWrapper<EstimatedArrivalDateEntity>().eq("delivery_date", deliveryDate).eq("warehouse_id", request.getWarehouseId()).eq("city", request.getCity()));return EstimatedArrivalDateEntity.builder().estimatedArrivalDate(estimatedArrivalDateEntity.getEstimatedArrivalDate()).build();}
}

这里的代码本来是采用了常规的写法,没有采用自定义注解的方式,注解的方式是参考了后面那位大佬的文章,加以修改实现的。因为我的CacheKey可能存在多个属性值的组合。

Annotitions

/*** @author yuanhewei* @date 2024/7/9 14:51* @description*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DoubleCache {/*** 缓存名称** @return*/String cacheName();/*** 缓存的key,支持springEL表达式** @return*/String[] key();/*** 过期时间,单位:秒** @return*/long expireTime() default 120;/*** 缓存类型** @return*/CacheType type() default CacheType.FULL;enum CacheType {/*** 存取*/FULL,/*** 只存*/PUT,/*** 删除*/DELETE}
}

Aspect

/*** @author yuanhewei* @date 2024/7/9 14:51* @description*/
@Slf4j
@Component
@Aspect
public class DoubleCacheAspect {@Resourceprivate Cache<String, Object> caffeineCache;@Resourceprivate RedisTemplate<String, Object> redisTemplate;@Pointcut("@annotation(com.itender.redis.annotation.DoubleCache)")public void doubleCachePointcut() {}@Around("doubleCachePointcut()")public Object doAround(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();// 拼接解析springEl表达式的mapString[] paramNames = signature.getParameterNames();Object[] args = point.getArgs();TreeMap<String, Object> treeMap = new TreeMap<>();for (int i = 0; i < paramNames.length; i++) {treeMap.put(paramNames[i], args[i]);}DoubleCache annotation = method.getAnnotation(DoubleCache.class);String elResult = DoubleCacheUtil.arrayParse(Lists.newArrayList(annotation.key()), treeMap);String realKey = annotation.cacheName() + RedisConstants.COLON + elResult;// 强制更新if (annotation.type() == DoubleCache.CacheType.PUT) {Object object = point.proceed();redisTemplate.opsForValue().set(realKey, object, annotation.expireTime(), TimeUnit.SECONDS);caffeineCache.put(realKey, object);return object;}// 删除else if (annotation.type() == DoubleCache.CacheType.DELETE) {redisTemplate.delete(realKey);caffeineCache.invalidate(realKey);return point.proceed();}// 读写,查询CaffeineObject caffeineCacheObj = caffeineCache.getIfPresent(realKey);if (Objects.nonNull(caffeineCacheObj)) {log.info("get data from caffeine");return caffeineCacheObj;}// 查询RedisObject redisCache = redisTemplate.opsForValue().get(realKey);if (Objects.nonNull(redisCache)) {log.info("get data from redis");caffeineCache.put(realKey, redisCache);return redisCache;}log.info("get data from database");Object object = point.proceed();if (Objects.nonNull(object)) {// 写入Redislog.info("get data from database write to cache: {}", object);redisTemplate.opsForValue().set(realKey, object, annotation.expireTime(), TimeUnit.SECONDS);// 写入CaffeinecaffeineCache.put(realKey, object);}return object;}
}

因为注解上的配置要支持Spring的EL表达式。

public static String parse(String elString, SortedMap<String, Object> map) {elString = String.format("#{%s}", elString);// 创建表达式解析器ExpressionParser parser = new SpelExpressionParser();// 通过evaluationContext.setVariable可以在上下文中设定变量。EvaluationContext context = new StandardEvaluationContext();map.forEach(context::setVariable);// 解析表达式Expression expression = parser.parseExpression(elString, new TemplateParserContext());// 使用Expression.getValue()获取表达式的值,这里传入了Evaluation上下文return expression.getValue(context, String.class);}public static String arrayParse(List<String> elStrings, SortedMap<String, Object> map) {List<String> result = Lists.newArrayList();elStrings.forEach(elString -> {elString = String.format("#{%s}", elString);// 创建表达式解析器ExpressionParser parser = new SpelExpressionParser();// 通过evaluationContext.setVariable可以在上下文中设定变量。EvaluationContext context = new StandardEvaluationContext();map.forEach(context::setVariable);// 解析表达式Expression expression = parser.parseExpression(elString, new TemplateParserContext());// 使用Expression.getValue()获取表达式的值,这里传入了Evaluation上下文result.add(expression.getValue(context, String.class));});return String.join(RedisConstants.COLON, result);}

Controller

/*** @author yuanhewei* @date 2024/7/9 14:14* @description*/
@RestController
@RequestMapping("/doubleCache")
public class DoubleCacheController {@Resourceprivate DoubleCacheService doubleCacheService;@PostMapping("/common")public EstimatedArrivalDateEntity getEstimatedArrivalDateCommon(@RequestBody EstimatedArrivalDateEntity estimatedArrivalDate) {return doubleCacheService.getEstimatedArrivalDateCommon(estimatedArrivalDate);}@PostMapping("/annotation")public EstimatedArrivalDateEntity getEstimatedArrivalDate(@RequestBody EstimatedArrivalDateEntity estimatedArrivalDate) {return doubleCacheService.getEstimatedArrivalDate(estimatedArrivalDate);}
}

代码中演示了Redis + Caffeine实现两级缓存的方式,一种是传统常规的方式,另一种是基于注解的方式实现的。具体实现可以根据自己项目中的实际场景。

最后的测试结果也是两种方式都可以实现查询先走一级缓存;一级缓存不存在查询二级缓存,然后写入一级缓存;二级缓存不存在,查询MySQL然后写入二级缓存,再写入一级缓存的目的。测试结果就不贴出来了

总结

本文介绍Redis+Caffeine实现两级缓存的方式。一种是常规的方式,一种的基于注解的方式。具体的实现可根据自己项目中的业务场景。

至于为什么要用Redis+Caffeine的方式,文章也提到了,目前我们Redis集群压力还算挺大的,而且接口对RT的要求也是比较高的。有一点好的就是我们的数据是每天全量推一边,总量也不大,实时性要求也不强。所以就很适合本地缓存的方式。

使用本地缓存也要注意设置容量的大小和过期时间,否则容易出现内存溢出。

其实现实中很多的场景直接使用Redis就可以搞定的,没必要硬要使用Caffeine。这里也只是简单的介绍了最简单基础的实现方式。对于其他一些复杂的场景还要根据自己具体的业务进行设计。我自己也是边学边用。如果有问题或者其他好的实现方式欢迎各位大佬评论,一起进步!!!

参考

https://blog.csdn.net/weixin_45334346/article/details/136310010

相关文章:

Redis+Caffeine 实现两级缓存实战

RedisCaffeine 实现两级缓存 背景 ​ 事情的开始是这样的&#xff0c;前段时间接了个需求&#xff0c;给公司的商城官网提供一个查询预计送达时间的接口。接口很简单&#xff0c;根据请求传的城市仓库发货时间查询快递的预计送达时间。因为商城下单就会调用这个接口&#xff…...

SpringBoot:SpringBoot中如何实现对Http接口进行监控

一、前言 Spring Boot Actuator是Spring Boot提供的一个模块&#xff0c;用于监控和管理Spring Boot应用程序的运行时信息。它提供了一组监控端点&#xff08;endpoints&#xff09;&#xff0c;用于获取应用程序的健康状态、性能指标、配置信息等&#xff0c;并支持通过 HTTP …...

STM32-I2C硬件外设

本博文建议与我上一篇I2C 通信协议​​​​​​共同理解 合成一套关于I2C软硬件体系 STM32内部集成了硬件I2C收发电路&#xff0c;可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能&#xff0c;减轻CPU的负担 特点&#xff1a; 多主机功能&#x…...

暑假第一次作业

第一步&#xff1a;给R1,R2,R3,R4配IP [R1-GigabitEthernet0/0/0]ip address 192.168.1.1 24 [R1-Serial4/0/0]ip address 15.0.0.1 24 [R2-GigabitEthernet0/0/0]ip address 192.168.2.1 24 [R2-Serial4/0/0]ip address 25.0.0.1 24 [R3-GigabitEthernet0/0/0]ip address 192.…...

【算法专题】快速排序

1. 颜色分类 75. 颜色分类 - 力扣&#xff08;LeetCode&#xff09; 依据题意&#xff0c;我们需要把只包含0、1、2的数组划分为三个部分&#xff0c;事实上&#xff0c;在我们前面学习过的【算法专题】双指针算法-CSDN博客中&#xff0c;有一道题叫做移动零&#xff0c;题目要…...

debian 12 PXE Server 批量部署系统

pxe server 前言 PXE&#xff08;Preboot eXecution Environment&#xff0c;预启动执行环境&#xff09;是一种网络启动协议&#xff0c;允许计算机通过网络启动而不是使用本地硬盘。PXE服务器是实现这一功能的服务器&#xff0c;它提供了启动镜像和引导加载程序&#xff0c;…...

【Pytorch】RNN for Image Classification

文章目录 1 RNN 的定义2 RNN 输入 input, h_03 RNN 输出 output, h_n4 多层5 小试牛刀 学习参考来自 pytorch中nn.RNN()总结RNN for Image Classification(RNN图片分类–MNIST数据集)pytorch使用-nn.RNNBuilding RNNs is Fun with PyTorch and Google Colab 1 RNN 的定义 nn.…...

基于Java的飞机大战游戏的设计与实现论文

点击下载源码 基于Java的飞机大战游戏的设计与实现 摘 要 现如今&#xff0c;随着智能手机的兴起与普及&#xff0c;加上4G&#xff08;the 4th Generation mobile communication &#xff0c;第四代移动通信技术&#xff09;网络的深入&#xff0c;越来越多的IT行业开始向手机…...

初识影刀:EXCEL根据部门筛选低值易耗品

第一次知道这个办公自动化的软件还是在招聘网站上&#xff0c;了解之后发现对于办公中重复性的工作还是挺有帮助的&#xff0c;特别是那些操作非EXCEL的重复性工作&#xff0c;当然用在EXCEL上更加方便&#xff0c;有些操作比写VBA便捷。 下面就是一个了解基本操作后&#xff…...

nginx的四层负载均衡实战

目录 1 环境准备 1.1 mysql 部署 1.2 nginx 部署 1.3 关闭防火墙和selinux 2 nginx配置 2.1 修改nginx主配置文件 2.2 创建stream配置文件 2.3 重启nginx 3 测试四层代理是否轮循成功 3.1 远程链接通过代理服务器访问 3.2 动图演示 4 四层反向代理算法介绍 4.1 轮询&#xff0…...

中职网络安全B模块Cenots6.8数据库

任务环境说明&#xff1a; ✓ 服务器场景&#xff1a;CentOS6.8&#xff08;开放链接&#xff09; ✓ 用户名&#xff1a;root&#xff1b;密码&#xff1a;123456 进入虚拟机操作系统&#xff1a;CentOS 6.8&#xff0c;登陆数据库&#xff08;用户名&#xff1a;root&#x…...

BGP笔记的基本概要

技术背景&#xff1a; 在只有IGP&#xff08;诸如OSPF、IS-IS、RIP等协议&#xff0c;因为最初是被设计在一个单域中进行一个路由操纵&#xff0c;因此被统一称为Interior Gateway Protocol&#xff0c;内部网关协议&#xff09;的时代&#xff0c;域间路由无法实现一个全局路由…...

【Redis】复制(Replica)

文章目录 一、复制是什么&#xff1f;二、 基本命令三、 配置&#xff08;分为配置文件和命令配置&#xff09;3.1 配置文件3.2 命令配置3.3 嵌套连接3.4 关闭从属关系 四、 复制原理五、 缺点 以下是本篇文章正文内容 一、复制是什么&#xff1f; 主从复制 master&#xff…...

封装了一个仿照抖音效果的iOS评论弹窗

需求背景 开发一个类似抖音评论弹窗交互效果的弹窗&#xff0c;支持滑动消失&#xff0c; 滑动查看评论 效果如下图 思路 创建一个视图&#xff0c;该视图上面放置一个tableView, 该视图上添加一个滑动手势&#xff0c;同时设置代理&#xff0c;实现代理方法 (BOOL)gestur…...

【JavaWeb程序设计】Servlet(二)

目录 一、改进上一篇博客Servlet&#xff08;一&#xff09;的第一题 1. 运行截图 2. 建表 3. 实体类 4. JSP页面 4.1 login.jsp 4.2 loginSuccess.jsp 4.3 loginFail.jsp 5. mybatis-config.xml 6. 工具类&#xff1a;创建SqlSessionFactory实例&#xff0c;进行 My…...

php探针

php探针是用来探测空间、服务器运行状况和PHP信息用的&#xff0c;探针可以实时查看服务器硬盘资源、内存占用、网卡流量、系统负载、服务器时间等信息。 下面就分享下我是怎样利用php探针来探测服务器网站空间速度、性能、安全功能等。 具体步骤如下&#xff1a; 1.从网上下…...

泰勒级数 (Taylor Series) 动画展示 包括源码

泰勒级数 (Taylor Series) 动画展示 包括源码 flyfish 泰勒级数&#xff08;英语&#xff1a;Taylor series&#xff09;用无限项连加式 - 级数来表示一个函数&#xff0c;这些相加的项由函数在某一点的导数求得。 定义了一个函数f(x)表示要近似的函数 sin ⁡ ( x ) \sin(x) …...

蔚来汽车:拥抱TiDB,实现数据库性能与稳定性的飞跃

作者&#xff1a; Billdi表弟 原文来源&#xff1a; https://tidb.net/blog/449c3f5b 演讲嘉宾&#xff1a;吴记 蔚来汽车Tidb爱好者 整理编辑&#xff1a;黄漫绅&#xff08;表妹&#xff09;、李仲舒、吴记 本文来自 TiDB 社区合肥站走进蔚来汽车——来自吴记老师的演讲…...

【Django+Vue3 线上教育平台项目实战】构建高效线上教育平台之首页模块

文章目录 前言一、导航功能实现a.效果图&#xff1a;b.后端代码c.前端代码 二、轮播图功能实现a.效果图b.后端代码c.前端代码 三、标签栏功能实现a.效果图b.后端代码c.前端代码 四、侧边栏功能实现1.整体效果图2.侧边栏功能实现a.效果图b.后端代码c.前端代码 3.侧边栏展示分类及…...

对比 UUIDv1 和 UUIDv6

UUIDv6是UUIDv1的字段兼容版本&#xff0c;重新排序以改善数据库局部性。UUIDv6主要在使用UUIDv1的上下文中实现。不涉及遗留UUIDv1的系统应该改用UUIDv7。 与 UUIDv1 将时间戳分割成低、中、高三个部分不同&#xff0c;UUIDv6 改变了这一序列&#xff0c;使时间戳字节从最重要…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关

在水泥厂的生产流程中&#xff0c;工业自动化网关起着至关重要的作用&#xff0c;尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关&#xff0c;为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多&#xff0c;其中不少设备采用Devicenet协议。Devicen…...

HTTPS证书一年多少钱?

HTTPS证书作为保障网站数据传输安全的重要工具&#xff0c;成为众多网站运营者的必备选择。然而&#xff0c;面对市场上种类繁多的HTTPS证书&#xff0c;其一年费用究竟是多少&#xff0c;又受哪些因素影响呢&#xff1f; 首先&#xff0c;HTTPS证书通常在PinTrust这样的专业平…...

高抗扰度汽车光耦合器的特性

晶台光电推出的125℃光耦合器系列产品&#xff08;包括KL357NU、KL3H7U和KL817U&#xff09;&#xff0c;专为高温环境下的汽车应用设计&#xff0c;具备以下核心优势和技术特点&#xff1a; 一、技术特性分析 高温稳定性 采用先进的LED技术和优化的IC设计&#xff0c;确保在…...

【记录坑点问题】IDEA运行:maven-resources-production:XX: OOM: Java heap space

问题&#xff1a;IDEA出现maven-resources-production:operation-service: java.lang.OutOfMemoryError: Java heap space 解决方案&#xff1a;将编译的堆内存增加一点 位置&#xff1a;设置setting-》构建菜单build-》编译器Complier...

【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器

从本章节开始&#xff0c;进入到函数有多个参数的情况&#xff0c;前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参&#xff0c;ECX是整型的第一个参数的寄存器&#xff0c;那么多个参数的情况下函数如何传参&#xff0c;下面展开介绍参数为整型时候的几种情…...