Smart Plugin-簡単なCacheから

9909 ワード

本文は『軽量級Java Webフレームワークアーキテクチャ設計』のシリーズ博文である.
現在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を使えば開発できます.ただし、次の仕様を満たす必要があります.
  • 包名統一为:com.smart.plugin.xxx、xxxはPluginの名前を表します.
  • MavenのGroup IDはcom.smart.plugin、Artifact IDはsmart-xxxです.
  • はsmart-framework、すなわちSmartフレームワークに依存する.

  • よし!次に、上記の例で発生したパフォーマンスの問題を向上させるための簡単な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の実現にすぎず、完備ではなく、優雅でもない.だから残りはみんながもっとアドバイスして、どのようにそれをもっとよくするかが必要です.
    メッセージをお待ちしております!