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

叁叁肆2018-12-18 10:40

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

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


2.2、ssmm0-cache


pom.xml完整版(只是添加了jedis的依赖包)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <!-- 指定父模块 -->
    <parent>
        <groupId>com.xxx</groupId>
        <artifactId>ssmm0</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.xxx.ssmm0</groupId>
    <artifactId>ssmm0-cache</artifactId>

    <name>ssmm0-cache</name>
    <packaging>jar</packaging>

    <!-- 引入实际依赖 -->
    <dependencies>
        <!-- memcached -->
        <dependency>
            <groupId>com.googlecode.xmemcached</groupId>
            <artifactId>xmemcached</artifactId>
            <version>1.4.3</version>
        </dependency>
        <!-- redis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.6.1</version>
        </dependency>
    </dependencies>
</project>

cache_config.properties(添加了redis相关配置)

#memcached配置#
#memcached服务器集群
memcached.servers = ${memcached.servers}
#缓存过期时间
memcached.expiretime = ${memcached.expiretime}
#是否使用一致性hash算法
memcached.hash.consistent = ${memcached.hash.consistent}
#memcached的最大客户端数量
memcached.max.client = ${memcached.max.client}
#每个客户端池子的连接数
memcached.connection.poolsize = ${memcached.connection.poolsize}
#操作超时时间
memcached.op.timeout = ${memcached.op.timeout}

#redis配置#
#redis集群
redis.servers = ${redis.servers}
#超时时间
redis.timeout = ${redis.timeout}
#是否启用后进先出
redis.conf.lifo = ${redis.conf.lifo}
#最多创建几个ShardJedis,即连接
redis.conf.maxTotal = ${redis.conf.maxTotal}
#连接耗尽是否阻塞等待
redis.conf.blockWhenExhausted = ${redis.conf.blockWhenExhausted}
#等待获取连接的最长时间
redis.conf.maxWaitMillis = ${redis.conf.maxWaitMillis}
#获取连接前,是否对连接进行测试
redis.conf.testOnBorrow = ${redis.conf.testOnBorrow}
#归还连接前,是否对连接进行测试
redis.conf.testOnReturn = ${redis.conf.testOnReturn}
#最大空闲连接数
redis.conf.maxIdle = ${redis.conf.maxIdle}
#最小空闲连接数
redis.conf.minIdle = ${redis.conf.minIdle}
#对空闲连接进行扫描,检查连接有效性
redis.conf.testWhileIdle = ${redis.conf.testWhileIdle}
#两次扫描空闲连接的时间间隔
redis.conf.timeBetweenEvictionRunsMillis = ${redis.conf.timeBetweenEvictionRunsMillis}
#每次空闲扫描时扫描的控线连接的个数
redis.conf.numTestsPerEvictionRun = ${redis.conf.numTestsPerEvictionRun}
#一个空闲连接至少连续保持多长时间空闲才会被空闲扫描
redis.conf.minEvictableIdleTimeMillis = ${redis.conf.minEvictableIdleTimeMillis}

7个Java类:

  • RedisFactory:构建ShardJedisPool。
    • 一个ShardJedisPool中配置了多个JedisShardInfo
    • 每一个JedisShardInfo都是一个server
    • 一个ShardJedisPool中可以获取多个ShardJedis连接实例,具体数目由maxTotal属性而定
  • RedisBaseUtil:获取获取ShardJedis连接与归还ShardJedis连接(这是其他所有缓存操作都需要的方法)
  • RedisStringUtil:redis的第一种数据结构--字符串,操作类
  • RedisListUtil:redis的第二种数据结构--list,操作类
  • RedisSetUtil:redis的第三种数据结构--set,操作类
  • RedisSortedSetUtil:redis的第四种数据结构--sorted set,操作类
  • RedisHashUtil:redis的第五种数据结构--hash,操作类

