doc: 阅读redis sentinel文档

This commit is contained in:
2025-12-22 16:44:19 +08:00
parent 61578e0ed2
commit f5d24865b5
9 changed files with 2140 additions and 2057 deletions

View File

@@ -1,33 +1,33 @@
# Spring Cloud简介
## eureka
eureka是由netflix公司开源的一个服务注册与发现组件。eureka和其他一些同样由netflix公司开源的组建一起被整合为spring cloud netflix模块。
### eureka和zookeeper的区别
#### CAP原则
CAP原则指的是在一个分布式系统中一致性Consistency、可用性Aailability和分区容错性Partition Tolerance三者中最多只能实现两者无法实现三者兼顾。
- C一致性表示分布式系统中不同节点中的数据都要相同
- A可用性代表可以允许某一段时间内分布式系统中不同节点的数据可以不同只要分布式系统能够保证最终数据的一致性。中途允许分布式系统中的节点数据存在不一致的情形
- P及Partition Tolerance通常情况下节点之间的网络的断开被称之为network partition。故而partition tolerance则是能够保证即使发生network partition分布式系统中的节点也能够继续运行
通常P是必选的而在满足P的前提下A和C只能够满足一条即AP或CP。
#### zookeeper遵循的原则
zookeeper遵循的是cp原则如果zookeeper集群中的leader宕机那么在新leader选举出来之前zookeeper集群是拒绝向外提供服务的。
#### eureka遵循的原则
和zookeeper不同eureka遵循的是ap原则这这意味着eureka允许多个节点之间存在数据不一致的情况即使某个节点的宕机eureka仍然能够向外提供服务。
### eureka使用
可以通过向项目中添加eureka-server的依赖来启动一个eureka-server实例。eureka-server作为注册中心会将其本身也作为一个服务注册到注册中心中。
#### 注册实例id
注册实例id由三部分组成`主机名称:应用名称:端口号`构成了实例的id。每个实例id都唯一。
#### eureka-server配置
- eviction-interval-timer-in-mseureka-server会运行固定的scheduled task来清除过期的clienteviction-interval-timer-in-ms属性用于定义task之间的间隔默认情况下该属性值为60s
- renewal-percent-threshold基于该属性eureka来计算每分钟期望从所有客户端接受到的心跳数。根据eureka-server的自我保护机制如果eureka-server收到的心跳数小于threshold那么eureka-server会停止进行客户端实例的淘汰直到接收到的心跳数大于threshold
#### eureka-instance配置
eureka-server-instance本身也作为一个instance注册到注册中心中故而可以针对eureka-instance作一些配置。
#### eureka集群
eureka集群是去中心化的集群没有主机和从机的概念eureka节点会向集群中所有其他的节点广播数据的变动。
## Ribbon
Spring Cloud Ribbon是一个基于Http和Tcp的客户端负载均衡工具基于Netflix Ribbon实现Ribbon主要用于提供负载均衡算法和服务调用。Ribbon的客户端组件提供了一套完善的配置项如超时和重试等。
在通过Spring Cloud构建微服务时Ribbon有两种使用方法一种是和RedisTemplate结合使用另一种是和OpenFegin相结合。
## OpenFeign
OpenFeign是一个远程调用组件使用接口和注解以http的形式完成调用。
Feign中集成了Ribbon而Ribbon中则集成了eureka。
# Spring Cloud简介
## eureka
eureka是由netflix公司开源的一个服务注册与发现组件。eureka和其他一些同样由netflix公司开源的组建一起被整合为spring cloud netflix模块。
### eureka和zookeeper的区别
#### CAP原则
CAP原则指的是在一个分布式系统中一致性Consistency、可用性Aailability和分区容错性Partition Tolerance三者中最多只能实现两者无法实现三者兼顾。
- C一致性表示分布式系统中不同节点中的数据都要相同
- A可用性代表可以允许某一段时间内分布式系统中不同节点的数据可以不同只要分布式系统能够保证最终数据的一致性。中途允许分布式系统中的节点数据存在不一致的情形
- P及Partition Tolerance通常情况下节点之间的网络的断开被称之为network partition。故而partition tolerance则是能够保证即使发生network partition分布式系统中的节点也能够继续运行
通常P是必选的而在满足P的前提下A和C只能够满足一条即AP或CP。
#### zookeeper遵循的原则
zookeeper遵循的是cp原则如果zookeeper集群中的leader宕机那么在新leader选举出来之前zookeeper集群是拒绝向外提供服务的。
#### eureka遵循的原则
和zookeeper不同eureka遵循的是ap原则这这意味着eureka允许多个节点之间存在数据不一致的情况即使某个节点的宕机eureka仍然能够向外提供服务。
### eureka使用
可以通过向项目中添加eureka-server的依赖来启动一个eureka-server实例。eureka-server作为注册中心会将其本身也作为一个服务注册到注册中心中。
#### 注册实例id
注册实例id由三部分组成`主机名称:应用名称:端口号`构成了实例的id。每个实例id都唯一。
#### eureka-server配置
- eviction-interval-timer-in-mseureka-server会运行固定的scheduled task来清除过期的clienteviction-interval-timer-in-ms属性用于定义task之间的间隔默认情况下该属性值为60s
- renewal-percent-threshold基于该属性eureka来计算每分钟期望从所有客户端接受到的心跳数。根据eureka-server的自我保护机制如果eureka-server收到的心跳数小于threshold那么eureka-server会停止进行客户端实例的淘汰直到接收到的心跳数大于threshold
#### eureka-instance配置
eureka-server-instance本身也作为一个instance注册到注册中心中故而可以针对eureka-instance作一些配置。
#### eureka集群
eureka集群是去中心化的集群没有主机和从机的概念eureka节点会向集群中所有其他的节点广播数据的变动。
## Ribbon
Spring Cloud Ribbon是一个基于Http和Tcp的客户端负载均衡工具基于Netflix Ribbon实现Ribbon主要用于提供负载均衡算法和服务调用。Ribbon的客户端组件提供了一套完善的配置项如超时和重试等。
在通过Spring Cloud构建微服务时Ribbon有两种使用方法一种是和RedisTemplate结合使用另一种是和OpenFegin相结合。
## OpenFeign
OpenFeign是一个远程调用组件使用接口和注解以http的形式完成调用。
Feign中集成了Ribbon而Ribbon中则集成了eureka。

