✅作者简介:大家好,我是 Meteors., 向往着更加简洁高效的代码写法与编程方式,持续分享Java技术内容。
🍎个人主页:Meteors.的博客
💞当前专栏: 深度学习
✨特色专栏: 知识分享
🥭本文内容:【2.1】Java微服务:详解Hystrix
📚 ** ps ** : 阅读这篇文章如果有问题或者疑惑,欢迎各位在评论区提问或指出!
----------------------------------------------------- 目录 ---------------------------------------------------------
目录
[1. 基本介绍](#1. 基本介绍)
[2. 实现原理](#2. 实现原理)
[1. 请求缓存](#1. 请求缓存)
1) 导入依赖 导入依赖)
2) 添加配置文件 添加配置文件)
3) 增加Redis 配置 增加Redis 配置)
5) 接口实现类增加缓存注解 接口实现类增加缓存注解)
[2. 请求合并实现](#2. 请求合并实现)
1). pom文件添加依赖. pom文件添加依赖)
2). 在实现类进行请求合并. 在实现类进行请求合并)
[4. 信号量隔离](#4. 信号量隔离)
3) 线程池颗粒与信号隔离对比 线程池颗粒与信号隔离对比)
- ------------------------------------------------------------------------------------ --------------------------------------------
一、基本介绍
1. 基本介绍
Hystix是一个延迟和容错库,旨在隔离对远程系统、服务和第三方库的访问点,停止级联故障,并在不可避免发生故障的复杂分布式系统中实现快速恢复。主要靠Spring的AOP实现
2. 实现原理
- 正常情况下,断路器关闭,服务消费者正常请求微服务
- 一段时间内,失败率达到一定阈值,断路器将断开,此时不在请求服务提供者,而只是快速失败的方法(断路方法)
- 断路器打开一段时间,自动进入"半开"状态,此时,断路器可允许一个请求方法服务提供者,如果请求调用成功,则关闭断路器,否则将保持断路器打开状态
断路器hystrix是保证了局部发生的错误,不会错扩展到整个系统, 从而保证系统 的即便出现局部问题也不会造成系统雪崩
二、相关功能
1. 请求缓存
概述
Hystirx为了降低访问服务的评率,支持将一个q8ingqiu与返回接口做缓存处理。如果再次请求的URL没有变化,nameHystrix不会请求服务,而是直接从请求中将结果放回。这样可以大大降低访问服务的压力。
缺点
- 本地缓存,集群模式下缓存无法同步
- 不支持第三方缓存容器,如Redis、MemCache
(现在一般都是使用Redis集成方案)
Redis的方式
1) 导入依赖
<!-- spring boot data redis 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- commons-pool2 对象池依赖 1 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
2) 添加配置文件
# redis缓存 redis: timeout: 10000 #设置连接超时时间 host: 127.0.0.1 port: 6379 database: 0 lettuce: pool: max-active: 8 # 最大连接数,默认8 max-wait: 10000 # 最大连接阻塞时间,单位毫秒,默认-1 max-idle: 200 #最大空闲连接,默认8 min-idle: 5 #最小空闲连接默认 0
3) 增加Redis 配置
javapackage cn.itmeteors.order.config; /** * Redis 配置类 */ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; @Configuration public class RedisConfig { // 重写 RedisTemplate 序列化 @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); // 为 String 类型 key 设置序列化器 template.setKeySerializer(new StringRedisSerializer()); // 为 String 类型 value 设置序列化器 template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // 为 Hash 类型 key 设置序列化器 template.setHashKeySerializer(new StringRedisSerializer()); // 为 Hash 类型 value 设置序列化器 template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } // 重写 Cache 序列化 @Bean public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory()); RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() // 设置默认过期时间 30 min .entryTtl(Duration.ofMinutes(30)) // 设置 key 和 value 的序列化 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getKeySerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())); return new RedisCacheManager (redisCacheWriter, redisCacheConfiguration); } }
4)增加缓存注解
5) 接口实现类增加缓存注解
java@Cacheable(cacheNames = "orderService:order:select") public Order selectOrderById(Long orderId) { // 调用list // 1.查询订单 Order order = orderMapper.findById(orderId); // 2.利用RestTemplate发起http请求,查询用户 // 2.1.url路径 String url = "http://userservice/user/list"; // 2.2.发送http请求,实现远程调用 List<User> userList = restTemplate.getForObject(url, List.class); assert userList != null; order.setUser(userList); return order; }
6)结果
2. 请求合并实现
1). pom文件添加依赖
<!-- spring-cloud netflix hystrix 依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2). 在实现类进行请求合并
3)开启熔断注解
java@EnableCaching // 开启熔断器注解 2 选 1,@EnableHystrix 封装了 @EnableCircuitBreaker // @EnableHystrix @EnableCircuitBreaker @EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class) public class OrderApplication {
4)模拟同时发起多个请求
javapublic Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // 2.利用RestTemplate发起http请求,查询用户 // 2.1.url路径 String url = "http://userservice/user/1"; String url2 = "http://userservice/user/2"; String url3 = "http://userservice/user/3"; String url4 = "http://userservice/user/4"; String url5 = "http://userservice/user/5"; // 2.2.发送http请求,实现远程调用 Future<User> user = (Future<User>) restTemplate.getForObject(url, User.class); Future<User> user2 = (Future<User>) restTemplate.getForObject(url2, User.class); Future<User> user3 = (Future<User>) restTemplate.getForObject(url3, User.class); Future<User> user4 = (Future<User>) restTemplate.getForObject(url4, User.class); Future<User> user5 = (Future<User>) restTemplate.getForObject(url5, User.class); // 3.封装user到Order List<User> userList = new ArrayList<>(1); try { userList.add(user.get()); userList.add(user2.get()); userList.add(user3.get()); userList.add(user4.get()); userList.add(user5.get()); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e); } order.setUser(userList); // 4.返回 return order; }
3.线程池隔离
1)介绍
对调用的接口进行隔离,一个接口因为并发过高瘫痪时,掉用的另一个接口不会瘫痪
2)优点
使用线程池隔离可以安全**「隔离依赖的服务」**,减少所依赖服务发生故障时的影响面。比如 A 服务发生异常,导致请求大量超时,对应的线程池被打满,这时并不影响 C、D 服务的调用。
当失败的服务再次变得可用时,线程池将清理并立即恢复,而不需要一个长时间的恢复。
独立的线程池**「提高了并发性」**。
3)缺点
请求在线程池中执行,肯定会带来任务调度、排队和上下文切换带来的 CPU 开销。
因为涉及到跨线程,那么就存在 ThreadLocal 数据的传递问题,比如在主线程初始化的 ThreadLocal 变量,在线程池线程中无法获取。
4)代码示例
java// 声明需要服务容错的方法 // 线程池隔离 @HystrixCommand(groupKey = "order-userService-listPool",// 服务名称,相同名称使用同一个线程池 commandKey = "getList",// 接口名称,默认为方法名 threadPoolKey = "order-userService-listPool",// 线程池名称,相同名称使用同一个线程池 commandProperties = { // 超时时间,默认 1000ms @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000") }, threadPoolProperties = { // 线程池大小 @HystrixProperty(name = "coreSize", value = "6"), // 队列等待阈值(最大队列长度,默认 -1) @HystrixProperty(name = "maxQueueSize", value = "100"), // 线程存活时间,默认 1min @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"), // 超出队列等待阈值执行拒绝策略 @HystrixProperty(name = "queueSizeRejectionThreshold", value = "100") }, fallbackMethod = "selectUserListFallback") public List<User> getList() { return userMapper.getAll(); } // 托底数据 private List<User> selectUserListFallback() { System.out.println("-----获得托底数据-----"); return Arrays.asList( new User(1L, "A", "地点1"), new User(2L, "B", "地点2"), new User(3L, "C", "地点3") ); }
4. 信号量隔离
1)介绍
每次调用线程,当前请求通过计数信号量进行限制,当信号量大于了最大请求数
maxConcurrentRequests
时,进行限制,调用fallback
接口快速返回。信号量的调用是同步的,也就是说,每次调用都得阻塞调用方的线程,直到结果返回。这样就导致了无法对访问做超时(只能依靠调用协议超时,无法主动释放)2)代码示例
java// 声明需要服务容错的方法 // 信号量隔离 @HystrixCommand(commandProperties = { // 超时时间,默认 1000ms @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"), // 信号量隔离 @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"), // 信号量最大并发,调小一些方便模拟高并发 @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "6") }, fallbackMethod = "selectUserListFallback") public List<User> getList() { return userMapper.getAll(); } // 托底数据 private List<User> selectUserListFallback() { System.out.println("-----获得托底数据-----"); return Arrays.asList( new User(1L, "A", "地点1"), new User(2L, "B", "地点2"), new User(3L, "C", "地点3") ); }
3) 线程池颗粒与信号隔离对比
隔离方式 是否支持超时 是否支持熔断 隔离原理 是否是异步调用 资源消耗 线程池隔离 支持 支持 每个服务单独用线程池 支持同步或异步 大 信号量隔离 不支持 支持 通过信号量的计数器 同步调用,不支持异步 小
5.服务熔断
1)介绍
服务熔断是一种机制,用于在出现服务故障、超时或异常情况时,阻止请求继续发送到故障的服务上。当达到一定的失败阈值时,熔断器会打开,后续的请求将被快速失败,而不再去调用故障的服务。当故障情况得到修复后,熔断器会尝试关闭,恢复对服务的正常调用。
2)调用
在上面的代码执行fallbackMethod就会执行服务熔断
其他
除了上述功能,Hystrix还有实时监控、指标收集、恢复等功能,这里会留到后面的时候进行更新,大家如果现在要学习的话,可以从官网或其它地方得到相关的介绍和代码
参考文献:
最后,
后续文章会陆续更新,希望文章对你有所帮助..!