SpringCloud概述
微服务架构概述
“微服务”源于MartinFowler的博文Microservices。
Martin说:微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间通过基于HTTP的RESTful API进行通信协作。被拆分成的每一个小型服务都围绕着系统中的某一项或者某些耦合度较高的业务功能进行构建,并且每个服务都维护着自身的数据存储、业务开发、自动化测试案例以及独立部署机制。由于有了轻量级的通信协作基础,所以这些微服务可以使用不同的语言来编写。
微服务架构

简而言之,微服务就是开发一组小型服务的方式来开发一个独立的应用系统,每个小型服务都运行在自己的进程中,并采用HTTP资源API轻量级的机制来互相通信。这些服务围绕业务功能进行构建,并能通过全自动的部署机制来进行独立部署。这些微服务可以使用不同的语言来编写,并且可以使用不同的数据库存储技术。
微服务优点
- 易于开发和维护
- 单个微服务启动快
- 故障隔离
- 局部修改容易且部署快
- 技术栈不受限制
SpringCloud官网
官网: http://projects.spring.io/spring-cloud/
手册: http://cloud.spring.io/spring-cloud-static/Dalston.SR2/
中文: https://springcloud.cc/
核心功能
- configuration management 配置中心
- service discovery 服务发现
- circuit breakers 断路器
- intelligent routing 智能路由
- micro-proxy 微代理
- control bus 控制总线
- one-time tokens 一次性令牌
- global locks 全局锁
- leadership election 选举算法
- distributed sessions 分布式会话
- cluster state 集群状态
核心架构图

规划内容和步骤
- 注册中心Eureka eureka+ provider-user + consumer-client
- 前端负载均衡Ribbon consumer-ribbon
- RESTFul简易封装 consumer-ribbon-feign
- 断路器支持 consumer-ribbon-feign-hystrix
- API网关 Zuul gateway-zuul
- 异构开发语言Sidecar sidecar+ nodejs
- 配置中心config configserver+consumer-ribbon-feign-hystrix
Eureka注册中心
eureka的注册中心原理

注意它的特点,结构类似于MessageQueue消息队列,服务(提供者、消费者)先都注册到注册中心。对于服务的消费者,**它的特点在于,不会每次都去注册中心获取,而是有本地缓存,加快访问性能。内部含有心跳机制,当注册中心信息改变,自动快速获取新的信息到本地。**心跳机制还保证分布式环境下,某个服务失败后,自动列表从注册中心移除。注册中心中保证所有可用的链接。
注册中心相关配置
自我保护模式
什么是自我保护模式?默认配置下,如果Eureka Server每分钟收到心跳续约的数量低于一个阈值(instance的数量(60/每个instance的心跳间隔秒数)自我保护系数),并且持续15分钟,就会触发自我保护。在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该Eureka Server节点就会自动退出自我保护模式。它的设计哲学前面提到过,那就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。
该模式可以通过eureka.server.enable-self-preservation = false来禁用,同时eureka.instance.lease-renewal-interval-in-seconds可以用来更改心跳间隔。
调用关系图
下图可见,对于负载均衡的Ribbon而言,它是基于消费者端的

