这是其他两篇文章的后续文章– 动机 ,说明为什么在分布式系统中需要类似Hystrix的内容,以及Hystrix的基本介绍 。
这将是我的Hystrix旅程的总结,其中包含各种属性的详细信息,可以对这些属性进行调整以更改Hystrix的行为,并涉及一些高级概念
调整Hystrix行为
Hystrix的配置在此Wiki中进行了解释,简要介绍了两个主要的组来控制Hystrix的属性,
- 命令属性
- ThreadPool属性
属性遵循Wiki中说明的优先顺序,这里我将重点介绍通过属性文件指定的属性。
对于示例命令,定义了以下方式:
public class HelloWorldCommand extends HystrixCommand<String> {
private static final Logger logger = LoggerFactory.getLogger(HelloWorldCommand.class);
private final String name;
public HelloWorldCommand(String name) {
super(HystrixCommandGroupKey.Factory.asKey("default"));
this.name = name;
}
@Override
protected String run() throws Exception {
logger.info("HelloWorld Command Invoked");
return "Hello " + name;
}
}
可以调整的第一个行为是在线程池中执行命令还是与调用方(SEMAPHORE策略类型)执行线程相同。 如果执行在线程池中,则可以设置请求超时。
hystrix.command.HelloWorldCommand.execution.isolation.strategy=THREAD
hystrix.command.HelloWorldCommand.execution.isolation.thread.timeoutInMilliseconds=1000
第二种行为是断路器,它根据在滚动时间窗内收集的信息进行工作,这种方式进行配置,例如持续10秒:
hystrix.command.HelloWorldCommand.metrics.rollingStats.timeInMilliseconds=10000
在此窗口中,如果一定百分比的故障(例如50%)发生在请求阈值(例如10秒内发生20个故障),则电路断开,其配置如下所示:
hystrix.command.HelloWorldCommand.circuitBreaker.requestVolumeThreshold=20
hystrix.command.HelloWorldCommand.circuitBreaker.errorThresholdPercentage=50
电路断开后,它将保持这种状态并保持以下设置的时间,在这种情况下为5秒:
hystrix.command.HelloWorldCommand.circuitBreaker.sleepWindowInMilliseconds=5000
线程池设置是使用指定的组密钥控制的,在本示例中称为默认组密钥。 不过,也可以将特定的“线程池键”指定为构造函数的一部分。
hystrix.threadpool.default.coreSize=10
hystrix.threadpool.default.queueSizeRejectionThreshold=5
在这里,可以并行运行10个命令,而其他5个则保留在队列中,超过该队列将拒绝请求。
要求折叠
Tomaz Nurkiewicz在他的博客网站NoBlogDefFound中做了出色的解释请求折叠的工作 。 我的示例有些简化,请考虑以下情况,其中有很多请求要检索给定id的Person,方法如下:
public class PersonService {
public Person findPerson(Integer id) {
return new Person(id, "name : " + id);
}
public List<Person> findPeople(List<Integer> ids) {
return ids
.stream()
.map(i -> new Person(i, "name : " + i))
.collect(Collectors.toList());
}
}
该服务以固定响应进行响应,但假定该调用是对远程数据存储的。 还可以看到,该服务实现了一个批处理方法,以在给定ID列表的情况下检索人员列表。
请求折叠是一项功能,它将一段时间内发生的多个用户请求批处理为一个这样的远程呼叫,然后将响应散发回用户。
可以通过以下方式定义获取一组ID并获取人员响应的hystrix命令:
public class PersonRequestCommand extends HystrixCommand<List<Person>>{
private final List<Integer> ids;
private final PersonService personService = new PersonService();
private static final Logger logger = LoggerFactory.getLogger(PersonRequestCommand.class);
public PersonRequestCommand(List<Integer> ids) {
super(HystrixCommandGroupKey.Factory.asKey("default"));
this.ids = ids;
}
@Override
protected List<Person> run() throws Exception {
logger.info("Retrieving details for : " + this.ids);
return personService.findPeople(this.ids);
}
}
到目前为止,非常简单,复杂的逻辑现在位于RequestCollapser中,如下所示:
package aggregate.commands.collapsed;
import com.netflix.hystrix.HystrixCollapser;
import com.netflix.hystrix.HystrixCollapserKey;
import com.netflix.hystrix.HystrixCollapserProperties;
import com.netflix.hystrix.HystrixCommand;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class PersonRequestCollapser extends HystrixCollapser<List<Person>, Person, Integer> {
private final Integer id;
public PersonRequestCollapser(Integer id) {
super(Setter.
withCollapserKey(HystrixCollapserKey.Factory.asKey("personRequestCollapser"))
.andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(2000)));
this.id = id;
}
@Override
public Integer getRequestArgument() {
return this.id;
}
@Override
protected HystrixCommand<List<Person>> createCommand(Collection<CollapsedRequest<Person, Integer>> collapsedRequests) {
List<Integer> ids = collapsedRequests.stream().map(cr -> cr.getArgument()).collect(Collectors.toList());
return new PersonRequestCommand(ids);
}
@Override
protected void mapResponseToRequests(List<Person> batchResponse, Collection<CollapsedRequest<Person, Integer>> collapsedRequests) {
Map<Integer, Person> personMap = batchResponse.stream().collect(Collectors.toMap(Person::getId, Function.identity()));
for (CollapsedRequest<Person, Integer> cr: collapsedRequests) {
cr.setResponse(personMap.get(cr.getArgument()));
}
}
}
这里发生了一些事情,首先,参数化类型签名中的类型指示响应的类型(List <Person>),调用方期望的响应类型(Person)和请求的请求类型(请求的ID)。人)。 然后有两种方法,一种创建批处理命令,第二种将响应映射回原始请求。
现在,从用户的角度来看,这没有多大变化,就好像对单个命令一样进行调用,并且Request Collapsing处理批处理,分派和映射回响应。 这是示例测试的样子:
@Test
public void testCollapse() throws Exception {
HystrixRequestContext requestContext = HystrixRequestContext.initializeContext();
logger.info("About to execute Collapsed command");
List<Observable<Person>> result = new ArrayList<>();
CountDownLatch cl = new CountDownLatch(1);
for (int i = 1; i <= 100; i++) {
result.add(new PersonRequestCollapser(i).observe());
}
Observable.merge(result).subscribe(p -> logger.info(p.toString())
, t -> logger.error(t.getMessage(), t)
, () -> cl.countDown());
cl.await();
logger.info("Completed executing Collapsed Command");
requestContext.shutdown();
}
结论
Hystrix的功能远不止我在这里介绍的内容。 它确实是一个很棒的库,对于创建弹性系统至关重要。我已经开始欣赏设计这个出色的库所花费的大量思考过程。
翻译自: https://www.javacodegeeks.com/2015/11/gentle-introduction-to-hystrix-wrapup.html