跳转到内容

缓存

Trantor缓存

开发文档

缓存作为优化查询性能最常用的手段之一,在很多场景都有广泛的使用,为了能更好地将缓存作为一种资源纳入 Trantor 内部并且提供更好的资源管理能力,所以 Trantor 提供了缓存机制。

您可能会有疑问,我们为什么推荐您使用trantor的缓存机制,而不是通过各类第三方工具类库直接操作缓存呢?答案有几点:

  1. 为了能将缓存具现化,真正作为一种资源在trantor体系内进行管理和维护,并使之具备扩展能力。
  2. 在实现资源管理和扩展能力后,在二开定制场景,能更加明确地向二开人员展现出缓存资源和统一的扩展方式。
  3. 另外一点,Trantor 的 LogicFlow/LogicFunction/ExtensionPoint 在编写时,不关心是否在同一个 JVM,是否是 RPC,所以 Trantor 提供的缓存最好也是集中式的,这样实现和排查问题都比较简单,暂不考虑二级缓存等特性。

缓存作为优化查询性能最常用的手段之一,在很多场景都有广泛的使用,所以 Trantor 也提供相应的支持。

在线编辑缓存声明

进入 制品中心->配置->业务域->缓存,新增缓存:

image-20211125145758570

定义缓存实现

  1. 缓存实现的声明是一个 Java Class,实现对应的缓存声明接口,并且实现 get 方法;
  2. 缓存实现需要打上 @CacheImpl 的注解,注解上可以声明缓存有效时间;
  3. 缓存实现逻辑上可以调用 DS 的相关 API 和可编排服务 Function;
  4. 缓存的实现建议统一放到 implement 子模块的 xxx.xxx.cache.impl 包下;
  • 简单key对象实现
@CacheImpl(name = "Item cache", expire = 30 * 60 * 1000)
public class SimpleKeyItemBrandCacheImpl implements SimpleKeyItemBrandCache {
@Override
public BrandBO get(Long id) {
ItemBO itemBO = DS.findOne(
ItemBO.class, "*, brand.*", "id = ?", id
);
return null == itemBO ? null : itemBO.getBrand();
}
}
  • 复杂key对象实现
@CacheImpl(name = "Item cache", expire = 30 * 60 * 1000)
public class ItemBrandCacheImpl implements ItemBrandCache {
@Override
public BrandBO get(ItemBO item) {
ItemBO itemBO = DS.findOne(
ItemBO.class, "*, brand.*", "id = ?", item.getId()
);
return null == itemBO ? null : itemBO.getBrand();
}
}

缓存使用

缓存对象可以在所有被 Spirng 管理的 Bean 中声明成一个属性对象,但一般我们更适合的场景是在现有的逻辑流 Flow 或可编排服务 Function 中使用。

  • get

    getAPI是一种LoadingCache的思路,使用方式如上面实例所示:

    • 当缓存中存在目标key时,直接返回缓存key的value;
    • 当缓存不存在目标key时,先执行get方法内部逻辑,将执行结果进行缓存后返回;
    @FunctionImpl(name = "find brand via item")
    public class QueryItemBrandFuncImpl implements QueryItemBrandFunc {
    // 通过构造方法自动注入
    private ItemBrandCache itemBrandCache;
    public QueryItemBrandFuncImpl(ItemBrandCache itemBrandCache) {
    this.itemBrandCache = itemBrandCache;
    }
    @Override
    public BrandBO execute(ItemBO item) {
    // 省略...
    BrandBO brandBO = itemBrandCache.get(item);
    // 省略...
    return brandBO;
    }
    }
  • put

    put方法的使用只需要定义声明ItemBrandCache即可,因为put方法不需要跟get方法一样支持自定义逻辑,可以被代理逻辑覆盖。

    itemBrandCache.put(brandBO, brandBO);
  • invalidate

    invalidate:表示失效当前缓存声明下的某个key的缓存数据。

    使用方式参考put

    itemBrandCache.invalidate(brandBO);
  • clear

    clear:表示清除继承于当前缓存声明的所有缓存实现类操作的缓存数据。

    使用方式参考put

    itemBrandCache.clear();

缓存资源的管理

本地编写好相关代码,进行元数据上报执行资源发布后,可以在交互控制台查看缓存资源信息,如下:

研发态:

image-20210701211014946

运行态:

image-20210608100604794

激活实现:我们可以看一下缓存 trantor_example_cache_ItemBrandCache 当前激活实现。

image-20210608104755407

