3.SpringCloud 熔断器-Hystrix

Hystrix 基本介绍与Hystrix服务搭建

Hystrix 叫做断路器/熔断器。微服务系统中,整个系统出错的概率非常高,因为涉及到的模块太多了。当某一个模块无法正常工作时,希望通过配置一些服务熔断的措施,来保证整个系统正常运行。

1. 添加相关依赖 Web、Eureka Client、Hystrix

<dependencies>
	<!-- SpringBoot Web 依赖 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<!-- Eureka-Client 依赖 -->
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
	</dependency>
	<!-- Hystrix 依赖 -->
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
	</dependency>
</dependencies>

<dependencyManagement>
	<dependencies>
		<!-- SpringCloud 版本控制依赖 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>${spring-cloud.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

2. 将 Hystrix服务 注册到 Eureka 上

spring.application.name=hystrix
server.port=3000
eureka.client.service-url.defaultZone=http://localhost:1111/eureka

3. 在项目启动类上添加 @EnableCircuitBreaker 注解,开启断路器,同时提供一个 RestTemplate 实例

@SpringBootApplication
@EnableCircuitBreaker
public class HystrixApplication {
    public static void main(String[] args) {
        SpringApplication.run(HystrixApplication.class, args);
   	}
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
   	}
}

启动类上的两个注解,也可以使用 @SpringCloudApplication 代替,这是一个组合注解。

Hystrix 服务熔断/服务降级

服务熔断:当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。

服务降级:当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务或页面不进行处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。

Hystrix 服务熔断/降级示例:

HelloService中 在方法上 添加 @HystrixCommand 注解,配置 fallbackMethod 属性即可:

@Service
public class HelloService {
    @Autowired
    RestTemplate restTemplate;
    /**
     * 在这个方法中,远程调用 provider 中提供的 /hello 接口
     * 添加 @HystrixCommand 注解,配置 fallbackMethod 属性,表示该方法调用失败时的临时替代方法
     * @return
     */
    @HystrixCommand(fallbackMethod = "error")
    public String hello() {
        return restTemplate.getForObject("http://provider/hello", String.class);
   	}
    /**
     * 注意,临时替代方法的 方法名称和返回值 要与 fallbackMethod 配置中保持一致
     * @return
     */
    public String error() {
        return "error";
   	}
}
@RestController
public class HelloController {
    @Autowired
    HelloService helloService;
    @GetMapping("/hello")
    public String hello() {
        return helloService.hello();
   	}
}

Hystrix 请求异步调用

定义方法的返回值类型为 Future<T>,返回AsyncResult对象,重写invoke()方法

@HystrixCommand(fallbackMethod = "error")
public Future<String> hello2() {
    return new AsyncResult<String>() {
        @Override
        public String invoke() {
            return restTemplate.getForObject("http://provider/hello", String.class);
       	}
   	};
}
@GetMapping("/hello2")
public void hello2() {
    Future<String> hello2 = helloService.hello2();
    try {
        String s = hello2.get();
        System.out.println(s);
   	} catch (InterruptedException e) {
        e.printStackTrace();
   	} catch (ExecutionException e) {
        e.printStackTrace();
   	}
}

Hystrix 异常处理

当发起服务调用时,consumer 中抛出了异常,也会自动进行服务降级,我们还可以获取异常的原因。

上述/hello接口中,定义一个异常情况,通过Throwable 参数获取异常的原因

@HystrixCommand(fallbackMethod = "error")
public String hello() {
	int i = 1 / 0;
    return restTemplate.getForObject("http://provider/hello", String.class);
}
public String error(Throwable t) {
  return "error:" + t.getMessage();
}

另一种情形:如果抛异常了,我们希望异常直接抛出,不要进行服务降级,那么只需要配置忽略某一个异常即可,配置ignoreExceptions属性

@HystrixCommand(fallbackMethod = "error",ignoreExceptions=ArithmeticException.class)
public String hello3() {
    int i = 1 / 0;
    return restTemplate.getForObject("http://provider/hello", String.class);
}

Hystrix 请求缓存

  • @CacheResult,表示该方法的请求结果会被缓存起来

请求缓存就是在 consumer 中调用同一个接口,如果参数相同,则可以使用之前缓存下来的数据。

1.首先在 provider 中添加 /testCache 接口,一会用来检测缓存配置是否生效:

@GetMapping("/testCache")
public String testCache(String name) {
    System.out.println(new Date() + ">>>" + name);
    return "hello " + name; 
}

2.在 hystrix 的请求方法中,添加 @CacheResult 注解

