Redis的基本原理,alone,sentinel,cluster三种模式和SpringBoot整合redis

目录

1.reids的简单介绍

2.redis的使用场景

3. 单例模式安装redis 

4.springBoot整合redis

A.redis单机版

B.redis sentinel 版

C.redis cluster 版


1.reids的简单介绍

https://redis.io/  redis的官网,引用官方的简单介绍

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps,hyperloglogs, geospatial indexes with radius queries and streams. Redis has built-in replication, Lua scripting, LRU eviction, transactions and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster

简单的翻译下 :

redis是一个开放代码(BSD许可),内存数据结构存储,用作数据库、缓存和消息代理。支持字符串、哈希、列表、集合、有序集合,Redis具有内置的复制、Lua脚本、LRU收回、事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis群集的自动分区提供高可用性

redis 的命令 是 atomic operations ,也就是说具有原子性

reids可持久化,将数据持续化,两种方式,一种设置一段时间后将数据转储到硬盘,另一种方式追加每一条命令到日志

Redis also supports trivial-to-setup master-slave asynchronous replication, with very fast non-blocking first synchronization, auto-reconnection with partial resynchronization on net split

reids还支持简单的主从异步复制设置,注意一下,这里没有说redis支持分布式集群

transactions   事物

 pub/sub    发布、订阅

lua scripting  lua 脚本语言

keys with a limited time-to -live   键的有限存活时间

LRU eviction of keys    最近最少使用回收键的策略

Automatic failover  自动切换,即集群的部署方式,利用的redis sentinel 哨兵模式

2.redis的使用场景

  • 缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)
  • 分布式集群架构中的session分离。
  • 聊天室的在线好友列表。
  • 任务队列。(秒杀、抢购、12306等等)
  • 应用排行榜。
  • 网站访问统计。
  • 数据过期处理(可以精确到毫秒)

3. 单例模式安装redis 

本人的主机是debian,于是就直接安装redis,因为目的是为了巩固和学习,所以选择redis最新稳定版本5.0.3

另外说一句就是debian9默认的gnome桌面环境配置的软件还挺多的,用熟了还挺好的,开发完全足够了,起码没有windows系统那样时不时的弹出一个广告。

wget http://download.redis.io/releases/redis-5.0.3.tar.gz

解压后,直接make  make test 没有问题后sudo make install

启动 redis   redis-server 直接在terminal启动,出来下面的效果,退出当前命令,redis-server就关闭了

Redis的基本原理,alone,sentinel,cluster三种模式和SpringBoot整合redis_第1张图片

 

默认情况下  reids是不允许远程连接的

修改redis.conf 中的配置

注释掉   bind 127.0.0.1

并将   protected-mode 设为no 

后台运行  daemonize 设为  yes

启动 redis    执行命令redis-server redis.conf  

使用Redis Desktop Manager进行远程连接

也可以直接在linux上运行客户端vi

redis-cli   

然后ping 出现PONG即说明redis正常启动并工作了

4.springBoot整合redis

A.redis单机版

设置RedisConnectionFactory,spring 认为单机版无需制定poolConfig 指定连接地址等信息即可

@Bean
	public RedisConnectionFactory JedisConnectionFactory() {
	
		//reids单例模式,这个没有set方法注入到JedisConnectionFactory所以只能根据构造方法
		RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
		//地址  端口  密码  数据库选择  spring认为这才是最基本的
		redisStandaloneConfiguration.setHostName("127.0.0.1");
		redisStandaloneConfiguration.setPort(6379);
		//redisStandaloneConfiguration.setPassword(password);若有密码提供密码
		redisStandaloneConfiguration.setDatabase(1);
		JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
	
		return jedisConnectionFactory;
	}

初始化redisTemplate ,开启transaction,并封装Util类

/**
	 * 实例化 RedisTemplate 对象
	 *
	 * @return
	 */
	@Bean
	public RedisTemplate functionDomainRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate redisTemplate = new RedisTemplate<>();
		initDomainRedisTemplate(redisTemplate, redisConnectionFactory);
		return redisTemplate;
	}

	/**
	 * 设置数据存入 redis 的序列化方式,并开启事务
	 * 
	 * @param redisTemplate
	 * @param factory
	 */
	private void initDomainRedisTemplate(RedisTemplate redisTemplate, RedisConnectionFactory factory) {
		// 如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to
		// String!
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
		redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
		// 开启事务
		redisTemplate.setEnableTransactionSupport(true);
		redisTemplate.setConnectionFactory(factory);
	}
