Smart Plugin —— 从一个简单的 Cache 开始

半腔热情 提交于 2019-12-07 11:29:22

本文是《轻量级 Java Web 框架架构设计》的系列博文。

目前在 Smart 中实现一个 Service,也许您会这样写:

@Bean
public class CustomerServiceImpl extends BaseService implements CustomerService {

    @Override
    public List<Customer> getCustomerList() {
        return DataSet.selectList(Customer.class, null, null);
    }

    @Override
    public boolean deleteCustomer(long id) {
        return DataSet.delete(Customer.class, "id = ?", id);
    }

    @Override
    public Customer getCustomer(long id) {
        return DataSet.select(Customer.class, "id = ?", id);
    }

    @Override
    public boolean updateCustomer(long id, Map<String, Object> fieldMap) {
        return DataSet.update(Customer.class, fieldMap, "id = ?", id);
    }

    @Override
    public boolean createCustomer(Map<String, Object> fieldMap) {
        return DataSet.insert(Customer.class, fieldMap);
    }
}

上面这个实现类,完成了简单的 CRUD 操作,每个方法中都调用了 DataSet API 来实现。

当然,这只是一个简单的示例,却能够反映出一个非常明显的问题 —— 性能!

因为每次调用 DataSet 其实底层都进行了数据库操作,众所周知,数据库操作是一种 I/O 操作,而 I/O 操作往往却是整个应用的性能瓶颈之一。

那么如何来提高性能呢?首先能够想到的就是减少 I/O 访问,也就是这里提到的数据库访问了。

那么怎样才能减小数据库访问呢?使用 Cache 或许是一种很好的选择吧。

大家熟知的 Cache 开源项目,例如:Ehcache、OScache 等,或者还有一些分布式 Cache,例如:Memcached、Redis 等。这些都是一些很好的解决方案,Smart 当然不会排斥这些方案,只要我们通过 Plugin 的方式,满足一定的开发规范,即可编写 Smart Plugin。

Smart Plugin 其实说白了没什么高深的东西,只要您会用 Maven,就可以开发。但您必须要满足以下规范:

  1. 包名统一为:com.smart.plugin.xxx,其中 xxx 表示 Plugin 的名称。
  2. Maven 的 Group ID 为 com.smart.plugin,并且 Artifact ID 为 smart-xxx。
  3. 依赖于 smart-framework,即 Smart 框架。

好!我们下面就开发一个简单的 Smart Cache Plugin,让它来提升以上示例中所遇到的性能问题。

第一步:定义一个 Cache 接口

public interface Cache<K, V> {

    // 从 Cache 中获取数据
    V get(K key);

    // 将数据放入 Cache 中
    void put(K key, V value);

    // 从 Cache 中移除数据
    boolean remove(K key);

    // 清空 Cache
    void clear();
}

在 Cache 接口中提供一系列对 Cache 的常用操作,请参见方法上的代码注释。

第二步:定义一个 CacheManager 接口。

public interface CacheManager {

    // 创建 Cache
    void createCache(String cacheName);

    // 获取 Cache
    <K, V> Cache<K, V> getCache(String cacheName);

    // 获取所有 Cache 名
    Iterable<String> getCacheNames();

    // 销毁指定 Cache
    void destroyCache(String cacheName);

    // 销毁所有 Cache
    void destroyCacheAll();
}

CacheManager 接口可对外提供 Cache,并对 Cache 的生命周期进行管理。

第三步:定义一个 CacheException 类

public class CacheException extends RuntimeException {

    public CacheException() {
        super();
    }

    public CacheException(String message) {
        super(message);
    }

    public CacheException(String message, Throwable cause) {
        super(message, cause);
    }

    public CacheException(Throwable cause) {
        super(cause);
    }
}

CacheException 只是一个简单的 RuntimeException,在以上接口的相关实现类中会使用该异常类。

第四步:实现 Cache 接口
public class DefaultCache<K, V> implements Cache<K, V> {

    // 定义一个 Data Map,用于存放该 Cache 中所有的数据
    private final Map<K, V> dataMap = new ConcurrentHashMap<K, V>();

    @Override
    public V get(K key) {
        if (key == null) {
            throw new NullPointerException("错误:参数 key 不能为空!");
        }
        return dataMap.get(key);
    }

    @Override
    public void put(K key, V value) {
        if (key == null) {
            throw new NullPointerException("错误:参数 key 不能为空!");
        }
        if (value == null) {
            throw new NullPointerException("错误:参数 value 不能为空!");
        }
        dataMap.put(key, value);
    }

    @Override
    public boolean remove(K key) {
        if (key == null) {
            throw new NullPointerException("错误:参数 key 不能为空!");
        }
        return dataMap.remove(key) != null;
    }

