企业项目开发--分布式缓存Redis(1)下篇

叁叁肆2018-12-18 10:43

此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。


RedisBaseUtil:

 1 package com.xxx.cache.redis;
 2 
 3 import redis.clients.jedis.ShardedJedis;
 4 import redis.clients.jedis.ShardedJedisPool;
 5 
 6 /**
 7  * 获取ShardJedis与归还实例 
 8  */
 9 public class RedisBaseUtil {
10     /**
11      * 从ShardJedisPool中获取ShardJedis
12      */
13     public static ShardedJedis getJedis(){
14         ShardedJedisPool jedisPool = RedisFactory.getJedisPool();//获取连接池
15         if(jedisPool == null){
16             return null;
17         }
18         return jedisPool.getResource();
19     }
20     
21     /**
22      * 归还jedis实例到连接池中
23      */
24     public static void returnJedis(ShardedJedis jedis, boolean broken){
25         if(jedis==null){//如果传入的jedis是null的话,不需要归还
26             return;
27         }
28         ShardedJedisPool jedisPool = RedisFactory.getJedisPool();//获取连接池
29         if(jedisPool == null){//如果连接池为null的话,不需要归还
30             return;
31         }
32         if(broken){//如果为true的话,表示是因为发生了异常才归还
33             jedisPool.returnBrokenResource(jedis);
34             return;
35         }
36         jedisPool.returnResource(jedis);//缓存正常操作结束之后,归还jedis
37     }
38 }

注意:

  • returnBrokenResource:操作被打断,即没有正常结束缓存操作,连接归还
  • returnResource:缓存正常操作结束后,连接归还

RedisStringUtil:

package com.xxx.cache.redis;

import com.xxx.cache.util.CachePrefix;

import redis.clients.jedis.ShardedJedis;

/**
 * 字符串缓存操作类或者JavaBean缓存操作类
 * key String, value String-->看下边的注意点2
 * key byte[], value byte[]-->key.getBytes[], value 序列化为byte[],通常需要自己写一个序列化工具
 * 注意:这一点与memcached不一样,memcached可以key String, value Object
 * 1、memcached直接加序列化器就可以,或者在业务层中将Object-->String
 * 2、redis执行此接口,一般只会采用后者Object-->String
 */
public class RedisStringUtil extends RedisBaseUtil{
    private static final String KEY_SPLIT = "-";//用于隔开缓存前缀与缓存键值
    /**
     * 设置缓存
     * 类似于memcached的set,不管是否已经有相同的key,都成功
     * 实际上只是set(String, String)
     */
    public static void set(CachePrefix keyPrefix, String key, String value){
        boolean broken = false;//标记:该操作是否被异常打断而没有正常结束
        ShardedJedis jedis = null;
        try {
            jedis = getJedis();//获取jedis实例
            if(jedis==null){
                broken = true;
                return;
            }
            jedis.set(keyPrefix+KEY_SPLIT+key, value);//set(String,String),value除了string以外,还可以是byte[]
        } catch (Exception e) {
            broken = true;
        }finally{
            returnJedis(jedis, broken);
        }
    }
    
    /**
     * 设置缓存,并指定缓存过期时间,单位是秒
     */
    public static void setex(CachePrefix keyPrefix, String key, String value, int expire){
        boolean broken = false;//该操作是否被异常打断而没有正常结束
        ShardedJedis jedis = null;
        try {
            jedis = getJedis();//获取jedis实例
            if(jedis==null){
                broken = true;
                return;
            }
            jedis.setex(keyPrefix+KEY_SPLIT+key, expire, value);
        } catch (Exception e) {
            broken = true;
        }finally{
            returnJedis(jedis, broken);
        }
    }
    
    /**
     * 设置缓存,如果设置的key不存在,直接设置,如果key已经存在了,则什么操作都不做,直接返回
     * 类似于memcached的add
     */
    public static boolean setnx(CachePrefix keyPrefix, String key, String value){
        boolean broken = false;//该操作是否被异常打断而没有正常结束
        ShardedJedis jedis = null;
        try {
            jedis = getJedis();//获取jedis实例
            if(jedis==null){
                broken = true;
                return false;
            }
            long setCount = jedis.setnx(keyPrefix+KEY_SPLIT+key, value);
            if(setCount == 1){
                return true;
            }
            return false;
        } catch (Exception e) {
            broken = true;
        }finally{
            returnJedis(jedis, broken);
        }
        return false;
    }
    
