项目实战--本地缓存方案选型及使用缓存的坑

一、摘要

在互联网公司面试时,说到缓存,面试官基本上会绕不开的几个话题:项目中哪些地方用到了缓存?为什么要使用缓存?怎么使用它的?引入缓存后会带来哪些问题?
在这里插入图片描述

引入缓存,其实主要有两个用途:高性能、高并发

性能体现在引入缓存之前,以商城网站为例,频繁的从数据库里面获取商品数据,也就需要频繁执行SQL等待结果,若数据量很大同时请求频次逐渐增高,响应就逐渐缓慢;引入缓存之后,将数据库里面查询出来的商品数据信息存入缓存,需要时直接从缓存服务获取结果,效率极大提升。

并发体现在引入缓存之前,以 MySQL数据库为例,单台机器一秒内的请求次数到达 2000 之后就会开始报警;引入缓存之后,比如以 Redis 缓存服务器为例,单台机器一秒内的请求次数支持 110000 次,两者支持的并发量完全不是一个数量级的。

缓存和数据库效率差距大的根本原因:缓存数据存储在内存,数据库数据存储在磁盘,
而计算机中内存的数据读写性能远超磁盘的读写性能。但电脑重启后内存数据易丢失,而磁盘数据不易丢失。

所以数据存储方案不同,造就不同的实践用途。接下来就浅谈缓存,主要是本地缓存的使用。

二、本地缓存

从缓存面向的对象不同,缓存分为:本地缓存分布式缓存和多级缓存
(1)本地缓存:在单个计算机服务实例中,直接把数据缓存到内存中进行使用。
(2)分布式缓存:将一个计算机服务,同时在多台计算机里部署,所需数据无法共享(比如session会话)而引入一个独立部署的缓存服务来连接多台服务器的技术实践方案。
(3)多级缓存:在实际的业务中,本地缓存和分布式缓存会同时结合进行使用,当收到访问某个数据的操作时,会优先从本地缓存服务(一级缓存)查询,如果没有,再从分布式缓存服务(二级缓存)里面获取,如果也没有,最后再从数据库里面获取;从数据库查询完成之后,在依次更新分布式缓存服务、本地缓存服务的技术实践方案。

三、本地缓存实现方案

缓存关注点:第一是内存持久化;第二是支持缓存的数据自动过期清除。

3.1 自己编程实现一个缓存

对于简单的数据缓存,完全可以自行编写一套缓存服务。实现思路很简单:采用ConcurrentHashMap作为缓存数据存储服务,然后开启一个定时调度,每隔500毫秒检查一下过期的缓存数据,然后清除。
首先创建一个缓存实体类:

public class CacheEntity {/*** 缓存键*/private String key;/*** 缓存值*/private Object value;/*** 过期时间*/private Long expireTime;//...set、get
}

接着,创建一个缓存操作工具类CacheUtils:

