缓存更新

This commit is contained in:
法然
2023-01-03 21:37:11 +08:00
parent 81783f8aa4
commit fb475942a2
9 changed files with 192 additions and 763 deletions

View File

@@ -182,6 +182,7 @@ public static void demo() throws ExecutionException, InterruptedException {
}
```
## 3 数据驱逐
Caffeine提供以下几种剔除方式基于大小、基于权重、基于时间、基于引用
@@ -530,4 +531,45 @@ public class MyTicker implements Ticker {
return this.nanos.get();
}
}
```
```
## 7 缓存常见问题处理
### 缓存穿透——不存在的key
尝试返回一个空对象。
### 缓存击穿——大量线程同时访问同一个Key
* 在一个key失效的瞬间有大量访问进入。
* 多线程单一key互斥该包已经实现。
* 如果对 get(K) 的另一个调用当前正在加载键的值,则该线程只是等待该线程完成并返回其加载的值。 请注意,多个线程可以同时加载不同键的值。
* 如果指定的键尚未与值关联,则尝试计算其值并将其输入此缓存,除非为空。 整个方法调用是原子执行的,因此每个键最多应用一次函数。 当计算正在进行时,其他线程对此缓存的一些尝试更新操作可能会被阻塞,因此计算应该简短,并且不得尝试更新此缓存的任何其他映射。
* 在一开始的时候该key还没有加入有大量key同时访问
* 缓存预热。
```java
@PostConstruct
private void preLoading(){
List<InstanceMetaDO> instanceMetaDOS = instanceMetaDAO.findByQuery();
int nums = 0;
for(InstanceMetaDO metaDO:instanceMetaDOS){
if(StringUtils.isNotEmpty(metaDO.getInstanceId())){
nums++;
if(StringUtils.isNotEmpty(metaDO.getWorkspaceId())){
workspaceIdCache.put(metaDO.getInstanceId(),metaDO.getWorkspaceId());
}
if(StringUtils.isNotEmpty(metaDO.getInstanceId())){
tenantIdCache.put(metaDO.getInstanceId(),metaDO.getTenantId());
}
}
}
log.info("[InstanceMetaCacheService] preload count:{}",nums);
}
```
### 缓存雪崩——大量key同时失效

View File

@@ -0,0 +1,115 @@
在生产环境中会因为很多的原因造成访问请求绕过了缓存都需要访问数据库持久层虽然对Redsi缓存服务器不会造成影响但是数据库的负载就会增大使缓存的作用降低
## 1 缓存穿透
### 1、缓存穿透理解
缓存穿透是指查询一个根本不存在的数据,缓存层和持久层都不会命中。在日常工作中出于容错的考虑,如果从持久层查不到数据则不写入缓存层,缓存穿透将导致不存在的数据每次请求都要到持久层去查询,失去了缓存保护后端持久的意义。
缓存穿透示意图:
![](image/2022-12-30-16-29-15.png)
缓存穿透问题可能会使后端存储负载加大由于很多后端持久层不具备高并发性甚至可能造成后端存储宕机。通常可以在程序中统计总调用数、缓存层命中数、如果同一个Key的缓存命中率很低可能就是出现了缓存穿透问题。
造成缓存穿透的基本原因有两个。第一自身业务代码或者数据出现问题例如set 和 get 的key不一致第二一些恶意攻击、爬虫等造成大量空命中爬取线上商城商品数据超大循环递增商品的ID
### 2、解决方案
**2.1 缓存空对象**
缓存空对象是指在持久层没有命中的情况下对key进行set key,null
缓存空对象会有两个问题:
* 第一value为null 不代表不占用内存空间,空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间,比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。
* 第二缓存层和存储层的数据会有一段时间窗口的不一致可能会对业务有一定影响。例如过期时间设置为5分钟如果此时存储层添加了这个数据那此段时间就会出现缓存层和存储层数据的不一致此时可以利用消息系统或者其他方式清除掉缓存层中的空对象。
**2.2 布隆过滤器拦截**
在访问缓存层和存储层之前将存在的key用布隆过滤器提前保存起来做第一层拦截当收到一个对key请求时先用布隆过滤器验证是key否存在如果存在在进入缓存层、存储层。可以使用bitmap做布隆过滤器。这种方法适用于数据命中不高、数据相对固定、实时性低的应用场景代码维护较为复杂但是缓存空间占用少。
布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。
它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
布隆过滤器拦截的算法描述:
* 初始状态时BloomFilter是一个长度为m的位数组每一位都置为0。
* 添加元素x时x使用k个hash函数得到k个hash值对m取余对应的bit位设置为1。
* 判断y是否属于这个集合对y使用k个哈希函数得到k个哈希值对m取余所有对应的位置都是1则认为y属于该集合哈希冲突可能存在误判否则就认为y不属于该集合。可以通过增加哈希函数和增加二进制位数组的长度来降低错报率。
**2.3、两种方案的对比**
![](image/2022-12-30-16-31-00.png)
## 2 缓存击穿
### 1、缓存击穿的理解
系统中存在以下两个问题时需要引起注意:
* 当前key是一个热点key例如一个秒杀活动并发量非常大。
* 重建缓存不能在短时间完成可能是一个复杂计算例如复杂的SQL、多次IO、多个依赖等。
在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。
### 2、解决方案
**2.1 分布式互斥锁**
只允许一个线程重建缓存其他线程等待重建缓存的线程执行完重新从缓存获取数据即可。set(key,value,timeout)
![](image/2022-12-30-16-31-48.png)
**2.2、永不过期**
从缓存层面来看确实没有设置过期时间所以不会出现热点key过期后产生的问题也就是“物理”不过期。
从功能层面来看为每个value设置一个逻辑过期时间当发现超过逻辑过期时间后会使用单独的线程去更新缓
![](image/2022-12-30-16-33-01.png)
### 3、两种方案对比
* 分布式互斥锁:这种方案思路比较简单,但是存在一定的隐患,如果在查询数据库 + 和 重建缓存key失效后进行了大量的计算时间过长也可能会存在死锁和线程池阻塞的风险高并发情景下吞吐量会大大降低但是这种方法能够较好地降低后端存储负载并在一致性上做得比较好。
* 永远不过期这种方案由于没有设置真正的过期时间实际上已经不存在热点key产生的一系列危害但是会存在数据不一致的情况同时代码复杂度会增大。
## 3 缓存雪崩
### 1、概念理解
如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。
### 2、解决方案
**2.1 缓存层高可用:**
可以把缓存层设计成高可用的即使个别节点、个别机器、甚至是机房宕掉依然可以提供服务。利用sentinel或cluster实现。
**2.2 做二级缓存,或者双缓存策略:**
采用多级缓存本地进程作为一级缓存redis作为二级缓存不同级别的缓存设置的超时时间不同即使某级缓存过期了也有其他级别缓存兜底
**2.3 数据预热:**
可以通过缓存reload机制预先去更新缓存再即将发生大并发访问前手动触发加载缓存不同的key设置不同的过期时间让缓存失效的时间点尽量均匀
**2.4 加锁排队**
* 限流-- 限流算法. 1.计数 2.滑动窗口 3. 令牌桶Token Bucket 4.漏桶 leaky bucket [1]
* 在缓存失效后通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存其他线程等待。
* 业界比较常用的做法是使用mutex。简单地来说就是在缓存失效的时候判断拿出来的值为空不是立即去load db而是先使用缓存工具的某些带成功操作返回值的操作比如Redis的SETNX或者Memcache的ADD去set一个mutex key当操作返回成功时再进行load db的操作并回设缓存否则就重试整个get缓存的方法。
* SETNX是「SET if Not eXists」的缩写也就是只有不存在的时候才设置可以利用它来实现锁的效果。
## 总结
* 不存在的key——缓存穿透——缓存空对象
* 同一个Key失效大量访问——缓存击穿——加锁互斥
* 大量key同时失效——缓存雪崩——双缓存、队列
GuavaCache本身实现了加锁互斥有效防止缓存击穿。
* 更新锁定类似于分布式锁作用体现于对同一个key只让一个请求去读源并回填缓存其他请求阻塞等待。

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

View File

@@ -168,6 +168,22 @@ lambda表达式是为了简化接口的实现的。在lambda表达式中
函数引用引用⼀个已经存在的方法使其替代lambda表达式完成接口的实现
### 与lambda表达式的不同
lambda和方法引用之间有一个关键的区别
* Lambda 是惰性的,它们只会在调用方法时调用类构造函数。
* 方法引用,构造函数只会在分配了方法引用的地方被立即调用,而不是在方法调用时调用。
对象方法引用,对象必须已经初始化。类方法引用,类也一定会进行初始化。例如
* lambda表达式这一行不会创建对象
```java
Runnable universeImpactRunnable = () -> new ChuckNorris().roundHouseKick();
```
* 方法引用,这一行会创建对象。
```
Runnable galaxyImpactRunnable = new ChuckNorris()::roundHouseKick;
```
### 静态方法的引用
```

