0%

Jedis开发redis单机和集群以及缓存同步

Jedis开发redis单机和集群

redis客户端依赖

1
2
3
4
5
6
 <!-- Redis客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.2</version>
</dependency>

redis单机

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testJedisPool() {
//config the properties of jedis pool
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(50);
poolConfig.setMinIdle(10);

JedisPool jedisPool = new JedisPool(poolConfig,"192.168.25.128",6379);

Jedis jedis = jedisPool.getResource();
jedis.set("age", "20");
System.out.println(jedis.get("age")); //20
}

redis集群

前提是你已经搭建好了redis集群——>搭建详情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testJedisCluster() {
Set cluster = new HashSet();
cluster.add(new HostAndPort("192.168.25.128", 7001));
cluster.add(new HostAndPort("192.168.25.128", 7002));
cluster.add(new HostAndPort("192.168.25.128", 7003));
cluster.add(new HostAndPort("192.168.25.128", 7004));
cluster.add(new HostAndPort("192.168.25.128", 7005));
cluster.add(new HostAndPort("192.168.25.128", 7006));

JedisCluster jedisCluster = new JedisCluster(cluster);
jedisCluster.set("blog", "www.zhenganwen.tech");
System.out.println(jedisCluster.get("blog"));
}

Jedis客户端接口的抽取

  • 在开发过程中,并不需要redis集群来缓解高并发,因此常常使用一个redis服务器做测试。那由上文可知,操作redis单机和redis集群的代码是不一样的,前者通过创建的Jedis对象,后者通过JedisCluster对象,那产品上线时岂不是要改代码?

    面向接口编程

  • 上述问题中,虽然操作redis数据库的对象不一样,但他们有共同的API,这时我们应该抽取出一个jedis客户端接口,只要求它提供操作redis数据库的方法而并不关心其具体实现,以达到解耦的效果。

    具体实现

  • 接口的抽取
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public interface JedisClient {

    String set(String key, String value);
    String get(String key);
    Boolean exists(String key);
    Long expire(String key, int seconds);
    Long ttl(String key);
    Long incr(String key);
    Long hset(String key, String field, String value);
    String hget(String key, String field);
    Long hdel(String key, String... field);
    }
  • 单机版
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    public class JedisClientPool implements JedisClient {

    public JedisPool getJedisPool() {
    return jedisPool;
    }

    public void setJedisPool(JedisPool jedisPool) {
    this.jedisPool = jedisPool;
    }

    private JedisPool jedisPool;

    @Override
    public String set(String key, String value) {
    Jedis jedis = jedisPool.getResource();
    String result = jedis.set(key, value);
    jedis.close();
    return result;
    }

    @Override
    public String get(String key) {
    Jedis jedis = jedisPool.getResource();
    String result = jedis.get(key);
    jedis.close();
    return result;
    }

    @Override
    public Boolean exists(String key) {
    Jedis jedis = jedisPool.getResource();
    Boolean result = jedis.exists(key);
    jedis.close();
    return result;
    }

    @Override
    public Long expire(String key, int seconds) {
    Jedis jedis = jedisPool.getResource();
    Long result = jedis.expire(key, seconds);
    jedis.close();
    return result;
    }

    @Override
    public Long ttl(String key) {
    Jedis jedis = jedisPool.getResource();
    Long result = jedis.ttl(key);
    jedis.close();
    return result;
    }

    @Override
    public Long incr(String key) {
    Jedis jedis = jedisPool.getResource();
    Long result = jedis.incr(key);
    jedis.close();
    return result;
    }

    @Override
    public Long hset(String key, String field, String value) {
    Jedis jedis = jedisPool.getResource();
    Long result = jedis.hset(key, field, value);
    jedis.close();
    return result;
    }

    @Override
    public String hget(String key, String field) {
    Jedis jedis = jedisPool.getResource();
    String result = jedis.hget(key, field);
    jedis.close();
    return result;
    }

    @Override
    public Long hdel(String key, String... field) {
    Jedis jedis = jedisPool.getResource();
    Long result = jedis.hdel(key, field);
    jedis.close();
    return result;
    }

    }
  • 集群版
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    public class JedisClientCluster implements JedisClient {

    private JedisCluster jedisCluster;

    @Override
    public String set(String key, String value) {
    return jedisCluster.set(key, value);
    }

    @Override
    public String get(String key) {
    return jedisCluster.get(key);
    }

    @Override
    public Boolean exists(String key) {
    return jedisCluster.exists(key);
    }

    @Override
    public Long expire(String key, int seconds) {
    return jedisCluster.expire(key, seconds);
    }

    @Override
    public Long ttl(String key) {
    return jedisCluster.ttl(key);
    }

    @Override
    public Long incr(String key) {
    return jedisCluster.incr(key);
    }

    @Override
    public Long hset(String key, String field, String value) {
    return jedisCluster.hset(key, field, value);
    }

    @Override
    public String hget(String key, String field) {
    return jedisCluster.hget(key, field);
    }

    @Override
    public Long hdel(String key, String... field) {
    return jedisCluster.hdel(key, field);
    }

    public JedisCluster getJedisCluster() {
    return jedisCluster;
    }

    public void setJedisCluster(JedisCluster jedisCluster) {
    this.jedisCluster = jedisCluster;
    }
    }

    按需注入

  • 引用接口,利用@Autowired灵活注入实现类
    1
    2
    @Autowired
    private JedisClient jedisClient;
  • 开发时注入单机版客户端
    1
    2
    3
    4
    5
    6
    7
    8
    <!--单机版redis-->
    <bean class="tech.zhenganwen.elifang.common.jedis.JedisClientPool" id="jedisClientPool">
    <property name="jedisPool" ref="jedisPool"/>
    </bean>
    <bean class="redis.clients.jedis.JedisPool" id="jedisPool">
    <constructor-arg name="host" value="192.168.25.128"/>
    <constructor-arg name="port" value="6379"/>
    </bean>
  • 上线时切换成集群版客户端
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <!--redis集群-->
    <bean class="tech.zhenganwen.elifang.common.jedis.JedisClientCluster" id="clientCluster">
    <property name="jedisCluster" ref="jedisCluster"/>
    </bean>
    <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
    <constructor-arg name="nodes">
    <set>
    <bean class="redis.clients.jedis.HostAndPort">
    <constructor-arg name="host" value="192.168.25.128"/>
    <constructor-arg name="port" value="7001"/>
    </bean>
    <bean class="redis.clients.jedis.HostAndPort">
    <constructor-arg name="host" value="192.168.25.128"/>
    <constructor-arg name="port" value="7002"/>
    </bean>
    <bean class="redis.clients.jedis.HostAndPort">
    <constructor-arg name="host" value="192.168.25.128"/>
    <constructor-arg name="port" value="7003"/>
    </bean>
    <bean class="redis.clients.jedis.HostAndPort">
    <constructor-arg name="host" value="192.168.25.128"/>
    <constructor-arg name="port" value="7004"/>
    </bean>
    <bean class="redis.clients.jedis.HostAndPort">
    <constructor-arg name="host" value="192.168.25.128"/>
    <constructor-arg name="port" value="7005"/>
    </bean>
    <bean class="redis.clients.jedis.HostAndPort">
    <constructor-arg name="host" value="192.168.25.128"/>
    <constructor-arg name="port" value="7006"/>
    </bean>
    </set>
    </constructor-arg>
    </bean>

