SpringCloud快速入门

文章目录

  • 1.初识 SpringCloud
    • 1.1.微服务
    • 1.2.简介
  • 2.Eureka 注册中心
    • 2.1.简易模拟一个微服务
      • 2.1.1.搭建EurekaServer:
      • 2.1.2.注册到Eureka
      • 2.1.3.从Eureka获取服务
    • 2.2.Eureka详解
      • 2.2.1.基础架构
      • 2.2.2.高可用的Eureka Server
      • 2.2.3.服务提供者
      • 2.2.4.服务消费者
      • 2.2.5.失效剔除和自我保护
  • 3.Ribbon负载均衡
    • 3.1.开启负载均衡
    • 3.2.获取访问原理
    • 3.3.负载均衡策略
  • 4.Hystrix
    • 4.1.服务雪崩
    • 4.2.熔断
    • 4.3.降级
    • 4.4.实践一下
  • 5.Feign
    • 5.1.快速使用
    • 5.2.负载均衡
    • 5.3.Hystrix支持
    • 5.4.请求压缩(了解)
    • 5.5.日志级别(了解)
  • 6.Zuul 网关
    • 6.1.配置路由
    • 6.2.过滤器
      • 6.2.1.ZuulFilter
      • 6.2.2.生命周期
      • 6.2.3.使用场景
    • 6.3.自定义过滤器
    • 6.4.负载均衡和熔断
  • 7.Config 配置中心
    • Spring Cloud Bus
  • 8.总结

1.初识 SpringCloud

1.1.微服务

微服务的特点:

  • 单一职责:微服务中每一个服务都对应唯一的业务能力,做到单一职责
  • 微:微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。
  • 面向服务:面向服务是说每个服务都要对外暴露Rest风格服务接口API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供 Rest 的接口即可。
  • 自治:自治是说服务间互相独立,互不干扰
    • 团队独立:每个服务都是一个独立的开发团队,人数不能过多。
    • 技术独立:因为是面向服务,提供Rest接口,使用什么技术没有别人干涉
    • 前后端分离:采用前后端分离开发,提供统一Rest接口,后端不用再为PC、移动段开发不同接口
    • 数据库分离:每个服务都使用自己的数据源
    • 部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护

1.2.简介

Spring 最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。官网

SpringCloud 也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:

  • Eureka:服务治理组件,包含服务注册中心,服务注册与发现机制的实现。(服务治理,服务注册/发现)
  • Zuul:网关组件,提供智能路由,访问过滤功能
  • Ribbon:客户端负载均衡的服务调用组件(客户端负载)
  • Feign:服务调用,给予Ribbon和Hystrix的声明式服务调用组件 (声明式服务调用)
  • Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和为故障提供强大的容错能力。(熔断、断路器,容错)

附图一张,看不懂没关系,回头再回顾

出处:写的不错

SpringCloud快速入门_第1张图片

2.Eureka 注册中心

总的来说,Eureka 就是一个服务发现框架。写的不错

我们就可以来看关于 Eureka 的一些基础概念了,你会发现这东西理解起来怎么这么简单。

服务发现:其实就是一个“中介”,整个过程中有三个角色:服务提供者(出租房子的)、服务消费者(租客)、服务中介(房屋中介)

服务提供者: 就是提供一些自己能够执行的一些服务给外界。

服务消费者: 就是需要使用一些服务的“用户”。

服务中介: 其实就是服务提供者和服务消费者之间的“桥梁”,服务提供者可以把自己注册到服务中介那里,而服务消费者如需要消费一些服务(使用一些功能)就可以在服务中介中寻找注册在服务中介的服务提供者。

服务注册 Register

官方解释:当 Eureka 客户端向 Eureka Server 注册时,它提供自身的元数据,比如IP地址、端口,运行状况指示符URL,主页等。

结合中介理解:房东 (提供者 Eureka Client Provider)在中介 (服务器 Eureka Server) 那里登记房屋的信息,比如面积,价格,地段等等(元数据 metaData)。

服务续约 Renew

官方解释:Eureka 客户会每隔30秒(默认情况下)发送一次心跳来续约。 通过续约来告知 Eureka ServerEureka 客户仍然存在,没有出现问题。 正常情况下,如果 Eureka Server 在90秒没有收到 Eureka 客户的续约,它会将实例从其注册表中删除。

结合中介理解:房东 (提供者 Eureka Client Provider) 定期告诉中介 (服务器 Eureka Server) 我的房子还租(续约) ,中介 (服务器Eureka Server) 收到之后继续保留房屋的信息。

获取注册列表信息 Fetch Registries

官方解释:Eureka 客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与 Eureka 客户端的缓存信息不同, Eureka 客户端自动处理。如果由于某种原因导致注册列表信息不能及时匹配,Eureka 客户端则会重新获取整个注册表信息。 Eureka 服务器缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka 客户端和 Eureka 服务器可以使用JSON / XML格式进行通讯。在默认的情况下 Eureka 客户端使用压缩 JSON 格式来获取注册列表的信息。

结合中介理解:租客(消费者 Eureka Client Consumer) 去中介 (服务器 Eureka Server) 那里获取所有的房屋信息列表 (客户端列表 Eureka Client List) ,而且租客为了获取最新的信息会定期向中介 (服务器 Eureka Server) 那里获取并更新本地列表。

