Spring+Dubbo集成Redis的两种解决方案
当下我们的系统数据库压力都非常大,解决数据库的瓶颈问题势在必行,为了解决数据库的压力等需求,我们常用的是各种缓存,比如redis,本文就来简单讲解一下如何集成redis缓存存储,附github源码。
环境准备
· redis
· IDEA 开发工具
· JDK 1.8及以上
· Maven 4.0及以上
redis的搭建网上有很多例子,这里就不细讲了,友友们可以网上浏览安装一波,下面我们就直接讲如何在spring中集成redis。
资源配置
1、spring集成redis
第一步我们先设置maven的pom.xml引用,代码如下:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.0.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency>
设置完引用以后,就可以开始着手编写redis在spring中的配置文件了,下面直接上代码 applicationContext.xml
文件:
<!-- redis -->
<import resource="spring-redis.xml" />
spring-redis.xml
文件:
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 加载redis参数 -->
<context:property-placeholder location="classpath:redis.properties" />
<!-- 自动注解 -->
<!--<context:component-scan base-package="service.impl" />-->
<!-- jedis 连接池配置参数: -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 设置最大连接数 -->
<property name="maxTotal" value="${redis.maxActive}"></property>
<!-- 设置最大空闲数 -->
<property name="maxIdle" value="${redis.maxIdle}"></property>
<!-- 设置超时时间 -->
<property name="maxWaitMillis" value="${redis.maxWait}"></property>
<property name="testOnBorrow" value="${redis.testOnBorrow}"></property>
<property name="testOnReturn" value="${redis.testOnReturn}"></property>
</bean>
<!-- jedis 连接池 连接本地redis服务 构造器注入 -->
<bean id="pool" class="redis.clients.jedis.JedisPool">
<constructor-arg index="0" ref="poolConfig"/>
<constructor-arg index="1" value="${redis.host}"/>
<constructor-arg index="2" value="${redis.port}"/>
<constructor-arg index="3" value="${redis.maxWait}"/>
<constructor-arg index="4" value="${redis.pass}"/>
</bean>
<!-- redis cache config -->
<bean id="redisCache" class="client.RedisCache">
<property name="pool" ref="pool"/>
</bean>
</beans>
此文件主要描述了jedis的连接池和配置参数,需要注意的是,jedis的版本不同可能会导致具体的参数不一样,比如2.5.1,大家引用的时候如果有其他版本可以看看源码中的属性参数。
下面是 redis.properties
配置文件,主要配置具体的参数值:
# Redis settings
redis.host=localhost
redis.port=6379
redis.pass=123456
redis.maxIdle=25
redis.maxActive=100
redis.maxWait=1000
redis.testOnBorrow=false
redis.testOnReturn=false
2、Redis客户端编写
环境和资源已经配置完成,下一次可以开始编写我们的redis客户端程序了,代码如下:
package client;
import com.alibaba.fastjson.JSON;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;
/**
*
* <p>
* Redis客户端访问
* </p>
*
* Created by yclimb on 2017/6/8.
*/
public class RedisClient {
/**
* 池化管理jedis链接池
*/
public static JedisPool jedisPool;
static {
//读取相关的配置
ResourceBundle resourceBundle = ResourceBundle.getBundle("redis");
int maxActive = Integer.parseInt(resourceBundle.getString("redis.pool.maxActive"));
int maxIdle = Integer.parseInt(resourceBundle.getString("redis.pool.maxIdle"));
int maxWait = Integer.parseInt(resourceBundle.getString("redis.pool.maxWait"));
String ip = resourceBundle.getString("redis.ip");
int port = Integer.parseInt(resourceBundle.getString("redis.port"));
JedisPoolConfig config = new JedisPoolConfig();
//设置最大连接数
config.setMaxTotal(maxActive);
//设置最大空闲数
config.setMaxIdle(maxIdle);
//设置超时时间
config.setMaxWaitMillis(maxWait);
//初始化连接池
jedisPool = new JedisPool(config, ip, port);
}
/**
* 向缓存中设置字符串内容
* @param key key
* @param value value
* @return
* @throws Exception
*/
public static boolean set(String key,String value) throws Exception{
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
jedis.set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}finally{
jedisPool.returnResource(jedis);
}
}
/**
* 向缓存中设置对象
* @param key
* @param value
* @return
*/
public static boolean set(String key,Object value){
Jedis jedis = null;
try {
String objectJson = JSON.toJSONString(value);
jedis = jedisPool.getResource();
jedis.set(key, objectJson);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}finally{
jedisPool.returnResource(jedis);
}
}
/**
* 删除缓存中得对象,根据key
* @param key
* @return
*/
public static boolean del(String key){
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
jedis.del(key);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}finally{
jedisPool.returnResource(jedis);
}
}
/**
* 根据key 获取内容
* @param key
* @return
*/
public static Object get(String key){
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
Object value = jedis.get(key);
return value;
} catch (Exception e) {
e.printStackTrace();
return false;
}finally{
jedisPool.returnResource(jedis);
}
}
/**
* 根据key 获取对象
* @param key
* @return
*/
public static <T> T get(String key,Class<T> clazz){
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String value = jedis.get(key);
return JSON.parseObject(value, clazz);
} catch (Exception e) {
e.printStackTrace();
return null;
}finally{
jedisPool.returnResource(jedis);
}
}
}
此文件是一个简单的redis客户端,可以直接使用此客户端操作jedis的存取方法,Test类如下:
package test;
import client.RedisClient;
import entity.City;
import org.junit.Test;
/**
*
* <p>
* 测试独立redis 客户端
* </p>
*
* Created by yclimb on 2017/6/8.
*/
public class SimpleClient {
@Test
public void userCache(){
//向缓存中保存对象
City city = new City();
city.setCity("city");
city.setCity("1");
city.setLastUpdate("2222");
//调用方法处理
boolean reusltCache = RedisClient.set("city1", city);
if (reusltCache) {
System.out.println("向缓存中保存对象成功。");
}else{
System.out.println("向缓存中保存对象失败。");
}
}
@Test
public void getUserInfo(){
City city = RedisClient.get("city1", City.class);
if (city != null) {
System.out.println("从缓存中获取的对象," + city.getCity() + "@" + city.getLastUpdate());
}
}
}
此时,我们的第一个简单的redis客户端就已经成功了;但是,平时我们都是使用rpc分布式架构,所以说我们还需要一个service接口化的redis存储器,方便dubbo服务调用,下面我们就一起来编写dubbo的redis service存储器。
dubbo服务化的redis存储器
首先,我们需要定义一个redis的缓存配置类,主要用户获取和关闭redis连接,需要使用资源配置时的jedis pool,代码如下:
package client;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.io.Serializable;
/**
* redis 缓存配置
* @author yclimb
*/
public class RedisCache implements Serializable {
/**
* 日志记录
*/
private static final Log LOG = LogFactory.getLog(RedisCache.class);
/**
* redis 连接池
*/
private JedisPool pool;
public void setPool(JedisPool pool) {
this.pool = pool;
}
/*static {
if (pool == null) {
//读取相关的配置
ResourceBundle resourceBundle = ResourceBundle.getBundle("redis");
int maxActive = Integer.parseInt(resourceBundle.getString("redis.maxActive"));
int maxIdle = Integer.parseInt(resourceBundle.getString("redis.maxIdle"));
int maxWait = Integer.parseInt(resourceBundle.getString("redis.maxWait"));
String host = resourceBundle.getString("redis.host");
int port = Integer.parseInt(resourceBundle.getString("redis.port"));
String pass = resourceBundle.getString("redis.pass");
JedisPoolConfig config = new JedisPoolConfig();
//设置最大连接数
config.setMaxTotal(maxActive);
//设置最大空闲数
config.setMaxIdle(maxIdle);
//设置超时时间
config.setMaxWaitMillis(maxWait);
//初始化连接池
pool = new JedisPool(config, host, port, 2000, pass);
}
}*/
/**
* 获取jedis
*
* @return jedis
*/
public Jedis getResource() {
Jedis jedis = null;
try {
jedis = pool.getResource();
} catch (Exception e) {
LOG.info("can't get the redis resource");
}
return jedis;
}
/**
* 关闭连接
*
* @param jedis j
*/
public void disconnect(Jedis jedis) {
jedis.disconnect();
}
/**
* 将jedis 返还连接池
*
* @param jedis j
*/
public void returnResource(Jedis jedis) {
if (null != jedis) {
try {
pool.returnResource(jedis);
} catch (Exception e) {
LOG.info("can't return jedis to jedisPool");
}
}
}
/**
* 无法返还jedispool,释放jedis客户端对象
*
* @param jedis j
*/
public void brokenResource(Jedis jedis) {
if (jedis != null) {
try {
pool.returnBrokenResource(jedis);
} catch (Exception e) {
LOG.info("can't release jedis Object");
}
}
}
}
默认使用spring中给的配置文件,自动注入,也可以使用代码中注释的静态代码块,这个看个人需求。
有了缓存配置和jedis pool,此时我们就可以开始编写增删改查的service存储器了,代码如下:
接口:RedisCacheStorageService.java
:
package service;
import java.util.Map;
/**
* 缓存存储接口
* @author yclimb
*
* @param <K> key
* @param <V> value
*/
public interface RedisCacheStorageService<K, V> {
/**
* 在redis数据库中插入 key 和value
*
* @param key
* @param value
* @return
*/
boolean set(K key, V value);
/**
* 在redis数据库中插入 key 和value 并且设置过期时间
*
* @param key
* @param value
* @param exp 过期时间 s
* @return
*/
boolean set(K key, V value, int exp);
/**
* 根据key 去redis 中获取value
*
* @param key
* @return
*/
V get(K key);
/**
* 删除redis库中的数据
*
* @param key
* @return
*/
boolean remove(K key);
/**
* 设置哈希类型数据到redis 数据库
*
* @param cacheKey 可以看做一张表
* @param key 表字段
* @param value
* @return
*/
boolean hset(String cacheKey, K key, V value);
/**
* 获取哈希表数据类型的值
*
* @param cacheKey
* @param key
* @return
*/
V hget(String cacheKey, K key);
/**
* 获取哈希类型的数据
*
* @param cacheKey
* @return
*/
Map<K, V> hget(String cacheKey);
}
实现类:RedisCacheStorageServiceImpl.java
:
package service.impl;
import client.RedisCache;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import service.RedisCacheStorageService;
import java.util.HashMap;
import java.util.Map;
/**
* redis 缓存存储器实现
* @author yclimb
*
* @param <V>
*/
@Service
public class RedisCacheStorageServiceImpl<V> implements RedisCacheStorageService<String, V> {
/**
* 日志记录
*/
public static final Log LOG = LogFactory.getLog(RedisCacheStorageServiceImpl.class);
/**
* 默认过时时间(60 * 60 * 24)
*/
private static final int EXPIRE_TIME = 86400;
@Autowired
private RedisCache redisCache;
/**
* 在redis数据库中插入 key和value
*
* @param key k
* @param value v
* @return boolean
*/
@Override
public boolean set(String key, V value) {
// 设置默认过时时间
return set(key, value, EXPIRE_TIME);
}
/**
* 在redis数据库中插入 key和value 并且设置过期时间
*
* @param key k
* @param value v
* @param exp 过期时间 s
* @return boolean
*/
@Override
public boolean set(String key, V value, int exp) {
Jedis jedis = null;
// 将key 和value 转换成 json 对象
String jKey = JSON.toJSONString(key);
String jValue = JSON.toJSONString(value);
// 操作是否成功
boolean isSucess = true;
if (StringUtils.isEmpty(jKey)) {
LOG.info("key is empty");
return false;
}
try {
// 获取客户端对象
jedis = redisCache.getResource();
// 执行插入
jedis.setex(jKey, exp, jValue);
} catch (Exception e) {
LOG.info("client can't connect server");
isSucess = false;
if (null != jedis) {
// 释放jedis对象
redisCache.brokenResource(jedis);
}
return false;
} finally {
if (isSucess) {
// 返还连接池
redisCache.returnResource(jedis);
}
}
return true;
}
/**
* 根据key去redis中获取value
*
* @param key k
* @return obj
*/
@Override
public V get(String key) {
Jedis jedis = null;
// 将key 和value 转换成 json 对象
String jKey = JSON.toJSONString(key);
V jValue = null;
// key 不能为空
if (StringUtils.isEmpty(jKey)) {
LOG.info("key is empty");
return null;
}
try {
// 获取客户端对象
jedis = redisCache.getResource();
// 执行查询
String value = jedis.get(jKey);
// 判断值是否非空
if (StringUtils.isEmpty(value)) {
return null;
} else {
jValue = (V) JSON.parse(value);
}
// 返还连接池
redisCache.returnResource(jedis);
} catch (Exception e) {
LOG.info("client can't connect server");
if (null != jedis) {
// 释放jedis对象
redisCache.brokenResource(jedis);
}
}
return jValue;
}
/**
* 删除redis库中的数据
*
* @param key k
* @return boolean
*/
@Override
public boolean remove(String key) {
Jedis jedis = null;
// 将key 和value 转换成 json 对象
String jKey = JSON.toJSONString(key);
// 操作是否成功
boolean isSucess = true;
if (StringUtils.isEmpty(jKey)) {
LOG.info("key is empty");
return false;
}
try {
jedis = redisCache.getResource();
// 执行删除
jedis.del(jKey);
} catch (Exception e) {
LOG.info("client can't connect server");
isSucess = false;
if (null != jedis) {
// 释放jedis对象
redisCache.brokenResource(jedis);
}
return false;
} finally {
if (isSucess) {
// 返还连接池
redisCache.returnResource(jedis);
}
}
return true;
}
/**
* 设置哈希类型数据到redis数据库
*
* @param cacheKey 可以看做一张表
* @param key 表字段
* @param value v
* @return boolean
*/
@Override
public boolean hset(String cacheKey, String key, V value) {
Jedis jedis = null;
// 将key 和value 转换成 json 对象
String jKey = JSON.toJSONString(key);
String jCacheKey = JSON.toJSONString(cacheKey);
String jValue = JSON.toJSONString(value);
// 操作是否成功
boolean isSucess = true;
if (StringUtils.isEmpty(jCacheKey)) {
LOG.info("cacheKey is empty");
return false;
}
try {
jedis = redisCache.getResource();
// 执行插入哈希
jedis.hset(jCacheKey, jKey, jValue);
} catch (Exception e) {
LOG.info("client can't connect server");
isSucess = false;
if (null != jedis) {
// 释放jedis对象
redisCache.brokenResource(jedis);
}
return false;
} finally {
if (isSucess) {
// 返还连接池
redisCache.returnResource(jedis);
}
}
return true;
}
/**
* 获取哈希表数据类型的值
*
* @param cacheKey cacheK
* @param key k
* @return obj
*/
@Override
public V hget(String cacheKey, String key) {
Jedis jedis = null;
// 将key 和value 转换成 json 对象
String jKey = JSON.toJSONString(key);
String jCacheKey = JSON.toJSONString(cacheKey);
V jValue = null;
if (StringUtils.isEmpty(jCacheKey)) {
LOG.info("cacheKey is empty");
return null;
}
try {
// 获取客户端对象
jedis = redisCache.getResource();
// 执行查询
String value = jedis.hget(jCacheKey, jKey);
// 判断值是否非空
if (StringUtils.isEmpty(value)) {
return null;
} else {
jValue = (V) JSON.parse(value);
}
// 返还连接池
redisCache.returnResource(jedis);
} catch (Exception e) {
LOG.info("client can't connect server");
if (null != jedis) {
// 释放jedis对象
redisCache.brokenResource(jedis);
}
}
return jValue;
}
/**
* 获取哈希类型的数据
*
* @param cacheKey cacheK
* @return map
*/
@Override
public Map<String, V> hget(String cacheKey) {
String jCacheKey = JSON.toJSONString(cacheKey);
// 非空校验
if (StringUtils.isEmpty(jCacheKey)) {
LOG.info("cacheKey is empty!");
return null;
}
Jedis jedis = null;
Map<String, V> result = null;
try {
jedis = redisCache.getResource();
// 获取列表集合
Map<String, String> map = jedis.hgetAll(jCacheKey);
if (null != map) {
for (Map.Entry<String, String> entry : map.entrySet()) {
if (result == null) {
result = new HashMap<String, V>();
}
result.put((String) JSON.parse(entry.getKey()), (V) JSON.parse(entry.getValue()));
}
}
} catch (Exception e) {
LOG.info("client can't connect server");
if (null != jedis) {
// 释放jedis对象
redisCache.brokenResource(jedis);
}
}
return result;
}
}
到这里我们的存储器就编写完成了,接下来就是看看如何注入dubbo服务了,下面是注入的示例代码:
<?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:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- redis -->
<dubbo:service timeout="${dubbo-timeout}" retries="${dubbo-retries}" interface="com.yc.redis.RedisCacheStorageService" ref="redisCacheStorageServiceImpl" group="${service.group}" />
</beans>
OK,代码编写完成,这里dubbo服务调用的代码我就不贴上了,各位可以自己试一试,到这里一套基于jedis的简单示例就完成了。
结语
源码地址:https://github.com/YClimb/redis-demo
到此本文就结束了,关注公众号查看更多推送!!!
关注我的公众号
网友评论