基于redis实现分布式锁案例

 

这是我的案例流程图。浏览器----->zuul--->feignclient(两个)---->service(两个实例)。

由于多个服务实例。所以在并发情况下。传统的线程并发解决方案不能实现。原因:传统synychnozed锁或者lock都是基于同一个

jvm内部的。因为共享变量存在方法区(线程共享区)。所以多线程间是共享该变量实现线程上下文的安全切换。达到线程安全。

但是在分布式情况下。多实例。那么就是多jvm.多进程之间。进程是具有隔离性的。变量互相不见不可共享。那么这个时候redis

提供了解决方案。redis是单线程的。同一时刻只能有一个线程运行。那么可以利用这个特性。用到redis的几个命令:

conn.setnx(lockKey, identifier) == 1.这个nx的意思就是key存在的情况下不执行。key不存在在执行。
conn.expire(lockKey, lockExpire);设置key的超时时间。超过时间。自动释放掉锁
jedis.decr(key);redis自减

大概的原理是这样:  多进程并发执行。同一个时刻只有一个进程。首先去获取锁。如果key已经存在。说明当前有客户端正在使用该锁。那么就等待。设置超过多长时间获取不到锁就不获取了。如果key并存在则获取到锁。并且为锁设置超时时间。时间到自动释放该锁(避免产生死锁)。执行业务逻辑。执行完手动释放锁。等待其他进程拿到锁。那么我们要用redis来存储数据。保证每个进程都是从redis中拿到同一份数据。大概的原理就是这样。细节还有很多隐藏的bug.以后慢慢深入研究就补上。我的代码如下:

基本实现分布式锁功能。但是有个问题就是redis链接对象无法释放。找了半天找不到问题。明天继续找问题。

zuul:
  routes:
    microservice-consumer-movie-feign:
      path: /api/feign/**
      stripPrefix: false


#路由配置
package com.itmuch.cloud.microserviceconsumermoviefeign.FeginClient;


import com.itmuch.cloud.microserviceconsumermoviefeign.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * : 描述信息
 *
 * @author liyy
 * @date 2018-07-28 14:59
 */
@FeignClient(value = "microservice-provider-user",path = "/userController"/*,fallback = UserFeignFallBack.class*/)
public interface UserFeignClient{

    @GetMapping(value = "/{id}")
    public User findOne(@PathVariable("id") Long id);

    @GetMapping(value = "/moreInstance")
    public int moreInstance();

}


#feginclient客户端调用
package com.itmuch.cloud.microserviceconsumermoviefeign.controller;

import com.itmuch.cloud.microserviceconsumermoviefeign.FeginClient.UserFeignClient;
import com.itmuch.cloud.microserviceconsumermoviefeign.entity.User;
import com.netflix.hystrix.HystrixCircuitBreaker;
import com.netflix.hystrix.HystrixCommandKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextListener;

import java.util.concurrent.*;

/**
 * : 描述信息
 *
 * @author liyy
 * @date 2018-07-28 15:13
 */
@RestController
@RequestMapping(value="/api")
public class FeignController {
    public static Logger logger = LoggerFactory.getLogger(FeignController.class);

    @Autowired
    private UserFeignClient feignClient;

    public static ExecutorService threadPool = Executors.newFixedThreadPool(3);//固定线程数量的线程池

    private RequestContextListener requestContestListener;

    @GetMapping(value="/feign/{id}")
    public User findOne(@PathVariable Long id){
        System.out.println("id:"+id);
        Long startTime  = System.currentTimeMillis();
        User u = feignClient.findOne(id);
        Long endTime  = System.currentTimeMillis();
        System.out.println("调用业务返回时间:"+(endTime - startTime));
        HystrixCircuitBreaker breaker = HystrixCircuitBreaker.Factory.getInstance(HystrixCommandKey.Factory.asKey("UserFeignClient#findOne()"));
        logger.info("断路器的状态:{}",breaker);
        return u;

    }

    @GetMapping("/feign/consumer")
    public String dc() throws ExecutionException, InterruptedException {
        logger.info("result:");
        final int[] count = {0};
        while(true){
            Future<Integer> future = threadPool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    Thread.sleep(1000);
//                    String taskResult = String.valueOf(restTemplate.getForObject("http://eureka-client/moreInstance", String.class));
                    int taskResult = feignClient.moreInstance();
                    count[0]++;
                    System.out.println("消费票数:"+count[0]);
                    return taskResult;
                }
            });
            try {
                System.out.println("剩余票数:"+future.get());
                if(future!=null && future.get()==0){
                    System.out.println("所有任务执行完毕,共卖掉了"+ count[0] +"张票");
                    threadPool.shutdown();
                    break;
                }else if(future!=null &&future.get() == 9999){
                    System.out.println("userService异常。熔断");
                    /*threadPool.shutdown();
                    break;*/
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        return String.valueOf(count.length);
    }
}

