前言
现在思考一个问题,如果调用的远程服务挂了,我们的消费端岂不是也要挂?这是绝对的不许的,HyStrix 容错机制 为我们提供了解决方法,通过服务降级,处理异常情况,并返回自己定义的结果。
HyStrix 具备了服务降级、服务熔断、线程隔离、请求缓存、请求合并以及服务监控等强大功能。
修改 客户服务端
增加代码,模拟超时,这个时候 消费端接受不到返回的信息,就会报错,显示 timeout,
/**
* 提供库存服务
*/
@RestController
public class RepoController {
@Autowired
DiscoveryClient discoveryClient;
@GetMapping("/productRepo")
public String productRepo() {
String services = "Services: " + discoveryClient.getServices(); // 调用本服务的消费者
System.out.println("调用本服务的消费方:"+services);
System.out.println("==================> 的更新库存信息接口被调用了");
String result = "success";
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
}
依赖
<!-- HyStrix 容错机制,具备了服务降级、服务熔断、线程隔离、请求缓存、请求合并以及服务监控等强大功能 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
启动类
增加一个注解
@EnableDiscoveryClient // 将当期应用加入到服务治理体系中
@SpringBootApplication
@MapperScan("com.example.eurekaconsumer.dao")
@EnableFeignClients // 开启扫描feign 客户端
@EnableCircuitBreaker // 或@EnableHystrix 或者 @SpringCloudApplication 这个注解包括了 服务发现和服务熔断
public class EurekaConsumerApplication {
@Bean
@LoadBalanced //ribbon 的组件
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
// SpringApplication.run(EurekaConsumerApplication.class, args);
new SpringApplicationBuilder(EurekaConsumerApplication.class).web(true).run(args);
}
}
创建Service
ConsumerService 把调用服务封装进去,在controller 我们注入 ConsumerService 就可以了,我们在这个类设置容错机制,fallbackMethod 对应服务挂了的处理方法。
package com.example.eurekaconsumer.service;
import com.example.eurekaconsumer.feignCilent.RepoClient;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class ConsumerService {
@Autowired
RestTemplate restTemplate;
@Autowired
RepoClient repoClient;
// 服务降级和线程隔离。这个是一体的
@HystrixCommand(fallbackMethod = "fallback")
public Object productRepo(){
String obj = repoClient.productRepo();
return obj;
}
public String fallback() {
return "fallback";
}
}
控制层
将 ConsumerService 注入,调用方法就可以了
@Controller
@RequestMapping(value = "/order")
public class OrderController {
@Resource
UserMapper userMapper;
@Resource
OrderMapper orderMapper;
@Resource
ProductMapper productMapper;
@Autowired
RestTemplate restTemplate;
@Autowired
RepoClient repoClient;
@Autowired
ConsumerService consumerService;
@RequestMapping(value = "/deal",method = RequestMethod.POST)
public String dealOrder(Model model, @RequestParam("productId") String productId,
@RequestParam("price") String price,@RequestParam("sum") String sum){
Map<String,String> map = new HashMap<>();
map.put("productId",productId);
map.put("sum",sum);
// 调用库存服务,更新库存信
// 第一个阶段:这个地址很特殊,没有端口和ip,而是要调用服务的名称,因为ribbon 有一个拦截器,会用名称选择服务,并且用真是地址和端口替换掉这个
// Object obj = restTemplate.getForObject("http://eureka-client/productRepo",String.class);
// 第二个阶段:使用feign 调用 ,比上边的更好,更简单
// String obj = repoClient.productRepo();
// System.out.println("调用 http://eureka-client/productRepo 返回结果:"+obj);
// 第三个阶段,使用Hystrix 服务降级,如果超时就 执行自己定义的
long start = System.currentTimeMillis();
Object obj = consumerService.productRepo();
long end = System.currentTimeMillis();
// 断路器,三个因素:快照时间窗,请求总数下限,错误百分比下限
System.out.println("使用hystric 返回:"+obj+",时间间隔:"+(end-start));
// 调用积分服务,更新积分信息(待添加)
// 处理需求购买一个苹果手机,需要 更新库存信息和 个人积分信息
List<ProductInfo> productInfoList = productMapper.selectAll();
model.addAttribute("productInfoList",productInfoList);
return "index";
}
}
测试
还是我自己的页面调用,查看控制台
使用hystric 返回:fallback,时间间隔:2069
使用hystric 返回:fallback,时间间隔:1002
使用hystric 返回:success,时间间隔:17