教做公众号的网站,外贸网站制作怎么选,WordPress自己写主题,淘宝网现状 网站建设缓存降级设计思想 接前文缺陷点 本地探针应该增加计数器#xff0c;多次异常再设置#xff0c;避免网络波动造成误判。耦合度过高#xff0c;远端缓存和本地缓存应该平行关系被设计为上下游关系了。公用的远端缓存的操作方法应该私有化#xff0c;避免集成方代码误操作多次异常再设置避免网络波动造成误判。耦合度过高远端缓存和本地缓存应该平行关系被设计为上下游关系了。公用的远端缓存的操作方法应该私有化避免集成方代码误操作导致受到攻击。探针改为轮训请求类似jedis底层心跳检测。抽象多层策略提供集成方自定义实现配置来决定采用什么方式进行降级缓存工厂或者控制器来决定Redis是否异常和异常后走本地缓存还是数据库还是zk等策略。底层应该做好职责分离异常往上抛由上层根据用户配置的策略做对应的处理逻辑。业务层采用模板模式对Redis降级后的业务逻辑提供有结构性的模板方法并要支持用户灵活重写支持重写的方法名不要取的具体应取的抽象。 1.创建缓存操作接口定义缓存数据的增删改查方法底层由redis工具类实现作为一级缓存另一个实现可以选择本地缓存工具类或第三方数据存储工具类实现作为二级缓存在redis降级时使用。
public interface IWecareCache {/*** 根据key获取到对应的value值* 注: 若key不存在 则返回null** param key key-value对应的key* return 该key对应的值。*/String get(String key);。。。
}Component(WecareSsoConstant.CACHE_TYPE_REDIS)
public class WecareRedisCache implements IWecareCache {private final JedisCluster jedis StaticSingletonFactory.getJedisSingleton();Overridepublic String get(String key) {log.debug([JedisCluster]: get - key{}, key);return jedis.get(key);}。。。
}Component(WecareSsoConstant.CACHE_TYPE_LOCAL)
public class WecareLocalCache implements IWecareCache {/*** 本地缓存记录str的key*/private static final String STRING_KEY StringKey;private final CacheString, MapString, String wecareLocalCache StaticSingletonFactory.getLocalCacheSingleton();Overridepublic String get(String key) {// 先判断是否key对应的map是否存在MapString, String map wecareLocalCache.getIfPresent(key);if (CollectionUtils.isEmpty(map)) {return null;}log.debug([localCache]: get - key{},value{}, key, map.get(STRING_KEY));return map.get(STRING_KEY);}。。。
} 2.创建缓存策略接口用于实现根据策略选择获取什么缓存核心进行操作并提供一个默认策略实现再在工厂中提供热加载方法如果存在自定义实现则优先获取自定义策略实现
public interface ICacheStrategy {// 根据策略自动选择具体缓存实现的方法IWecareCache getCacheByStrategy();// 根据指定缓存名称获取缓存实现的方法IWecareCache getCacheByName(String cacheName);// 查询redis运行状态是否OKboolean isRedisOK();// 设置redis状态为正常void setRedisAvailable();// 设置redis状态为异常void setRedisNotAvailable();
}// SDK缓存策略默认实现根据redis监听状态进行远端缓存和本地缓存的热切换
Component(WecareSsoConstant.DEFAULT_CACHE_STRATEGY)
public class DefaultCacheStrategy implements ICacheStrategy {// redis是否可用--策略读取监控更新private final AtomicBoolean redisIsLive new AtomicBoolean(false);// 可用缓存工具-自动注入Autowiredprivate MapString, IWecareCache cacheMap;//默认缓存策略-如果redis异常则使用jvm缓存//--如果不需要降级请实现自定义策略始终返回redis缓存即可Overridepublic IWecareCache getCacheByStrategy() {IWecareCache cacheByDefaultStrategy getCacheByDefaultStrategy();if (cacheByDefaultStrategy null) {log.error(no config cache);throw new BizException(no config cache);}return cacheByDefaultStrategy;}Overridepublic IWecareCache getCacheByName(String cacheName) {return cacheMap.get(cacheName);}Overridepublic boolean isRedisOK() {return redisIsLive.get();}// 默认redis状态实现-通过AtomicBoolean控制Overridepublic void setRedisAvailable() {redisIsLive.set(true);}// 默认redis状态实现-通过AtomicBoolean控制Overridepublic void setRedisNotAvailable() {redisIsLive.set(false);}private IWecareCache getCacheByDefaultStrategy() {if (redisIsLive.get()) {return cacheMap.get(WecareSsoConstant.CACHE_TYPE_REDIS);} else {return cacheMap.get(WecareSsoConstant.CACHE_TYPE_LOCAL);}}
} 3.创建缓存工具类提供缓存操作方法每次操作缓存都通过策略获取对应的缓存核心进行操作实现热降级
public class WecareCacheUtil {private static final ICacheStrategy cacheStrategy StaticSingletonFactory.getCacheStrategy();// 根据策略获取缓存实现private static IWecareCache getCacheByStrategy() {return cacheStrategy.getCacheByStrategy();}// 根据缓存名称获取指定缓存实现--默认拥有REDIS和JVM两个private static IWecareCache getCacheByName(String cacheName) {return cacheStrategy.getCacheByName(cacheName);}public static boolean isRedisOK(){return cacheStrategy.isRedisOK();}public static String get(String key) {return getCacheByStrategy().get(key);}// 根据名称操作指定缓存实现业务需要在正常流程时预先在jvm存储用户信息。public static String get(String key, String cacheName) {return getCacheByName(cacheName).get(key);}。。。4.创建redis监控接口用于定义redis的监控方式提供默认实现并同策略接口一样支持自定义实现的热加载创建执行监控的线程类
public interface IRedisMonitor {void healthCheck();
}Component(WecareSsoConstant.DEFAULT_REDIS_MONITOR)
public class DefaultRedisMonitor implements IRedisMonitor {// 连接异常最大次数private static final int MAX_ERROR_COUNT 2;// 心跳频率:多少毫秒检测一次private static final int HEART_BEAT_FREQUENCY 5000;private final JedisCluster jedis StaticSingletonFactory.getJedisSingleton();private final ICacheStrategy cacheStrategy StaticSingletonFactory.getCacheStrategy();// redis集群为6主多备因此检测到任意一个主节点宕机就算集群failed,集群failed超过阈值则设置redis集群状态不可用,此方法是否可监控连接数满导致的异常还有待测试private boolean isAllNodesActivated() {try {MapString, JedisPool clusterNodes jedis.getClusterNodes();for (Map.EntryString, JedisPool entry : clusterNodes.entrySet()) {JedisPool pool entry.getValue();String clusterInfo;try (Jedis jedisResource pool.getResource()) {clusterInfo jedisResource.clusterInfo();}if (!clusterInfo.contains(cluster_state:ok)) {log.error(redis node:{} cluster_state:fail, entry.getKey());log.error(clusterInfo{}, clusterInfo);return false;}}return true;}catch (JedisException jedisException){log.error(redis 读取节点信息异常 jedisException:,jedisException);}catch (Exception exception){try {jedis.set(testHealthCheck,true);String testHealthCheck jedis.get(testHealthCheck);if (true.equals(testHealthCheck)) {return true;}}catch (Exception e){log.error(redis 操作测试异常 exception:,e);}log.error(redis 读取节点信息异常 exception:,exception);}return false;}Overridepublic void healthCheck() {int threadException 0;int redisErrorCount 0;LocalDateTime lastLogTime LocalDateTime.now().minusMinutes(1);while (true) {try {Thread.sleep(HEART_BEAT_FREQUENCY);if (isAllNodesActivated()) {redisErrorCount 0;cacheStrategy.setRedisAvailable();LocalDateTime now LocalDateTime.now();if (Duration.between(lastLogTime,now).toMinutes()0) {log.info(Redis Cluster Nodes Health Check OK);lastLogTime now;}} else {redisErrorCount;if (redisErrorCount MAX_ERROR_COUNT) {redisErrorCount 0;cacheStrategy.setRedisNotAvailable();log.info(Redis Cluster Nodes Health Check Failed!!!);}}} catch (InterruptedException interruptedException) {log.error(redis监控线程休眠异常, interruptedException);if (threadException 3) {log.error(redis监控线程因休眠异常强制终止, interruptedException);break;}threadException;}}}
}
// 监控线程类提供启动redis监控的方法
public class RedisMonitorThread implements Runnable {private final IRedisMonitor monitor;public RedisMonitorThread(IRedisMonitor monitor) {this.monitor monitor;}Overridepublic void run() {monitor.healthCheck();}public static void startMonitor(IRedisMonitor monitor) {new Thread(new RedisMonitorThread(monitor),monitor.getClass().getSimpleName()-Thread).start();}
} 5.创建单例转静态对象的工厂用于将spring管理的动态单例转换为静态单例全局提供静态方法并定义线程初始化和类加载
Component
public class StaticSingletonFactory implements ApplicationContextAware {// 缓存策略实现private static MapString, ICacheStrategy cacheStrategyMap;// redis监控实现private static MapString, IRedisMonitor redisMonitorMap;private static JedisCluster jedis;// 本地缓存private static CacheString, MapString, String wecareSsoLocalCache;// 配置参数private static ConfigProperty configProperty;// 静态工厂bean初始化--赋值顺序要按照使用顺序且代码避免循环依赖Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {configProperty applicationContext.getBean(ConfigProperty.class);// 策略集合赋值cacheStrategyMap applicationContext.getBeansOfType(ICacheStrategy.class);if (configProperty.isRedisEnabled()) {// 确认开启使用redis功能jedis applicationContext.getBean(sdkJedisClient, JedisCluster.class);// 监控集合赋值redisMonitorMap applicationContext.getBeansOfType(IRedisMonitor.class);// 在赋值jedis客户端后启动监听线程RedisMonitorThread.startMonitor(getRedisMonitor());}wecareSsoLocalCache (CacheString, MapString, String) applicationContext.getBean(wecareSsoLocalCache);}public static JedisCluster getJedisSingleton() {return jedis;}public static CacheString, MapString, String getLocalCacheSingleton() {return wecareSsoLocalCache;}public static ConfigProperty getConfigProperty() {return configProperty;}// 指定策略实现public static ICacheStrategy getCacheStrategy(String cacheStrategyName) {ICacheStrategy iCacheStrategy cacheStrategyMap.get(cacheStrategyName);if (iCacheStrategy null) {throw new BizException(Select CacheStrategy Error, cacheStrategyName cacheStrategyName undefined !);}return iCacheStrategy;}// 获取缓存策略实现public static ICacheStrategy getCacheStrategy() {return hotLoading(cacheStrategyMap, WecareSsoConstant.DEFAULT_CACHE_STRATEGY, ICacheStrategy.class);}// 获取redis监控实现public static IRedisMonitor getRedisMonitor() {return hotLoading(redisMonitorMap, WecareSsoConstant.DEFAULT_REDIS_MONITOR, IRedisMonitor.class);}// 接口实现类热加载有自定义实现返回自定义无自定义实现返回默认实现private static T T hotLoading(MapString, T map, String defaultName, ClassT obj) {String className obj.getSimpleName();int size map.size();switch (size) {case 0:throw new BizException(className init Error, no implements !);case 1:return map.get(defaultName);case 2:for (Map.EntryString, T entry : map.entrySet()) {if (!defaultName.equals(entry.getKey())) {return entry.getValue();}}break;default:break;}throw new BizException(Select className Error, expected 1 but found size);}
}