這篇文章將為大家詳細(xì)講解有關(guān)Java中怎么實(shí)現(xiàn)一個(gè)redis緩存服務(wù),文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括平谷網(wǎng)站建設(shè)、平谷網(wǎng)站制作、平谷網(wǎng)頁制作以及平谷網(wǎng)絡(luò)營(yíng)銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,平谷網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到平谷省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
緩存服務(wù)的意義
為什么要使用緩存?說到底是為了提高系統(tǒng)的運(yùn)行速度。將用戶頻繁訪問的內(nèi)容存放在離用戶最近,訪問速度最快的地方,提高用戶的響應(yīng)速度。一個(gè) web 應(yīng)用的簡(jiǎn)單結(jié)構(gòu)如下圖。
web 應(yīng)用典型架構(gòu)
在這個(gè)結(jié)構(gòu)中,用戶的請(qǐng)求通過用戶層來到業(yè)務(wù)層,業(yè)務(wù)層在從數(shù)據(jù)層獲取數(shù)據(jù),返回給用戶層。在用戶量小,數(shù)據(jù)量不太大的情況下,這個(gè)系統(tǒng)運(yùn)行得很順暢。但是隨著用戶量越來越大,數(shù)據(jù)庫中的數(shù)據(jù)越來越多,系統(tǒng)的用戶響應(yīng)速度就越來越慢。系統(tǒng)的瓶頸一般都在數(shù)據(jù)庫訪問上。這個(gè)時(shí)候可能會(huì)將上面的架構(gòu)改成下面的來緩解數(shù)據(jù)庫的壓力。
一主多從結(jié)構(gòu)
在這個(gè)架構(gòu)中,將數(shù)據(jù)庫的讀請(qǐng)求和寫請(qǐng)求進(jìn)行分離。數(shù)量眾多的讀請(qǐng)求都分配到從數(shù)據(jù)庫上,主數(shù)據(jù)庫只負(fù)責(zé)寫請(qǐng)求。從庫保持主動(dòng)和主庫保持同步。這個(gè)架構(gòu)比上一個(gè)有了很大的改進(jìn),一般的互聯(lián)網(wǎng)應(yīng)用。這個(gè)架構(gòu)就能夠很好的支持了。他的一個(gè)缺點(diǎn)是比較復(fù)雜,主從庫之間保持高效實(shí)時(shí),或者準(zhǔn)實(shí)時(shí)的同步是一個(gè)不容易做到的事情。所以我們有了另一個(gè)思路,采用一個(gè)緩存服務(wù)器來存儲(chǔ)熱點(diǎn)數(shù)據(jù),而關(guān)系數(shù)據(jù)用來存儲(chǔ)持久化的數(shù)據(jù)。結(jié)構(gòu)如下圖所示
采用緩存服務(wù)器讀的架構(gòu)
采用緩存服務(wù)器讀的架構(gòu)
在這個(gè)架構(gòu)中,當(dāng)讀取數(shù)據(jù)的時(shí)候,先從緩存服務(wù)器中獲取數(shù)據(jù),如果獲取調(diào),則直接返回該數(shù)據(jù)。如果沒有獲取調(diào),則從數(shù)據(jù)庫中獲取數(shù)據(jù)。獲取到后,將該數(shù)據(jù)緩存到換出數(shù)據(jù)庫中,供下次訪問使用。當(dāng)插入或者更新數(shù)據(jù)的時(shí)候,先將數(shù)據(jù)寫入到關(guān)系數(shù)據(jù)庫中,然后再更新緩存數(shù)據(jù)庫中的數(shù)據(jù)。當(dāng)然了,為了應(yīng)付更大規(guī)模的訪問量,我們還可以將上面兩個(gè)改進(jìn)的架構(gòu)組合起來使用,既有讀寫分離的關(guān)系數(shù)據(jù)庫,又有可以高速訪問的緩存服務(wù)。以上緩存服務(wù)器架構(gòu)的前提就是從緩存服務(wù)器中獲取數(shù)據(jù)的效率大大高于從關(guān)系型數(shù)據(jù)庫中獲取的效率。否則緩存服務(wù)器就沒有任何意義了。redis 的數(shù)據(jù)是保存在內(nèi)存中的,能夠保證從 redis 中獲取數(shù)據(jù)的時(shí)間效率比從關(guān)系數(shù)據(jù)庫中獲取高出很多。
基于 redis 緩存服務(wù)的實(shí)現(xiàn)
這一章節(jié)用一個(gè)實(shí)例來說明如何來在 Java 中實(shí)現(xiàn)一個(gè) redis 的緩存服務(wù)。
建立 maven 工程并引入依賴
定義接口類com.x9710.common.redis.CacheService
在這個(gè)接口類中,主要定了下面的接口
void putObject(String key, Object value); void putObject(String key, Object value, int expiration); Object pullObject(String key); Long ttl(String key); boolean delObject(String key); boolean expire(String key, int expireSecond); void clearObject();
這些接口分別用于存儲(chǔ)不過期的對(duì)象、存儲(chǔ)將來過期對(duì)象、獲取緩存對(duì)象、獲取緩存對(duì)象剩余存活時(shí)間、刪除緩存對(duì)象、設(shè)置緩存對(duì)象過期時(shí)間、清除所有緩存對(duì)象的功能
package com.x9710.common.redis;/*** 緩存服務(wù)接口** @author 楊高超* @since 2017-12-09*/public interface CacheService {/*** 將對(duì)象存放到緩存中** @param key 存放的key* @param value 存放的值*/void putObject(String key, Object value);/*** 將對(duì)象存放到緩存中** @param key 存放的key* @param value 存放的值* @param expiration 過期時(shí)間,單位秒*/void putObject(String key, Object value, int expiration);/*** 從緩存中獲取對(duì)象** @param key 要獲取對(duì)象的key* @return 如果存在,返回對(duì)象,否則,返回null*/Object pullObject(String key);/*** 給緩存對(duì)象設(shè)置過期秒數(shù)** @param key 要獲取對(duì)象的key* @param expireSecond 過期秒數(shù)* @return 如果存在,返回對(duì)象,否則,返回null*/boolean expire(String key, int expireSecond);/*** 獲取緩存對(duì)象過期秒數(shù)** @param key 要獲取對(duì)象的key* @return 如果對(duì)象不存在,返回-2,如果對(duì)象沒有過期時(shí)間,返回-1,否則返回實(shí)際過期時(shí)間*/Long ttl(String key);/*** 從緩存中刪除對(duì)象** @param key 要?jiǎng)h除對(duì)象的key* @return 如果出現(xiàn)錯(cuò)誤,返回 false,否則返回true*/boolean delObject(String key);/*** 從緩存中清除對(duì)象*/void clearObject();}
定義序列號(hào)輔助類com.x9710.common.redis.SerializeUtil
所有要保存到 redis 數(shù)據(jù)庫中的對(duì)象需要先序列號(hào)為二進(jìn)制數(shù)組,這個(gè)類的作用是將 Java 對(duì)象序列號(hào)為二級(jí)制數(shù)組或者將二級(jí)制數(shù)組反序列化為對(duì)象。
package com.x9710.common.redis;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;/*** 對(duì)象序列化工具類** @author 楊高超* @since 2017-10-09*/public class SerializeUtil {/*** 將一個(gè)對(duì)象序列化為二進(jìn)制數(shù)組** @param object 要序列化的對(duì)象,該必須實(shí)現(xiàn)java.io.Serializable接口* @return 被序列化后的二進(jìn)制數(shù)組*/public static byte[] serialize(Object object) {try {ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(object);return baos.toByteArray();} catch (Exception e) {e.printStackTrace();}return null;}/*** 將一個(gè)二進(jìn)制數(shù)組反序列化為一個(gè)對(duì)象。程序不檢查反序列化過程中的對(duì)象類型。** @param bytes 要反序列化的二進(jìn)制數(shù)* @return 反序列化后的對(duì)象*/public static Object unserialize(byte[] bytes) {try {ByteArrayInputStream bais = new ByteArrayInputStream(bytes);ObjectInputStream ois = new ObjectInputStream(bais);return ois.readObject();} catch (Exception e) {e.printStackTrace();}return null;}}
實(shí)現(xiàn) redis 緩存服務(wù)類 com.x9710.common.redis.impl.CacheServiceRedisImpl
package com.x9710.common.redis.impl;import com.x9710.common.redis.CacheService;import com.x9710.common.redis.RedisConnection;import com.x9710.common.redis.SerializeUtil;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import redis.clients.jedis.Jedis;/*** 緩存服務(wù) redis 實(shí)現(xiàn)類** @author 楊高超* @since 2017-12-09*/public class CacheServiceRedisImpl implements CacheService {private static Log log = LogFactory.getLog(CacheServiceRedisImpl.class);private RedisConnection redisConnection;private Integer dbIndex;public void setRedisConnection(RedisConnection redisConnection) {this.redisConnection = redisConnection;}public void setDbIndex(Integer dbIndex) {this.dbIndex = dbIndex;}public void putObject(String key, Object value) {putObject(key, value, -1);}public void putObject(String key, Object value, int expiration) {Jedis jedis = null;try {jedis = redisConnection.getJedis();jedis.select(dbIndex);if (expiration > 0) {jedis.setex(key.getBytes(), expiration, SerializeUtil.serialize(value));} else {jedis.set(key.getBytes(), SerializeUtil.serialize(value));}} catch (Exception e) {log.warn(e.getMessage(), e);} finally {if (jedis != null) {jedis.close();}}}public Object pullObject(String key) {log.trace("strar find cache with " + key);Jedis jedis = null;try {jedis = redisConnection.getJedis();jedis.select(dbIndex);byte[] result = jedis.get(key.getBytes());if (result == null) {log.trace("can not find caceh with " + key);return null;} else {log.trace("find cache success with " + key);return SerializeUtil.unserialize(result);}} catch (Exception e) {log.warn(e.getMessage(), e);} finally {if (jedis != null) {jedis.close();}}return null;}public boolean expire(String key, int expireSecond) {log.trace("strar set expire " + key);Jedis jedis = null;try {jedis = redisConnection.getJedis();jedis.select(dbIndex);return jedis.expire(key, expireSecond) == 1;} catch (Exception e) {log.warn(e.getMessage(), e);} finally {if (jedis != null) {jedis.close();}}return false;}public Long ttl(String key) {log.trace("get set expire " + key);Jedis jedis = null;try {jedis = redisConnection.getJedis();jedis.select(dbIndex);return jedis.ttl(key);} catch (Exception e) {log.warn(e.getMessage(), e);} finally {if (jedis != null) {jedis.close();}}return -2L;}public boolean delObject(String key) {log.trace("strar delete cache with " + key);Jedis jedis = null;try {jedis = redisConnection.getJedis();jedis.select(dbIndex);return jedis.del(key.getBytes()) > 0;} catch (Exception e) {log.warn(e.getMessage(), e);} finally {if (jedis != null) {jedis.close();}}return false;}public void clearObject() {Jedis jedis = null;try {jedis = redisConnection.getJedis();jedis.select(dbIndex);jedis.flushDB();} catch (Exception e) {log.warn(e.getMessage(), e);} finally {if (jedis != null) {jedis.close();}}}}
編寫測(cè)試用例
package com.x9710.common.redis.test;import com.x9710.common.redis.RedisConnection;import com.x9710.common.redis.impl.CacheServiceRedisImpl;import com.x9710.common.redis.test.domain.Student;import org.junit.Assert;import org.junit.Before;import org.junit.Test;/*** 緩存服務(wù)測(cè)試類** @author 楊高超* @since 2017-12-09*/public class RedisCacheTest {private CacheServiceRedisImpl cacheService;@Beforepublic void before() {RedisConnection redisConnection = RedisConnectionUtil.create();cacheService = new CacheServiceRedisImpl();cacheService.setDbIndex(2);cacheService.setRedisConnection(redisConnection);}@Testpublic void testStringCache() {String key = "name";String value = "grace";cacheService.putObject(key, value);String cachValue = (String) cacheService.pullObject(key);//檢查從緩存中獲取的字符串是否等于原始的字符串Assert.assertTrue(value.equals(cachValue));//檢查從緩存刪除已有對(duì)象是否返回 trueAssert.assertTrue(cacheService.delObject(key));//檢查從緩存刪除已有對(duì)象是否返回 falseAssert.assertFalse(cacheService.delObject(key + "1"));//檢查從緩存獲取已刪除對(duì)象是否返回 nullAssert.assertTrue(cacheService.pullObject(key) == null);}@Testpublic void testObjectCache() {Student oriStudent = new Student();oriStudent.setId("2938470s9d8f0");oriStudent.setName("柳白猿");oriStudent.setAge(36);cacheService.putObject(oriStudent.getId(), oriStudent);Student cacheStudent = (Student) cacheService.pullObject(oriStudent.getId());Assert.assertTrue(oriStudent.equals(cacheStudent));Assert.assertTrue(cacheService.delObject(oriStudent.getId()));Assert.assertTrue(cacheService.pullObject(oriStudent.getId()) == null);}@Testpublic void testExpireCache() {String key = "name";String value = "grace";cacheService.putObject(key, value);cacheService.expire(key, 300);String cachValue = (String) cacheService.pullObject(key);Assert.assertTrue(value.equals(cachValue));Long ttl = cacheService.ttl(key);Assert.assertTrue(ttl > 250 && ttl <= 300);Assert.assertTrue(value.equals(cachValue));Assert.assertTrue(cacheService.delObject(key));}}
關(guān)于Java中怎么實(shí)現(xiàn)一個(gè)redis緩存服務(wù)就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。