zoukankan      html  css  js  c++  java
  • 5台redis实现红锁(完整demo)

    示例环境:

    Spring Boot 

    JDK1.8.0_131  

    apache-maven-3.5.4  

    nginx-1.14.2   

    redis-3.2.1

    1.Controller :

    package com..redlock;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import javax.servlet.http.HttpServletRequest;
    
    @RestController
    @RequestMapping("/lock")
    public class RedLockController {
    
        @Autowired
        // 红锁
        @Qualifier("RedLockLockService")
        private RedLockService grabService;
    
        @RequestMapping("/do")
        public String grabMysql(){
    
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = servletRequestAttributes.getRequest();
            int port = request.getLocalPort();
            long i = Thread.currentThread().getId();
              String type = grabService.grabOrder(port+"-"+i);
            System.out.println(port+"-"+i+"返回页面:"+type);
            return type;
        }
    }

    2. Service  :

    package com..redlock;
    
    public interface RedLockService {
    
        public String grabOrder(String orderId);
    }

    3. ServiceImpl:

    package com..redlock;
    
    import com..dao.SaveInfoMapper;
    import com..entity.SaveInfo;
    import org.redisson.RedissonRedLock;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    import java.sql.Timestamp;
    import java.util.Date;
    
    
    @Service("RedLockLockService")
    public class RedLockLockServiceImpl implements RedLockService {
    
        // 红锁
        @Autowired
        @Qualifier("redissonRed1")
        private RedissonClient redissonRed1;
        @Autowired
        @Qualifier("redissonRed2")
        private RedissonClient redissonRed2;
        @Autowired
        @Qualifier("redissonRed3")
        private RedissonClient redissonRed3;
        @Autowired
        @Qualifier("redissonRed4")
        private RedissonClient redissonRed4;
        @Autowired
        @Qualifier("redissonRed5")
        private RedissonClient redissonRed5;
    
    
        @Autowired
        SaveInfoMapper saveInfoMapper;
    
        @Override
        public String grabOrder(String orderId ){
    //        System.out.println(orderId+" : 来加锁");
    
            //生成key
            String lockKey = ("lock_red").intern();
    
            //红锁 redis son
            RLock rLock1 = redissonRed1.getLock(lockKey);
            RLock rLock2 = redissonRed2.getLock(lockKey);
            RLock rLock3 = redissonRed3.getLock(lockKey);
            RLock rLock4 = redissonRed4.getLock(lockKey);
            RLock rLock5 = redissonRed5.getLock(lockKey);
            RedissonRedLock rLock = new RedissonRedLock(rLock1,rLock2,rLock3,rLock4,rLock5);
            String count ="";
            boolean b1= true;
            boolean b2= true;
            try {
                //包含锁续命 默认设置key 超时时间30秒,过10秒,再延时
                 b1 = rLock.tryLock();
                if (b1==true){
                    System.out.println(orderId+" : 加锁成功");
                    count = updataData();
                    System.out.println(orderId+" 数据库返回结果 :"+count);
                    if(count.equals("0")){
                        return "数据库总数已为0";
                    }
                }else {
    //                System.out.println(orderId+" : 加锁失败#####");
                    b2 = false;
                    try {
                        //未拿到锁,暂停一下再去加锁
                        Thread.sleep(100);
                        grabOrder(orderId);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }catch (Exception e){
                System.out.println("加锁过程异常 :"+e);
            }finally {
                if(b2==true){
                    System.out.println(orderId+" : 去解锁~~~~~");
                    rLock.unlock();
                }
            }
            return count;
        }
    
    
        //数据库写入操作
        public Boolean sendData(String orderId){
    
            Timestamp saveTime=new Timestamp(new Date().getTime());
            SaveInfo info = new SaveInfo();
            info.setId(orderId);
            info.setApplyno("004098020000002");
            info.setUserName("user");
            info.setUserNumber("002");
            info.setAddress("上海");
            info.setSaveTime(saveTime);
            info.setType(orderId);
            info.setCount("count");
            int mu = saveInfoMapper.insert(info);
            if(mu!=1){
               return false;
            }
            return true;
        }
    
    
        //数据库减一操作
        public String updataData() {
    
            //查出id为1112的数据,判断需要减1的字段当前值
            SaveInfo save = saveInfoMapper.selectInfoByid("1112");
            String type = save.getType();
            if(type.equals("0")){
                return "0";
            }
            int count = Integer.parseInt(type)-1;
            System.out.println("数据库剩余个数 :"+count);
            SaveInfo info = new SaveInfo();
            info.setId("1112");
            info.setType(count+"");
            int mu = saveInfoMapper.updateByPrimaryid(info);
            System.out.println("mu : "+mu);
            if(mu!=1){
                return "数据库扣除报错";
            }
            System.out.println("mu2 : "+mu);
            return "数据库总数减一";
        }
    }

    4.RedLockConfig:

    package com..redlock;
    
    import org.redisson.Redisson;
    import org.redisson.api.RedissonClient;
    import org.redisson.config.Config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Primary;
    import org.springframework.stereotype.Component;
    
    
    @Component
    public class RedLockConfig {
    
        //红锁:
        @Bean(name = "redissonRed1")
        @Primary
        public RedissonClient redissonRed1(){
            Config config = new Config();
            config.useSingleServer().setAddress("127.0.0.1:6379").setDatabase(0);
            return Redisson.create(config);
        }
        @Bean(name = "redissonRed2")
        public RedissonClient redissonRed2(){
            Config config = new Config();
            config.useSingleServer().setAddress("127.0.0.1:6380").setDatabase(0);
            return Redisson.create(config);
        }
        @Bean(name = "redissonRed3")
        public RedissonClient redissonRed3(){
            Config config = new Config();
            config.useSingleServer().setAddress("127.0.0.1:6381").setDatabase(0);
            return Redisson.create(config);
        }
        @Bean(name = "redissonRed4")
        public RedissonClient redissonRed4(){
            Config config = new Config();
            config.useSingleServer().setAddress("127.0.0.1:6382").setDatabase(0);
            return Redisson.create(config);
        }
        @Bean(name = "redissonRed5")
        public RedissonClient redissonRed5(){
            Config config = new Config();
            config.useSingleServer().setAddress("127.0.0.1:6383").setDatabase(0);
            return Redisson.create(config);
        }
    
    }

    5.dao :

    package com..dao;
    
    import com..entity.SaveInfo;
    import org.apache.ibatis.annotations.Mapper;
    import org.springframework.stereotype.Component;
    import java.util.List;
    
    @Mapper
    @Component(value = "SaveInfoMapper")
    public interface SaveInfoMapper {
    
        int insert(SaveInfo record);
    
        List<SaveInfo> selectAll();
    
        SaveInfo selectInfoByid(String id);
    
        int updateByPrimaryid(SaveInfo record);
    
    }

    6.entity :

    package com..entity;
    
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     *
     * This class was generated by MyBatis Generator.
     * This class corresponds to the database table save
     */
    public class SaveInfo{
    
        private String id;
    
        private String applyno;
    
        private String userName;
    
        private String userNumber;
    
        private String address;
    
        private Date saveTime;
    
        private String type;
    
        private String count;
    
        private byte[] saveBolb;
    
        private String saveClob;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id == null ? null : id.trim();
        }
    
        public String getApplyno() {
            return applyno;
        }
    
        public void setApplyno(String applyno) {
            this.applyno = applyno == null ? null : applyno.trim();
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName == null ? null : userName.trim();
        }
    
        public String getUserNumber() {
            return userNumber;
        }
    
        public void setUserNumber(String userNumber) {
            this.userNumber = userNumber == null ? null : userNumber.trim();
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address == null ? null : address.trim();
        }
    
        public Date getSaveTime() {
            return saveTime;
        }
    
        public void setSaveTime(Date saveTime) {
            this.saveTime = saveTime;
        }
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type == null ? null : type.trim();
        }
    
        public String getCount() {
            return count;
        }
    
        public void setCount(String count) {
            this.count = count == null ? null : count.trim();
        }
    
        public byte[] getSaveBolb() {
            return saveBolb;
        }
    
        public void setSaveBolb(byte[] saveBolb) {
            this.saveBolb = saveBolb;
        }
    
        public String getSaveClob() {
            return saveClob;
        }
    
        public void setSaveClob(String saveClob) {
            this.saveClob = saveClob == null ? null : saveClob.trim();
        }
    }

    7.Mapper.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com..dao.SaveInfoMapper">
    
      <resultMap id="BaseResultMap" type="com..entity.SaveInfo">
        <result column="ID" jdbcType="VARCHAR" property="id" />
        <result column="APPLYNO" jdbcType="VARCHAR" property="applyno" />
        <result column="USER_NAME" jdbcType="VARCHAR" property="userName" />
        <result column="USER_NUMBER" jdbcType="VARCHAR" property="userNumber" />
        <result column="ADDRESS" jdbcType="VARCHAR" property="address" />
        <result column="SAVE_TIME" jdbcType="TIMESTAMP" property="saveTime" />
        <result column="TYPE" jdbcType="VARCHAR" property="type" />
        <result column="COUNT" jdbcType="VARCHAR" property="count" />
        <result column="SAVE_BOLB" jdbcType="LONGVARBINARY" property="saveBolb" />
        <result column="SAVE_CLOB" jdbcType="LONGVARCHAR" property="saveClob" />
      </resultMap>
    
      <insert id="insert" parameterType="com..entity.SaveInfo">
        insert into save (ID, APPLYNO, USER_NAME, 
          USER_NUMBER, ADDRESS, SAVE_TIME, 
          TYPE, COUNT, SAVE_BOLB, 
          SAVE_CLOB)
        values (#{id,jdbcType=VARCHAR}, #{applyno,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}, 
          #{userNumber,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}, #{saveTime,jdbcType=TIMESTAMP}, 
          #{type,jdbcType=VARCHAR}, #{count,jdbcType=VARCHAR}, #{saveBolb,jdbcType=LONGVARBINARY}, 
          #{saveClob,jdbcType=LONGVARCHAR})
      </insert>
    
      <select id="selectAll" resultMap="BaseResultMap">
        select ID, APPLYNO, USER_NAME, USER_NUMBER, ADDRESS, SAVE_TIME, TYPE, COUNT, SAVE_BOLB, 
        SAVE_CLOB
        from save
      </select>
    
      <select id="selectInfoByid" parameterType="java.lang.String" resultMap="BaseResultMap">
        select ID, APPLYNO, USER_NAME, USER_NUMBER, ADDRESS, SAVE_TIME, TYPE, COUNT, SAVE_BOLB,
        SAVE_CLOB
        from save
        where id = #{id,jdbcType=VARCHAR}
      </select>
    
      <update id="updateByPrimaryid" parameterType="com..entity.SaveInfo">
            update save
            set type = #{type,jdbcType=VARCHAR}
            where id = #{id,jdbcType=INTEGER}
        </update>
    </mapper>

    application.properties 配置:

    #服务端口--模仿集群 (6011,7011,8011)
    server.port: 6011
    
    #redis配置
    spring.redis.database=0
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    spring.redis.timeout=2000
    spring.redis.password=
    
    #数据库配置
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    spring.datasource.url=jdbc:mysql://localhost:3306/ac-new?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    spring.datasource.username=root
    spring.datasource.password=root
    spring.druid.datasource.max-idle=10
    spring.druid.datasource.max-wait=10000
    spring.druid.datasource.minIdle=5
    spring.druid.datasource.initial-size=5
    
    #mybatis配置
    mybatis.mapper-locations:classpath:mapper/*.xml

    pom 配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.1</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.wond</groupId>
        <artifactId>redlock</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>redlock</name>
        <description>redLock</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson</artifactId>
                <version>3.3.2</version>
            </dependency>
    
            <!-- mysql:MyBatis相关依赖 -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.0.0</version>
            </dependency>
            <!-- 整合MyBatis java类依赖 -->
            <dependency>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.4.0</version>
                <type>maven-plugin</type>
            </dependency>
            <!-- mysql:mysql驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!-- mysql:阿里巴巴数据库连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.12</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

    Jmeter 测试:

    后台打印信息:

     

     

    最后,两个坑:

    1.如果第一个业务线程过来加锁,加了3台之后redis突然挂掉一台,怎么解决?

      方案:延时启动挂掉的redis.

    2.一个业务线程加锁成功,突然出现 Stop-The-World(GC导致),锁超时之后系统(集群)其他服务的业务线程来 加锁成功,第一个线程又恢复了 怎么办?

    方案:(目前还没好的解决方案) 建议换zookeeper.

  • 相关阅读:
    C语言学习笔记之 程序流程结构
    C语言学习笔记之 类型转换
    C语言学习笔记之 标准输入输出函数
    C语言学习笔记之 运算符
    bzoj 4322 东西分配问题
    bzoj 3240 矩阵乘法+十进制快速幂
    bzoj 4017 子序列和的异或以及异或的和
    bzoj 1934 最小割
    bzoj 3275 最小割
    bzoj 3931 最短路+最大流
  • 原文地址:https://www.cnblogs.com/lifan12589/p/14270945.html
Copyright © 2011-2022 走看看