@HystrixCommand(fallbackMethod = "error")
@CacheResult
// @CacheResult表示该方法的请求结果会被缓存起来,默认情况下,缓存的 key 就是方法的参数,
// 缓存的 value 就是方法的返回值。
public String testCache(String name) {
    return restTemplate.getForObject("http://provider/testCache?name={1}", String.class, name);
}

3.初始化 HystrixRequestContext,初始化完成后,缓存开始生效,HystrixRequestContext 关闭之后,缓存失效。

@GetMapping("/testCache")
public void testCache() {
	// 缓存开始生效
    HystrixRequestContext ctx = HystrixRequestContext.initializeContext();
    String s= helloService.testCache("zhangsan");
    s= helloService.testCache("zhangsan");
    // 缓存失效
    ctx.close();
}

这里调用了两遍testCache方法,但是provider中只会输出一次信息,因为第二次是缓存数据。

默认情况下,缓存的 key 就是所调用方法的参数,如果参数有多个,就是多个参数组合起来作为key

@HystrixCommand(fallbackMethod = "error")
@CacheResult
public String testCache2(String name, Integer age) {
    return restTemplate.getForObject("http://provider/testCache?name={1}&age={2}", 
String.class, name, age);
}

此时缓存的 key 就是 name+age,但是,如果有多个参数,又只想使用其中一个作为缓存的 key,那么可以通过 @CacheKey 注解来指定缓存的key

@HystrixCommand(fallbackMethod = "error")
@CacheResult
public String testCache2(@CacheKey String name, Integer age) {
    return restTemplate.getForObject("http://provider/testCache?name={1}&age={2}", 
String.class, name, age);
}

此时虽然有两个参数,但是缓存时以 name 作为缓存的key。也就是说,两次请求中,只要 name 一
样,即使 age 不一样,第二次请求也会使用第一次请求缓存的结果。

  • @CacheRemove,表示删除缓存的数据

@CacheRemove在使用时,必须指定 commandKey 属性:缓存方法的名字

例如如下方法定义缓存与删除缓存:

@HystrixCommand(fallbackMethod = "error")
@CacheResult
// @CacheResult表示该方法的请求结果会被缓存起来,默认情况下,缓存的 key 就是方法的参数,
// 缓存的 value 就是方法的返回值。
public String testCache(String name) {
    return restTemplate.getForObject("http://provider/testCache?name={1}", String.class, name);
}
@HystrixCommand
@CacheRemove(commandKey = "testCache")
// 删除缓存,必须指定 commandKey 属性:缓存方法的名字
public String deleteUserByName(String name) {
    return null; 
}
@GetMapping("/testCache2")
public void testCache2() {
	// 缓存开始生效
    HystrixRequestContext ctx = HystrixRequestContext.initializeContext();
    //第一请求完,数据已经缓存下来了
    String javaboy = helloService.testCache("zhangsan");
    //删除数据,同时缓存中的数据也会被删除
    helloService.deleteUserByName("zhangsan");
    //第二次请求时,虽然参数还是 zhangsan,但是缓存数据已经没了,所以这一次,provider 还是会收到请求
    javaboy = helloService.testCache("zhangsan");
    // 缓存失效
    ctx.close();
}

Hystrix 请求合并

如果 consumer 中,频繁的调用 provider 中的同一个接口,在调用时,只是参数不一样,那么这样情况下,就可以将多个请求合并成一个,这样可以有效提高请求发送的效率。

添加 @HystrixCollapser 注解,batchMethod指定批处理的方法,collapserProperties指定请求合并的条件属性

@Service
public class UserService {
    @Autowired
    RestTemplate restTemplate;
    
    @HystrixCollapser(batchMethod = "getUsersByIds",collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds",value = "200")})
    public Future<User> getUserById(Integer id) {
        return null;
   	}
   	
    @HystrixCommand
    public List<User> getUsersByIds(List<Integer> ids) {
        User[] users = restTemplate.getForObject("http://provider/user/{1}", User[].class, StringUtils.join(ids, ","));
        return Arrays.asList(users);
   	}
}

测试示例:

@GetMapping("/testRequestMerge")
public void testRequestMerge() throws ExecutionException, InterruptedException {
    HystrixRequestContext ctx = HystrixRequestContext.initializeContext();
    Future<User> q1 = userService.getUserById(99);
    Future<User> q2 = userService.getUserById(98);
    Future<User> q3 = userService.getUserById(97);
    User u1 = q1.get();
    User u2 = q2.get();
    User u3 = q3.get();
    System.out.println(u1);
    System.out.println(u2);
    System.out.println(u3);
    Thread.sleep(2000);
    Future<User> q4 = userService.getUserById(96);
    User u4 = q4.get();
    System.out.println(u4);
    ctx.close();
}

参考文章:http://itboyhub.com/2021/01/31/spring-cloud-hystrix/

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值