#并发启用三个线程去执行任务
@GetMapping(value = "/moreInstance")
    @ResponseBody
    public int moreInstance() {
        int temp = 0;
        //先获取锁
        String identifier = distributedLock.lockWithTimeout("resource", 5000, 1000);
        if(identifier!=null){
            //当前线程拿到了锁,然后再执行业务逻辑,从redis中
            int value = Integer.parseInt(distributedLock.getValueKey(KEY));
            temp = value;
            System.out.println("获取redis锁,当前剩余票数:"+value+"张");
            if(value>0){
                //执行减减操作
                Long i = distributedLock.decr(KEY);
                System.out.println(Thread.currentThread().getName()+"消费一张票");
                //释放锁
                distributedLock.releaseLock("resource", identifier);
                System.out.println("释放redis锁,当前剩余票数:"+Integer.parseInt(distributedLock.getValueKey(KEY))+"张");
            }else{
                System.out.println("票买完了");
                temp = 0;
            }
        }
        return temp;

    }

#业务端代码
package com.itmuch.cloud.microserviceprovideruser.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;

import java.util.List;
import java.util.UUID;

/**
 * @author liyy
 * @description:分布式锁
 * @date 2019-04-02 16:27
 * @program spring-cloud
 */
@Component
@Slf4j
public class DistributedLock {


    @Autowired
    private JedisPool jedisPool;

    public static  Jedis conn = null;


    /**
     * 加锁
     * @param lockName       锁的key
     * @param acquireTimeout 获取超时时间 2s
     * @param timeout        锁的超时时间 3s
     * @return 锁标识
     */
    public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {
//        Jedis conn = null;
        String retIdentifier = null;
        try {
            // 获取连接
            conn = jedisPool.getResource();
            // 随机生成一个value
            String identifier = UUID.randomUUID().toString();
            System.out.println(Thread.currentThread().getName()+"获取到value的值identifier:"+identifier);
            // 锁名,即key值
            String lockKey = "lock:" + lockName;
            // 超时时间,上锁后超过此时间则自动释放锁
            int lockExpire = (int) (timeout / 1000);//秒。传入毫秒

            // 获取锁的超时时间,超过这个时间则放弃获取锁
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                if (conn.setnx(lockKey, identifier) == 1) {//加锁成功
                    System.out.println(Thread.currentThread().getName()+"加锁成功并设置锁的超时时间,key:"+"lock:" + lockName+",value:"+identifier);
                    conn.expire(lockKey, lockExpire);
                    // 返回value值,用于释放锁时间确认
                    retIdentifier = identifier;
                    return retIdentifier;
                }
                // 返回-1代表key没有设置超时时间,为key设置一个超时时间
                if (conn.ttl(lockKey) == -1) {
                    System.out.println("没有超时时间设置锁的超时时间");
                    conn.expire(lockKey, lockExpire);
                }
                try {
                    System.out.println(Thread.currentThread().getName()+"没有获取到锁");
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
//                jedisPool.returnBrokenResource(conn);
            }
        }
        return retIdentifier;
    }

