在Spring Cloud 中,Hystrix 主要用在被调用方,服务A调用服务B,如果服务A调用不同服务B是,及时回退信息给用户,防止因为超时问题引起更多的问题至服务不可用。
本次演示需要先将前面的ribbon-server启动起来,用于提供服务,也就是上图的服务B,现在我们需要自己开发服务A。
新建项目模块cloud-hystrix-ribbon,引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
启动类:
@EnableHystrix
@EnableEurekaClient
@SpringBootApplication
public class HystrixRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixRibbonApplication.class, args);
}
}
和前面一样实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
/**
* 主键Id
*/
private long id;
/**
* 用户名
*/
private String username;
/**
* 昵称
*/
private String rolename;
/**
* 邮箱
*/
private String email;
/**
* 备注
*/
private String remark;
}
创建Ribbon调用类:
这里需要注意我们在远程调用的方法上添加了@HystrixCommand(fallbackMethod = "findUserByIdFailure"),也就是说如果调用失败会执行findUserByIdFailure方法
@Service
@Slf4j
public class EurekaHystrixRibbonService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "findUserByIdFailure")
public User findUserById(Long id) {
// http://服务提供者的serviceId/url
return restTemplate.getForObject("http://ribbon-server/user/" + id, User.class);
}
/**
* 服务feign-server/user/id 调用失败的回调方法
*
* @return
*/
public User findUserByIdFailure(Long id) {
return new User(id, null, null, null, "网络繁忙,请稍后再试");
}
}
Ribbon 基本配置
这里添加了一个getServlet()方法,用于后面Hystrix dashboard,见后面。
@Configuration
public class EurekaRibbonConfig {
@Bean
@LoadBalanced // 实现负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public IRule ribbonRule() {
//默认ZoneAvoidanceRule请求,实现自定义的规则
//自定义成随机
return new RandomRule();
}
/**
* 解决dashboard显示 Unable to connect to Command Metric Stream
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
控制层:
@RestController
public class EurekaHystrixController {
@Resource
private EurekaHystrixRibbonService eurekaRibbonService;
/**
* 根据id获取用户信息
*
* @param id
* @return
*/
@GetMapping("/user/{id:\\d+}")
public User findUserById(@PathVariable long id) {
return eurekaRibbonService.findUserById(id);
}
}
应用配置
server:
port: 8675
spring:
application:
name: hystrix-ribbon
eureka:
client:
service-url:
defaultZone: http://xunfei:12345@127.0.0.1:8761/eureka/
启动Eureka和cloud-ribbon-server再启动当前项目,postman测试127.0.0.1:8675/user/1
下面我们将ribbon-server关掉,再测试,可以看到返回就是findUserByIdFailure设置的描述
除了指定fallbackMethod以外,还有很多属性可以配置,下图是@HystrixCommand的全部属性
例如
指定回退方法和默认方法都是findUserByIdFailure,也可以不同,指定超时时间100毫秒,线程池大小为1,忽略Exception触发的回退等,具体的属性,根据字面意思应该没问题。一般我们也用不到这么多属性
@HystrixCommand(fallbackMethod = "findUserByIdFailure"
, groupKey = "MyGroup"
, commandKey = "MyCommandKey"
, threadPoolKey = "MyThreadPool"
, commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "100")}
, threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "1")}
, ignoreExceptions = {Exception.class}
, observableExecutionMode = ObservableExecutionMode.EAGER
, raiseHystrixExceptions = {HystrixException.RUNTIME_EXCEPTION}
, defaultFallback = "findUserByIdFailure"
)
对于一些默认的配置,可以使用@DefaultProperties,可以减少@HystrixCommand中的代码量,如下
@Service
@Slf4j
@DefaultProperties(groupKey = "MyGroup")
public class EurekaHystrixRibbonService {
@Autowired
RestTemplate restTemplate;
......
在上一章原生使用中已经讲过了Hystrix的缓存,我们现在在Spring Cloud 篇也实现一个缓存的功能。
创建缓存我们需要创建一个实现java.servlet.Filter类的过滤器,并且需要初始化请求上下文,跟入门篇一样。这次可以减少每次创建上下文的代码量。
新建过滤器
这里我们对全部的url进行过滤,因此配置了/*
@WebFilter(urlPatterns = "/*",filterName = "hystrixFilter")
@Component
public class HystrixFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
filterChain.doFilter(servletRequest,servletResponse);
}finally {
context.shutdown();
}
}
}
对于接口请求的改造只需要加一个@CacheResult的注解,注意需要配合@HystrixCommand一起使用。
重启项目将ribbon-server启动起来,并且重启当前hystrix项目,侯然Postman 测试接口127.0.0.1:8675/user/1
可以看到,第一次请求之后,同样的请求就走缓存了,后面不会再调用接口了。
缓存其它注解
同样的问题,缓存不能一直不变的,如果数据发生变动,缓存里的数据要可以更新或者删除,否则就会产生脏数据。
可以使用@CacheRemove移除缓存 、@CacheKey修饰方法参数
同样上一章中我们已经讲过请求合并,Spring Cloud 中也同样支持请求合并,并且Spring Cloud 提供了一个注解,非常方便处理合并请求。
为了方便演示合并请求,我们需要在ribbon-server里添加一个接收方法,用于接收id的集合。
新建ribbon-server的controller方法
@GetMapping("/list")
public List<User> getUserById(String ids, HttpServletRequest req){
log.info("ids,{}",ids);
List<User> list=new ArrayList<>();
String[] splitIds = ids.split(",");
for (String id : splitIds) {
String url = req.getRequestURL().toString();
User user = userService.getUserById(Long.valueOf(id));
user.setRemark(user.getRemark()+":提供服务的是:"+url);
list.add(user);
}
return list;
}
在EurekaHystrixRibbonService中新建合并请求方法
在hystrix-ribbon里EurekaHystrixRibbonService类中新建合并请求的方法,在一个单个接收方法中加上@HystrixCollapser注解,用于收集相同请求的数据,然后批量执行。
@HystrixCollapser(batchMethod = "findUsers"
,scope = com.netflix.hystrix.HystrixCollapser.Scope.REQUEST,
collapserProperties = {
@HystrixProperty(name = "timerDelayInMilliseconds",value = "1000")
})
public Future<User> getUserSingle(Long id){
log.info("执行单调调用");
return null;
}
/**
* 请求多个用户数据
* @param ids
* @return
*/
@HystrixCommand(fallbackMethod = "findUsersFailure")
public List<User> findUsers(List<Long> ids){
return restTemplate.getForObject("http://ribbon-server/user/list?ids=" + StringUtils.join(ids, ","), List.class);
}
public List<User> findUsersFailure(List<Long> ids){
log.info("fallback");
List<User> list=new ArrayList<>();
for (Long id : ids) {
list.add(new User(id, null, null, null, "网络繁忙,请稍后再试,请确认手牌"));
}
return list;
}
fallbackMethod里面返回的数量要和@HystrixCollapser里面接收到的一致,否则汇报下面的错误
Caused by: java.lang.RuntimeException: Failed to map all collapsed requests to response. The expected contract has not been respected. Collapser key: 'getUserSingle', requests size: '3', response size: '1'
合并作用域,默认是REQUEST,就是不会跨越多个请求会话的,只在当前用户请求中合并多次请求为批处理请求。这里改成GLOBAL,就是可以跨越request context,合并不同用户的请求为一次批处理请求。
添加EurekaHystrixController控制层方法
这里是模拟,一次请求发出3个调用方法,返回是一个数组。同时,如果失败,也会拿到一个失败的数组。
/**
* 根据id获取用户信息
* @return
*/
@GetMapping("/user/list")
public List<User> findUsers() throws ExecutionException, InterruptedException {
Future<User> userSingle1 = eurekaRibbonService.getUserSingle(1L);
Future<User> userSingle2 = eurekaRibbonService.getUserSingle(2L);
Future<User> userSingle3 = eurekaRibbonService.getUserSingle(3L);
List<User> list=new ArrayList<>();
list.add(userSingle1.get());
list.add(userSingle2.get());
list.add(userSingle3.get());
return list;
}
重启ribbon-server项目和当前项目,然后Postman 测试127.0.0.1:8675/user/list.再将ribbon关掉再次请求,可以看到请求合并成功。
前面我们详细介绍了Hystrix与Ribbon的使用,下面我们来详细说明Hystrix与Feign的使用,Feign本身已经对Hystrix提供了支持,使用时只要打开开关就行了。下面我们新建一个cloud-hystrix-feign项目模块演示。
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
启动类
@EnableFeignClients
@EnableHystrix
@EnableDiscoveryClient
@SpringBootApplication
public class HystrixFeignApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixFeignApplication.class, args);
}
}
应用配置
最主要的是feign.hystrix.enabled: true需要打开,否则Hystrix 不起作用,默认是关闭的。
server:
port: 8676
spring:
application:
name: hystrix-feign
eureka:
client:
service-url:
defaultZone: http://xunfei:12345@127.0.0.1:8761/eureka/
feign:
hystrix:
enabled: true #开启hystrix
新建调用的service
@FeignClient注解的使用和普通的Feign 客户端没什么区别,主要就是加了一个fallback的类,回退的方法需要实现当前service的接口。
@FeignClient(value = "register-eureka-client", fallback = EurekaFeignServiceFailure.class)
public interface EurekaFeignService {
//feign中你可以有多个@RequestParam,但只能有不超过一个@RequestBody
@GetMapping("/hi")
String hello();
}
新建回退类EurekaFeignServiceFailure
@Service
public class EurekaFeignServiceFailure implements EurekaFeignService {
@Override
public String hello() {
return "网络异常,稍后再试,请拿好手牌";
}
}
新建控制层EurekaFeignController
@RestController
@Slf4j
public class EurekaFeignController {
@Resource
private EurekaFeignService eurekaFeignService;
@GetMapping("/feignInfo")
public String feignInfo() {
String message = eurekaFeignService.hello();
log.info(message);
return message;
}
}
注释掉前面我们在eureka-client的controller中的休眠等待时间(否则会超时)
启动项目
先启动Eureka和cloud-client-eureka项目,然后在启动当前项目.
测试:
关掉eureka-client,再次请求返回网络异常。
对Hystrix配置
指定接口方法的配置就是默认的CommandKey,@FeignClient注解上的名称是默认的GroupKey,具体很多配置,可以自己尝试一下
hystrix:
command:
EurekaFeignService#hello(): #指定接口方法的超时规则
execution:
isolation:
thread:
timeoutInMilliseconds: 500 #超时时间
circuitBreaker:
requestVolumeThreshold: 3 #请求并发数
#全局的
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500 #超时时间
circuitBreaker:
requestVolumeThreshold: 3 #请求并发数
Hystrix Dashboard
Hystrix提供了对于微服务调用状态的监控信息,需要结合spring-boot-actuator模块一起使用。Hystrix Dashboard主要用来实时监控Hystrix的各项指标信息。通过Hystrix Dashboard反馈的实时信息,可以帮助我们快速发现系统中存在的问题
已下是官方展示的图片
新建项目模块cloud-hystrix-dashboard,引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
启动类
额外@EnableHystrixDashboard注解
@EnableHystrixDashboard
@EnableEurekaClient
@SpringBootApplication
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
配置应用信息
server:
port: 8677
spring:
application:
name: hystrix-feign-dashboard
eureka:
client:
service-url:
defaultZone: http://xunfei:12345@127.0.0.1:8761/eureka/
项目启动
浏览器访问127.0.0.1:8677/hystrix
从页面上的文件内容我们可以知道,Hystrix Dashboard共支持三种不同的监控方式:
默认的集群监控: http://turbine-hostname:port/turbine.stream
指定的集群监控: http://turbine-hostname:port/turbine.stream?cluster=[clusterName]
单体应用的监控: http://hystrix-app:port/actuator/hystrix.stream
页面上面的几个参数局域
最上面的输入框: 输入上面所说的三种监控方式的地址,用于访问具体的监控信息页面。
我们先来看下单个服务实例的监控,从http://hystrix-app:port/actuator/hystrix.stream连接中可以看出,Hystrix Dashboard监控单节点实例需要访问实例的actuator/hystrix.stream接口
下面我们来看一下具体的监控信息
在输入框输入 http://127.0.0.1:8676/actuator/hystrix.stream
其他默认,注意不要输错
若出现如下页面:
原因如下:
在cloud-hystrix-feign中添加配置
management:
endpoints:
web:
exposure:
include: '*'
需要注意的是Spring Boot 2.0 之后所有对外暴露的端点都加上/actuator前缀
再次访问发现监控页面出现loading
通过postman多次调用127.0.0.1:8676/feignInfo接口。显示如下监控信息:
具体每个状态什么意思,直接拿官方的图片来看,看不懂应该的可以翻译一下。
Turbine是什么?
是Netflix提供了一个用来提供把多个hystrix.stream的内容聚合为一个数据源供Dashboard展示
从Hystrix Dashboard的监控首页中可以看出,集群的监控端点是http://turbine-hostname:port/turbine.stream,需要我们引入Trubine聚合服务,通过它来汇集监控信息,并将聚合后的信息提供给Hystrix Dashboard,下面我们来看一下多个应用的监控。
新建项目模块cloud-hystrix-turbine,引入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
启动类
加上@EnableTurbine注解
@EnableTurbine
@EnableDiscoveryClient
@SpringBootApplication
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
应用配置
server:
port: 8678
spring:
application:
name: hystrix-feign-dashboard
eureka:
client:
service-url:
defaultZone: http://xunfei:12345@127.0.0.1:8761/eureka/
turbine:
app-config: hystrix-ribbon, hystrix-feign # 配置Eureka中的serviceId列表,表明监控哪些服务
aggregator:
cluster-config: default # 指定聚合哪些集群,多个使用","分割,默认为default。
cluster-name-expression: new String("default")
instanceUrlSuffix: actuator/hystrix.stream # turbine在收集时访问的后缀
cluster-name-expression的配置默认default
启动cloud-hystrix-ribbon、cloud-hystrix-feign、ribbon-server项目、cloud-hystrix-dashboard
在Dashboard中输入http://127.0.0.1:8678/turbine.stream
在Postman测试两个接口127.0.0.1:8676/feignInfo和127.0.0.1:8675/user/1
因为我们启动cloud-ribbon-server项目,所以findUserById报了100%的错误,你也可以尝试启动cloud-ribbon-server项目看看展示的效果。
本文由 寻非 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
原文链接:https://www.zhouning.group/archives/springcloud05hystrix下springcloud使用
最后更新:2020-04-26 18:52:35
Update your browser to view this website correctly. Update my browser now