京淘Day14

BolunWu

1.Redis分片机制

1.1为什么需要分片机制

如果需要储存海量的内存数据,如果只使用一台redis,无法保证redis的工作效率.大量时间都浪费在了寻址当中.所以需要一种机制满足该要求.

采用分片机制实现

image

1.2Redis分片搭建

1.2.1搭建注意事项

redis服务的启动都依赖于redis.conf的配置文件.如果需要准备3台redis.则需要准备3个redis.conf的配置.
准备端口号
1. 6379 2. 6380 3. 6381

1.2.2分片的实现

image

修改端口号:将各自的端口号进行修改

image

启动3台redis服务器

image

校验服务是否正常运行

image

1.2.3关于分片的注意事项

1.问题描述:
当启动多台redis服务器以后,多台redis暂时没有必然的联系,各自都是独立的实体.可以数据的储存.如图所示
2.如果将分片通过程序的方式进行操作,要把3台redis当做一个整体,所以与上述的操作完全不同.不会出现一个key同时保存到多个redis的现象.

image

1.3分片入门案例

 /**
     * 测试Redis分片机制
     * 思考: shards 如何确定应该存储到哪台redis中呢???
     */
    @Test
    public void testShards(){
        List<JedisShardInfo> shards = new ArrayList<>();
        shards.add(new JedisShardInfo("192.168.126.129",6379));
        shards.add(new JedisShardInfo("192.168.126.129",6380));
        shards.add(new JedisShardInfo("192.168.126.129",6381));
        //准备分片对象
        ShardedJedis shardedJedis = new ShardedJedis(shards);
        shardedJedis.set("shards","redis分片测试");
        System.out.println(shardedJedis.get("shards"));
    }

1.4一致性hash算法

1.4.0常识说明

常识1: 一般的hash是8位16进制数. 0-9 A-F (24)8 = 2^32
常识2:常识2: 如果对相同的数据进行hash运算 结果必然相同的.
常识3: 一个数据1M 与数据1G的hash运算的速度一致.

1.4.1一致性hash算法介绍

一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 [1] 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 [2] 。

image

1.4.2特性1:平衡性

概念:平衡性是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡的问题(大致平均).
问题描述:由于节点都是通过hash方式进行计算.所以可能出现 如图所示的现象导致负载严重不平衡.

image

解决方法:引入虚拟的节点

image

1.4.3特性2:单调性

特点:单调性是指在新增或者删减节点时,不影响系统正常运行

image

1.4.4特性3:分散性

谚语鸡蛋不要放到一个篮子里.

分散性是指数据应该分散的存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都储存数据.

1.5SpringBoot整合Redis分片

1.5.1编辑配置文件

# 配置redis单台服务器
redis.host=192.168.126.129
redis.port=6379

# 配置redis分片机制
redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381

1.5.2编辑配置类

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {

    @Value("${redis.nodes}")
    private String nodes;  //node,node,node.....

    //配置redis分片机制
    @Bean
    public ShardedJedis shardedJedis(){
        nodes = nodes.trim();   //去除两边多余的空格
        List<JedisShardInfo> shards = new ArrayList<>();
        String[] nodeArray = nodes.split(",");
        for (String strNode : nodeArray){   //strNode = host:port
            String host = strNode.split(":")[0];
            int port = Integer.parseInt(strNode.split(":")[1]);
            JedisShardInfo info = new JedisShardInfo(host, port);
            shards.add(info);
        }
        return new ShardedJedis(shards);
    }
   }

1.5.3修改AOP注入项

image

Redis哨兵机制

2.1关于Redis分片说明

优点:实现内存数据的扩容
缺点:如果redis分片中有一个节点出现问题,则整个redis分片机制用户访问必然有问题 直接影响用户使用.
解决方案:实现redis的高可用

2.2配置Redis主从结构

策略划分:1主2从 6379主 6380/6381从
1.将分片的目录复制 改名为sentinel

image

2.重启3台redis服务器

image

3.检查redis节点的主从状态

image

4.实现主从挂载

image

5.检查主机状态

image

2.3哨兵的工作原理

image