服务下线 Cancel

官方解释:Eureka客户端在程序关闭时向Eureka服务器发送取消请求。 发送请求后,该客户端实例信息将从服务器的实例注册表中删除。该下线请求不会自动完成,它需要调用以下内容:DiscoveryManager.getInstance().shutdownComponent();

结合中介理解:房东 (提供者 Eureka Client Provider) 告诉中介 (服务器 Eureka Server) 我的房子不租了,中介之后就将注册的房屋信息从列表中剔除。

服务剔除 Eviction

官方解释:在默认的情况下,当Eureka客户端连续90秒(3个续约周期)没有向Eureka服务器发送服务续约,即心跳,Eureka服务器会将该服务实例从服务注册列表删除,即服务剔除。

结合中介理解:房东(提供者 Eureka Client Provider) 会定期联系 中介 (服务器 Eureka Server) 告诉他我的房子还租(续约),如果中介 (服务器 Eureka Server) 长时间没收到提供者的信息,那么中介会将他的房屋信息给下架(服务剔除)。

2.1.简易模拟一个微服务

SpringCloud快速入门_第2张图片

模拟一个服务调用的场景,搭建两个工程:leyou-service-provider(服务提供方)和 leyou-service-consumer(服务调用方)。方便后面学习微服务架构

  • 服务提供方:使用 mybatis 操作数据库,实现对数据的增删改查;并对外提供rest接口服务。

  • 服务消费方:使用 restTemplate 远程调用服务提供方的 rest 接口服务,获取数据。

什么是 RestTemplate?

RestTemplateSpring提供的一个访问Http服务的客户端类,怎么说呢?就是微服务之间的调用是使用的 RestTemplate

2.1.1.搭建EurekaServer:

  1. 导入依赖:
  
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
    dependencies>
    
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>Finchley.SR2version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>
  1. 修改配置:
server:
  port: 10086 # 端口
spring:
  application:
    name: eureka-server # 应用名称,会在Eureka中显示
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:${server.port}/eureka
    register-with-eureka: false # 把自己注册到eureka服务列表
    fetch-registry: false # 拉取eureka服务信息
  server:
    enable-self-preservation: false # 关闭自我保护
    eviction-interval-timer-in-ms: 5000 # 每隔5秒钟,进行一次服务列表的清理
  1. 修改引导类,在类上添加@EnableEurekaServer注解:
@SpringBootApplication
@EnableEurekaServer // 声明当前springboot应用是一个eureka服务中心
public class LeyouEurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(LeyouEurekaApplication.class, args);
    }
}

2.1.2.注册到Eureka

注册服务,就是在服务上添加Eureka的客户端依赖,客户端代码会自动把服务注册到EurekaServer中。

修改 leyou-service-provider工程

  1. 在pom.xml中,添加springcloud的相关依赖。(省略)
  2. 在application.yml中,添加springcloud的相关依赖。
server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/leyou
    username: root
    password: root
    driverClassName: com.mysql.jdbc.Driver
  application:
    name: service-provider # 应用名称,注册到eureka后的服务名称
mybatis:
  type-aliases-package: cn.leyou.service.pojo
eureka:
  client:
    service-url: # EurekaServer地址
      defaultZone: http://127.0.0.1:10086/eureka
  1. 通过添加@EnableDiscoveryClient来开启Eureka客户端功能
@SpringBootApplication
@EnableDiscoveryClient
public class LeyouServiceProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(LeyouServiceApplication.class, args);
    }
}

2.1.3.从Eureka获取服务

接下来我们修改 leyou-service-consumer,尝试从EurekaServer获取服务。

  1. 在pom.xml中,添加springcloud的相关依赖。(省略)
  2. 在application.yml中,添加springcloud的相关依赖。
server:
  port: 80
spring:
  application:
    name: service-consumer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10086/eureka
  1. 在启动类开启Eureka客户端
@SpringBootApplication
@EnableDiscoveryClient // 开启Eureka客户端
public class ItcastServiceConsumerApplication {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceConsumerApplication.class, args);
    }
}
  1. 修改UserController代码,用DiscoveryClient类的方法,根据服务名称,获取服务实例:
@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient; // eureka客户端,可以获取到eureka中服务的信息

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        // 根据服务名称,获取服务实例。有可能是集群,所以是service实例集合
        List<ServiceInstance> instances = discoveryClient.getInstances("service-provider");
        // 因为只有一个Service-provider。所以获取第一个实例
        ServiceInstance instance = instances.get(0);
        // 获取ip和端口信息,拼接成服务地址
        String baseUrl = "http://" + instance.getHost() + ":" + instance.getPort() + "/user/" + id;
        User user = this.restTemplate.getForObject(baseUrl, User.class);
        return user;
    }

}

2.2.Eureka详解

2.2.1.基础架构

SpringCloud快速入门_第3张图片

Eureka 架构中的三个核心角色:

  • 服务注册中心

    Eureka的服务端应用,提供服务注册和发现功能,就是我们建立的这个注册 leyout-eureka。

  • 服务提供者

    提供服务的应用,可以是 SpringBoot 应用,也可以是其它任意技术实现,只要对外提供的是Rest风格服务即可。本例中就是我们实现的leyou-service-provider

  • 服务消费者

    消费应用从注册中心获取服务列表,从而得知每个服务方的信息,知道去哪里调用服务方。本例中就是我们实现的 leyou-service-consumer

