redis 是個幹嗎的 ? 看官網:http://redis.io/java
一句話,這裏redis當作緩存(或者原本就是), 利用java寫一個jedis的讀寫的組建redis
no bb, review codespring
package com.mushroom.hui.common.cache; import com.alibaba.fastjson.JSON; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.CollectionUtils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Created by july on 16/4/1. */ public class CacheWrapper implements InitializingBean { private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CacheWrapper.class); private JedisPool masterJedis; private List<JedisPool> slaveJedisList; private String masterConf; private String slaveConf; private int maxIdle; private int minIdle; private int maxTotal; private int timeout; private int database; public String getMasterConf() { return masterConf; } public void setMasterConf(String masterConf) { this.masterConf = masterConf; } public String getSlaveConf() { return slaveConf; } public void setSlaveConf(String slaveConf) { this.slaveConf = slaveConf; } public int getMaxIdle() { return maxIdle; } public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } public int getMinIdle() { return minIdle; } public void setMinIdle(int minIdle) { this.minIdle = minIdle; } public int getMaxTotal() { return maxTotal; } public void setMaxTotal(int maxTotal) { this.maxTotal = maxTotal; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public int getDatabase() { return database; } public void setDatabase(int database) { this.database = database; } private class ConfAddress { private String ip; private int port; public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public ConfAddress(String conf) { init(conf); } private void init(String conf) { if (!StringUtils.contains(conf, ":")) { return; } String[] pair = StringUtils.split(conf, ":"); if (pair == null || pair.length != 2) { return; } this.ip = pair[0]; this.port = Integer.parseInt(pair[1]); } public boolean isIllegal() { return StringUtils.isBlank(ip) || port <= 0; } } @Override public void afterPropertiesSet() throws Exception { // 池基本配置 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(maxTotal <= 0 ? 300 : maxTotal); config.setMaxIdle(maxIdle <= 0 ? 10 : maxIdle); config.setMinIdle(minIdle <= 0 ? 3 : minIdle); config.setMaxWaitMillis(timeout <= 0 ? 1000 : timeout); config.setTestOnBorrow(false); // init master jedis ConfAddress masterAddr = new ConfAddress(masterConf); if (masterAddr.isIllegal()) { throw new JedisException("master jedis conf is error!"); } masterJedis = new JedisPool(config, masterAddr.getIp(), masterAddr.getPort(), this.timeout, null, this.database); // init slave jedis String[] slaveConfs = StringUtils.split(slaveConf, ","); if (slaveConfs == null || slaveConfs.length == 0) { slaveJedisList = Collections.emptyList(); } slaveJedisList = new ArrayList<>(slaveConfs.length); ConfAddress slaveTmpAddr; for (String conf: slaveConfs) { slaveTmpAddr = new ConfAddress(conf); if(slaveTmpAddr.isIllegal()) { continue; } JedisPool slaveJedis = new JedisPool(config, slaveTmpAddr.getIp(), slaveTmpAddr.getPort(), this.timeout, null, this.database); slaveJedisList.add(slaveJedis); } } final int MASTER_JEDIS = 0; final int SLAVE_JEIDS = 1; // 保證線程安全的自動技術器 private AtomicInteger chooseCounter = new AtomicInteger(); /** * 獲取使用的jedis,這裏採用標準的一主多備模式 * @param type * @return */ public JedisPool getJedisPool(int type) { if (type == MASTER_JEDIS) { return masterJedis; } if (CollectionUtils.isEmpty(slaveJedisList)) { return masterJedis; } final int chooseIndex = this.chooseCounter.incrementAndGet(); final int index = chooseIndex % slaveJedisList.size(); return slaveJedisList.get(index); } public String get(String key) { if(StringUtils.isBlank(key)) { throw new IllegalArgumentException("key is null!"); } Jedis jedis = null; JedisPool pool = getJedisPool(SLAVE_JEIDS); try { jedis = pool.getResource(); String ans = jedis.get(key); return ans; } catch (Exception e) { logger.error("get string from cache error!"); logger.error("Exception: {}", e); return null; } finally { if (jedis != null) { jedis.close(); } } } /** * 採用fastJson做爲序列化工具 * @param key cache key * @param clz object class * @param <T> type * @return object in cache! */ public <T> T getObject(String key, Class<T> clz) { if(StringUtils.isBlank(key)) { throw new IllegalArgumentException("key is null"); } Jedis jedis = null; JedisPool pool = getJedisPool(SLAVE_JEIDS); try { jedis = pool.getResource(); String ans = jedis.get(key); if (StringUtils.isBlank(ans) || "nil".equals(ans)) { return null; } T obj = JSON.parseObject(ans, clz); return obj; } catch (Exception e) { logger.error("get object from cache error!"); logger.error("Exception: {}", e); return null; } finally { if(jedis != null) { jedis.close(); } } } public boolean set(String key, String value, int expire) { if(StringUtils.isBlank(key) || StringUtils.isBlank(value) || expire <= 0) { throw new IllegalArgumentException("key || value || expire are illegal"); } Jedis jedis = null; JedisPool pool = getJedisPool(MASTER_JEDIS); try { jedis = pool.getResource(); String ans = jedis.setex(key, expire, value); } catch (Exception e) { logger.error("set string into cache error!"); logger.error("Exception: {}", e); return false; } finally { if (jedis != null) { jedis.close(); } } return true; } public boolean setObject(String key, Object value, int expire) { if (StringUtils.isBlank(key) || value == null || expire <= 0) { throw new IllegalArgumentException("key value expire are illegal!"); } Jedis jedis = null; JedisPool pool = getJedisPool(MASTER_JEDIS); try { jedis = pool.getResource(); String data = JSON.toJSONString(value); jedis.setex(key, expire, data); } catch (Exception e) { logger.error("set object into cache error!");; logger.error("Exception: {}", e); return false; } finally { if(jedis != null) { jedis.close(); } } return true; } }
配置文件 cache.xmlapache
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:beans="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!-- 下面的這個掃描很重要,用於創建uri與controller指尖的映射 --> <context:component-scan base-package="com.mushroom.hui"/> <context:annotation-config/> <beans:annotation-driven/> <bean id="cachePropertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="order" value="2" /> <property name="ignoreUnresolvablePlaceholders" value="true" /> <property name="locations"> <list> <value>classpath*:conf/cache.properties</value> </list> </property> </bean> <bean id="cacheWrapper" class="com.mushroom.hui.common.cache.CacheWrapper"> <property name="masterConf" value="${cache.masterConfig}" /> <property name="slaveConf" value="${cache.slaveConfigs}" /> <property name="timeout" value="${cache.timeout}" /> <property name="database" value="${cache.database}" /> <property name="maxTotal" value="${cache.maxTotal}" /> <property name="maxIdle" value="${cache.maxIdle}" /> <property name="minIdle" value="${cache.minIdle}" /> </bean> </beans>
cache.propertiesjson
cache.masterConfig=127.0.0.1:6379 cache.slaveConfigs=127.0.0.1:6379 cache.passwd=123 cache.timeout=60 cache.database=5 cache.maxTotal=10 cache.maxIdle=4 cache.minIdle=2
test測試文件緩存
package com.mushroom.hui.test; import com.mushroom.hui.common.cache.CacheWrapper; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by yihui on 16/4/2. */ public class CacheWrapperTest { private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CacheWrapperTest.class); private CacheWrapper cacheWrapper; @Before public void init() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:spring/*.xml"); cacheWrapper = applicationContext.getBean("cacheWrapper", CacheWrapper.class); } private class Point { String name; Float x; float y; public Point(String name, Float x, float y) { this.name = name; this.x = x; this.y = y; } public float getY() { return y; } public void setY(float y) { this.y = y; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Float getX() { return x; } public void setX(Float x) { this.x = x; } } @Test public void cacheTest() { String key = "hello_001"; String value = "woca"; boolean ans = cacheWrapper.set(key, value, 600); logger.info("The result is: {}", ans); Object obj = cacheWrapper.get(key); logger.info("The obj is {}", obj); Map<String, List<Point>> map = new HashMap<>(2); List<Point> pointList = new ArrayList<>(); pointList.add(new Point("a1", 123f, 123)); pointList.add(new Point("a2", 10f, 20f)); map.put("a", pointList); List<Point> pointList2 = new ArrayList<>(); pointList2.add(new Point("b1", 10f, 110f)); pointList2.add(new Point("b2", -10f, -20f)); pointList2.add(new Point("b3", -100f, -200f)); map.put("b2", pointList2); String key2 = "world_001"; boolean ans2 = cacheWrapper.setObject(key2, map, 600); logger.info("The ans2 is {}", ans2); Object result = cacheWrapper.getObject(key2, map.getClass()); logger.info("Thre result2 is {}", result); } }
因爲項目工程是機遇spring框架的,因此上面的代碼中能夠很清晰的看到soring bean的相關內容安全
若是不瞭解spring,也不想使用spring相關的東西,能夠無視上面的xml, properties 文件,直接用上面的java類便可(附件給出相關代碼)併發
上面的組建支持一主多備的使用方式,寫maser,讀slaveapp
- 初始話redis相關的配置參數
初始化 JedisPool 實例框架
get/set設計與實現
參數初始化 redis相關的經常使用參數:JedisPoolConfig, (masterAddr, port), (slaveAddr, port), timeout
maxTotal 最多的jedis實例 maxIdel, minIdel 最大最小的空閒jedis實例 masterConfig: master redis的ip(or域名)和端口號 slaveConfig: slave redis的ip(or域名)和端口號 timeout: 連接超時時間(大於這個時間則拋鏈接超時異常)
這些參數的初始化是由spring框架完成的,在bean的聲明處,即將properties中的相關參數注入到CacheWrapper中的成員變量中
why redisPool not jedis? - 併發量大時(qps高),耽擱jedis處理不過來!!! - 使用pool減小了jedis實例的頻繁銷燬和新建的開銷
public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database);
這段代碼邏輯也很簡單,稍稍注意一下slaveJedisPool 是一個列表(一主多備嘛)
getJedisPool()
方法, why? 代碼中有一個masterPool和多個slavePool,那麼選擇哪個來使用,也是一個問題getJedisPool 邏輯 :
get(key) / getObject(key, clz) 從redis讀
set(key, value, expire) / setObject(key, object, expire) 寫入redis
基本流程完結