public class CacheUtils {/*** 缓存数据*/private final static Map<String, CacheEntity> CACHE_MAP = new ConcurrentHashMap<>();/*** 定时器线程池,用于清除过期缓存*/private static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();static {// 注册一个定时线程任务,服务启动1秒之后,每隔500毫秒执行一次executor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {// 清理过期缓存clearCache();}},1000,500,TimeUnit.MILLISECONDS);}/*** 添加缓存* @param key    缓存键* @param value  缓存值*/public static void put(String key, Object value){put(key, value, 0);}/*** 添加缓存* @param key    缓存键* @param value  缓存值* @param expire 缓存时间,单位秒*/public static void put(String key, Object value, long expire){CacheEntity cacheEntity = new CacheEntity().setKey(key).setValue(value);if(expire > 0){Long expireTime = System.currentTimeMillis() + Duration.ofSeconds(expire).toMillis();cacheEntity.setExpireTime(expireTime);}CACHE_MAP.put(key, cacheEntity);}/*** 获取缓存* @param key* @return*/public static Object get(String key){if(CACHE_MAP.containsKey(key)){return CACHE_MAP.get(key).getValue();}return null;}/*** 移除缓存* @param key*/public static void remove(String key){if(CACHE_MAP.containsKey(key)){CACHE_MAP.remove(key);}}/*** 清理过期的缓存数据*/private static void clearCache(){if(CACHE_MAP.size() > 0){return;}Iterator<Map.Entry<String, CacheEntity>> iterator = CACHE_MAP.entrySet().iterator();while (iterator.hasNext()){Map.Entry<String, CacheEntity> entry = iterator.next();if(entry.getValue().getExpireTime() != null && entry.getValue().getExpireTime().longValue() > System.currentTimeMillis()){iterator.remove();}}}
}

最后,创建测试main方法:

/ 写入缓存数据,过期时间为3CacheUtils.put("userName", "张三", 3);// 读取缓存数据
Object value1 = CacheUtils.get("userName");
System.out.println("第一次查询结果:" + value1);// 停顿4秒
Thread.sleep(4000);// 读取缓存数据
Object value2 = CacheUtils.get("userName");
System.out.println("第二次查询结果:" + value2);

结果:

第一次查询结果:张三
第二次查询结果:null
3.2 基于 Guava Cache 实现本地缓存

Guava 是 Google 团队开源的一款 Java 核心增强库,包含集合、并发原语、缓存、IO、反射等工具箱,性能和稳定性上都有保障,应用十分广泛。而Guava Cache 很强大,支持很多特性如下:

支持最大容量限制
支持两种过期删除策略(插入时间和读取时间)
支持简单的统计功能
基于 LRU 算法实现

首先pom.xml引入guava依赖:

<!--guava-->
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version>
</dependency>

使用:

// 创建一个缓存实例
Cache<String, String> cache = CacheBuilder.newBuilder()// 初始容量.initialCapacity(5)// 最大缓存数,超出淘汰.maximumSize(10)// 过期时间.expireAfterWrite(3, TimeUnit.SECONDS).build();// 写入缓存数据
cache.put("userName", "张三");// 读取缓存数据
String value1 = cache.get("userName", () -> {// 如果key不存在,会执行回调方法return "key已过期";
});
System.out.println("第一次查询结果:" + value1);// 停顿4秒
Thread.sleep(4000);// 读取缓存数据
String value2 = cache.get("userName", () -> {// 如果key不存在,会执行回调方法return "key已过期";
});
System.out.println("第二次查询结果:" + value2);

输出结果:

第一次查询结果:张三
第二次查询结果:key已过期
3.3 基于 Caffeine 实现本地缓存

Caffeine 是基于 java8 实现的新一代缓存工具,缓存性能接近理论最优,可以看作是 Guava Cache 的增强版,功能上两者类似,不同的是 Caffeine 采用了一种结合 LRU、LFU 优点的算法:W-TinyLFU,在性能上有明显的优越性。
首先pom.xml引入caffeine依赖:

<!--caffeine-->
<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.9.3</version>
</dependency>

使用:

// 创建一个缓存实例
Cache<String, String> cache = Caffeine.newBuilder()// 初始容量.initialCapacity(5)// 最大缓存数,超出淘汰.maximumSize(10)// 设置缓存写入间隔多久过期.expireAfterWrite(3, TimeUnit.SECONDS)// 设置缓存最后访问后间隔多久淘汰,实际很少用到//.expireAfterAccess(3, TimeUnit.SECONDS).build();// 写入缓存数据
cache.put("userName", "张三");// 读取缓存数据
String value1 = cache.get("userName", (key) -> {// 如果key不存在,会执行回调方法return "key已过期";
});
System.out.println("第一次查询结果:" + value1);// 停顿4秒
Thread.sleep(4000);// 读取缓存数据
String value2 = cache.get("userName", (key) -> {// 如果key不存在,会执行回调方法return "key已过期";
});
System.out.println("第二次查询结果:" + value2);

输出结果:

第一次查询结果:张三
第二次查询结果:key已过期
3.4 基于 Encache 实现本地缓存

Encache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。

同 Caffeine 和 Guava Cache 相比,Encache 的功能更加丰富,扩展性更强,特性如下:

支持多种缓存淘汰算法,包括 LRULFUFIFO
缓存支持堆内存储、堆外存储、磁盘存储(支持持久化)三种
支持多种集群方案,解决数据共享问题

首先pom.xml引入ehcache依赖:

<!--ehcache-->
<dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId><version>3.9.7</version>
</dependency>

使用:

/*** 自定义过期策略实现*/
public  class CustomExpiryPolicy<K, V> implements ExpiryPolicy<K, V> {private final Map<K, Duration> keyExpireMap = new ConcurrentHashMap();public Duration setExpire(K key, Duration duration) {return keyExpireMap.put(key, duration);}public Duration getExpireByKey(K key) {return Optional.ofNullable(keyExpireMap.get(key)).orElse(null);}public Duration removeExpire(K key) {return keyExpireMap.remove(key);}@Overridepublic Duration getExpiryForCreation(K key, V value) {return Optional.ofNullable(getExpireByKey(key)).orElse(Duration.ofNanos(Long.MAX_VALUE));}@Overridepublic Duration getExpiryForAccess(K key, Supplier<? extends V> value) {return getExpireByKey(key);}@Overridepublic Duration getExpiryForUpdate(K key, Supplier<? extends V> oldValue, V newValue) {return getExpireByKey(key);}
}
public static void main(String[] args) throws InterruptedException {String userCache = "userCache";// 自定义过期策略CustomExpiryPolicy<Object, Object> customExpiryPolicy = new CustomExpiryPolicy<>();// 声明一个容量为20的堆内缓存配置CacheConfigurationBuilder configurationBuilder = CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(20)).withExpiry(customExpiryPolicy);// 初始化一个缓存管理器CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()// 创建cache实例.withCache(userCache, configurationBuilder).build(true);// 获取cache实例Cache<String, String> cache = cacheManager.getCache(userCache, String.class, String.class);// 获取过期策略CustomExpiryPolicy expiryPolicy = (CustomExpiryPolicy)cache.getRuntimeConfiguration().getExpiryPolicy();// 写入缓存数据cache.put("userName", "张三");// 设置3秒过期expiryPolicy.setExpire("userName", Duration.ofSeconds(3));// 读取缓存数据String value1 = cache.get("userName");System.out.println("第一次查询结果:" + value1);// 停顿4秒Thread.sleep(4000);// 读取缓存数据String value2 = cache.get("userName");System.out.println("第二次查询结果:" + value2);
}

输出结果:

第一次查询结果:张三
第二次查询结果:null
3.5 小结

在这里插入图片描述
对于本地缓存的技术选型,推荐采用 Caffeine,性能上遥遥领先。功能与Guava 类似,而Encache虽支持持久化和集群,但不如分布式缓存中间件Redis。

四、使用缓存的坑

在项目中经常会使用缓存,但用不好的话坑也挺多的:
在这里插入图片描述

4.1 缓存穿透

用户请求的id在缓存中不存在恶意用户伪造不存在的id发起请求,每次从缓存中都查不到数据,而需要查询数据库,同时数据库中也没有查到该数据,也没法放入缓存。也就是每次这个用户请求过来的时候,都要查询一次数据库。
很显然,缓存根本没起作用,好像被穿透一样,每次都会去访问数据库,而直接请求数据库数量非常多,数据库可能因为扛不住压力而崩溃。
解决方案: 缓存空值
当某个用户id在缓存中查不到,在数据库中也查不到时,也要将该用户id缓存起来,只不过值是空的。这样后面的请求,再拿相同的用户id发起请求时,就能从缓存中获取空数据,直接返回而无需再去查数据库。
比如redis:

redisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
4.2 缓存击穿

在访问热点数据时,该热点在缓存中过期失效,导致这些大量请求短时间都直接怼到数据库,可能会造成瞬间数据库压力过大,而直接挂掉。
解决方案:
(1)加锁。在访问数据库时加锁,防止多个相同keyId的请求同时访问数据库。

try {String result = jedis.set(keyId, requestId, "NX", "PX", expireTime);if ("OK".equals(result)) {return queryInfoById(keyId);}
} finally{unlock(keyId,requestId);
}  
return null;

(2)自动续期
在key快要过期之前,用job给指定key自动续期。比如redis使用lua脚本。
(3)永久有效
对于很多热门key,其实是可以不用设置过期时间,让其永久有效的。

4.3 缓存雪崩

而缓存雪崩是缓存击穿的升级版,缓存击穿说的是某一个热门key失效了,而缓存雪崩说的是有多个热门key同时失效。
缓存雪崩目前有两种:

1)有大量的热门缓存,同时失效。会导致大量的请求,访问数据库。而数据库很有可能因为扛不住压力,而直接挂掉。
(2)缓存服务器down机,可能是机器硬件问题,或者机房网络问题。总之,造成了整个缓存的不可用。

