redis常见问题及解决方案

缓存预热

定义

缓存预热是一种优化方案,它可以提高用户的使用体验。
缓存预热是指在系统启动的时候,先把查询结果预存到缓存中,以便用户后面查询时可以直接从缓存中读取,节省用户等待时间

实现思路

  • 把需要缓存的方法写在初始化方法中,让程序启动时自动加载数据并缓存数据。
  • 把需要缓存的方法挂在某个页面或是后端接口上,手动触发缓存预热。
  • 设置定时任务,定时进行缓存预热。

解决方案

使用 @PostConstruct 初始化白名单数据

缓存雪崩(大量数据同时失效/Redis 崩了,没有数据了)

定义

缓存雪崩是指在短时间内大量缓存同时过期,导致大量请求直接查询数据库, 从而对数据库造成巨大压力,严重情况下可能会导致数据库宕机

解决方案

  • 加锁排队:起到缓冲作用,防止大量请求同时操作数据库,但缺点是增加了系统的响应时间,降低了系统的吞吐量,牺牲一部分用户体验。
  • 设置二级缓存:二级缓存是除了 Redis 本身的缓存,再设置一层缓存,当 Redis 失效后,就先去查询二级缓存
  • 随机化过期时间:为了避免缓存同时过期,可以设置缓存时添加随机时间,这样就可以极大的避免大量缓存同时失效
  • redis 缓存集群实现高可用
    • 主从 + 哨兵
    • Redis 集群
    • 开启Redis 持久化机制 aof / rdb,尽快恢复缓存集群
  • 服务降级
    • Hystrix 或者 sentinel 限流 & 降级
// 缓存原本的失效时间
int exTime = 10 * 60;
// 随机数⽣成类
Random random = new Random();
// 缓存设置
jedis.setex(cacheKey, exTime+random.nextInt(1000) , value);

缓存穿透(黑客攻击/空数据/穿过 Redis 和数据库)

定义

  • 缓存穿透是指查询数据库和缓存都无数据,因此每次请求都会去查询数据库

解决方案

  • **缓存空结果:**对查询的空结果也进行缓存,如果是集合,可以缓存一个空的的集合,如果是缓存单个对象,可以字段标识来区分,避免请求穿透到数据库。
  • **布隆过滤器处理:**将所有可能对应的数据为空的 key 进行统一的存放,并在请求前做拦截,避免请求穿透到数据库(这样的方式实现起来相对麻烦,比较适合命中不高,但是更新不频繁的数据)。
  • 双锁锁策略机制
