Redis的优惠券秒杀问题(七)在集群模式下的问题

问题描述

伪集群模式搭建 

(1)IDEA启动镜像 

(2)修改nginx配置 

(3)验证nginx是否启动成功

BUG复现 

(1)获取Token 

(2)使用Postman 发请求

(3)锁“失效”发生

问题分析 

解决思路 


Redis的优惠券秒杀问题(七)在集群模式下的问题

问题描述

我们在上一篇文章中提到过超卖问题、一人一单问题

Redis的优惠券秒杀问题(六)超卖问题、一人一单问题_面向鸿蒙编程的博客-CSDN博客Redis的优惠券秒杀问题之超卖问题、一人一单问题!!!超卖问题是典型的多线程安全问题,针对这一问题的常见解决方案就是加锁!!!所以我们现在要研究是就是要加什么类型的锁?要怎么加锁?在哪里加锁?什么是一人一单问题?简单的来说就是模拟为了防止黄牛”屯“货而设计的,每一个用户ID,只能下一单!如下图,同一个用户下了很多单!!!这里使用”悲观锁"来实现!通过加锁可以解决在单机情况下的一人一单安全问题,但是在集群模式下就不行了!!!

伪集群模式搭建 

(1)IDEA启动镜像 

我们可以使用IDEA自带的镜像,启动多个实例,来模拟集群!

linux redis 集群模式启动 redis开启集群模式_nginx

-Dserver.port=8082

然后将这两个实例都启动起来。

(2)修改nginx配置 

再打开nginx的配置文件——nginx.conf  

完整配置如下: 

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/json;

    sendfile        on;
    
    keepalive_timeout  65;

    server {
        listen       8080;
        server_name  localhost;
        # 指定前端项目所在的位置
        location / {
            root   html/hmdp;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }


        location /api {  
            default_type  application/json;
            #internal;  
            keepalive_timeout   30s;  
            keepalive_requests  1000;  
            #支持keep-alive  
            proxy_http_version 1.1;  
            rewrite /api(/.*) $1 break;  
            proxy_pass_request_headers on;
            #more_clear_input_headers Accept-Encoding;  
            proxy_next_upstream error timeout;  
            # 打开集群模式
            # proxy_pass http://127.0.0.1:8081;
            proxy_pass http://backend;
        }
    }

    # 打开集群模式 backend
    upstream backend {
        server 127.0.0.1:8081 max_fails=5 fail_timeout=10s weight=1;
        server 127.0.0.1:8082 max_fails=5 fail_timeout=10s weight=1;
    }  
}

proxy_pass http://backend;

......
upstream backend {
        server 127.0.0.1:8081 max_fails=5 fail_timeout=10s weight=1;
        server 127.0.0.1:8082 max_fails=5 fail_timeout=10s weight=1;
}  
 

重启nginx.exe

nginx.exe -s reload

(3)验证nginx是否启动成功

linux redis 集群模式启动 redis开启集群模式_linux redis 集群模式启动_02

因为默认是采用轮询的模式,再刷新一下上面的网页,8081和8082都是看到日志信息

HmDianPingApplication1 

linux redis 集群模式启动 redis开启集群模式_加锁_03

HmDianPingApplication2

linux redis 集群模式启动 redis开启集群模式_nginx_04

到这里,说明我们的模拟集群已经配置成功了!接下来就是复现这个BUG 

BUG复现 

(1)获取Token 

先登入一下,从页面中拿到这个token(authorization

linux redis 集群模式启动 redis开启集群模式_linux redis 集群模式启动_05

我们在代码中打上一个断点,这段代码中是有加锁的!即如果是同一个用户ID是不能同时进来的! 

linux redis 集群模式启动 redis开启集群模式_加锁_06

(2)使用Postman 发请求

然后,将authorization配置到postman的请求头中,分别用两个postman实例去发送请求! 

linux redis 集群模式启动 redis开启集群模式_JVM_07

但是,在下面的两个实例中,显然同一个用户ID请求都进去了,就出现了锁“失效”的问题!

(3)锁“失效”发生

两个请求,分别进入到不同的IDEA实例中(8081、8082),但是这一块内容我们是上锁的!也就是说在集群模式下,JVM级别synchronized锁是起不了作用的!

HmDianPingApplication1 

linux redis 集群模式启动 redis开启集群模式_JVM_08

HmDianPingApplication2

linux redis 集群模式启动 redis开启集群模式_JVM_09

问题分析 

JVM级别synchronized锁失效

linux redis 集群模式启动 redis开启集群模式_JVM_10

换而言之,就是系统部署在不同的服务器中,那它们的JVM是不一样的,而 synchronized 只能锁当前JVM中的线程,是不能操作其它JVM的!

解决思路 

要解决这个问题,就必须要借助别的工具!——分布式锁就孕育而生! 

linux redis 集群模式启动 redis开启集群模式_linux redis 集群模式启动_11