在SpringCloud2组件之Ribbon详解和SpringCloud2组件之Feign详解中,我们通过Ribbon和Feign实现了微服务之间的调用。然而,在互联网中,可能存在某一个服务在某个时刻压力变大导致服务缓慢,甚至出现故障,导致服务不能响应。特别是在分布式系统中,如果一个服务不可用,而其他微服务还大量地调用这个不可用的微服务,也会导致其本身不可用,其自身不可用之后又可能继续蔓延到其他与之相关的服务上,这样就导致更多的微服务不可用,最终导致分布式服务瘫痪。为了防止这样的蔓延,Spring Cloud提供了断路器组件—Hystrix。
在微服务系统之间大量调用可能导致服务消费者自身出现瘫痪的情况下,断路器就会将这些积压的请求做限制请求处理,来保证自身服可用,而不会蔓延到其他微服务系统上。通过这样的断路机制可以保持各个微服务之间持续可用。
Hystrix处理限制请求的方式有很多,如降级、限流、缓存等。在这里,主要介绍下最为常用的降级服务。所谓降级服务,就是当请求其他微服务出现超时或者发生故障时,就会使用自身服务的其他方法进行响应。
通过Spring Cloud的Ribbon和Feign组件来实现client-product微服务调用client-user微服务。这里,我们不测试这两个组件的负载均衡功能(详见SpringCloud2组件之Ribbon详解和SpringCloud2组件之Feign详解这两篇博客),只是单纯的实现微服务之间的调用,使用Hystrix实现超时进行降级服务。
组件选择完成后,需在pom.xml修改Hystrix的依赖,解决jar包冲突问题,如下:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
<exclusions>
<exclusion>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
exclusion>
exclusions>
dependency>
server:
#服务端口
port: 9000
spring:
application:
#服务名称
name: server
eureka:
#配置环境
environment: dev
#配置数据中心
datacenter: nanjing
instance:
#注册服务器名称
hostname: localhost
client:
#是否注册到服务中心
register-with-eureka: false
#是否检索服务
fetch-registry: false
service-url:
#客户端服务域
defaultZone: http://localhost:9000/eureka/
package com.ming.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //开启Eureka服务端
public class ServerEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(ServerEurekaApplication.class, args);
}
}
server:
#服务端口
port: 7000
spring:
#服务名称
application:
name: user
eureka:
client:
service-url:
#服务注册地址
defaultZone: http://localhost:9000/eureka/
package com.ming.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //开启Eureka客户端
public class ClientUserApplication {
public static void main(String[] args) {
SpringApplication.run(ClientUserApplication.class, args);
}
}
package com.ming.user.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping("/timeout")
public String timeout() {
//调用服务时间
Timestamp time = new Timestamp(System.currentTimeMillis());
//生成一个2000之内的随机数
long ms = (long) (2000 * Math.random());
System.out.println("服务耗时:" + ms + " ms");
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("调用服务时间:" + time + ",服务耗时:" + ms + " ms");
return "调用服务时间:" + time + " -> 未超时,服务正常";
}
}
server:
#服务端口
port: 8000
spring:
#服务名称
application:
name: product
eureka:
client:
service-url:
#服务注册地址
defaultZone: http://localhost:9000/eureka/
package com.ming.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //开启Eureka客户端
@EnableFeignClients //开启Feign客户端
@EnableCircuitBreaker //开启Hystrix断路器
public class ClientProductApplication {
public static void main(String[] args) {
SpringApplication.run(ClientProductApplication.class, args);
}
}
package com.ming.product.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestConfig {
@Bean
@LoadBalanced //提供客户端负载均衡
public RestTemplate initRestTemplate() {
return new RestTemplate();
}
}
package com.ming.product.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient("USER") //开启Feign客户端,USER是client-user微服务的serviceId
public interface FeignService {
//对应要调用的client-user微服务控制层请求方法
@RequestMapping("/timeout")
String timeout();
}
package com.ming.product.controller;
import com.ming.product.service.FeignService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ProductController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private FeignService feignService;
@RequestMapping("/testHystrixByRibbon")
//设置出现超时(默认1000ms)断路时,跳转的服务降级方法
@HystrixCommand(fallbackMethod = "error")
/*@HystrixCommand(fallbackMethod = "error", commandProperties = {
//设置超时时间为2000ms
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
})*/
public String testHystrixByRibbon() {
//USER是client-user微服务的serviceId
return restTemplate.getForObject("http://USER/timeout", String.class);
}
@RequestMapping("/testHystrixByFeign")
//设置出现超时(默认1000ms)断路时,跳转的服务降级方法
@HystrixCommand(fallbackMethod = "error")
/*@HystrixCommand(fallbackMethod = "error", commandProperties = {
//设置超时时间为2000ms
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
})*/
public String testHystrixByFeign() {
return feignService.timeout();
}
//服务降级方法
private String error() {
return "超时,服务异常";
}
}
依次点击ServerEurekaApplication、ClientProductApplication、ClientUserApplication,工程都启动成功后。在浏览器地址栏访问 http://localhost:9000,其结果如下:
在浏览器地址栏访问 http://localhost:8000/testHystrixByRibbon,其结果如下:
查看控制台,其结果如下:
在浏览器地址栏访问 http://localhost:8000/testHystrixByFeign,其结果如下:
查看控制台,其结果如下: