import
java.util.Date
;
import
java.util.Iterator
;
import
java.util.Random
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
/**
* 本地缓存组件
*/
public
class
LocalCache
implements
Cache
{
private
Logger
logger
=
LoggerFactory
.
getLogger
(
this
.
getClass
());
private
LRULinkedHashMap
<
Object
,
CacheElement
>
cacheMap
;
protected
boolean
initFlag
=
false
;
//初始化标识
protected
final
long
defaultLifeTime
=
5
*
60
*
1000
;
//5分钟
protected
boolean
warnLongerLifeTime
=
false
;
protected
final
int
DEFAULT_INITIAL_CAPACITY
=
100
;
protected
final
int
DEFAULT_MAX_CAPACITY
=
100000
;
protected
int
initialCapacity
=
DEFAULT_INITIAL_CAPACITY
;
//初始化缓存容量
protected
int
maxCapacity
=
DEFAULT_MAX_CAPACITY
;
//最大缓存容量
protected
int
timeout
=
20
;
//存取缓存操作响应超时时间(毫秒数)
private
boolean
enabled
=
true
;
private
Thread
gcThread
=
null
;
private
String
lastGCInfo
=
null
;
//最后一次GC清理信息{ size, removeCount, time ,nowTime}
private
boolean
logGCDetail
=
false
;
//记录gc清理细节
private
boolean
enableGC
=
true
;
//是否允许清理的缓存(添加元素时)
private
int
gcMode
=
0
;
//清理过期元素模式 { 0=迭代模式 ; 1=随机模式 }
private
int
gcIntervalTime
=
2
*
60
*
1000
;
//间隔时间(分钟)
private
boolean
iterateScanAll
=
true
;
//是否迭代扫描全部
private
float
gcFactor
=
0.5
F
;
//清理百分比
private
int
maxIterateSize
=
DEFAULT_MAX_CAPACITY
/
2
;
//迭代模式下一次最大迭代数量
private
volatile
int
iterateLastIndex
=
0
;
//最后迭代下标
private
int
maxRandomTimes
=
100
;
//随机模式下最大随机次数
protected
final
static
Random
random
=
new
Random
();
private
static
LocalCache
instance
=
new
LocalCache
();
public
static
LocalCache
getInstance
()
{
return
instance
;
}
private
LocalCache
(){
this
.
init
();
}
protected
synchronized
void
init
()
{
if
(
initFlag
){
logger
.
warn
(
"init repeat."
);
return
;
}
this
.
initCache
();
this
.
startGCDaemonThread
();
initFlag
=
true
;
if
(
logger
.
isInfoEnabled
()){
logger
.
info
(
"init -- OK"
);
}
}
private
void
startGCDaemonThread
(){
if
(
initFlag
){
return
;
}
this
.
maxIterateSize
=
maxCapacity
/
2
;
try
{
this
.
gcThread
=
new
Thread
()
{
public
void
run
()
{
logger
.
info
(
"["
+
(
Thread
.
currentThread
().
getName
())
+
"]start..."
);
//sleep
try
{
Thread
.
sleep
(
getGcIntervalTime
()
<
30000
?
30000
:
getGcIntervalTime
());
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
while
(
true
){
//gc
gc
();
//sleep
try
{
Thread
.
sleep
(
getGcIntervalTime
()
<
30000
?
30000
:
getGcIntervalTime
());
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
}
};
this
.
gcThread
.
setName
(
"localCache-gcThread"
);
this
.
gcThread
.
setDaemon
(
true
);
this
.
gcThread
.
start
();
if
(
logger
.
isInfoEnabled
()){
logger
.
info
(
"startGCDaemonThread -- OK"
);
}
}
catch
(
Exception
e
){
logger
.
error
(
"[localCache gc]DaemonThread -- error: "
+
e
.
getMessage
(),
e
);
}
}
private
void
initCache
(){
if
(
initFlag
){
return
;
}
initialCapacity
=
(
initialCapacity
<=
0
?
DEFAULT_INITIAL_CAPACITY
:
initialCapacity
);
maxCapacity
=
(
maxCapacity
<
initialCapacity
?
DEFAULT_MAX_CAPACITY
:
maxCapacity
);
cacheMap
=
new
LRULinkedHashMap
<
Object
,
CacheElement
>(
initialCapacity
,
maxCapacity
);
if
(
logger
.
isInfoEnabled
()){
logger
.
info
(
"initCache -- OK"
);
}
}
/*
* (non-Javadoc)
*/
@SuppressWarnings
(
"unchecked"
)
public
<
T
>
T
getCache
(
Object
key
)
{
if
(!
isEnabled
()){
return
null
;
}
long
st
=
System
.
currentTimeMillis
();
T
objValue
=
null
;
CacheElement
cacheObj
=
cacheMap
.
get
(
key
);
if
(
isExpiredCache
(
cacheObj
))
{
cacheMap
.
remove
(
key
);
}
else
{
objValue
=
(
T
)
(
cacheObj
==
null
?
null
:
cacheObj
.
getValue
());
}
long
et
=
System
.
currentTimeMillis
();
if
((
et
-
st
)>
timeout
){
if
(
this
.
logger
.
isWarnEnabled
()){
this
.
logger
.
warn
(
"getCache_timeout_"
+
(
et
-
st
)
+
"_["
+
key
+
"]"
);
}
}
if
(
logger
.
isDebugEnabled
()){
String
message
=
(
"get( "
+
key
+
") return: "
+
objValue
);
logger
.
debug
(
message
);
}
return
objValue
;
}
/*
* (non-Javadoc)
*/
public
void
putCache
(
Object
key
,
Object
value
,
Long
lifeTime
)
{
if
(!
isEnabled
()){
return
;
}
Long
st
=
System
.
currentTimeMillis
();
lifeTime
=
(
null
==
lifeTime
?
defaultLifeTime
:
lifeTime
);
CacheElement
cacheObj
=
new
CacheElement
();
cacheObj
.
setCreateTime
(
System
.
currentTimeMillis
());
cacheObj
.
setLifeTime
(
lifeTime
);
cacheObj
.
setValue
(
value
);
cacheObj
.
setKey
(
key
);
cacheMap
.
put
(
key
,
cacheObj
);
long
et
=
System
.
currentTimeMillis
();
if
((
et
-
st
)>
timeout
){
if
(
this
.
logger
.
isWarnEnabled
()){
this
.
logger
.
warn
(
"putCache_timeout_"
+
(
et
-
st
)
+
"_["
+
key
+
"]"
);
}
}
if
(
logger
.
isDebugEnabled
()){
String
message
=
(
"putCache( "
+
cacheObj
+
" ) , 耗时 "
+
(
et
-
st
)
+
"(毫秒)."
);
logger
.
debug
(
message
);
}
if
(
lifeTime
>
defaultLifeTime
&&
this
.
isWarnLongerLifeTime
()){
if
(
logger
.
isWarnEnabled
()){
String
message
=
(
"LifeTime["
+
(
lifeTime
/
1000
)
+
"秒] too long for putCache("
+
cacheObj
+
")"
);
logger
.
warn
(
message
);
}
}
}
/**
* key 是否过期
* @param key
* @return
*/
protected
boolean
isExpiredKey
(
Object
key
)
{
CacheElement
cacheObj
=
cacheMap
.
get
(
key
);
return
this
.
isExpiredCache
(
cacheObj
);
}
/**
* cacheObj 是否过期
* @param key
* @return
*/
protected
boolean
isExpiredCache
(
CacheElement
cacheObj
)
{
if
(
cacheObj
==
null
)
{
return
false
;
}
return
cacheObj
.
isExpired
();
}
/*
* (non-Javadoc)
*/
public
void
invalidateCaches
(){
try
{
cacheMap
.
clear
();
}
catch
(
Exception
e
){
e
.
printStackTrace
();
}
}
/*
* (non-Javadoc)
*/
public
void
invalidateCache
(
Object
key
){
try
{
cacheMap
.
remove
(
key
);
}
catch
(
Exception
e
){
e
.
printStackTrace
();
}
}
/*
* (non-Javadoc)
*/
public
boolean
containsKey
(
Object
key
)
{
return
cacheMap
.
containsKey
(
key
);
}
/*
* (non-Javadoc)
*/
public
int
getSize
()
{
return
cacheMap
.
size
();
}
/*
* (non-Javadoc)
*/
public
Iterator
<
Object
>
getKeyIterator
()
{
return
cacheMap
.
keySet
().
iterator
();
}
/*
* (non-Javadoc)
*/
public
boolean
isEnabled
()
{
return
this
.
enabled
;
}
/*
* (non-Javadoc)
*/
public
void
setEnabled
(
boolean
enabled
)
{
this
.
enabled
=
enabled
;
if
(!
this
.
enabled
){
//清理缓存
this
.
invalidateCaches
();
}
}
/**
* 清理过期缓存
*/
protected
synchronized
boolean
gc
(){
if
(!
isEnableGC
()){
return
false
;
}
try
{
iterateRemoveExpiredCache
();
}
catch
(
Exception
e
){
logger
.
error
(
"gc() has error: "
+
e
.
getMessage
(),
e
);
}
return
true
;
}
/**
* 迭代模式 - 移除过期的 key
* @param exceptKey
*/
private
void
iterateRemoveExpiredCache
(){
long
startTime
=
System
.
currentTimeMillis
();
int
size
=
cacheMap
.
size
();
if
(
size
==
0
){
return
;
}
int
keyCount
=
0
;
int
removedCount
=
0
;
int
startIndex
=
0
;
int
endIndex
=
0
;
try
{
Object
[]
keys
=
cacheMap
.
keySet
().
toArray
();
keyCount
=
keys
.
length
;
int
maxIndex
=
keyCount
-
1
;
//初始化扫描下标
if
(
iterateScanAll
){
startIndex
=
0
;
endIndex
=
maxIndex
;
}
else
{
int
gcThreshold
=
this
.
getGcThreshold
();
int
iterateLen
=
gcThreshold
>
this
.
maxIterateSize
?
this
.
maxIterateSize
:
gcThreshold
;
startIndex
=
this
.
iterateLastIndex
;
startIndex
=
(
(
startIndex
<
0
||
startIndex
>
maxIndex
)
?
0
:
startIndex
);
endIndex
=
(
startIndex
+
iterateLen
);
endIndex
=
(
endIndex
>
maxIndex
?
maxIndex
:
endIndex
);
}
//迭代清理
boolean
flag
=
false
;
for
(
int
i
=
startIndex
;
i
<=
endIndex
;
i
++){
flag
=
this
.
removeExpiredKey
(
keys
[
i
]);
if
(
flag
){
removedCount
++;
}
}
this
.
iterateLastIndex
=
endIndex
;
keys
=
null
;
}
catch
(
Exception
e
){
logger
.
error
(
"iterateRemoveExpiredCache -- 移除过期的 key时出现异常: "
+
e
.
getMessage
(),
e
);
}
long
endTime
=
System
.
currentTimeMillis
();
StringBuffer
sb
=
new
StringBuffer
();
sb
.
append
(
"iterateRemoveExpiredCache [ size: "
).
append
(
size
).
append
(
", keyCount: "
).
append
(
keyCount
)
.
append
(
", startIndex: "
).
append
(
startIndex
).
append
(
", endIndex: "
).
append
(
iterateLastIndex
)
.
append
(
", removedCount: "
).
append
(
removedCount
).
append
(
", currentSize: "
).
append
(
this
.
cacheMap
.
size
())
.
append
(
", timeConsuming: "
).
append
(
endTime
-
startTime
).
append
(
", nowTime: "
).
append
(
new
Date
())
.
append
(
" ]"
);
this
.
lastGCInfo
=
sb
.
toString
();
if
(
logger
.
isInfoEnabled
()){
logger
.
info
(
"iterateRemoveExpiredCache -- 清理结果 -- "
+
lastGCInfo
);
}
}
/**
* 随机模式 - 移除过期的 key
*/
private
void
randomRemoveExpiredCache
(){
long
startTime
=
System
.
currentTimeMillis
();
int
size
=
cacheMap
.
size
();
if
(
size
==
0
){
return
;
}
int
removedCount
=
0
;
try
{
Object
[]
keys
=
cacheMap
.
keySet
().
toArray
();
int
keyCount
=
keys
.
length
;
boolean
removeFlag
=
false
;
int
removeRandomTimes
=
this
.
getGcThreshold
();
removeRandomTimes
=
(
removeRandomTimes
>
this
.
getMaxRandomTimes
()
?
this
.
getMaxRandomTimes
()
:
removeRandomTimes
);
while
(
removeRandomTimes
--
>
0
){
int
index
=
random
.
nextInt
(
keyCount
);
boolean
flag
=
this
.
removeExpiredKey
(
keys
[
index
]);
if
(
flag
){
removeFlag
=
true
;
removedCount
++;
}
}
//尝试 移除 首尾元素
if
(!
removeFlag
){
this
.
removeExpiredKey
(
keys
[
0
]);
this
.
removeExpiredKey
(
keys
[
keyCount
-
1
]);
}
keys
=
null
;
}
catch
(
Exception
e
){
logger
.
error
(
"randomRemoveExpiredCache -- 移除过期的 key时出现异常: "
+
e
.
getMessage
(),
e
);
}
long
endTime
=
System
.
currentTimeMillis
();
StringBuffer
sb
=
new
StringBuffer
();
sb
.
append
(
"randomRemoveExpiredCache [ size: "
).
append
(
size
).
append
(
", removedCount: "
).
append
(
removedCount
)
.
append
(
", currentSize: "
).
append
(
this
.
cacheMap
.
size
()).
append
(
", timeConsuming: "
).
append
(
endTime
-
startTime
)
.
append
(
", nowTime: "
).
append
(
new
Date
())
.
append
(
" ]"
);
this
.
lastGCInfo
=
sb
.
toString
();
if
(
logger
.
isInfoEnabled
()){
logger
.
info
(
"randomRemoveExpiredCache -- 清理结果 -- "
+
lastGCInfo
);
}
}
private
boolean
removeExpiredKey
(
Object
key
){
boolean
flag
=
false
;
CacheElement
cacheObj
=
null
;
if
(
null
!=
key
){
try
{
cacheObj
=
cacheMap
.
get
(
key
);
boolean
isExpiredCache
=
this
.
isExpiredCache
(
cacheObj
);
if
(
isExpiredCache
){
cacheMap
.
remove
(
key
);
flag
=
true
;
}
}
catch
(
Exception
e
){
logger
.
error
(
"removeExpired("
+
key
+
") -- error: "
+
e
.
getMessage
(),
e
);
}
}
if
(!
flag
&&
logGCDetail
){
this
.
logger
.
warn
(
"removeExpiredKey("
+
key
+
") return ["
+
flag
+
"]--"
+
cacheObj
);
}
return
flag
;
}
public
int
getInitialCapacity
()
{
return
initialCapacity
;
}
public
int
getMaxCapacity
()
{
return
maxCapacity
;
}
public
int
getGcMode
()
{
return
gcMode
;
}
public
void
setGcMode
(
int
gcMode
)
{
this
.
gcMode
=
gcMode
;
}
public
int
getGcIntervalTime
()
{
return
gcIntervalTime
;
}
public
void
setGcIntervalTime
(
int
gcIntervalTime
)
{
this
.
gcIntervalTime
=
gcIntervalTime
;
}
public
boolean
isEnableGC
()
{
return
enableGC
;
}
public
void
setEnableGC
(
boolean
enableGC
)
{
this
.
enableGC
=
enableGC
;
}
public
boolean
isIterateScanAll
()
{
return
iterateScanAll
;
}
public
void
setIterateScanAll
(
boolean
iterateScanAll
)
{
this
.
iterateScanAll
=
iterateScanAll
;
}
public
float
getGcFactor
()
{
return
gcFactor
;
}
public
void
setGcFactor
(
float
gcFactor
)
{
this
.
gcFactor
=
gcFactor
;
}
/**
* gc 阀值
* @return
*/
public
int
getGcThreshold
()
{
int
threshold
=
(
int
)(
this
.
cacheMap
.
getMaxCapacity
()
*
gcFactor
);
return
threshold
;
}
public
String
getLastGCInfo
()
{
return
lastGCInfo
;
}
public
void
setLastGCInfo
(
String
lastGCInfo
)
{
this
.
lastGCInfo
=
lastGCInfo
;
}
public
boolean
isLogGCDetail
()
{
return
logGCDetail
;
}
public
void
setLogGCDetail
(
boolean
logGCDetail
)
{
this
.
logGCDetail
=
logGCDetail
;
}
public
int
getTimeout
()
{
return
timeout
;
}
public
void
setTimeout
(
int
timeout
)
{
this
.
timeout
=
timeout
;
}
public
int
getMaxIterateSize
()
{
return
maxIterateSize
;
}
public
void
setMaxIterateSize
(
int
maxIterateSize
)
{
this
.
maxIterateSize
=
maxIterateSize
;
}
public
int
getMaxRandomTimes
()
{
return
maxRandomTimes
;
}
public
void
setMaxRandomTimes
(
int
maxRandomTimes
)
{
this
.
maxRandomTimes
=
maxRandomTimes
;
}
public
boolean
isInitFlag
()
{
return
initFlag
;
}
public
long
getDefaultLifeTime
()
{
return
defaultLifeTime
;
}
public
boolean
isWarnLongerLifeTime
()
{
return
warnLongerLifeTime
;
}
public
void
setWarnLongerLifeTime
(
boolean
warnLongerLifeTime
)
{
this
.
warnLongerLifeTime
=
warnLongerLifeTime
;
}
//======================== dynMaxCapacity ========================
private
int
dynMaxCapacity
=
maxCapacity
;
public
int
getDynMaxCapacity
()
{
return
dynMaxCapacity
;
}
public
void
setDynMaxCapacity
(
int
dynMaxCapacity
)
{
this
.
dynMaxCapacity
=
dynMaxCapacity
;
}
public
void
resetMaxCapacity
(){
if
(
dynMaxCapacity
>
initialCapacity
&&
dynMaxCapacity
!=
maxCapacity
){
if
(
logger
.
isInfoEnabled
()){
logger
.
info
(
"resetMaxCapacity( "
+
dynMaxCapacity
+
" ) start..."
);
}
synchronized
(
cacheMap
){
LRULinkedHashMap
<
Object
,
CacheElement
>
cacheMap0
=
new
LRULinkedHashMap
<
Object
,
CacheElement
>(
initialCapacity
,
dynMaxCapacity
);
cacheMap
.
clear
();
cacheMap
=
cacheMap0
;
this
.
maxCapacity
=
dynMaxCapacity
;
}
if
(
logger
.
isInfoEnabled
()){
logger
.
info
(
"resetMaxCapacity( "
+
dynMaxCapacity
+
" ) OK."
);
}
}
else
{
if
(
logger
.
isWarnEnabled
()){
logger
.
warn
(
"resetMaxCapacity( "
+
dynMaxCapacity
+
" ) NO."
);
}
}
}
//======================== showCacheElement ========================
private
String
showCacheKey
;
public
String
getShowCacheKey
()
{
return
showCacheKey
;
}
public
void
setShowCacheKey
(
String
showCacheKey
)
{
this
.
showCacheKey
=
showCacheKey
;
}
public
Object
showCacheElement
(){
Object
v
=
null
;
if
(
null
!=
this
.
showCacheKey
){
v
=
cacheMap
.
get
(
showCacheKey
);
}
return
v
;
}
}