随着Docker的推进,微服务越来越热了.在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用。为了保证其高可用,单个服务又必须集群部署。由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务累计,导致服务瘫痪,甚至导致服务“雪崩”。
特别是app和api是通过http(如httpClient工具)进行连接通信时,我们一般都是设置个超时而已。但是这仍然有弊端,比如超时设置为2s,网络不稳定出问题了或者api挂了,app不知道啊,仍然会向api发出请求,如果一个业务需要调用多个3个api时,那响应给用户客户端(如浏览器)就起码超过6s了;如果并发量高,还会倒是app积累非常多的进程或线程,消耗了系统资源。如果有这么一个工具,在app端帮助app对api的响应情况进行记录,如果某个api的不响应了或者经常响应超时,那app再次请求该api时,那工具立刻识别到该api是不正常的,立刻告诉app。当api又正常了,那app的请求又会正常到达api。
Hystrix就是专门为解决这些问题的。
本博文的代码例子,github上。
org.springframework.cloud
spring-cloud-starter-hystrix
org.springframework.boot
spring-boot-starter-actuator
logging.config=classpath:logback.xml
logging.path=d:/logs
##tomcat set###
# eureka的默认端口是8761
server.port=7081
server.session-timeout=60
###########
spring.application.name=app-user
#像eureka服务注册信息时,使用ip地址,默认使用hostname
eureka.instance.preferIpAddress=true
#服务的instance-id默认默认值是${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}} ,
#也就是机器主机名:应用名称:应用端口
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
# 开启健康检查(需要spring-boot-starter-actuator依赖)
eureka.client.healthcheck.enabled=true;
# 续约更新时间间隔(默认30秒)
eureka.instance.lease-renewal-interval-in-seconds=30
# 续约到期时间(默认90秒)
eureka.instance.lease-expiration-duration-in-seconds=10
#eureka的服务地址
eureka.client.serviceUrl.defaultZone=http://01.server.eureka:8081/eureka/
#ribbo负载均衡策略配置,默认是依次轮询
API-USER-SERVER.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
#Hystrix是否启用超时时间
hystrix.command.default.execution.timeout.enabled=true;
#Hystrix断路器的超时时间,默认是1s
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000
package com.fei.springcloud.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "findError") //如果请求失败或超时
@GetMapping(value = "/find")
@ResponseBody
public String find() {
//url中对应api提供者的名称,全大写
System.out.println("向api提供者发起请求........");
String s = restTemplate.getForEntity("http://API-USER-SERVER/user/find/123", String.class).getBody();
return s;
}
public String findError(){
return "查询用户,调用api失败....";
}
}
package com.fei.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableEurekaClient
@SpringBootApplication
@EnableHystrix //增加hystrix断路器
public class Application {
@Bean //定义REST客户端,RestTemplate实例
@LoadBalanced //开启负债均衡的能力
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
feign.hystrix.enabled=false
package com.fei.springcloud.service;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value="API-USER-SERVER",fallback=UserServiceErrorImpl.class)
public interface UserService {
@GetMapping(value="/user/find/{id}")
String find(@PathVariable("id") String id);
}
增加fallback的支持,UserServiceErrorImpl是UserService的实现类
package com.fei.springcloud.service;
import org.springframework.stereotype.Component;
@Component
public class UserServiceErrorImpl implements UserService{
@Override
public String find(String id) {
return "根据id查询用户,调用api提供者失败...";
}
}
logging.config=classpath:logback.xml
logging.path=d:/logs
##tomcat set###
# eureka的默认端口是8761
server.port=7081
server.session-timeout=60
###########
spring.application.name=app-user
#像eureka服务注册信息时,使用ip地址,默认使用hostname
eureka.instance.preferIpAddress=true
# 续约更新时间间隔(默认30秒)
eureka.instance.lease-renewal-interval-in-seconds=30
# 续约到期时间(默认90秒)
eureka.instance.lease-expiration-duration-in-seconds=10
#服务的instance-id默认默认值是${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}} ,
#也就是机器主机名:应用名称:应用端口
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
#eureka的服务地址
eureka.client.serviceUrl.defaultZone=http://01.server.eureka:8081/eureka/
#开启健康检查
eureka.client.healthcheck.enabled=true
#开启hystrix,默认是false,这个是必须的开放,否则会出现找不到消费者或者拒绝连接的错误
feign.hystrix.enabled=true
#ribbo负载均衡策略配置,默认是依次轮询
API-USER-SERVER.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
#Hystrix是否启用超时时间
hystrix.command.default.execution.timeout.enabled=true;
#Hystrix断路器的超时时间,默认是1s
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000
feign.hystrix.enabled=true这个默认是false,如果使用了hystrix,则必须配置为true,否则会出现找不到消费者就报错,或者拒绝连接等异常报错.
org.springframework.cloud
spring-cloud-starter-hystrix
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-hystrix-dashboard
启动类加上@EnableHystrixDashboard,如
package com.fei.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients //用于启动Fegin功能
@EnableHystrix //增加hystrix断路器
@EnableHystrixDashboard //支持hystrix dashboard
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4.0.0
com.fei.springcloud
springcloud-eureka-hystrix-turbine
0.0.1-SNAPSHOT
turbine收集显示各个hystrix的信息
alimaven
http://maven.aliyun.com/nexus/content/repositories/central/
true
true
alimaven
http://maven.aliyun.com/nexus/content/repositories/central/
true
true
UTF-8
UTF-8
UTF-8
1.8
1.8
1.8
org.springframework.boot
spring-boot-starter-parent
1.5.2.RELEASE
org.springframework.cloud
spring-cloud-starter-turbine
org.springframework.cloud
spring-cloud-netflix-turbine
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-hystrix-dashboard
org.springframework.cloud
spring-cloud-dependencies
Dalston.RELEASE
pom
import
org.springframework.boot
spring-boot-maven-plugin
logging.config=classpath:logback.xml
logging.path=d:/logs
##tomcat set###
server.port=6081
server.session-timeout=60
###########
spring.application.name=hystrix-turebin
#配置Eureka中的serviceId列表,表明监控哪些服务,也就是app消费者的应用名称,多个时用英文逗号隔开
#turbine.appConfig=app-user-ribbon-hystrix,app-user-feign-hystrix
turbine.appConfig=APP-USER-RIBBON-HYSTRIX,APP-USER-FEIGN-HYSTRIX
turbine.aggregator.clusterConfig=default
turbine.clusterNameExpression=new String("default")
#像eureka服务注册信息时,使用ip地址,默认使用hostname
eureka.instance.preferIpAddress=true
#服务的instance-id默认默认值是${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}} ,
#也就是机器主机名:应用名称:应用端口
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
#eureka的服务地址,/eureka是固定的
eureka.client.serviceUrl.defaultZone=http://01.server.eureka:8081/eureka/
package com.fei.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
@SpringBootApplication
@EnableHystrixDashboard
@EnableTurbine
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}