什么是微服务?
举个简单的例子,做一个用户管理项目,里边就三个功能:用户注册、用户登录、用户详情浏览。按照传统的软件开发方式直接创建一个Web项目,分分钟就把这三个功能开发出来了,但是我现在想使用微服务+服务治理的方式来开发:首先我将这个项目拆分为四个微服务,四个微服务各建一个模块,分别是用户注册模块、用户登录模块、用户详情浏览模块和数据库操作模块,这四个模块通过内部服务治理互相调用。但是现在存在一个问题,这四个模块通过服务注册与订阅的方式互相依赖,如果一个模块出现故障会导致依赖它的模块也发生故障从而发生故障蔓延,进而导致整个服务的瘫痪。比如说这里的登录模块依赖于数据库模块,如果数据库模块发生故障,那么当登录模块去调用数据库模块的时候可能得不到响应,这个调用的线程被挂起,如果处于高并发的环境下,就会导致登录模块也崩溃。当一个系统划分的模块越多,这种故障发生的频率就会越高,对于这个问题,Spring Cloud中最重要的解决方案就是断路器,那么本文我们就来看看什么是断路器。
首先我们分别启动服务注册中心,再启动两个服务提供者的实例,端口号分别是8080和8081,然后再启动一个服务消费者,服务消费者的端口号为9000,这几个都启动成功之后,我们访问http://localhost:9000/ribbon-consumer
这个地址,可以看到如下效果:
此时我们关闭掉任意一个服务提供者,再去访问这个地址会看到效果
Spring Cloud中采取的默认负载均衡策略就是轮询,所以当一个服务提供者关掉之后,刷新的时候服务请求成功和请求失败是成对出现的:当服务消费者去请求那个被关掉的服务提供者的时候就会请求失败,当服务消费者去请求正常的服务提供者时就能获得期望的结果。请求失败时不能给用户展示这样一个ErrorPage,而应该是一个可控的页面,OK,我们来看看如何使用断路器来解决这个问题。
服务消费者中加入断路器
首先我们需要在服务消费者中引入hystrix,如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
修改服务消费者启动入口类
引入hystrix之后,我们需要在入口类上通过@EnableCircuitBreaker开启断路器功能,如下:
@EnableCircuitBreaker
@SpringBootApplication
@EnableDiscoveryClient
public class RibbonConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
}
@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
我们也可以使用一个名为@SpringBootApplication的注解代替这三个注解,@SpringBootApplication注解的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}
实际上就是这三个注解的一个整合。
修改Controller
然后我们创建一个HelloService类,如下:
@Service
public class HelloService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "error")
public String hello() {
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class);
return responseEntity.getBody();
}
public String error() {
return "error";
}
}
关于这个HelloService类我说如下几点:
1.RestTemplate执行网络请求的操作我们放在HelloService中来完成。
2.error方法是一个请求失败时回调的方法。
3.在hello方法上通过@HystrixCommand注解来指定请求失败时回调的方法。
OK,最后我们将ConsumerController的逻辑修改成下面这样:
@RestController
public class ConsumerController {
@Autowired
private HelloService helloService;
@RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET)
public String helloController() {
return helloService.hello();
}
}
此时我们就开启了断路器功能。
测试
我们先确认服务注册中心,两个服务提供者的实例,端口号分别是8080和8081,一个服务消费者,端口号为9000,一共四个实例都启动成功,启动成功之后,我们再关掉一个服务提供者,此时访问http://localhost:9000/ribbon-consumer会看到一定的效果
此时如果服务调用失败,就会调用失败的那个回调方法。
事实上,不仅仅是服务提供者被关闭时我们需要断路器,如果请求超时也会触发熔断请求,调用回调方法返回数据。