3.1 Caching模式
Caching(缓存)模式描述了如何通过用完资源后不立刻释放资源来避免对资源的昂贵的重新获取。资源会维持它们的标识,并保留在某种可以快速访问的存储器中。它们可以被重用,从而避免了重新获取。
1.问题
对相同资源的重复的获取、初始化以及释放造成了不必要的开销。在系统的同一个组件或者多个组件访问相同资源的情形下,重复的获取和初始化带来了CPU周期和系统整体性能的开销。应该减少获取、访问和释放频繁使用的资源的开销,以提高性能。为了解决这个问题,需要关注以下几点:
1)性能(Performance)。重复获取、初始化和释放资源的开销必须最小化。
2)复杂性(Complexity)。解决方案不应当使得获取和释放资源的操作变得更复杂,更麻烦。此外,解决方案也不应对资源访问增加不必要的间接层。
3)可用性(Availability)。解决方案应当在资源提供者暂时不可用的情况下也使得资源可以被访问。
4)可伸缩性(Scalability)。解决方案应该对于资源的数目而言是可伸缩的。
2.解决方案
暂时把资源存储在称做“缓存”的快速访问缓冲区中。当资源后来被访问时,用缓存来获取和返回资源,而不需要从资源提供者(比如管理资源的操作系统)重新获取它。缓存通过资源标识(比如指针、引用、主键)来识别资源。
保持频繁访问的资源,不释放它们,这避免了重新获取和释放资源的开销。使用缓存简化了对访问资源的组件的管理。
当缓存中的资源不再需要时,它们就被释放了。缓存的实现决定了如何去除不再需要的资源。这一行为受策略控制。
3.结构
资源使用者使用资源。
资源是一个实体(比如连接)。
资源缓存(Resource Cache)缓冲了资源使用者释放的资源。
资源提供者拥有并管理多种资源。
4.实现
1)选择资源。选择可以从缓存获益的资源。这些通常是获取代价高昂,使用频繁的资源。缓存经常在发现性能瓶颈之后作为优化手段被引入。
在分布式系统中,存在两种缓存,客户端缓存和服务器端缓存。客户端的缓存对于节省带宽以及节省用于重复地从服务端传送数据到客户端的时间很有效。而服务端缓存则有助于解决多个客户请求导致重复获取和释放服务端的相同资源的问题。
2)决定缓存接口。资源若要被释放,并被资源使用者直接从缓存中重新获取,就必须设计一个合适的接口。这个接口需要提供release()和acquire()方法。
public void release(Resource resource);
public Resource acquire(Identity id);
}
上面的接口依赖于独立的资源标识。但不是所有的资源都有独立的标识。在某些情况下,资源标识可能需要从资源的属性计算得出。
资源使用者把资源释放到缓存(而不是释放给资源提供者)时会调用release()方法。
3)实现缓存。Cache接口中acquire()和release()方法的实现提供了缓存的主要功能。
public void release(Resource resource) {
String id = resource.getId();
map.put(id, resource);
}
// ....
HashMap map;
}
release()方法把资源添加到map中,这样稍后对acquire()的调用就可以通过它的标识找到资源。为了达到优化的目的,建议使用哈希表,因为哈希表可以执行几乎是常数时间复杂度的查找。
public Resource acquire(Identity id) {
Resource resource = map.get(id);
if (resource == null)
throw new ResourceNotFound();
return resource;
}
}