在service中增加redis缓存

  • 原来每次调用服务都从数据库查询:
    1
    2
    3
    4
    5
    6
    7
    8
    public Collection<TbContent> getContentListByCatId(long categoryId) {

    TbContentExample example = new TbContentExample();
    TbContentExample.Criteria criteria = example.createCriteria();
    criteria.andCategoryIdEqualTo(categoryId);
    List<TbContent> tbContents = tbContentMapper.selectByExampleWithBLOBs(example);
    return tbContents;
    }
  • 现在为了减少数据库的压力和提高查询效率需要借助于redis缓存:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    //根据分类的id查询内容
    public Collection<TbContent> getContentListByCatId(long categoryId) {
    /**
    *查询数据库前看缓存中是否已存在,不应该让操作缓存时抛出的异常影响查询流程因此需要try-catch
    */
    try {
    String contentsJson = jedisClient.hget(CONTENT_LIST, categoryId + "");
    if (StringUtils.isNotBlank(contentsJson)) {
    return JsonUtils.jsonToList(contentsJson,TbContent.class);
    }
    } catch (Exception e) {
    e.printStackTrace();
    }

    TbContentExample example = new TbContentExample();
    TbContentExample.Criteria criteria = example.createCriteria();
    criteria.andCategoryIdEqualTo(categoryId);
    List<TbContent> tbContents = tbContentMapper.selectByExampleWithBLOBs(example);

    /**
    将数据库中查出的放入缓存
    */
    try {
    //使用哈希数据类型,key为所有的内容,field为内容分类的id
    jedisClient.hset("CONTENT_LIST", categoryId + "", JsonUtils.objectToJson(tbContents));
    } catch (Exception e) {
    e.printStackTrace();
    }

    return tbContents;
    }

    缓存同步

  • 上述方法存在缺陷:如果数据库中content表中的数据发生改变,该方法无从知晓,仍旧从缓存中查询数据。因此我们需要在对content表进行增删改操作时将缓存中对应的hash中的field删除以达到缓存同步:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Override
    //增加内容的方法
    public E3Result addContent(TbContent tbContent) {
    try {
    tbContent.setCreated(new Date());
    tbContent.setUpdated(new Date());
    tbContentMapper.insert(tbContent);
    /**
    * 缓存同步,因为这里是按内容分类来增加内容,因此没必要删除所有的内容缓存,只需将某个分类下的内容删除即可
    */
    jedisClient.hdel(CONTENT_LIST, tbContent.getCategoryId().toString());
    return E3Result.ok();
    } catch (Exception e) {
    e.printStackTrace();
    return E3Result.build(500, "添加失败!");
    }
    }
鼓励一下~