在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。为了解决这个问题,业界提出了断路器模型。当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
接下来我们这篇文章就说一下服务消费与断路器。
Ribbon
ribbon是一个负载均衡客户端,可以很好的控制http和tcp的一些行为。Feign默认集成了ribbon。
我们在RibbonApplication
中添加SpringCloudApplication
注解将此项目作为微服务注册再Eureka,并开启Hystrix功能,通过LoadBalanced
与Bean
实例化RestTemplate 并开启负载均衡功能(访问服务提供者集群)
/**
* 服务Ribbon消费
* 2018年6月15日09:26:54
* yangzhap
*/
@RestController
@SpringCloudApplication
/**
* 相当于这三个注解
* @SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker//开启熔断器(@EnableHystrix)
*/
public class RibbonApplication {
@Bean
@LoadBalanced//开启负载均衡
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(RibbonApplication.class, args);
}
}
我们编写服务消费方法
@Service
public class RIbbonService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "addServiceFallback")//处理熔断
public String add(Integer a, Integer b) {
return restTemplate.getForObject("http://service-provider/add?a="+a+"&b="+b, String.class);//url指定调用的服务以及接口
}
//放生错误进入熔断方法
//com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException(add方法要和熔断方法参数一致)
public String addServiceFallback(Integer a, Integer b) {
return a+b+"error";
}
}
可以看到我们通过RestTemplate
对象访问服务的应用名(service-provider)以及具体的服务(add)即可实现服务消费
通过再add方法中加上service-provider
并指定fallbackMethod
属性,即服务提供者down掉时,执行addServiceFallback
方法
我们创建一个controller 来调用消费方法
@RestController
public class RibbonCustomer {
@Autowired
RIbbonService ribbonService;
@RequestMapping(value = "/add" ,method = RequestMethod.GET)
public String add(@RequestParam Integer a, @RequestParam Integer b) {
return ribbonService.add(a,b);
}
}
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8763
spring:
application:
name: service-ribbon
启动项目,访问http://localhost:8763/add?a=4&b=2
可以看到我们成功消费了provider
接下来我们测试断路器,我们将provider停掉
接着访问http://localhost:8763/add?a=4&b=2
Feign
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
配置文件:
Feign是自带断路器的,在D版本的Spring Cloud中,它没有默认打开。需要在配置文件中配置打开它,在配置文件加以下代码:feign.hystrix.enabled=true
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8764
spring:
application:
name: service-feign
feign:
hystrix:
enabled: true
我们通过EnableFeignClients
开启Feign功能,并注册到Eureka(EnableDiscoveryClient
)
/**
* feign消费者(自带断路器)
* 2018年6月15日10:16:22
* yangzhao
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients//加上此注解开启Feign功能
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
编写IFeignCustomer
接口消费服务
@FeignClient(value = "SERVICE-PROVIDER" ,fallback = FeignError.class)//指定调用哪个服务 指定熔断回调类实现接口
public interface IFeignCustomer {
@RequestMapping(value = "/add",method = RequestMethod.GET)//指定接口
String add(@RequestParam(value = "a")Integer a,@RequestParam(value = "b") Integer b);
}
通过FeignClient
注解的属性value
指明要消费的服务,并通过fallback
属性指明熔断方法的实现类,通过RequestMapping
注解指明要消费的服务中的哪个接口
我们看一下熔断方法(接口的实现类中)
@Component
public class FeignError implements IFeignCustomer {
@Override
public String add(@RequestParam(value = "a")Integer a, @RequestParam(value = "b") Integer b){
return a+b+"errorFeign";
};
}
我们编写controller 调用消费方法
@RestController
public class FeginController {
@Autowired
IFeignCustomer iFeignCustomer;
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String add(@RequestParam Integer a, @RequestParam Integer b) {
return iFeignCustomer.add(a,b);
}
}
启动项目
访问http://localhost:8764/add?a=1&b=2
已经成功消费提供者
我们关掉提供者的服务