缓存中间件

缓存与分布式锁

即时性、数据一致要求不高的
访问量大且更新频率不高的数据
(读多,写少)

常用缓存中间件 redis

Spring

如果用spring的情况下,由于redis没有受spring的管理,
则我们需要自己先写一个redis的配置类,写好方法把redistemplate 作为bean return出来受 spring管理,
(很老的方法了,但现在企业也在用)或者 直接写xml进行注册,然后放入web.xml中

	<context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/classes/applicationContext.xml,/WEB-INF/classes/config/spring/xjtec_helps/spring_xjtec_helps_taskhelp_starts.xml,/WEB-INF/classes/spring-session.xml</param-value></context-param>

Springboot 或cloud

引入maven redis的starter,然后spring把基础工作都给你写好了,你只需要引入,按照规则前缀,在配置中写好配置值,想用的时候 @autowired进来用就完事了

配置类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.boot.autoconfigure.data.redis;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;@Configuration(proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
//所有的配置信息通过RedisProperties.class得到,这里引入过来
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {public RedisAutoConfiguration() {}@Bean@ConditionalOnMissingBean(name = {"redisTemplate"})@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}@Bean@ConditionalOnMissingBean@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {return new StringRedisTemplate(redisConnectionFactory);}
}//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.boot.autoconfigure.data.redis;import java.time.Duration;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(
//配置文件中,按照这个前缀写就可以了prefix = "spring.redis"
)
public class RedisProperties {private int database = 0;private String url;private String host = "localhost";private String password;private int port = 6379;private boolean ssl;private Duration timeout;private String clientName;private Sentinel sentinel;private Cluster cluster;private final Jedis jedis = new Jedis();private final Lettuce lettuce = new Lettuce();public static class Pool {private int maxIdle = 8;private int minIdle = 0;private int maxActive = 8;private Duration maxWait = Duration.ofMillis(-1L);private Duration timeBetweenEvictionRuns;public Pool() {}public int getMaxIdle() {return this.maxIdle;}public void setMaxIdle(int maxIdle) {this.maxIdle = maxIdle;}public int getMinIdle() {return this.minIdle;}public void setMinIdle(int minIdle) {this.minIdle = minIdle;}public int getMaxActive() {return this.maxActive;}public void setMaxActive(int maxActive) {this.maxActive = maxActive;}public Duration getMaxWait() {return this.maxWait;}public void setMaxWait(Duration maxWait) {this.maxWait = maxWait;}public Duration getTimeBetweenEvictionRuns() {return this.timeBetweenEvictionRuns;}public void setTimeBetweenEvictionRuns(Duration timeBetweenEvictionRuns) {this.timeBetweenEvictionRuns = timeBetweenEvictionRuns;}}
}

则会自动引入

三级目录的展示,通过缓存一点一点优化

最基本的缓存的加入