    /**
     * 释放锁
     * @param lockName   锁的key
     * @param identifier 释放锁的标识
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        System.out.println("进入释放锁方法开始释放锁,lockName:"+lockName+",identifier:"+identifier);
//        Jedis conn = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            System.out.println("准备获取获取redis连接对象");
            conn = jedisPool.getResource();
            System.out.println("获取redis连接对象:"+conn);
            while (true) {
                // 监视lock,准备开始事务
                log.info("监视lock,准备开始事务,{}",lockKey);
                conn.watch(lockKey);
                // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁
                System.out.println(Thread.currentThread().getName()+"当前传入的value值:"+identifier);
                System.out.println(Thread.currentThread().getName()+"当前锁的value值:"+conn.get(lockKey));
                if (identifier.equals(conn.get(lockKey))) {
                    Transaction transaction = conn.multi();
                    transaction.del(lockKey);
                    System.out.println(Thread.currentThread().getName()+"释放锁KEY:"+lockKey+",value:"+identifier);
                    List<Object> results = transaction.exec();
                    if (results == null) {
                        System.out.println(Thread.currentThread().getName()+"当前锁无法释放,只能等锁到期后自动释放");
                        continue;
                    }
                    retFlag = true;
                }
                conn.unwatch();
                break;
            }
        } catch (JedisException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                System.out.println("每次用完后都要释放conn对象");
//                jedisPool.returnBrokenResource(conn);
                conn.close();
            }
        }
        return retFlag;
    }

    //redis自减
    public Long decr(String key) {
        Jedis jedis = null;
        Long result = 0l;
        try {
            jedis =  jedisPool.getResource();
            result = jedis.decr(key);
        } catch (Exception e) {
            log.error("redis decr error and key = " + key, e);
        }
        return result;
    }

    //redis自增
    public Long incr(String key) {
        Jedis jedis = null;
        Long result = 0l;
        try {
            jedis =  jedisPool.getResource();
            result = jedis.incr(key);
        } catch (Exception e) {
            log.error("redis incr error and key = " + key, e);
        }
        return result;
    }

    //设置key
    public String setkeyvalue(String key,String value){
        String result = jedisPool.getResource().set(key,value);
        return result;
    }

    //根据key获取value
    public String getValueKey(String key){
        String value  = jedisPool.getResource().get(key);
        return value;
    }
}


#jedis获取锁和释放锁代码

运行结果:

两个控制台分别打印数据。不会出现一张票被多个线程同时消费的情况。但是我这执行到一半就挂了。因为我再redis客户单观

redis链接对象越来越多。没有释放。最后挂了

--------------------------------------------------------------进一步的研究-----------------------------------------------------------------

上面的问题就是redis连接对象有两个地方(自增和自减两个方法)获取后,没有释放导致的。关闭后redis连接对象就很稳定在一个数量。

然后将redis分布式锁的实现方式换成RedissionLock(可重入锁(Reentrant Lock))。redis提供了多重锁的算法。可参考:

http://blog.csdn.net/l1028386804/article/details/73523810

修改后的代码如下:

package com.itmuch.cloud.microserviceprovideruser.config.redislock;

import com.itmuch.cloud.microserviceprovideruser.config.redisconfig.RedissonConnector;
import com.itmuch.cloud.microserviceprovideruser.exception.UnableToAquireLockException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.concurrent.TimeUnit;

/**
 * Created by fangzhipeng on 2017/4/5.
 */
@Component
@Slf4j
public class RedisLocker  implements IDistributedLocker{

    private final static String LOCKER_PREFIX = "lock:";

    private Jedis jedis = null;

    @Autowired
    private JedisPool jedisPool;

    /**
     * 注入redisson连接对象
     */
    @Autowired
    RedissonConnector redissonConnector;

    @Override
    public <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws InterruptedException, UnableToAquireLockException, Exception {

        return lock(resourceName, worker, 2000);
    }

    @Override
    public <T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws UnableToAquireLockException, Exception {
        //获取redssion连接对象
        RedissonClient redisson= redissonConnector.getClient();
        //创建redissionlock
        RLock lock = redisson.getLock(LOCKER_PREFIX + resourceName);
        // Wait for 100 seconds seconds and automatically unlock it after lockTime seconds
        boolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS);
        if (success) {
            try {
                return worker.invokeAfterLockAquire();
            } finally {
                lock.unlock();
                System.out.println("释放锁...");
            }
        }
        throw new UnableToAquireLockException();
    }

    //redis自减
    public Long decr(String key) {
        Long result = 0l;
        try {
            jedis =  jedisPool.getResource();
            result = jedis.decr(key);
        } catch (Exception e) {
            log.error("redis decr error and key = " + key, e);
        }
        return result;
    }

    //redis自增
    public Long incr(String key) {
        Long result = 0l;
        try {
            jedis =  jedisPool.getResource();
            result = jedis.incr(key);
        } catch (Exception e) {
            log.error("redis incr error and key = " + key, e);
        }
        return result;
    }

    //设置key
    public String setkeyvalue(String key,String value){
        String result = jedisPool.getResource().set(key,value);
        return result;
    }

    //根据key获取value
    public String getValueKey(String key){
        String value  = jedisPool.getResource().get(key);
        return value;
    }

}

package com.itmuch.cloud.microserviceprovideruser.config.redislock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;

import java.util.List;
import java.util.UUID;

/**
 * @author liyy
 * @description:分布式锁
 * @date 2019-04-02 16:27
 * @program spring-cloud
 */
@Component
@Slf4j
public class JedisPoolConfig {