2.2.2.高可用的Eureka Server

Eureka Server即服务的注册中心,在刚才的案例中,我们只有一个EurekaServer,事实上EurekaServer也可以是一个集群,形成高可用的Eureka中心。

服务同步

多个Eureka Server之间也会互相注册为服务,当服务提供者注册到Eureka Server集群中的某个节点时,该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到Eureka Server集群中的任意一个节点,都可以获取到完整的服务列表信息。

集群搭建,实现思路

我们假设要运行两个EurekaServer的集群,端口分别为:10086和10087。在两个启动器上配置好端口,启动即可。

所谓的高可用注册中心,其实就是把 EurekaServer 自己也作为一个服务进行注册,这样多个EurekaServer之间就能互相发现对方,从而形成集群。

  • 把 service-url 的值改成了另外一台EurekaServer的地址,而不是自己
  • 这里示例一个,另一个类似
server:
  port: 10086 # 端口
spring:
  application:
    name: eureka-server # 应用名称,会在Eureka中显示
eureka:
  client:
    service-url: # 配置其他Eureka服务的地址,而不是自己,比如10087
      defaultZone: http://127.0.0.1:10087/eureka

因为EurekaServer不止一个,因此注册服务到集群的时候,service-url 参数需要变化:

eureka:
  client:
    service-url: # EurekaServer地址,多个地址以','隔开
      defaultZone: http://127.0.0.1:10086/eureka,http://127.0.0.1:10087/eureka

2.2.3.服务提供者

服务提供者要向 EurekaServer 注册服务,并且完成服务续约等工作。

服务注册

服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-eureka=true参数是否正确,事实上默认就是 true 。如果值确实为 true,则会向EurekaServer发起一个Rest请求,并携带自己的元数据信息,Eureka Server 会把这些信息保存到一个双层Map结构中。

  • 第一层 Map 的 Key 就是服务 id,一般是配置中的spring.application.name属性
  • 第二层 Map 的 key 是服务的实例 id。一般host+ serviceId + port,例如:locahost:service-provider:8081
  • 值则是服务的实例对象,也就是说一个服务,可以同时启动多个不同实例,形成集群。

服务续约

在注册服务完成以后,服务提供者会维持一个心跳(定时向EurekaServer发起Rest请求),告诉EurekaServer:“我还活着”。这个我们称为服务的续约(renew)

有两个重要参数可以修改服务续约的行为:

eureka:
  instance:
    lease-expiration-duration-in-seconds: 10 # 10秒即过期
    lease-renewal-interval-in-seconds: 5 # 5秒一次心跳
  • lease-renewal-interval-in-seconds:服务续约(renew)的间隔,默认为30秒

  • lease-expiration-duration-in-seconds:服务失效时间,默认值90秒

  • 默认情况下每个30秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过90秒没有发送心跳,EurekaServer就会认为该服务宕机,会从服务列表中移除,这两个值在生产环境不要修改,默认即可。

  • 但是在开发时,这个值有点太长了,所以我们在开发阶段可以适当调小。

2.2.4.服务消费者

获取服务列表

当服务消费者启动时,会检测eureka.client.fetch-registry=true参数的值,如果为true,则会拉取Eureka Server服务的列表只读备份,然后缓存在本地。并且每隔30秒会重新获取并更新数据。我们可以通过下面的参数来修改:

eureka:
  client:
    registry-fetch-interval-seconds: 5

2.2.5.失效剔除和自我保护

服务下线

当服务进行正常关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线了”。服务中心接受到请求之后,将该服务置为下线状态。

失效剔除

有些时候,我们的服务提供方并不一定会正常下线,可能因为内存溢出、网络故障等原因导致服务无法正常工作。Eureka Server需要将这样的服务剔除出服务列表。因此它会开启一个定时任务,每隔60秒对所有失效的服务(超过90秒未响应)进行剔除。

可以通过eureka.server.eviction-interval-timer-in-ms参数对其进行修改,单位是毫秒,生产环境不要修改。

自我保护

我们关停一个服务,就会在Eureka面板看到一条警告:

GJGp6O.png

会触发了Eureka的自我保护机制。当一个服务未按时进行心跳续约时,Eureka会统计最近15分钟心跳失败的服务实例的比例是否超过了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka就会把当前实例的注册信息保护起来,不予剔除。生产环境下这很有效,保证了大多数服务依然可用。

但是这给我们的开发带来了麻烦, 因此开发阶段我们都会关闭自我保护模式:

eureka:
  server:
    enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
    eviction-interval-timer-in-ms: 1000 # 扫描失效服务的间隔时间(缺省为60*1000ms)

3.Ribbon负载均衡

​ 在刚才的案例中,我们启动了一个 leyou-service-provider,然后通过DiscoveryClient来获取服务实例信息,然后获取ip和端口来访问。

​ 在实际环境中,我们往往会开启很多个 leyou-service-provider的集群。此时我们获取的服务列表中就会有多个,到底该访问哪一个呢?

