redis cluster集群和SSM整合,用两种方式来实现整合
手动方式(也是客户端(JedisCluster )的方式来实现)
关于手动方式请参考Spring+Mybatis+redis整合(用依赖注入的方式手动修改缓存)
redis.properties
redis.pool.maxTotal=30
redis.pool.maxIdle=10
redis.pool.numTestsPerEvictionRun=1024
redis.pool.timeBetweenEvictionRunsMillis=30000
redis.pool.minEvictableIdleTimeMillis=1800000
redis.pool.softMinEvictableIdleTimeMillis=10000
redis.pool.maxWaitMillis=1500
redis.pool.testOnBorrow=true
redis.pool.testWhileIdle=true
redis.pool.blockWhenExhausted=false
redis.maxRedirects=3
redis.host=127.0.0.1
redis.port=6383
redis.host2=127.0.0.1
redis.port2=6384
redis.host3=127.0.0.1
redis.port3=6385
redis.host4=127.0.0.1
redis.port4=6380
redis.host5=127.0.0.1
redis.port5=6381
redis.host6=127.0.0.1
redis.port6=6382
spring-redis.xml
<!--扫描包-->
<context:component-scan base-package="cn.qlq.Redis"/>
<!--扫描配置文件-->
<context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/>
<!-- 连接池配置,下面值的注入有的是${},有的是直接写的值,因为这里有类型注入错误,redis.properties里的值注入是String类型,
但是在JedisPoolConfig有的不是String类型,有in,long等,会报错 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="${redis.pool.maxTotal}" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="${redis.pool.maxIdle}" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="${redis.pool.softMinEvictableIdleTimeMillis}" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="${redis.pool.testWhileIdle}" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="${redis.pool.blockWhenExhausted}" />
</bean>
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg>
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="${redis.host}"></constructor-arg>
<constructor-arg name="port" value="${redis.port}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="${redis.host2}"></constructor-arg>
<constructor-arg name="port" value="${redis.port2}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="${redis.host3}"></constructor-arg>
<constructor-arg name="port" value="${redis.port3}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="${redis.host4}"></constructor-arg>
<constructor-arg name="port" value="${redis.port4}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="${redis.host5}"></constructor-arg>
<constructor-arg name="port" value="${redis.port5}"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="${redis.host6}"></constructor-arg>
<constructor-arg name="port" value="${redis.port6}"></constructor-arg>
</bean>
</set>
</constructor-arg>
</bean>
redisDao接口
public interface RedisDao {
/**
* show 获得缓存中的value
* */
String get(String key) throws Exception;
/**
* show 往缓存中添加value
* */
void set(String key,String value) throws Exception;
/**
* show 获取hash
* */
String hget(String hkey,String key) throws Exception;
/**
* show 插入一个hash
* */
void hset(String hkey,String key,String value)throws Exception;
/**
* show 插入一个列表
* */
void lpush(String messageKey,String message)throws Exception;
/**
* 取出一个列表,循环取出,用rpop
* */
String rpop(String messageKey)throws Exception;
/**
* 把列表中的元素取出,返回list集合,里面是列表的key和弹出的消息
* */
List<String> brpop(String messageKey)throws Exception;
/**
* show 发布/订阅 的发布者
* */
void publish(String channel,String message)throws Exception;
/**
* show 用subscribe订阅的订阅者
* */
void subscribe(String channel)throws Exception;
}
RedisDaoImpl实现类
@Repository("redisDao")
public class RedisDaoImpl implements RedisDao{
@Autowired
private JedisCluster jedisCluster;
/**
* 处理订阅/发布的订阅者根据频道接收的信息
* */
private MyJedisPubSub myJedisPubSub = new MyJedisPubSub();
@Override
public String get(String key) throws Exception {
return jedisCluster.get(key);
}
@Override
public void set(String key,String value) throws Exception {
//获得6小时到9小时的随机时间
int time=(int)(6+Math.random()*(4))*60*60;
//添加键
jedisCluster.set(key, value);
//给键添加生命周期
jedisCluster.expire(key, time);
}
@Override
public String hget(String hkey, String key) throws Exception {
return jedisCluster.hget(hkey, key);
}
@Override
public void hset(String hkey, String key,String value) throws Exception {
int time=(int)(6+Math.random()*(4))*60*60;
jedisCluster.hset(hkey, key,value);
jedisCluster.expire(key, time);
}
@Override
public void lpush(String messageKey, String message) {
//这就不加生命周期了
jedisCluster.lpush(messageKey, message);
}
@Override
public String rpop(String messageKey) throws Exception {
return jedisCluster.rpop(messageKey);
}
@Override
public List<String> brpop(String messageKey) throws Exception {
List<String> list = new ArrayList<String>();
System.out.println("准备接受列表的全部数据");
list = jedisCluster.brpop(5, messageKey);
System.out.println("接收完成,返回");
return list;
}
@Override
public void publish(String channel, String message) throws Exception {
jedisCluster.publish(channel, message);
}
@Override
public void subscribe(String channel) throws Exception {
jedisCluster.subscribe(myJedisPubSub, channel);
System.out.println("取消订阅后返回主线程");
}
}
/**
* 继承JedisPubSub,重写接收消息的方法
*/
class MyJedisPubSub extends JedisPubSub {
/**
* 结束程序的消息
* */
private static final String EXIT_COMMAND = "exit";
/** JedisPubSub类是一个没有抽象方法的抽象类,里面方法都是一些空实现
* 所以可以选择需要的方法覆盖,这儿使用的是SUBSCRIBE指令,所以覆盖了onMessage
* 如果使用PSUBSCRIBE指令,则覆盖onPMessage方法
* 当然也可以选择BinaryJedisPubSub,同样是抽象类,但方法参数为byte[]
**/
@Override
public void onMessage(String channel, String message) {
System.out.println("-接收到消息:channel=" + channel + ",message=" + message);
//接收到exit消息后退出返回主线程
if (EXIT_COMMAND.equals(message)) {
unsubscribe(channel);
}
}
@Override
public void unsubscribe(String... channels) {
super.unsubscribe(channels);
}
}
上面下半部分是关于消息队列和订阅发布模式的(测试过了,一样可以执行)
关于发布/订阅模式可以参考redis实现消息队列(发布/订阅模式)
service接口
public interface UserService {
/**
* show 获取所有的User
* */
List<User> getUser()throws Exception;
/**
* show 插入新的User
* */
void insertUser(User user)throws Exception;
/**
* 通过id查询用户
*
* @param id
* @return
* @throws Exception
*/
User findUserById(int id) throws Exception;
/**
* show 列表生产者持续生产信息
* @param message 往缓存中添加的信息
* */
void putMessage(int message)throws Exception;
/**
* show 列表消费者准备消费,取出列表里的元素,不放回,用rpop
* */
String getMessage() throws Exception;
/**
* show 取出列表的元素(弹出一个),返回list集合(里面有key和value),用brpop
* */
List<String> getMessageList()throws Exception;
/**
* show 实现发布/订阅的发布者
* */
void publish(int i)throws Exception;
/**
* show 用subscribe订阅的订阅者
* */
void subscribe()throws Exception;
}
serviceImpl实现类
@Transactional(rollbackFor=Exception.class)
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper=null; //这是往数据库也就是MySQL中查询的,如果不
@Autowired
private RedisDao redisDao =null;
@Override
public List<User> getUser() throws Exception {
return userMapper.getUser();
}
@Override
public void insertUser(User user) throws Exception {
userMapper.insertUser(user);
String userJson = redisDao.get("user_"+user.getId());
if(StringUtils.isEmpty(userJson)) {
System.out.println("插入缓存");
//因为是缓存中存的是字符串,所以需要转成json字符串
redisDao.set("user_"+user.getId(), JSON.toJSONString(user));
}
}
@Override
public User findUserById(int id) throws Exception {
String userjson = redisDao.get("user_"+id);
User user =null;
//判断缓存中存在对应的id的
if(StringUtils.isEmpty(userjson)) {
//不存在去数据库中取出,再放进缓存
user = userMapper.findUserById(id);
if(user != null) {
redisDao.set("user_"+id, JSON.toJSONString(user));
}else {
//数据库中也不存在就把这个id也存进缓存中标识,防止雪崩或者穿透
}
}else {
//存在就直接把缓存里的json拿出来转换成对象
System.out.println("从缓存拿是数据,不去数据库");
user = JSON.parseObject(userjson,User.class);
}
return user;
}
@Override
public void putMessage(int message) throws Exception {
//把消息发布
String messageKey = "message:queue";
redisDao.lpush(messageKey, String.valueOf(message));
}
@Override
public String getMessage() throws Exception {
//把消息取出来
String messageKey = "message:queue";
return redisDao.rpop(messageKey);
}
@Override
public List<String> getMessageList() throws Exception {
String messageKey = "message:queue";
return redisDao.brpop(messageKey);
}
@Override
public void publish(int i) throws Exception {
//频道号
String channel = "channel:1";
redisDao.publish(channel, String.valueOf(i));
}
@Override
public void subscribe() throws Exception {
String channel = "channel:1";
redisDao.subscribe(channel);
}
}
测试类
//根据id,存进一个对象,String类型,用fastjson把对象转换成String类型的json
@Test
public void testInsertUser() throws Exception{
ctx = new ClassPathXmlApplicationContext("spring-service.xml");
userService = ctx.getBean(UserService.class);
User user = new User();
user.setId(11);
user.setName("第一个");
userService.insertUser(user);
}
//通过id获得指定的String的对象(用fastjson转换成对象了)
@Test
public void testGetUserById() throws Exception{
ctx = new ClassPathXmlApplicationContext("spring-service.xml");
userService = ctx.getBean(UserService.class);
System.out.println(userService.findUserById(11));
}
结果
存进集群里了
也从redis cluster中把数据取出来了,这种方式完成
注解的方式(redisTemplate )
注解方式实现SSM和单机redis请参考Spring+Mybatis+redis整合(用注解的方式)
这个请参考java项目中配置redis-cluster集群的两种方式
上面用两种方式的第二种方式用redisTemplate的我还以为是用注解的呢,其实不是,现在我先学到这,redis方面的虽然还有很多方面没有解决,但是总算大体弄完了,下面继续学习MongonDB了