天天看點

redis應用—緩存

  項目的緩存設計主要是這樣的,監聽資料庫中表的改變,如果會發生變動,則對緩存做一些處理,具體實作如下:

 (1)緩存支援類(實體、輔助類等)的設計

實體緩存注釋類 NeedCache

/**
 * 緩存的注釋類,如果需要緩存某個類,請在類上添加此注釋類,并給出cache的處理類
 * 
 * @author liuyang
 * 
 */
@Documented
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedCache {

	Class<?> cacheDealClass();

}
           

緩存類 Cache

/**
 * 對象緩存類
 * 
 * @author liuyang
 * 
 */
public class Cache {

	private String key;

	private Map<String,String> fieldHashMap;

	private int expiredSeconds;

	private long expiredTime;

	/**
	 * 鍵
	 * 
	 * @return
	 */
	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	/**
	 * 緩存的對象map
	 * @return
	 */
	public Map<String, String> getFieldHashMap() {
		return fieldHashMap;
	}

	public void setFieldHashMap(Map<String, String> fieldHashMap) {
		this.fieldHashMap = fieldHashMap;
	}

	/**
	 * 過期秒數
	 * 
	 * @return
	 */
	public int getExpiredSeconds() {
		return expiredSeconds;
	}

	public void setExpiredSeconds(int expiredSeconds) {
		this.expiredSeconds = expiredSeconds;
	}

	/**
	 * 過期時間
	 * 
	 * @return
	 */
	public long getExpiredTime() {
		return expiredTime;
	}

	public void setExpiredTime(long expiredTime) {
		this.expiredTime = expiredTime;
	}

}
           

緩存處理接口類 CacheDeal

/**
 * 緩存處理接口
 * 
 * @author liuyang
 * 
 */
public interface CacheDeal {

	/**
	 * 添加緩存
	 * 
	 * @param cachedEntity
	 */
	void addCache(Object cachedEntity);

	/**
	 * 移除緩存
	 * 
	 * @param cachedEntity
	 */
	void removeCache(Object cachedEntity);

	/**
	 * 更新緩存
	 * 
	 * @param cachedEntity
	 */
	void updateCache(Object cachedEntity);

	/**
	 * 擷取緩存
	 * 
	 * @param key
	 * @return
	 */
	Cache getCache(String key);

}
           

(2)緩存監聽、管理類設計

          這裡用了hibernate的監聽類,建立3個監聽類,插入、修改、删除分别繼承Ejb3PostInsertEventListener,Ejb3PostUpdateEventListener,Ejb3DeleteInsertEventListener,srping配置檔案如下配置:

<!-- Jpa Entity Manager 配置 -->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="persistenceUnitName" value="defaultPU" />
		<property name="persistenceUnitManager" ref="persistenceUnitManager"/>
		<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
		<property name="jpaProperties">
			<props>
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
				<prop key="hibernate.show_sql">false</prop>
				<prop key="hibernate.format_sql">false</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
				<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
				<prop key="hibernate.search.default.directory_provider">org.hibernate.search.store.impl.FSDirectoryProvider</prop>
				<prop key="hibernate.search.default.indexBase">./build/indexes</prop>
				<prop key="hibernate.search.lucene_version">LUCENE_35</prop>
				
				<!-- for hibernate ejb event -->
				<prop key="hibernate.ejb.event.post-insert">
					com.liuyang.infrastructure.cache.listener.ejb.PostInsertEventListener
				</prop>
				<prop key="hibernate.ejb.event.post-update">
					com.liuyang.infrastructure.cache.listener.ejb.PostUpdateEventListener
				</prop>
				<prop key="hibernate.ejb.event.post-delete">
					com.liuyang.infrastructure.cache.listener.ejb.PostDeleteEventListener
				</prop>
				
			</props>
		</property>
	</bean>
           

緩存管理類

public class CacheManager {

	private static CacheManager instance = null;

	private static CacheDeal cacheDeal = null;

	private static Object cachedEntity = null;

	private CacheManager() {
	}

	public static CacheManager getNewInstance(Object cachedEntity) {
		CacheManager.cacheDeal = getCacheDealObject(cachedEntity.getClass());
		CacheManager.cachedEntity = cachedEntity;
		if (instance == null)
			synchronized (CacheManager.class) {
				if (instance == null) {
					instance = new CacheManager();
				}
			}
		return instance;
	}