    @Autowired
    private JedisPool jedisPool;

    private Jedis jedis = null;

    //redis自减
    public Long decr(String key) {
        Long result = 0l;
        try {
            jedis =  jedisPool.getResource();
            result = jedis.decr(key);
        } catch (Exception e) {
            log.error("redis decr error and key = " + key, e);
        }finally {
            if(jedis!=null){
                jedis.close();
                System.out.println("释放jedis");
            }
        }
        return result;
    }

    //redis自增
    public Long incr(String key) {
        Long result = 0l;
        try {
            jedis =  jedisPool.getResource();
            result = jedis.incr(key);
        } catch (Exception e) {
            log.error("redis incr error and key = " + key, e);
        }finally {
            if(jedis!=null){
                jedis.close();
                System.out.println("释放jedis");
            }
        }
        return result;
    }

    //设置key
    public String setkeyvalue(String key,String value){
        jedis = jedisPool.getResource();
        String result =null;
        try{
            result = jedis.set(key,value);
        }catch (Exception e){
            log.error("reids set value error and key="+key,e);
        }finally {
            jedis.close();
            System.out.println("释放jedis");
        }
        return result;
    }

    //根据key获取value
    public String getValueKey(String key){
        Jedis jedis = jedisPool.getResource();
        String value = null;
        try{
            value  = jedis.get(key);
        }catch (Exception e){
            log.error("redis get value error and key="+key,e);
        }finally {
            jedis.close();
            System.out.println("释放jedis");
        }
        return value;
    }

}
package com.itmuch.cloud.microserviceprovideruser.config.redislock;

import com.itmuch.cloud.microserviceprovideruser.exception.UnableToAquireLockException;

/**
 * @author liyy
 * @description:获取锁管理类
 * @date 2019-04-04 10:29
 * @program spring-cloud-microservice-study
 */
public interface IDistributedLocker {
    /**
     * 获取锁
     * @param resourceName  锁的名称
     * @param worker 获取锁后的处理类
     * @param <T>
     * @return 处理完具体的业务逻辑要返回的数据
     * @throws
     * @throws Exception
     */
    <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws UnableToAquireLockException, Exception;

    <T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws UnableToAquireLockException, Exception;

}
package com.itmuch.cloud.microserviceprovideruser.config.redislock;


/**
 * Created by fangzhipeng on 2017/4/5.
 * 获取锁后需要处理的逻辑
 */
public interface AquiredLockWorker<T> {
    T invokeAfterLockAquire() throws Exception;
}


package com.itmuch.cloud.microserviceprovideruser.config.redisconfig;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * Created by fangzhipeng on 2017/4/5.
 * 获取RedissonClient连接类
 */
@Component
public class RedissonConnector {
    RedissonClient redisson;

    @PostConstruct
    public void init(){
        redisson = Redisson.create();
    }

    public RedissonClient getClient(){
        return redisson;
    }

}


package com.itmuch.cloud.microserviceprovideruser.controller;


import com.itmuch.cloud.microserviceprovideruser.asyncservice.IAsyncService;
import com.itmuch.cloud.microserviceprovideruser.config.redislock.*;
import com.itmuch.cloud.microserviceprovideruser.dao.UserRepository;
import com.itmuch.cloud.microserviceprovideruser.entity.User;
import com.netflix.discovery.converters.Auto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * : 描述信息
 *
 * @author liyy
 * @date 2018-07-27 16:06
 */
@Controller
@RequestMapping("/userController")
public class UserController {


    @Autowired
    private UserRepository userRepository;

    @Autowired
    private DiscoveryClient discoveryClient;


    @Autowired
    private RedisLocker distributedLocker;

    @Autowired
    private JedisPoolConfig jedisPoolConfig;

    public static final String KEY = "TICKET_TOTAL";

    public static final String KEY_CONSUMER = "TICKET_CONSUMER";

//    public static AtomicInteger count = new AtomicInteger(0);


    @GetMapping(value = "/moreInstance")
    @ResponseBody
    public int moreInstance() throws InterruptedException {
        CountDownLatch begin = new CountDownLatch(1);
        CountDownLatch end = new CountDownLatch(1);
        new Thread(new UserController.Worker(begin,end)).start();
        begin.countDown(); // 唤醒所有子线程执行任务
        end.await();//阻塞当前主线程
        System.out.println("All processors done. Shutdown connection");
        System.out.println("countcoutncot:"+jedisPoolConfig.getValueKey(KEY_CONSUMER));
        return Integer.parseInt(jedisPoolConfig.getValueKey(KEY_CONSUMER));

    }
    class Worker implements Runnable {
        private final CountDownLatch begin;
        private final CountDownLatch end;
        private int temp= 0;

