zoukankan      html  css  js  c++  java
  • 分布式锁实现(Redis和zookeeper)

    锁,就是在同一时刻,某个资源被某一个线程独占。单机系统中,由于是在同一个虚拟机中,为了使得线程能够独占资源,我们通常是对资源加锁,或者每一个线程维护一个资源的备份。在分布式环境中,由于对资源的操作是跨域的,因此需要组件来实现分分布式锁。

    一,使用redis实现分布式锁

    redis中的set  nx 命令,当key不存在时,才能在redis中将key添加成功,利用该属性可以实现分布式锁,并且redis对于key有失效时间,可以控制当某个客户端加锁成功之后挂掉,导致阻塞的问题。

    废话不多说,上代码:

    1,POM文件

    <?xml version="1.0"?>
    <project
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
        xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <modelVersion>4.0.0</modelVersion>
    
    
        <!-- 项目信息 begin -->
        <groupId>com.microservice</groupId>
        <artifactId>spring-web</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>spring-web</name>
        <url>http://maven.apache.org</url>
        <!-- 项目信息end -->
        <!-- 属性配置 begin -->
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>
        <!-- 属性配置end -->
        <!-- 父依赖 begin -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.4.1.RELEASE</version>
            <relativePath /> <!-- lookup parent from repository -->
        </parent>
        <!-- 父依赖 end -->
        <dependencies>
            <!-- 添加web包 begin -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <!-- 该包中包含requestMapping restController 等注解 -->
            <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>
            <!-- 添加web包 end -->
            <!-- mybatis依赖 begin -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.1.1</version>
            </dependency>
            <!-- mybatis依赖 end -->
            <!-- mysql数据库配置 begin -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!-- mysql数据库配置 end -->
            <!-- redis配置 begin -->
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>2.7.3</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-redis</artifactId>
                <version>1.7.2.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-redis</artifactId>
                <version>RELEASE</version>
            </dependency>
            <!-- redis配置 end -->
            <!-- mq begin -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-activemq</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.apache.activemq</groupId>
                <artifactId>activemq-pool</artifactId>
                <!-- <version>5.7.0</version> -->
            </dependency>
    
            <!-- mq end -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>3.8.1</version>
                <scope>test</scope>
            </dependency>
            <!-- 热部署 begin -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional>
            </dependency>
            <!-- 热部署 end -->
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <fork>true</fork><!-- 如果没有该项配置,devtools不会起作用,即应用不会restart -->
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    2,添加jedis客户端

    package org.spring.web.component;
    import java.util.Arrays;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    import redis.clients.jedis.Jedis;
    
    /**
     * redis配置类
     */
    @Configuration
    @EnableCaching
    @PropertySource(value = "classpath:application.properties", encoding = "UTF-8")
    public class RedisConfig extends CachingConfigurerSupport{
        
        @Value("${spring.redis.host}")
        private String redisHost;
        @Value("${spring.redis.port}")
        private int redisPort;
        
    
        @SuppressWarnings("rawtypes")
        @Bean
        public CacheManager cacheManager(RedisTemplate redisTemplate) {
            RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
            // 多个缓存的名称,目前只定义了一个
            rcm.setCacheNames(Arrays.asList("thisredis"));
            //设置缓存过期时间(秒)
            rcm.setDefaultExpiration(600);
            return rcm;
        }
    
        @Bean
        public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
            StringRedisTemplate template = new StringRedisTemplate(factory);
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            template.setValueSerializer(jackson2JsonRedisSerializer);
            template.afterPropertiesSet();
            return template;
        }
        
        @Bean
        //客户端连接信息是怎么获取的?不设置,或者设置错误的值也可以连接到redis
        public Jedis jedis(){      
            //return new Jedis(redisHost,redisPort);
            System.out.println("redisHost>>>>>>>"+redisHost);
            System.out.println("redisPort>>>>>>>"+redisPort);
            return new Jedis();
        }
    
    }

    3,重点,Redis加锁和解锁,此处代码来源   https://www.cnblogs.com/linjiqin/p/8003838.html

    package org.spring.web.component;
    
    import java.util.Collections;
    import redis.clients.jedis.Jedis;
    
    /**
     *
     * 项目名称:spring-web 类名称:RedisDistributeLocak 类描述: 创建人:john 创建时间:2018年8月2日
     * 上午11:50:10 修改人:john 修改时间:2018年8月2日 上午11:50:10 修改备注:
     * 
     * @version
     *
     */
    public class RedisDistributeLock {
        private static final String LOCK_SUCCESS = "OK";
        private static final String SET_IF_NOT_EXIST = "NX";
        private static final String SET_WITH_EXPIRE_TIME = "PX";
        private static final Long RELEASE_SUCCESS = 1L;
    
        /**
         * 尝试获取分布式锁
         * 
         * @param jedis
         *            Redis客户端
         * @param lockKey
         *            锁
         * @param requestId
         *            请求标识
         * @param expireTime
         *            超期时间
         * @return 是否获取成功
         */
        public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
            
            System.out.println("jedis>>>>>>>>"+jedis);
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
            
            System.out.println("redisLockResult>>>>>>>"+result);
    
            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
            return false;
    
        }
    
        /**
         * 释放分布式锁
         * 
         * @param jedis
         *            Redis客户端
         * @param lockKey
         *            锁
         * @param requestId
         *            请求标识
         * @return 是否释放成功
         */
        public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
    
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
    
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
            return false;
        }
    }

    4,redis加锁服务

    package org.spring.web.service.serviceImpl;
    
    import org.spring.web.component.RedisDistributeLock;
    import org.spring.web.service.RedisService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import redis.clients.jedis.Jedis;
    
    /**
    *
    * 项目名称:spring-web
    * 类名称:RedisServiceImpl
    * 类描述:
    * 创建人:john
    * 创建时间:2018年8月2日 下午12:34:29
    * 修改人:john
    * 修改时间:2018年8月2日 下午12:34:29
    * 修改备注:
    * @version
    *
    */
    @Service
    public class RedisServiceImpl{
        @Autowired
        private Jedis jedis;
        public boolean tryGetDistributedLock(String lockKey,String requestId,int expireTime ){
            return RedisDistributeLock.tryGetDistributedLock(jedis, lockKey, requestId, expireTime);
        }
        
        public boolean releaseDistributedLock(String lockKey,String requestId){
            return RedisDistributeLock.releaseDistributedLock(jedis, lockKey, requestId);
            
        }
    
    }

    5,测试验证

    package org.spring.web.controller;
    
    import org.spring.web.service.serviceImpl.RedisServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
    *
    * 项目名称:spring-web
    * 类名称:RedisController
    * 类描述:
    * 创建人:john
    * 创建时间:2018年8月2日 下午2:16:22
    * 修改人:john
    * 修改时间:2018年8月2日 下午2:16:22
    * 修改备注:
    * @version
    *
    */
    @RestController
    @RequestMapping("/redis")
    public class RedisController {
      
        @Autowired
        private RedisServiceImpl redisService;
        @RequestMapping("/getLock")
        public boolean getRedisLock(){
            System.out.println(">>>>>>>>>>>>>>>>>>");
            return redisService.tryGetDistributedLock("redisLock", "11200", 2000000000);
        }
        
        @GetMapping("/releaseLock")
        public boolean releaseRedisLock(){
            return redisService.releaseDistributedLock("redisLock", "11200");
        }
    }

    二,使用zookeeper实现分布式锁

    zookeeper中在node下,可以创建临时节点,当加锁的客户端挂掉是,临时节点就会自动删除,利用该特性,可以实现分布式锁。

    1,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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.example</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>demo</name>
        <description>Demo project for Spring Boot</description>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.4.2.RELEASE</version>
            <relativePath /> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
            <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
        </properties>
    
        <dependencies>
            <!-- 提供zookeeper整合的包 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-config</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            </dependency>
        
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!-- 热部署工具 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Camden.SR2</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    
    </project>

    2,单例的 CuratorFramework,该类是操作 zookeeper的客户端

    package zookeper.componnent;
    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    
    /**
    *
    * 项目名称:zookeper
    * 类名称:ClientSingleton
    * 类描述:
    * 创建人:john
    * 创建时间:2018年8月1日 下午9:06:00
    * 修改人:john
    * 修改时间:2018年8月1日 下午9:06:00
    * 修改备注:
    * @version
    *
    */
    
    //@Configuration
    public class ClientSingleton {
        private static CuratorFramework client = null;
        
        @Value("${zookeeper.connectString}")
        private String connectString;
        
        
    
        private ClientSingleton() {
            System.out.println("zookeeper.connectString>>>>>>>>>>>>>>>>"+connectString);
            RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
            client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181").retryPolicy(retryPolicy)
                    .sessionTimeoutMs(1000 * 6).connectionTimeoutMs(1000 * 6).build();
        }
    
        public static synchronized CuratorFramework newClient() {
            if (client == null) {
                new ClientSingleton();
            }
            return client;
        }
    
        public static void start() {
            client.start();
        }
    
        public static void close() {
            client.close();
        }
    }

    3,zookeeper锁服务层

    package zookeper.service;
    
    import java.util.concurrent.TimeUnit;
    
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.web.bind.annotation.RestController;
    
    import zookeper.componnent.ClientSingleton;
    
    /**
    *
    * 项目名称:zookeper
    * 类名称:DistributeZookLock
    * 类描述:
    * 创建人:john
    * 创建时间:2018年8月1日 下午5:07:56
    * 修改人:john
    * 修改时间:2018年8月1日 下午5:07:56
    * 修改备注:
    * @version
    *
    */
    
    @Service
    public class DistributeZookLock {
        
        
        private CuratorFramework curatorFramework=ClientSingleton.newClient();
        
        //@Autowired
        private InterProcessSemaphoreMutex InterProcessMutex=new InterProcessSemaphoreMutex(curatorFramework, "/config");
        public boolean tryLock(long time,TimeUnit unit){        
            try {
                return InterProcessMutex.acquire(time, unit);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }        
            return true;
            
        }
        
        public boolean unLock(){
              try {
                InterProcessMutex.release();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
              return true;
        }
        
    
          public static void main(String[] args){
               
              System.out.println("》》》》》"+new DistributeZookLock().curatorFramework);
          }
    }

    4,验证  controller层

    package zookeper.controller;
    
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    import org.springframework.core.env.Environment;
    import org.apache.curator.framework.CuratorFramework;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import zookeper.service.DistributeZookLock;
    
    /**
     *
     * 项目名称:zookeper 类名称:ZookController 类描述: 创建人:john 创建时间:2018年7月31日 下午3:51:56
     * 修改人:john 修改时间:2018年7月31日 下午3:51:56 修改备注:
     * 
     * @version
     *
     */
    @RestController
    @RequestMapping("/zook")
    public class ZookController {
        @Autowired
        private DiscoveryClient client;
        @Autowired
        private Environment environment;
        @Autowired
        private CuratorFramework curatorFramework;
        @Autowired
        private DistributeZookLock  zookLock;
    
        public String getZook() {
            return "";
        }
    
        @RequestMapping("/getServices")
        public String discoveryClent() {
            List<String> serviceList = client.getServices();
            List<ServiceInstance> list=client.getInstances("info");
             //获取实例化的服务
            StringBuffer sb = new StringBuffer();
            if (list != null && list.size() > 0 ) {
                sb.append(list.get(0).getUri()+",");
                System.out.println(">>>>>>>>>>>>>>>>"+list.get(0).isSecure());
            }
            System.out.println("sb>>>>>"+sb);
            System.out.println("注册服务的数量>>>>>>>>>>>>>>>>>" + serviceList.size());
            for (String service : serviceList) {
                System.out.println("注册的服务>>>>>>" + service);
            }
            return "info";
        }
    
        @GetMapping("/env")
        public String test() {
            String[] profiles = environment.getActiveProfiles();
            System.out.println("profiles>>>>>>>" + profiles.length);
            for (String item : profiles) {
                System.out.println("item>>>>>>>>>>>>>>>" + item);
            }
            
            String name = environment.getProperty("url");
            
            try {
                System.out.println(">>>>>>curatorFramework>>>>>>"+curatorFramework);
                List <String> listChildren=curatorFramework.getChildren().forPath("/config/zook");
                for(String child:listChildren ){
                    System.out.println("child>>>>>>>"+child);
                    System.out.println(child+"的值是>>>>>>"+environment.getProperty(child));
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            System.out.println(name);
    
            return "Hello," + name;
        }
        
        @RequestMapping("/lock")
        public boolean zookLock(){
            
            return zookLock.tryLock(10L, TimeUnit.MINUTES);
        }
        
        
    }
  • 相关阅读:
    [FAQ] Cannot use object of type MongoInt64 as array
    [Go] 选择 Beego 的三个理由
    [PHP] 有关PHP浮点数默认显示位数 precision 以及如何调整
    [FAQ] Beego2.0.2 bee 生成的 api 项目运行 404, http server Running on http://:8080
    [K8s] Kubernetes核心基础概念 Node, Pod, ReplicaSet, Deployment, Service, Ingress, ConfigMap
    [FE] uViewUI u-navbar 曲线解决 uni onNavigationBarButtonTap 的限制与失效
    Xmind 8 pro 破解版详细安装教程
    SQL 触发器 暂停 和 启动
    SQL 代理服务无法启动
    MongoDB 项目实例-用户信息增删改查
  • 原文地址:https://www.cnblogs.com/li-zhan/p/9408201.html
Copyright © 2011-2022 走看看