注册中心搭建
pom.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <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"> <modelVersion>4.0.0</modelVersion> <groupId>eureka.server</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
|
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| security: basic: enabled: true user: name: user password: password123 server: port: 8761 eureka: client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://user:password123@localhost:8761/eureka logging: level: root: INFO
|
服务启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.ypjiao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer @SpringBootApplication public class RunServices { public static void main(String[] args) { SpringApplication.run(RunServices.class,args); } }
|
地址:http://localhost:8761
启动后可通过地址访问各种微服务实例
Eureka服务提供者
服务提供者搭建
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <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"> <modelVersion>4.0.0</modelVersion> <groupId>com.ypjiao</groupId> <artifactId>eureka-supplier</artifactId> <version>0.0.1-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
|
application.yml
1 2 3 4 5 6 7 8 9 10 11 12
| server: port: 7900 spring: application: name: supplier-user eureka: client: serviceUrl: defaultZone: http://user:password123@localhost:8761/eureka logging: level: root: INFO
|
Controller.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.ypjiao.controller;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class SupplierController { @RequestMapping("/supplier/{name}") public String getName(@PathVariable String name){ return "hello,"+name; } }
|
服务启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.ypjiao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableEurekaClient public class RunSupplier1 { public static void main(String[] args) { SpringApplication.run(RunSupplier1.class,args); } }
|
Eureka服务消费者
服务消费者搭建
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <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"> <modelVersion>4.0.0</modelVersion> <groupId>com.ypjiao</groupId> <artifactId>ribbon-client</artifactId> <packaging>pom</packaging> <version>0.0.1-SNAPSHOT</version> <modules> <module>Tomorow</module> </modules>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
|
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13
| server: port: 7902 spring: application: name: ribbon-client eureka: client: serviceUrl: defaultZone: http://user:password123@localhost:8761/eureka logging: level: root: INFO
|
Controller.java
- RestTemplate对象是在RunApp中声明并创建的,用它才可以实现负载均衡,同时注意url中的地址为VIP虚拟IP,为application.yml中配置的application-name。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.ypjiao.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;
@RestController public class RibbonController { @Autowired RestTemplate restTemplate; @RequestMapping("/ribbon/{name}") String getName(@PathVariable String name){ String url = "http://SUPPLIER-USER/supplier/"+name; return restTemplate.getForObject(url,String.class); }
}
|
服务启动类
- @LoadBalanced注解:用于制定RestTemplate对象的负载均衡
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.ypjiao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate;
@SpringBootApplication @EnableEurekaClient public class RunRibbon { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RunRibbon.class,args); } }
|
Ribbon负载均衡
Ribbon概述
- Ribbon是基于客户端的,因此需要在消费者客户端中进行配置

负载均衡策略
常见的负载均衡策略有三种
- 第一种也是默认为轮询
- 第二种为random随机
- 第三种为WeightedResponseTimeRule,响应时间
负载均衡型消费者搭建
ribbon其实无异于普通消费者,只需对代码中Template对象添加负载均衡注解@LoadBlance即可,此外还需切换调用虚拟IP的方法来实现负载均衡
ribbon相关jar包已存在于eureka的jar包中,因此无需更改pom文件
Controller.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.ypjiao.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;
@RestController public class RibbonController { @Autowired RestTemplate restTemplate; @RequestMapping("/ribbon/{name}") String getName(@PathVariable String name){ String url = "http://SUPPLIER-USER/supplier/"+name; return restTemplate.getForObject(url,String.class); }
}
|
服务启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.ypjiao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate;
@SpringBootApplication @EnableEurekaClient public class RunRibbon { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RunRibbon.class,args); } }
|
Ribbon随机负载算法配置
Ribbon规则配置类
需先自定义一个规则配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule;
@Configuration public class RibbonRuleConfig { @Bean public IRule ribbonRule(){ return new RandomRule(); } }
|
服务启动类
在原有的基础上新增@RibbonClient注解配置规则即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.ypjiao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate;
@SpringBootApplication @EnableEurekaClient @RibbonClient(name="provider-user", configuration=RibbonRuleConfig.class) public class RunRibbon { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RunRibbon.class,args); } }
|
Feign服务消费者(申明式客户端)
Feign概述
Feigh是一个声明式web服务客户端。它能让开发web服务变得容易。使用Feign需要创建一个接口并注解它。它拥有包括Feign注解和JAX-RS注解的可插拔支持。它还支持可插拔的编码器和解码器。Spring Cloud拥有Spring MVC支持,并使用Spring Web中默认同样的HttpMessageConverters。在使用Feign时,SpringCloud集成了Ribbon和Eureka来提供负载均衡的HTTP客户端。
总结:Feign简化HttpClient开发,封装了JAX-RS和SprinMVC的注解,学习成本很低。
Feign原理
- 首先通过@EnableFeignCleints注解开启FeignCleint
- 根据Feign的规则实现接口,并加@FeignCleint注解
- 程序启动后,会进行包扫描,扫描所有的@ FeignCleint的注解的类,并将这些信息注入到ioc容器中。
- 当接口的方法被调用,通过jdk的代理,来生成具体的RequesTemplate
- RequesTemplate在生成Request
- Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
- 最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。
原文链接:https://blog.csdn.net/forezp/article/details/73480304
服务提供者搭建
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <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"> <modelVersion>4.0.0</modelVersion> <groupId>com.ypjiao</groupId> <artifactId>feign=client</artifactId> <version>0.0.1-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
|
application.yml
1 2 3 4 5 6 7 8 9 10
| server: port: 7903 spring: application: name: feign-client eureka: client: serviceUrl: defaultZone: http://user:password123@localhost:8761/eureka
|
FeignService.java
以接口对外暴露,从而封装底层操作。
- 注解:
- @FeignClient:申明该接口为Feign管理
- value值中指定对应的服务提供者
- config:指定具体的配置类
- fallback:Feign的降级
- @RequestMapping:将来作为请求模板的一员指定请求路径
- @PathVariable:指定请求参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.ypjiao.controller;
import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(value = "supplier-user") public interface FeignService {
@RequestMapping("/supplier/{name}") String getName(@PathVariable("name") String value); }
|
FeignConfig.java
Feign相关配置设置
1 2 3 4 5 6 7 8
| @Configuration public class FeignConfig { @Bean public Retryer feignRetryer() { return new Retryer.Default(100, SECONDS.toMillis(1), 5); } }
|
Controller.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.ypjiao.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class FeignController { @Autowired FeignService feignService; @RequestMapping("/ypFeign/{name}") String getName(@PathVariable String name){ return feignService.getName(name); } }
|
服务启动类
- 注解说明
- @EnableFeignClients:开启Feign功能,这样容器才回去扫描带有@FeignClient注解的接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.ypjiao;
import org.springframework.boot.SpringApplication; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringCloudApplication @EnableFeignClients public class RunFeign { public static void main(String[] args) { SpringApplication.run(RunFeign.class,args); } }
|
Feign实现降级
Fallback.java
直接实现消费者的CartFeign接口,给每个方法设置异常时的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.ypjiao.feign;
import org.springframework.stereotype.Component;
@Component public class FeignServiceFallback implements FeignService{ @Override public SysResult mycart(Long userId) { return SysResult.build(400, "mycart error."); } @Override public SysResult save(Cart cart) { return SysResult.build(400, "save error."); } @Override public SysResult update(Cart cart) { return SysResult.build(400, "update error."); } @Override public SysResult delete(Cart cart) { return SysResult.build(400, "delete error."); } }
|
修改FeignService.java
需指定降级配置
1 2 3 4 5 6 7 8 9 10 11 12
| package com.ypjiao.feign;
import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(value = "supplier-user", fallback = CartFeignFallback.class) public interface FeignService { @RequestMapping("/supplier/{name}") String getName(@PathVariable("name") String value); }
|
修改Application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| server: port: 9001
spring: application: name: jt-cart
eureka: client: serviceUrl: defaultZone: http://user:password123@localhost:8761/eureka
feign: hystrix: enabled: true logging: level: root: INFO
|
Feign调用过程

