Hystrix高级特性与配置详解
Hystrix高级特性与配置详解
前置知识
在学习本教程之前,建议您已经掌握:
- Hystrix的基本概念和使用方法
- Spring Cloud基础知识
- 微服务架构设计原则
Hystrix高级配置
Hystrix提供了丰富的配置选项,可以根据不同的业务场景进行精细调整。本章将详细介绍这些配置选项及其最佳实践。
命令配置
Hystrix命令是Hystrix的核心概念,每个命令代表一个依赖调用。以下是常用的命令配置选项:
执行配置
@HystrixCommand(
commandProperties = {
// 执行隔离策略,THREAD 或 SEMAPHORE
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
// 执行超时时间
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
// 是否启用超时
@HystrixProperty(name = "execution.timeout.enabled", value = "true"),
// 超时时是否中断执行
@HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
// 取消时是否中断执行
@HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "false")
}
)
public String commandMethod() {
// 方法实现
return "result";
}
断路器配置
@HystrixCommand(
commandProperties = {
// 是否启用断路器
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 请求阈值,断路器启动的最小请求数
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
// 错误百分比阈值
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
// 断路器打开后的休眠时间窗口
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),
// 强制打开断路器
@HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
// 强制关闭断路器
@HystrixProperty(name = "circuitBreaker.forceClosed", value = "false")
}
)
public String circuitBreakerMethod() {
// 方法实现
return "result";
}
指标收集配置
@HystrixCommand(
commandProperties = {
// 统计滚动窗口的时间长度
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000"),
// 统计滚动窗口中桶的数量
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
// 是否启用百分位统计
@HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "true"),
// 百分位统计的滚动窗口时间
@HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
// 百分位统计滚动窗口中的桶数量
@HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "6"),
// 百分位统计中每个桶的请求数
@HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
// 健康快照的间隔时间
@HystrixProperty(name = "metrics.healthSnapshot.intervalInMilliseconds", value = "500")
}
)
public String metricsMethod() {
// 方法实现
return "result";
}
请求缓存配置
@HystrixCommand(
commandProperties = {
// 是否启用请求缓存
@HystrixProperty(name = "requestCache.enabled", value = "true")
}
)
public String cacheMethod() {
// 方法实现
return "result";
}
请求合并配置
@HystrixCommand(
commandProperties = {
// 是否启用请求合并
@HystrixProperty(name = "requestCache.enabled", value = "true")
}
)
public String collapserMethod() {
// 方法实现
return "result";
}
线程池配置
Hystrix使用线程池隔离依赖调用,以下是线程池的配置选项:
@HystrixCommand(
threadPoolKey = "userThreadPool",
threadPoolProperties = {
// 核心线程数
@HystrixProperty(name = "coreSize", value = "10"),
// 最大队列大小
@HystrixProperty(name = "maxQueueSize", value = "100"),
// 队列拒绝阈值
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "50"),
// 保持活跃时间
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
// 线程池指标滚动窗口时间
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000"),
// 线程池指标滚动窗口桶数量
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10")
}
)
public String threadPoolMethod() {
// 方法实现
return "result";
}
信号量配置
除了线程池隔离,Hystrix还支持信号量隔离:
@HystrixCommand(
commandProperties = {
// 使用信号量隔离
@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
// 信号量大小
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 降级方法信号量大小
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10")
}
)
public String semaphoreMethod() {
// 方法实现
return "result";
}
全局配置
除了通过注解配置单个命令,还可以在application.properties
或application.yml
中进行全局配置:
# 默认命令配置
hystrix.command.default.execution.isolation.strategy=THREAD
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
hystrix.command.default.circuitBreaker.enabled=true
hystrix.command.default.circuitBreaker.requestVolumeThreshold=20
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
# 特定命令配置
hystrix.command.UserService.getUser.execution.isolation.thread.timeoutInMilliseconds=3000
# 默认线程池配置
hystrix.threadpool.default.coreSize=10
hystrix.threadpool.default.maxQueueSize=100
hystrix.threadpool.default.queueSizeRejectionThreshold=50
# 特定线程池配置
hystrix.threadpool.userThreadPool.coreSize=20
Hystrix高级特性
请求缓存
Hystrix提供了请求缓存功能,可以减少对依赖服务的重复调用。
启用请求缓存
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@CacheResult
@HystrixCommand(commandKey = "getUserById")
public User getUserById(Long id) {
System.out.println("Getting user by id: " + id);
return new User(id, "User " + id, "user" + id + "@example.com");
}
@CacheResult
@HystrixCommand(commandKey = "getUserByIdWithName")
public User getUserByIdWithName(@CacheKey("id") Long id, String name) {
System.out.println("Getting user by id and name: " + id + ", " + name);
return new User(id, name, "user" + id + "@example.com");
}
}
缓存上下文
要使用Hystrix缓存,需要设置缓存上下文:
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// 初始化Hystrix请求上下文
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
// 第一次调用
User user1 = userService.getUserById(id);
// 第二次调用(从缓存获取)
User user2 = userService.getUserById(id);
return user2;
} finally {
// 关闭上下文
context.shutdown();
}
}
}
使用过滤器自动管理上下文
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class HystrixRequestContextFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
filterChain.doFilter(request, response);
} finally {
context.shutdown();
}
}
}
缓存清除
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@CacheResult
@HystrixCommand(commandKey = "getUserById")
public User getUserById(Long id) {
System.out.println("Getting user by id: " + id);
return new User(id, "User " + id, "user" + id + "@example.com");
}
@CacheRemove(commandKey = "getUserById")
@HystrixCommand
public void updateUser(@CacheKey Long id, String name) {
System.out.println("Updating user: " + id);
// 更新用户逻辑
}
}
请求合并
Hystrix请求合并(Request Collapsing)可以将多个请求合并成一个批量请求,减少网络开销。
定义请求合并器
import com.netflix.hystrix.HystrixCollapser;
import com.netflix.hystrix.HystrixCollapserKey;
import com.netflix.hystrix.HystrixCollapserProperties;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class UserCollapser extends HystrixCollapser<List<User>, User, Long> {
private final UserService userService;
private final Long userId;
public UserCollapser(UserService userService, Long userId) {
super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("userCollapser"))
.andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter()
.withTimerDelayInMilliseconds(100)));
this.userService = userService;
this.userId = userId;
}
@Override
public Long getRequestArgument() {
return userId;
}
@Override
protected HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Long>> collapsedRequests) {
List<Long> userIds = collapsedRequests.stream()
.map(CollapsedRequest::getArgument)
.collect(Collectors.toList());
return new BatchCommand(userService, userIds);
}
@Override
protected void mapResponseToRequests(List<User> batchResponse, Collection<CollapsedRequest<User, Long>> collapsedRequests) {
Map<Long, User> userMap = batchResponse.stream()
.collect(Collectors.toMap(User::getId, user -> user));
for (CollapsedRequest<User, Long> request : collapsedRequests) {
User user = userMap.get(request.getArgument());
request.setResponse(user);
}
}
private static class BatchCommand extends HystrixCommand<List<User>> {
private final UserService userService;
private final List<Long> userIds;
public BatchCommand(UserService userService, List<Long> userIds) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("userGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("getUsersByIds")));
this.userService = userService;
this.userIds = userIds;
}
@Override
protected List<User> run() {
return userService.getUsersByIds(userIds);
}
@Override
protected List<User> getFallback() {
List<User> fallbackUsers = new ArrayList<>();
for (Long userId : userIds) {
fallbackUsers.add(new User(userId, "Default User", "default@example.com"));
}
return fallbackUsers;
}
}
}
使用注解简化请求合并
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
@Service
public class UserService {
@HystrixCollapser(
batchMethod = "getUsersByIds",
collapserProperties = {
@HystrixProperty(name = "timerDelayInMilliseconds", value = "100"),
@HystrixProperty(name = "maxRequestsInBatch", value = "50")
}
)
public Future<User> getUserAsync(Long id) {
// 此方法不会被调用,而是被合并到批量方法中
return null;
}
@HystrixCommand
public List<User> getUsersByIds(List<Long> ids) {
System.out.println("Getting users by ids: " + ids);
List<User> users = new ArrayList<>();
for (Long id : ids) {
users.add(new User(id, "User " + id, "user" + id + "@example.com"));
}
return users;
}
}
自定义Hystrix钩子
Hystrix提供了多个钩子点,可以自定义Hystrix的行为。
自定义并发策略
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomConcurrencyStrategy extends HystrixConcurrencyStrategy {
private final HystrixConcurrencyStrategy existingStrategy;
public CustomConcurrencyStrategy(HystrixConcurrencyStrategy existingStrategy) {
this.existingStrategy = existingStrategy;
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return existingStrategy != null
? existingStrategy.wrapCallable(new DelegatingUserContextCallable<>(callable))
: new DelegatingUserContextCallable<>(callable);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize,
HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
return existingStrategy != null
? existingStrategy.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
: super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
return existingStrategy != null
? existingStrategy.getRequestVariable(rv)
: super.getRequestVariable(rv);
}
// 自定义Callable包装器,用于传递上下文
private static class DelegatingUserContextCallable<V> implements Callable<V> {
private final Callable<V> delegate;
private final UserContext originalUserContext;
public DelegatingUserContextCallable(Callable<V> delegate) {
this.delegate = delegate;
this.originalUserContext = UserContextHolder.getContext();
}
@Override
public V call() throws Exception {
UserContextHolder.setContext(originalUserContext);
try {
return delegate.call();
} finally {
UserContextHolder.clear();
}
}
}
}
注册自定义并发策略
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
public class HystrixConfig {
@PostConstruct
public void init() {
// 获取当前的Hystrix插件
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
// 重置Hystrix插件
HystrixPlugins.reset();
// 注册自定义并发策略
HystrixPlugins.getInstance().registerConcurrencyStrategy(new CustomConcurrencyStrategy(concurrencyStrategy));
// 重新注册其他插件
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
}
}
Hystrix监控
Hystrix Dashboard
Hystrix Dashboard是一个可视化工具,用于监控Hystrix命令的执行情况。
配置Hystrix Dashboard
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableCircuitBreaker
@EnableHystrixDashboard
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
management.endpoints.web.exposure.include=hystrix.stream,info,health
使用Hystrix Dashboard
- 访问Dashboard:
http://localhost:8080/hystrix
- 输入Hystrix流地址:
http://localhost:8080/actuator/hystrix.stream
- 点击"Monitor Stream"按钮
Turbine聚合监控
在微服务架构中,通常有多个服务实例。Turbine可以聚合多个Hystrix流,提供统一的监控视图。
配置Turbine
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
@SpringBootApplication
@EnableTurbine
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
turbine.app-config=user-service,order-service
turbine.cluster-name-expression=new String("default")
使用Turbine
- 访问Dashboard:
http://localhost:8080/hystrix
- 输入Turbine流地址:
http://localhost:8989/turbine.stream
- 点击"Monitor Stream"按钮
实战示例:构建弹性微服务系统
服务架构
我们将构建一个简单的电子商务系统,包含以下服务:
- 用户服务(User Service)
- 商品服务(Product Service)
- 订单服务(Order Service)
- API网关(Gateway)
用户服务
用户服务代码
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.List;
@SpringBootApplication
@EnableCircuitBreaker
@EnableHystrixDashboard
public class UserServiceApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUser(id);
}
@GetMapping("/users/{id}/with-orders")
public UserWithOrders getUserWithOrders(@PathVariable Long id) {
return userService.getUserWithOrders(id);
}
}
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
private List<User> users = Arrays.asList(
new User(1L, "John Doe", "john@example.com"),
new User(2L, "Jane Smith", "jane@example.com"),
new User(3L, "Bob Johnson", "bob@example.com")
);
@CacheResult
@HystrixCommand(
fallbackMethod = "getUserFallback",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000")
}
)
public User getUser(@CacheKey Long id) {
System.out.println("Getting user by id: " + id);
return users.stream()
.filter(user -> user.getId().equals(id))
.findFirst()
.orElseThrow(() -> new RuntimeException("User not found"));
}
public User getUserFallback(Long id) {
return new User(id, "Default User", "default@example.com");
}
@HystrixCommand(
fallbackMethod = "getUserWithOrdersFallback",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
},
threadPoolKey = "userWithOrdersPool",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "20"),
@HystrixProperty(name = "maxQueueSize", value = "50")
}
)
public UserWithOrders getUserWithOrders(Long id) {
User user = getUser(id);
// 调用订单服务获取用户订单
List<Order> orders = restTemplate.getForObject("http://localhost:8082/orders/user/" + id, List.class);
return new UserWithOrders(user, orders);
}
public UserWithOrders getUserWithOrdersFallback(Long id) {
User user = getUser(id);
return new UserWithOrders(user, Arrays.asList());
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class User {
private Long id;
private String name;
private String email;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Order {
private Long id;
private Long userId;
private String product;
private Double amount;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class UserWithOrders {
private User user;
private List<Order> orders;
}
订单服务
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@SpringBootApplication
@EnableCircuitBreaker
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/orders/user/{userId}")
public List<Order> getOrdersByUserId(@PathVariable Long userId) {
return orderService.getOrdersByUserId(userId);
}
}
@Service
public class OrderService {
private List<Order> orders = Arrays.asList(
new Order(1L, 1L, "iPhone", 999.99),
new Order(2L, 1L, "MacBook", 1999.99),
new Order(3L, 2L, "iPad", 799.99),
new Order(4L, 3L, "Apple Watch", 399.99)
);
@HystrixCommand(
fallbackMethod = "getOrdersByUserIdFallback",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")
}
)
public List<Order> getOrdersByUserId(Long userId) {
// 模拟延迟
try {
Thread.sleep((long) (Math.random() * 1500));
} catch (InterruptedException e) {
e.printStackTrace();
}
return orders.stream()
.filter(order -> order.getUserId().equals(userId))
.collect(Collectors.toList());
}
public List<Order> getOrdersByUserIdFallback(Long userId) {
return Arrays.asList();
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Order {
private Long id;
private Long userId;
private String product;
private Double amount;
}
配置文件
用户服务配置
spring.application.name=user-service
server.port=8081
# Hystrix配置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
hystrix.command.default.circuitBreaker.enabled=true
hystrix.command.default.circuitBreaker.requestVolumeThreshold=5
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
# 线程池配置
hystrix.threadpool.default.coreSize=10
hystrix.threadpool.default.maxQueueSize=100
hystrix.threadpool.default.queueSizeRejectionThreshold=50
# 特定命令配置
hystrix.command.getUserWithOrders.execution.isolation.thread.timeoutInMilliseconds=2000
# 特定线程池配置
hystrix.threadpool.userWithOrdersPool.coreSize=20
hystrix.threadpool.userWithOrdersPool.maxQueueSize=50
# Actuator配置
management.endpoints.web.exposure.include=*
订单服务配置
spring.application.name=order-service
server.port=8082
# Hystrix配置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
hystrix.command.default.circuitBreaker.enabled=true
hystrix.command.default.circuitBreaker.requestVolumeThreshold=5
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
# Actuator配置
management.endpoints.web.exposure.include=*
常见问题
1. 如何处理Hystrix线程池饱和?
Hystrix线程池饱和时,新的请求会被拒绝。可以通过以下方式解决:
- 增加线程池大小:
hystrix.threadpool.userThreadPool.coreSize=30
- 增加队列大小:
hystrix.threadpool.userThreadPool.maxQueueSize=200
hystrix.threadpool.userThreadPool.queueSizeRejectionThreshold=100
- 使用信号量隔离代替线程池隔离:
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "100")
}
)
public String method() {
// 方法实现
}
2. 如何优化Hystrix性能?
调整超时时间:根据实际服务响应时间设置合理的超时时间
使用请求缓存:对于相同的请求,使用缓存减少重复调用
使用请求合并:合并多个请求为一个批量请求
调整断路器阈值:根据实际流量设置合理的请求阈值和错误百分比
选择合适的隔离策略:
- 线程池隔离:适用于可能阻塞的I/O操作
- 信号量隔离:适用于非阻塞、快速返回的操作
3. Hystrix与Spring Cloud其他组件如何集成?
- Hystrix与Feign集成:
feign.hystrix.enabled=true
- Hystrix与Ribbon集成:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
ribbon.ReadTimeout=5000
ribbon.ConnectTimeout=2000
- Hystrix与Zuul集成:
zuul.routes.user-service.path=/users/**
zuul.routes.user-service.serviceId=user-service
zuul.ribbon-isolation-strategy=THREAD
zuul.thread-pool.use-separate-thread-pools=true
总结
本文详细介绍了Hystrix的高级特性和配置选项:
- ✅ 高级配置:命令配置、线程池配置、信号量配置和全局配置
- ✅ 请求缓存:减少对依赖服务的重复调用
- ✅ 请求合并:将多个请求合并成一个批量请求
- ✅ 自定义钩子:通过自定义并发策略等扩展Hystrix功能
- ✅ 监控方案:使用Hystrix Dashboard和Turbine监控系统运行状态
通过这些高级特性,我们可以更精细地控制Hystrix的行为,构建更加健壮和高性能的微服务系统。
下一步学习
- 学习Hystrix的最佳实践和设计模式
- 了解Hystrix与Spring Cloud其他组件的集成
- 探索Hystrix的替代方案,如Resilience4j
希望这个教程对您有所帮助!如果您有任何问题,欢迎在评论区讨论。