一、Hystrix介绍
Hystrix是一个延迟容错库。在分布式环境中,许多服务依赖项中的一些不可避免地会失败。如果这时候有大量的请求请求这个故障的服务,由于服务之间的依赖关系,故障会进行蔓延,这时候会导致调用服务自身也出现不可用的情况,使用Hystrix可以解决这个问题。当某个服务发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
二、实现服务提供方短路
2.1Api工程搭建
- maven
<parent>
<groupId>com.yk</groupId>
<artifactId>cloud-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>springcloud-api</artifactId>
- Entity
@Data
public class User {
/**
* ID
*/
private Long id;
/**
* 用户名称
*/
private String name;
}
- Service
public interface UserService {
/**
* 保存用户
* @param user
*/
boolean saveUser(User user);
/**
* 查询所有的用户列表
*/
List<User> findAll();
}
2.2服务提供方工程搭建
- Maven
<parent>
<groupId>com.yk</groupId>
<artifactId>cloud-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>springcloud-api-provider</artifactId>
<dependencies>
<!-- 依赖 API -->
<dependency>
<groupId>com.yk</groupId>
<artifactId>springcloud-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 依赖 Spring Cloud Netflix Hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
- application.properties配置
## 用户服务提供方应用信息
spring.application.name = spring-cloud-api-provider
## 服务端口
server.port = 801
- 启动入口
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableHystrix //服务端实现
public class ApiProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ApiProviderApplication.class, args);
}
}
- 服务实现类
@Service
public class UserServiceImpl implements UserService {
private Map<Long, User> repository = new ConcurrentHashMap<>();
@Override
public boolean saveUser(User user) {
return repository.put(user.getId(), user) == null;
}
@Override
public List<User> findAll() {
return new ArrayList(repository.values());
}
}
- Controller
@RestController
public class UserController {
@Autowired
private UserService userService;
private final static Random random = new Random();
@GetMapping("/user/save")
public boolean getUser(){
User user=new User();
user.setId(1L);
user.setName("张三");
return userService.saveUser(user);
}
/**
* 获取所有用户列表
* @return
*/
@HystrixCommand(
// Command 配置,设置操作时间为 100 毫秒
commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "100")},
// 设置 fallback 方法
fallbackMethod = "fallbackForGetUsers"
)
@GetMapping("/user/list")
public Collection<User> getUsers() throws InterruptedException {
// 通过休眠来模拟执行时间
long executeTime = random.nextInt(200);
System.out.println("Execute Time : " + executeTime + " ms");
Thread.sleep(executeTime);
return userService.findAll();
}
/**
* 超过100毫秒就返回空集合
* @return
*/
public Collection<User> fallbackForGetUsers() {
return Collections.emptyList();
}
}
2.3Demo演示
先调用/user/save增加一个用户
三、实现服务调用方短路
服务调用方工程
- Maven
<parent>
<groupId>com.yk</groupId>
<artifactId>cloud-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>springcloud-ribbon-client</artifactId>
<dependencies>
<!-- 依赖API -->
<dependency>
<groupId>com.yk</groupId>
<artifactId>springcloud-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 依赖 Ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 依赖 Spring Cloud Netflix Hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
- application.properties
## 用户 Ribbon 客户端应用
spring.application.name = spring-cloud-api-client
## 服务端口
server.port = 802
## 提供方服务名称
provider.service.name = spring-cloud-api-provider
## 提供方服务主机
provider.service.host = 192.168.0.102
## 提供方服务端口
provider.service.port = 801
## 定义 spring-cloud-ribbon-provider Ribbon 的服务器地址
## 为 RibbonLoadBalancerClient 提供服务列表
spring-cloud-api-provider.ribbon.listOfServers = \
http://${provider.service.host}:${provider.service.port}
- 入口
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@RibbonClient("spring-cloud-api-provider") // 指定目标应用名称
@EnableCircuitBreaker // 使用客户端短路
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
/**
* 负载均衡
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
- 使用编程方式实现服务短路
public class UserHystrixCommand extends HystrixCommand<Collection> {
private final String providerServiceName;
private final RestTemplate restTemplate;
public UserHystrixCommand(String providerServiceName, RestTemplate restTemplate) {
super(//组名称
HystrixCommandGroupKey.Factory.asKey(
"spring-cloud-api-client"),
//超时时间
100);
this.providerServiceName = providerServiceName;
this.restTemplate = restTemplate;
}
/**
* 具体实现
*/
@Override
protected Collection run() throws Exception {
return restTemplate.getForObject("http://" + providerServiceName + "/user/list", Collection.class);
}
/**
* Fallback 实现 返回空集合
*/
protected Collection getFallback() {
return Collections.emptyList();
}
}
- Controller
package com.yk.client.controller;
import com.yk.api.entity.User;
import com.yk.client.hystrix.UserRibbonClientHystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.Collection;
@RestController
public class UserController {
@Value("${provider.service.name}")
private String providerServiceName;
@Autowired
private RestTemplate restTemplate;
/**
* 调用 /spring-cloud-api-provider服务的 /user/list REST 接口,并且直接返回内容
* 增加 短路功能
*/
@GetMapping("/user/list")
public Collection<User> getUsersList() {
return new UserRibbonClientHystrixCommand(providerServiceName, restTemplate).execute();
}
}
- Demo演示
先打断点,然后在释放断点就可以看到服务调用方的短路效果了