​应用级缓存示例

本文介绍了一种多级缓存策略,包括本地缓存与分布式缓存的结合使用,通过API封装简化缓存操作,利用NULLCache避免空值查询,以及如何通过性能测试评估缓存效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用级缓存示例

  多级缓存API封装

  我们的业务数据如商品类目、店铺、商品基本信息都可以进行适当的本地缓存,以提升性能。对于多实例的情况时不仅会使用本地缓存,还会使用分布式缓存,因此需要进行适当的API封装以简化缓存操作。

  1.本地缓存初始化

  public class LocalCacheInitService extends BaseService {

  @Override

  publicvoid afterPropertiesSet() throws Exception {

  //商品类目缓存

  Cache<String, Object> categoryCache =

  CacheBuilder.newBuilder()

  .softValues()

  .maximumSize(1000000)

  .expireAfterWrite(Switches.CATEGORY.getExpiresInSeconds()/ 2, TimeUnit.SECONDS)

  .build();

  addCache(CacheKeys.CATEGORY_KEY, categoryCache);

  }

  privatevoid addCache(String key, Cache<?, ?> cache) {

  localCacheService.addCache(key,cache);

  }

  }

  本地缓存过期时间使用分布式缓存过期时间的一半,防止本地缓存数据缓存时间太长造成多实例间的数据不一致。

  另外,将缓存KEY前缀与本地缓存关联,从而匹配缓存KEY前缀就可以找到相关联的本地缓存。

  2.写缓存API封装

  先写本地缓存,如果需要写分布式缓存,则通过异步更新分布式缓存。

  public void set(final String key, final Object value, final intremoteCacheExpiresInSeconds) throws RuntimeException {

  if (value== null) {

  return;

  }

  //复制值对象

  //本地缓存是引用,分布式缓存需要序列化

  //如果不复制的话,则假设之后数据改了将造成本地缓存与分布式缓存不一致

  final Object finalValue = copy(value);

  //如果配置了写本地缓存,则根据KEY获得相关的本地缓存,然后写入

  if (writeLocalCache) {

  Cache localCache = getLocalCache(key);

  if(localCache != null) {

  localCache.put(key, finalValue);

  }

  }

  //如果配置了不写分布式缓存,则直接返回

  if (!writeRemoteCache) {

  return;

  }

  //异步更新分布式缓存

  asyncTaskExecutor.execute(() -> {

  try {

  redisCache.set(key,JSONUtils.toJSON(finalValue), remoteCacheExpiresInSeconds);

  } catch(Exception e) {

  LOG.error("updateredis cache error, key : {}", key, e);

  }

  });

  }

  此处使用了异步更新,目的是让用户请求尽快返回。而因为有本地缓存,所以即使分布式缓存更新比较慢又产生了回源,也可以在本地缓存命中。

  读缓存API封装

  先读本地缓存,本地缓存不命中的再批量查询分布式缓存,在查询分布式缓存时通过分区批量查询。

  private Map innerMget(List<String> keys, List<Class> types) throwsException {

  Map<String, Object> result = Maps.newHashMap();

  List<String> missKeys = Lists.newArrayList();

  List<Class> missTypes = Lists.newArrayList();

  //如果配置了读本地缓存,则先读本地缓存

  if(readLocalCache) {

  for(int i = 0; i < keys.size(); i++) {

  String key = keys.get(i);

  Class type = types.get(i);

  Cache localCache = getLocalCache(key);

  if(localCache != null) {

  Object value = localCache.getIfPresent(key);

  result.put(key, value);

  if (value == null) {

  missKeys.add(key);

  missTypes.add(type);

  }

  } else {

  missKeys.add(key);

  missTypes.add(type);

  }

  }

  }

  //如果配置了不读分布式缓存,则返回

  if(!readRemoteCache) {

  returnresult;

  }

  finalMap<String, String> missResult = Maps.newHashMap();

  //对KEY分区,不要一次性批量调用太大

  final List<List<String>>keysPage = Lists.partition(missKeys, 10);

  List<Future<Map<String, String>>> pageFutures = Lists.newArrayList();

  try {

  //批量获取分布式缓存数据

  for(final List<String>partitionKeys : keysPage) {

  pageFutures.add(asyncTaskExecutor.submit(() -> redisCache.mget(partitionKeys)));

  }

  for(Future<Map<String,String>> future : pageFutures) {

  missResult.putAll(future.get(3000, TimeUnit.MILLISECONDS));

  }

  } catch(Exception e) {

  pageFutures.forEach(future -> future.cancel(true));

  throw e;

  }

  //合并result和missResult,此处实现省略

  return result;

  }

  此处将批量读缓存进行了分区,防止乱用批量获取API。

  NULL Cache

  首先,定义NULL对象。

  private static final String NULL_STRING =new String();

  当DB没有数据时,写入NULL对象到缓存

  //查询DB

  String value = loadDB();

  //如果DB没有数据,则将其封装为NULL_STRING并放入缓存

  if(value == null) {

  value = NULL_STRING;

  }

  myCache.put(id, value);

  读取数据时,如果发现NULL对象,则返回null,而不是回源到DB

  value = suitCache.getIfPresent(id);

  //DB没有数据,返回null

  if(value == NULL_STRING) {

  return null;

  }

  通过这种方式可以防止当KEY对应的数据在DB不存在时频繁查询DB的情况。

  强制获取最新数据

  在实际应用中,我们经常需要强制更新数据,此时就不能使用缓存数据了,可以通过配置ThreadLocal开关来决定是否强制刷新缓存(refresh方法要配合CacheLoader一起使用)。

  if(ForceUpdater.isForceUpdateMyInfo()) {

  myCache.refresh(skuId);

  }

  String result = myCache.get(skuId);

  if(result == NULL_STRING) {

  return null;

  }

  失败统计

  private LoadingCache<String, AtomicInteger> failedCache =

  CacheBuilder.newBuilder()

  .softValues()

  .maximumSize(10000)

  .build(new CacheLoader<String, AtomicInteger>() {

  @Override

  public AtomicIntegerload(String skuId) throws Exception {

  return new AtomicInteger(0);

  }

  });

  当失败时,通过failedCache.getUnchecked(id).incrementAndGet()增加失败次数;当成功时,使用failedCache.invalidate(id)失效缓存。通过这种方式可以控制失败重试次数,而且又是内存敏感缓存。当内存不足时,可以清理该缓存腾出一些空间。

  延迟报警

  private static LoadingCache<String, Integer> alarmCache =

  CacheBuilder.newBuilder()

  .softValues()

  .maximumSize(10000).expireAfterAccess(1, TimeUnit.HOURS)

  .build(new CacheLoader<String, Integer>() {

  @Override

  public Integer load(String key) throws Exception {

  return 0;

  }

  });

  //报警代码

  Integer count = 0;

  if(redis != null) {

  StringcountStr = Objects.firstNonNull(redis.opsForValue().get(key), "0");

  count =Integer.valueOf(countStr);

  } else {

  count = alarmCache.get(key);

  }

  if(count % 5 == 0) { //5次报一次

  //报警

  }

  count = count + 1;

  if(redis != null) {

  redis.opsForValue().set(key,String.valueOf(count), 1, TimeUnit. HOURS);

  } else {

  alarmCache.put(key,count);

  }

  如果一出问题就报警,则存在报警量非常多或者假报警,因此,可以考虑N久报警了M次,才真正报警。此时,也可以使用Cache来统计。本示例还加入了Redis分布式缓存记录支持。

  性能测试

  笔者使用JMH 1.14进行基准性能测试,比如测试写。

  @Benchmark

  @Warmup(iterations = 10, time = 10, timeUnit =TimeUnit.SECONDS)

  @Measurement(iterations = 10, time = 10, timeUnit= TimeUnit.SECONDS)

  @BenchmarkMode(Mode.Throughput)

  @OutputTimeUnit(TimeUnit.SECONDS)

  @Fork(1)

  public void test_1_Write() {

  counterWriter= counterWriter + 1;

  myCache.put("key"+ counterWriter, "value" + counterWriter);

  }

  使用JMH时首先进行JVM预热,然后进行度量,产生测试结果(本文使用吞吐量)。建议读者按照需求进行基准性能测试来选择适合自己的缓存框架。