View File

@@ -1,234 +1,234 @@
- [caffeine](#caffeine)
- [Cache](#cache)
- [注入](#注入)
- [手动](#手动)
- [Loading](#loading)
- [异步(手动)](#异步手动)
- [Async Loading](#async-loading)
- [淘汰](#淘汰)
- [基于时间的](#基于时间的)
- [基于时间的淘汰策略](#基于时间的淘汰策略)
- [基于引用的淘汰策略](#基于引用的淘汰策略)
- [移除](#移除)
- [removal listener](#removal-listener)
- [compute](#compute)
- [统计](#统计)
- [cleanup](#cleanup)
- [软引用和弱引用](#软引用和弱引用)
- [weakKeys](#weakkeys)
- [weakValues](#weakvalues)
- [softValues](#softvalues)
# caffeine
caffeine是一个高性能的java缓存库其几乎能够提供最佳的命中率。
cache类似于ConcurrentMap但并不完全相同。在ConcurrentMap中会维护所有添加到其中的元素直到元素被显式移除而Cache则是可以通过配置来自动的淘汰元素从而限制cache的内存占用。  
## Cache
### 注入
Cache提供了如下的注入策略
#### 手动
```java
Cache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.build();
// Lookup an entry, or null if not found
Graph graph = cache.getIfPresent(key);
// Lookup and compute an entry if absent, or null if not computable
graph = cache.get(key, k -> createExpensiveGraph(key));
// Insert or update an entry
cache.put(key, graph);
// Remove an entry
cache.invalidate(key);
```
Cache接口可以显示的操作缓存条目的获取、失效、更新。
条目可以直接通过cache.put(key,value)来插入到缓存中该操作会覆盖已存在的key对应的条目。
也可以使用cache.get(key,k->value)的形式来对缓存进行插入该方法会在缓存中查找key对应的条目如果不存在会调用k->value来进行计算并将计算后的将计算后的结果插入到缓存中。该操作是原子的。如果该条目不可计算会返回null如果计算过程中发生异常则是会抛出异常。
除上述方法外也可以通过cache.asMap()返回map对象并且调用ConcurrentMap中的接口来对缓存条目进行修改。
#### Loading
```java
// build方法可以指定一个CacheLoader参数
LoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// Lookup and compute an entry if absent, or null if not computable
Graph graph = cache.get(key);
// Lookup and compute entries that are absent
Map<Key, Graph> graphs = cache.getAll(keys);
```
LoadingCache和CacheLoader相关联。
可以通过getAll方法来执行批量查找默认情况下getAll方法会为每个cache中不存在的key向CacheLoader.load发送一个请求。当批量查找比许多单独的查找效率更加高时可以重写CacheLoader.loadAll方法。
#### 异步(手动)
```java
AsyncCache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.buildAsync();
// Lookup an entry, or null if not found
CompletableFuture<Graph> graph = cache.getIfPresent(key);
// Lookup and asynchronously compute an entry if absent
graph = cache.get(key, k -> createExpensiveGraph(key));
// Insert or update an entry
cache.put(key, graph);
// Remove an entry
cache.synchronous().invalidate(key);
```
AsyncCache允许异步的计算条目并且返回CompletableFuture。
AsyncCache可以调用synchronous方法来提供同步的视图。
默认情况下executor是ForkJoinPool.commonPool()可以通过Caffeine.executor(threadPool)来进行覆盖。
#### Async Loading
```java
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
// Either: Build with a synchronous computation that is wrapped as asynchronous
.buildAsync(key -> createExpensiveGraph(key));
// Or: Build with a asynchronous computation that returns a future
.buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
// Lookup and asynchronously compute an entry if absent
CompletableFuture<Graph> graph = cache.get(key);
// Lookup and asynchronously compute entries that are absent
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);
```
AsyncLoadingCache是一个AsyncCache加上一个AsyncCacheLoader。
同样地AsyncCacheLoader支持重写load和loadAll方法。
### 淘汰
Caffeine提供了三种类型的淘汰基于size的基于时间的基于引用的。
#### 基于时间的
```java
// Evict based on the number of entries in the cache
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.build(key -> createExpensiveGraph(key));
// Evict based on the number of vertices in the cache
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumWeight(10_000)
.weigher((Key key, Graph graph) -> graph.vertices().size())
.build(key -> createExpensiveGraph(key));
```
如果你的缓存不应该超过特定的容量限制,应该使用`Caffeine.maximumSize(long)`方法。该缓存会对不常用的条目进行淘汰。
如果每条记录的权重不同,那么可以通过`Caffeine.weigher(Weigher)`指定一个权重计算方法,并且通过`Caffeine.maximumWeight(long)`指定缓存最大的权重值。
#### 基于时间的淘汰策略
```java
// Evict based on a fixed expiration policy
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// Evict based on a varying expiration policy
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfter(new Expiry<Key, Graph>() {
public long expireAfterCreate(Key key, Graph graph, long currentTime) {
// Use wall clock time, rather than nanotime, if from an external resource
long seconds = graph.creationDate().plusHours(5)
.minus(System.currentTimeMillis(), MILLIS)
.toEpochSecond();
return TimeUnit.SECONDS.toNanos(seconds);
}
public long expireAfterUpdate(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
public long expireAfterRead(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
})
.build(key -> createExpensiveGraph(key));
```
caffine提供三种方法来进行基于时间的淘汰
- expireAfterAccess(long, TimeUnit):基于上次读写操作过后的时间来进行淘汰
- expireAfterWrite(long, TimeUnit):基于创建时间、或上次写操作执行的时间来进行淘汰
- expireAfter(Expire):基于自定义的策略来进行淘汰
过期在写操作之间周期性的进行触发偶尔也会在读操作之间进行出发。调度和发送过期事件都是在o(1)时间之内完成的。
为了及时过期,而不是通过缓存活动来触发过期,可以通过`Caffeine.scheuler(scheduler)`来指定调度线程  
#### 基于引用的淘汰策略
```java
// Evict when neither the key nor value are strongly reachable
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.weakKeys()
.weakValues()
.build(key -> createExpensiveGraph(key));
// Evict when the garbage collector needs to free memory
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.softValues()
.build(key -> createExpensiveGraph(key));
```
caffeine允许设置cache支持垃圾回收通过使用为key指定weak reference为value制定soft reference
### 移除
可以通过下述方法显式移除条目:
```java
// individual key
cache.invalidate(key)
// bulk keys
cache.invalidateAll(keys)
// all keys
cache.invalidateAll()
```
#### removal listener
```java
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.evictionListener((Key key, Graph graph, RemovalCause cause) ->
System.out.printf("Key %s was evicted (%s)%n", key, cause))
.removalListener((Key key, Graph graph, RemovalCause cause) ->
System.out.printf("Key %s was removed (%s)%n", key, cause))
.build();
```
在entry被移除时可以指定listener来执行一系列操作通过`Caffeine.removalListener(RemovalListener)`。操作是通过Executor异步执行的。
当想要在缓存失效之后同步执行操作时,可以使用`Caffeine.evictionListener(RemovalListener)`.该监听器将会在`RemovalCause.wasEvicted()`时被触发
### compute
通过computecaffeine可以在entry创建、淘汰、更新时原子的执行一系列操作
```java
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.evictionListener((Key key, Graph graph, RemovalCause cause) -> {
// atomically intercept the entry's eviction
}).build();
graphs.asMap().compute(key, (k, v) -> {
Graph graph = createExpensiveGraph(key);
... // update a secondary store
return graph;
});
```
### 统计
通过`Caffeine.recordStats()`方法,可以启用统计信息的收集,`cache.stats()`方法将返回一个CacheStats对象提供如下接口
- hitRate():返回请求命中率
- evictionCount():cache淘汰次数
- averageLoadPenalty():load新值花费的平均时间
```java
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.recordStats()
.build();
```
### cleanup
默认情况下Caffine并不会在自动淘汰entry后或entry失效之后立即进行清理而是在写操作之后执行少量的清理工作如果写操作很少则是偶尔在读操作后执行少量读操作。
如果你的缓存是高吞吐量的,那么不必担心过期缓存的清理,如果你的缓存读写操作都比较少,那么需要新建一个外部线程来调用`Cache.cleanUp()`来进行缓存清理。
```java
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.scheduler(Scheduler.systemScheduler())
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
```
scheduler可以用于及时的清理过期缓存
### 软引用和弱引用
caffeine支持基于引用来设置淘汰策略。caffeine支持针对key和value使用弱引用针对value使用软引用。
#### weakKeys
`caffeine.weakKeys()`存储使用弱引用的key如果没有强引用指向key那么key将会被垃圾回收。垃圾回收时只会比较对象地址故而整个缓存在比较key时会通过`==`而不是`equals`来进行比较
#### weakValues
`caffeine.weakValues()`存储使用弱引用的value如果没有强引用指向valuevalue会被垃圾回收。同样地在整个cache中会使用`==`而不是`equals`来对value进行比较
#### softValues
软引用的value通常会在垃圾回收时按照lru的方式进行回收根据内存情况决定是否进行回收。由于使用软引用会带来性能问题通常更推荐使用基于max-size的回收策略。
同样地基于软引用的value在整个缓存中会通过`==`而不是`equals()`来进行垃圾回收。
- [caffeine](#caffeine)
- [Cache](#cache)
- [注入](#注入)
- [手动](#手动)
- [Loading](#loading)
- [异步(手动)](#异步手动)
- [Async Loading](#async-loading)
- [淘汰](#淘汰)
- [基于时间的](#基于时间的)
- [基于时间的淘汰策略](#基于时间的淘汰策略)
- [基于引用的淘汰策略](#基于引用的淘汰策略)
- [移除](#移除)
- [removal listener](#removal-listener)
- [compute](#compute)
- [统计](#统计)
- [cleanup](#cleanup)
- [软引用和弱引用](#软引用和弱引用)
- [weakKeys](#weakkeys)
- [weakValues](#weakvalues)
- [softValues](#softvalues)
# caffeine
caffeine是一个高性能的java缓存库其几乎能够提供最佳的命中率。
cache类似于ConcurrentMap但并不完全相同。在ConcurrentMap中会维护所有添加到其中的元素直到元素被显式移除而Cache则是可以通过配置来自动的淘汰元素从而限制cache的内存占用。  
## Cache
### 注入
Cache提供了如下的注入策略
#### 手动
```java
Cache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.build();
// Lookup an entry, or null if not found
Graph graph = cache.getIfPresent(key);
// Lookup and compute an entry if absent, or null if not computable
graph = cache.get(key, k -> createExpensiveGraph(key));
// Insert or update an entry
cache.put(key, graph);
// Remove an entry
cache.invalidate(key);
```
Cache接口可以显示的操作缓存条目的获取、失效、更新。
条目可以直接通过cache.put(key,value)来插入到缓存中该操作会覆盖已存在的key对应的条目。
也可以使用cache.get(key,k->value)的形式来对缓存进行插入该方法会在缓存中查找key对应的条目如果不存在会调用k->value来进行计算并将计算后的将计算后的结果插入到缓存中。该操作是原子的。如果该条目不可计算会返回null如果计算过程中发生异常则是会抛出异常。
除上述方法外也可以通过cache.asMap()返回map对象并且调用ConcurrentMap中的接口来对缓存条目进行修改。
#### Loading
```java
// build方法可以指定一个CacheLoader参数
LoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// Lookup and compute an entry if absent, or null if not computable
Graph graph = cache.get(key);
// Lookup and compute entries that are absent
Map<Key, Graph> graphs = cache.getAll(keys);
```
LoadingCache和CacheLoader相关联。
可以通过getAll方法来执行批量查找默认情况下getAll方法会为每个cache中不存在的key向CacheLoader.load发送一个请求。当批量查找比许多单独的查找效率更加高时可以重写CacheLoader.loadAll方法。
#### 异步(手动)
```java
AsyncCache<Key, Graph> cache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(10_000)
.buildAsync();
// Lookup an entry, or null if not found
CompletableFuture<Graph> graph = cache.getIfPresent(key);
// Lookup and asynchronously compute an entry if absent
graph = cache.get(key, k -> createExpensiveGraph(key));
// Insert or update an entry
cache.put(key, graph);
// Remove an entry
cache.synchronous().invalidate(key);
```
AsyncCache允许异步的计算条目并且返回CompletableFuture。
AsyncCache可以调用synchronous方法来提供同步的视图。
默认情况下executor是ForkJoinPool.commonPool()可以通过Caffeine.executor(threadPool)来进行覆盖。
#### Async Loading
```java
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
// Either: Build with a synchronous computation that is wrapped as asynchronous
.buildAsync(key -> createExpensiveGraph(key));
// Or: Build with a asynchronous computation that returns a future
.buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
// Lookup and asynchronously compute an entry if absent
CompletableFuture<Graph> graph = cache.get(key);
// Lookup and asynchronously compute entries that are absent
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);
```
AsyncLoadingCache是一个AsyncCache加上一个AsyncCacheLoader。
同样地AsyncCacheLoader支持重写load和loadAll方法。
### 淘汰
Caffeine提供了三种类型的淘汰基于size的基于时间的基于引用的。
#### 基于时间的
```java
// Evict based on the number of entries in the cache
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.build(key -> createExpensiveGraph(key));
// Evict based on the number of vertices in the cache
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumWeight(10_000)
.weigher((Key key, Graph graph) -> graph.vertices().size())
.build(key -> createExpensiveGraph(key));
```
如果你的缓存不应该超过特定的容量限制,应该使用`Caffeine.maximumSize(long)`方法。该缓存会对不常用的条目进行淘汰。
如果每条记录的权重不同,那么可以通过`Caffeine.weigher(Weigher)`指定一个权重计算方法,并且通过`Caffeine.maximumWeight(long)`指定缓存最大的权重值。
#### 基于时间的淘汰策略
```java
// Evict based on a fixed expiration policy
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
// Evict based on a varying expiration policy
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfter(new Expiry<Key, Graph>() {
public long expireAfterCreate(Key key, Graph graph, long currentTime) {
// Use wall clock time, rather than nanotime, if from an external resource
long seconds = graph.creationDate().plusHours(5)
.minus(System.currentTimeMillis(), MILLIS)
.toEpochSecond();
return TimeUnit.SECONDS.toNanos(seconds);
}
public long expireAfterUpdate(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
public long expireAfterRead(Key key, Graph graph,
long currentTime, long currentDuration) {
return currentDuration;
}
})
.build(key -> createExpensiveGraph(key));
```
caffine提供三种方法来进行基于时间的淘汰
- expireAfterAccess(long, TimeUnit):基于上次读写操作过后的时间来进行淘汰
- expireAfterWrite(long, TimeUnit):基于创建时间、或上次写操作执行的时间来进行淘汰
- expireAfter(Expire):基于自定义的策略来进行淘汰
过期在写操作之间周期性的进行触发偶尔也会在读操作之间进行出发。调度和发送过期事件都是在o(1)时间之内完成的。
为了及时过期,而不是通过缓存活动来触发过期,可以通过`Caffeine.scheuler(scheduler)`来指定调度线程  
#### 基于引用的淘汰策略
```java
// Evict when neither the key nor value are strongly reachable
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.weakKeys()
.weakValues()
.build(key -> createExpensiveGraph(key));
// Evict when the garbage collector needs to free memory
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.softValues()
.build(key -> createExpensiveGraph(key));
```
caffeine允许设置cache支持垃圾回收通过使用为key指定weak reference为value制定soft reference
### 移除
可以通过下述方法显式移除条目:
```java
// individual key
cache.invalidate(key)
// bulk keys
cache.invalidateAll(keys)
// all keys
cache.invalidateAll()
```
#### removal listener
```java
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.evictionListener((Key key, Graph graph, RemovalCause cause) ->
System.out.printf("Key %s was evicted (%s)%n", key, cause))
.removalListener((Key key, Graph graph, RemovalCause cause) ->
System.out.printf("Key %s was removed (%s)%n", key, cause))
.build();
```
在entry被移除时可以指定listener来执行一系列操作通过`Caffeine.removalListener(RemovalListener)`。操作是通过Executor异步执行的。
当想要在缓存失效之后同步执行操作时,可以使用`Caffeine.evictionListener(RemovalListener)`.该监听器将会在`RemovalCause.wasEvicted()`时被触发
### compute
通过computecaffeine可以在entry创建、淘汰、更新时原子的执行一系列操作
```java
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.evictionListener((Key key, Graph graph, RemovalCause cause) -> {
// atomically intercept the entry's eviction
}).build();
graphs.asMap().compute(key, (k, v) -> {
Graph graph = createExpensiveGraph(key);
... // update a secondary store
return graph;
});
```
### 统计
通过`Caffeine.recordStats()`方法,可以启用统计信息的收集,`cache.stats()`方法将返回一个CacheStats对象提供如下接口
- hitRate():返回请求命中率
- evictionCount():cache淘汰次数
- averageLoadPenalty():load新值花费的平均时间
```java
Cache<Key, Graph> graphs = Caffeine.newBuilder()
.maximumSize(10_000)
.recordStats()
.build();
```
### cleanup
默认情况下Caffine并不会在自动淘汰entry后或entry失效之后立即进行清理而是在写操作之后执行少量的清理工作如果写操作很少则是偶尔在读操作后执行少量读操作。
如果你的缓存是高吞吐量的,那么不必担心过期缓存的清理,如果你的缓存读写操作都比较少,那么需要新建一个外部线程来调用`Cache.cleanUp()`来进行缓存清理。
```java
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.scheduler(Scheduler.systemScheduler())
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> createExpensiveGraph(key));
```
scheduler可以用于及时的清理过期缓存
### 软引用和弱引用
caffeine支持基于引用来设置淘汰策略。caffeine支持针对key和value使用弱引用针对value使用软引用。
#### weakKeys
`caffeine.weakKeys()`存储使用弱引用的key如果没有强引用指向key那么key将会被垃圾回收。垃圾回收时只会比较对象地址故而整个缓存在比较key时会通过`==`而不是`equals`来进行比较
#### weakValues
`caffeine.weakValues()`存储使用弱引用的value如果没有强引用指向valuevalue会被垃圾回收。同样地在整个cache中会使用`==`而不是`equals`来对value进行比较
#### softValues
软引用的value通常会在垃圾回收时按照lru的方式进行回收根据内存情况决定是否进行回收。由于使用软引用会带来性能问题通常更推荐使用基于max-size的回收策略。
同样地基于软引用的value在整个缓存中会通过`==`而不是`equals()`来进行垃圾回收。