这是我的案例流程图。浏览器----->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返回的记过
###########