解决方案:
(1) 过期时间加随机数,不要设置相同的过期时间,可以在设置的过期时间基础上,再加个1~60秒的随机数。

实际过期时间 = 过期时间 + 1~60秒的随机数

(2)保证高可用
比如:如果使用了redis,可以使用哨兵模式,或者集群模式,避免出现单节点故障导致整个redis服务不可用的情况。

(3)服务降级
需要配置一些默认的兜底数据。程序中有个全局开关,比如有10个请求在最近一分钟内,从redis中获取数据失败,则全局开关打开。后面的新请求,就直接从配置中心中获取默认的数据。

4.4 数据不一致

数据库和缓存(比如:redis)双写数据一致性问题,是一个跟开发语言无关的公共问题。尤其高并发场景这个问题尤为严重。
解决方案
先写数据库,再删缓存!
先写数据库,再删缓存!
先写数据库,再删缓存!
除非同时满足:

缓存刚好自动失效。
读请求从数据库查出旧值,更新缓存的耗时,比写请求写数据库,并且删除缓存的还长。

才会出现数据不一致,但系统同时满足上述两个条件的概率非常小。

4.5 大key问题

在使用缓存的时候,特别是Redis,经常会遇到大key问题(缓存中单个key的value值过大)。
项目经历:

在一个风控项目中曾开发过一个分类树查询接口,系统刚上线时,数据量少,在Redis中定义的key比较小,
我在做系统设计时,也没考虑到这个问题。系统运行很长一段时间也没有问题。但随着时间的推移,用户的数据越来越多,
用户的购买行为分类树也越来越大,慢慢形成大key问题。后来某一天之后发现,线上查询客户画像接口耗时越来越长,
追查原因,发现单个用户分类数据涨到上万个,导致该接口出现性能问题,追查发现分类树json串已经接近16MB,而引发大key问题导致的。

