SpringCloud 系列 --5.Hystrix

本贴最后更新于 1861 天前,其中的信息可能已经天翻地覆

Hystrix 功能:

  • 当所依赖的网络服务发生延迟或者失败时,对访问的客户端程序进行保护。
  • 在分布式系统中,停止级联故障
  • 网络服务恢复正常后,可以快速恢复客户端的访问能力
  • 调用失败时,执行服务回退
  • 支持实时监控、报警和其他操作。

先简单用示例看下 Hystrix 的作用

搭建一个简单的服务端接口,端口 8080

package org.crazyit.cloud; import javax.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController public class MyController { @GetMapping("/normalHello") @ResponseBody public String normalHello(HttpServletRequest request) { return "Hello World"; } @GetMapping("/errorHello") @ResponseBody public String errorHello(HttpServletRequest request) throws Exception { // 模拟需要处理10秒 Thread.sleep(10000); return "Error Hello World"; } }

搭建客户端来调用

客户端先继承 HystrixComman 类

package org.crazyit.cloud; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; /** * 调用一个正常的服务 * @author 杨恩雄 * */ public class HelloCommand extends HystrixCommand<String> { private String url; CloseableHttpClient httpclient; public HelloCommand(String url) { // 调用父类的构造器,设置命令组的key,默认用来作为线程池的key super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); // 创建HttpClient客户端 this.httpclient = HttpClients.createDefault(); this.url = url; } protected String run() throws Exception { try { // 调用 GET 方法请求服务 HttpGet httpget = new HttpGet(url); // 得到服务响应 HttpResponse response = httpclient.execute(httpget); // 解析并返回命令执行结果 return EntityUtils.toString(response.getEntity()); } catch (Exception e) { e.printStackTrace(); } return ""; } protected String getFallback() { System.out.println("执行 HelloCommand 的回退方法"); return "error"; } }

正常调用服务端

package org.crazyit.cloud; public class HelloMain { public static void main(String[] args) { // 请求正常的服务 String normalUrl = "http://localhost:8080/normalHello"; HelloCommand command = new HelloCommand(normalUrl); String result = command.execute(); System.out.println("请求正常的服务,结果:" + result); } }

输出: 请求正常的服务,结果:Hello World

调用超时的服务

package org.crazyit.cloud; public class HelloErrorMain { public static void main(String[] args) { // 请求异常的服务 String normalUrl = "http://localhost:8080/errorHello"; HelloCommand command = new HelloCommand(normalUrl); String result = command.execute(); System.out.println("请求异常的服务,结果:" + result); } }

输出:
执行 HelloCommand 的回退方法
请求异常的服务,结果:error

可以看到超时之后会触发 HelloCommand 中 getFallback 方法执行回退操作(默认是超过 1 秒则回退)。

Hystrix 运作流程图:
image.png

上面例子中我们使用了 HystrixCommand 的 execute 方法命令,除此外还有以下方法来执行命令:

  • toObservable: 返回一个最原始的课观察的实例(Observable),Observable 是 RxJava 的类,使用该对象可以观察命令的执行过程,并且将执行的信息传递给订阅者。
  • observe: 调用 toObservable 方法,获得一个原始的 Observable 实例,使用 ReplaySubject 作为原始 Observable 的订阅者。
  • queue: 通过 toObservable 方法获取原始的 Observable 实例,再调用 Observable 的 toBlocking 方法得到一个 BlockingObservable 实例,最后调用 BlockingObservable 的 toFuture 方法返回 Future 实例,调用 Future 的 get 方法得到执行结果
  • execute: 调用 queue 的 get 方法返回命令的执行结果,该方法同步执行。

以上 4 个方法,除 execute 方法外,其他方法均为异步执行.observe 与 toOBservable 方法的区别在于,toObservalbe 被调用后,命令不会立即执行,只有当返回的 Observable 实例被订阅后,才会真正执行命令。而 observe 方法的实现中,会调用 toObservable 得到的 Observable 实例,在对其进行订阅,因此调用 observ 方法后会立即执行命令(异步).

代码参考:

package org.crazyit.cloud.run; import rx.Observable; import rx.Observer; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; public class RunTest { public static void main(String[] args) throws Exception { // 使用execute方法 RunCommand c1 = new RunCommand("使用execute方法执行命令"); c1.execute(); // 使用queue方法 RunCommand c2 = new RunCommand("使用queue方法执行命令"); c2.queue(); // 使用 observe 方法 RunCommand c3 = new RunCommand("使用 observe 方法执行命令"); c3.observe(); // 使用 toObservable 方法 RunCommand c4 = new RunCommand("使用 toObservable 方法执行命令"); // 调用 toObservable 方法后,命令不会马上执行 Observable<String> ob = c4.toObservable(); // 进行订阅,此时会执行命令 ob.subscribe(new Observer<String>() { public void onCompleted() { System.out.println(" 命令执行完成"); } public void onError(Throwable e) { } public void onNext(String t) { System.out.println(" 命令执行结果:" + t); } }); Thread.sleep(100); } // 测试命令 static class RunCommand extends HystrixCommand<String> { String msg; public RunCommand(String msg) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.msg = msg; } protected String run() throws Exception { System.out.println(msg); return "success"; } } }

输出:
使用 execute 方法执行命令
使用 queue 方法执行命令
使用 observe 方法执行命令
使用 toObservable 方法执行命令
命令执行结果:success
命令执行完成

Hystrix 可以配置一些属性可以参考 https://github.com/Netflix/Hystrix/wiki/Configuration

关于回退需要注意的是,回退方法 A 中也可以触发另一个命令 B,如果 B 执行失败也会触发 B 回退

特殊的情况:
image.png

断路器开启需要满足两个条件:

  • 整个链路达到一定阈值,默认情况下,10s 内产生超过 20 次请求,则符合第一个条件。
  • 满足第一个条件的情况下,如果请求的错误百分比大于阈值,则会打开断路器,默认为 50%
package org.crazyit.cloud.breaker; import com.netflix.config.ConfigurationManager; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandMetrics.HealthCounts; import com.netflix.hystrix.HystrixCommandProperties; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; /** * 断路器开启测试 * @author 杨恩雄 * */ public class OpenTest { public static void main(String[] args) throws Exception { /** * 配置了三项 * 1.配置了数据的统计时间10000毫秒=10s * 2.配置了阈值10个请求 * 3.配置了错误百分比50%, * 注: MyCommand中配置了超时时间为500毫秒,命令执行睡眠800毫秒,也就是该命令总会超时(因为500+800 = 1.3 > 1s) */ // 10秒内有10个请求,则符合第一个条件 ConfigurationManager.getConfigInstance().setProperty( "hystrix.command.default.metrics.rollingStats.timeInMilliseconds", 10000); ConfigurationManager.getConfigInstance().setProperty( "hystrix.command.default.circuitBreaker.requestVolumeThreshold", 10); ConfigurationManager.getConfigInstance().setProperty( "hystrix.command.default.circuitBreaker.errorThresholdPercentage", 50); for(int i = 0; i < 15; i++) { // 执行的命令全部都会超时 MyCommand c = new MyCommand(); c.execute(); // 断路器打开后输出信息 if(c.isCircuitBreakerOpen()) { System.out.println("断路器被打开,执行第 " + (i + 1) + " 个命令"); } } } /** * 模拟超时的命令 * @author 杨恩雄 * */ static class MyCommand extends HystrixCommand<String> { // 设置超时的时间为500毫秒 public MyCommand() { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionTimeoutInMilliseconds(500)) ); } protected String run() throws Exception { // 模拟处理超时 Thread.sleep(800); return ""; } @Override protected String getFallback() { return ""; } } }

运行结果:
断路器被打开,执行第 11 个命令
断路器被打开,执行第 12 个命令
断路器被打开,执行第 13 个命令
断路器被打开,执行第 14 个命令
断路器被打开,执行第 15 个命令
可见前 10 个命令没有开启断路器,到了第 11 个命令,断路器被打开。

断路器关闭

断路器打开后,在一段时间内,命令不会再执行(一直触发回退),这段时间我们称作"休眠期",休眠期默认为 5s,休眠期结束后,Hystrix 会尝试性地执行一次命令,此时断路器的状态不是开启,也不是关闭,而是一个半开的状态,如果这一次命令执行成功,则会关闭断路器并清空链路的健康信息; 如果执行失败,断路器会继续保持打开的状态。

package org.crazyit.cloud.breaker; import com.netflix.config.ConfigurationManager; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandMetrics.HealthCounts; import com.netflix.hystrix.HystrixCommandProperties; public class CloseTest { public static void main(String[] args) throws Exception { // 10秒内有3个请求就满足第一个开启断路器的条件 ConfigurationManager.getConfigInstance().setProperty( "hystrix.command.default.metrics.rollingStats.timeInMilliseconds", 10000); ConfigurationManager.getConfigInstance().setProperty( "hystrix.command.default.circuitBreaker.requestVolumeThreshold", 3); // 请求的失败率,默认值为50% ConfigurationManager.getConfigInstance().setProperty( "hystrix.command.default.circuitBreaker.errorThresholdPercentage", 50); // 设置休眠期,断路器打开后,这段时间不会再执行命令,默认值为5秒,此处设置为3秒 ConfigurationManager.getConfigInstance().setProperty( "hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds", 3000); // 该值决定是否执行超时 boolean isTimeout = true; for(int i = 0; i < 10; i++) { // 执行的命令全部都会超时 MyCommand c = new MyCommand(isTimeout); c.execute(); // 输出健康状态等信息 HealthCounts hc = c.getMetrics().getHealthCounts(); System.out.println("断路器状态:" + c.isCircuitBreakerOpen() + ", 请求总数:" + hc.getTotalRequests()); if(c.isCircuitBreakerOpen()) { // 断路器打开,让下一次循环成功执行命令 isTimeout = false; System.out.println("===== 断路器打开了,等待休眠期结束 ====="); // 休眠期会在3秒后结束,此处等待4秒,确保休眠期结束 Thread.sleep(4000); } } } /** * 模拟超时的命令 * @author 杨恩雄 * */ static class MyCommand extends HystrixCommand<String> { private boolean isTimeout; // 设置超时的时间为500毫秒 public MyCommand(boolean isTimeout) { super( Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionTimeoutInMilliseconds(500)) ); this.isTimeout = isTimeout; } protected String run() throws Exception { // 让外部决定是否超时 if(isTimeout) { // 模拟处理超时 Thread.sleep(800); } else { Thread.sleep(200); } return ""; } @Override protected String getFallback() { return ""; } } }

运行结果:

断路器状态:false, 请求总数:0
断路器状态:false, 请求总数:1
断路器状态:false, 请求总数:2
断路器状态:true, 请求总数:3
===== 断路器打开了,等待休眠期结束 =====
断路器状态:false, 请求总数:0
断路器状态:false, 请求总数:0
断路器状态:false, 请求总数:0
断路器状态:false, 请求总数:3
断路器状态:false, 请求总数:3
断路器状态:false, 请求总数:5

我们再代码中配置休眠期为 3s,循环 10 次,创建 10 个命令并执行,在执行完第 4 个命令后,断路器被打开,此时我们等待休眠期结束,让下一次循环命令执行成功。第 5 次会执行成功,此时断路器会被关闭,剩下的命令全部都可以正常执行,在循环体内使用了 HealthCounts 对象,用于记录链路的健康信息,如果断路器关闭(链路恢复健康),HealthCounts 里面的健康信息会被重置。

隔离机制

命令真正执行除了要保证断路器关闭外,还需要判断执行命令的线程池或者信号量是否满载的情况,如果满载则不会执行,直接回退,这样的机制在控制命令的执行上,实现了错误的隔离,Hystrix 提供两种隔离策略:

  • THREAD(线程)默认值,由线程池来决定命令的执行,如果线程池满载则不会执行命令.Hystrix 使用了 ThreadPoolExecutor 来控制线程池行为,线程池的默认大小为 10.
  • SEMAPHORE(信号量):由信号量来决定命令的执行,当请求的并发数高于阈值时,就不在执行命令,相当于线程池策略,信号量策略开销更小,但是该策略不支持超时以及异步,除非对调用的服务有足够的信任,否则不建议使用该策略进行隔离。
package org.crazyit.cloud.isolation; import java.util.concurrent.ThreadPoolExecutor; import com.netflix.hystrix.HystrixCircuitBreaker; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandProperties; public class MyCommand extends HystrixCommand<String> { int index; public MyCommand(int index) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory .asKey("ExampleGroup"))); this.index = index; } protected String run() throws Exception { Thread.sleep(500); System.out.println("执行方法,当前索引:" + index); return ""; } @Override protected String getFallback() { System.out.println("执行 fallback,当前索引:" + index); return ""; } }

测试线程池

package org.crazyit.cloud.isolation; import java.util.concurrent.ThreadPoolExecutor; import com.netflix.config.ConfigurationManager; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandProperties; /** * 线程隔离策略测试类 * @author 杨恩雄 * */ public class ThreadIso { public static void main(String[] args) throws Exception { // 配置线程池大小为3 ConfigurationManager.getConfigInstance().setProperty( "hystrix.threadpool.default.coreSize", 3); for(int i = 0; i < 6; i++) { MyCommand c = new MyCommand(i); c.queue(); } Thread.sleep(5000); } }

配置了线程池大小为 3,进行 6 次循环,意味着有 3 次会回退。
输出:
执行 fallback,当前索引:3
执行 fallback,当前索引:4
执行 fallback,当前索引:5
执行方法,当前索引:1
执行方法,当前索引:0
执行方法,当前索引:2

测试信号量

package org.crazyit.cloud.isolation; import java.util.concurrent.ThreadPoolExecutor; import com.netflix.config.ConfigurationManager; import com.netflix.hystrix.HystrixCircuitBreaker; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandProperties; import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy; /** * 信号量隔离策略测试类 * * @author 杨恩雄 * */ public class SemaphoreIso { public static void main(String[] args) throws Exception { // 配置使用信号量的策略进行隔离 ConfigurationManager.getConfigInstance().setProperty( "hystrix.command.default.execution.isolation.strategy", ExecutionIsolationStrategy.SEMAPHORE); // 设置最大并发数,默认值为10 ConfigurationManager .getConfigInstance() .setProperty( "hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests", 2); // 设置执行回退方法的最大并发,默认值为10 ConfigurationManager .getConfigInstance() .setProperty( "hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests", 20); for (int i = 0; i < 6; i++) { final int index = i; Thread t = new Thread() { public void run() { MyCommand c = new MyCommand(index); c.execute(); } }; t.start(); } Thread.sleep(5000); } }

配置了线程池最大并发数为 2,循环 6 次,意味着有 4 次会回退
输出:
执行 fallback,当前索引:4
执行 fallback,当前索引:0
执行 fallback,当前索引:1
执行 fallback,当前索引:3
执行方法,当前索引:5
执行方法,当前索引:2

请求合并

对于URL相同但是参数不同的请求,Hystrix提供了合并请求的功能,减少线程开销和网络连接,提高性能,有点像批处理功能。 实现合并请求功能,至少包含以下3个条件:
  • 需要有一个执行请求的命令,将全部参数进行整理,然后调用外部服务。
  • 需要有一个合并处理器,用于手机请求,以及处理结果。
  • 外部接口提供支持,例如一个接口是根据姓名查询人员信息/person/{personName} ,另外服务端还提供批量查询/persons 用于查找多个 Person

这种情况,如果有多个请求单个 Person 的请求,就可以合并为一个批量查询进行处理。

package org.crazyit.cloud.collapse; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.UUID; import java.util.concurrent.Future; import com.netflix.config.ConfigurationManager; import com.netflix.hystrix.HystrixCollapser; import com.netflix.hystrix.HystrixCollapser.CollapsedRequest; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; public class CollapseTest { public static void main(String[] args) throws Exception { // 收集 1 秒内发生的请求,合并为一个命令执行 ConfigurationManager.getConfigInstance().setProperty( "hystrix.collapser.default.timerDelayInMilliseconds", 1000); // 请求上下文 HystrixRequestContext context = HystrixRequestContext .initializeContext(); // 创建请求合并处理器 MyHystrixCollapser c1 = new MyHystrixCollapser("Angus"); MyHystrixCollapser c2 = new MyHystrixCollapser("Crazyit"); MyHystrixCollapser c3 = new MyHystrixCollapser("Sune"); MyHystrixCollapser c4 = new MyHystrixCollapser("Paris"); // 异步执行 Future<Person> f1 = c1.queue(); Future<Person> f2 = c2.queue(); Future<Person> f3 = c3.queue(); Future<Person> f4 = c4.queue(); System.out.println(f1.get()); System.out.println(f2.get()); System.out.println(f3.get()); System.out.println(f4.get()); context.shutdown(); } /** * 合并执行的命令类 * * @author 杨恩雄 * */ static class CollapserCommand extends HystrixCommand<Map<String, Person>> { // 请求集合,第一个类型是单个请求返回的数据类型,第二是请求参数的类型 Collection<CollapsedRequest<Person, String>> requests; private CollapserCommand( Collection<CollapsedRequest<Person, String>> requests) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory .asKey("ExampleGroup"))); this.requests = requests; } @Override protected Map<String, Person> run() throws Exception { System.out.println("收集参数后执行命令,参数数量:" + requests.size()); // 处理参数 List<String> personNames = new ArrayList<String>(); for(CollapsedRequest<Person, String> request : requests) { personNames.add(request.getArgument()); } // 调用服务(此处模拟调用),根据名称获取Person的Map Map<String, Person> result = callService(personNames); return result; } // 模拟服务返回 private Map<String, Person> callService(List<String> personNames) { Map<String, Person> result = new HashMap<String, Person>(); for(String personName : personNames) { Person p = new Person(); p.id = UUID.randomUUID().toString(); p.name = personName; p.age = new Random().nextInt(30); result.put(personName, p); } return result; } } static class Person { String id; String name; Integer age; public String toString() { // TODO Auto-generated method stub return "id: " + id + ", name: " + name + ", age: " + age; } } /** * 合并处理器 * 第一个类型为批处理返回的结果类型 * 第二个为单请求返回的结果类型 * 第三个是请求参数类型 * @author 杨恩雄 */ static class MyHystrixCollapser extends HystrixCollapser<Map<String, Person>, Person, String> { String personName; public MyHystrixCollapser(String personName) { this.personName = personName; } @Override public String getRequestArgument() { return personName; } @Override protected HystrixCommand<Map<String, Person>> createCommand( Collection<CollapsedRequest<Person, String>> requests) { return new CollapserCommand(requests); } @Override protected void mapResponseToRequests(Map<String, Person> batchResponse, Collection<CollapsedRequest<Person, String>> requests) { // 让结果与请求进行关联 for (CollapsedRequest<Person, String> request : requests) { // 获取单个响应返回的结果 Person singleResult = batchResponse.get(request.getArgument()); // 关联到请求中 request.setResponse(singleResult); } } } }

设置了"时间段",在 1 秒内执行的请求将会被合并到一起执行,该"时间段"的默认值为 10 毫秒。
执行结果:
收集参数后执行命令,参数数量:4
id: 0a41cb54-fc48-470a-89bd-964f3d4dbb03, name: Angus, age: 28
id: 9a7cfdf5-eab8-47f3-ae0b-e17948cb54e6, name: Crazyit, age: 4
id: 943de7e9-5c0e-4629-9b95-6ff5c9a469f9, name: Sune, age: 29
id: 59282c89-d0d5-4dde-9296-0478b72df8c9, name: Paris, age: 8

一般来书合并请求进行批处理,比发送多个请求快,对于 URL 相同、参数不同的请求,推荐使用合并请求功能。

请求缓存

Hystrix 支持缓存功能,一次请求过程中,多个地方调用同一个接口考虑使用缓存,缓存打开后下一次命令不会执行直接从缓存中获取响应;开启缓存较为简单在命令中重写父类的 getCacheKey 即可。

package org.crazyit.cloud.cache; import org.crazyit.cloud.HelloCommand; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixCommandProperties; import com.netflix.hystrix.HystrixRequestCache; import com.netflix.hystrix.HystrixCommand.Setter; import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; public class CacheMain { public static void main(String[] args) { // 初始化请求上下文 HystrixRequestContext context = HystrixRequestContext.initializeContext(); // 请求正常的服务 String key = "cache-key"; MyCommand c1 = new MyCommand(key); MyCommand c2 = new MyCommand(key); MyCommand c3 = new MyCommand(key); // 输出结果 System.out.println(c1.execute() + "c1 是否读取缓存: " + c1.isResponseFromCache()); System.out.println(c2.execute() + "c2 是否读取缓存: " + c2.isResponseFromCache()); System.out.println(c3.execute() + "c3 是否读取缓存: " + c3.isResponseFromCache()); // 获取缓存实例 HystrixRequestCache cache = HystrixRequestCache.getInstance( HystrixCommandKey.Factory.asKey("MyCommandKey"), HystrixConcurrencyStrategyDefault.getInstance()); // 清空缓存 cache.clear(key); // 重新执行命令 MyCommand c4 = new MyCommand(key); System.out.println(c4.execute() + "c4 是否读取缓存: " + c4.isResponseFromCache()); context.shutdown(); } static class MyCommand extends HystrixCommand<String> { private String key; public MyCommand(String key) { super( Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")) .andCommandKey(HystrixCommandKey.Factory.asKey("MyCommandKey")) ); this.key = key; } protected String run() throws Exception { System.out.println("执行命令"); return ""; } @Override protected String getCacheKey() { return this.key; } } }

输出:
执行命令
c1 是否读取缓存: false
c2 是否读取缓存: true
c3 是否读取缓存: true
执行命令
c4 是否读取缓存: false

可见 c2,c3 都读取了缓存。c4 因为执行前清空了缓存,所有没有读取缓存。

注意: 合并请求,请求缓存,必须在一次请求过程中才能实现,因此需要先初始化请求上下文:

// 初始化请求上下文 HystrixRequestContext context = HystrixRequestContext.initializeContext(); xxxxxxxxxx context.shutdown();
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    948 引用 • 1460 回帖 • 1 关注
  • Hystrix
    7 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...