Fork me on GitHub

6.宜立方商城——redis集群

1.课程计划

1
2
3
4
5
6
1、首页轮播图展示(已经完成)
2、Redis服务器搭建
3、向业务逻辑中添加缓存。
4、使用redis做缓存
5、缓存同步。
6、Solr服务器安装

2.Redis集群的搭建

2.1.redis-cluster架构图 (redis官方给出的集群方案)

image

redis-cluster投票:容错

image

架构细节:

  1. 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
  2. 节点的fail是通过集群中超过半数的节点检测失效时才生效.
  3. 客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
  4. redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value

Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点

image

5.2Redis集群的搭建

Redis集群中至少应该有三个节点。要保证集群的高可用,需要每个节点有一个备份机。
Redis集群至少需要6台服务器。

搭建伪分布式。可以使用一台虚拟机运行6个redis实例。需要修改redis的端口号7001-7006

5.3搭建步骤

需要6台redis服务器。搭建伪分布式。

需要6个redis实例。

需要运行在不同的端口7001-7006

第一步:创建redis-cluster目录,并修改配置文件,删除无用文件
image

image

image

image

image

image

第二步:复制实例(6个)
image

第三步:修改六个实例的端口号
image

第四步:写批处理文件

image

image

第五步:修改批处理文件的权限

image

image

image

第六步:运行批处理文件

image

第七步:拷贝redis集群所用的工具

image

image

image

image

image

image

第八步:安装ruby的运行环境

image

image

第九步:导入ruby所用的一个第三方类库

image

第十步:安装这个类库

image

第十一步:运行redis-trib.rb这个工具(用来构建redis集群)

image

image

image

image

恭喜你!!!搭建成功!!!!

3.Jedis

需要把jedis依赖的jar包添加到工程中。Maven工程中需要把jedis的坐标添加到依赖。

推荐添加到服务层。E3-content-Service工程中。

3.1连接单机版

第一步:创建一个Jedis对象。需要指定服务端的ip及端口。

第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。

第三步:打印结果。

第四步:关闭Jedis

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testJedis() throws Exception{
//创建一个连接jedis对象,参数:host、port
Jedis jedis = new Jedis("192.168.25.128",6379);
//直接使用jedis操作redis,所有jedis的命令都对应一个方法
jedis.set("test123","my first jedis test");
String test123 = jedis.get("test123");
System.out.println(test123);


//关闭连接
jedis.close();
}

3.2连接单机版使用连接池

第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。

第二步:从JedisPool中获得Jedis对象。

第三步:使用Jedis操作redis服务器。

第四步:操作完毕后关闭jedis对象,连接池回收资源。

第五步:关闭JedisPool对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testJedisPool(){
//创建一个连接池,两个参数host、port
JedisPool jedisPool = new JedisPool("192.168.25.128",6379);
//从连接池获得一个连接,就是一个jedis对象
Jedis jedis = jedisPool.getResource();
//使用jedis操作redis
String test123 = jedis.get("test123");
System.out.println(test123);

//关闭连接,每次使用完毕后关闭连接,连接池回收资源
jedis.close();
//关闭连接池
jedisPool.close();
}

3.3连接集群版

第一步:使用JedisCluster对象。需要一个Set参数。Redis节点的列表。

第二步:直接使用JedisCluster对象操作redis。在系统中单例存在。

第三步:打印结果

第四步:系统关闭前,关闭JedisCluster对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testJedisCluster() throws Exception{
//创建一个JedisCluster对象。有一个参数nodes是一个set类型。set中包含若干个HostAndPort对象
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.25.128",7001));
nodes.add(new HostAndPort("192.168.25.128",7002));
nodes.add(new HostAndPort("192.168.25.128",7003));
nodes.add(new HostAndPort("192.168.25.128",7004));
nodes.add(new HostAndPort("192.168.25.128",7005));
nodes.add(new HostAndPort("192.168.25.128",7006));
JedisCluster jedisCluster = new JedisCluster(nodes);

//直接使用JedisCluster对象操作redis
jedisCluster.set("test","123");
String test = jedisCluster.get("test");
System.out.println(test);
//关闭JedisCluster对象
jedisCluster.close();
}

4.向业务逻辑中添加缓存

4.1接口封装

常用的操作redis的方法提取出一个接口,分别对应单机版和集群版创建两个实现类。

4.1.1接口定义
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);
}
4.1.2单机版实现类
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 {

private JedisPool jedisPool;

public JedisPool getJedisPool() {
return jedisPool;
}

public void setJedisPool(JedisPool jedisPool) {
this.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;
}

}

image

image

image

4.1.3集群版实现类
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
public class JedisClientCluster implements JedisClient {

private JedisCluster jedisCluster;

public JedisCluster getJedisCluster() {
return jedisCluster;
}

public void setJedisCluster(JedisCluster jedisCluster) {
this.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);
}

}

image

image

image

image

image

4.1.4封装代码测试
1
2
3
4
5
6
7
8
9
10
@Test
public void testJedisClient() throws Exception {
//初始化Spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");
//从容器中获得JedisClient对象
JedisClient jedisClient = applicationContext.getBean(JedisClient.class);
jedisClient.set("first", "100");
String result = jedisClient.get("first");
System.out.println(result);
}

4.2添加缓存

4.2.1功能分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
查询内容列表时添加缓存。
1、查询数据库之前先查询缓存。
2、查询到结果,直接响应结果。
3、查询不到,缓存中没有需要查询数据库。
4、把查询结果添加到缓存中。
5、返回结果。

向redis中添加缓存:
Key:cid
Value:内容列表。需要把java对象转换成json。

使用hash对key进行归类。
HASH_KEY:HASH
|--KEY:VALUE
|--KEY:VALUE
|--KEY:VALUE
|--KEY:VALUE

注意:添加缓存不能影响正常业务逻辑。

4.2.1代码实现
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
@Override
public List<TbContent> getContentListByCid(long cid) {
//查询缓存
//如果缓存中有,就直接返回
try {
String json = jedisClient.hget(CONTENT_LIST, cid + "");
System.out.println("json:"+json);
if(StringUtils.isNotBlank(json)){
List<TbContent> tbContents = JsonUtils.jsonToList(json, TbContent.class);

return tbContents;
}
}catch (Exception e){
//写日志
e.printStackTrace();
}

//如果没有就查询数据库
TbContentExample tbContentExample = new TbContentExample();
TbContentExample.Criteria criteria = tbContentExample.createCriteria();
criteria.andCategoryIdEqualTo(cid);
List<TbContent> tbContents = tbContentMapper.selectByExampleWithBLOBs(tbContentExample);

//把结果添加到缓存
try {
jedisClient.hset(CONTENT_LIST,cid+"", JsonUtils.objectToJson(tbContents));
}catch (Exception e){
//写日志
e.printStackTrace();
}
return tbContents;
}

image

image

image

image

image

image

image

4.2.1缓存同步
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public E3Result addContent(TbContent tbContent) {
//补全内容,因为数据库中有update和creatDate
tbContent.setCreated(new Date());
tbContent.setUpdated(new Date());
//将内容插入内容表
tbContentMapper.insert(tbContent);

//缓存同步
jedisClient.hdel(CONTENT_LIST,tbContent.getCategoryId().toString());

return E3Result.ok();
}

image