blog

Javaでローカルキャッシュを作成し、redisのキャッシュ操作をシミュレートする

1. キャッシュ・エンティティ・クラスの作成 2. ローカル・キャッシュ・ツール・クラスの作成 3....

Oct 19, 2020 · 4 min. read
シェア

Javaでローカルキャッシュを作成し、redisの使用をシミュレートします。

一般的に、小さなプロジェクトでは、データの量は大きくありません。しかし、いくつかの識別やチケットなどを記録するためにキャッシュを使用する必要があるときは、例えば、私は達成したい、あなたは、システムのオンラインユーザデータを記録することができます、または他のデータのキャッシュレコードは、DBAの要求を減らすために。

キャッシュ・エンティティ・クラスを作成します。

package com.adingxiong.cft.entity;
import java.io.Serializable;
/**
 * 
 * 
 */
public class CacheEntity implements Serializable {
 private static final long serialVersionUID = -107853226360392750L;
 /**
 *  
 */
 private Object value;
 /**
 * タイムスタンプを保存する
 */
 private long gmtModify;
 /**
 * 有効期限
 */
 private int expire;
 public Object getValue() {
 return value;
 }
 public void setValue(Object value) {
 this.value = value;
 }
 public long getGmtModify() {
 return gmtModify;
 }
 public void setGmtModify(long gmtModify) {
 this.gmtModify = gmtModify;
 }
 public int getExpire() {
 return expire;
 }
 public void setExpire(int expire) {
 this.expire = expire;
 }
 public CacheEntity(Object value, long gmtModify, int expire) {
 super();
 this.value = value;
 this.gmtModify = gmtModify;
 this.expire = expire;
 }
}

2.ローカルキャッシュツールクラスの作成

package com.adingxiong.cft.cache;
import com.adingxiong.cft.entity.CacheEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import java.io.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
 * @ClassName LocalCache
 * @Description 単純なデータ・ロギングのために、redisの代わりにローカル・キャッシュを使う。
 * 
 * 
 **/
public class LocalCache {
 private static final Logger log = LoggerFactory.getLogger(LocalCache.class);
 /**
 * デフォルトのキャッシュ・サイズ
 */
 private static final int DEFAULT_CAPACITY = 512;
 /**
 * 最大容量
 */
 private static final int MAX_CAPACITY = 100000;
 /**
 * キャッシュの更新頻度
 */
 private static final int MONITOR_DURATION = 2;
 // 監視スレッドを開始する
 static {
 new Thread(new TimeoutTimerThread()).start();
 }
 // シングルトン実装の内部クラス
 private static class LocalCacheInstance{
 private static final LocalCache INSTANCE=new LocalCache();
 }
 public static LocalCache getInstance() {
 return LocalCacheInstance.INSTANCE;
 }
 private LocalCache() {
 }
 /**
 * デフォルトのサイズでマップを作成する
 */
 private static Map<String, CacheEntity> cache = new ConcurrentHashMap<>(DEFAULT_CAPACITY);
 /**
 * ローカル・キャッシュにキー・バリューを保存し、キャッシュの有効期限を設定する。
 *
 * @param key
 * @param value
 * @param expireTime もし有効期限が-1であれば、有効期限が切れることはない。
 * @return
 */
 public <T> boolean putValue(String key, T value, int expireTime) {
 return putCloneValue(key, value, expireTime);
 }
 /**
 * シリアライズとクローン処理後に値をキャッシュに保存することで、値の参照の問題を解決することができる。
 *
 * @param key
 * @param value
 * @param expireTime
 * @return
 */
 private <T> boolean putCloneValue(String key, T value, int expireTime) {
 try {
 if (cache.size() >= MAX_CAPACITY) {
 return false;
 }
 // シリアライズされた割り当て
 CacheEntity entityClone = clone(new CacheEntity(value, System.nanoTime(), expireTime));
 cache.put(key, entityClone);
 return true;
 } catch (Exception e) {
 log.error("キャッシュの追加に失敗した:{}", e.getMessage());
 }
 return false;
 }
 /**
 * シリアライゼーション・クローニング
 *
 * @param object
 * @return
 */
 private <E extends Serializable> E clone(E object) {
 E cloneObject = null;
 try {
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 ObjectOutputStream oos = new ObjectOutputStream(baos);
 oos.writeObject(object);
 oos.close();
 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
 ObjectInputStream ois = new ObjectInputStream(bais);
 cloneObject = (E) ois.readObject();
 ois.close();
 } catch (Exception e) {
 log.error("キャッシュのシリアライズに失敗した:{}", e.getMessage());
 }
 return cloneObject;
 }
 /**
 * ローカル・キャッシュからキーの値を取得し、値が保存されていない場合はnullを返す。
 *
 * @param key
 * @return
 */
 public Object getValue(String key) {
 if(CollectionUtils.isEmpty(cache)){
 return null;
 }
 return cache.get(key).getValue();
 }
 /**
 * すべてを空にする
 */
 public void clear() {
 cache.clear();
 }
 /**
 * 期限切れスレッド
 */
 static class TimeoutTimerThread implements Runnable {
 @Override
 public void run() {
 while (true) {
 try {
 TimeUnit.SECONDS.sleep(MONITOR_DURATION);
 checkTime();
 } catch (Exception e) {
 log.error("有効期限後にキャッシュのクリーンアップに失敗した:{}", e.getMessage());
 }
 }
 }
 /**
 * 有効期限が切れたキャッシュの処理方法
 *
 * @throws Exception
 */
 private void checkTime() throws Exception {
 // 有効期限が切れた
 for (String key : cache.keySet()) {
 CacheEntity tce = cache.get(key);
 long timoutTime = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - tce.getGmtModify());
 // 有効期限 : timoutTime
 if (tce.getExpire() > timoutTime) {
 continue;
 }
 log.info(" 期限切れのキャッシュを消去する: " + key);
 //期限切れのキャッシュを消去し、対応するキャッシュ・キューを削除する。
 cache.remove(key);
 }
 }
 }
}

テスト

簡単なコントローラの作成

@GetMapping("/cache")
 public Object testCache(String name){
 Object a = LocalCache.getInstance().getValue("name");
 if(a == null){
 LocalCache.getInstance().putValue("name" ," ,10);
 return "キャッシュされたデータは読み込まれない。;
 }
 return "キャッシュされたデータを読むと、データは:" + a;
 }

ファーストコール

セカンドコール

その間、サービスは定期的にスレッドを起動し、設定した有効期限に従ってキャッシュされたデータをクリーンアップします。

Read next

V8ガベージコレクション

1.なぜゴミ収集が必要なのか JSのデータは現在7つの基本データ型と参照データ型に分けられ、V8にはスタック領域とヒープ領域という2つのデータ格納領域があることがわかっています。 スタック領域は一度割り当てられると ...

Oct 19, 2020 · 4 min read