当前位置 博文首页 > wyn-365:会会大厂面试官四-----Redis-Springboot+redisson【实

    wyn-365:会会大厂面试官四-----Redis-Springboot+redisson【实

    作者:[db:作者] 时间:2021-09-07 10:14

    一、Redisson【面试复盘】

    1.1 Redis除了做缓存,你还见过Redis的什么用法?

    1.2 Redis做分布式锁有时候需要注意神魔问题?

    1.3 如果是Redis单点部署的,会带来神魔问题?

    1.4 集群模式下,比如主从模式。会又什么问题?

    1.5 简单介绍下Redlock吧,看你简历上有redissson?

    1.6 Redis分布式锁如何续期?看门狗知道吗?

    二、Redis分布式锁

    1.JVM层面的锁
    2.分布式微服务架构,拆分后各个微服务之间为了避免冲突和数据故障而加入的一种锁。
    3.显示方案:zookeeper mysql redis【推荐】–redlock----redisson lock/unlock

    三、超卖程序采坑案例【Springboot2+Redis5/6】

    使用场景:多个服务之间,同一时刻,同一个用户只能有一个请求,防止关键业务数据冲突和并发错误。

    3.1 建立Module

    • boot_redis01
    • boot_redis02
    • boot_redis_test

    3.2 修改pom

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.apache.commons</groupId>
    	<artifactId>commons-pools</artifactId>
    </dependency>
    <dependency>
    	<groupId>redis.clients</groupId>
    	<artifactId>jedis</artifactId>
    	<version>3.1.0</version>
    </dependency>
    

    3.3 修改yaml

    # 第一个模块的配置,第二个修改端口号就好2222
    server.port=1111
    spring.redis.database=0
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    spring.redis.password=
    spring.redis.lettuce.pool.max-active=8
    spring.redis.lettuce.pool.max-wait=-1
    spring.redis.lettuce.pool.max-idle=8
    spring.redis.lettuce.pool.min-idle=0
    

    3.4 RedisConfig配置类

    @Configuration
    public class RedisConfig{
    
    	@Bean
    	public RedisTemplate<String,Serializable> redisTemplate(lettuceConnectionFactory connectionFactory){
    		new RedisTemplate<String,Serializable> redisTemplate = new RedisTemplate<>();
    		redisTemplate.setConnectionFactory(connectionFactory);
    		redisTemplate.setKeySerializer(new StringRedisSerializer());
    		redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    		
    		return redisTemplate;
    	}
    }
    

    3.5 GoodController

    @RestController
    public class GoodController{
    	
    	@Autowired
    	private StringRedisTemplate stringRedisTemplate;
    
    	@Value("${server.port}")
    	private String serverPort;
    
    	@GetMapping("/buy_Goods")
    	public String buy_Goods(){
    		// 1.查看库存数量
    		String result = stringRedisTemplate.opsForValue().get("goods:001");
    		int goodsNumber = result == null ? 0 : Interger.parseInt(result);
    		
    		// 2.卖商品
    		if(goodsNumber > 0){
    			int realNumber = goodsNumber - 1;
    			// 3.成功买入,库存减少一件
    			stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
    			return "成功买入商品,库存还剩下:"+realNumber+"服务端口:"+serverPort;
    		}else{
    			System.out.println("商品卖完"+"服务端口:"+serverPort);
    		}
    		return "商品卖完!"+"服务端口:"+serverPort;
    	}
    
    }
    

    3.6 Redis数据

    # 放入001库存100个
    set goods:001 100
    

    3.7 测试

    在这里插入图片描述
    在这里插入图片描述

    四、找上面程序Bug

    4.1 高并发下,又什么问题?

    单机版没有枷锁100%故障的,没有原子性,多线程下没有枷锁是不可以的。

    4.1.1 单机版加synchronized锁

    关键字,拿不到商品不走,容易造成线程积压,卡在外面,时间比较久。

    @GetMapping("/buy_Goods")
    	public String buy_Goods(){
    	
    	// 加锁
    	synchronized(this){
    		// 1.查看库存数量
    		String result = stringRedisTemplate.opsForValue().get("goods:001");
    		int goodsNumber = result == null ? 0 : Interger.parseInt(result);
    		
    		// 2.卖商品
    		if(goodsNumber > 0){
    			int realNumber = goodsNumber - 1;
    			// 3.成功买入,库存减少一件
    			stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
    			return "成功买入商品,库存还剩下:"+realNumber+"服务端口:"+serverPort;
    		}else{
    			System.out.println("商品卖完"+"服务端口:"+serverPort);
    		}
    		return "商品卖完!"+"服务端口:"+serverPort;
    	}
    }
    

    4.1.1 单机版加ReentrantLock锁

    类 ,try lock,时间内抢的到就去抢,抢不到就走人。

    
    private final Lock lock = new ReentrantLock();
    
    @GetMapping("/buy_Goods")
    	public String buy_Goods(){
    	
    	// 加锁,抢锁3S小规模等待
    	try(lock.tryLock(3L,TimUnit.SECONDS)){
    		lock.lock();
    		...
    	}finally{
    		lock.unlock();
    	}else{
    	}
    }
    

    4.2 架构变为Nginx分布式微服务,单机锁会不会有问题?

    在这里插入图片描述
    单机版的解决不了的,需要加入分布式锁

    4.2.1 引入反向代理Nginx

    # 权重一半一半
    vi nginx.conf
    

    在这里插入图片描述
    测试访问nginx

    192.168.11.147/buy_goods
    

    似乎轮询策略没有发现神魔问题。

    4.2.2 Jmeter性能压测会不会有问题?

    压测:1S中100个线程进行并发访问
    在这里插入图片描述
    在这里插入图片描述
    结果:发现了严重的超卖现象,单机版的锁是控制不住问题的。
    在这里插入图片描述
    Redis性能极高,岁分布式锁的支持比较优秀。

    4.2.3 加入分布式锁

    public static final String REDIS_LOCK = "atguiguLock";
    
    @GetMapping("/buy_Goods")
    	public String buy_Goods(){
    		String value = UUID.randomUUID().toString()+Thread.currentThread().getName();	
    		
    		Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK,value);
    		
    		// 加锁不成功
    		if(!flag){
    			return "抢锁失败";
    		}
    		
    		String result = stringRedisTemplate.opsForValue().get("goods:001");
    		int goodsNumber = result == null ? 0 : Interger.parseInt(result);
    		
    		// 2.卖商品
    		if(goodsNumber > 0){
    			int realNumber = goodsNumber - 1;
    			// 3.成功买入,库存减少一件
    			stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
    
    			// 4.解锁
    			stringRedisTemplate.delete(REDIS_LOCK);
    			
    			return "成功买入商品,库存还剩下:"+realNumber+"服务端口:"+serverPort;
    		}else{
    			System.out.println("商品卖完"+"服务端口:"+serverPort);
    		}
    		return "商品卖完!"+"服务端口:"+serverPort;
    }
    

    4.3 程序异常,没有执行解锁命令怎么办?

    下一篇:没有了