解决方案:
(1)缩减字段名
优化在Redis中存储数据的大小,首先需要对数据进行瘦身。只保存需要用到的字段:

@AllArgsConstructor
@Data
public class Category {private Long id;private String name;private Long parentId;private Date inDate;private Long inUserId;private String inUserName;private List<Category> children;
}

这个分类对象中inDate、inUserId和inUserName字段是可以不用保存的。
然后,修改自动名称:

@AllArgsConstructor
@Data
public class Category {/*** 分类编号*/@JsonProperty("i")private Long id;/*** 分类层级*/@JsonProperty("l")private Integer level;/*** 分类名称*/@JsonProperty("n")private String name;/*** 父分类编号*/@JsonProperty("p")private Long parentId;/*** 子分类列表*/@JsonProperty("c")private List<Category> children;
}

由于在一万多条数据中,每条数据的字段名称是固定的,他们的重复率太高,由此,可以在json序列化时,改成一个简短的名称,以便于返回更少的数据大小。

(2)压缩数据
由于在Redis中保存的key/value,其中的value我是存储json格式的字符串,但是占用内存很大,所以需要对存储的数据做压缩。

由于RedisTemplate支持,value保存byte数组,因此先将json字符串数据用GZip工具类压缩成byte数组,然后保存到Redis中。

在获取数据时,将byte数组转换成json字符串,然后再转换成分类树。

这样优化之后,保存到Redis中的分类树的数据大小减少10倍,从而解决大key问题。

4.6 热key问题

二八原理描述:80%的用户经常访问20%的热点数据。引发数据倾斜,不能均匀分布,尤其是高并发系统中问题比较大。

比如有个促销系统,有几款商品性价比非常高,这些商品数据在Redis中按分片保存的,不同的数据保存在不同的服务器节点上。
如果用户疯狂抢购其中3款商品,而这3款商品正好保存在同一台Redis服务端节点。
这样会出现大量的用户请求集中访问同一天Redis服务器节点,该节点很有可能会因为扛不住这么大的压力,而直接down机。

解决方案:
(1)拆分key:提前做好评估,将热点数据分开存储在不同redis服务器来分摊压力。
(2)增加本地缓存:对于热key数据,可以增加一层本地缓存(见前文),能够提升性能的同时也能避免Redis访问量过大的问题。但可能会出现数据不一致问题。

4.7 命中率问题