        Worker(CountDownLatch begin, CountDownLatch end) {
            this.begin = begin;
            this.end = end;
        }

        public void run() {
            try {
                begin.await();//阻塞线程等待唤醒
                distributedLocker.lock("test",new AquiredLockWorker<Object>() {
                    @Override
                    public Object invokeAfterLockAquire() {
                        doTask();
                        return null;
                    }
                });
            }catch (Exception e){

            }
        }

        private int doTask() {
            System.out.println(Thread.currentThread().getName() + " start");
            //执行具体业务逻辑开始
            int value = Integer.parseInt(jedisPoolConfig.getValueKey(KEY));
            temp = value;
            System.out.println("当前线程拿到剩余票数:"+value+"张");
            if(value>0){
                //执行减减操作
                Long i = jedisPoolConfig.decr(KEY);//库存的key减减
                jedisPoolConfig.incr(KEY_CONSUMER);//消费的key加加
                System.out.println(Thread.currentThread().getName()+"消费一张票,总共消费了"+jedisPoolConfig.getValueKey(KEY_CONSUMER));
                System.out.println("当前剩余票数:"+Integer.parseInt(jedisPoolConfig.getValueKey(KEY))+"张");
            }else{
                System.out.println("票买完了");
                temp = 0;
                //执行具体业务逻辑结束
                System.out.println(Thread.currentThread().getName() + " end");
                end.countDown();//唤醒主线程
            }
            return temp;
        }
    }


    @RequestMapping(value = "/instanceInfo",method = RequestMethod.GET)
    public ServiceInstance getServiceInstance(){
        return this.discoveryClient.getLocalServiceInstance();
    }


    @GetMapping("/{id}")
    @ResponseBody
    public User findById(@PathVariable Long id, HttpServletRequest request) throws InterruptedException, ParseException {
        System.out.println("traceId:"+request.getHeader("X-B3-TraceId")+"spanId:"+request.getHeader("X-B3-SpanId"));
        System.out.println("服务调用进来。。。。id:"+id);
        User user = userRepository.findUser(id);
//        Thread.sleep(800);//延迟800ms相应
        System.out.println(user.getUsername());
        return user;
    }
}


#########################################

这是feignclient的配置。调用userseivce超时配置。如果超时就走熔断。
################

eureka:
  server:
    #每***s剔除续约到期的服务
    eviction-interval-timer-in-ms: 30000
    #关闭eureka自我保护机制
    enable-self-preservation: false
  client:
    service-url:
      defaultZone: http://master:8761/eureka/,http://slave:8762/eureka/
  instance:
    prefer-ip-address: true
    #超过3s每收到心跳踢掉服务
    lease-expiration-duration-in-seconds: 10
    #每1s发送一次心跳
    lease-renewal-interval-in-seconds: 5
    instance-id: ${spring.cloud.client.ipAddress}:${spring.application.name}:${server.port}

#请求处理的超时时间
ribbon.ReadTimeout: 3000
#请求连接的超时时间
ribbon.ConnectTimeout: 2000

##开启feign对hystrix的支持
feign:
  hystrix:
    enabled: true
#修改feign客户端的超时时间 500ms
hystrix:
  command:
      #针对某一个客户端的配置
#    UserFeignClient#findOne:
      #全局默认配置
    default:
      metrics:
        rollingStats:
          #10s内
          timeInMilliseconds: 10000
      circuitBreaker:
        #10s内请求达到3次
        requestVolumeThreshold: 10
        #失败率达到50%。打开断路器
        errorThresholdPercentage: 100
        #设置断路器打开休眠3s
        sleepWindowInMilliseconds: 0
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2000  #针对microservice-consumer-movie-feign里面配置调用userController的超时熔断时间
spring:
  application:
    name: microservice-api-gateway
server:
  port: 8050


eureka:
  server:
    #每***s剔除续约到期的服务
    eviction-interval-timer-in-ms: 30000
    #关闭eureka自我保护机制
    enable-self-preservation: false
  client:
    service-url:
      defaultZone: http://master:8761/eureka/,http://slave:8762/eureka/
  instance:
    prefer-ip-address: true
    #超过3s每收到心跳踢掉服务
    lease-expiration-duration-in-seconds: 10
    #每1s发送一次心跳
    lease-renewal-interval-in-seconds: 5
    instance-id: ${spring.cloud.client.ipAddress}:${spring.application.name}:${server.port}