原理说明:
1.配置redis主从结构
2.哨兵服务启动时,会监控当前的主机,同时获取主机的详情信息(主从结构)
3.当哨兵利用心跳检测机制(PING-PONG)连续三次都没有收到主机的反馈信息则判定主机宕机.
4.当哨兵发现主机宕机以后,则开启选举机制,在当前的从机中挑选一台Redis当做主机.

2.4配置哨兵配置文件

1.复制配置文件
cp sentinel.conf sentinel/
2.修改保护模式

image

3.开启后台运行

image

4.设置哨兵的监控
其中的1表示投票生成的票数 当前只有一个哨兵所以写1

image

5.修改宕机时间

image

6.选举失败的时间

image

7.启动哨兵服务

image

2.5Redis哨兵高可用性

测试步骤:
1.检查主机状态
2.将redis主服务器宕机 等待10秒 之后检查从机是否当选新的主机

image

3.重启6379服务器,检查是否成为新主机的从

image

2.6哨兵入门案例

/**
     * 测试Redis哨兵
     */
    @Test
    public void testSentinel(){
        Set<String> set = new HashSet<>();
        //1.传递哨兵的配置信息
        set.add("192.168.126.129:26379");
        JedisSentinelPool sentinelPool =
                new JedisSentinelPool("mymaster",set);
        Jedis jedis = sentinelPool.getResource();
        jedis.set("aa","哨兵测试");
        System.out.println(jedis.get("aa"));
    }

2.7SpringBoot整合Redis哨兵

2.7.1 编辑pro配置文件

# 配置redis单台服务器
redis.host=192.168.126.129
redis.port=6379

# 配置redis分片机制
redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381

# 配置哨兵节点
redis.sentinel=192.168.126.129:26379

2.7.2编辑redis配置类

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {

    @Value("${redis.sentinel}")
    private String sentinel;        //暂时只有单台

    @Bean
    public JedisSentinelPool jedisSentinelPool(){

        Set<String> sentinels = new HashSet<>();
        sentinels.add(sentinel);
        return new JedisSentinelPool("mymaster",sentinels);
    }
  }

2.7.3修改CacheAOP中的注入项

package com.jt.aop;

import com.jt.anno.CacheFind;
import com.jt.config.JedisConfig;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.ShardedJedis;

import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect     //我是一个AOP切面类
@Component  //将类交给spring容器管理
public class CacheAOP {

    @Autowired
    //private Jedis jedis;      //单台redis
    //private ShardedJedis jedis; //分片机制
    private JedisSentinelPool jedisSentinelPool;

    /**
     * 切面 = 切入点 + 通知方法
     *        注解相关 + 环绕通知  控制目标方法是否执行
     *
     *  难点:
     *      1.如何获取注解对象
     *      2.动态生成key  prekey + 用户参数数组
     *      3.如何获取方法的返回值类型
     */
    @Around("@annotation(cacheFind)")  //参数传递变量的传递
    //@Around("@annotation(com.jt.anno.CacheFind)")
    public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){
        //从池中获取jedis对象
        Jedis jedis = jedisSentinelPool.getResource();
        Object result = null;
        try {
            //1.拼接redis存储数据的key
            Object[] args = joinPoint.getArgs();
            String key = cacheFind.preKey() +"::" + Arrays.toString(args);

            //2. 查询redis 之后判断是否有数据
            if(jedis.exists(key)){
                //redis中有记录,无需执行目标方法
                String json = jedis.get(key);
                //动态获取方法的返回值类型   向上造型  向下造型
                MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
                Class returnType = methodSignature.getReturnType();
                result = ObjectMapperUtil.toObj(json,returnType);
                System.out.println("AOP查询redis缓存");
            }else{
                //表示数据不存在,需要查询数据库
                result = joinPoint.proceed();  //执行目标方法及通知
                //将查询的结果保存到redis中去
                String json = ObjectMapperUtil.toJSON(result);
                //判断数据是否需要超时时间
                if(cacheFind.seconds()>0){
                    jedis.setex(key,cacheFind.seconds(),json);
                }else {
                    jedis.set(key, json);
                }
                System.out.println("aop执行目标方法查询数据库");
            }

        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        jedis.close();  //将使用完成的链接记得关闭.
        return result;
    }

}

阅读 96
4 声望
3 粉丝
0 条评论
4 声望
3 粉丝
宣传栏