Spring Cloud(四)断路器Hystrix
新的一年开始了,先在这里祝各位朋友新年快乐、猪年大吉。新的一年可以拿到自己心仪的offer,开到满意的工资。总而言之,就是升职加薪,迎娶白富美,走上人生巅峰!
回归正题,前面三章,分别讲了Eureka注册中心,Ribbon服务消费、Feign声明式调用,这节课主要介绍Hystrix断路器。在讲这个之前,不知道大家听说过“雪崩”效应没?
雪崩效应
为了保证高可用,单个服务通常会以集群的方式进行部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,则调用方不能得到及时响应。当并发量大时,容器资源会被消耗殆尽,导致服务瘫痪。又因为服务之间存在依赖、调用的关系,所以,故障在服务之间进行传播。对整个微服务架构造成严重的后果,这就是服务故障所引起的“雪崩”效应。
熔断、降级
很多人觉得,自己接触的项目,访问量不会有那么大,服务器不会发生故障,也不会出现高并发。但是,谁又能保证,服务器不会发神经,网络不会出现问题,出现宕机的情况呢?当服务器宕机,无法正常提供服务?服务之间的调用不就出现问题了吗?此时,就要说到Hystrix了,它能为上面出现的问题,提供相应的解决方案:熔断、降级、熔断和降级互相交集。
那么什么是熔断?什么是降级呢?
一、服务降级:
系统的整体资源匮乏,为了使系统正常运转,就需要关闭某些服务,等到资源充足,再将服务开启。
二、服务熔断
当服务出现了异常,但是,还是会有大量请求调用这个服务,从而浪费大量的系统资源。为了保护整个系统不因单个服务的异常而发生故障,这个时候,就需要使用熔断器,来阻止外来请求的访问,防止系统资源被耗尽。比如:家中的电闸,当电流过大的时候,出问题,电闸就会自动断开,保护用电器,防止用电器烧坏。
实战
在Ribbon中使用断路器
项目是在前面的订单和用户服务的基础上进行改造。
首先在order-service的pom文件中添加相应的约束文件。
<!-- 断路器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在启动类上加入@EnableCircuitBreaker注解,表示开启断路器Hystrix。
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
/**
* 标记是负载均衡
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
因为这里出现了很多注解,所以Spring人性化的提供了新的注解,来代替上面的注解。它就是
@SpringCloudApplication
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.cloud.client;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}
通过源码,可以看到,@SpringCloudApplication 是一个注解的集合,其中包含:
@SpringBootApplication:表明这是一个SpringBoot应用。
@EnableDiscoveryClient:表明将服务注册到Eureka,让注册中心可以扫描到此服务。
@EnableCircuitBreaker:是断路器的注解,表明开启Hystrix。
接下来,需要对业务层进行修改。在save()方法上加上@HystrixCommand注解,表示该方法使用熔断器的功能,并指定了fallback方法。
package com.root.project.orderservice.service.impl;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.root.project.orderservice.pojo.Order;
import com.root.project.orderservice.service.OrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* @ClassName: OrderServiceImpl
* @Author: 清风一阵吹我心
* @Description: TODO 订单服务实现类
* @Date: 2019/1/18 16:15
* @Version 1.0
**/
@Service
public class OrderServiceImpl implements OrderService {
private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class);
/**
* url:服务名称+接口名称
*/
private static final String USER_REQ_URL = "http://user-service/api/v1.0/user/{userId}";
@Autowired
private RestTemplate restTemplate;
/**
* 注意,这里要设置Hystrix的超时时间,默认时间是一秒
* 服务端方法响应超过1秒将会触发降级
*
* @param userId
* @return
*/
@Override
@HystrixCommand(fallbackMethod = "saveError")
public Order save(Long userId) {
Object object = restTemplate.getForObject(USER_REQ_URL, Object.class, userId);
LOGGER.info("object:{}", object);
Order order = new Order();
order.setObject(object);
return order;
}
public Order saveError(Long userId) {
Order order = new Order();
String msg = "请求超时,服务发生异常!";
order.setObject(msg);
return order;
}
}
注意:在定义fallbackMethod时,必须保证熔断方法和本身的save()方法一致(签名一定要和api方法一致,对应的参数的个数也要一致)。伪代码没有那么严谨,主要是为了展示效果。希望大家可以理解。
然后启动注册中心、用户服务、订单服务。访问:http://localhost:8901/api/v1.0/order?userId=3 出现了让人难以接受的东东。
这里疑惑的是,我的用户服务明明正常,但是为什么访问走的却是fallbackMethod的方法。然后根据相关资料了解到,Hystrix的超时时间,默认①秒,服务端方法响应超过①秒将会触发降级。然后,打开Google浏览器看响应时长。
确实是超过了①秒。当然并不是所有人都会遇到这种情况。所以,出现了问题,就要解决。以下提供两种解决办法。
①、修改配置文件。
#1.默认
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000
#2.配置具体方法的超时时间
#hystrix:
# command:
# serverMethod:
# execution:
# isolation:
# thread:
# timeoutInMilliseconds: 3000
设置Hystrix的超时时间为5秒。重启订单服务。再次访问:http://localhost:8901/api/v1.0/order?userId=3
看到了想要的结果。
②、通过@HystrixCommand注解,增加子属性@HystrixProperty来解决。
上面使用了@HystrixCommand注解,来表明某个方法使用熔断器的功能。同时,还可以指明它的超时时间。
/**
* 注意,这里要设置Hystrix的超时时间,默认时间是一秒
* 服务端方法响应超过1秒将会触发降级
*
* @param userId
* @return
*/
@Override
@HystrixCommand(fallbackMethod = "saveError", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
})
public Order save(Long userId) {
Object object = restTemplate.getForObject(USER_REQ_URL, Object.class, userId);
LOGGER.info("object:{}", object);
Order order = new Order();
order.setObject(object);
return order;
}
这里就只对save方法上的注解进行了修改,所以就贴了部分代码,其余的代码不变。可以看到括号中的(“execution.isolation.thread.timeoutInMilliseconds”)实际上和配置文件的配置相似。大致都是对Hystrix的超时时间进行修改。
重启服务,访问:http://localhost:8901/api/v1.0/order?userId=1
看到了正常的响应。
在Feign中使用断路器
项目是在Feign博文中的管理员服务和用户服务进行改造。如有不明白的朋友,请看前面的几篇博文。
Feign默认集成了断路器,所以不用导入相关的约束。在老的本版中,默认打开断路器。但是新版本中需要进行配置,来开启。修改Admin-service的yml文件。
feign:
#开启Feign对Hystrix的支持
hystrix:
enabled: true
加入上面的配置后,对以前的UserFeignClient进行修改。加上fallback,后面指定一个实现类,表明发生异常的处理方法。
package com.root.project.adminservice.service;
import com.root.project.adminservice.service.impl.UserFeignClientFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @ClassName: UserClient
* @Author: 清风一阵吹我心
* @Description: TODO 加入注解,指定需要调用的服务名称
* @Date: 2019/1/27 15:13
* @Version 1.0
**/
@FeignClient(name = "user-service",fallback = UserFeignClientFallBack.class)
public interface UserFeignClient {
/**
* 调用用户接口,查询用户信息,让其返回字符串
* @param userId
* @return
*/
@GetMapping("/api/v1.0/user/{userId}")
String findUserById(@PathVariable("userId")Long userId);
}
然后,实现这个接口,编写实现类UserFeignClientFallBack
package com.root.project.adminservice.service.impl;
import com.root.project.adminservice.service.UserFeignClient;
import org.springframework.stereotype.Component;
/**
* @ClassName: UserFeignClientFallBack
* @Author: 清风一阵吹我心 @component注解不加 启动会报错
* @Description: TODO
* @Date: 2019/2/11 16:13
* @Version 1.0
**/
@Component
public class UserFeignClientFallBack implements UserFeignClient {
@Override
public String findUserById(Long userId) {
String str = "不好意思,用户id为" + userId + "的服务调用超时";
return str;
}
}
注意,这里使用了@Component注解,将这个类加入到IOC容器中。为了能直观的反映调用结果,还对AdminServiceImpl进行了修改。
@Service
public class AdminServiceImpl implements AdminService {
private static final Logger LOGGER = LoggerFactory.getLogger(AdminServiceImpl.class);
@Resource
private UserFeignClient userFeignClient;
@Override
public String save(Long uId) {
String result = userFeignClient.findUserById(uId);
return result;
}
}
让它返回字符串。然后启动注册中心、管理员服务、用户服务。访问:http://localhost:9100/api/v1.0/admin?userId=1
果然没让我失望。同样的结果,又出现了一次。不用我说,根据前面的教训,应该就是要配置Hystrix的超时时间了。直接使用配置文件进行配置。
#hystrix的熔断机制
hystrix:
command:
#default全局有效,service id 指定应用有效
default:
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制,为true,则超时作为熔断根据
enabled: true
isolation:
thread:
#断路器超时时间,默认1000ms
timeoutInMilliseconds: 3000
设置完后,重启服务,进行访问,又出现了相同的问题。这下我有些迷茫了,为什么该配置的都配置了,还是出现问题呢?那是因为,这里使用的是Feign。
上一篇博文讲了Feign默认集成了Ribbon。而使用Feign调用接口,分为两层,Ribbon的调用和Hystrix的调用。所以Ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间。所以,这里需要对Feign中的Ribbon进行超时时间的配置。修改yml文件。
yml最终版本
feign:
#开启Feign对Hystrix的支持
hystrix:
enabled: true
#设置调用超时时间
client:
config:
default:
connectTimeout: 1000
readTimeout: 2000
#hystrix的熔断机制
hystrix:
command:
#default全局有效,service id 指定应用有效
default:
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制,为true,则超时作为熔断根据
enabled: true
isolation:
thread:
#断路器超时时间,默认1000ms
timeoutInMilliseconds: 3000
然后再重启服务,访问相应的接口。
得到了正确的响应。
以上就是断路器Hystrix的使用,如果上面有什么问题或者哪里出了错,欢迎各位朋友指出。新的一年,大家共同进步,完成自己的梦想。
做一只有梦想的咸鱼