zuul:
  routes:
    microservice-consumer-movie-feign:
      path: /api/feign/**
      stripPrefix: false

#请求路径:http://localhost:8050/microservice-consumer-movie-feign/api/feign/3可以把请求路由到microservice-consumer-movie-feign服务
#zuul:
#   routes:
#     microservice-consumer-movie-feign:
#      url: http://localhost:8020



#zuul集成hystrix默认1s.默认使用ribbon内部的httpclient管理连接池链接数

ribbon:
  #结果返回的超时时间10s,默认5s,等待userService返回数据
  ReadTimeout: 20000
  #httpClient获取socket链接的超时时间。默认2s
  ConnectTimeout: 2000

#熔断机制.这里配置的熔断时间一般是(ReadTimeout+ConnectTimeout)*2。但是不能小于microservice-consumer-movie-feign里面配置的熔断时间,否则就会走熔断机制
#如果你在zuul配置了熔断fallback的话,熔断超时也要配置,不然如果你配置的ribbon超时时间大于熔断的超时,那么会先走熔断,相当于你配的ribbon超时就不生效了。
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 600000

#开发所有的端点的访问权限
management:
  security:
    enabled: false





###########################

这是zuul路由配置。请求经过zuul后调用feignclient调用userservice去执行任务。任务执行完成前。zuul
每次请求都是走熔断。当任务执行完毕。拿到最后userservice返回的记过
###########








 

 

 

好的,下面是 Redis 实现分布式锁案例Redis 实现分布式锁的方式主要有两种:基于 SETNX 命令和基于 RedLock 算法。 1. 基于 SETNX 命令 SETNX 命令可以在键不存在的情况下设置键的值,如果键已经存在,则不做任何操作。我们可以利用 SETNX 命令来实现一个分布式锁,具体步骤如下: 1. 客户端尝试使用 SETNX 命令设置一个键的值,如果返回值为 1,则表示设置成功,即获取到了锁;如果返回值为 0,则表示设置失败,即锁已经被其他客户端获取。 2. 客户端可以使用 EXPIRE 命令为锁设置一个过期时间,防止锁一直被持有。 3. 当客户端释放锁时,需要使用 DEL 命令删除键,将锁释放。 下面是一个基于 SETNX 命令实现Redis 分布式锁的 Python 代码: ```python import redis class RedisLock(object): def __init__(self, redis_client, key, expire=60): self.redis_client = redis_client self.key = key self.expire = expire def acquire(self): return self.redis_client.setnx(self.key, 1) def release(self): self.redis_client.delete(self.key) ``` 2. 基于 RedLock 算法 RedLock 算法是一种多节点分布式锁算法,它采用了多个 Redis 节点来实现分布式锁。RedLock 算法的具体实现步骤如下: 1. 客户端获取当前时间戳 time1。 2. 客户端依次向多个 Redis 节点(一般为 5 个)发送 SETNX 命令,尝试获取锁。如果 SETNX 命令在某个节点上执行成功,则表示客户端获取到了锁。 3. 客户端计算获取锁的时间 cost_time,如果 cost_time 小于锁的有效时间(一般为 30 秒),则表示客户端获取到了锁。否则,客户端需要释放之前获取到的锁。 4. 当客户端需要释放锁时,需要向所有 Redis 节点发送 DEL 命令,将锁释放。 下面是一个基于 RedLock 算法实现Redis 分布式锁的 Python 代码: ```python import redis import time class RedisLock(object): def __init__(self, redis_nodes, key, expire=30): self.redis_nodes = redis_nodes self.key = key self.expire = expire def acquire(self): n = len(self.redis_nodes) start_time = int(time.time() * 1000) for i in range(n): redis_client = redis.StrictRedis(host=self.redis_nodes[i]['host'], port=self.redis_nodes[i]['port']) # 计算当前时间距离锁过期的时间 remaining_time = self.expire - int((int(time.time() * 1000) - start_time) / 1000) if remaining_time <= 0: break # 尝试获取锁 if redis_client.setnx(self.key, 1): redis_client.expire(self.key, remaining_time) return True # 获取锁失败,需要释放已经获取到的锁 self.release() return False def release(self): n = len(self.redis_nodes) for i in range(n): redis_client = redis.StrictRedis(host=self.redis_nodes[i]['host'], port=self.redis_nodes[i]['port']) redis_client.delete(self.key) ``` 以上就是 Redis 实现分布式锁案例,希望能对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值