	public void addCache() {
		if (isNull())
			return;
		cacheDeal.addCache(cachedEntity);
	}

	public void removeCache() {
		if (isNull())
			return;
		cacheDeal.removeCache(cachedEntity);
	}

	public void updateCache() {
		if (isNull())
			return;
		cacheDeal.updateCache(cachedEntity);
	}

	public Cache getCache(String key) {
		if (isNull())
			return null;
		return cacheDeal.getCache(key);
	}

	private boolean isNull() {
		return cacheDeal == null || cachedEntity == null;
	}

	private static CacheDeal getCacheDealObject(Class<?> cachedClazz) {
		CacheDeal cacheDealObject = null;
		try {
			boolean hasNeedCacheAnnotation = cachedClazz
					.isAnnotationPresent(NeedCache.class);
			if (hasNeedCacheAnnotation) {
				NeedCache needCache = cachedClazz
						.getAnnotation(NeedCache.class);
				cacheDealObject = (CacheDeal) SpringContextHolder
						.getBean(needCache.cacheDealClass());
			}
		} catch (Exception e) {
			LoggerUtil.error(CacheManager.class, cachedClazz + "擷取緩存處理類失敗", e);
		}
		return cacheDealObject;
	}

}
           

PostInsertEventListener類為例

public class PostInsertEventListener extends EJB3PostInsertEventListener{

	private static final long serialVersionUID = 7401854577429048855L;

	@Override
	public void onPostInsert(PostInsertEvent event) {
		super.onPostInsert(event);
		CacheManager cacheManager = CacheManager.getNewInstance(event.getEntity());
		cacheManager.addCache();
	}

}

           

以上是緩存設計的核心代碼,如果要實作使用者的緩存管理需要建立使用者緩存管理類,并實作CacheDeal接口:

需要緩存的使用者實體類 UserInfo

/**
 * 使用者類
 * 
 * @author liuyang
 * 
 */
@Entity
@Indexed
@Table(name = "user_info")
@NeedCache(cacheDealClass = UserCacheManager.class)//使用者緩存管理類
public class UserInfo extends IdEntity implements Serializable {

	private static final long serialVersionUID = 8046430120913324930L;

	private String loginName;

	private String password;

	private String name;

	private String email;
           
//getter setter省略
           
}
           

使用者緩存管理類

/**
 * 使用者緩存管理類
 * 
 * @author liuyang
 * 
 */
@Component
public class UserCacheManager implements CacheDeal {

	@Autowired
	private UserRepository userRepository;//資料庫user dao

	@Autowired
	private JedisManagerCache jedisManagerCache;//jedis管理類

	@Override
	public void addCache(Object cachedEntity) {
		UserInfo user = (UserInfo) cachedEntity;
		addUserCache(user);
	}

	@Override
	public void removeCache(Object cachedEntity) {
		UserInfo user = (UserInfo) cachedEntity;
		removeUserCache(user);
	}

	@Override
	public void updateCache(Object cachedEntity) {
		UserInfo user = (UserInfo) cachedEntity;
		String userKey = KeyConstants.USER_ + user.getId();
		Map<String, String> userHash = buildUserHash(user);
		jedisManagerCache.saveHash(userKey, userHash);
	}

	@Override
	public Cache getCache(String key) {
		Cache cache = new Cache();
		cache.setKey(key);
		cache.setFieldHashMap(jedisManagerCache.getHashMapByKey(key));
		return cache;
	}

	@Override
	public void init() {
		List<UserInfo> allUserList = getAllUsers();
		for (UserInfo user : allUserList) {
			addUserCache(user);
		}
	}

	/**
	 * 新增使用者時,添加使用者緩存
	 * 
	 * @param user
	 */
	private void addUserCache(UserInfo user) {
		Jedis jedis = null;
		Transaction tran = null;
		try {
			jedis = jedisManagerCache.getJedis();
			tran = jedis.multi();
			String userKey = KeyConstants.USER_ + user.getId();
			String userLesseeKey = KeyConstants.USER_LESSEE_
					+ user.getLessee().getId();
			Map<String, String> userHash = buildUserHash(user);
			tran.hmset(userKey, userHash);
			tran.sadd(userLesseeKey, user.getId().toString());
			tran.set("user:" + user.getEmail() + ":id", user.getId().toString());
			tran.exec();
		} catch (Exception e) {
			tran.discard();
			LoggerUtil.error(UserCacheManager.class, "添加" + user.getEmail()
					+ "緩存失敗", e);
		} finally {
			jedisManagerCache.returnResource(jedis);
		}
	}

