#搭建 Eureka 服务
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.crazyit.cloud</groupId>
<artifactId>first-hystrix-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.4.RELEASE</version>
</dependency>
</dependencies>
</project>
applicaiton.yml
server:
port: 8761
eureka:
client:
registerWithEureka: false
fetchRegistry: false
logging:
level:
com.netflix: INFO
启动类
package org.crazyit.cloud;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class ServerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ServerApplication.class).run(args);
}
}
搭建 provider
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.crazyit.cloud</groupId>
<artifactId>spring-hystrix-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
</project>
application.yml
spring:
application:
name: spring-hystrix-provider
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
实体类 Person
package org.crazyit.cloud;
public class Person {
private Integer id;
private String name;
private Integer age;
private String message;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
启动类
package org.crazyit.cloud;
import java.util.Scanner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ProviderApplication {
public static void main(String[] args) {
// 设置启动的服务器端口
new SpringApplicationBuilder(ProviderApplication.class).properties(
"server.port=8080").run(args);
}
}
Controller 类
package org.crazyit.cloud;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PersonController {
@RequestMapping(value = "/person/{personId}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Person findPerson(@PathVariable("personId") Integer personId, HttpServletRequest request) {
Person person = new Person();
person.setId(personId);
person.setName("Crazyit");
person.setAge(33);
person.setMessage(request.getRequestURL().toString());
return person;
}
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() throws Exception {
Thread.sleep(800);
return "Hello World";
}
@RequestMapping(value = "/persons", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Person> findPersons(@RequestBody List<Integer> personIds, HttpServletRequest request) {
List<Person> result = new ArrayList<Person>();
for(Integer id : personIds) {
Person person = new Person();
person.setId(id);
person.setName("angus");
person.setAge(new Random().nextInt(30));
person.setMessage(request.getRequestURL().toString());
result.add(person);
}
return result;
}
}
搭建调用方
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.crazyit.cloud</groupId>
<artifactId>spring-hystrix-invoker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 9000
spring:
application:
name: spring-hystrix-invoker
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
feign:
hystrix:
enabled: true
hystrix:
command:
HelloClient#hello():
execution:
isolation:
thread:
timeoutInMilliseconds: 500
circuitBreaker:
requestVolumeThreshold: 3
InvokerApplication 在启动类上面加入断路器注解 @EnableCircuitBreaker
package org.crazyit.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
@ServletComponentScan
@EnableFeignClients
public class InvokerApplication {
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(InvokerApplication.class, args);
}
}
InvokerController
package org.crazyit.cloud;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import org.crazyit.cloud.cache.CacheService;
import org.crazyit.cloud.collapse.CollapseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Configuration
public class InvokerController {
@Autowired
private PersonService personService;
@Autowired
private CacheService cacheService;
@Autowired
private CollapseService collapseService;
@RequestMapping(value = "/router/{personId}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Person router(@PathVariable Integer personId) {
Person p = personService.getPerson(personId);
return p;
}
@RequestMapping(value = "/testConfig", method = RequestMethod.GET)
public String testConfig() {
String result = personService.testConfig();
return result;
}
@RequestMapping(value = "/testException", method = RequestMethod.GET)
public String testException() {
String result = personService.testException();
return result;
}
@RequestMapping(value = "/cache1/{personId}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Person testCacheResult(@PathVariable Integer personId) {
// 调用多次服务
for(int i = 0; i < 3; i++) {
Person p = cacheService.getPerson(personId);
System.out.println("控制器调用服务 " + i);
}
return new Person();
}
@RequestMapping(value = "/cache2", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public String testCacheRemove() {
for(int i = 0; i < 3; i++) {
cacheService.cacheMethod("a");
System.out.println("控制器调用服务 " + i);
}
// 清空缓存
cacheService.updateMethod("a");
System.out.println("========== 清空了缓存");
// 再执行多次
for(int i = 0; i < 3; i++) {
cacheService.cacheMethod("a");
System.out.println("控制器调用服务 " + i);
}
return "";
}
@RequestMapping(value = "/collapse", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public String testCollapse() throws Exception {
// 连续执行3次请求
Future<Person> f1 = collapseService.getSinglePerson(1);
Future<Person> f2 = collapseService.getSinglePerson(2);
Future<Person> f3 = collapseService.getSinglePerson(3);
Person p1 = f1.get();
Person p2 = f2.get();
Person p3 = f3.get();
System.out.println(p1.getId() + "---" + p1.getName());
System.out.println(p2.getId() + "---" + p2.getName());
System.out.println(p3.getId() + "---" + p3.getName());
return "";
}
}
Person
package org.crazyit.cloud;
public class Person {
Integer id;
String name;
int age;
String message;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
PersonService
package org.crazyit.cloud;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
@Component
public class PersonService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "getPersonFallback")
public Person getPerson(Integer id) {
// 使用RestTemplate调用Eureka服务
Person p = restTemplate.getForObject(
"http://spring-hystrix-provider/person/{personId}",
Person.class, id);
return p;
}
/**
* 回退方法,返回一个默认的Person
*/
public Person getPersonFallback(Integer id) {
Person p = new Person();
p.setId(0);
p.setName("Crazyit");
p.setAge(-1);
p.setMessage("request error");
return p;
}
/**
* 测试配置,对3个key进行命名
* 设置命令执行超时时间为1000毫秒
* 设置命令执行的线程池大小为1
*/
@HystrixCommand(
fallbackMethod="testConfigFallback", groupKey="MyGroup",
commandKey="MyCommandKey", threadPoolKey="MyCommandPool",
commandProperties={
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
value = "1000")
},
threadPoolProperties={
@HystrixProperty(name = "coreSize",
value = "1")
})
public String testConfig() {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
return "ok";
}
public String testConfigFallback() {
return "error";
}
/**
* 声明了忽略MyException,如果方法抛出MyException,则不会触发回退
*/
@HystrixCommand(ignoreExceptions = {MyException.class},
fallbackMethod="testExceptionFallBack")
public String testException() {
throw new MyException();
}
public String testExceptionFallBack() {
return "error";
}
}
HystrixFilter
package org.crazyit.cloud;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
@WebFilter(urlPatterns = "/*", filterName = "hystrixFilter")
public class HystrixFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HystrixRequestContext context = HystrixRequestContext
.initializeContext();
try {
chain.doFilter(request, response);
} finally {
context.shutdown();
}
}
public void destroy() {
}
}
CacheService
package org.crazyit.cloud.cache;
import org.crazyit.cloud.Person;
import org.springframework.stereotype.Component;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
@Component
public class CacheService {
@CacheResult
@HystrixCommand
public Person getPerson(Integer id) {
System.out.println("执行 getPerson 方法");
Person p = new Person();
p.setId(id);
p.setName("angus");
return p;
}
/**
* 测试删除缓存
*
* @param name
* @return
*/
@CacheResult()
@HystrixCommand(commandKey = "removeKey")
public String cacheMethod(String name) {
System.out.println("执行命令");
return "hello";
}
@CacheRemove(commandKey = "removeKey")
@HystrixCommand
public String updateMethod(String name) {
return "update";
}
}
CollapseService
package org.crazyit.cloud.collapse;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import org.crazyit.cloud.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
@Component
public class CollapseService {
// 配置收集1秒内的请求
@HystrixCollapser(batchMethod = "getPersons", collapserProperties =
{
@HystrixProperty(name = "timerDelayInMilliseconds", value = "1000")
}
)
public Future<Person> getSinglePerson(Integer id) {
System.out.println("执行单个获取的方法");
return null;
}
@HystrixCommand
public List<Person> getPersons(List<Integer> ids) {
System.out.println("收集请求,参数数量:" + ids.size());
List<Person> ps = new ArrayList<Person>();
for (Integer id : ids) {
Person p = new Person();
p.setId(id);
p.setName("crazyit");
ps.add(p);
}
return ps;
}
}
feign 相关
HelloClient
package org.crazyit.cloud.feign;
import org.crazyit.cloud.feign.HelloClient.HelloClientFallback;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import feign.Feign;
@FeignClient(name = "spring-hystrix-provider", fallback = HelloClientFallback.class)
public interface HelloClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
public String hello();
@Component
static class HelloClientFallback implements HelloClient {
public String hello() {
return "error hello";
}
}
}
HelloController
package org.crazyit.cloud.feign;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.netflix.hystrix.HystrixCircuitBreaker;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandMetrics;
/**
* Feign与Hystrix整合
*
* @author 杨恩雄
*
*/
@RestController
public class HelloController {
@Autowired
HelloClient helloClient;
@RequestMapping(value = "/feign/hello", method = RequestMethod.GET)
public String feignHello() {
// hello方法会超时
String helloResult = helloClient.hello();
// 获取断路器
HystrixCircuitBreaker breaker = HystrixCircuitBreaker.Factory
.getInstance(HystrixCommandKey.Factory
.asKey("HelloClient#hello()"));
System.out.println("断路器状态:" + breaker.isOpen());
return helloResult;
}
}
TestFeignClient
package org.crazyit.cloud.feign;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
public class TestFeignClient {
public static void main(String[] args) throws Exception {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger logger = loggerContext.getLogger("root");
logger.setLevel(Level.toLevel("INFO"));
// 创建默认的HttpClient
final CloseableHttpClient httpclient = HttpClients.createDefault();
// 调用多次服务并输出结果
for(int i = 0; i < 6; i++) {
// 建立线程访问接口
Thread t = new Thread() {
public void run() {
try {
String url = "http://localhost:9000/feign/hello";
// 调用 GET 方法请求服务
HttpGet httpget = new HttpGet(url);
// 获取响应
HttpResponse response = httpclient.execute(httpget);
// 根据 响应解析出字符串
System.out.println(EntityUtils.toString(response.getEntity()));
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
// 等待完成
Thread.sleep(15000);
}
}
访问 http://localhost:9000/router/111
把 provider 服务停止后
可见 @HystrixCommand 注解的 PersonService.getPerson
方法断路器生效了。
默认配置
对于一些默认的配置,例如命令组的key等,可以使用@DefaultProperties注解,这样就减少了@HystrixCommand注解的代码量
package org.crazyit.cloud;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@DefaultProperties(groupKey = "GroupPersonKey")
public class PersonService2 {
@HystrixCommand //groupKey将使用GroupPersonKey
public String hello(){
return "";
}
}
缓存注解:
* 缓存与合并请求功能需要先初始化请求上下文才能实现,所有才有了上面的HystrixFilter
* CacheService中getPerson方法使用注解`@CacheResult`
调用 http://localhost:9000/cache1/22
输出:
执行 getPerson 方法
控制器调用服务 0
控制器调用服务 1
控制器调用服务 2
可见 @CacheResult
注解的 getPerson 方法其实只执行了一次,缓存生效。
缓存主要 3 个注解:
- @CacheResult: 注释方法,标识被注解的方法返回结构将会被缓存,需要与 @HystrixCommand 一起使用
- @CacheRemove: 用于修饰方法让缓存失效,需要与 @CacheResult 的缓存 key 关联。
- @CacheKey: 用于修饰方法参数,标识该参数作为缓存的 key.
如上述代码 CacheService 中,先调用 cacheMethod 方法会把结果存储起来,再次调用则不会执行方法,然后调用 updateMethod 方法可以清除对应的缓存,下次再调用 cacheMethod 方法则会执行具体的方法逻辑。
访问 http://localhost:9000/cache2
输出如下:
执行命令
控制器调用服务 0
控制器调用服务 1
控制器调用服务 2
========== 清空了缓存
执行命令
控制器调用服务 0
控制器调用服务 1
控制器调用服务 2
注意: 这里说的缓存指的是在一次请求中,如果单独请求多次,则每次都会执行对应的方法。
合并请求注解 见代码 CollapseService
最后真实执行的方法为 getPersons,getSinglePerson 方法使用了 @HystrixCollapser 注解,会收集 1s 内调用 getSinglePerson 的请求,放到 getPersons 方法中进行批处理。
访问 http://localhost:9000/collapse
控制台输出:
收集请求,参数数量:3
1---crazyit
2---crazyit
3---crazyit
Feign 与 Hystrix 整合
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
feign:
hystrix:
enabled: true
启动类加对应注解:
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
@ServletComponentScan
@EnableFeignClients
新建 Feign 接口
package org.crazyit.cloud.feign;
import org.crazyit.cloud.feign.HelloClient.HelloClientFallback;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import feign.Feign;
@FeignClient(name = "spring-hystrix-provider", fallback = HelloClientFallback.class)
public interface HelloClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
public String hello();
@Component
static class HelloClientFallback implements HelloClient {
public String hello() {
return "error hello";
}
}
}
调用服务提供者 spring-hystrix-provider 的 /hello 服务,默认情况下 Hystrix 超时时间为 1s,让服务端睡眠 800 毫秒
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() throws Exception {
Thread.sleep(800);
return "Hello World";
}
然后配置 hystrix 超时为 500 毫秒
hystrix:
command:
HelloClient#hello():
execution:
isolation:
thread:
timeoutInMilliseconds: 500
circuitBreaker:
requestVolumeThreshold: 3
如果全局配置超时可以使用
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
默认时间段内发生的请求数:
hystrix.command.default.circuitBreaker.requestVolumeThreshold
如果针对某个客户端,使用下面的配置片段(CommandKey):
hystrix.command.CommandKey.circuitBreaker.requestVolumeThreshold
Feign 与 Hystrix 整合使用时,会自动帮我们生产 CommandKey,格式为:Feign 客户端接口名#房法名().例如本例中的客户端为 HelloClient,方法为 hello,生成的 CommandKey 为 HelloClient#hello()。默认情况下,生成的 GroupKey 为 @FeignClient 注解的 name 属性。
在 HelloController 中调用 helloClient 的 hello 方法. 由于配置请求超过 3 次并且失败率超过 50% 断路器将会被打开。
运行 TestFeignClient 输出:
断路器状态:false
断路器状态:false
断路器状态:false
断路器状态:false
断路器状态:true
断路器状态:true
可见到第 4 个请求之后断路器被打开了,但是为什么是第四个之后 ,莫非是设置的 requestVolumeThreshold
+ 1 得到的?(经测试如果设置 requestVolumeThreshold
为 1,则(第二个之后)第三个会输出断路器状态 true).
Hystrix 监控
加入Actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
访问 http://localhost:9000/hystrix.stream 会输出 stream 数据
新建一个监控项目加入依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.crazyit.cloud</groupId>
<artifactId>hystrix-dashboard</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
</dependencies>
</project>
打开 @EnableHystrixDashboard
注解:
package org.crazyit.cloud;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class MyApplication {
public static void main(String[] args) {
// 设置启动的服务器端口
new SpringApplicationBuilder(MyApplication.class).properties(
"server.port=8082").run(args);
}
}
启动项目访问: http://localhost:8082/hystrix
并输入我们刚才的 stream 地址:
可以看到 HelloClient#hello()断路器被打开了。
如果需要监控整个集群的情况,可以使用 Turbine 框架。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于