本文原創,轉載請注明:http://www.cnblogs.com/fengzheng/p/5941953.html
關於 Redis 集群搭建可以參考我的另一篇文章 Redis集群搭建與簡單使用
Redis 是什麼,能做什麼
Redis 是一個開源(BSD許可),內存存儲的數據結構服務器,可用作數據庫,高速緩存和消息隊列代理。它支持字符串、哈希表、列表、集合、有序集合,位圖,hyperloglogs等數據類型。內置復制、Lua腳本、LRU收回、事務以及不同級別磁盤持久化功能,同時通過Redis Sentinel 提供高可用,通過 Redis Cluster 提供自動分區。(摘自 Redis 官網)
作為內存數據庫,在現代互聯網 web 系統中,還是主要將 Redis 作為緩存使用。大型互聯網 Web 系統對性能要求很高,而在前端和數據層之間增加數據緩存已成為必不可少的手段之一,當前比較流行的兩個技術就是 Redis 和 Memcached,至於兩者有什麼區別,不是本文要說的內容。本文主要講 Java web 如何操作 Redis 及 Redis 集群。
一般 Java 程序操作Redis
Redis 提供了多種語言的客戶端,在 Java 中最流行的是 Jedis 。訪問可查看源碼及使用方式。目前 Jedis 最新版本是2.9.0。無論是單機還是集群,Jedis 都有很詳細的說明和實例代碼,這裡只做簡單說明。如果用 Maven 做包管理,需要引用 jedis 包,本例使用最新的2.9.0版本,如下:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
操作 Redis 單機
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; /** * Created by fengdezitai on 2016/10/9. */ public class JedisClient { private static final String host= "192.168.31.121"; private static final JedisClient jedisClient = new JedisClient(); private Jedis jedis = null; /** * 私有構造函數 */ private JedisClient(){} public static JedisClient getInstance(){ return jedisClient; } private JedisPoolConfig getPoolConfig(){ JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(10); jedisPoolConfig.setMaxTotal(100); jedisPoolConfig.setMaxWaitMillis(3000); return jedisPoolConfig; } /** * 添加 * @param key * @param value * @return * @throws Exception */ public Boolean add(String key,String value) throws Exception{ JedisPool pool = new JedisPool(getPoolConfig(),host); Jedis jedis = null; try { jedis = pool.getResource(); if(jedis.exists(key)){ throw new Exception(String.format("key (%s) 已存在 ",key)); } jedis.set(key,value); }catch (Exception e){ throw e; } finally { if(jedis!=null){ jedis.close(); } } pool.destroy(); return true; } /** * 獲取值 * @param key * @return * @throws Exception */ public String get(String key) throws Exception{ JedisPool pool = new JedisPool(getPoolConfig(),host); Jedis jedis = null; String result = ""; try { jedis = pool.getResource(); result = jedis.get(key); }catch (Exception e){ throw e; } finally { if(jedis!=null){ jedis.close(); } } pool.destroy(); return result; } public static void main(String[] args) { JedisClient jedisClient = JedisClient.getInstance(); try { /*Boolean result = jedisClient.add("hello", "redis1"); if(result){ System.out.println("success"); }*/ System.out.println(jedisClient.get("hello")); }catch (Exception e){ e.printStackTrace(); } } }
操作 redis 集群
import redis.clients.jedis.*; import java.util.HashSet; import java.util.Set; /** * Created by fengdezitai on 2016/10/13. */ public class JedisClusterClient { private static int count = 0; private static final JedisClusterClient redisClusterClient = new JedisClusterClient(); /** * 私有構造函數 */ private JedisClusterClient() {} public static JedisClusterClient getInstance() { return redisClusterClient; } private JedisPoolConfig getPoolConfig(){ JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(1000); config.setMaxIdle(100); config.setTestOnBorrow(true); return config; } public void SaveRedisCluster() { Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>(); jedisClusterNodes.add(new HostAndPort("192.168.31.245", 7000)); jedisClusterNodes.add(new HostAndPort("192.168.31.245", 7001)); jedisClusterNodes.add(new HostAndPort("192.168.31.245", 7002)); jedisClusterNodes.add(new HostAndPort("192.168.31.210", 7003)); jedisClusterNodes.add(new HostAndPort("192.168.31.210", 7004)); jedisClusterNodes.add(new HostAndPort("192.168.31.210", 7005)); JedisCluster jc = new JedisCluster(jedisClusterNodes,getPoolConfig()); jc.set("cluster", "this is a redis cluster"); String result = jc.get("cluster"); System.out.println(result); } public static void main(String[] args) { JedisClusterClient jedisClusterClient = JedisClusterClient.getInstance(); jedisClusterClient.SaveRedisCluster(); } }
Spring mvc 操作 Redis
在 Spring mvc 中操作 Redis ,首先當然要搭好 Spring mvc 框架了。以下是在假設 Spring mvc 環境已經架好的情況下。本例中 Spring 版本為 4.3.2 RELEASE。關於 Spring 的 maven 引用如下:
<!-- spring版本號 --> <spring.version>4.3.2.RELEASE</spring.version> <!-- spring核心包 --> <!-- springframe start --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- springframe end -->
操作 Redis 單機
只用 Jedis 自己實現注入(區別於下面的引用spring-data-redis)
把前面的 JedisClient 代碼拿過來引用即可,只需實現一個訪問 Redis 的 Service ,就可以集成到 Spring mvc 。Service 代碼如下:
import org.springframework.stereotype.Service; import util.JedisClient; /** * Created by fengdezitai on 2016/10/9. */ @Service public class RedisService { public String get(String key) throws Exception{ JedisClient jedisClient = JedisClient.getInstance(); //上面實現的JedisClient String result = ""; try { result = jedisClient.get("hello"); }catch (Exception e){ throw e; } return result; } }
Controller 實現如下:
@Controller @RequestMapping(value = "redisAllInOne") public class RedisAllInOneController { @Autowired private RedisService redisService; @RequestMapping(value = "get",method = RequestMethod.GET) @ResponseBody public Object getByMyService(String key){ try { String result = redisService.get(key); return result; }catch (Exception e){ e.printStackTrace(); } return null; } }
用 spring-data-redis 包做集成
上面是自己實現的注入,這裡用 spring-data-redis 進行集成,只需簡單配置即可,需要引用 maven 包如下,版本為目前最新版 1.7.2.RELEASE:
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.7.2.RELEASE</version> </dependency>
使用 spring-data-redis ,即省去了自己實現注入的過程,通過它提供的一些配置,即可實現連接池配置、RedisTemplate 配置、JedisConnectionFactory 配置;通過 JedisConnectionFactory 可配置連接池參數、redis 服務器、端口、密碼、超時時間、database索引等;RedisTemplate 即注入的bean ,可以使用 RedisTemplate 自動注入的實體進行 redis 的一系列操作,具體看配置;
redis 服務屬性配置文件:
redis.maxIdle=300 redis.maxWait=3000 redis.testOnBorrow=true redis.host=192.168.31.121 redis.port=6379 redis.password=password redis.timeout=3000
spring-data-redis xml 配置文件 redis-context.xml:
<!-- jedis 連接池 配置 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" > <property name="maxIdle" value="${redis.maxIdle}" /> <property name="maxWaitMillis" value="${redis.maxWait}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <!-- redis服務器中心 --> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > <property name="poolConfig" ref="poolConfig" /> <property name="port" value="${redis.port}" /> <property name="hostName" value="${redis.host}" /> <!--<property name="password" value="${redis.password}" />--> <property name="timeout" value="${redis.timeout}" ></property> <property name="database" value="1"></property> </bean> <bean id="commonRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate" > <property name="connectionFactory" ref="connectionFactory" /> <property name="keySerializer" ref="stringRedisSerializer" /> <property name="hashKeySerializer" ref="stringRedisSerializer" /> <property name="valueSerializer" ref="stringRedisSerializer" /> <property name="hashValueSerializer" ref="stringRedisSerializer" /> </bean> <bean id="connectionFactory1" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > <property name="poolConfig" ref="poolConfig" /> <property name="port" value="${redis.port}" /> <property name="hostName" value="${redis.host}" /> <!--<property name="password" value="${redis.password}" />--> <property name="timeout" value="${redis.timeout}" ></property> <property name="database" value="2"></property> </bean> <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" /> <bean id="cacheRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate" > <property name="connectionFactory" ref="connectionFactory1" /> <property name="keySerializer" ref="stringRedisSerializer" /> <property name="hashKeySerializer" ref="stringRedisSerializer" /> <property name="valueSerializer" ref="stringRedisSerializer" /> <property name="hashValueSerializer" ref="stringRedisSerializer" /> </bean>
之後在 spring 配置文件中引用以上文件:
<import resource="redis-context.xml" />
解釋一下上面的配置:
poolConfig 即配置 redis 連接池,之後配置了兩個 JedisConnectionFactory 和 RedisTemplate ,一個 RedisTemplate 對應一個 JedisConnectionFactory ,這樣可以配置根據場景配置不同的 Redis 連接,比如超時時間要求不一致、database 0-15 可以存儲不同的數據等。這裡就配置了database 1 和 2 ,調用 commonRedisTemplate 會存到 database1 ,調用 cacheRedisTemplate 會存到 database2。
之後在 Service 層即可注入並引用這兩個 RedisTemplate ,如下代碼:
import org.apache.commons.lang3.StringUtils; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Repository; import javax.annotation.Resource; import java.io.*; @Repository public class RedisCache {
@Resource(name = "cacheRedisTemplate") private RedisTemplate<String, String> cacheRedisTemplate; public void put(Object key, Object value) { if(null == value) { return; } if(value instanceof String) { if(StringUtils.isEmpty(value.toString())) { return; } } // TODO Auto-generated method stub final String keyf = key + ""; final Object valuef = value; final long liveTime = 86400; cacheRedisTemplate.execute(new RedisCallback<Long>() { public Long doInRedis(RedisConnection connection) throws DataAccessException { byte[] keyb = keyf.getBytes(); byte[] valueb = toByteArray(valuef); connection.set(keyb, valueb); if (liveTime > 0) { connection.expire(keyb, liveTime); } return 1L; } }); } public Object get(Object key) { final String keyf = (String) key; Object object; object = cacheRedisTemplate.execute(new RedisCallback<Object>() { public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] key = keyf.getBytes(); byte[] value = connection.get(key); if (value == null) { return null; } return toObject(value); } }); return object; } /** * 描述 : <byte[]轉Object>. <br> * <p> * <使用方法說明> * </p> * * @param bytes * @return */ private Object toObject(byte[] bytes) { Object obj = null; try { ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis); obj = ois.readObject(); ois.close(); bis.close(); } catch (IOException ex) { ex.printStackTrace(); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } return obj; } private byte[] toByteArray(Object obj) { byte[] bytes = null; ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); oos.flush(); bytes = bos.toByteArray(); oos.close(); bos.close(); } catch (IOException ex) { ex.printStackTrace(); } return bytes; } }
最後在 Controller 中調用即可
@Autowired private RedisCache redisCache; @RequestMapping(value = "get", method = RequestMethod.GET) @ResponseBody public Object getByMyService(String key) { try { String result = redisService.get(key); return result; } catch (Exception e) { e.printStackTrace(); } return null; } @RequestMapping(value = "save", method = RequestMethod.GET) @ResponseBody public Object save() { Token token = new Token(); token.setAccess_token("token"); token.setExpires_in(1000); try { redisCache.put("token", token); } catch (Exception e) { e.printStackTrace(); } return "ok"; }
操作 Redis 集群
只用 Jedis 自己實現注入(區別於下面的引用spring-data-redis)
把前面的 JedisClusterClient 代碼拿過來引用即可,只需實現一個訪問 Redis 的 Service ,就可以集成到 Spring mvc 。Service 代碼如下:
import org.springframework.stereotype.Service; import util.JedisClusterClient; /** * Created by fengdezitai on 2016/10/13. */ @Service public class RedisClusterService { public void save() throws Exception{ //調用 JedisClusterClient 中的方法 JedisClusterClient jedisClusterClient = JedisClusterClient.getInstance(); try { jedisClusterClient.SaveRedisCluster(); }catch (Exception e){ throw e; } } }
最後在 Controller 中調用實現的 Service 即可
@Controller @RequestMapping(value = "redisCluster") public class RedisClusterController { @Autowired private RedisClusterService redisClusterService; @RequestMapping(value = "save",method = RequestMethod.GET) @ResponseBody public Object save(){ try{ redisClusterService.save(); }catch (Exception e){ e.printStackTrace(); return String.format("error: %s",e.getMessage()); } return "ok"; } }
用 spring-data-redis 包做集成
Spring 和 spring-data-redis maven 包引用和前面一致,之所以引用 spring-data-redis 1.7.2.RELEASE,是因為目前只有這個最新版本才支持集群操作。
redis 集群服務屬性配置
redis.maxIdle=300 redis.maxWait=3000 redis.testOnBorrow=false redis.timeout=3000
spring-data-redis xml 集群配置文件 redis-cluster-context.xml
<!-- 連接池 配置 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" > <property name="maxIdle" value="${redis.maxIdle}" /> <property name="maxWaitMillis" value="${redis.maxWait}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <bean id="redisClusterConfig" class="org.springframework.data.redis.connection.RedisClusterConfiguration"> <property name="maxRedirects" value="3"></property> <property name="clusterNodes"> <set> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.31.245"></constructor-arg> <constructor-arg name="port" value="7000"></constructor-arg> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.31.245"></constructor-arg> <constructor-arg name="port" value="7001"></constructor-arg> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.31.245"></constructor-arg> <constructor-arg name="port" value="7002"></constructor-arg> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.31.210"></constructor-arg> <constructor-arg name="port" value="7003"></constructor-arg> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.31.210"></constructor-arg> <constructor-arg name="port" value="7004"></constructor-arg> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="192.168.31.210"></constructor-arg> <constructor-arg name="port" value="7005"></constructor-arg> </bean> </set> </property> </bean> <bean id="redis4CacheConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <constructor-arg name="clusterConfig" ref="redisClusterConfig" /> <property name="timeout" value="${redis.timeout}" /> <property name="poolConfig" ref="poolConfig"/> </bean> <bean name="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" /> <bean id="clusterRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="redis4CacheConnectionFactory" /> <property name="keySerializer" ref="stringRedisSerializer" /> <property name="hashKeySerializer" ref="stringRedisSerializer" /> <property name="valueSerializer" ref="stringRedisSerializer" /> <property name="hashValueSerializer" ref="stringRedisSerializer" /> </bean>
之後在 Spring 配置文件中引用
<import resource="redis-cluster-context.xml" />
解釋以上配置:
poolConfig是連接池配置,redisClusterConfig 配置了 Redis 集群的各個節點(節點 host 和 port 最好寫在屬性配置文件中),集群搭建可見 我的 另一篇博客 。然後下面和單機配置一樣了,一對 JedisConnectionFactory 和 RedisTemplate 。
之後在 Service 層即可注入並引用這個 RedisTemplate,代碼如下:
import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Repository; import java.io.*; /** * Created by fengdezitai on 2016/9/29. */ @Repository public class RedisClusterCache { @Autowired private RedisTemplate clusterRedisTemplate; public void put(Object key, Object value) { if(null == value) { return; } if(value instanceof String) { if(StringUtils.isEmpty(value.toString())) { return; } } // TODO Auto-generated method stub final String keyf = key + ""; final Object valuef = value; final long liveTime = 86400; clusterRedisTemplate.execute(new RedisCallback<Long>() { public Long doInRedis(RedisConnection connection) throws DataAccessException { byte[] keyb = keyf.getBytes(); byte[] valueb = toByteArray(valuef); connection.set(keyb, valueb); if (liveTime > 0) { connection.expire(keyb, liveTime); } return 1L; } }); } public Object get(Object key) { final String keyf = (String) key; Object object; object = clusterRedisTemplate.execute(new RedisCallback<Object>() { public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] key = keyf.getBytes(); byte[] value = connection.get(key); if (value == null) { return null; } return toObject(value); } }); return object; } /** * 描述 : <byte[]轉Object>. <br> * <p> * <使用方法說明> * </p> * * @param bytes * @return */ private Object toObject(byte[] bytes) { Object obj = null; try { ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis); obj = ois.readObject(); ois.close(); bis.close(); } catch (IOException ex) { ex.printStackTrace(); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } return obj; } private byte[] toByteArray(Object obj) { byte[] bytes = null; ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); oos.flush(); bytes = bos.toByteArray(); oos.close(); bos.close(); } catch (IOException ex) { ex.printStackTrace(); } return bytes; } }
最後在 Controller 中調用即可
@Controller @RequestMapping(value = "redisCluster") public class RedisClusterController { @Autowired private RedisClusterCache redisClusterCache; @RequestMapping(value = "clusterSave",method = {RequestMethod.GET,RequestMethod.POST}) @ResponseBody public Object clusterSave(){ //redisClusterCache.put("cluster","save cluster"); Token token = new Token(); token.setExpires_in(1000); token.setAccess_token("hello world"); redisClusterCache.put("token",token); return "ok"; } @RequestMapping(value = "getKey",method = RequestMethod.GET) @ResponseBody public Object getCluster(String key){ Object val = redisClusterCache.get(key); return val; } }
注意事項: