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

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.datagroupId>
    <artifactId>spring-data-redisartifactId>
    <version>1.6.0.RELEASEversion>
dependency>
<dependency>
    <groupId>redis.clientsgroupId>
    <artifactId>jedisartifactId>
    <version>2.7.3version>
dependency>

设置完引用以后,就可以开始着手编写redis在spring中的配置文件了,下面直接上代码 applicationContext.xml 文件:


<import resource="spring-redis.xml" />

spring-redis.xml 文件:


<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">

    
    <context:property-placeholder location="classpath:redis.properties" />

    
    

    
    <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>

    
    <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>

    
    <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;

/**
 *
 * 

* Redis客户端访问 *

* * 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 get(String key,Class 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;

/**
 *
 * 

* 测试独立redis 客户端 *

* * 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  key
 * @param  value
 */
public interface RedisCacheStorageService {
    /**
     * 在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 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 
 */
@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 hget(String cacheKey) {
        String jCacheKey = JSON.toJSONString(cacheKey);
        // 非空校验
        if (StringUtils.isEmpty(jCacheKey)) {
            LOG.info("cacheKey is empty!");
            return null;
        }
        Jedis jedis = null;
        Map result = null;
        try {
            jedis = redisCache.getResource();
            // 获取列表集合
            Map map = jedis.hgetAll(jCacheKey);

            if (null != map) {
                for (Map.Entry entry : map.entrySet()) {
                    if (result == null) {
                        result = new HashMap();
                    }
                    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服务了,下面是注入的示例代码:


<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">

    
    <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

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


关注我的公众号


你可能感兴趣的:(redis)