Smart Plugin-簡単なCacheから
9909 ワード
本文は『軽量級Java Webフレームワークアーキテクチャ設計』のシリーズ博文である.
現在Smartでサービスが実装されています.次のように書くかもしれません.
上記の実装クラスでは,簡単なCRUD操作が完了し,各メソッドでDataSet APIが呼び出されて実装される.
もちろん、これは単純な例ですが、パフォーマンスという非常に明らかな問題を反映することができます.
DataSetを呼び出すたびにデータベース操作が行われるため、データベース操作はI/O操作であり、I/O操作はアプリケーション全体のパフォーマンスボトルネックの一つであることが知られています.
では、パフォーマンスを向上させるにはどうすればいいのでしょうか.まず考えられるのは,I/Oアクセスを減らすこと,すなわちここで述べたデータベースアクセスである.
では、データベースへのアクセスを減らすにはどうすればいいのでしょうか.Cacheを使うのはいい選択かもしれません.
よく知られているCacheオープンソースプロジェクト、例えばEhcache、OScacheなど、あるいはMemcached、Redisなどの分布式Cacheもあります.これらはすべていくつかの良い解決策で、Smartはもちろんこれらの方案を排斥することはできなくて、私達がPluginの方式を通じて、一定の開発規範を満たす限り、Smart Pluginを書くことができます.
Smart Pluginは実は何も深いものはありません.Mavenを使えば開発できます.ただし、次の仕様を満たす必要があります.包名統一为:com.smart.plugin.xxx、xxxはPluginの名前を表します. MavenのGroup IDはcom.smart.plugin、Artifact IDはsmart-xxxです. はsmart-framework、すなわちSmartフレームワークに依存する.
よし!次に、上記の例で発生したパフォーマンスの問題を向上させるための簡単なSmart Cache Pluginを開発します.
ステップ1:Cacheインタフェースを定義する
Cacheインタフェースでは、Cacheの一般的な操作の一連を提供します.方法のコードコメントを参照してください.
ステップ2:CacheManagerインタフェースを定義します.
CacheManagerインタフェースは、Cacheを外部に提供し、Cacheのライフサイクルを管理します.
ステップ3:CacheExceptionクラスを定義する
CacheExceptionは単純なRuntimeExceptionであり、上記のインタフェースの関連実装クラスでこの例外クラスが使用されます.
ステップ4:Cacheインタフェースの実装
Cacheの中で最も核心的なのは1つのMapで、しかもこのMapは同時制御を備えており、スレッド間の共有反発を保証するためにJDK 1.5のjavaを用いた.util.concurrent.ConcurrentHashMap.
ステップ5:CacheManagerインタフェースの実装
同じように1つのConcurrentHashMapですべてのCacheを管理し、1つのcacheNameをMapのkeyとして、CacheインスタンスはMapのvalueである.
ステップ6:Cacheの使用
ビジネスメソッドではCacheインタフェースを用い,データを取得する必要がある場合はまずCacheから取得し,ある場合はそのまま返し,ない場合はデータベースから取得し,取得後に結果をCacheに入れる.また、データが更新された場合、Cacheを削除または破棄することができます.
もちろん、これは簡単なCacheの実現にすぎず、完備ではなく、優雅でもない.だから残りはみんながもっとアドバイスして、どのようにそれをもっとよくするかが必要です.
メッセージをお待ちしております!
現在Smartでサービスが実装されています.次のように書くかもしれません.
@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など、あるいはMemcached、Redisなどの分布式Cacheもあります.これらはすべていくつかの良い解決策で、Smartはもちろんこれらの方案を排斥することはできなくて、私達がPluginの方式を通じて、一定の開発規範を満たす限り、Smart Pluginを書くことができます.
Smart Pluginは実は何も深いものはありません.Mavenを使えば開発できます.ただし、次の仕様を満たす必要があります.
よし!次に、上記の例で発生したパフォーマンスの問題を向上させるための簡単なSmart Cache Pluginを開発します.
ステップ1: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の一般的な操作の一連を提供します.方法のコードコメントを参照してください.
ステップ2: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のライフサイクルを管理します.
ステップ3: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であり、上記のインタフェースの関連実装クラスでこの例外クラスが使用されます.
ステップ4: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の中で最も核心的なのは1つのMapで、しかもこのMapは同時制御を備えており、スレッド間の共有反発を保証するためにJDK 1.5のjavaを用いた.util.concurrent.ConcurrentHashMap.
ステップ5: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);
}
}
}
同じように1つのConcurrentHashMapですべてのCacheを管理し、1つのcacheNameをMapのkeyとして、CacheインスタンスはMapのvalueである.
ステップ6: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には、customerListCacheとcustomerCacheの2つのCacheを作成するcacheManagerが作成されています.コンストラクタにすべてのメンバー変数を作成することを推奨します.もちろん、メンバー変数定義と同時に作成することもできます.ビジネスメソッドではCacheインタフェースを用い,データを取得する必要がある場合はまずCacheから取得し,ある場合はそのまま返し,ない場合はデータベースから取得し,取得後に結果をCacheに入れる.また、データが更新された場合、Cacheを削除または破棄することができます.
もちろん、これは簡単なCacheの実現にすぎず、完備ではなく、優雅でもない.だから残りはみんながもっとアドバイスして、どのようにそれをもっとよくするかが必要です.
メッセージをお待ちしております!