一般这种情况下我们就需要编写负载均衡算法,在多个实例列表中进行选择。不过Eureka中已经帮我们集成了负载均衡组件:Ribbon,简单修改代码即可使用。

什么是Ribbon?

Ribbon是一个基于HTTP 和 TCP 的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。

  • 服务端负载:服务器端负载均衡是对客户透明的,用户请求到LB服务器,真正的Application服务器是由LB服务器分发控制的,目前的实现有软件(ngnix,HA Proxy等)和硬件(F5等).
  • 客户端负载:是客户端软件的一部分,客户端获知到可用的服务器列表按一定的均衡策略,分发请求.

3.1.开启负载均衡

SpringCloud快速入门_第4张图片

因为Eureka中已经集成了Ribbon,所以我们无需引入新的依赖,直接修改代码。

修改 leyou-service-consumer的引导类,在 RestTemplate 的配置方法上添加@LoadBalanced注解:

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}

修改调用方式,不再手动获取 ip 和端口,而是直接通过服务名称调用:

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    //@Autowired
    //private DiscoveryClient discoveryClient; // 注入discoveryClient,通过该客户端获取服务列表
    //原方法
 /*   
 @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        // 根据服务名称,获取服务实例。有可能是集群,所以是service实例集合
        List instances = discoveryClient.getInstances("service-provider");
        // 因为只有一个Service-provider。所以获取第一个实例
        ServiceInstance instance = instances.get(0);
        // 获取ip和端口信息,拼接成服务地址
        String baseUrl = "http://" + instance.getHost() + ":" + instance.getPort() + "/user/" + id;
        User user = this.restTemplate.getForObject(baseUrl, User.class);
        return user;
    }
    */
    
    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        // 通过client获取服务提供方的服务列表,这里我们只有一个
        // ServiceInstance instance = discoveryClient.getInstances("service-provider").get(0);
        String baseUrl = "http://service-provider/user/" + id;
        User user = this.restTemplate.getForObject(baseUrl, User.class);
        return user;
    }

}

3.2.获取访问原理

为什么我们只输入了service名称就可以访问了呢?之前还要获取ip和端口。

显然有人帮我们根据service名称,获取到了服务实例的ip和端口。它就是LoadBalancerInterceptor

3.3.负载均衡策略

负载均衡,不管 Nginx 还是 Ribbon 都需要其算法的支持,如果我没记错的话 Nginx 使用的是 轮询和加权轮询算法。而在 Ribbon 中有更多的负载均衡调度算法,其默认是使用的 RoundRobinRule 轮询策略

  • RoundRobinRule轮询策略Ribbon 默认采用的策略。若经过一轮轮询没有找到可用的 provider,其最多轮询 10 轮。若最终还没有找到,则返回 null
  • RandomRule: 随机策略,从所有可用的 provider 中随机选择一个。
  • RetryRule: 重试策略。先按照 RoundRobinRule 策略获取 provider,若获取失败,则在指定的时限内重试。默认的时限为 500 毫秒。

我们是否可以修改负载均衡的策略呢?

我们注意一下 IRule 这个类:

SpringCloud快速入门_第5张图片

SpringBoot也帮我们提供了修改负载均衡规则的配置入口,在leyou-service-consumerapplication.yml中添加

格式是:{服务名称}.ribbon.NFLoadBalancerRuleClassName,值就是IRule的实现类。

server:
  port: 80
spring:
  application:
    name: service-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
service-provider:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

当然,在 Ribbon 中你还可以自定义负载均衡算法,你只需要实现 IRule 接口,然后修改配置文件或者自定义 Java Config 类。

4.Hystrix

SpringCloud快速入门_第6张图片

什么是 Hystrix?

​ 在分布式环境中,不可避免地会有许多服务依赖项中的某些失败。Hystrix是一个库,可通过添加等待时间容限和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的,所有这些都可以提高系统的整体弹性。参考

  • 总体来说 Hystrix 就是一个能进行 熔断降级 的库,通过使用它能提高整个系统的弹性。

4.1.服务雪崩

讲一个场景:

此时我们整个微服务系统是这样的。服务A调用了服务B,服务B再调用了服务C,但是因为某些原因,服务C顶不住了,这个时候大量请求会在服务C阻塞。

服务C阻塞了还好,毕竟只是一个系统崩溃了。但是请注意这个时候因为服务C不能返回响应,那么服务B调用服务C的的请求就会阻塞,同理服务B阻塞了,那么服务A也会阻塞崩溃。

请注意,为什么阻塞会崩溃。因为这些请求会消耗占用系统的线程、IO 等资源,消耗完你这个系统服务器不就崩了么。

这就叫 服务雪崩

Hystix解决雪崩问题的手段有两个:

  • 线程隔离,服务降级
  • 服务熔断

那么什么是 熔断和降级 呢?

4.2.熔断

所谓 熔断 就是服务雪崩的一种有效解决方案。当指定时间窗内的请求失败率达到设定阈值时,系统将通过 断路器 直接将此请求链路断开。

也就是我们上面服务B调用服务C在指定时间窗内,调用的失败率到达了一定的值,那么 Hystrix 则会自动将 服务B与C 之间的请求都断了,以免导致服务雪崩现象。