RedisFactory:

  1 package com.xxx.cache.redis;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 import java.util.Properties;
  6 
  7 import org.apache.commons.lang3.math.NumberUtils;
  8 
  9 import com.xxx.cache.util.FileUtil;
 10 
 11 import redis.clients.jedis.JedisPoolConfig;
 12 import redis.clients.jedis.JedisShardInfo;
 13 import redis.clients.jedis.ShardedJedisPool;
 14 
 15 public class RedisFactory {
 16     private static ShardedJedisPool jedisPool = null;
 17     /**
 18      * 构建ShardJedisPool
 19      * 一个ShardJedisPool中配置了多个JedisShardInfo
 20      * 每一个JedisShardInfo都是一个server
 21      * 一个ShardJedisPool中可以获取多个ShardJedis连接实例,具体数目由maxTotal属性而定
 22      * 注意:
 23      * 1、这里只有一个ShardJedisPool,如果你有很多业务,而且不想这些业务都共用几台redis服务器的话,
 24      *         你可以创建多个ShardJedisPool,每个pool中放置不同的服务器即可
 25      * 2、这时候多个ShardJedisPool可以放置在一个hashmap中,key由自己指定(写在一个Enum类中去),key的名称一般与业务挂钩就好
 26      */
 27     static{
 28         Properties props = FileUtil.loadProps("cache_config.properties");//加载属性文件
 29         /*
 30          * 从属性文件读取参数
 31          */
 32         String servers = props.getProperty("redis.servers", "127.0.0.1:6379");
 33         String[] serverArray = servers.split(" ");//获取服务器数组
 34         
 35         int timeout = FileUtil.getInt(props, "redis.timeout", 5000);//默认:2000ms(超时时间:单位ms)
 36         boolean lifo = FileUtil.getBoolean(props, "redis.conf.lifo", true);//默认:true
 37         
 38         int maxTotal = FileUtil.getInt(props, "redis.conf.maxTotal", 64);//默认:8个(最多创建几个ShardJedis,即连接)
 39         boolean blockWhenExhausted = FileUtil.getBoolean(props, "redis.conf.blockWhenExhausted", true);//默认:true(连接耗尽是否阻塞等待)
 40         long maxWaitMillis = FileUtil.getLong(props, "redis.conf.maxWaitMillis", -1);//默认:-1,即无限等待(等待获取连接的最长时间)
 41         
 42         boolean testOnBorrow = FileUtil.getBoolean(props, "redis.conf.testOnBorrow", false);//默认:false(获取连接前,是否对连接进行测试)
 43         boolean testOnReturn = FileUtil.getBoolean(props, "redis.conf.testOnReturn", false);//默认:false(归还连接前,是否对连接进行测试)
 44         
 45         int maxIdle = FileUtil.getInt(props, "redis.conf.maxIdle", 8);//默认:8(最大空闲连接数)
 46         int minIdle = FileUtil.getInt(props, "redis.conf.minIdle", 0);//默认:0(最小空闲连接数)
 47         boolean testWhileIdle = FileUtil.getBoolean(props, "redis.conf.testWhileIdle", true);//默认:false(对空闲连接进行扫描,检查连接有效性)
 48         long timeBetweenEvictionRunsMillis = FileUtil.getLong(props, "redis.conf.timeBetweenEvictionRunsMillis", 30000);//默认:-1,(两次扫描空闲连接的时间间隔)
 49         int numTestsPerEvictionRun = FileUtil.getInt(props, "redis.conf.numTestsPerEvictionRun", 3);//默认:3(每次空闲扫描时扫描的控线连接的个数)
 50         long minEvictableIdleTimeMillis = FileUtil.getLong(props, "redis.conf.minEvictableIdleTimeMillis", 60000);//默认:30min(一个空闲连接至少连续保持30min中空闲才会被空闲扫描)
 51         /*
 52          * 配置redis参数
 53          */
 54         JedisPoolConfig config = new JedisPoolConfig();
 55         config.setLifo(lifo);//(last in, first out)是否启用后进先出,默认true
 56         /*
 57          * 即原来的maxActive,能够同时建立的最大连接个数(就是最多分配多少个ShardJedis实例),
 58          * 默认8个,若设置为-1,表示为不限制,
 59          * 如果pool中已经分配了maxActive个jedis实例,则此时pool的状态就成exhausted了
 60          * 
 61          * 这里最多可以生产64个shardJedis实例
 62          */
 63         config.setMaxTotal(maxTotal);
 64         config.setBlockWhenExhausted(blockWhenExhausted);//连接耗尽时是否阻塞, false报异常,true阻塞直到超时, 默认true, 达到maxWait时抛出JedisConnectionException
 65         config.setMaxWaitMillis(maxWaitMillis);//获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
 66         
 67         config.setTestOnBorrow(testOnBorrow);//使用连接时,先检测连接是否成功,若为true,则获取到的shardJedis连接都是可用的,默认false
 68         config.setTestOnReturn(testOnReturn);//归还连接时,检测连接是否成功
 69         
 70         /*
 71          * 空闲状态
 72          */
 73         config.setMaxIdle(maxIdle);//空闲连接数(即状态为idle的ShardJedis实例)大于maxIdle时,将进行回收,默认8个
 74         config.setMinIdle(minIdle);//空闲连接数小于minIdle时,创建新的连接,默认0
 75         /*
 76          * 在空闲时检查有效性, 默认false,如果为true,表示有一个idle object evitor线程对idle object进行扫描,
 77          * 如果validate失败,此object会被从pool中drop掉;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义
 78          */
 79         config.setTestWhileIdle(testWhileIdle);
 80         config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);//表示idle object evitor两次扫描之间要sleep的毫秒数;
 81         config.setNumTestsPerEvictionRun(numTestsPerEvictionRun);//表示idle object evitor每次扫描的最多的对象数;
 82         //表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;
 83         config.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
 84         
 85         /*
 86          * 构建JedisShardInfo集合
 87          */
 88         List<JedisShardInfo> jedisList = new ArrayList<JedisShardInfo>(1);//我这里只有一台机器,所以传入参数1,否则默认为10,浪费空间
 89         for(String server : serverArray){
 90             String[] hostAndPort = server.split(":");
 91             /*
 92              * 这句代码中我没有判断hostAndPort是不是长度为2,而且端口如果没有指定或指定错误的话,就直接转到6379
 93              * 实际中,我们在配置服务器的时候就一定要注意配置格式正确:host:port
 94              */
 95             JedisShardInfo shardInfo = new JedisShardInfo(hostAndPort[0], 
 96                                                           NumberUtils.toInt(hostAndPort[1], 6379), 
 97                                                           timeout);
 98             jedisList.add(shardInfo);
 99         }
100         /*
101          * 创建ShardJedisPool
102          */
103         jedisPool = new ShardedJedisPool(config, jedisList);//构建jedis池
104     }
105     
106     /**
107      * 如果有多个ShardJedisPool,则需要写一个hash算法从hashmap中选一个pool返回
108      */
109     public static ShardedJedisPool getJedisPool() {
110         return jedisPool;
111     }
112 }