首先,提供者provider-user和消费者custorm-feign都注册到Eureka中。用户请求feign中的controller,feign中的controller调用feign定义的接口方法。接口的方法根据注解去找到eureka注册中心中的provider-user地址,然后请求远程provider-user所在服务器的地址,然后调用远程的provider-user提供者的具体服务。提供者响应返回json,json被feign封装传输给“接口”的返回值,“接口”在返回给feign的controller,最终响应给用户。
Hystrix断路器
Hystrx断路器概述
微服务的设计,服务分散在多个服务器上,服务之间互相调用,要调用的服务由于跨网络跨服务器调用,响应速度明显比传统项目单机调用慢很多,甚至由于网络涌动的不稳定的现象发生导致调用超时;还有类似级联失败、雪崩效应(依赖的基础服务宕机,关联的服务导致失败甚至宕机,就像滚雪球一样层层失败。)
如何解决这类新的问题呢?传统的机制就是超时机制。
良好的设计,在通过网络请求其他服务时,都必须设置超时时间。正常情况下,一个远程调用几十毫秒内返回。当要调用的服务不可用时或者网络问题,响应时间要等超时,如HttpClient几十秒才超时返回。通常,**一次远程调用对应一个线程/进程,如果大量的线程/进程得不到释放,并且越积越多,服务资源就会被耗尽,从而导致资深服务不可用。**所以必须为每个请求设置超时时间
特别像微服务这样基于多个服务,服务之间都是远程调用,如果一个服务长时间等待,用户体验会极差的,那怎么办呢?断路器模式应运而生。
**断路器可以实现快速失败,如果它在一段时间内检测到许多失败,如超时,就会强迫其以后的多个调用快速失败,**不再请求所依赖的服务,从而防止应用程序不断地尝试执行可能会失败的操作,这样应用程序可以继续执行而不用等待修正错误,或者浪费CPU时间去等待长时间的超时。断路器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。
断路器几个状态