package com.redis.redis01.service;import com.redis.redis01.bean.RedisBs;
import com.redis.redis01.mapper.RedisBsMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.beans.Transient;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;@Slf4j
@Service
public class RedisBsService {//定义key前缀/命名空间public static final String CACHE_KEY_USER = "user:";@Autowiredprivate RedisBsMapper mapper;@Resourceprivate RedisTemplate<String, Object> redisTemplate;private static ReentrantLock lock = new ReentrantLock();/*** 业务逻辑没有写错,对于中小长(qps<=1000)可以使用,但是大厂不行:大长需要采用双检加锁策略** @param id* @return*/@Transactionalpublic RedisBs findUserById(Integer id,int type,int qps) {//qps<=1000if(qps<=1000){return qpsSmall1000(id);}//qps>1000return qpsBig1000(id, type);}/*** 加强补充,避免突然key失效了,或者不存在的key穿透redis打爆mysql,做一下预防,尽量不出现缓存击穿的情况,进行排队等候* @param id* @param type 0使用synchronized重锁,1ReentrantLock轻量锁* @return*/private RedisBs qpsBig1000(Integer id, int type) {RedisBs redisBs = null;String key = CACHE_KEY_USER + id;//1先从redis里面查询,如果有直接返回,没有再去查mysqlredisBs = (RedisBs) redisTemplate.opsForValue().get(key);if (null == redisBs) {switch (type) {case 0://加锁,假设请求量很大,缓存过期,大厂用,对于高qps的优化,进行加锁保证一个请求操作,让外面的redis等待一下,避免击穿mysqlsynchronized (RedisBsService.class) {//第二次查询缓存目的防止加锁之前刚好被其他线程缓存了redisBs = (RedisBs) redisTemplate.opsForValue().get(key);if (null != redisBs) {//查询到数据直接返回return redisBs;} else {//数据缓存//查询mysql,回写到redis中redisBs = mapper.findUserById(id);if (null == redisBs) {// 3 redis+mysql都没有数据,防止多次穿透(redis为防弹衣,mysql为人,穿透直接伤人,就是直接访问mysql),优化:记录这个null值的key,列入黑名单或者记录或者异常return new RedisBs(-1, "当前值已经列入黑名单");}//4 mysql有,回写保证数据一致性//setifabsentredisTemplate.opsForValue().setIfAbsent(key, redisBs,7l, TimeUnit.DAYS);}}break;case 1://加锁,大厂用,对于高qps的优化,进行加锁保证一个请求操作,让外面的redis等待一下,避免击穿mysqllock.lock();try {//第二次查询缓存目的防止加锁之前刚好被其他线程缓存了redisBs = (RedisBs) redisTemplate.opsForValue().get(key);if (null != redisBs) {//查询到数据直接返回return redisBs;} else {//数据缓存//查询mysql,回写到redis中redisBs = mapper.findUserById(id);if (null == redisBs) {// 3 redis+mysql都没有数据,防止多次穿透(redis为防弹衣,mysql为人,穿透直接伤人,就是直接访问mysql),优化:记录这个null值的key,列入黑名单或者记录或者异常return new RedisBs(-1, "当前值已经列入黑名单");}//4 mysql有,回写保证数据一致性redisTemplate.opsForValue().set(key, redisBs);}} catch (Exception e) {e.printStackTrace();} finally {//解锁lock.unlock();}}}return redisBs;}private RedisBs qpsSmall1000(Integer id) {RedisBs redisBs = null;String key = CACHE_KEY_USER + id;//1先从redis里面查询,如果有直接返回,没有再去查mysqlredisBs = (RedisBs) redisTemplate.opsForValue().get(key);if (null == redisBs) {//2查询mysql,回写到redis中redisBs = mapper.findUserById(id);if (null == redisBs) {// 3 redis+mysql都没有数据,防止多次穿透(redis为防弹衣,mysql为人,穿透直接伤人,就是直接访问mysql),优化:记录这个null值的key,列入黑名单或者记录或者异常return new RedisBs(-1, "当前值已经列入黑名单");}//4 mysql有,回写保证数据一致性redisTemplate.opsForValue().set(key, redisBs);}return redisBs;}}
package com.redis.redis01.service;import com.google.common.collect.Lists;
import com.redis.redis01.bean.RedisBs;
import com.redis.redis01.mapper.RedisBsMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;@Slf4j
@Service
public class BitmapService {@Resourceprivate RedisTemplate<String, Object> redisTemplate;private static ReentrantLock lock = new ReentrantLock();@Autowiredprivate RedisBsMapper redisBsMapper;/*** 场景一:布隆过滤器解决缓存穿透问题(null/黑客攻击);利用redis+bitmap实现* 有可能有,没有一定没有*                                                    无-------------》mysql查询*                     有--------》redis查询----------》有-----------》返回* 请求-----》布隆过滤器-----------》*                      无-------终止** @param type:0初始化,1常规查询*/public void booleanFilterBitmap(int type, Integer id) {switch (type) {case 0://初始化数据for (int i = 0; i < 10; i++) {RedisBs initBs = RedisBs.builder().id(i).name("赵三" + i).phone("1580080569" + i).build();//1 插入数据库redisBsMapper.insert(initBs);//2 插入redisredisTemplate.opsForValue().set("customer:info" + i, initBs);}//3 将用户id插入布隆过滤器中,作为白名单for (int i = 0; i < 10; i++) {String booleanKey = "customer:booleanFilter:" + i;//3.1 计算hashvalueint abs = Math.abs(booleanKey.hashCode());//3.2 通过abs和2的32次方取余,获得布隆过滤器/bitmap对应的下标坑位/indexlong index = (long) (abs % Math.pow(2, 32));log.info("坑位:{}", index);//3.3 设置redis里面的bitmap对应类型的白名单redisTemplate.opsForValue().setBit("whiteListCustomer", index, true);}break;case 1://常规查询//1 获取当前传过来的id对应的哈希值String inputBooleanKey = "customer:booleanFilter:" + id;int abs = Math.abs(inputBooleanKey.hashCode());long index = (long) (abs % Math.pow(2, 32));Boolean whiteListCustomer = redisTemplate.opsForValue().getBit("whiteListCustomer", index);//加入双检锁//加锁,大厂用,对于高qps的优化,进行加锁保证一个请求操作,让外面的redis等待一下,避免击穿mysqllock.lock();try {if (null == whiteListCustomer) {whiteListCustomer = redisTemplate.opsForValue().getBit("whiteListCustomer", index);if (null != whiteListCustomer && whiteListCustomer) {//布隆过滤器中存在,则可能存在//2 查找redisObject queryCustomer = redisTemplate.opsForValue().get("customer:info" + id);if (null != queryCustomer) {log.info("返回客户信息:{}", queryCustomer);break;} else {//3 redis没有查找mysqlRedisBs userById = redisBsMapper.findUserById(id);if (null != userById) {log.info("返回客户信息:{}", queryCustomer);redisTemplate.opsForValue().set("customer:info" + id, userById);break;} else {log.info("当前客户信息不存在:{}", id);break;}}} else {//redis没有,去mysql中查询//3 redis没有查找mysqlRedisBs userById = redisBsMapper.findUserById(id);if (null != userById) {log.info("返回客户信息:{}", userById);redisTemplate.opsForValue().set("customer:info" + id, userById);break;} else {log.info("当前客户信息不存在:{}", id);break;}}}} finally {lock.unlock();}log.info("当前客户信息不存在:{}", id);break;default:break;}}
}

缓存击穿(热点数据/刚失效/定点打击)

定义

缓存击穿是指某个经常使用的缓存,在某一个时刻恰好失效了(例如缓存过期),并且此时刚好有大量的并发请求,这些请求就会给数据库造成巨大的压力

解决方案

