简介
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
为了解决这个问题,业界提出了断路器模型。
较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystric 是5秒20次) 断路器将会被打开。
分类
1.基于ribbon
2.基于feign
实战一
首先我们来看下基于ribbon的实战
一.环境准备
1.启动服务注册中心:spring-cloud-eureka
2.启动服务提供方:spring-cloud-eureka-client【端口为2002】
3.基于SpringCloud教程 | 三.服务消费者(Ribbon)
二.调整项目
1.调整项目spring-cloud-eureka-ribbon,在对应的pom中加入spring-cloud-starter-hystrix的起步依赖
【注意】除了加上上面说的hystrix依赖外还需要添加如下依赖【务必注意依赖版本,最开始我用的是1.8.3(默认就是),结果报错】:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Edgware.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.application.properties
server.port=8764
spring.application.name=eureka-ribbon
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/
3.启动类上使用@EnableCircuitBreaker注解开启断路器功能:
package com.aikucun;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
@EnableCircuitBreaker
public class SpringCloudEurekaRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaRibbonApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
4.修改service,在使用ribbon消费服务的函数上增加@HystrixCommand注解来指定回调方法
当服务提供者挂了之后会自动返回sorry,error
package com.aikucun.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
/**
* @FileName TestService.java
* @Description:TODO
* @author JackHisen(gu.weidong)
* @version V1.0
* @createtime 2018年1月18日 上午10:25:51
* 修改历史:
* 时间 作者 版本 描述
*====================================================
*/
@Service
public class TestService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod="hiError")
public String hiService() {
return restTemplate.getForObject("http://EUREKA-CLIENT/dc",String.class);
}
public String hiError() {
return "sorry,error";
}
}
5.controller不变
package com.aikucun.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.aikucun.service.TestService;
/**
* @FileName TestController.java
* @Description:TODO
* @author JackHisen(gu.weidong)
* @version V1.0
* @createtime 2018年1月21日 下午1:08:26
* 修改历史:
* 时间 作者 版本 描述
*====================================================
*
*/
@RestController
public class TestController {
@Autowired
TestService testService;
@RequestMapping("/hi")
public String testController() {
return testService.hiService();
}
}
挂掉后:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
基于Feign使用Hystrix
注意这里说的是“使用”,没有错,我们不需要在Feigh工程中引入Hystix,Feign中已经依赖了Hystrix。
一.环境准备
1.启动服务注册中心:spring-cloud-eureka
2.启动服务提供方:spring-cloud-eureka-client【端口为2002】
二.调整项目
1.调整项目spring-cloud-eureka-feign,对应的pom.xml如下
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.application.properties
【注意】:如下图所示,feign.hystrix.enabled在不设置的情况下默认是false,所以这边需要设置一下,否则熔断器无效
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka
server.port=8000
feign.hystrix.enabled=true
spring.application.name=eureka-feign
3.启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SpringCloudEurekaFeignApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaFeignApplication.class, args);
}
}
4.service层
接口:
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import com.aikucun.service.ComputeClientHystrix;
/**
* @FileName TestService.java
* @Description:TODO
* @author JackHisen(gu.weidong)
* @version V1.0
* @createtime 2018年1月22日 上午10:29:54
* 修改历史:
* 时间 作者 版本 描述
*====================================================
*
*/
@FeignClient(value = "eureka-client", fallback = ComputeClientHystrix.class)
public interface TestService {
@RequestMapping("/dc")
public String testFeign();
}
实现类:
import org.springframework.stereotype.Component;
import com.aikucun.feign.TestService;
/**
* @FileName ComputeClientHystrix.java
* @Description:TODO
* @author JackHisen(gu.weidong)
* @version V1.0
* @createtime 2018年1月22日 下午5:32:29
* 修改历史:
* 时间 作者 版本 描述
*====================================================
*
*/
@Component
public class ComputeClientHystrix implements TestService {
@Override
public String testFeign() {
return "------------------------------------------Feign-Error";
}
}
5.controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.aikucun.feign.TestService;
/**
* @FileName TestFeignController.java
* @Description:TODO
* @author JackHisen(gu.weidong)
* @version V1.0
* @createtime 2018年1月22日 上午10:32:52
* 修改历史:
* 时间 作者 版本 描述
*====================================================
*
*/
@RestController
public class TestFeignController {
@Autowired
private TestService testService;
@RequestMapping("/feign")
public String testFeignController() {
return testService.testFeign();
}
}