一、断路器简介
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
为了解决这个问题,业界提出了断路器模型。
创建Module->microservicecloud-provider-dept-hystrix
1.服务提供者POM配置 (ribbon方式熔断配置)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>mall</artifactId> <groupId>com.linjia</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>microservicecloud-provider-dept-hystrix</artifactId> <name>microservicecloud-provider-dept-hystrix</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>com.linjia</groupId> <artifactId>microservicecloud-api</artifactId> <version>${project.version}</version> </dependency> <!-- hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <!-- actuator监控信息完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- 将微服务provider侧注册进eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</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-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies> <build> <finalName>${name}</finalName> <!-- 打包jar名称--> <plugins> <plugin><!-- 打包可运行的jar --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
2.服务提供者yml配置
server: port: 8002 mybatis: type-aliases-package: com.linjia.springcloud.entity # 所有Entity别名类所在包 mapper-locations: - classpath:mybatis/mapper/*.xml # mapper映射文件 spring: application: name: microservicecloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型 driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包 url: jdbc:mysql://192.168.0.38:3306/test # 数据库名称 username: root password: linjia dbcp2: min-idle: 5 # 数据库连接池的最小维持连接数 initial-size: 5 # 初始化连接数 max-total: 5 # 最大连接数 max-wait-millis: 200 # 等待连接获取的最大超时时间 jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate.format_sql: true database-platform: org.hibernate.dialect.MySQL5InnoDBDialect eureka: client: service-url: # defaultZone: http://localhost:7001/eureka #将客户端注册进eureka服务列表内 defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka,http://localhost:7003/eureka instance: instance-id: ${spring.cloud.client.ipAddress}-hystrix:${server.port} #自定义服务名称信息 启动多个服务做集群需要设置为不同的ID值 prefer-ip-address: true #访问路径可以显示IP地址 leaseRenewalIntervalInSeconds: 10 #剔除不健康节点。 leaseExpirationDurationInSeconds: 30 #剔除不健康节点。 info: app.name: microservicecloud-dept company.name: www.baidu.cn build.artifactId: $project.artifactId$ build.version: $project.version$3.服务提供者yml配置
package com.linjia; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.context.annotation.ComponentScan; /** * Hello world! * */ @SpringBootApplication @ComponentScan("com.linjia.springcloud") @EnableEurekaClient//启动服务注册 @EnableDiscoveryClient @EnableHystrix public class HystrixProviderApplication { public static void main( String[] args ) { SpringApplication.run(HystrixProviderApplication.class, args); } }
4.服务提供者Controller
package com.linjia.springcloud.controller; import com.linjia.springcloud.entity.Dept; import com.linjia.springcloud.service.DeptService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** * @date 2018/5/14 */ @RestController public class DeptController { private static final Logger logger= LoggerFactory.getLogger(DeptController.class); @Autowired private DeptService service; @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET) //在ribbon使用断路器 //一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法 @HystrixCommand(fallbackMethod = "processHystrix") public Dept get(@PathVariable("id") Long id) { Dept dept = this.service.get(id); if (null == dept) { throw new RuntimeException("该ID:" + id + "没有没有对应的信息"); } return dept; } public Dept processHystrix(@PathVariable("id") Long id) { return new Dept(id,"该ID:" + id + "没有没有对应的信息,null--@HystrixCommand","no this database in MySQL"); } }
启动注册中心,服务提供者,消费者,访问http://localhost/consumer/dept/get/11
返回:{"deptno":11,"dname":"该ID:11没有没有对应的信息,null--@HystrixCommand","db_source":"no this database in MySQL"}
方式二.服务提供者POM配置 (feign方式熔断配置)feign.hystrix
修改 microservicecloud-api 配置
2.1 新建 DeptClientServiceFallbackFactory
package com.linjia.springcloud.service; import com.linjia.springcloud.entity.Dept; import feign.hystrix.FallbackFactory; import org.springframework.stereotype.Component; import java.util.List; @Component // 不要忘记添加,不要忘记添加 public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> { @Override public DeptClientService create(Throwable throwable) { return new DeptClientService() { @Override public Dept get(long id) { return new Dept(id,"该ID:" + id + "没有没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭","no this database in MySQL"); } @Override public List<Dept> list() { return null; } @Override public boolean add(Dept dept) { return false; } }; } }
2.2 修改DeptClientService接口注释
package com.linjia.springcloud.service; import com.linjia.springcloud.entity.Dept; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import java.util.List; /** * * @Description: 修改microservicecloud-api工程,根据已经有的DeptClientService接口 * @author * @date 2018年4月21日 */ //@FeignClient(value = "MICROSERVICECLOUD-DEPT") @FeignClient(value = "MICROSERVICECLOUD-DEPT",fallbackFactory=DeptClientServiceFallbackFactory.class) public interface DeptClientService { @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET) public Dept get(@PathVariable("id") long id); @RequestMapping(value = "/dept/list", method = RequestMethod.GET) public List<Dept> list(); @RequestMapping(value = "/dept/add", method = RequestMethod.POST) public boolean add(Dept dept); }运行:启动注册中心,消息提供者,启动
microservicecloud-consumer-dept-feign (参考其他章节) 访问:
http://localhost:8089/consumer/dept/get/11 返回JSON:
{"deptno":11,"dname":"该ID:11没有没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭","db_source":"no this database in MySQL"}