- 关闭:当访问没有问题时,断路器处于关闭未使用。
- 打开:当访问开始出现异常,错误次数增多,达到阀值时就会打开断路器,这样服务直接访问断路器,进行快速失败返回。
- 半开:那服务一直走断路器,系统就没法用了,万一被调用的服务已经稳定了呢。断路器的优势就来了,过一定时间窗口后(若干秒)它就会自动分流一部分服务再去尝试访问之前失败的服务。如果继续失败,那就不再转发,如果成功了,成功率高了,那会关闭断路器。
断路器搭建
- 由于Feign是基于Hystrix因此pom文件和Feign一致
- SpringClouldApplication注解内置开启hystrix
application.yml
1 2 3 4 5 6 7 8 9 10
| server: port: 9001 spring: application: name: consumer-feign-hystrix eureka: client: serviceUrl: defaultZone: http://user:password123@localhost:8761/eureka
|
修改FeignController
- 注解说明
- @HystrixCommand:为注解的方法指定断路方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.ypjiao.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class FeignController { @Autowired FeignService feignService; @RequestMapping("/ypFeign/{name}") @HystrixCommand(fallbackMethod = "fallbackfeign") String getName(@PathVariable String name){ return feignService.getName(name); } String fallbackfeign(String name){ return "我是断路器,你好,"+name; } }
|
Zuul网关
API网关概述
通常来说,使用
API 网关是更好的解决方式。API 网关是一个服务器,也可以说是进入系统的唯一节点。API 网关封装内部系统的架构,并且提供 API 给各个客户端。它还可能还具备授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等功能。下图展示了一个适应当前架构的 API 网关
Zuul搭建
Zuul的核心还yml配置文件,用映射关系来匹配请求和访问的服务
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <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"> <modelVersion>4.0.0</modelVersion> <groupId>com.ypjiao</groupId> <artifactId>eureka-zull</artifactId> <version>0.0.1-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
|
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| server: port: 7904
spring: application: name: zull-service
eureka: client: serviceUrl: defaultZone: http://user:password123@localhost:8761/eureka
logging: level: root: INFO
zuul: routes: app-a: path: /user/** serviceId: feign-client app-b: path: /sidecar/** serviceId: eureka-sidecar
|
服务启动类
- 注解说明
- @EnableZuulProxy:开启zuul配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.ypjiao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy @SpringBootApplication public class RunZull { public static void main(String[] args) { SpringApplication.run(RunZull.class,args); } }
|
基于Zuul的断路器
如果继续使用基于消费者端的Hystrix断路器,则断路器会失效,因此只能在zuul中配置断路器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| package com.ypjiao.fallback;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream;
@Component public class ZullFallback implements ZuulFallbackProvider { @Override public String getRoute() { return "*"; }
@Override public ClientHttpResponse fallbackResponse() { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.BAD_REQUEST; }
@Override public int getRawStatusCode() throws IOException { return HttpStatus.BAD_REQUEST.value(); }
@Override public String getStatusText() throws IOException { return HttpStatus.BAD_REQUEST.getReasonPhrase(); }
@Override public void close() {
} @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream(("fallbackzull"+ZullFallback.this.getRoute()).getBytes()); }
@Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON_UTF8); return headers; } }; } }
|
sidecar异构系统
sidecar概述
如果有非jvm的开发语言开发的项目,想使用Eureka注册中心、Ribbon负载均衡、ConfigServer配置中心,就可以使用Sidecar。Sidecar可以通过Http api的方式访问。异构语言项目应该实现一个心跳检查,这样Eureka就能知道程序的死活。
注意:运行Sidecar必须在断路器、Eureka客户端、Zuul之上。
异构系统搭建
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <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"> <modelVersion>4.0.0</modelVersion> <groupId>com.ypjiao</groupId> <artifactId>eureka-sidecar</artifactId> <version>0.0.1-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-sidecar</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
|
applicaton.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| server: port: 7905 spring: application: name: eureka-sidecar eureka: client: serviceUrl: defaultZone: http://user:password123@localhost:8761/eureka
sidecar: port: 8060 health-uri: http://localhost:8060/health.json
|
服务启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.ypjiao;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.sidecar.EnableSidecar;
@EnableSidecar @SpringBootApplication public class RunSideCar { public static void main(String[] args) { SpringApplication.run(RunSideCar.class,args); } }
|