/**
	 * 注入封装RedisTemplate @Title: redisUtil @return RedisUtil @autor lpl @date
	 * 2017年12月21日 @throws
	 */
	@Bean(name = "redisUtil")
	public RedisUtil redisUtil(RedisTemplate redisTemplate) {
		RedisUtil redisUtil = new RedisUtil();
		redisUtil.setRedisTemplate(redisTemplate);
		return redisUtil;
	}

具体的RedisUtils类

package com.songhq.data.redis;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

public class RedisUtil {

private RedisTemplate redisTemplate;  

    public void setRedisTemplate(RedisTemplate redisTemplate) {  
        this.redisTemplate = redisTemplate;  
    }  
    //=============================common============================  
    /** 
     * 指定缓存失效时间 
     * @param key 键 
     * @param time 时间(秒) 
     * @return 
     */  
    public boolean expire(String key,long time){  
        try {  
            if(time>0){  
                redisTemplate.expire(key, time, TimeUnit.SECONDS);  
            }  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 根据key 获取过期时间 
     * @param key 键 不能为null 
     * @return 时间(秒) 返回0代表为永久有效 
     */  
    public long getExpire(String key){  
        return redisTemplate.getExpire(key,TimeUnit.SECONDS);  
    }  

    /** 
     * 判断key是否存在 
     * @param key 键 
     * @return true 存在 false不存在 
     */  
    public boolean hasKey(String key){  
        try {  
            return redisTemplate.hasKey(key);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 删除缓存 
     * @param key 可以传一个值 或多个 
     */  
    @SuppressWarnings("unchecked")  
    public void del(String ... key){  
        if(key!=null&&key.length>0){  
            if(key.length==1){  
                redisTemplate.delete(key[0]);  
            }else{  
                redisTemplate.delete(CollectionUtils.arrayToList(key));  
            }  
        }  
    }  

    //============================String=============================  
    /** 
     * 普通缓存获取 
     * @param key 键 
     * @return 值 
     */  
    public Object get(String key){  
        return key==null?null:redisTemplate.opsForValue().get(key);  
    }  

    /** 
     * 普通缓存放入 
     * @param key 键 
     * @param value 值 
     * @return true成功 false失败 
     */  
    public boolean set(String key,Object value) {  
         try {  
            redisTemplate.opsForValue().set(key, value);  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  

    }  

    /** 
     * 普通缓存放入并设置时间 
     * @param key 键 
     * @param value 值 
     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 
     * @return true成功 false 失败 
     */  
    public boolean set(String key,Object value,long time){  
        try {  
            if(time>0){  
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);  
            }else{  
                set(key, value);  
            }  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 递增 
     * @param key 键 
     * @param by 要增加几(大于0) 
     * @return 
     */  
    public long incr(String key, long delta){    
        if(delta<0){  
            throw new RuntimeException("递增因子必须大于0");  
        }  
        return redisTemplate.opsForValue().increment(key, delta);  
    }  

    /** 
     * 递减 
     * @param key 键 
     * @param by 要减少几(小于0) 
     * @return 
     */  
    public long decr(String key, long delta){    
        if(delta<0){  
            throw new RuntimeException("递减因子必须大于0");  
        }  
        return redisTemplate.opsForValue().increment(key, -delta);    
    }    

    //================================Map=================================  
    /** 
     * HashGet 
     * @param key 键 不能为null 
     * @param item 项 不能为null 
     * @return 值 
     */  
    public Object hget(String key,String item){  
        return redisTemplate.opsForHash().get(key, item);  
    }  

    /** 
     * 获取hashKey对应的所有键值 
     * @param key 键 
     * @return 对应的多个键值 
     */  
    public Map hmget(String key){  
        return redisTemplate.opsForHash().entries(key);  
    }  

    /** 
     * HashSet 
     * @param key 键 
     * @param map 对应多个键值 
     * @return true 成功 false 失败 
     */  
    public boolean hmset(String key, Map map){    
        try {  
            redisTemplate.opsForHash().putAll(key, map);  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * HashSet 并设置时间 
     * @param key 键 
     * @param map 对应多个键值 
     * @param time 时间(秒) 
     * @return true成功 false失败 
     */  
    public boolean hmset(String key, Map map, long time){    
        try {  
            redisTemplate.opsForHash().putAll(key, map);  
            if(time>0){  
                expire(key, time);  
            }  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 向一张hash表中放入数据,如果不存在将创建 
     * @param key 键 
     * @param item 项 
     * @param value 值 
     * @return true 成功 false失败 
     */  
    public boolean hset(String key,String item,Object value) {  
         try {  
            redisTemplate.opsForHash().put(key, item, value);  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 向一张hash表中放入数据,如果不存在将创建 
     * @param key 键 
     * @param item 项 
     * @param value 值 
     * @param time 时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间 
     * @return true 成功 false失败 
     */  
    public boolean hset(String key,String item,Object value,long time) {  
         try {  
            redisTemplate.opsForHash().put(key, item, value);  
            if(time>0){  
                expire(key, time);  
            }  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 删除hash表中的值 
     * @param key 键 不能为null 
     * @param item 项 可以使多个 不能为null 
     */  
    public void hdel(String key, Object... item){    
        redisTemplate.opsForHash().delete(key,item);  
    }   

    /** 
     * 判断hash表中是否有该项的值 
     * @param key 键 不能为null 
     * @param item 项 不能为null 
     * @return true 存在 false不存在 
     */  
    public boolean hHasKey(String key, String item){  
        return redisTemplate.opsForHash().hasKey(key, item);  
    }   

    /** 
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回 
     * @param key 键 
     * @param item 项 
     * @param by 要增加几(大于0) 
     * @return 
     */  
    public double hincr(String key, String item,double by){    
        return redisTemplate.opsForHash().increment(key, item, by);  
    }  

    /** 
     * hash递减 
     * @param key 键 
     * @param item 项 
     * @param by 要减少记(小于0) 
     * @return 
     */  
    public double hdecr(String key, String item,double by){    
        return redisTemplate.opsForHash().increment(key, item,-by);    
    }    

    //============================set=============================  
    /** 
     * 根据key获取Set中的所有值 
     * @param key 键 
     * @return 
     */  
    public Set sGet(String key){  
        try {  
            return redisTemplate.opsForSet().members(key);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  

    /** 
     * 根据value从一个set中查询,是否存在 
     * @param key 键 
     * @param value 值 
     * @return true 存在 false不存在 
     */  
    public boolean sHasKey(String key,Object value){  
        try {  
            return redisTemplate.opsForSet().isMember(key, value);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 将数据放入set缓存 
     * @param key 键 
     * @param values 值 可以是多个 
     * @return 成功个数 
     */  
    public long sSet(String key, Object...values) {  
        try {  
            return redisTemplate.opsForSet().add(key, values);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return 0;  
        }  
    }  

    /** 
     * 将set数据放入缓存 
     * @param key 键 
     * @param time 时间(秒) 
     * @param values 值 可以是多个 
     * @return 成功个数 
     */  
    public long sSetAndTime(String key,long time,Object...values) {  
        try {  
            Long count = redisTemplate.opsForSet().add(key, values);  
            if(time>0) expire(key, time);  
            return count;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return 0;  
        }  
    }  

    /** 
     * 获取set缓存的长度 
     * @param key 键 
     * @return 
     */  
    public long sGetSetSize(String key){  
        try {  
            return redisTemplate.opsForSet().size(key);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return 0;  
        }  
    }  

    /** 
     * 移除值为value的 
     * @param key 键 
     * @param values 值 可以是多个 
     * @return 移除的个数 
     */  
    public long setRemove(String key, Object ...values) {  
        try {  
            Long count = redisTemplate.opsForSet().remove(key, values);  
            return count;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return 0;  
        }  
    }  
    //===============================list=================================  

    /** 
     * 获取list缓存的内容 
     * @param key 键 
     * @param start 开始 
     * @param end 结束  0 到 -1代表所有值 
     * @return 
     */  
    public List lGet(String key,long start, long end){  
        try {  
            return redisTemplate.opsForList().range(key, start, end);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  

    /** 
     * 获取list缓存的长度 
     * @param key 键 
     * @return 
     */  
    public long lGetListSize(String key){  
        try {  
            return redisTemplate.opsForList().size(key);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return 0;  
        }  
    }  

    /** 
     * 通过索引 获取list中的值 
     * @param key 键 
     * @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 
     * @return 
     */  
    public Object lGetIndex(String key,long index){  
        try {  
            return redisTemplate.opsForList().index(key, index);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  

    /** 
     * 将list放入缓存 
     * @param key 键 
     * @param value 值 
     * @param time 时间(秒) 
     * @return 
     */  
    public boolean lSet(String key, Object value) {  
        try {  
            redisTemplate.opsForList().rightPush(key, value);  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 将list放入缓存 
     * @param key 键 
     * @param value 值 
     * @param time 时间(秒) 
     * @return 
     */  
    public boolean lSet(String key, Object value, long time) {  
        try {  
            redisTemplate.opsForList().rightPush(key, value);  
            if (time > 0) expire(key, time);  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 将list放入缓存 
     * @param key 键 
     * @param value 值 
     * @param time 时间(秒) 
     * @return 
     */  
    public boolean lSet(String key, List value) {  
        try {  
            redisTemplate.opsForList().rightPushAll(key, value);  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 将list放入缓存 
     * @param key 键 
     * @param value 值 
     * @param time 时间(秒) 
     * @return 
     */  
    public boolean lSet(String key, List value, long time) {  
        try {  
            redisTemplate.opsForList().rightPushAll(key, value);  
            if (time > 0) expire(key, time);  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }  

    /** 
     * 根据索引修改list中的某条数据 
     * @param key 键 
     * @param index 索引 
     * @param value 值 
     * @return 
     */  
    public boolean lUpdateIndex(String key, long index,Object value) {  
        try {  
            redisTemplate.opsForList().set(key, index, value);  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return false;  
        }  
    }   

    /** 
     * 移除N个值为value  
     * @param key 键 
     * @param count 移除多少个 
     * @param value 值 
     * @return 移除的个数 
     */  
    public long lRemove(String key,long count,Object value) {  
        try {  
            Long remove = redisTemplate.opsForList().remove(key, count, value);  
            return remove;  
        } catch (Exception e) {  
            e.printStackTrace();  
            return 0;  
        }  
    }  
}
 
  

至此,单机版的redis配置完成,接下来可以使用RedisUtil来访问redis.

package com.songhq.data.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.songhq.data.bean.User;
import com.songhq.data.redis.RedisUtil;


@RestController
@RequestMapping("/redis")
public class RedisController {

	@Autowired
	private RedisUtil redisUtil;
	
	
	@RequestMapping("/set/{key}/{value}")
	public String set(@PathVariable("key")String key ,@PathVariable("value")String value) {
		
		redisUtil.set(key, value);
		
		return "key is "+ key +"and value is "+value;
	}
	@RequestMapping("/get/{key}")
	public String get(@PathVariable("key")String key ) {
		
		Object object = redisUtil.get(key);
		
		return "key is "+ key +"and value is "+object.toString();
	}
	@RequestMapping("/setuser/{key}")
	public User setUser(@PathVariable("key")String key ) {
		
		User user = new User();
		user.setName(key);
		user.setSex("female");
		user.setAge("26");
		
		redisUtil.set(key, user);
		
		return user;
	}
	
	@RequestMapping("/getuser/{key}")
	public User getUser(@PathVariable("key")String key ) {
		
		User user =(User) redisUtil.get(key);
		
		return user;
	}
 //判断一个key是否存在
    public boolean exist(String key) {
    	return redisTemplate.hasKey(key);
    }
    
    //set一个值,仅当key不存在的
    public Boolean setnx(String key ,Object value) {
    	
    	return  redisTemplate.opsForValue().setIfAbsent(key, value);
    	
    }
}

一个redis的简单应用,应用场景并发下的数量控制,奖品抢购,v1和v2是简单实现抢购的两种方案,其中v1是并发不安全的,

v2是并发安全的

/**
	 * 
	 * redis应用在数量控制器的业务场景
	 * 第一个  购物节抢优惠名额有限
	 * 第二个  就是促销活动数量有限
	 * 第三个  就是抢红包,红包的个数有限
	 * 
	 * 计数器版本1
	 * incr 获取增加量 或者 decr 获取减少量 
	 * 
	 * get(key)  (判断) (问题1  两个同时get(key) 不存在,都设置初始值)
	 * 
	 * key不存在的话,就初始化
	 * 
	 * 存在的话,get(key)得到当前的数量
	 * 
	 * 
	 * 当前数量+ 要增加的数量  程序代码判断是否是超过数量(判断)(两个客户端 同时判断都没超过总素,都进行加数量的操作,会导致结果超出设定值)
	 * 
	 * 如果没有则增加  并持久化到mysql等数据库
	 * 如果超过则结束
	 * 
	 * 
	 * 
	 * 版本2
	 * 
	 * 获取将要增加的数量
	 * 
	 * get(key)不存在  设置 值  setnx
	 * 
	 * 再get(key)得到当前数量
	 * 
	 * incrby 当前数量+增加的数量  并会返回增加后的数量
	 * 
	 * (用incrby 返回的结果进行判断)如果没有超过则成功 
	 * 如果超过  则结束
	 * 
	 * 
	 * 
	 * 抽奖限额 
	 * @param key
	 * @return
	 */
	@RequestMapping("/counter/v1")
	public String counter( ) {
		//抽奖限额100
		int limitAmount = 100;
		//每个用户的中奖额为1
		int decrAmount = 1;
		//记录奖品数量的key
		String key = "v1";
		//如果不存v1这个键
		if(!redisUtil.exist(key)) {
			//进行初始化,只剩下5件商品了
			redisUtil.set(key, limitAmount-95);
		}
		//获取当前量
		Object object = redisUtil.get(key);
		int currentAmount = Integer.valueOf(object.toString());
		if(currentAmount -decrAmount < 0) {
			//奖品完了
			System.out.println("很抱歉,奖品被抢完了");
			return  "很抱歉,奖品被抢完了";
		}else {
			//中奖成功,奖奖品数量减1
			redisUtil.decr(key, decrAmount);
			System.out.println("恭喜你中奖了");
			return "恭喜你中奖了";
		}
	}
	@RequestMapping("/counter/v2")
	public String counter2( ) {
		//抽奖限额100
		int limitAmount = 100;
		//每个用户的中奖额为1
		int decrAmount = 1;
		//记录奖品数量的key
		String key = "v2";
		//如果不存v1这个键
		if(!redisUtil.exist(key)) {
			//进行初始化,只剩下5件商品了
			redisUtil.setnx(key, limitAmount-95);
		}
		//获取当前量
		Object object = redisUtil.get(key);
		int currentAmount = Integer.valueOf(object.toString());
		long result = redisUtil.decr(key, decrAmount);
		if(result < 0) {
			//奖品完了
			System.out.println("很抱歉,奖品被抢完了");
			return  "很抱歉,奖品被抢完了";
		}else {
			System.out.println("恭喜你中奖了");
			return "恭喜你中奖了";
		}
	}

测试  liunx平台 使用并发测试工具ab

ab -c 100 -n 200 http://localhost:9005/redis/counter/v1

ab -c 100 -n 200 http://localhost:9005/redis/counter/v2

-c是并发输 -n 是访问量

测试结果,v2实现了数量控制,只有5人中奖,v1存在并发问题,不止5人中奖

注意并发测试下比较耗内存,有可能默认的tomcat java参数不够使用,会导致tomcat无法正常响应 

 启动程序时加大数配置 -Xms1024M -Xmx1024M -XX:PermSize=256M -XX:MaxNewSize=256M -XX:MaxPermSize=256M

在debug试图下可以看到tomcat启动了100多个线程,这也验证了tomcat是一个请求对应一个线程的模式。

B.redis sentinel 版

1.介绍和搭建 redis sentinel

官方介绍

Redis Sentinel provides high availability for Redis. In practical terms this means that using Sentinel you can create a Redis deployment that resists without human intervention to certain kind of failures.

Redis Sentinel also provides other collateral tasks such as monitoring, notifications and acts as a configuration provider for clients.

简单的说redis sentinel 可以自动抵抗某种失败,也就是某个redis server宕机,而且附属的功能有监视 、通知和作为客户端的配置,原文讲的比较清楚

  • Monitoring. Sentinel constantly checks if your master and slave instances are working as expected.
  • Notification. Sentinel can notify the system administrator, another computer programs, via an API, that something is wrong with one of the monitored Redis instances.
  • Automatic failover. If a master is not working as expected, Sentinel can start a failover process where a slave is promoted to master, the other additional slaves are reconfigured to use the new master, and the applications using the Redis server informed about the new address to use when connecting.
  • Configuration provider. Sentinel acts as a source of authority for clients service discovery: clients connect to Sentinels in order to ask for the address of the current Redis master responsible for a given service. If a failover occurs, Sentinels will report the new address

2.首先搭建Replication

cp redis.conf  redis-master.conf

cp redis.conf  redis-slave.conf

mater的配置不用改,这里只需改redis-slave的配置

修改port 的端口为 6380

增加  replicaof  127.0.0.1 6379

注释掉master特有的配置 export XMODIFIERS=@im=fctix 设置只读

先后启动master 和slave 简单测试一下在master上面set值,在slave上可以获取该值,默认情况下slave只读

3.搭建sentinel 至少需要三台

port 5000
sentinel myid a5740c4b73fe8ca091e3612a222538dc19509fc5
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
daemonize yes
protected-mode no
# Generated by CONFIG REWRITE
dir "/usr/local/my/redis"
maxclients 4064

sentinel failover-timeout mymaster 60000
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel known-replica mymaster 127.0.0.1 6380
sentinel known-sentinel mymaster 127.0.0.1 5002 46a90470ce2c585845d9a75abd3a3211f029bdad
sentinel known-sentinel mymaster 127.0.0.1 5001 64488234fa6525fe9a2f04d8f610c738ae725db3
sentinel current-epoch 0

redis-sentinel  sentinel-5000.conf 启动这台,接下来改一下端口号,启动5001和5002这两台

netstat -tnulp|grep redis 查看redis相关进程的端口情况,所有端口启动成功
Redis的基本原理,alone,sentinel,cluster三种模式和SpringBoot整合redis_第2张图片

redis-cli -p 5000

连接到5000的sentinel,并查看master的信息

sentinel master mymaster 

获取master

SENTINEL get-master-addr-by-name mymaster

出现6379 ,测试高可用,睡眠6379,再次运行SENTINEL get-master-addr-by-name mymaster 出现6380,即master为6380了,

即重新选举了,一个简单的sentinel就搭建成功了

redis-cli -p 6379 DEBUG sleep 30

4.springBoot的设置

@Bean
	public JedisPoolConfig jedisPoolConfig() {
		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		// 最大空闲数
		jedisPoolConfig.setMaxIdle(maxIdle);
		// 连接池的最大数据库连接数
		jedisPoolConfig.setMaxTotal(maxTotal);
		// 最大建立连接等待时间
		jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
		// 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
		jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
		// 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
		jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
		// 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
		jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
		// 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
		jedisPoolConfig.setTestOnBorrow(testOnBorrow);
		// 在空闲时检查有效性, 默认false
		jedisPoolConfig.setTestWhileIdle(testWhileIdle);
		return jedisPoolConfig;
	}
/**
	 * redis sentinel版配置
	 * @param jedisPoolConfig
	 * @return
	 */
	@Bean
	public RedisConnectionFactory JedisConnectionFactory(JedisPoolConfig jedisPoolConfig ) {
	
		//使用RedisSentinelConfiguration进行配置
		RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
		redisSentinelConfiguration.setDatabase(0);
		redisSentinelConfiguration.setMaster("mymaster");
		ArrayList arrayList = new ArrayList<>();
		RedisNode redisNode1 = new RedisNode("127.0.0.1", 5000);
		RedisNode redisNode2 = new RedisNode("127.0.0.1", 5001);
		RedisNode redisNode3 = new RedisNode("127.0.0.1", 5002);
		arrayList.add(redisNode1);
		arrayList.add(redisNode2);
		arrayList.add(redisNode3);
		redisSentinelConfiguration.setSentinels(arrayList);
		JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisSentinelConfiguration,jedisPoolConfig);
	
		return jedisConnectionFactory;
	}

其他的不用变,测试访问可行,sentinel的搭建和配置就完成了。

C.redis cluster 版

1.redis cluster 的搭建,参见官方教程 https://redis.io/topics/cluster-tutorial,

开启集群的配置  如下 ,为增加集群的可靠性,为每个集群节点增加一个slave,这里搭6台; 端口分别是 7000

7001,7002,7003,7004,7005

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

分别启动上面的relids-server

redis 5版本后就继承集群的命令,不用像redis 4那样依赖rubby  执行gem install redis 然后  "\" 表示换行

./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

  redis 5版本后的直接执行

redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1

注意这里值得一提的是,根据提示,到这里基本上redis集权就搭建好了

Redis的基本原理,alone,sentinel,cluster三种模式和SpringBoot整合redis_第3张图片

但是如果远程连接的话,会出现问题,原因是这里使用的127.0.0.1,要想远程也能连接的话,需将127.0.0.1更改为本机的在互联网上的ip地址,注意这是个坑

我们测试一下搭建的集群

redis-cli -c -p 7000登入到7000的server上,执行get zhangsan 发现出现redirected  到7001 的server上,说明集群搭建成功

-> Redirected to slot [10454] located at 47.100.212.206:7001
(nil)

再使用命令查看redis cluster的状态

47.100.212.206:7001> cluster info 
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:2
cluster_stats_messages_ping_sent:734362
cluster_stats_messages_pong_sent:731393
cluster_stats_messages_meet_sent:2
cluster_stats_messages_sent:1465757
cluster_stats_messages_ping_received:731389
cluster_stats_messages_pong_received:734364
cluster_stats_messages_meet_received:4
cluster_stats_messages_received:1465757
47.100.212.206:7001> cluster nodes
4ee8576b9e307c704a512ffae789696b08dc14d7 47.100.212.206:7002@17002 master - 0 1553581104308 3 connected 10923-16383
7b039631155681cc6b614a32fc72cf1544de5952 47.100.212.206:7003@17003 slave e565d66cde9f312d59e95e4be38eab9e4265063f 0 1553581103574 4 connected
e565d66cde9f312d59e95e4be38eab9e4265063f 47.100.212.206:7000@17000 master - 0 1553581103000 1 connected 0-5460
45319ce6f0a9d07d4e6eda626a4b9f95bfb02873 47.100.212.206:7004@17004 slave 2f9dbf4b14c11584b75feb3330390499c5b4e32d 0 1553581103273 5 connected
2f9dbf4b14c11584b75feb3330390499c5b4e32d 172.19.146.101:7001@17001 myself,master - 0 1553581103000 2 connected 5461-10922
84b09336b2334a68f470c318959ce88ee5ea5944 47.100.212.206:7005@17005 slave 4ee8576b9e307c704a512ffae789696b08dc14d7 0 1553581104527 6 connected

2,springBoot整合reids集群

其它的配置都不变,只需更改RedisConnectionFactory  这个连接工厂的生成方式

/**
	 * redis集群版配置
	 * @param jedisPoolConfig  配置有问题,没有通过
	 * @return
	 */
	@Bean
	public RedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig ) {
		
		
		HashSet hashSet = new HashSet();
		hashSet.add("47.100.212.206:7000");
		hashSet.add("47.100.212.206:7001");
		hashSet.add("47.100.212.206:7002");
		hashSet.add("47.100.212.206:7003");
		hashSet.add("47.100.212.206:7004");
		hashSet.add("47.100.212.206:7005");
		RedisClusterConfiguration clusterConf = new RedisClusterConfiguration(hashSet);
		clusterConf.setMaxRedirects(3);
		JedisConnectionFactory factory = new JedisConnectionFactory(clusterConf,jedisPoolConfig);
        return factory;
	}

到这里,springBoot整合完毕。

 

 

 

 

你可能感兴趣的:(redis)