其实这里所讲的 熔断 就是指的 Hystrix 中的 断路器模式 ,你可以使用简单的 @HystrixCommand 注解来标注某个方法,这样 Hystrix 就会使用 断路器 来“包装”这个方法,每当调用时间超过指定时间时(默认为1000ms),断路器将会中断对这个方法的调用。

熔断的3个状态:

  • Closed:关闭状态,所有请求都正常访问。
  • Open:打开状态,所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全打开。默认失败比例的阈值是50%,请求次数最少不低于20次。
  • Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全关闭断路器,否则继续保持打开,再次进行休眠计时

4.3.降级

Hystrix 为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队.加速失败判定时间。

用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超时,则会进行降级处理,

服务降级:优先保证核心服务,而非核心服务不可用或弱可用。

以我的理解,降级是为了更好的用户体验,当一个方法调用异常时,通过执行另一种代码逻辑来给用户友好的回复。这也就对应着 Hystrix后备处理 模式。你可以通过设置 fallbackMethod 来给一个方法设置备用的代码逻辑。比如这个时候有一个热点新闻出现了,我们会推荐给用户查看详情,然后用户会通过id去查询新闻的详情,但是因为这条新闻太火了(比如最近什么*易对吧),大量用户同时访问可能会导致系统崩溃,那么我们就进行 服务降级 ,一些请求会做一些降级处理比如当前人数太多请稍后查看等等。

  • 用户的请求故障时,不会被阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回友好的提示信息) 。

  • 服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个依赖服务对应的线程池中的资源,对其它服务没有响应。

触发Hystix服务降级的情况:

  • 线程池已满
  • 请求超时

4.4.实践一下

  1. 导入依赖,在 leyou-service-consumer 的 pom.xml 中
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
  1. 开启熔断,使用 @EnableCircuitBreaker注解

我们可以使用 @SpringCloudApplication 注解,他默认开启了三个注解

/**
 * @author Spencer Gibb
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}

如下:

@SpringCloudApplication
public class ItcastServiceConsumerApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceConsumerApplication.class, args);
    }
}
  1. 编写降级逻辑

我们改造 leyou-service-consumer,当目标服务的调用出现故障,我们希望快速失败,给用户一个友好提示。因此需要提前编写好失败时的降级处理逻辑,要使用 HystixCommond 来完成:

  • @HystrixCommand(fallbackMethod = "queryByIdFallBack"):用来声明一个降级逻辑的方法
@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping
    @ResponseBody
    @HystrixCommand(fallbackMethod = "queryUserByIdFallBack")
    public String queryUserById(@RequestParam("id") Long id) {
        String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
        return user;
    }

    public String queryUserByIdFallBack(Long id){
        return "请求繁忙,请稍后再试!";
    }
}

要注意,因为熔断的降级逻辑方法必须跟正常逻辑方法保证:相同的参数列表和返回值声明。失败逻辑中返回User对象没有太大意义,一般会返回友好提示。所以我们把queryById的方法改造为返回String,反正也是Json数据。这样失败逻辑中返回一个错误说明,会比较方便。

我们可以把 Fallback配置加在类上,实现默认fallback:

  • @DefaultProperties(defaultFallback = "defaultFallBack"):在类上指明统一的失败降级方法
  • @HystrixCommand:在方法上直接使用该注解,使用默认的降级方法。
  • defaultFallback:默认降级方法,不用任何参数,以匹配更多方法,但是返回值一定一致
@Controller
@RequestMapping("consumer/user")
@DefaultProperties(defaultFallback = "fallBackMethod") // 指定一个类的全局熔断方法
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping
    @ResponseBody
    @HystrixCommand // 标记该方法需要熔断
    public String queryUserById(@RequestParam("id") Long id) {
        String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
        return user;
    }

    /**
     * 熔断方法
     * 返回值要和被熔断的方法的返回值一致
     * 熔断方法不需要参数
     * @return
     */
    public String fallBackMethod(){
        return "请求繁忙,请稍后再试!";
    }
}
  1. 设置超时

在之前的案例中,请求在超过1秒后都会返回错误信息,这是因为Hystix的默认超时时长为1,我们可以通过配置修改这个值:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 # 设置hystrix的超时时间为6000ms
  1. 模拟熔断

为了能够精确控制请求的成功或失败,我们在consumer的调用业务中加入一段逻辑:

@GetMapping("{id}")
@HystrixCommand
public String queryUserById(@PathVariable("id") Long id){
    if(id == 1){
        throw new RuntimeException("太忙了");
    }
    String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
    return user;
}
  • 这样如果参数是id为1,一定失败,其它情况都成功。
  • 我们疯狂访问id为1的请求时(超过20次),就会触发熔断。断路器会断开,一切请求都会被降级处理。
  • 此时你访问id为2的请求,会发现返回的也是失败,而且失败时间很短,只有几毫秒左右

不过,默认的熔断触发要求较高,休眠时间窗较短,为了测试方便,我们可以通过配置修改熔断策略:

hystrix:
  command:
    default:
      circuitBreaker:
        requestVolumeThreshold: 10
        sleepWindowInMilliseconds: 10000
        errorThresholdPercentage: 50
  • requestVolumeThreshold:触发熔断的最小请求次数,默认20
  • errorThresholdPercentage:触发熔断的失败请求最小占比,默认50%
  • sleepWindowInMilliseconds:休眠时长,默认是5000毫秒
  • 给个链接