注意:

  • 这里只有一个ShardJedisPool,如果你有很多业务,而且不想这些业务都共用几台redis服务器的话,你可以创建多个ShardJedisPool,每个pool中放置不同的服务器即可;这时候多个ShardJedisPool可以放置在一个hashmap中,key由自己指定(写在一个Enum类中去),key的名称一般与业务挂钩就好。如果没说清,看下图:(在我当前的程序中由于只有一个业务简单,值设置了一个ShardJedisPool,所以没有下图的hashmap)

  • 配置参数较多(这里列出几乎所有的配置参数)
    • testOnBorrow:使用连接时,先检测连接是否成功,若为true,则获取到的shardJedis连接都是可用的,默认false;在实际使用中,直接使用默认值,因为虽然该参数配置为true可以保证获取到的连接一定可用,但是由于每次获取连接都要进行测试,所以效率会变低;考虑到获取到的连接不可用的概率很低,综合考虑下,将该值设为false还是比较合适的。
    • testOnReturn:同上,在我们下面的程序中可以看到每个缓存操作方法的流程都是"获取连接-->进行缓存操作-->归还连接"
    • 注意这里jedis的版本是2.6.1,一些配置属性可能在其他版本看不到(eg.maxTotal),而其他版本的一些属性可能在该版本中没有(eg.maxActive)。
  • jedis参数介绍参考:http://blog.csdn.net/huahuagongzi99999/article/details/13631579,但是由于版本不同,请注意上边这一条所说的。


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

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