前面的情况都影响缓存的命中率问题,因为可能会出现缓存不存在,或者缓存过期等问题,导致缓存不能命中。
解决方案:
(1)缓存预热
在API服务启动之前,可以先用job,将相关数据先保存到缓存中,做预热。
这样后面的请求,就能直接从缓存中获取数据,而无需访问数据库。
(2)合理调整过期时间
(3)增加缓存内存

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

lvs集群、NAT模式和DR模式

lvs集群概念 全称是linux virtual server&#xff0c;是在Linux的内核层面实现负载均衡的软件。 主要作用&#xff1a;将多个后端服务器组成一个高可用高性能的服务器集群&#xff0c;通过负载均衡的算法将客户端的请求分发到后端的服务器上&#xff0c;来实现高可用和负载均…

百元挂耳式耳机哪个品牌音质好、五大优质臻品错过可惜!

开放式耳机这么火&#xff0c;相信大家也都知道了吧&#xff0c;它避免了入耳式耳机对耳道的直接压迫&#xff0c;为用户提供了更加自然、舒适的佩戴体验。长时间使用不再感觉耳朵被束缚。但是面对市面上众多的开放式耳机品牌和产品&#xff0c;百元挂耳式耳机哪个品牌音质好&a…

米家立式学习灯怎么样?书客、米家、孩视宝三款护眼大路灯巅峰PK!

米家立式学习灯怎么样?不知从什么时候开始&#xff0c;青少年成为了近视重灾区&#xff0c;主要促成近视的原因有长时间接触电子产品、学习时的不正确姿势、不良的灯光环境等&#xff0c;除了减少电子产品的使用以及多室外活动之外&#xff0c;剩下的就是室内孩子经常学习的光…

软件渗透测试有哪些测试流程?专业CMA、CNAS软件测评机构推荐

最近几年&#xff0c;随着互联网的迅猛发展和各种网络安全事件的频发&#xff0c;软件渗透测试逐渐成为了软件开发过程中不可或缺的一环。那么&#xff0c;什么是软件渗透测试呢?软件渗透测试&#xff0c;顾名思义就是对软件系统中的安全漏洞进行评估和检测的过程&#xff0c;…

原生小程序生成二维码方法之一

效果图&#xff1a; 第一步&#xff1a;下载对应的包并构建&#xff08;工具---》构建npm&#xff09; npm install weapp-qrcode --save 第二步&#xff1a;在wxml页面声明canvas <canvas style"width: 200px; height: 200px;margin:0 auto;" canvas-id"myQ…

【MPPT太阳能升压控制器方案】远翔升压恒流驱动芯片FP7209单节电池升压24V,30V,36V,42V,48V全系列方案,高转换效率,输出带短路保护功能

高转换效率&#xff0c;太阳能控制器方案——详解太阳能控制器PWM / MPPT极简方案其设计要点&#xff0c;升压30V&#xff0c;36V&#xff0c;42V&#xff0c;48V 使用单颗芯片FP7209即实现两级升压到30V&#xff0c;36V&#xff0c;42V&#xff0c;48V&#xff0c;相对于单极升…

EtherCAT笔记(六)—— 分布时钟之一

目录 1. 分布时钟的功能 2. 分布时钟涉及到的概念 2.1 系统时间 2.2 参考时钟 & 从时钟 2.3 主站时钟 2.4 本地时钟 2.4.1 本地时钟的初始偏移量 2.4.2 本地时钟的时钟漂移 2.5 本地系统时间 2.6 传输延时 人们理解知识的一个阻碍就是那些从没见过的概念和这些概念的随意使…

【排序 - 快速排序】

快速排序&#xff08;Quick Sort&#xff09;是一种高效的排序算法&#xff0c;它基于分治&#xff08;Divide and Conquer&#xff09;的策略。这种排序算法的核心思想是选择一个基准元素&#xff0c;将数组分割成两部分&#xff0c;使得左边的元素都小于等于基准元素&#xf…

【机器学习】机器学习详解-小白入门(随记)

&#x1f388;边走、边悟&#x1f388;迟早会好 机器学习&#xff08;Machine Learning&#xff09;是一种人工智能技术&#xff0c;通过让计算机系统从数据中学习并改进其性能&#xff0c;而不是通过显式编程来完成特定任务。其核心概念是利用算法和统计模型对大量数据进行分…

农业采摘--RGBD数据转point cloud