5.Feign

SpringCloud快速入门_第7张图片

进入正题

在前面的学习中,我们使用了Ribbon的负载均衡功能,大大简化了远程调用时的代码

String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);

有没有更优雅的方式,来对这些代码再次优化呢?

聪明的小朋友肯定想到了,那就用 映射 呀,就像域名和IP地址的映射。我们可以将被调用的服务代码映射到消费者端,这样我们就可以 **“无缝开发” **啦。

  • OpenFeign 也是运行在消费者端的,使用 Ribbon 进行负载均衡,所以 OpenFeign 直接内置了 Ribbon

5.1.快速使用

  1. 导入依赖,改造 leyou-service-consumer工程
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
  1. 我们在启动类上,添加注解,开启Feign功能 ,@EnableFeignClients
@SpringCloudApplication
@EnableFeignClients // 开启feign客户端
public class ItcastServiceConsumerApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceConsumerApplication.class, args);
    }
}

删除RestTemplate:feign 已经自动集成了Ribbon负载均衡的RestTemplate。所以,此处不需要再注册RestTemplate。

  1. 添加 UserClient 接口:
@FeignClient(value = "service-provider") // 标注该类是一个feign接口
public interface UserClient {

    @GetMapping("user/{id}")
    User queryById(@PathVariable("id") Long id);
}
  • 首先这是一个接口,Feign 会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像
  • @FeignClient,声明这是一个Feign客户端,类似@Mapper注解。同时通过value属性指定服务名称
  • 接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果
  1. 改造原来的调用逻辑,调用UserClient接口:
@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private UserClient userClient;

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        User user = this.userClient.queryUserById(id);
        return user;
    }

}

5.2.负载均衡

Feign 中本身已经集成了 Ribbon 依赖和自动配置,因此我们不需要额外引入依赖,也不需要再注册RestTemplate对象。可以在配置文件中再额外配置

5.3.Hystrix支持

Feign 默认也有对 Hystrix 的集成:只不过,默认情况下是关闭的。我们需要通过下面的参数来开启:

在 consumer工程添加配置内容:

feign:
  hystrix:
    enabled: true # 开启Feign的熔断功能

但是,Feign中的Fallback配置不像hystrix中那样简单了。

首先,我们要定义一个类UserClientFallback,实现刚才编写的UserClient,作为fallback的处理类

@Component
public class UserClientFallback implements UserClient {

    @Override
    public User queryById(Long id) {
        User user = new User();
        user.setUserName("服务器繁忙,请稍后再试!");
        return user;
    }
}

然后在 UserFeignClient 中,指定刚才编写的实现类:

@FeignClient(value = "service-provider", fallback = UserClientFallback.class) // 标注该类是一个feign接口
public interface UserClient {

    @GetMapping("user/{id}")
    User queryUserById(@PathVariable("id") Long id);
}

5.4.请求压缩(了解)

Spring Cloud Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。

  • 通过下面的参数即可开启请求与响应的压缩功能
  • 同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置
    • 若不设置,数据类型、压缩大小下限均为默认值。
feign:
  compression:
    request:
      enabled: true # 开启请求压缩
      mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
      min-request-size: 2048 # 设置触发压缩的大小下限
    response:
      enabled: true # 开启响应压缩

5.5.日志级别(了解)

前面讲过,通过logging.level.xx=debug来设置日志级别。然而这个对 Fegin 客户端而言不会产生效果。因为@FeignClient注解修改的客户端在被代理时,都会创建一个新的 Fegin.Logger实例。我们需要额外指定这个日志的级别才可以。

  1. 设置 com.leyou 包下的日志级别都为 debug
logging:
  level:
    cn.leyou: debug
  1. 编写配置类,定义日志级别
@Configuration
public class FeignLogConfiguration {

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

这里指定的 Level 级别是FULL,Feign支持4种级别

  • NONE:不记录任何日志信息,这是默认值。
  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
  1. 在 FeignClient 中指定配置类:
@FeignClient(value = "service-privider", fallback = UserFeignClientFallback.class, configuration = FeignConfig.class)
public interface UserFeignClient {
    @GetMapping("/user/{id}")
    User queryUserById(@PathVariable("id") Long id);
}
  1. 重启项目,即可看到每次访问的日志

6.Zuul 网关

SpringCloud快速入门_第8张图片

进入正题

Zuul 是从设备和 web 站点到 Netflix 流应用后端的所有请求的前门。作为边界服务应用,Zuul 是为了实现动态路由、监视、弹性和安全性而构建的。它还具有根据情况将请求路由到多个 Amazon Auto Scaling Groups(亚马逊自动缩放组,亚马逊的一种云计算方式) 的能力

不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个网关,然后再由网关来实现 鉴权、动态路由等等操作。Zuul 就是我们服务的统一入口。

SpringCloud快速入门_第9张图片

Zuul 中最关键的就是 路由和过滤器 了。

6.1.配置路由

  1. 新建 leyou-zuul工程,导入依赖
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
  1. 开启 Eureka客户端发现功能
@SpringBootApplication
@EnableZuulProxy // 开启Zuul的网关功能
@EnableDiscoveryClient
public class ZuulDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(ZuulDemoApplication.class, args);
	}
}
  1. 配置路由

