Java技术升华

Spring+Dubbo集成Redis的两种解决方案

2018-04-17  本文已影响118人  YClimb

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

到此本文就结束了,关注公众号查看更多推送!!!


关注我的公众号
上一篇下一篇

猜你喜欢

热点阅读