一.简介

Spring Cloud 为开发者提供了快速构建分布式系统中一些常见模式的工具(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式 会话、集群状态)。使用 Spring Cloud 开发人员可以快速建立实现这些模式的服务和应用程序。 它们将在任何分布式环境中运行良好,包括开发人员自己的笔记本电脑、裸机数据中心和 Cloud Foundry 等托管平台。SpringCloud 早期对于分布式中所需组件 并未自己完全独立开发,而是整合了Netflix公司的各种组件,对其稍加整合形成就形成了 spring-cloud-netflix-xxxx-starter 。

简单的说,就是SpringCloud 是一整套分布系统的解决方案。不是一个技术,而是一个技术栈。

二.特征

Spring Cloud 专注于为典型用例和可扩展性机制提供良好的开箱即用体验以覆盖其他用例。

  • 分布式/版本化配置
  • 服务注册和发现
  • 路由
  • 服务到服务呼叫
  • 负载均衡
  • 断路器
  • 全局锁
  • 领导选举和集群状态
  • 分布式消息传递

三.版本问题

SpringCloud 版本与SpringBoot存在一定的关系,下表概述了 Spring Cloud 的哪个版本映射到 Spring Boot 的哪个版本。

Release Train Boot Version
2020.0.x aka Ilford 2.4.x, 2.5.x (Starting with 2020.0.3)
Hoxton 2.2.x, 2.3.x (Starting with SR5)
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x

Spring Cloud Dalston、Edgware、Finchley 和 Greenwich 都已达到生命周期终止状态,不再受支持。

四.服务注册与发现

对于一个分布式/微服务系统而言,整个系统中包含了大量的服务,这些服务中,必定存在某些服务相互调用的关系,调用方也就是 **服务消费者 被调用方 就是服务提供者**。消费者如何发现提供者呢(服务者的IP+端口)?同一提供者的多个实例如何选择呢?

在传统的系统部署中,服务运行在一个固定的已知的 IP 和端口上,如果一个服务需要调用另外一个服务,可以通过地址直接调用

在微服务架构下,服务实例的启动和销毁是很频繁的,服务地址在动态的变化,而且,由于自动扩展,失败和更新,服务实例的配置也经常变化,所以,无法通过硬编码服务地址的方法来访问该服务。因此,需要设置专门的服务来对实时变化的服务状态进行同步。 所以这时需要引入一个服务注册中心。

src=http___img.mp.itc.cn_upload_20170405_e6727ed9739c4724abb9750073b6a90f_th.jpeg&refer=http___img.mp.itc.jpg

目前微服务的服务发现机制主要包含三个角色:服务提供者、服务消费者和服务注册表

  • 服务提供者(Service Provider):服务启动时将服务信息注册到服务注册表,服务退出时将服务注册表的服务信息删除掉
  • 服务消费者(Service Consumer):从服务注册表获取服务提供者的最新网络位置等服务信息,维护与服务提供者之间的通信。
  • 服务注中心(Service Registry):联系服务提供者和服务消费者的桥梁,维护服务提供者的最新网络位置等服务信息

4.1/ 服务注册

SpringCloud 前面说到它是微服务系统的技术解决方案,但是很多分布式系统中间件,并不是由Spring开发,而是整合第三方的技术来实现。比如服务注册中可以使用 Eureka 、Zookeeper、Consul、Nacos 。
image.png

中间件: 中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。 中间件是一种独立的系统软件服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源,中间件位于客户机服务器的操作系统之上,管理计算资源和网络通信。

4/2 服务发现

  • 客户端服务发现模式

无标题.png

  • 服务端发现模式

无标题2.png

4/3 Eureka服务注册与发现

步骤① 创建项目

image.png

image.png

步骤② 引入依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-test</artifactId>
  9. <scope>test</scope>
  10. </dependency>
  11. </dependencies>
  12. <dependencyManagement>
  13. <dependencies>
  14. <dependency>
  15. <groupId>org.springframework.cloud</groupId>
  16. <artifactId>spring-cloud-dependencies</artifactId>
  17. <version>Hoxton.SR9</version>
  18. <type>pom</type>
  19. <scope>import</scope>
  20. </dependency>
  21. </dependencies>
  22. </dependencyManagement>

步骤③ 编写配置文件

server:
  port: 8888
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

步骤④ 添加注解

在主启动类上添加注解 @EnableEurekaServer 启动Eureka服务器。

@SpringBootApplication
@EnableEurekaServer
public class EurekaServer8888Application {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer8888Application.class, args);
    }
}

步骤⑤ 测试

image.png

步骤⑥ 创建服务提供者

image.png
image.png

步骤⑦ 引入依赖

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  </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>Hoxton.SR9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
</dependencyManagement>

步骤⑧ 编写配置文件

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8889/eureka/
  instance:
    appname: provider-a
    hostname: localhost
server:
  port: 8081

步骤⑨ 添加注解

@EnableEurekaClient 表示启用eureka客户端功能,这样这个程序将作为客户端注册到eureka服务端。使用 @EnableDiscoveryClient 也是可以的,这个注解功能更大,它兼容不同的注册中心。

@SpringBootApplication
@EnableEurekaClient
public class ProviderAApplication {

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

启动服务

步骤⑩ 测试

image.png

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%(eureka.server.renewal-percent-threshold = 0.85),如果低于 85%,就会触发 Eureka 的保护机制,Eureka Server 会将这些实例保护起来,让这些实例不会过期(即注册中心服务列表不会剔除该实例),但是在保护期内如果服务刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等。

五.负载均衡服务调用

5.1 概念

当服务消费者调用 服务提供者时,需要询问服务注册中心服务提供方法的地址,这时服务注册中心下发可用的服务地址列表,客户端在从服务地址列表中选择一个地址请求。这里地址的过程就是负载均衡。Eureak采用的是 客户端侧 实现负载均衡。
无标题.png

5.2 Ribbon 客户端负载均衡器

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

5.2.1、Ribbon 引入

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

5.2.2、Ribbon 负载均衡策略

负载均衡策略

  • RandomRule:随机策略
  • RoundRobbinRule:轮询策略
  • WeightedResponseTimeRule:默认会采用轮询的策略,后续会根据服务的响应时间,自动给你分配权重
  • BestAvailableRule:根据被调用方并发数最小的去分配

采用注解的形式

@Bean
public IRule robbinRule(){
    return new RandomRule();
}

配置文件去指定负载均衡的策略(推荐)

# 指定具体服务的负载均衡策略
SEARCH:      # 编写服务名称
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule  # 具体负载均衡使用的类

5.3 RestTemplate 客户端

RestTemplate 是一个访问Rest接口的工具,实际上它就是一个Http客户端,可以发送Http请求对api接口发起Http请求而已,底层做了封装,使用起来比较方便,可以整合Ribbon负载均衡器

5.3.1 整合Ribbon

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

5.3.2 使用RestTemplate

@RequestMapping("/test.do")
@ResponseBody
public Object test(){
    Object forObject = restTemplate.getForObject("http://java/hello.do", String.class);
    return forObject;
}

六、声明式客户端