因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。

eureka:
  client:
    registry-fetch-interval-seconds: 5 # 获取服务列表的周期:5s
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
zuul:
  routes:
    service-provider: # 这里是路由id,随意写
      path: /service-provider/** # 这里是映射路径
      serviceId: service-provider # 指定服务名称

而大多数情况下,我们的路由名称往往和服务名会写成一样的。因此Zuul就提供了一种简化的配置语法:zuul.routes.=

比方说上面我们关于 service-provider 的配置可以简化为一条:

zuul:
  routes:
    service-provider: /service-provider/** # 这里是映射路径

默认的路由规则

在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁琐的。因此Zuul就指定了默认的路由规则:

  • 默认情况下,一切服务的映射路径就是服务名本身。例如服务名为:service-provider,则默认的映射路径就 是:/service-provider/**

也就是说,刚才的映射规则我们完全不配置也是OK的,不信就试试看。

  1. 路由前缀

我们通过zuul.prefix=/api来指定了路由的前缀,这样在发起请求时,路径就要以/api开头。

zuul:
  routes:
    service-provider: /service-provider/**
    service-consumer: /service-consumer/**
  prefix: /api # 添加路由前缀

拓展

  • ** 代表匹配多级任意路径
  • *代表匹配一级任意路径
zuul:
  prefix: /api # 路由路径前缀
  routes:
    item-service: /item/** # 商品微服务的映射路径
    search-service: /search/** #路由到搜索微服务
    user-service: /user/** # 用户微服务
  add-host-header: true #携带请求本身的head头信息
  sensitive-headers:   # 覆盖默认敏感头信息,设置为 null
  ignore-services: "*" # 服务名屏蔽
  ignore-patterns: **/auto/** # 路径屏蔽
  • add-host-header:使zuul 转发时,附带自身的host**,解决host地址变化的问题**,当然使用 nginx 时,需要在nginx的配置文件中添加 proxy_set_header Host $host;
  • sensitive-headers:Zuul内部有默认的过滤器,会对请求和响应头信息进行重组,过滤掉敏感的头信息,设置全局属性,可解决无法 拿到 cookie 问题

6.2.过滤器

Zuul 作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作我们往往是通过Zuul提供的过滤器来实现的,这样我们就能实现 限流灰度发布权限控制 等等。

6.2.1.ZuulFilter

ZuulFilter 是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法:

public abstract ZuulFilter implements IZuulFilter{

    abstract public String filterType();

    abstract public int filterOrder();
    
    boolean shouldFilter();// 来自IZuulFilter

    Object run() throws ZuulException;// IZuulFilter
}
  • shouldFilter:返回一个Boolean值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
  • run:过滤器的具体业务逻辑。
  • filterType:返回字符串,代表过滤器的类型。包含以下4种:
    • pre:请求在被路由之前执行
    • route:在路由请求时调用
    • post:在route和errror过滤器之后调用
    • error:处理请求时发生错误调用
  • filterOrder:通过返回的 int 值来定义过滤器的执行顺序,数字越小优先级越高

6.2.2.生命周期

这张是Zuul官网提供的请求生命周期图,清晰的表现了一个请求在各个过滤器的执行顺序。

SpringCloud快速入门_第10张图片
正常流程:

  • 请求到达首先会经过pre类型过滤器,而后到达route类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

异常流程:

  • 整个过程中,pre或者route过滤器出现异常,都会直接进入error过滤器,在error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
  • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,将最终结果返回给请求客户端。
  • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和route不同的是,请求不会再到达POST过滤器了。

6.2.3.使用场景

场景非常多:

  • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
  • 异常处理:一般会在error类型和post类型过滤器中结合来处理。
  • 服务调用时长统计:pre和post结合使用。

6.3.自定义过滤器

接下来,我们在Zuul编写拦截器,对用户的token进行校验,如果发现未登录,则进行拦截。

  1. 依赖导入,这里就省略了,示意一下
  2. 配置 yml

关注一下末尾的 filter ,并不是所有的路径我们都需要拦截,所以,我们需要在拦截时,配置一个白名单,如果在名单内,则不进行拦截。

server:
  port: 10010
spring:
  application:
    name: leyou-gateway
eureka:
  client:
    registry-fetch-interval-seconds: 5
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
zuul:
  prefix: /api # 路由路径前缀
  routes:
    item-service: /item/** # 商品微服务的映射路径
    search-service: /search/** #路由到搜索微服务
    user-service: /user/** # 用户微服务
    auth-service: /auth/** # 授权中心微服务
    cart-service: /cart/** # 购物车微服务
    order-service: /order/** # 购物车微服务
  add-host-header: true
  sensitive-headers:   # 覆盖默认敏感头信息
leyou:
  jwt:
    pubKeyPath: C:\\tmp\\rsa\\rsa.pub # 公钥地址
    cookieName: LY_TOKEN # token
  filter:   
    allowPaths:
      - /api/auth
      - /api/search
      - /api/user/register
      - /api/user/check
      - /api/user/code
      - /api/item
  1. 编写白名单的配置类
@Component
@ConfigurationProperties(prefix = "leyou.filter")
public class FilterProperties {

    private List<String> allowPaths;

    public List<String> getAllowPaths() {
        return allowPaths;
    }

    public void setAllowPaths(List<String> allowPaths) {
        this.allowPaths = allowPaths;
    }
}
  1. 编写 jwt 验证配置类
@Component
@ConfigurationProperties(prefix = "leyou.jwt")
public class JwtProperties {

    private String pubKeyPath;// 公钥

    private PublicKey publicKey; // 公钥

    private String cookieName; //cookie

    private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);

    /**
     * @PostContruct:在构造方法执行之后执行该方法
     */
    @PostConstruct
    public void init(){
        try {

            // 获取公钥和私钥
            this.publicKey = RsaUtils.getPublicKey(pubKeyPath);

        } catch (Exception e) {
            logger.error("初始化公钥和私钥失败!", e);
            throw new RuntimeException();
        }
    }
	//省略 getter与setter

}
  1. 重头:配置过滤器

    继承 ZuulFilter,重写里面方法,忘了的话,可跳至 6.2.1. 章节回顾。