缓存中有则直接用,无则进入数据库查询并存放

  @Overridepublic Map<String, List<Catalog2VO>> getCatalogJson() {//给缓存中放json字符串,拿出的json字符串,还用逆转为能用的对象类型 【序列化与反序列化】//1.加入缓存逻辑String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");if(StringUtils.isEmpty(catalogJSON)){//2.缓存中没有,查询数据库Map<String, List<Catalog2VO>> catalogJsonFromDb = getCatalogJsonFromDb();//3.查到的数据再放入缓存,将对象转为json放在缓存中String s = JSON.toJSONString(catalogJsonFromDb);redisTemplate.opsForValue().set("catalogJSON",s);}//转为我们指定的对象Map<String,List<Catalog2VO>> result = JSON.parseObject(catalogJSON,new TypeReference<Map<String,List<Catalog2VO>>>(){});return result;}

为了解决击穿的问题,优化代码

缓存中所遇到的三种问题

这块代码只能在单项目(this)中能控制到1人进到数据库查找值,分布式时则控制不住(分了几台服务器就有几个人能进),但也比所有有人进入到数据库查找好

锁中的代码,一定要由三部分,
1.查缓存,又则返回
2.查数据库
3.再入缓存

    //首先尝试缓存拿值@Overridepublic Map<String, List<Catalog2VO>> getCatalogJson() {//给缓存中放json字符串,拿出的json字符串,还用逆转为能用的对象类型 【序列化与反序列化】//1.加入缓存逻辑String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");if(StringUtils.isEmpty(catalogJSON)){//2.缓存中没有,查询数据库Map<String, List<Catalog2VO>> catalogJsonFromDb = getCatalogJsonFromDb();return catalogJsonFromDb;}//转为我们指定的对象Map<String,List<Catalog2VO>> result = JSON.parseObject(catalogJSON,new TypeReference<Map<String,List<Catalog2VO>>>(){});return result;}//从数据库查询并封装分类数据public Map<String, List<Catalog2VO>> getCatalogJsonFromDb() {//1.synchronized(this): SpringBoot所有的组件在容器中都是单列的,则此时this可以进行锁住,但分布式时无法锁住synchronized (this){//得到锁以后,我们应该再去缓存中查找一次,以防有人已经查到了String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");if(!StringUtils.isEmpty(catalogJSON)){//进锁后,有可能别人已经提前进来并且找到了,则直接缓存拿到返回即可Map<String,List<Catalog2VO>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catalog2VO>>>(){});return result;}//1.将数据库的多次查询变为一次List<CategoryEntity> selectList = baseMapper.selectList(null);List<CategoryEntity> level1Categorys = getParent_cid(selectList,0L);Map<String,List<Catalog2VO>> parent_cid = level1Categorys.stream().collect(Collectors.toMap(k->k.getCatId().toString(),v->{List<CategoryEntity> categoryEntities = getParent_cid(selectList,v.getCatId());List<Catalog2VO> catalog2VOS = null;if(categoryEntities!=null){catalog2VOS = categoryEntities.stream().map(l2->{Catalog2VO catalog2VO = new Catalog2VO(v.getCatId().toString(),null,l2.getCatId().toString(),l2.getName());//1、找当前二级分类的三级分类封装成voList<CategoryEntity> level3Catelog = getParent_cid(selectList, l2.getCatId());if(level3Catelog!=null){List<Catalog2VO.Catalog3Vo> collect = level3Catelog.stream().map(l3->{Catalog2VO.Catalog3Vo catalog3Vo = new Catalog2VO.Catalog3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName());return catalog3Vo;}).collect(Collectors.toList());catalog2VO.setCatalog3List(collect);}return catalog2VO;}).collect(Collectors.toList());}return catalog2VOS;}));//3.查到的数据再放入缓存,将对象转为json放在缓存中String s = JSON.toJSONString(parent_cid);redisTemplate.opsForValue().set("catalogJSON",s,1, TimeUnit.DAYS);return parent_cid;}}

本地尝试分布式

赋值多台服务器但注意端口分开
在这里插入图片描述
在这里插入图片描述
然后nginx访问过来时,由网关帮我们赋值均衡
在这里插入图片描述
根据主机名进行断言拦截到请求,然后转到uri

穿透

访问的数据,数据库不存在,则也无法放入缓存中,则不断访问数据库
设置null值返回

雪崩

key大批量同时过期
设置过期时间时加入随机值

击穿

热点key,过期后,同时又大量的值进行访问到数据库了
给热点key加锁,不能让所有人都去数据库进行查询

分布式锁

小提示

docker中引进的redis,通过命令 docker exec -it redis redis-cli
进入到redis的操作界面

同过redis的setnx进行一次简易的分布式锁

    public Map<String, List<Catalog2VO>> getCatalogJsonFromDbWithRedisLock() {//1、占分布式锁。去redis占坑Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock","111");//这里的操作就是redis的setnx操作if(lock){//加锁成功..执行业务Map<String, List<Catalog2VO>> dataFromDb = getDataFromDb();redisTemplate.delete("lock");//操作完成后,释放锁return dataFromDb;}else{//加锁失败。。则重试return getCatalogJsonFromDbWithRedisLock();//自旋的方式,不断重新尝试}}

简易锁的缺点
解锁之前,出异常了,则会导致死锁
补救:加锁时一定要带上过期时间
eg. set lock 111 EX 300 NX(redis命令)

加锁和过期时间的设置要进行原子操作

  public Map<String, List<Catalog2VO>> getCatalogJsonFromDbWithRedisLock() {//1、占分布式锁。去redis占坑Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock","111",300, TimeUnit.SECONDS);//这里的操作就是redis的setnx操作if(lock){//加锁成功..执行业务Map<String, List<Catalog2VO>> dataFromDb = getDataFromDb();redisTemplate.delete("lock");//操作完成后,释放锁return dataFromDb;}else{//加锁失败。。则重试return getCatalogJsonFromDbWithRedisLock();//自旋的方式,不断重新尝试}}

删锁时也要进行原子操作,并且要避免不要删除掉别人的加的锁,所有锁的值要是唯一的,删除要进行原子操作时,运行lua脚本

  public Map<String, List<Catalog2VO>> getCatalogJsonFromDbWithRedisLock() {String UUID = java.util.UUID.randomUUID().toString();//1、占分布式锁。去redis占坑Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock",UUID,300, TimeUnit.SECONDS);//这里的操作就是redis的setnx操作if(lock){//加锁成功..执行业务Map<String, List<Catalog2VO>> dataFromDb = getDataFromDb();String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";redisTemplate.execute(new DefaultRedisScript<Integer>(script,Integer.class),Arrays.asList("lock"), UUID);return dataFromDb;}else{//加锁失败。。则重试return getCatalogJsonFromDbWithRedisLock();//自旋的方式,不断重新尝试}}

最终版本

public Map<String, List<Catalog2VO>> getCatalogJsonFromDbWithRedisLock() {String UUID = java.util.UUID.randomUUID().toString();//1、占分布式锁。去redis占坑Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock",UUID,300, TimeUnit.SECONDS);//这里的操作就是redis的setnx操作Map<String, List<Catalog2VO>> dataFromDb = new HashMap<>();if(lock){try{//加锁成功..执行业务dataFromDb = getDataFromDb();}finally {String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";redisTemplate.execute(new DefaultRedisScript<Integer>(script,Integer.class),Arrays.asList("lock"), UUID);}return dataFromDb;}else{//加锁失败。。则重试return getCatalogJsonFromDbWithRedisLock();//自旋的方式,不断重新尝试}}

分布式锁框架 redisson

引用springboot的redissonstarter则基本不用自己再写代码,只需配置文件写好响应的属性即可

首次使用则我们自己进行配置一下,则只单独引入redisson的配置

写好配置类(放入到ioc容器中,后面则可直接使用)

单个redis的情况,集群则写入多个ip

@Configuration()
public class MyRedissonConfig {@Bean(destroyMethod="shutdown")public RedissonClient redisson() throws IOException{//创建配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.29.103:6379");//根据conf创建使用redisson所必须要的 RedissonClient示例RedissonClient redissonClient = Redisson.create(config);return redissonClient;}
}

redisson的锁都是可重入锁
可重入锁:
可重入锁是指一个线程可以重复获取它已经持有的锁。当一个线程已经持有某个锁时,它可以再次获取该锁而不会被阻塞,这种特性称为"可重入性"(Reentrancy)。

重入计数:每次获取锁计数器加1,释放时减1,计数器为0时锁才真正释放
避免死锁:允许同一线程多次获取同一把锁
公平性选择:ReentrantLock可以配置为公平锁或非公平锁
中断响应:ReentrantLock支持在等待锁时响应中断

redisson的api用法与lock的api用法相同

redisson的基础应用

  @GetMapping("/hello")public String hello(){//1.获取一把锁,只要锁的名字一样,就是同一把锁RLock lock = redisson.getLock("my-lock");//2.加锁lock.lock();//阻塞式等待,默认加的锁30s,只要这边把锁放了,其他人可以获得到//看门狗,业务时间长时,自动给锁续期//业务完成,(获取不到线程id)则不再续期,不手动解锁的话,30s后自动解锁//2.1 可以自己指定过期时间,但指定时间后,不会触发开门狗,则不会自动续期,我们指定了时间后,就会去执行 固定的脚本//lock.lock(10,TimeUnit.SECONDS);//2.2lock.tryLock(100,10,TimeUnit.SECONDS); 可以设定线程等待锁的时间,超过则放弃//实际应用时,则还是需要指定时间,不需要续期,只要指定的时间长一点即可(超过业务时间)try {System.out.println("加锁成功,执行业务..."+Thread.currentThread().getId());Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}finally {lock.unlock();}}
读写锁(也是可重入锁,当前线程可对锁多次操作)
//通过读写锁,能保证读的数据一定是最新的, 写锁是一个排他锁,读锁是一个共享锁//写锁没释放时,其他人既不能写也不能读,没有写锁时,则读锁就像不存在一样
//正在读的过程中,写锁也一定要等待读锁释放@GetMapping("/write")@ResponseBodypublic String writeValue(){//读写锁RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");String s = "";RLock rLock = lock.writeLock();try{//1、操作数据库 则加写锁 , 读数据加读锁rLock.lock();s = UUID.randomUUID().toString();Thread.sleep(3000);stringRedisTemplate.opsForValue().set("writeValue",s);} catch (InterruptedException e) {e.printStackTrace();}finally {rLock.unlock();}return s;}@GetMapping("/read")@ResponseBodypublic String readValue(){RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");String s = "";RLock rLock = lock.readLock();rLock.lock();try{s = stringRedisTemplate.opsForValue().get("writeValue");} catch (Exception e) {e.printStackTrace();}finally {rLock.unlock();}return s;}
信号量(主要用于限流操作,可以设置线程上线)
 @GetMapping("/park")@ResponseBodypublic String park() throws InterruptedException{RSemaphore park = redisson.getSemaphore("park");//park.acquire();//直接获取一个信号量,如果此时信号量没有了,则会卡住//则通常用下面的方法,得到boolean返回值boolean b = park.tryAcquire();if(b){//执行业务}else{//返回提示return "fail";}return "ok=>"+b;}@GetMapping("/go")@ResponseBodypublic String go() throws InterruptedException{RSemaphore park = redisson.getSemaphore("park");park.release();return "ok";}
闭锁
 /*** 等待5个班级都没人后,才能锁大门*/@GetMapping("/lockDoor")@ResponseBodypublic String lockDoor() throws InterruptedException{RCountDownLatch door = redisson.getCountDownLatch("door");door.trySetCount(5);//设置好锁需要达到的数量door.await();//等待闭锁的完成//到达数量后则可以放行return "放假了...";}@GetMapping("/gogogo/{id}")public String gogogo(@PathVariable("id") Long id){RCountDownLatch door = redisson.getCountDownLatch("door");door.countDown();//触发一次,则减少一个return id+"班的人都走了";}

缓存一致性

   /**(根据自己的业务仔细判断)* 缓存里面的数据如何和数据库保持一致* 缓存数据一致性* 1)、双写模式  操作数据库的时候 ,重新缓存保存* 2)、失效模式  操作数据库的时候,直接删除相应缓存* 但这两个操作如果不加锁的话,还是会导致脏数据* 加锁后性能很低,要加也要加读写锁* 总结:* 我们能放入缓存的数据本就不应该是实时性,一致性要求超高的数据,所以一般设定好过期时间都是能正常使用的* 我们不应该过度设计,增加系统的复杂性* 遇到频繁修改、实时要求高,那就不要缓存、不加锁、自己查数据库就好了* 阿里有一个cannal* @return*/

SpringCache

不同的CacheManager 都对缓存进行了进一次的分区,例如对于redis的CacheManager,虽然缓存都是存在redis得同一个库,但到这边还是进行了分区处理

整合SpringCache简化缓存开发
  1. 引入依赖
    spring-boot-starter-cache
    spring-boot-starter-data-redis //我们用redis进行的缓存

2)写配置
(1)自动配置了那些
CacheAutoConfiguration会导入 RedisCacheConfiguration;
自动配置好了缓存管理器RedisCacheManager
(2)配置使用了redis作为缓存
spring.cache.type = redis
3)测试使用缓存
@Cacheable 触发将数据保存到缓存的操作
@CacheEvict 触发将数据从缓存删除的操作
@CachePut 不影响方法的执行进行缓存的更新
@Cacheing 组合以上多个操作
@CacheConfig 在类别共享缓存的相同配置

3)测试使用缓存
主类加注解 启动缓存 @EnbaleCaching
然后进行相应的注解进行相应的缓存操作

   // 每一个需要缓存的数据我们都来指定要放到那个名字的缓存【缓存的分区(按照业务类型分)】@Cacheable({"category"})//代表当前方法的结果需要缓存,如果缓存中有,则不调用此方法@Overridepublic List<CategoryEntity> getLevel1Categorys() {System.out.println("调用了getLevel1Categorys...");// 查询父id=0return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));}

自定义调整缓存配置

 /*** 启动Cacheable注释能进行缓存的使用 是有默认值的* key自动生产名字* 缓存的值默认使用jdk序列化* 默认ttl时间 -1* * 则这三样内容我们肯定要进行自定义的* 注解中定义了 key值 通过表达式或者字符串的形式定义key值* 缓存时间则需要在配置文件中进行修改* 为了更好的迁移性 在缓存信息保存为json形式,调整格式时,就需要写类调整配置类了* @return*/// 每一个需要缓存的数据我们都来指定要放到那个名字的缓存【缓存的分区(按照业务类型分)】@Cacheable(value = {"category"},key = "'level1Categorys'")//代表当前方法的结果需要缓存,如果缓存中有,则不调用此方法@Overridepublic List<CategoryEntity> getLevel1Categorys() {System.out.println("调用了getLevel1Categorys...");// 查询父id=0return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));}
自定义配置类
package com.atguigu.gulimall.product.config;import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** 通过注解 @EnableConfigurationProperties 可以绑定配置类* 但为什么我们要通过注解的形式拿到该类(CacheProperties)和该类的值呢,因为CacheProperties并不是受spring容器管理,则通过这种形式导入*/
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
//把启动注解放在我们的自己自定义缓存配置上面
@EnableCaching
//自定义缓存配置
public class MyCacheConfig {//在类CacheProperties的源码我们可以看到/*** @ConfigurationProperties(prefix = "spring.cache") 是通过这个前缀和自己的属性进行值匹配的** 然后为了让这个类生效 我们就加了顶部的 @EnableConfigurationProperties(CacheProperties.class)*///@Autowired 可以通过属性的方式得到//CacheProperties cache//这里是该redis的相关配置,要该其他的,导入其他的配置类在按这要写就可以了@Bean                                         //放在参数上面 也是可以直接导入值的RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){//拿到最原始的实例,然后进行修改覆盖即可RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();//设置key的序列化方式config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));//主要就是把默认jdk序列化的形式 转为jsonconfig = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));CacheProperties.Redis redis = cacheProperties.getRedis();//将配置文件中的所以配置都生效if(redis.getTimeToLive()!=null){config = config.entryTtl(redis.getTimeToLive());}if(redis.getKeyPrefix()!=null){config = config.prefixKeysWith(redis.getKeyPrefix());}if(!redis.isCacheNullValues()){config = config.disableCachingNullValues();}if(!redis.isUseKeyPrefix()){config = config.disableKeyPrefix();}return config;}}
再看下我们的配置文件中进行了那些配置
spring.cache.type=redis
spring.cache.redis.time-to-live=360000
#指定我们的缓存前缀,如果没有就用缓存的名字作为前缀
spring.cache.redis.key-prefix=CACHE_
#但需要手动开启
spring.cache.redis.use-key-prefix=false
#防止缓存击穿 是否缓存空值
spring.cache.redis.cache-null-values=true

数据更新时,缓存注释的操作

    /*** 级联更新所有关联的数据* @param category*/                                                      //注意key为固定字符串时一定要加单引号,不然会被当作动态的表达式//删除单个指定key缓存 @CacheEvict(value = "category", key = "'getLevel1Categorys'")//写的方法上加入该注解@CacheEvict 进行的失效模式,删除之前的缓存//删除多个指定key缓存 则要用到注解 @Caching 可以进行组合操作@Caching(evict = {@CacheEvict(value = "category", key = "'getLevel1Categorys'"),@CacheEvict(value = "category", key = "'getCatalogJson'")})//直接进行分区删除缓存 更为方便@CacheEvict(value = "category",allEntries = true)//如果数据返回值时 用注解CachePut 则会把数据写入缓存中@CachePut@Transactional@Overridepublic void updateCascade(CategoryEntity category) {this.updateById(category);categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());}

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

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

相关文章

大语言模型全方位解析:从基础认知到RESTful API应用

文章目录 前言一、初见大模型1.1 大语言模型基本知识了解&#xff08;一&#xff09;日常可能用到的大语言模型&#xff08;二&#xff09;大模型的作用&#xff08;三&#xff09;核心价值 1.2 大模型与人工智能关系1.3 大语言模型的“前世今生”与发展1.3.1 大语言模型的发展…

网安系列【11】之目录穿越与文件包含漏洞详解

文章目录 前言一 目录穿越漏洞1.1 什么是目录穿越&#xff1f;1.2 目录穿越的原理1.3 目录穿越的常见形式1.3.1 基本形式1.3.2 编码绕过1.3.3 绝对路径攻击 1.4 实战案例解析1.4.1 案例1&#xff1a;简单的目录穿越1.4.2 案例2&#xff1a;编码绕过 1.5 目录穿越的危害 二、文件…

uri-url-HttpServletRequest

1. 使用HttpServletRequest UrlPathHelper 解析 出 url路径 org.springframework.web.util.UrlPathHelper 是 Spring 框架中用于处理 HTTP 请求路径的一个工具类&#xff0c;它帮助解析和处理与请求路径相关的细节。特别是 getLookupPathForRequest(HttpServletRequest request…

Ubuntu22.04安装p4显卡 nvidia-utils-570-server 570.133.20驱动CUDA Version: 12.8

Ubuntu22.04安装p4显卡 nvidia-utils-570-server 570.133.20驱动CUDA Version: 12.8专业显卡就是专业显卡&#xff0c;尽管p4已经掉到了白菜价&#xff0c;官方的支持却一直都保持&#xff0c;比如它可以装上cuda12.8,这真的出乎我意料。NVIDIA Tesla P4显卡的主要情况Pascal架…

工业日志AI大模型智能分析系统-前端实现

目录 主要架构 前端项目结构 1. 核心实现代码 1.1 API服务封装 (src/api/log.ts) 1.2 TS类型定义 (src/types/api.ts) 1.3 Pinia状态管理 (src/stores/logStore.ts) 1.4 日志分析页面 (src/views/LogAnalysis.vue) 1.5 日志详情组件 (src/components/LogDetail.vue) 2…

C++内存泄漏排查

引言 C内存泄漏问题的普遍性与危害内存泄漏排查大赛的背景与目标文章结构和主要内容概述 内存泄漏的基本概念 内存泄漏的定义与类型&#xff08;显式、隐式、循环引用等&#xff09;C中常见的内存泄漏场景&#xff08;指针管理不当、资源未释放等&#xff09;内存泄漏对程序性能…

20250706-4-Docker 快速入门(上)-常用容器管理命令_笔记

一、常用管理命令1. 选项&#xfeff;&#xfeff;1&#xff09;ls&#xfeff;功能&#xff1a;列出容器常用参数&#xff1a;-a&#xff1a;查看所有容器包含退出的-q&#xff1a;列出所有容器ID-l&#xff1a;列出最新创建的容器状态使用技巧&#xff1a;容器很多时使用dock…

基于 Camunda BPM 的工作流引擎示例项目

项目介绍 这是一个基于 Camunda BPM 的工作流引擎示例项目&#xff0c;包含完整的后台接口和前端页面&#xff0c;实现了流程的设计、部署、执行等核心功能。 技术栈 后端 Spring Boot 2.7.9Camunda BPM 7.18.0MySQL 8.0JDK 1.8 前端 Vue 3Element PlusBpmn.jsVite 功能…

Day06_刷题niuke20250707

试卷01&#xff1a; 单选题 C 1. 在C中,一个程序无论由多少个源程序文件组成,其中有且仅有一个主函数main().说法是否正确&#xff1f; A 正确 B 错误 正确答案&#xff1a;A 官方解析&#xff1a; 在C程序设计中,一个完整的程序确实有且仅有一个main函数作为程序的入口点,这…

洛谷 P5788 【模板】单调栈

题目背景模板题&#xff0c;无背景。2019.12.12 更新数据&#xff0c;放宽时限&#xff0c;现在不再卡常了。题目描述给出项数为 n 的整数数列 a1…n​。定义函数 f(i) 代表数列中第 i 个元素之后第一个大于 ai​ 的元素的下标&#xff0c;即 f(i)mini<j≤n,aj​>ai​​{…

linux系统运行时_安全的_备份_还原_方法rsync

1.问题与需求 问题: 新部署的机器设备(主控RK3588), 没有经过烧录定制镜像, 研发部署, 直接组装发送到客户现场需要通过frpc远程部署: 安装ros2 python包 docker镜像 环境配置 自启动配置 SN设备信息写自动部署脚本, 实现一键部署升级无奈物联网卡做了白名单限制, apt 和…

18套精美族谱Excel模板,助力家族文化传承!

【资源分享】18套精美族谱Excel模板&#xff0c;助力家族文化传承&#xff01; &#x1f3af; 本文分享一套完整的家族谱系资源&#xff0c;包含18个精心设计的Excel模板&#xff0c;从基础模板到专业图表&#xff0c;满足各类家族的族谱制作需求。 一、为什么要制作族谱&…

MySQL Galera Cluster企业级部署

一、MySQL Galera Cluster简介 主要特点 同步复制&#xff1a; 所有的写操作&#xff08;包括插入、更新、删除&#xff09;在集群中的所有节点上都是同步的。这意味着每个节点上的数据是完全一致的。 多主节点&#xff1a; 集群中的每个节点都是主节点。所有节点都可以处理读…

HTTP 重定向

什么是 HTTP 重定向&#xff1f; HTTP 重定向&#xff08;HTTP Redirect&#xff09; 是服务器向客户端&#xff08;通常是浏览器&#xff09;发出的指令&#xff0c;告诉客户端某个请求的资源已被移到新的位置。重定向通常通过发送一个特殊的 HTTP 状态码&#xff08;例如 3x…

本地加载非在线jar包设置

项目中存在私有jar包&#xff0c;提示在线获取不到&#xff0c;需要先获取到完整的jar包在打进maven中再在项目中进行maven依赖引入 mvn install:install-file -DfileD:\tools\maven\apache-maven-3.5.2\local_repository2\org\ahjk\SixCloudCommon\1.0\SixCloudCommon-1.0-SN…

Codeforces Round 979 (Div. 2)

A c[1]-b[1]0&#xff0c;之后每个c[1]-b[1]最大都是maxa-mina&#xff0c;最大和最小放前两个 B ans2^(a1)-2^s-1&#xff0c;1一个最小 C 我们可以把式子化为(....)||(....)||(....)括号里没有||&#xff0c;如果括号全是1那么A赢&#xff0c;A尽量选择把1选在一起 D …

UI前端大数据处理性能瓶颈突破:分布式计算框架的应用

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!一、引言&#xff1a;前端大数据处理的性能困境与破局之路在数据爆炸增长的时代&#xff0c;UI…

病虫害数据集

数据是泰迪杯主办方提供的已经标记好的数据&#xff0c;4k画质的图片&#xff0c;总大小8个G 链接&#xff1a;https://pan.baidu.com/s/1fvmNHGrLvflEovjfCjDLOw?pwd6666 提取码&#xff1a;6666 虫害包括&#xff1a; 八点灰灯蛾 褐飞虱属 白背飞虱 二化螟 蟋蟀 黄足…

JAVA基础:关于JDK环境变量设置的若干相关细节及注意事项

一、JDK下载安装 网址&#xff1a;https://www.oracle.com/java/technologies/downloads/ 以 win11 为例&#xff0c;根据网址下载安装包后&#xff0c;点击安装&#xff0c;注意设置安装路径 二、基础常识 1.Java三大使用平台 Java SE(Java Standard Edition): 标准版&…

C++高频知识点(四)

文章目录 16. 虚基类要解决什么问题&#xff1f;17. C中如何进行类型转换操作&#xff1f;列举并解释四种类型转换方式。18. 什么是函数重载&#xff1f;如何进行函数重载&#xff1f;19. 解释C中的友元函数和友元类&#xff0c;并解释其使用场景。友元函数友元类 20. 请解释C中…