1. 程式人生 > >快取模式【其他模式】

快取模式【其他模式】

快取模式

public class Cache {
    /**
     * Caching Pattern【快取模式】
     * readThrough:先嚐試從快取中讀取資料,如果存在,則直接返回快取中的資料;如果不存在,則從資料來源載入,
     *              資料來源中存在目標物件,則將其寫入快取,並返回載入的物件。
     * writeThrough:先將目標物件寫入資料來源,再將其寫入快取中
     * writeAround:先將目標物件寫入資料來源,則將快取失效
     * writeBehind:如果快取已滿 && 目標物件不再快取中,則將 LRU 物件從快取中移除,並非同步將其寫入資料來源中,
     *              同時將目標物件寫入快取中。
     * cacheAside:readThrough && writeAround
     */
    @Test
    public void all() {
        final CacheStore store = new CacheStore();
        final long id = 1L;
        store.writeThrough(User.of(id, "zxd"));
        final User user = store.readThrough(id);
        store.writeAround(user);
        store.readThrough(id);
        store.writeBehind(User.of(2L, "princess"));
    }
}

@Value(staticConstructor = "of")
class User {
    private Long id;
    private String name;
}

class LruCache<K, V> extends LinkedHashMap<K, V> {
    private static final long serialVersionUID = 3409436250531011182L;
    private final int maxSize;

    public LruCache(int maxSize) {
        super(16, 0.75F, true);
        this.maxSize = maxSize;
    }

    public Entry<K, V> removeEldest() {
        final Entry<K, V> entry = entrySet().iterator().next();
        if (Optional.ofNullable(entry).isPresent()) {
            remove(entry.getKey());
        }
        return entry;
    }

    public boolean isfull() {
        return maxSize >= size();
    }
}

class DataSource {
    private static ConcurrentMap<Long, User> users = new ConcurrentHashMap<>();

    public static User load(Long id) {
        return users.get(id);
    }

    public static void persist(User user) {
        users.put(user.getId(), user);
    }

}

@Slf4j
class CacheStore {
    LruCache<Long, User> cache = new LruCache<>(1);

    public User readThrough(Long id) {
        User user;
        // 1)首先嚐試從快取中讀取,如果存在則直接返回
        if (Optional.ofNullable(user = cache.get(id)).isPresent()) {
            log.info("get from cache {}", user.getId());
            return user;
        }

        // 2)如果快取中不存在,則從資料來源讀取,如果讀取到值,則將其寫入快取中
        final User load = DataSource.load(id);
        Optional.ofNullable(load).ifPresent(u -> {
            log.info("load data from dataSource and set cache {}", u.getId());
            cache.put(u.getId(), u);
        });
        // 返回讀取的結果
        return load;
    }

    public void writeThrough(User user) {
        // 1)將目標物件持久化到資料來源
        DataSource.persist(user);
        // 2)將其更新到快取中
        cache.put(user.getId(), user);
        log.info("update dataSource and set cache{}", user.getId());
    }

    public void writeAround(User user) {
        // 1)將目標物件持久化到資料來源
        DataSource.persist(user);
        // 2)使得快取失效【即將其從快取中移除】
        cache.remove(user.getId());
        log.info("update dataSource and invalid cache {}", user.getId());
    }

    public void writeBehind(User user) {
        // 1)如果快取已滿 && 目標值未在快取中 && LRU移除快取並將其持久化到資料來源中
        if (cache.isfull() && !cache.containsKey(user.getId())) {
            final Entry<Long, User> entry = cache.removeEldest();
            final User value = entry.getValue();
            log.info("async update dataSource {}", value.getId());
            // 非同步將資料寫入資料來源
            CompletableFuture.runAsync(() -> {
                DataSource.persist(value);
            });
        }
        // 2)將目標物件更新到快取中
        cache.put(user.getId(), user);
    }

    public User readAside(Long id) {
        return readThrough(id);
    }

    public void writeAside(User user) {
        writeAround(user);
    }
}