@Component
@EnableConfigurationProperties({JwtProperties.class, FilterProperties.class})
public class LoginFilter extends ZuulFilter {
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private FilterProperties filterProperties;
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 10;
    }

    @Override
    public boolean shouldFilter() {
        //获取context
        RequestContext context = RequestContext.getCurrentContext();
        //获取request对象
        HttpServletRequest request = context.getRequest();
        //获取请求路径
        String url = request.getRequestURL().toString();
        //获取白名单
        List<String> allowPaths = this.filterProperties.getAllowPaths();
        // 判断白名单
        // 遍历允许访问的路径
        for (String allowPath : allowPaths) {
            if (StringUtils.contains(url,allowPath)){
                return  false;
            }
        }
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //获取context
        RequestContext context = RequestContext.getCurrentContext();
        //获取request对象
        HttpServletRequest request = context.getRequest();
        //获取token
        String token = CookieUtils.getCookieValue(request, this.jwtProperties.getCookieName());
        /*if (StringUtils.isBlank(token)){
            // 校验出现异常,返回403
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }*/
        try {
            //解析
            JwtUtils.getInfoFromToken(token,this.jwtProperties.getPublicKey());
        } catch (Exception e) {
            e.printStackTrace();
            // 校验出现异常,返回403
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        return null;
    }
}

6.4.负载均衡和熔断

Zuul 中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议我们手动进行配置:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2000 # 设置hystrix的超时时间为6000ms

7.Config 配置中心

当我们的微服务系统开始慢慢地庞大起来,那么多 ConsumerProviderEureka ServerZuul 系统都会持有自己的配置,这个时候我们在项目运行的时候可能需要更改某些应用的配置,如果我们不进行配置的统一管理,我们只能去每个应用下一个一个寻找配置文件然后修改配置文件再重启应用

那么有没有一种方法既能对配置文件统一地进行管理,又能在项目运行时动态修改配置文件呢?

Config 是什么?

Spring Cloud Config 为分布式系统中的外部化配置提供服务器和客户端支持。使用 Config 服务器,可以在中心位置管理所有环境中应用程序的外部属性。

SpringCloud快速入门_第11张图片

简单来说,Spring Cloud Config 就是能将各个 应用/系统/模块 的配置文件存放到 统一的地方然后进行管理(Git 或者 SVN)。

当然这里你肯定还会有一个疑问,如果我在应用运行时去更改远程配置仓库(Git)中的对应配置文件,那么依赖于这个配置文件的已启动的应用会不会进行其相应配置的更改呢?

答案是不会的。

什么?那怎么进行动态修改配置文件呢?这不是出现了 配置漂移 吗?

一般我们会使用 Bus 消息总线 + Spring Cloud Config 进行配置的动态刷新。

Spring Cloud Bus

用于将服务和服务实例与分布式消息系统链接在一起的事件总线。在集群中传播状态更改很有用(例如配置更改事件)。

你可以简单理解为 Spring Cloud Bus 的作用就是管理和广播分布式系统中的消息,也就是消息引擎系统中的广播模式。当然作为 消息总线Spring Cloud Bus 可以做很多事而不仅仅是客户端的配置刷新功能。

而拥有了 Spring Cloud Bus 之后,我们只需要创建一个简单的请求,并且加上 @ResfreshScope 注解就能进行配置的动态修改了,下面我画了张图供你理解。

SpringCloud快速入门_第12张图片

实现思路

  • 参考博客
  • 官方中文文档
  1. config 微服务,配置连接到 git 上(配置文件丢到远程了)
  2. 其它微服务,编写 bootstrap.yml ,连接 config 微服务,
  3. application.yml 一般只要写服务名就好了
  4. SpringCloudBus 就是把它放到消息队列中,比如 RabbitMQ

8.总结

这篇文章中我带大家初步了解了 Spring Cloud 的各个组件,他们有

  • Eureka 服务发现框架
  • Ribbon 进程内负载均衡器
  • Open Feign 服务调用映射
  • Hystrix 服务降级熔断器
  • Zuul 微服务网关
  • Config 微服务统一配置中心
  • Bus 消息总线

这时候再来看看这张图试试吧!

SpringCloud快速入门_第13张图片

你可能感兴趣的:(学习笔记)