Play 框架手册(13) – 使用 cache


为了创建高效的系统,有里会需要缓存数据。play 有一个 cache 库,在分布式 环境中使用 Memcached。

如果没有配置 Memcached,play 将使用标准的缓存来存储数据到 JVM heap 堆中。

在 JVM 应用程序里缓存数据打破了 play 创造的“什么都不共享”的原则:你不 能在多个服务器上运行同一应用程序,否则不能保证应用行为的一致性。每个应 用实例将拥有不同的数据备份。

清晰理解缓存约定非常重要:当把数据放入缓存时,就不能保证数据永久存在。

事实上不能这样做,缓存非常快,只在内存里存在(并没有进行持久化)。 

因此使用缓存最好的用途就是数据不需要进行修改的情况:

public static void allProducts() {
    List<Product> products = Cache.get("products", List.class);
    if(products == null) {
        products = Product.findAll();
        Cache.set("products", products, "30mn");
    }
    render(products);
}

13.1. The cache API

缓存 API 由 play.cache.Cache 类提供,这个类包含了许多用于从缓存设置、替 换、获取数据的方法。参考 Memcached 文档以了解每个方法的用法。

示例:

public static void showProduct(String id) {
    Product product = Cache.get("product_" + id, Product.class);
    if(product == null) {
        product = Product.findById(id);
        Cache.set("product_" + id, product, "30mn");
    }
    render(product);
}

public static void addProduct(String name, int price) {
    Product product = new Product(name, price);
    product.save();
    showProduct(product.id);
}

public static void editProduct(String id, String name, int price) {
    Product product = Product.findById(id);
    product.name = name;
    product.price = price;
    Cache.set("product_" + id, product, "30mn");
    showProduct(id);
}

public static void deleteProduct(String id) {
    Product product = Product.findById(id);
    product.delete();
    Cache.delete("product_" + id);
    allProducts();
}

有一些方法是以 safe 前缀开头的方法,比如 safeDelete, safeSet。而标准方 法是非阻塞式的,比如:

Cache.delete("product_" + id);

delete 方法将立即返回,不会一直等到缓存对象是否真的删除。因此,如果发 生错误时,比如 IO 错误,那么要删除的对象仍旧存在。

在继续之前如果需要确定是否真的把对象删除了, 就可以使用 safeDelete 方法: 

Cache.safeDelete("product_" + id);

这个方法是阻塞式的,并且会返回一个 boolean 值来确定是否真的把对象删除 了。因此,从缓存中删除对象的完整模式应该是这样的:

if(!Cache.safeDelete("product_" + id)) {
    throw new Exception("Oops, the product has not been removed from the
cache");
}
...

注意: 这些方法会导致调用中断,安全方法会慢慢的顺着执行下去,一般情况下, 只要确实需要的时候才使用这些方法。

同时要注意,当 expiration == "0s" (0 秒)时, 真正的中止时间可能与缓存执 行的时间不同。

13.2. 不要把 Session 当成缓存! 

如果使用框架带来的内存式的 session 来作缓存,  你就会发现 play 只允许很小 的字符串数据可以存入 HTTP Session,这里不应该是缓存应用程序数据的地方!   

如果你已经习惯了这样做:

httpServletRequest.getSession().put("userProducts", products);
...
// 之后进行请求
products = (List<Product>)httpServletRequest.getSession().get("userProducts");

在 Play 里可以用其他方式实现相同效果:

Cache.set(session.getId(), products);
...
//接下来的请求为:
List<Product> products = Cache.get(session.getId(), List.class)

在这里,我们使用唯一的 UUID 来为每个用户在缓存里保存唯一信息。请记住, 这和 session 对象不同,缓存并不绑定任何特定的用户!

13.3. 配置 mcached

如果需要允许真正的 Memcached 实现,就需在 memcached configuration 定义 守护地址 memcached.host configuration。


前一篇:
后一篇:

发表评论