<think>好的,用户现在想了解模块化布局相关的设计模式和UI实现方式。之前用户提到了Qt的REST API开发,但现在转向模块化设计,特别是结合了引用的内容。需要整合用户提供的三个引用,同时参考之前的对话。 首先,引用1提到了模块化设计中的单例、工厂、观察者模式,用户可能想知道这些模式如何应用到UI布局中。引用2讨论了动态UI生成,包括模块化组件和条件渲染,这可能和UI的灵活性有关。引用3提到Qt的UI设计文件生成机制,说明UI布局文件改动后会自动生成代码,这可能影响模块化的实现方式。 用户的问题集中在模块化布局的设计模式和UI实现,可能需要结合设计模式(如单例、工厂、观察者)与UI组件化、动态生成等技术。要确保回答涵盖理论模式和实践方法,并结合Qt或其他框架的例子。 需要区分设计模式在模块化布局中的应用,比如单例管理全局UI组件,工厂创建动态元素,观察者处理状态变化。同时,UI实现部分要讲组件化设计、布局策略、动态加载,可能涉及Qt的机制如.ui文件的使用。 还要注意用户之前的问题涉及Qt的REST API,但这次转向UI,可能需要适当联系,比如之前提到的Qt Network模块和现在UI模块化的结合,但主要焦点应在UI上。 需要检查是否有遗漏的引用内容,确保每个引用都被合理利用。例如,引用3提到UI文件自动生成,这可能影响模块化的维护和扩展,需在实现注意事项中提到。 最后,生成的相关问题要围绕模块化布局的扩展,比如具体设计模式的应用案例、Qt中的实现技巧、性能优化等,引导用户深入探讨。</think>根据技术文档和开发实践,模块化布局的设计需要结合架构模式与UI实现技术,以下是综合设计方法及实现方案: #### 一、模块化设计模式体系 $$ \text{模块化系统} = \underbrace{\text{结构模式}}_{\text{骨架}} + \underbrace{\text{行为模式}}_{\text{神经}} + \underbrace{\text{创建模式}}_{\text{血液}} $$ 1. **工厂模式+组件库** 通过预制UI组件工厂实现动态布局生成: ```python class UIComponentFactory: @classmethod def create_button(cls, style_type): if style_type == "primary": return QPushButton().setStyleSheet("background: #2196F3") elif style_type == "warning": return QPushButton().setStyleSheet("background: #FF9800") # 动态生成警告按钮 alert_btn = UIComponentFactory.create_button("warning") ``` 2. **观察者模式+状态同步** 使用Qt信号槽机制实现跨模块状态同步[^3]: ```python class GameState(QObject): score_changed = Signal(int) class UI_Manager: def __init__(self, state): state.score_changed.connect(self.update_score_display) def update_score_display(self, new_score): self.score_label.setText(str(new_score)) ``` #### 二、Qt模块化UI实现方案 ##### 1. 组件化布局结构 $$ \begin{array}{ccc} \text{主窗口} & \rightarrow & \text{布局管理器} \\ & \downarrow & \\ \text{功能模块A} & \leftrightarrow & \text{公共组件池} \\ \end{array} $$ **实现步骤:** 1. 创建`.ui`文件定义基础布局 2. 使用`QUiLoader`动态加载部件 3. 通过`QStackedLayout`实现模块切换 ##### 2. 动态加载示例 ```python def load_module(module_name): loader = QUiLoader() file = QFile(f"modules/{module_name}.ui") file.open(QFile.ReadOnly) widget = loader.load(file, parent_window) file.close() main_layout.addWidget(widget) ``` #### 三、性能优化关键技术 1. **延迟加载策略** 采用按需加载模式,初始只加载核心模块: $$ \text{内存占用} = \sum_{i=1}^n \delta(i) \cdot M_i \quad (\delta(i)= \begin{cases} 1 & \text{模块已加载} \\ 0 & \text{模块未加载} \end{cases}) $$ 2. **缓存复用机制** ```python class UI_Cache: __instance = None # 单例模式实现[^1] @classmethod def get_instance(cls): if not cls.__instance: cls.__instance = UI_Cache() return cls.__instance def __init__(self): self.cached_widgets = {} def get_widget(self, widget_id): if widget_id not in self.cached_widgets: self.cached_widgets[widget_id] = create_complex_widget() return self.cached_widgets[widget_id] ``` #### 四、设计验证指标 | 维度 | 合格标准 | 检测方法 | |------------|---------------------------|--------------------------| | 加载速度 | ≤300ms/模块 | QElapsedTimer计时 | | 内存波动 | ≤±5MB 切换模块 | Qt Creator内存分析器 | | 渲染帧率 | ≥30fps 复杂布局 | QOpenGLDebugLogger | #### 五、扩展应用场景 1. **动态主题切换** 基于策略模式实现运行时样式变更: ```python class DarkThemeStrategy: def apply_theme(self, widget): widget.setStyleSheet("background: #333; color: white") class ThemeContext: def __init__(self, strategy): self._strategy = strategy def apply_theme(self, target): self._strategy.apply_theme(target) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值