通用设计概述
通用设计思路如下图
内容分发网络(CDN)
可以理解为一些服务器的副本,这些副本服务器可以广泛的部署在服务器提供服务的区域内,并存有服务器中的一些数据。
用户访问原始服务器时,其中的静态资源(比如banner图片、图标、js脚本、css文件、静态页面等)会由 CDN 分发到距离用户较近的副本服务器上并返回对应内容,加速访问效率
原始请求经过 CDN 后,会自动按请求内容分发至距离合适的副本服务器或原始服务器
OpenResty
OpenResty,附 官网,是一个基于 Nginx + Lua 实现的服务端开发平台。
其目的旨在在合适的情况下跳过服务,直接访问服务对应的资源本身
JVM 进程缓存(caffeine)
服务级缓存(Redis)
数据持久层
§2 优势
- 分担缓存压力
- 进程缓存相对于分布式缓存,节省了网络io的开销,速度更快
- 使用时,可以减少访问redis的频率,从根源上避免缓存的穿透、击穿、雪崩
- 可用性更强,即使进程缓存、分布式缓存之一不可用,也不会导致整体缓存失效,可以争取服务恢复时间
§3 最佳实践
最佳实践
- 使用 caffeine + redis + mysql 搭建二级缓存
- 自定义缓存控制器
CacheManager
- 增设监听 redis 失效 key
- 支持断线重连
自定义缓存控制器 & redis 监听
示例代码
//缓存管理器
public class CacheManager{
//redis
private CacheFrontend cf;//香菜连接池
private RedisClient rc;
//本地缓存
private Cache lc;
//redis 连接栈
StatefulRedisConnection<String,String> connection;
//监听 redis 失效 key
//连接正常时,通过 lisenter 监听redis事件,当事件为失效时同步清理本地缓存
public void check(){
if(connection != null && connection.isOpen)
return ;
try{
connection = rc.connect();
cf= ClientSideCaching.enable(new CaffeineCacheAccessor(lc),connection,TrackingArgs.Builder.enabled());
connection.addListener(msg->{
List<Object> content = msg.getContent(StringCodec.UTF8::decodeKey);
//日志输出
if(msg.getType().equalsIgnoreCase("invalidate")){
List<String> keys = (List<String>)content.get(1);
keys.forEach(k->lc.invalidate(k));
}
});
}catch(){//略}
}
}
//本地缓存访问器
//代理 caffeine client,使之实现香菜连接池中 CacheAccessor 接口
public class CaffeineCacheAccessor implements CacheAccessor{
private Cache lc;//本地缓存客户端,这里可以是 caffeine
//示例方法,其他方法同样
public Object get(Object key){
return lc.getIfPresent(key);
}
}
//心跳检测
@Bean
public CommandLimeRunner init(CacheManager cm){
return new CommandLineRunner(){
public void run(String... args) throws Exception{
while(true){
cm.check();
TimeUnit.SECONDS.sleep(5);
}
}
}
}