    /**
     * 根据key获取缓存
     * @param key
     * @return String
     */
    public static String get(CachePrefix keyPrefix, String key){
        boolean broken = false;//该操作是否被异常打断而没有正常结束
        ShardedJedis jedis = null;
        try {
            jedis = getJedis();//获取jedis实例
            if(jedis==null){
                broken = true;
                return null;
            }
            return jedis.get(keyPrefix+KEY_SPLIT+key);
        } catch (Exception e) {
            broken = true;
        }finally{
            returnJedis(jedis, broken);
        }
        return null;
    }
    
    /**
     * 删除缓存
     */
    public static void delete(CachePrefix keyPrefix, String key){
        boolean broken = false;//该操作是否被异常打断而没有正常结束
        ShardedJedis jedis = null;
        try {
            jedis = getJedis();//获取jedis实例
            if(jedis==null){
                broken = true;
                return;
            }
            jedis.del(keyPrefix+KEY_SPLIT+key);
        } catch (Exception e) {
            broken = true;
        }finally{
            returnJedis(jedis, broken);
        }
    }
    
    /**
     * 更新缓存过期时间,单位:秒
     * 从运行该方法开始,为相应的key-value设置缓存过期时间expire
     * 类似于memcached中的touch命令
     */
    public static void setExpire(CachePrefix keyPrefix, String key, int expire){
        boolean broken = false;
        ShardedJedis jedis = null;
        try {
            jedis = getJedis();
            if(jedis==null){
                broken = true;
                return;
            }
            jedis.expire(keyPrefix+KEY_SPLIT+key, expire);
        } catch (Exception e) {
            broken = true;
        }finally{
            returnJedis(jedis, broken);
        }
    }
    
    /**
     * 测试
     */
    public static void main(String[] args) {
        //System.out.println(RedisStringUtil.get("hello"));
        //RedisStringUtil.delete("hello");
        //RedisStringUtil.setex("hello1", "word1", 1);
        //RedisStringUtil.setExpire("hello1", 20);
        //System.out.println(RedisStringUtil.get("hello1"));
    }

}

注意:

  • 只有set(string,string)和set(byte[],byte[]),前者类似于Xmemcached的文本协议,后者类似于Xmemcached的二进制协议
  • set-->Xmemcached的set,redis上是否已经有与将要存放的key相同的key,都会操作成功
  • setnx-->Xmemcached的add,redis上有与将要存放的key相同的key,操作失败
  • expire-->Xmemcached的touch,该方法会在方法执行的时候,为还存在的key-value重新指定缓存过期时间

 

附:

这里需要安装一个redis服务器,redis在实际使用中是安装在Linux上的,我们为了方便,使用windows版本的(我这里使用了redis2.6-win32),如果是64bit的,可以使用redis2.8。

redis2.6(32bit)的文件下载链接:http://pan.baidu.com/s/1hri1erq

安装方式如下:

下载后解压,此时如果直接双击"redis-server.exe",可能会报内存警告,所以先修改redis.conf文件,添加如下配置,如果你下载的上边的链接,可能已经配置了。


之后,以管理员身份运行cmd.exe,并在命令窗口中进入redis-server.exe所在目录下,执行"redis-server.exe redis.conf"即可。

 

2.3、ssmm0-data


AdminService:

    /*********************redis********************/
    public Admin findAdminByIdFromRedis(int id) {
        //从缓存中获取数据
        String adminStr = RedisStringUtil.get(CachePrefix.USER_MANAGEMENT, String.valueOf(id));
        //若缓存中有,直接返回
        if(StringUtils.isNoneBlank(adminStr)){
            return Admin.parseJsonToAdmin(adminStr);
        }
        //若缓存中没有,从数据库查询
        Admin admin = adminDao.getUserById(id);
        //若查询出的数据不为null
        if(admin!=null){
            //将数据存入缓存
            RedisStringUtil.set(CachePrefix.USER_MANAGEMENT, String.valueOf(id), admin.toJson());
        }
        //返回从数据库查询的admin(当然也可能数据库中也没有,就是null)
        return admin;
    }

说明:只添加了如上方法,相当于将上一节的memcached缓存换成了redis

 

2.4、ssmm0-userManagement


AdminController:

    /*************************redis******************************/
    /**
     * 根据id查找Admin
     */
    @ResponseBody
    @RequestMapping("/findAdminByIdFromRedis")
    public Admin findAdminByIdFromRedis(@RequestParam(value="id") int id){
        
        return adminService.findAdminByIdFromRedis(id);
    }

说明:只添加了如上方法。

 

3、测试

首先对ssmm0整个项目"clean compile",然后通过浏览器访问,进行整体测试,测试方法与上一章《第八章 企业项目开发--分布式缓存memcached》完全相同

 

在以上的代码中,我只写了redis的String类型数据结构的缓存操作,其余的四种下一篇再说。


免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击