192
test.json
View File

@@ -1,191 +1 @@
| sofa.ms.sg.apprelations.query | |
| sofa.ms.sg.apps.list | |
| sofa.ms.sg.serviceclients.sync | -线 |
| sofa.ms.sg.serviceheartbeats.sync | - |
| sofa.ms.sg.servicekeyword.query | |
| sofa.ms.sg.serviceparam.batchquery | - |
| sofa.ms.sg.serviceparams.disable | |
| sofa.ms.sg.serviceparams.enable | |
| sofa.ms.sg.serviceparams.reinit | |
| sofa.ms.sg.serviceparamweight.set | |
| sofa.ms.sg.servicepubkeys.query | ip |
| sofa.ms.sg.servicepublishers.offline | paas 线 |
| sofa.ms.sg.servicepublishers.query | |
| sofa.ms.sg.servicepublishers.sync | - |
| sofa.ms.sg.servicepubrelations.query | |
| sofa.ms.sg.servicepubs.list | -DataIdIPpub |
| sofa.ms.sg.servicepubs.query | |
| sofa.ms.sg.services.query | - |
| sofa.ms.sg.servicesubkeys.query | ip |
| sofa.ms.sg.servicesubrelations.query | |
| sofa.ms.sg.servicesubs.list | -DataIdIPsub |
| sofa.ms.sg.servicesubs.query | |
| sofa.ms.sg.servicesubscribers.query | |
| sofa.ms.sg.servicesubscribers.sync | - |
| sofa.ms.sg.servicesummarys.query | - |
| sofa.ms.sg.serviceapps.list | |
### DRM
| | |
| ---- | ---- |
| sofa.ms.drm.drmapps.all | App name |
| sofa.ms.drm.drmattributes.add | drm |
| sofa.ms.drm.drmattributes.delete | drm |
| sofa.ms.drm.drmattributes.get | drm |
| sofa.ms.drm.drmattributes.update | drm |
| sofa.ms.drm.drmcells.all | |
| sofa.ms.drm.drmclients.query | |
| sofa.ms.drm.drmdata.add | drm |
| sofa.ms.drm.drmdatacells.list | Drm |
| sofa.ms.drm.drmgraydata.add | |
| sofa.ms.drm.drmhosts.query | |
| sofa.ms.drm.drmpushlogs.list | |
| sofa.ms.drm.drmpushlogs.query | |
| sofa.ms.drm.drmresources.add | drm |
| sofa.ms.drm.drmresources.delete | Delete resource |
| sofa.ms.drm.drmresources.export | workspace |
| sofa.ms.drm.drmresources.import | drm |
| sofa.ms.drm.drmresources.query | Drm |
| sofa.ms.drm.drmresources.update | drm |
| sofa.ms.sg.resource.export | |
| sofa.ms.sg.resource.import | |
###
| | |
| ---- | ---- |
| sofa.ms.guardian.guardianappnames.all | -appName |
| sofa.ms.guardian.guardianapps.add | App |
| sofa.ms.guardian.guardianapps.all | -app |
| sofa.ms.guardian.guardianapps.delete | app |
| sofa.ms.guardian.guardianapps.get | ms -appNameapp |
| sofa.ms.guardian.guardianapps.query | -app |
| sofa.ms.guardian.guardianapps.update | ws-app |
| sofa.ms.guardian.guardianconfig.get | - |
| sofa.ms.guardian.guardiangrayrules.add | |
| sofa.ms.guardian.guardiangrayrulestatus.update | |
| sofa.ms.guardian.guardianrulepushhistorys.list | |
| sofa.ms.guardian.guardianrulepushhistorys.query | |
| sofa.ms.guardian.guardianrules.add | |
| sofa.ms.guardian.guardianrules.delete | |
| sofa.ms.guardian.guardianrules.export | guardian |
| sofa.ms.guardian.guardianrules.get | id |
| sofa.ms.guardian.guardianrules.import | guardian |
| sofa.ms.guardian.guardianrules.query | ms - |
| sofa.ms.guardian.guardianrules.update | |
| sofa.ms.guardian.guardianrulestatus.update | |
###
| | |
| ---- | ---- |
| sofa.ms.sg.routerruleallapps.list | |
| sofa.ms.sg.routerruleallservices.list | |
| sofa.ms.sg.routerruleappnames.list | -DataIdAppNames |
| sofa.ms.sg.routerruledispatchlogs.query | - |
| sofa.ms.sg.routerrulegroup.delete | |
| sofa.ms.sg.routerrulegroup.export | |
| sofa.ms.sg.routerrulegroup.import | |
| sofa.ms.sg.routerrulegroupapps.list | |
| sofa.ms.sg.routerrulegroups.add | |
| sofa.ms.sg.routerrulegroups.list | |
| sofa.ms.sg.routerrulegroups.update | |
| sofa.ms.sg.routerrulegroupstatus.update | |
| sofa.ms.sg.routerrules.enable | - |
| sofa.ms.sg.routerrules.get | |
| sofa.ms.sg.routerrulesnapshots.get | - |
| sofa.ms.sg.routerrulesnapshots.rollback | - |
| sofa.ms.sg.routerrulestatus.query | |
| sofa.ms.sg.routerruleversions.list | |
###
| | |
| ---- | ---- |
| sofa.ms.sg.circuitbreakerrules.add | |
| sofa.ms.sg.circuitbreakerrules.all | appName |
| sofa.ms.sg.circuitbreakerrules.delete | |
| sofa.ms.sg.circuitbreakerrules.enable | |
| sofa.ms.sg.circuitbreakerrules.export | |
| sofa.ms.sg.circuitbreakerrules.get | appName |
| sofa.ms.sg.circuitbreakerrules.import | |
| sofa.ms.sg.circuitbreakerrules.list | |
| sofa.ms.sg.circuitbreakerrules.query | idid |
| sofa.ms.sg.circuitbreakerrules.set | |
| sofa.ms.sg.circuitbreakerrules.update | |
| sofa.ms.sg.downgradegroups.disable | |
###
| | |
| ---- | ---- |
| sofa.ms.sg.downgradegroups.enable | |
| sofa.ms.sg.downgradegroups.query | |
| sofa.ms.sg.downgrades.add | |
| sofa.ms.sg.downgrades.delete | |
| sofa.ms.sg.downgrades.disable | |
| sofa.ms.sg.downgrades.enable | |
| sofa.ms.sg.downgrades.export | |
| sofa.ms.sg.downgrades.import | |
| sofa.ms.sg.downgrades.update | |
###
| | |
| ---- | ---- |
| sofa.ms.sg.faultinjectallapps.list | |
| sofa.ms.sg.faultinjectallservices.list | |
| sofa.ms.sg.faultinjectapps.list | |
| sofa.ms.sg.faultinjectrule.add | |
| sofa.ms.sg.faultinjectrule.delete | |
| sofa.ms.sg.faultinjectrule.export | |
| sofa.ms.sg.faultinjectrule.import | |
| sofa.ms.sg.faultinjectrule.update | |
| sofa.ms.sg.faultinjectrules.list | |
| sofa.ms.sg.faultinjectrulestatus.update | |
| sofa.ms.sg.faulttoleranceapps.list | App |
| sofa.ms.sg.faulttolerancerule.add | |
| sofa.ms.sg.faulttolerancerule.delete | |
| sofa.ms.sg.faulttolerancerule.enable | |
| sofa.ms.sg.faulttolerancerule.export | |
| sofa.ms.sg.faulttolerancerule.import | |
| sofa.ms.sg.faulttolerancerule.update | |
| sofa.ms.sg.faulttolerancerules.list | |
###
| | |
| ---- | ---- |
| sofa.ms.sg.authruleactiveappnames.list | |
| sofa.ms.sg.authruleactivedataids.list | |
| sofa.ms.sg.authruleappnames.list | |
| sofa.ms.sg.authruledataids.list | ID |
| sofa.ms.sg.authrulegroupapps.list | |
| sofa.ms.sg.authrulegroups.disable | |
| sofa.ms.sg.authrulegroups.enable | |
| sofa.ms.sg.authrulegroups.export | |
| sofa.ms.sg.authrulegroups.import | |
| sofa.ms.sg.authrulegroups.list | |
| sofa.ms.sg.authrulegroups.retry | |
| sofa.ms.sg.authrulegroups.save | |
| sofa.ms.sg.authrulegroupsbatch.add | () |
| sofa.ms.sg.authrulegroupsbatch.disable | |
| sofa.ms.sg.authrulegroupsbatch.enable | |
| sofa.ms.sg.authrulegroupservices.list | |
| sofa.ms.sg.authrules.add | |
| sofa.ms.sg.authrules.delete | |
| sofa.ms.sg.authrules.disable | |
| sofa.ms.sg.authrules.enable | |
| sofa.ms.sg.authrules.reinit | |
| sofa.ms.sg.authrules.update | |
###
| | |
| ---- | ---- |
| sofa.ms.sg.auditlogs.list | |
"7HBPQOV8F4OK","VRULPU2VVMZN","LARX4EGMTLT9","F5Z2YNZT2RNI","3GIVRPTL0IGK","VRULPU2VVMZN","LARX4EGMTLT9","EQLBTL6URR8H","VRULPU2VVMZN","LARX4EGMTLT9","BFHOBR8AYDYC","ZIC5FSHHEKL2","VIFEZDN1YVYU"

