Redis集群和缓存
1、安装redis
安装的前提条件:
需要安装gcc:yum install gcc-c++
1、下载redis的源码包。
2、把源码包上传到linux服务器
3、解压源码包:tar -zxvf redis-3.0.0.tar.gz
4、Make
5、Make install:[root@bogon redis-3.0.0]# make install PREFIX=/usr/local/redis
2、启动redis
1、前端启动模式
/usr/local/redis/bin/redis-server
默认是前端启动模式,端口是6379
2、后端启动
1)从redis的源码目录中复制redis.conf到redis的安装目录。
2)修改配置文件
image.png
3)[root@bogon bin]# ./redis-server redis.conf
3、Redis常用命令
127.0.0.1:6379> set a 10
OK
127.0.0.1:6379> get a
"10"
4、集群结构
集群中有三个节点的集群,每个节点有一主一备。需要6台虚拟机。
搭建一个伪分布式的集群,使用6个redis实例来模拟。
1、搭建集群需要的环境
搭建集群需要使用到官方提供的ruby脚本。需要安装ruby的环境。
安装ruby
yum install ruby
yum install rubygems
redis集群管理工具redis-trib.rb
[root@bogon ~]# cd redis-3.0.0
[root@bogon redis-3.0.0]# cd src
[root@bogon src]# ll *.rb
-rwxrwxr-x. 1 root root 48141 Apr 1 07:01 redis-trib.rb
脚本需要的ruby包:
image.png
需要上传到linux服务。
安装ruby的包:
[root@bogon ~]# gem install redis-3.0.0.gem
2、集群的搭建
第一步:创建6个redis实例,端口号从7001~7006
第二步:修改redis的配置文件
1、修改端口号
2、打开cluster-enable前面的注释。
image.png
第三步:把创建集群的ruby脚本redis-trib.rb复制到redis-cluster目录下。
第四步:启动6个redis实例
第五步:创建集群。
[root@bogon redis-cluster]# ./redis-trib.rb create --replicas 1 192.168.25.153:7001 192.168.25.153:7002 192.168.25.153:7003 192.168.25.153:7004 192.168.25.153:7005 192.168.25.153:7006
3、集群测试
[root@bogon redis-cluster]# redis01/redis-cli -h 192.168.25.153 -p 7002 -c
192.168.25.153:7002> set a 100
-> Redirected to slot [15495] located at 192.168.25.153:7003
OK
5、Jedis客户端
需要把jedis的jar包添加到工程中,如果是maven需要添加jar包的坐标。
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.2</version>
</dependency>
1、单机版
/**
* redis单机版测试
*/
@Test
public void testJedisSingle(){
Jedis jedis = new Jedis("10.10.1.146",6379);
jedis.set("name","abc");
String name = jedis.get("name");
System.out.println(name);
jedis.close();
}
/**
* redis连接池测试
*/
@Test
public void testJedisPool(){
JedisPool pool = new JedisPool("10.10.1.146",6379);
Jedis jedis = pool.getResource();
jedis.set("age","100");
String age = jedis.get("age");
System.out.println(age);
jedis.close();
pool.close();
}
2、集群版
/**
* redis集群测试(自带连接池)
*/
@Test
public void testJedisCluster(){
//添加节点
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("10.10.1.146",7001));
nodes.add(new HostAndPort("10.10.1.146",7002));
nodes.add(new HostAndPort("10.10.1.146",7003));
nodes.add(new HostAndPort("10.10.1.146",7004));
nodes.add(new HostAndPort("10.10.1.146",7005));
nodes.add(new HostAndPort("10.10.1.146",7006));
JedisCluster jedisCluster = new JedisCluster(nodes);
jedisCluster.set("key1","redis cluster");
String str = jedisCluster.get("key1");
System.out.println(str);
jedisCluster.close();
}
3、jedis整合spring(单机版)
<!--redis连接池配置-->
<bean name="redisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="true" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!--redis单机版配置-->
<bean id="redisClient" class="redis.clients.jedis.JedisPool">
<constructor-arg name="host" value="10.10.1.146"></constructor-arg>
<constructor-arg name="port" value="6379"></constructor-arg>
<constructor-arg name="poolConfig" ref="redisPoolConfig"></constructor-arg>
</bean>
/**
* redis整合spring单机版测试
*/
@Test
public void testSpringJedisSingle(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
JedisPool pool = (JedisPool) applicationContext.getBean("redisClient");
Jedis jedis = pool.getResource();
String name = jedis.get("name");
System.out.println(name);
jedis.close();
pool.close();
}
4、jedis整合spring(集群版)
<!--redis连接池配置-->
<bean name="redisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="true" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!--redis集群版配置-->
<bean name="redisClient" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="10.10.1.146"></constructor-arg>
<constructor-arg name="port" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="10.10.1.146"></constructor-arg>
<constructor-arg name="port" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="10.10.1.146"></constructor-arg>
<constructor-arg name="port" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="10.10.1.146"></constructor-arg>
<constructor-arg name="port" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="10.10.1.146"></constructor-arg>
<constructor-arg name="port" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="10.10.1.146"></constructor-arg>
<constructor-arg name="port" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
<constructor-arg name="poolConfig" ref="redisPoolConfig"></constructor-arg>
</bean>
/**
* redis整合spring集群版测试
*/
@Test
public void testSpringJedisCluster(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
JedisCluster jedisCluster = (JedisCluster) applicationContext.getBean("redisClient");
String str = jedisCluster.get("key1");
System.out.println(str);
jedisCluster.close();
}
6、把缓存添加到业务逻辑(把业务逻辑查询到的数据放入redis数据库)
注意:缓存的添加不能影响正常的业务逻辑。
public List<TbContent> getContentList(long contentCid) {
//从缓存中取内容
try {
String result = jedisClient.hget(INDEX_CONTENT_REDIS_KEY, contentCid + "");
if (!StringUtils.isBlank(result)) {
//把字符串转换成list
List<TbContent> resultList = JsonUtils.jsonToList(result, TbContent.class);
return resultList;
}
} catch (Exception e) {
e.printStackTrace();
}
//业务逻辑:根据内容分类id在mysql数据库中查询内容列表
TbContentExample example = new TbContentExample();
Criteria criteria = example.createCriteria();
criteria.andCategoryIdEqualTo(contentCid);
List<TbContent> list = contentMapper.selectByExample(example);
//向缓存中添加内容
try {
//把list转换成字符串
String cacheString = JsonUtils.objectToJson(list);
jedisClient.hset(INDEX_CONTENT_REDIS_KEY, contentCid + "", cacheString);
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
7、缓存同步
解决方案:在taotao-rest工程中发布一个服务。当后台管理系统修改内容后,调用此服务,同步缓存。
当后台管理系统,修改内容之后需要通知redis把修改的内容对应的分类id的key删除。
Service层:
public class RedisServiceImpl implements RedisService {
@Autowired
private JedisClient jedisClient;
@Value("${INDEX_CONTENT_REDIS_KEY}")
private String INDEX_CONTENT_REDIS_KEY;
public TaotaoResult syncContent(long contentCid) {
try {
jedisClient.hdel(INDEX_CONTENT_REDIS_KEY, contentCid + "");
} catch (Exception e) {
e.printStackTrace();
return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
}
return TaotaoResult.ok();
}
}
Controller层:
@Controller
@RequestMapping("/cache/sync")
public class RedisController {
@Autowired
private RedisService redisService;
@RequestMapping("/content/{contentCid}")
public TaotaoResult contentCacheSync(@PathVariable Long contentCid) {
TaotaoResult result = redisService.syncContent(contentCid);
return result;
}
}
同步缓存服务的调用(使用HttpClient调用http://localhost:8080//cache/sync/content/{contentCid}):