可以看到现在缓存已经激活的实现是 trantor_example_cache_ItemBrandCacheImpl ,在存在定制二开的场景下,缓存可能存在多个实现,我们可以在这里更换激活实现,让对应的缓存实现生效。

image-20210608104731086

功能验证

为了验证缓存的效果,我们可以使用前文里给出的代码仓库下载相关代码demo,并且稍微调整下缓存的 get 逻辑,加个日志打印,如下:

@Slf4j
@CacheImpl(name = "Item cache", expire = 30 * 60 * 1000)
public class ItemBrandCacheImpl implements ItemBrandCache {
@Override
public BrandBO get(ItemBO item) {
log.info("Query brand info from DB and cache it");
ItemBO itemBO = DS.findOne(
ItemBO.class, "*, brand.*", "id = ?", item.getId()
);
return null == itemBO ? null : itemBO.getBrand();
}
}

编写一个 Function 调用缓存加载逻辑,并且用于前端调用,Function 声明省略,如下:

@FunctionImpl(name = "query item brand")
public class QueryItemBrandFuncImpl implements QueryItemBrandFunc {
// 通过构造方法自动注入
private ItemBrandCache itemBrandCache;
public QueryItemBrandFuncImpl(ItemBrandCache itemBrandCache) {
this.itemBrandCache = itemBrandCache;
}
@Override
public BrandBO execute(ItemBO item) {
return itemBrandCache.get(item);
}
}

视图 CustomBrandDetail.view.xml 调用 QueryItemBrandFunc 加载品牌详情数据,如下:

<View title="品牌" forModel="trantor_example_cache_BrandBO" type="Detail" version="2">
<Detail model="trantor_example_cache_BrandBO"
dataFunction="trantor_example_cache_QueryItemBrandFunc"
dataParams="{id: pageContext.record.id}">
<Fields>
<Field name="brandName"/>
</Fields>
</Detail>
</View>

视图 CustomItemList.view.xml 用于配置菜单,如下:

<View title="商品" forModel="trantor_example_cache_ItemBO" type="List" menuView="true" version="2">
<Table key="table" model="trantor_example_cache_ItemBO" dataCondition="">
<Search>
<Fields>
<Field name="itemName"/>
<Field name="itemNumber"/>
<Field name="itemDesc"/>
<Field name="itemPrice"/>
<Field name="brand"/>
</Fields>
</Search>
<RecordActions label="操作">
<Action label="查询品牌详情" targetView="trantor_example_cache_BrandBO_CustomBrandDetail"/>
<Action label="失效品牌缓存" logicFunction="trantor_example_cache_InvalidateItemBrandFunc" after="Refresh"/>
</RecordActions>
<Fields>
<Field name="itemName"/>
<Field name="itemNumber"/>
<Field name="itemDesc"/>
<Field name="itemImage"/>
<Field name="itemPrice"/>
<Field name="brand"/>
</Fields>
<Actions>
<Action layout="Header" type="Create" targetView="trantor_example_cache_ItemBO__Form__builtIn"/>
</Actions>
</Table>
</View>

在本地编译上报元信息后进入交互控制台创建一个测试模块,如下:

image-20210608110928869

配置菜单:菜单视图选择demo里自定义的视图资源,如下:

image-20210608115557399

相关数据准备:主要的商品和品牌数据可以自行创建,如何创建?您可以直接通过脚本插入数据,或者同样在模块里配置相关数据对象的菜单,然后在交互统一工作台上通过界面创建,这里就不展开说了。

新创建的模块资源需要通过 资源发布 推送到运行态环境,请别忘了这一步,执行完之后,我们可以打开统一工作台,打开模块中心,选择对应的模块应用进入。

image-20210608120438794

在我们配置的菜单视图里创建完数据后,点击每条记录右侧的 “查询品牌详情” 按钮,可以触发缓存加载逻辑。

image-20210608120106392

触发后查看应用日志,有打印出自定义的日志信息,如下:

2021-06-08 11:52:23.079 INFO 86386 --- [nio-8080-exec-2] i.t.t.e.cache.impl.ItemBrandCacheImpl : Query brand info from DB and cache it

由于我们给缓存设置了过期时间,因此在过期之前重复触发不会打印日志,在过期时间达到后再触发,会再次看到目标日志。

2021-06-08 13:44:00.136 INFO 86386 --- [nio-8080-exec-9] i.t.t.e.cache.impl.ItemBrandCacheImpl : Query brand info from DB and cache it

另外,视图上也提供了主动失效目标商品品牌缓存的按钮,可自行验证。

image-20210608140538854