588
test.sh
View File

@@ -1,571 +1,17 @@
# cat zclean.sh
#!/bin/bash
# version: 0.9.2
export LANG=C
export PATH=/sbin:/bin:/usr/local/sbin:/usr/sbin:/usr/local/bin:/usr/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/admin/bin
readonly LOGS_DIR=/home/admin/logs/
readonly CONF_FILE=/home/admin/conf/zclean.conf
# 兼容逻辑,部分老应用通过云游部署会带上 app.env 等前缀,这里为了处理这种情况用 grep 来过滤
RESERVE=`([[ -f /opt/antcloud/conf/env.file ]] && cat /opt/antcloud/conf/env.file || env) |grep ZCLEAN_RESERVE_DAYS | awk -F= '{print $2}'`
RESERVE=${RESERVE:-14}
MAX_LOG_DIR_SIZE=`([[ -f /opt/antcloud/conf/env.file ]] && cat /opt/antcloud/conf/env.file || env) |grep ZCLEAN_MAX_LOG_DIR_SIZE | awk -F= '{print $2}'`
MAX_LOG_DIR_SIZE=${MAX_LOG_DIR_SIZE:-100} # unit is G
DELETE_FLAG='-delete'
DEBUG=''
CHUNK_SIZE=''
INTERACTIVE=0
ZCLEAN_DIGEST="${LOGS_DIR}/zclean.log.$(date +%F)"
{
readonly ZCLEAN_OK=1
readonly ZCLEAN_CRUSH=2
readonly ZCLEAN_ERROR=3
readonly ZCLEAN_IGNORE=4
}
[[ ! -d $LOGS_DIR ]] && exit
CMD_PREFIX=''
if $(which ionice >& /dev/null); then
CMD_PREFIX="ionice -c3 "
fi
if $(which nice >& /dev/null); then
CMD_PREFIX="nice -n 19 $CMD_PREFIX"
fi
FIND_CMD="${CMD_PREFIX}find"
RM_CMD="${CMD_PREFIX}rm"
TRUNCATE_CMD=''
if $(which truncate >& /dev/null); then
TRUNCATE_CMD="${CMD_PREFIX}truncate"
fi
LSOF_CMD=''
if $(which lsof >& /dev/null); then
LSOF_CMD="lsof"
fi
LSOF_FILE=/tmp/zclean_lsof.out
if [[ -d /dev/shm ]]; then
shm_mode=$(stat -c "%A" /dev/shm)
if [[ $shm_mode == drwxrwxrwt ]]; then
LSOF_FILE=/dev/shm/zclean_lsof.out
fi
fi
prepare_lsof() {
# walkaroud for Alios7 kenrel bug
if [[ $HOSTNAME =~ paycorecloud-30- ]]; then
FIND_CMD $LOGS_DIR -name '*.log' > $LSOF_FILE
return
fi
if [[ $HOSTNAME =~ paycorecloud-31- ]]; then
FIND_CMD $LOGS_DIR -name '*.log' > $LSOF_FILE
return
fi
if [[ -n $LSOF_CMD ]]; then
ulimit -n 1024
$LSOF_CMD +D $LOGS_DIR 2> /dev/null > $LSOF_FILE
fi
}
delete_lsof() {
$RM_CMD -rf $LSOF_FILE
}
# only return true when all ready
file_in_lsof() {
local fpath=$1
if [[ -n $LSOF_CMD && -f $LSOF_FILE ]]; then
grep -q $fpath $LSOF_FILE
return $?
else
return 1
fi
}
log_error() {
echo $(date +"%F %T") [ERROR] $@ >> $ZCLEAN_DIGEST
}
log_info() {
echo $(date +"%F %T") [INFO] $@ >> $ZCLEAN_DIGEST
}
log_warn() {
echo $(date +"%F %T") [WARN] $@ >> $ZCLEAN_DIGEST
}
log_debug() {
[[ $DEBUG != '-debug' ]] && return
echo $(date +"%F %T") [DEBUG] $@ >> $ZCLEAN_DIGEST
}
delete_files() {
[[ $DELETE_FLAG != '-delete' ]] && return
$RM_CMD -rf "$@" >& /dev/null
}
crush_files() {
[[ $DELETE_FLAG != '-delete' ]] && return
for f in "$@"; do
> $f
done
}
clean_file() {
# eliminates file in a low-speed way (default: 20MB/S)
local fpath=$1
local fsize=$2
local chunksize=${CHUNK_SIZE:-20}
if [[ $DELETE_FLAG != '-delete' || ! -f $fpath ]]; then
return $ZCLEAN_ERROR
fi
local is_open=0
if file_in_lsof $fpath >& /dev/null; then
is_open=1
fi
if [[ $is_open -eq 1 && $fsize -eq 0 ]]; then
log_debug "ignore $fpath(+) size $fsize"
return $ZCLEAN_IGNORE
fi
if [[ $chunksize -eq 0 || -z $TRUNCATE_CMD ]]; then
# fast delete
if [[ $is_open -eq 1 ]]; then
crush_files $fpath
log_debug "removed $fpath(+) size $fsize directly"
else
delete_files $fpath
log_debug "removed $fpath size $fsize directly"
fi
else
# slow delete
local tstart=$SECONDS
local tstake=$((1+tstart))
local loop=$((fsize/(1048576*chunksize)+1))
local tdiff
if [[ $fsize -eq 0 ]]; then
loop=0
fi
for ((i=0; i<loop; ++i)); do
$TRUNCATE_CMD -s "-${chunksize}M" $fpath
tdiff=$((tstake-SECONDS))
if [[ $tdiff -gt 0 ]]; then
sleep $tdiff
fi
tstake=$((tstake+1))
done
if [[ $is_open -eq 1 ]]; then
log_debug \
"removed $fpath(+) size $fsize in $((SECONDS-tstart)) seconds"
else
log_debug \
"removed $fpath size $fsize in $((SECONDS-tstart)) seconds"
fi
fi
# here a time delta between lsof and remove
if [[ -n $LSOF_CMD && $is_open -eq 0 ]]; then
delete_files $fpath
return $ZCLEAN_OK
else
return $ZCLEAN_CRUSH
fi
}
get_home_usage() {
local usage
#usage=$(df $LOGS_DIR|awk 'END {print $5}'|tr -d '%')
usage=$(df $LOGS_DIR|tail -n 1|awk '{print $(NF-1)}'|sed -e 's/%//g')
if [[ -z $usage ]]; then
log_error "can't get home partition usage"
exit 1
fi
usage_by_du=`du -sk $LOGS_DIR | awk '{print $1}'`
usage_by_du=$(( (usage_by_du * 100) / (MAX_LOG_DIR_SIZE * 1024 * 1024) ))
if [[ $usage_by_du -gt $usage ]]; then
log_info "calculate usage based on MAX_LOG_DIR_SIZE, MAX_LOG_DIR_SIZE: $MAX_LOG_DIR_SIZE, usage: $usage_by_du"
usage=$usage_by_du
fi
echo $usage
}
sleep_dif()
{
local secs idc index
if [[ $HOSTNAME =~ ^[a-z0-9]+-[0-9]+-[0-9]+$ ]]; then
idc=$(echo $HOSTNAME|awk -F- '{print $2}')
index=$(echo $HOSTNAME|awk -F- '{print $3}')
secs=$(( (index*19 +idc*7)%233 ))
else
secs=$((RANDOM%133))
fi
sleep $secs
log_info slept $secs seconds
}
clean_expired() {
local keep_days=$((RESERVE-1))
if [[ $HOSTNAME =~ paycorecloud-30- ]]; then
keep_days=1
fi
local fpath fsize fmtime how_long expired
local ret_code=$ZCLEAN_OK
$FIND_CMD $LOGS_DIR \
-type f \
-name '*log*' \
! -name '*\.[0-9]dt\.log*' \
! -name '*\.[0-9][0-9]dt\.log*' \
! -name '*\.[0-9][0-9][0-9]dt\.log*' \
-mtime +$keep_days \
-printf '%p %s\n' | \
while read fpath fsize; do
clean_file $fpath $fsize
ret_code=$?
if [[ $ret_code -eq $ZCLEAN_OK || $ret_code -eq $ZCLEAN_CRUSH ]]; then
log_info "deleted expired file $fpath size $fsize"
fi
done
# http://doc.alipay.net/pages/viewpage.action?pageId=71187095
$FIND_CMD $LOGS_DIR \
-type f \
\( -name '*\.[0-9]dt\.log*' -o \
-name '*\.[0-9][0-9]dt\.log*' -o \
-name '*\.[0-9][0-9][0-9]dt\.log*' \) \
-printf '%p %s %TY-%Tm-%Td\n' | \
while read fpath fsize fmtime; do
how_long=$(echo $fpath | grep -o '[0-9]\+dt' | tr -d '[a-z]')
expired=$(date -d"$how_long days ago" +"%F")
if [[ $fmtime > $expired ]]; then
continue
else
clean_file $fpath $fsize
ret_code=$?
if [[ $ret_code -eq $ZCLEAN_OK || $ret_code -eq $ZCLEAN_CRUSH ]]; then
log_info "deleted expired file $fpath size $fsize"
fi
fi
done
}
clean_huge() {
local blocks big_size fpath fsize
blocks=$(df /home -k|awk 'END {print $2}')
if [[ ! $? ]]; then
log_error "can't get home partition total size"
exit 1
fi
if [[ $blocks -ge ${MAX_LOG_DIR_SIZE}*1024*1024 ]]; then
blocks=$(( MAX_LOG_DIR_SIZE*1024*1024 ))
fi
# 120G
if [[ $blocks -ge 125829120 ]]; then
big_size=50G
else
big_size=30G
fi
$FIND_CMD $LOGS_DIR \
-type f \
-name '*log*' \
-size +$big_size \
-printf '%p %s\n' | \
while read fpath fsize; do
crush_files "$fpath"
log_warn "deleted huge file $fpath size $fsize"
done
}
clean_by_day() {
local how_long=$1
local ret_code=$ZCLEAN_OK
$FIND_CMD $LOGS_DIR \
-type f \
-name '*log*' \
-mtime "+${how_long}" \
-printf '%p %s\n' | \
while read fpath fsize; do
clean_file $fpath $fsize
ret_code=$?
if [[ $ret_code -eq $ZCLEAN_OK || $ret_code -eq $ZCLEAN_CRUSH ]]; then
log_info "deleted $((how_long+1)) days ago file $fpath size $fsize"
fi
done
}
clean_by_hour() {
local how_long=$1
local ret_code=$ZCLEAN_OK
$FIND_CMD $LOGS_DIR \
-type f \
-name '*log*' \
-mmin "+$((how_long*60))" \
-printf '%p %s\n' | \
while read fpath fsize; do
clean_file $fpath $fsize
ret_code=$?
if [[ $ret_code -eq $ZCLEAN_OK || $ret_code -eq $ZCLEAN_CRUSH ]]; then
log_info "deleted $how_long hours ago file $fpath size $fsize"
fi
done
}
clean_largest() {
local fsize fpath fblock
local ret_code=$ZCLEAN_OK
$FIND_CMD $LOGS_DIR \
-type f \
-printf '%b %s %p\n' | \
sort -nr | head -1 | \
while read fblock fsize fpath ; do
# 10G
if [[ $fsize -gt 10737418240 ]]; then
crush_files $fpath
else
clean_file $fpath $fsize
fi
ret_code=$?
if [[ $ret_code -eq $ZCLEAN_OK || $ret_code -eq $ZCLEAN_CRUSH ]]; then
log_info "deleted largest file $fpath size $fsize"
fi
done
}
in_low_traffic() {
local now=$(date '+%R')
if [[ "$now" > "04:00" && "$now" < "04:30" ]]; then
return 0
else
return 1
fi
}
clean_until() {
local from_rate to_rate cur_usage old_usage how_long count force
how_long=$((RESERVE-1))
from_rate=$1
to_rate=$2
force=$3
count=0
cur_usage=$(get_home_usage)
# should exist some huge files
if [[ $cur_usage -ge 97 ]]; then
clean_huge
old_usage=$cur_usage
cur_usage=$(get_home_usage)
if [[ $cur_usage -ne $old_usage ]]; then
log_info "usage from $old_usage to $cur_usage"
fi
fi
if ! in_low_traffic; then
[[ $cur_usage -lt $from_rate ]] && return
fi
prepare_lsof
clean_expired
old_usage=$cur_usage
cur_usage=$(get_home_usage)
if [[ $cur_usage -ne $old_usage ]]; then
log_info "usage from $old_usage to $cur_usage"
fi
# now we have to remove recent logs by date
while [[ $cur_usage -gt $to_rate ]]; do
if [[ $how_long -lt 1 ]]; then
break
else
how_long=$((how_long-1))
fi
clean_by_day $how_long
old_usage=$cur_usage
cur_usage=$(get_home_usage)
if [[ $cur_usage -ne $old_usage ]]; then
log_info "usage from $old_usage to $cur_usage"
fi
done
# in hours
how_long=24
while [[ $cur_usage -gt $to_rate ]]; do
if [[ $how_long -lt 2 ]]; then
break
else
how_long=$((how_long-1))
fi
clean_by_hour $how_long
old_usage=$cur_usage
cur_usage=$(get_home_usage)
if [[ $cur_usage -ne $old_usage ]]; then
log_info "usage from $old_usage to $cur_usage"
fi
done
[[ $force -ne 1 ]] && return
# last resort, find top size logs to deleted
if [[ $CHUNK_SIZE -ne 0 ]]; then
CHUNK_SIZE=100
fi
while [[ $cur_usage -gt $to_rate ]]; do
if [[ $count -gt 5 ]]; then
log_error "give up deleting largest files"
break
fi
count=$((count+1))
clean_largest
old_usage=$cur_usage
cur_usage=$(get_home_usage)
if [[ $cur_usage -ne $old_usage ]]; then
log_info "usage from $old_usage to $cur_usage"
fi
done
delete_lsof
}
ensure_unique() {
local pgid=$(ps -p $$ -o pgid=)
local pids=$(ps -e -o pid,pgid,cmd | \
grep [z]clean | grep bash | \
awk "\$2 != $pgid {print \$1}")
if [[ -n $pids ]]; then
if [[ $INTERACTIVE -eq 1 ]]; then
kill $pids
else
log_info "$0 is running, wait for another round of dispatch"
exit 0
fi
fi
}
_main() {
local to_rate=90
local from_rate=$to_rate
local do_sleep=0
local force=0
# load config
if [[ -f $CONF_FILE && ! "$*" =~ --noconf ]]; then
while read -r line; do
key=$(echo $line|cut -d= -f1)
value=$(echo $line|cut -d= -f2)
case $key in
to)
to_rate=$value;;
block)
CHUNK_SIZE=$value;;
fast)
CHUNK_SIZE=0;;
from)
from_rate=$value;;
max_size)
MAX_LOG_DIR_SIZE=$value;;
sleep)
do_sleep=1;;
debug)
DEBUG='-debug';;
force)
force=1;;
*)
;;
esac
done < $CONF_FILE
fi
# option help
# -r clean to this ratio
# -b wipe this blocksize each time
# -t start cleaning when above this ratio
# -m max size of log dirunit is G
# -n fast delete (use rm -rf)
# -s random sleep awhile in a app clusters
# -d extra debug logging
# -f force delete largest file
while getopts ":r:b:t:nsdfi" opt; do
case $opt in
r)
if [[ ! $OPTARG =~ ^[0-9]+$ ]]; then
echo "$0: rate $OPTARG is an invalid number" >&2
exit 1;
fi
if [[ $OPTARG -le 1 || $OPTARG -ge 99 ]]; then
echo "$0: rate $OPTARG out of range (1, 99)" >&2
exit 1;
fi
to_rate=$OPTARG ;;
b)
if [[ ! $OPTARG =~ ^[0-9]+[mMgG]?$ ]]; then
echo "$0: block size $OPTARG is invalid" >&2
exit 1;
fi
if [[ $OPTARG =~ [gG]$ ]]; then
CHUNK_SIZE=$(echo $OPTARG|tr -d 'gG')
CHUNK_SIZE=$((CHUNK_SIZE*1024))
else
CHUNK_SIZE=$(echo $OPTARG|tr -d 'mM')
fi ;;
t)
if [[ ! $OPTARG =~ ^[0-9]+$ ]]; then
echo "$0: rate $OPTARG is an invalid number" >&2
exit 1;
fi
if [[ $OPTARG -le 1 || $OPTARG -ge 99 ]]; then
echo "$0: rate $OPTARG out of range (1, 99)" >&2
exit 1;
fi
from_rate=$OPTARG ;;
m)
if [[ ! $OPTARG =~ ^[0-9]+$ ]]; then
echo "$0: max size $OPTARG is invalid" >&2
exit 1;
fi
MAX_LOG_DIR_SIZE=$OPTARG ;;
n)
CHUNK_SIZE=0 ;;
s)
do_sleep=1 ;;
d)
DEBUG='-debug' ;;
f)
force=1 ;;
i)
INTERACTIVE=1 ;;
\?)
echo "$0: invalid option: -$OPTARG" >&2
exit 1;;
:)
echo "$0: option -$OPTARG requires an argument" >&2
exit 1 ;;
esac
done
if [[ $to_rate -ge $from_rate ]]; then
to_rate=$from_rate
fi
ensure_unique
[[ $do_sleep -eq 1 ]] && sleep_dif
clean_until $from_rate $to_rate $force
}
# TODO make a decision whether /home/admin is innocent
# TODO deamonize
_main "$@"
+-----+--------------+---------------+------------------+-----------+----------+---------------------+---------------------+
| id | instance_id | instance_name | workspace_id | tenant_id | operator | gmt_create | gmt_modified |
+-----+--------------+---------------+------------------+-----------+----------+---------------------+---------------------+
| 432 | 7HBPQOV8F4OK | default | acbossprod | ANTCLOUDF | NULL | 2022-02-07 10:13:11 | 2022-02-07 10:13:11 |
| 443 | VRULPU2VVMZN | default | acgatewaypre | ANTCLOUDF | NULL | 2022-03-01 21:00:01 | 2022-03-01 21:00:01 |
| 461 | LARX4EGMTLT9 | default | acgatewayprod | ANTCLOUDF | NULL | 2022-03-25 16:03:15 | 2022-03-25 16:03:15 |
| 425 | F5Z2YNZT2RNI | default | acnbosspre | ANTCLOUDF | NULL | 2021-12-22 21:11:02 | 2021-12-22 21:11:02 |
| 424 | 3GIVRPTL0IGK | default | acnbossprod | ANTCLOUDF | NULL | 2021-12-22 21:10:12 | 2021-12-22 21:10:12 |
| 404 | VRULPU2VVMZN | default | antchainbosspre | ANTCLOUDF | NULL | 2021-10-13 00:52:24 | 2021-10-13 00:52:24 |
| 402 | LARX4EGMTLT9 | default | antchainbossprod | ANTCLOUDF | NULL | 2021-09-30 14:51:19 | 2021-09-30 14:51:19 |
| 473 | EQLBTL6URR8H | default | antstackDoc | ANTCLOUDF | NULL | 2022-05-10 14:41:23 | 2022-05-10 14:41:23 |
| 428 | VRULPU2VVMZN | default | bosspre | ANTCLOUDF | NULL | 2022-01-05 11:17:40 | 2022-01-05 11:17:40 |
| 429 | LARX4EGMTLT9 | default | bossprod | ANTCLOUDF | NULL | 2022-01-05 11:18:10 | 2022-01-05 11:18:10 |
| 409 | BFHOBR8AYDYC | default | cnhzpfmayprekms | ANTCLOUDF | NULL | 2021-10-29 10:33:39 | 2021-10-29 10:33:39 |
| 401 | ZIC5FSHHEKL2 | default | cnhzpfmaypub01 | ANTCLOUDF | NULL | 2021-09-27 17:15:23 | 2021-09-27 17:15:23 |
| 476 | VIFEZDN1YVYU | default | traasprod | ANTCLOUDF | NULL | 2022-05-20 11:51:34 | 2022-05-20 11:51:34 |
+-----+--------------+---------------+------------------+-----------+----------+---------------------+---------------------+