    @Override
    public void clear() {
        dataMap.clear();
    }
}

在 Cache 中最核心的就是一个 Map,而且这个 Map 是具备并发控制的,为了保证线程之间的共享互斥,所以用到了 JDK 1.5 中的 java.util.concurrent.ConcurrentHashMap。

第五步:实现 CacheManager 接口

public class DefaultCacheManager implements CacheManager {

    // 定义一个 Cache Map,用于存放该 Cache Manager 中所有的 Cache
    private final Map<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>();

    // 构造 Cache Manager,可同时创建若干 Cache
    public DefaultCacheManager(String... cacheNames) {
        if (cacheNames == null) {
            throw new NullPointerException("错误:参数 cacheNames 不能为空!");
        }
        for (String cacheName : cacheNames) {
            createCache(cacheName);
        }
    }

    @Override
    public void createCache(String cacheName) {
        if (cacheName == null) {
            throw new NullPointerException("错误:参数 cacheName 不能为空!");
        }
        if (cacheMap.containsKey(cacheName)) {
            throw new CacheException("错误:同名的 Cache 已存在,无法创建!");
        }
        Cache cache = new DefaultCache();
        cacheMap.put(cacheName, cache);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <K, V> Cache<K, V> getCache(String cacheName) {
        if (cacheName == null) {
            throw new NullPointerException("错误:参数 cacheName 不能为空!");
        }
        return cacheMap.get(cacheName);
    }

    @Override
    public Iterable<String> getCacheNames() {
        return cacheMap.keySet();
    }

    @Override
    public void destroyCache(String cacheName) {
        if (cacheName == null) {
            throw new NullPointerException("错误:参数 cacheName 不能为空!");
        }
        Cache cache = getCache(cacheName);
        if (cache != null) {
            cache.clear();
        }
    }

    @Override
    public void destroyCacheAll() {
        for (String cacheName : getCacheNames()) {
            destroyCache(cacheName);
        }
    }
}

同样用一个 ConcurrentHashMap 管理所有的 Cache,通过一个 cacheName 作为 Map 的 key,Cache 实例是 Map 的 value。

第六步:使用 Cache

@Bean
public class CustomerServiceCacheImpl extends BaseService implements CustomerService {

    private final CacheManager cacheManager;
    private final Cache<String, List<Customer>> customerListCache;
    private final Cache<Long, Customer> customerCache;

    public CustomerServiceCacheImpl() {
        cacheManager = new DefaultCacheManager("customer_list_cache", "customer_cache");
        customerListCache = cacheManager.getCache("customer_list_cache");
        customerCache = cacheManager.getCache("customer_cache");
    }

    @Override
    public List<Customer> getCustomerList() {
        List<Customer> customerList = customerListCache.get("customer_list");
        if (customerList == null) {
            customerList = DataSet.selectList(Customer.class, null, null);
            if (CollectionUtil.isNotEmpty(customerList)) {
                customerListCache.put("customer_list", customerList);
            }
        }
        return customerList;
    }

    @Override
    public boolean deleteCustomer(long id) {
        boolean result = DataSet.delete(Customer.class, "id = ?", id);
        if (result) {
            customerListCache.clear();
            customerCache.remove(id);
        }
        return result;
    }

    @Override
    public Customer getCustomer(long id) {
        Customer customer = customerCache.get(id);
        if (customer == null) {
            customer = DataSet.select(Customer.class, "id = ?", id);
            if (customer != null) {
                customerCache.put(id, customer);
            }
        }
        return customer;
    }

    @Override
    public boolean updateCustomer(long id, Map<String, Object> fieldMap) {
        boolean result = DataSet.update(Customer.class, fieldMap, "id = ?", id);
        if (result) {
            cacheManager.destroyCacheAll();
        }
        return result;
    }

    @Override
    public boolean createCustomer(Map<String, Object> fieldMap) {
        boolean result = DataSet.insert(Customer.class, fieldMap);
        if (result) {
            cacheManager.destroyCacheAll();
        }
        return result;
    }
}
在 CustomerServiceCacheImpl 中,创建了一个 cacheManager,通过它可创建 customerListCache 与 customerCache 这两个 Cache,推荐在构造器中创建所有成员变量,当然也可以直接在成员变量定义的同时进行创建。

在业务方法中,使用了 Cache 接口,当需要获取数据时,首先可从 Cache 中获取,如果有就直接返回,如果没有就从数据库中获取,获取过后再将结果放入 Cache 中。此外,当数据有更新时,可移除或销毁 Cache。

当然,这只是一个简单的 Cache 实现,并非完备,也并非优雅。所以剩下的就需要大家多提提建议了,如何去让它变得更好。

期待您的留言!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!