  • **加锁排队:**和处理缓存雪崩的加锁类似,都是在查询数据库的时候加锁排队,缓存操作请求以此来减少服务器的运行压力。
  • **设置永不过时:**对于某些经常使用的缓存,我们可以设置为永不过期,这样就能保证缓存的稳定性,但要注意在数据更改后,要及时更新此热点缓存,否则就会造成查询结果误差。

总结
image.png

脑裂

分布式session

分布式锁

  • 分布式锁需要的条件和刚需
    • 独占性
      • 任何时刻有且只有一个线程持有这个锁
    • 高可用
      • 若redis集群环境下,不能因为某一个节点挂了而出现获取锁和释放锁失败的情况
      • 高并发请求下,依旧性能很好
    • 防死锁
      • 不能出现死锁问题,必须有超时重试机制或者撤销操作,有个终止跳出的途径
    • 不乱抢
      • 防止张冠李戴,只能解锁自己的锁,不能把别人的锁给释放了
    • 重入性
      • 同一节点的同一线程如果获得锁之后,他可以再次获取这个锁

v 8.0 其实面对不是特别高的并发场景足够用了,单机redis也够用了

  • 要兼顾锁的重入性
    • setnx不满足了,需要hash结构的hset
  • 上锁和解锁都用 Lua 脚本来实现原子性
  • 引入工厂模式 DistributedLockFactory, 实现 Lock 接口,实现redis的可重入锁
    • lock() 加锁的关键逻辑
      • 加锁 实际上就是在redis中,给Key键设置一个值,为避免死锁,并给定一个过期时间
      • 自旋
      • 续期
    • unlock() 解锁关键逻辑
    • 将 Key 键删除,但是也不能乱删,只能自己删自己的锁
  • 实现自动续期功能的完善,后台自定义扫描程序,如果规定时间内没有完成业务逻辑,会调用加钟自动续期的脚本
    @Autowiredprivate DistributedLockFactory distributedLockFactory;/*** v8.0  实现自动续期功能的完善,后台自定义扫描程序,如果规定时间内没有完成业务逻辑,会调用加钟自动续期的脚本** @return*/public String sale() {String retMessage = "";Lock redisLock = distributedLockFactory.getDistributedLock("redis");redisLock.lock();try {//1 查询库存信息String result = stringRedisTemplate.opsForValue().get("inventory001");//2 判断库存是否足够Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);//3 扣减库存if (inventoryNumber > 0) {stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;// 演示自动续期的的功能
//                try {
//                    TimeUnit.SECONDS.sleep(120);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }} else {retMessage = "商品卖完了,o(╥﹏╥)o";}} finally {redisLock.unlock();}return retMessage + "\t" + "服务端口号:" + port;}/*** v7.0     兼顾锁的可重入性   setnx不满足了,需要hash结构的hset* 上锁和解锁都用 Lua 脚本实现原子性* 引入工厂模式 DistributedLockFactory    实现Lock接口 ,实现 redis的可重入锁** @return*/
//    //private Lock redisDistributedLock = new RedisDistributedLock(stringRedisTemplate, "xfcyRedisLock");
//
//    public String sale() {
//        String retMessage = "";
//
//        Lock redisLock = distributedLockFactory.getDistributedLock("redis");
//        redisLock.lock();
//
//        //redisDistributedLock.lock();
//        try {
//            //1 查询库存信息
//            String result = stringRedisTemplate.opsForValue().get("inventory001");
//            //2 判断库存是否足够
//            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
//            //3 扣减库存
//            if (inventoryNumber > 0) {
//                stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
//                retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
//                System.out.println(retMessage);
//
//                // 测试可重入性
//                //testReEntry();
//
//            } else {
//                retMessage = "商品卖完了,o(╥﹏╥)o";
//            }
//        } finally {
//            redisLock.unlock();
//            //redisDistributedLock.unlock();
//        }
//        return retMessage + "\t" + "服务端口号:" + port;
//    }
//
//    private void testReEntry() {
//        Lock redisLock = distributedLockFactory.getDistributedLock("redis");
//        redisLock.lock();
//
//        //redisDistributedLock.lock();
//        try {
//            System.out.println("测试可重入锁");
//        } finally {
//            redisLock.unlock();
//            //redisDistributedLock.unlock();
//        }
//    }
package com.xfcy.mylock;import cn.hutool.core.util.IdUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.util.concurrent.locks.Lock;/*** @author 晓风残月Lx* @date 2023/4/1 22:14*/
@Component
public class DistributedLockFactory {@Autowiredprivate StringRedisTemplate stringRedisTemplate;private String lockName;private String uuidValue;public DistributedLockFactory() {this.uuidValue = IdUtil.simpleUUID();}public Lock getDistributedLock(String lockType) {if (lockType == null) {return null;}if (lockType.equalsIgnoreCase("REDIS")) {this.lockName = "xfcyRedisLock";return new RedisDistributedLock(stringRedisTemplate, lockName, uuidValue);}else if (lockType.equalsIgnoreCase("ZOOKEEPER")) {this.lockName = "xfcyZookeeperLock";// TODO zoookeeper 版本的分布式锁return null;}else if (lockType.equalsIgnoreCase("MYSQL")){this.lockName = "xfcyMysqlLock";// TODO MYSQL 版本的分布式锁return null;}return null;}}
package com.xfcy.mylock;import cn.hutool.core.util.IdUtil;
import com.sun.org.apache.xpath.internal.operations.Bool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;/*** @author 晓风残月Lx* @date 2023/4/1 21:38* 自研的redis分布式锁,实现 Lock 接口*/
// @Component 引入DistributedLockFactory工厂模式,从工厂获得即可
public class RedisDistributedLock implements Lock {@Autowiredprivate StringRedisTemplate stringRedisTemplate;private String lockName;    // KEYS[1]private String uuidValue;   // ARGV[1]private long expireTime;    // ARGV[2]public RedisDistributedLock(StringRedisTemplate stringRedisTemplate, String lockName, String uuidValue) {this.stringRedisTemplate = stringRedisTemplate;this.lockName = lockName;this.uuidValue = uuidValue + ":" + Thread.currentThread().getId();this.expireTime = 30L;}@Overridepublic void lock() {tryLock();}@Overridepublic boolean tryLock() {try {tryLock(-1L, TimeUnit.SECONDS);} catch (InterruptedException e) {e.printStackTrace();}return false;}@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {if (time == -1L) {String script = "if redis.call('exists',KEYS[1]) == 0 or redis.call('hexists',KEYS[1],ARGV[1]) == 1 then " +"redis.call('hincrby',KEYS[1],ARGV[1],1)  " +"redis.call('expire',KEYS[1],ARGV[2]) " +"return 1 " +"else " +"return 0 " +"end";System.out.println("lockName = " + lockName +"\t" + "uuidValue = " + uuidValue);while (!stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime))) {// 暂停 60msThread.sleep(60);}// 新建一个后台扫描程序,来监视key目前的ttl,是否到我们规定的 1/2 1/3 来实现续期resetExpire();return true;}return false;}@Overridepublic void unlock() {String script = "if redis.call('HEXISTS',KEYS[1],ARGV[1]) == 0 then " +"return nil " +"elseif redis.call('HINCRBY',KEYS[1],ARGV[1],-1) == 0 then  " +"return redis.call('del',KEYS[1]) " +"else  " +"return 0 " +"end";// nil = false  1 = true  0 = falseLong flag = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime));if (null == flag) {throw new RuntimeException("this lock doesn't exists0");}}private void resetExpire() {String script = "if redis.call('HEXISTS',KEYS[1],ARGV[1]) == 1 then " +"return redis.call('expire',KEYS[1],ARGV[2]) " +"else " +"return 0 " +"end";new Timer().schedule(new TimerTask() {@Overridepublic void run() {if (stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime))) {resetExpire();}}}, (this.expireTime * 1000) / 3);}// 下面两个用不上// 下面两个用不上// 下面两个用不上@Overridepublic void lockInterruptibly() throws InterruptedException {}@Overridepublic Condition newCondition() {return null;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/145951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】聚合函数:汇总、分组数据

文章目录 学习目标MAX()、MIN()、AVG()、SUM()、COUNT()COUNT(*) 得到所有记录条目DISTINCT去重练习1&#xff08;使用UNION &#xff0c; SUM&#xff0c; BETEEN AND&#xff09;GROUP BY子句练习2&#xff08;使用sum&#xff0c;group by&#xff0c; join on&#xff0c; …

S25FL256S介绍及FPGA实现思路

本文介绍 S25FL256S 这款 FLASH 芯片&#xff0c;并进行 FPGA 读写控制的实现&#xff08;编程思路及注意事项&#xff09;。 文章目录 S25FL-S 介绍管脚功能说明SPI 时钟模式SDRDDR 工作模式FLASH存储阵列&#xff08;地址空间映射&#xff09;常用寄存器及相关指令Status Reg…

Stable Diffusion WebUI使用AnimateDiff插件生成动画

AnimateDiff 可以针对各个模型生成的图片&#xff0c;一键生成对应的动图。 配置要求 GPU显存建议12G以上&#xff0c;在xformers或者sdp优化下显存要求至少6G以上。 要开启sdp优化&#xff0c;在启动参数加上--sdp-no-mem-attention 实际的显存使用量取决于图像大小&#…

毫米波雷达模块的目标检测与跟踪

毫米波雷达技术在目标检测与跟踪方面具有独特的优势&#xff0c;其高精度、不受光照影响等特点使其在汽车、军事、工业等领域广泛应用。本文深入探讨毫米波雷达模块在目标检测与跟踪方面的研究现状、关键技术以及未来发展方向。 随着科技的不断进步&#xff0c;毫米波雷达技术在…

Nginx 修改server_name后无法访问

问题&#xff1a; 在nginx.conf配置中, server_name 为 localhost 时可以正常访问&#xff0c;但改成自定义的域名后无法访问 解决方法&#xff1a; - Window系统 修改本地hosts文件&#xff0c;一般路径在&#xff1a;C:\Windows\System32\drivers\etc\hosts 在文件最后…

目标检测—YOLO系列(二 ) 全面解读论文与复现代码YOLOv1 PyTorch

精读论文 前言 从这篇开始&#xff0c;我们将进入YOLO的学习。YOLO是目前比较流行的目标检测算法&#xff0c;速度快且结构简单&#xff0c;其他的目标检测算法如RCNN系列&#xff0c;以后有时间的话再介绍。 本文主要介绍的是YOLOV1&#xff0c;这是由以Joseph Redmon为首的…

Nginx配置开启HTTPS

获取证书文件 Nginx 开启SSL server {listen 443 default ssl;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;proxy_set_header Host $host;ssl_certificate /usr/local/nginx/cert/server.pem;ssl_certificate_key /usr/local/ngin…

京东推出数据平台云海 API接口将达700个

1月16日消息&#xff0c;继上周面对企业用户发布京东电商云解决方案后&#xff0c;日前&#xff0c;京东云平台又发布了全新的数据开放平台——“云海”&#xff0c;以开放商家、商品、点击流等相关数据。 在京东主办&#xff0c;思路网协办的京东开放云服务合作伙伴2014峰会&…

[CSS] 文本折行

文本折行一般分为两种情况&#xff1a; CJK&#xff08;Chinese/Japanese/Korean&#xff09; 字符和非 CJK 字符。一般非 CJK 字符折行发生在两个单词的空格中间&#xff0c;见下图&#xff1a; 图中文本 “hello world” 包裹容器的宽度为 2rem&#xff0c;但是 hello 并没有…

JavaWeb[总结]

文章目录 一、Tomcat1. BS 与 CS 开发介绍1.1 BS 开发1.2 CS 开发 2. 浏览器访问 web 服务过程详解(面试题)2.1 回到前面的 JavaWeb 开发技术栈图2.2 浏览器访问 web 服务器文件的 UML时序图(过程) &#xff01; 二、动态 WEB 开发核心-Servlet1. 为什么会出现 Servlet2. 什么是…

微服务学习 | Eureka注册中心

微服务远程调用 在order-service的OrderApplication中注册RestTemplate 在查询订单信息时&#xff0c;需要同时返回订单用户的信息&#xff0c;但是由于微服务的关系&#xff0c;用户信息需要在用户的微服务中去查询&#xff0c;故需要用到上面的RestTemplate来让订单的这个微…

Linux网络之传输层协议tcp/udp

文章目录 目录 一、再谈端口号 1.端口号划分 2.知名端口号 3.netstat&#xff0c;pidof 二、UDP协议 1.udp协议格式 2.udp特点 3.基于udp的应用层协议 三、TCP协议 1.tcp报头 确认应答机制&#xff08;ACK) 超时重传机制 连接管理机制&#xff08;三次握手四次挥…

前端案例-css实现ul中对li进行换行

场景描述&#xff1a; 我想要实现&#xff0c;在展示的item个数少于4个的时候&#xff0c;则排成一行&#xff0c;并且均分&#xff08;比如说有3个&#xff0c;则每个的宽度为33.3%&#xff09;&#xff0c;如果item 个数大于4&#xff0c;则进行换行。 效果如下&#xff1a…

【VSCode】配置C/C++开发环境教程(Windows系统)

下载和配置MinGW编译器 首先&#xff0c;我们需要下载并配置MinGW编译器。 下载MinGW编译器&#xff0c;并将其放置在一个不含空格和中文字符的目录下。 配置环境变量PATH 打开控制面板。可以通过在Windows搜索栏中输入"控制面板"来找到它。 在控制面板中&#xf…

数据结构-哈希表(C语言)

哈希表的概念 哈希表就是&#xff1a; “将记录的存储位置与它的关键字之间建立一个对应关系&#xff0c;使每个关键字和一个唯一的存储位置对 应。” 哈希表又称&#xff1a;“散列法”、“杂凑法”、“关键字&#xff1a;地址法”。 哈希表思想 基本思想是在关键字和存…

电子器件系列44:环形线圈电感

干货&#xff01;电感最重要、最常见的几个作用_线圈 环形线圈电感的原理&#xff1a; 电感中包含了哪三个物理学定律&#xff0c;为什么它能以磁场形式储能_哔哩哔哩_bilibili 电感的基本原理_哔哩哔哩_bilibili 环形线圈电感的作用: 1.储能器&#xff1a; 环形线圈电感能够…

学习c#的第十四天

目录 C# 接口&#xff08;Interface&#xff09; 接口的特点 定义接口 接口继承 接口和抽象类的区别 C# 命名空间&#xff08;Namespace&#xff09; using 关键字 定义命名空间 嵌套命名空间 C# 接口&#xff08;Interface&#xff09; 接口定义了所有类继承接口时应…

01ctfer 文件上传

01ctfer 文件上传 启动靶场 访问该地址 代码审计 <?php header("Content-Type:text/html; charsetutf-8"); // 每5分钟会清除一次目录下上传的文件 require_once(pclzip.lib.php);if(!$_FILES){echo <!DOCTYPE html> <html lang"zh">…

设计模式-备忘录模式-笔记

动机&#xff08;Motivation&#xff09; 在软件构建过程中&#xff0c;某些对象的状态在转换过程中&#xff0c;可能由于某种需要&#xff0c;要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态&#xff0c;便会暴露对象的细节…

FPGA模块——IIC协议(FPGA做主机操作24C64)

FPGA模块——IIC协议&#xff08;FPGA做主机操作24C64&#xff09; EEPROM&#xff08;24C64&#xff09;向器件写数据时序向器件读数据时序 IIC协议FPGA主机代码IIC读寄存器驱动&#xff08;指定地址单次读写&#xff09;使用 IIC模块 EEPROM&#xff08;24C64&#xff09; 掉…