	/**
	 * 移除使用者緩存
	 * 
	 * @param user
	 */
	private void removeUserCache(UserInfo user) {
		Jedis jedis = null;
		Transaction tran = null;
		try {
			jedis = jedisManagerCache.getJedis();
			tran = jedis.multi();
			String userKey = KeyConstants.USER_ + user.getId();
			String userLesseeKey = KeyConstants.USER_LESSEE_
					+ user.getLessee().getId();
			tran.del(userKey);
			tran.srem(userLesseeKey, user.getId().toString());
			tran.del("user:" + user.getEmail() + ":id");
			tran.exec();
		} catch (Exception e) {
			tran.discard();
			LoggerUtil.error(UserCacheManager.class, "删除" + user.getEmail()
					+ "緩存失敗", e);
		} finally {
			jedisManagerCache.returnResource(jedis);
		}
	}

	/**
	 * 建構使用者hashmap
	 * 
	 * @param user
	 * @return
	 */
	private Map<String, String> buildUserHash(UserInfo user) {
		Map<String, String> userHash = new HashMap<String, String>();
		userHash.put("login_name", user.getLoginName());
		userHash.put("name", user.getName());
		userHash.put("email", user.getEmail());
		if (null != user.getUserGroup())
			userHash.put("user_group_name", user.getUserGroup().getName());
		return userHash;
	}

}
           

這樣,就可以監聽資料庫改變并跟心緩存了,其中jedisCacheManager就是我自己封裝的常用的jedis操作方法,這個大家可以自己實作,用到什麼去做封裝。

(3)添加伺服器啟動時,寫入緩存功能

我們肯定會遇到,在伺服器啟動時,去寫入很多緩存,這樣,在伺服器運作中,就可以直接調用緩存了,下面就是這個功能的設計:

讓上面的UserCacheManager在多實作一個接口InitializeCache

/**
 * 緩存資料初始化接口,如果某個實體的緩存資料需要在伺服器啟動時初始化請實作此接口
 * 
 * @author liuyang
 * 
 */
public interface InitializeCache {

	void init();

}
           
@Component
public class UserCacheManager implements CacheDeal, InitializeCache {
	//前面的省略
	@Override
	public void init() {
		List<UserInfo> allUserList = getAllUsers();
		for (UserInfo user : allUserList) {
			addUserCache(user);
		}
	}
}
           

初始化緩存管理類,這類需要在spring中注入

public class InitializeCahceManager {

	private List<String> cachedEntityClazzNameList = new ArrayList<String>();

	public void setCachedEntityClazzNameList(
			List<String> cachedEntityClazzNameList) {
		this.cachedEntityClazzNameList = cachedEntityClazzNameList;
	}

	public void init() {
		try {
			for (String clazzName : cachedEntityClazzNameList) {
				Class<?> clazz = Class.forName(clazzName);
				boolean hasNeedCacheAnnotation = clazz
						.isAnnotationPresent(NeedCache.class);
				if (hasNeedCacheAnnotation) {
					NeedCache needCache = clazz.getAnnotation(NeedCache.class);
					InitializeCache initObject = (InitializeCache) SpringContextHolder
							.getBean(needCache.cacheDealClass());
					initObject.init();
				}
			}
		} catch (Exception e) {
			LoggerUtil.error(InitializeCahceManager.class, "初始化緩存失敗", e);
		}
	}

}
           
<!-- 初始化緩存管理類
    	如果需要緩存實體,添加實作類,實作cacheDeal、InitializeCache接口,然後在實體上加needclass的注釋,
    	并把需要緩存的實體加入到下面的集合
     -->
    <bean id="initializeCacheManager" class="com.liuyang.infrastructure.cache.InitializeCahceManager">
		<property name="cachedEntityClazzNameList">
			<list>
				<value>com.liuyang.security.model.UserInfo</value>
			</list>
		</property>
	</bean>
           

list是需要初始化資料的實體類,下面的serlvet需要加入到web.xml中伺服器啟動時運作,就大功告成了,啟動試試看

@SuppressWarnings("serial")
public class InitializeCacheServlet extends HttpServlet {

	@Override
	public void init() throws ServletException {
		super.init();
		InitializeCahceManager manager = SpringContextHolder
				.getBean(InitializeCahceManager.class);
		manager.init();
	}

}