一、RGBD图像转点云数据的步骤 将RGBD图像转点云数据常包含五个步骤&#xff1a; 1. 图像采集&#xff1a; 使用RGBD相机同时捕获颜色&#xff08;RGB&#xff09;和深度&#xff08;Depth&#xff09;信息。颜色记录了场景的彩色视觉信息&#xff0c;而深度图像记录了场景中每…

程序员学长 | PyCaret,一个超强的 python 库

本文来源公众号“程序员学长”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;PyCaret&#xff0c;一个超强的 python 库 今天给大家分享一个超强的 python 库&#xff0c;PyCaret。 https://github.com/pycaret/pycaret 简介 …

通过Umijs从0到1搭建一个React项目

有一阵时间没写react了&#xff0c;今天通过umi搭建一个demo项目复习一下react&#xff1b;umi是一个可扩展的企业级前端应用框架&#xff0c;在react市场中还是比较火的一个框架。 Umi官方文档&#xff1a;Umi 介绍 (umijs.org) 一、构建项目。 1、安装包管理工具。 官方推…

不入耳耳机哪个品牌好便宜学生、不入耳式蓝牙耳机推荐

开放式耳机相较于传统的入耳式耳机&#xff0c;极大地提升了用户的听觉享受和佩戴时的持久舒适度。然而&#xff0c;如何找到一款性价比高、品质优良的开放式耳机也是一个不小的问题。不入耳耳机哪个品牌好便宜学生&#xff1f;为了帮助大家更好地做出选择&#xff0c;我结合自…

Python爬虫:基础爬虫架构及爬取证券之星全站行情数据!

爬虫成长之路&#xff08;一&#xff09;里我们介绍了如何爬取证券之星网站上所有A股数据&#xff0c;主要涉及网页获取和页面解析的知识。爬虫成长之路&#xff08;二&#xff09;里我们介绍了如何获取代理IP并验证&#xff0c;涉及了多线程编程和数据存储的知识。此次我们将在…

在攻防演练中遇到的一个“有马蜂的蜜罐”

在攻防演练中遇到的一个“有马蜂的蜜罐” 有趣的结论&#xff0c;请一路看到文章结尾 在前几天的攻防演练中&#xff0c;我跟队友的气氛氛围都很好&#xff0c;有说有笑&#xff0c;恐怕也是全场话最多、笑最多的队伍了。 也是因为我们遇到了许多相当有趣的事情&#xff0c;其…

获取商铺信息,以及商铺信息的增删改查

本文章主要讲述如何对商铺信息进行基本的增删改查操作&#xff0c;及数据库对比。 1、获取首页仪表盘统计数据接口 待收费金额&#xff1a; SELECT count(1) as count,IFNULL(sum(total),0)as sum FROM payment_bill WHERE enabled_mark 1 AND pay_state0 欠费数据&#xf…

集群管理脚本

虚拟机集群管理脚本 文章目录 虚拟机集群管理脚本一、远程调用脚本(remote_call.sh)二、远程复制目录脚本(remote_copy.sh) 一、远程调用脚本(remote_call.sh) 如果有传命令参数&#xff0c;则执行该命令&#xff1b;如果没有传命令参数&#xff0c;则不执行。 #!/bin/bashcm…

[C++] 轻熟类和对象

类的定义 格式规范 class为定义类的关键字&#xff0c;后有类名&#xff0c;类的主体存于{}中&#xff1b;类定义结束时后面的分号不能省略&#xff1b;类体的内容成为类的成员&#xff0c;类中的变量成为成员变量&#xff0c;函数成为方法或成员函数&#xff1b;C兼容C语言的…

开发个人Go-ChatGPT--8 网站部署

开发个人Go-ChatGPT–8 网站部署 白嫖&#xff0c;白嫖&#xff0c;白嫖 平替 aliyun的收费服务&#xff0c; 白嫖&#xff0c;白嫖&#xff0c;白嫖, 以下功能全部白嫖。 Cloudflare 提供了许多便捷且免费的服务&#xff0c;以下是一些主要的免费功能&#xff1a; 免费且快…

递归 迷宫问题-java

1&#xff09;findWay方法是为了找出走出迷宫的路径&#xff0c;找到返回true&#xff0c;否则返回false 2&#xff09;&#xff08;i&#xff0c;j&#xff09;是老鼠的位置&#xff0c;初始化的位置为&#xff08;1&#xff0c;1&#xff09; 3&#xff09;因为是递归找路&am…