admin管理员组

文章数量:1650921

1、【狂神说Java】SpringCloud最新教程IDEA版

视频教程:【狂神说Java】SpringCloud最新教程IDEA版
教程地址:https://www.bilibili/video/BV1jJ411S7xr
感谢狂神老师的教程

2、前言略

3.、SpringCloud入门概述

3.1、SpringCloud是什么

官网:https://spring.io/

SpringCloud,基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。

SpringCloud利用SpringBoot的开发便利性,巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等,它们都可以利用SpringBoot的开发风格做到一键启动和部署。

SpringBoot并没有重复造轮子,它只是将目前各家公司开发的比较成熟,经得起实际考验的服务框架组合起来,通过SpringBoot风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂,易部署和易维护的分布式系统开发工具包

SpringCloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。

3.2、SpringCloud和SpringBoot关系

  • SpringBoot专注于快速方便的开发单个个体微服务(jar)。
  • SpringCloud是关注全局的微服务协调治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,断路器,路由,微代理,时间总线,全局锁,决策竞选,分布式会话等等集成服务。
  • SpringBoot可以离开SpringCloud独立使用,开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系
  • SpringBoot专注于快速、方便地开发单个个体服务,SpringCloud关注全局服务治理框架

3.3、Dubbo 和 SpringCloud技术选型

1、分布式+服务治理Dubbo

目前成熟的互联网架构:应用服务化拆分 + 消息中间件。(传统型)

2、Dubbo 和 SpringCloud对比

可以看一下社区活跃度

http://github/dubbo

http://github/spring-cloud

结果

DubboSpring
服务注册中心ZookeeperSpring Cloud Netfilx Eureka
服务调用方式RPCREST API
服务监控Dubbo-monitorSpring Boot Admin
断路器不完善Spring Cloud Netflix Hystrix
服务网关Spring Cloud Netflix Zuul
分布式配置Spring Cloud Config
服务跟踪Spring Cloud Sleuth
消息总线Spring Cloud Bus
数据流Spring Cloud Stream
批量任务Spring Cloud Task

最大区别:SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式。

两种方式各有优劣。在一定程度上,SpringCloud牺牲了服务调用性能,但也避免了原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更加合适。

品牌机与组装机的区别

SpringCloud的功能比Dubbo更加强大,涵盖面更广,而且作为Spring的拳头项目,他也能够与Spring Framework、Spring Boot、Spring Data、Spring Batch等等其他Spring项目完美融合,这对于微服务而言值至关重要的。使用Dubbo构建的微服务架构就像组装电脑,各个环节选择自由度很高,但最终结构有可能因为一条内存条质量不行就点不亮了,让人不怎么放心,但是如果你是一名高手,那么这些都不是问题;而Spring Cloud就像品牌机,在Spring Source的整合下,做了大量的兼容测试,保证了机器拥有更高的稳定性,但是如果非要只用原装组件之外的东西,就需要对其基础有足够的了解。

解决问题领域不一样,Dubbo的定位是一款RPC框架,Spring Cloud的目标是微服务架构下的一站式解决方案

此后的重点:设计模式 + 微服务拆分思想

3.4、SpringCloud能干什么

  • Distributed/version configuration(分布式/版本控制配置)
  • Service registration and discovery(服务注册与发现)
  • Routing(路由)
  • Service-toservice calls(服务到服务的调用)
  • Load balancing(负载均衡配置)
  • Circuit Breakers(断路器)
  • Distributed messaging(分布式消息管理)

3.5 Spring Cloud下载

官网:https://spring.io/projects/spring-cloud

版本号有点特别

spring cloud是一个由众多独立子项目组合成的大型综合项目,每个子项目有不同的发行节奏,都维护者自己的发布版本号。Spring cloud通过一个资源清单BOM(Bill of Materials)来管理每个版本的子项目清单。为避免与子项目的发布号混淆,所以没有采用版本号的方式,而是通过命名的方式。

这些版本名称的命名采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序。比如:最早的Release版本(发行版本):Angel,第二个Release版本:Brixton,然后是Camden、Dalston、Edgware,目前最新的是Finchley版本。

参考书:

  • https://www.springcloud/spring-cloud-netflix.html
  • 中文API文档:https://www.springcloud/spring-cloud-dalston.html
  • SpringCloud中国社区:https://www.springcloud/ (没打开)
  • SpringCloud中文网:https://www.springcloud

4、SpringCloud Rest学习环境搭建:服务提供者

4.1 介绍

  • 使用一个Dept部门模块做一个微服务通用案例,Consumer消费者(Client)通过REST调用Provider提供者(Server)提供的服务。

  • 回顾Spring,SpringMVC,Mybatis等以往学习的知识。

  • Maven的分包分模块架构复习。

    一个简单的Maven模块结构是这样的:
    -- app-parent: 一个父项目(app-parent)聚合了很多子项目(app-util\app-dao\app-web...)
      |-- pom.xml
      |
      |-- app-core
      ||---- pom.xml
      |
      |-- app-web
      ||---- pom.xml
      ......
    

一个父工程带着多个Moudule子模块

MicroServiceCloud父工程(Project)下初次带着3个子模块(Module)

  • microservicecloud-api 【封装的整体entity/接口/公共配置等】
  • microservicecloud-consumer-dept-80 【服务提供者】
  • microservicecloud-provider-dept-8001 【服务消费者】

4.2 Spring Cloud版本选择

SpringBootSpringCloud关系
1.2.xAngel版本(天使)兼容SpringBoot1.2x
1.3.xBrixton版本(布里克斯顿)兼容SpringBoot1.3x,也兼容SpringBoot1.4x
1.4.xCamden版本(卡姆登)兼容SpringBoot1.4x,也兼容SpringBoot1.5x
1.5.xDalston版本(多尔斯顿)兼容SpringBoot1.5x,不兼容SpringBoot2.0x
1.5.xEdgware版本(埃奇韦尔)兼容SpringBoot1.5x,不兼容SpringBoot2.0x
2.0.xFinchley版本(芬奇利)兼容SpringBoot2.0x,不兼容SpringBoot1.5x
2.1.xGreenwich版本(格林威治)

实际开发版本关系

spring-boot-starter-parentspring-cloud-dependencles
版本号发布日期版本号发布日期
1.5.2.RELEASE2017-03Dalston.RC12017-x
1.5.9.RELEASE2017-11Edgware.RELEASE2017-11
1.5.16.RELEASE2018-04Edgware.SR52018-10
1.5.20.RELEASE2018-09Edgware.SR52018-10
2.0.2.RELEASE2018-05Fomchiey.BULD-SNAPSHOT2018-x
2.0.6.RELEASE2018-10Fomchiey-SR22018-10
2.1.4.RELEASE2019-04Greenwich.SR12019-03

新建Maven项目

https://gitee/Crater/springcloud.git

5、Eureka服务注册与发现

5.1 什么是Eureka

  • Netflix在涉及Eureka时,遵循AP原则
  • Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务注册与发现,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如Zookeeper.

5.2、原理讲解

  • Eureka基本架构

    • SpringCloud封装了 Netflix 公司开发的Eureka模块来实现服务注册与发现(对比Zookeeper)

    • Eureka采用了C-S的架构设计,EurekaServer作为服务注册功能的服务器,它是服务注册中心

    • 系统中的其他微服务,使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer来监控系统中各个微服务是否正常运行,SpringCloud的一些其他模块(比如Zuul)就可以通过EurekaServer来发现系统中的其他微服务,并执行相关逻辑。

    • 对比

      Eureka架构:

​ Dubbo架构:

  • Eureka 包含两个组件:Eureka ServerEureka Client.

    • Eureka Server 提供服务注册,各个节点启动后,会在EurekaServer中进行注册,这样Eureka Server中的服务注册表中将会储存所有课用服务节点的信息,服务节点的信息可以在界面中直观的看到.
    • Eureka Client 是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳 (默认周期为30秒) 。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉 (默认周期为90s).
  • 三大角色

    • Eureka Server:提供服务注册与发现
    • Server Provider:将自身服务注册到Eureka,从而使消费者能找到
    • Server Consumer:从Eureka中获取注册服务列表,从而找到消费者

5.3、构建步骤

1、eureka-server
  1. springcloud-eureka-7001 模块建立

  2. pom.xml 配置

    <!--导包~-->
    <dependencies>
        <!-- https://mvnrepository/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <!--导入Eureka Server依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--热部署工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
    
  3. application.yml

    server:
      port: 7001
    
    # Eureka配置
    eureka:
      instance:
        hostname: 127.0.0.1   # Eureka服务端的实例名字
      client:
        register-with-eureka: false   # 表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
        fetch-registry: false         # fetch-registry如果为false,则表示自己为注册中心,客户端的则为 ture
        service-url:                  # Eureka监控页面~
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
          							  # 默认路径:http://localhost:8761/eureka/
    
  4. 启动类

    
    @SpringBootApplication
    @EnableEurekaServer     // EnableEurekaServer 服务端的启动类,可以接受别人注册进来~
    public class EurekaServer_7001 {
        public static void main(String[] args) {
            SpringApplication.run(EurekaServer_7001.class, args);
        }
    }
    
    
  5. 访问 http://localhost:7001/

2、eureka-client

调整之前创建的springlouc-provider-dept-8001

  1. 导入Eureka依赖

    <!--Eureka依赖-->
    <!-- https://mvnrepository/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    
  2. application中新增Eureca配置

    # Eureka配置:配置服务注册中心地址
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:7001/eureka/
    
  3. 为主启动类添加@EnableEurekaClient注解

    
    @SpringBootApplication
    @EnableEurekaClient    // @EnableEurekaClient 开启Eureka客户端注解,在服务启动后自动向注册中心注册服务
    public class DeptProvider_8001 {
        public static void main(String[] args) {
            SpringApplication.run(DeptProvider_8001.class, args);
        }
    }
    
  4. 启动7001服务端,后启动8001客户端进行测试,然后访问监控页http://localhost:7001/ ,成功

3、actuator与注册服务信息完善
  1. 更改实例ID

    instanceId属性,它是区分同一服务中不同实例的唯一标识

    在配置文件application.yml的Eureka下,做如下修改

    # Eureka配置:配置服务注册中心地址
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:7001/eureka/
      instance:
        instance-id: springcloud-provider-dept-8001 #修改Eureka上的默认描述信息
    

    访问监控页面,

  2. 配置关于服务加载的监控信息

    pom 中添加依赖

    <!--actuator完善监控信息-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    application.yml中添加配置

    # info配置
    info:
    # 项目的名称
      app.name: crater-springcloud
    # 公司的名称
      company.name: 自学成才公司
    

    刷新监控页,点击实例ID

4、Eureka的自我保护机制

出现的红色字

紧急!Eureka可能错误地宣称实例已经启动,而实际上并没有。续期低于阈值,因此为了安全起见,实例没有过期。

一句话总结就是:某时刻某一个微服务不可用,eureka不会立即清理,依旧会对该微服务的信息进行保存!

  • 默认情况下,当eureka server在一定时间内没有收到实例的心跳,EurekaServer将会注销该实例(默认是90秒)。

    但是当网络分区故障发生时,微服务与Eureka之间无法正常通信,以上行为可能变得非常危险——因为微服务本身是健康的,此时不应该注销这个服务。

    当EurekaServer节点在短时间内丢失过多客户端时(发生网络分区故障),那么这个节点便会触发eureka server的自我保护机制。一旦进入该模式,Eureka-Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(不会注销任何微服务)。当网络故障恢复后,该Eureka-Server节点会自动退出自我保护机制。

  • 当Eureka-Server收到的心跳数重新恢复到阈值以上时,该Eureka-Server节点就会退出自我保护机制。设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务,这是一种应对网络异常的安全保护措施,可以让Eureka集群更加地健壮和稳定。

  • 在SpringCloud中,可以使用eureka.server.enable-self-preservation=false禁用自我保护模式【不推荐关闭】

【扩展】注册进来的微服务,获取一些消息(团队开发会用到)

  • 在8001的 DeptController.java 新增一个方法

    @Autowired
    private DiscoveryClient client;
    
    @GetMapping("/dept/discovery")
    public Object discovery() {
    
        // 获取微服务列表的清单
        List<String> services = client.getServices();
        System.out.println("discovery=>services:" + services);
    
        // 得到一个具体的微服务信息,通过具体的微服务id,applicationName;
        List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
        for (ServiceInstance instance : instances) {
            System.out.println(
                instance.getHost() + "\t" +     // 主机名称
                instance.getPort() + "\t" +     // 端口号
                instance.getUri() + "\t" +      // uri
                instance.getServiceId() + "\t"  // 服务id
            );
        }
    
        return this.client;
    }
    
  • 主启动类添加

    @SpringBootApplication
    @EnableEurekaClient     //  开启Eureka客户端注解,在服务启动后自动向注册中心注册服务
    @EnableDiscoveryClient  //  开启服务发现客户端的注解,获取一些配置的信息,得到具体的微服务
    public class DeptProvider_8001 {
        public static void main(String[] args) {
            SpringApplication.run(DeptProvider_8001.class, args);
        }
    }
    
  • 在浏览器访问

  • 控制台输出

5.4、Eureka 集群环境配置

1、初始化

搭建springcloud-eureka-7002、springcloud-eureka-7003 模块

新建Maven模块,pom、application.yml、主启动类与springcloud-eureka-7001相同。

2、集群相互关联
  • 制造一种不在本地部署的假象,修改 C:\Windows\System32\drivers\etc\hosts 文件,添加IP地址和主机名的映射关系

  • 修改各个Eureka的application.yml配置

  • 在集群中使springcloud-eureka-7001关联springcloud-eureka-7002、springcloud-eureka-7003,其他两个yml配置同理

    server:
      port: 7001
    
    # Eureka配置
    
    eureka:
      instance:
        hostname: eureka7001   # Eureka服务端的实例名字
      client:
        register-with-eureka: false   # 表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
        fetch-registry: false         # fetch-registry如果为false,则表示自己为注册中心,客户端的化为 ture
        service-url:                  # Eureka监控页面~
          # 单机:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
          # 集群(关联):7001关联7002、7003
          defaultZone: http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka
    
  • 通过springcloud-provider-dept-8001下的yml配置文件,修改Eureka配置:配置服务注册中心地址

    这样模拟集群就搭建好了,就可以把一个项目挂载到三个服务器上了

  • 启动三个EurekaServer和DeptProvider_8001,查看监控页

5.5、对比和Zookeeper区别

1、CAP原则

RDBMS (MySQL\Oracle\sqlServer) ===> ACID

NoSQL (Redis\MongoDB) ===> CAP

2.、ACID
  • A (Atomicity) 原子性
  • C (Consistency) 一致性
  • I (Isolation) 隔离性
  • D (Durability) 持久性
3.、CAP
  • C (Consistency) 强一致性
  • A (Availability) 可用性
  • P (Partition tolerance) 分区容错性

CAP的三进二:CA、AP、CP

4、CAP理论的核心
  • 一个分布式系统不可能同时很好的满足一致性可用性分区容错性这三个需求

  • 根据CAP原理,将NoSQL数据库分成了满足CA原则,满足CP原则和满足AP原则三大类

    • CA:单点集群,满足一致性,可用性的系统,通常可扩展性较差
    • CP:满足一致性,分区容错的系统,通常性能不是特别高
    • AP:满足可用性,分区容错的系统,通常可能对一致性要求低一些
5、Eureka比Zookeeper的优点

著名的CAP理论指出,一个分布式系统不可能同时满足C (一致性) 、A (可用性) 、P (容错性),由于分区容错性P再分布式系统中是必须要保证的,因此我们只能再A和C之间进行权衡。

  • Zookeeper 保证的是 CP —> 满足一致性,分区容错的系统。通常性能不是特别高

  • Eureka 保证的是 AP —> 满足可用性,分区容错的系统。通常可能对一致性要求低一些

当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性

  • Zookeeper保证的是CP

    zookeeper会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30-120s,且选举期间整个zookeeper集群是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因为网络问题使得zookeeper集群失去master节点是较大概率发生的事件,虽然服务最终能够恢复,但是,漫长的选举时间导致注册长期不可用,是不可容忍的。

  • Eureka保证的是AP

    优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保住注册服务的可用性,只不过查到的信息可能不是最新的,除此之外,Eureka还有之中自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:

    • Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
    • Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上 (即保证当前节点依然可用)
    • 当网络稳定时,当前实例新的注册信息会被同步到其他节点中

因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪

6、Ribbon:负载均衡(基于客户端)

6.1、负载均衡及Ribbon

Ribbon是什么

  • Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具
  • Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 的客户端组件提供一系列完整的配置项,如:连接超时、重试等。简单的说,就是在配置文件中列出 LoadBalancer (简称LB:负载均衡) 后面所有的机器,Ribbon 会自动的帮助你基于某种规则 (如简单轮询,随机连接等等) 去连接这些机器。我们也容易使用 Ribbon 实现自定义的负载均衡算法!

Ribbon能做什么

  • LB,即负载均衡 (LoadBalancer) ,在微服务或分布式集群中经常用的一种应用。
  • 负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高可用)。
  • 常见的负载均衡软件有 Nginx、Lvs 等等。
  • Dubbo、SpringCloud 中均提供了负载均衡,SpringCloud 的负载均衡算法可以自定义

负载均衡简单分类

  • 集中式LB

    即在服务的提供方和消费方之间使用独立的LB设施,如Nginx(反向代理服务器),由该设施负责把访问请求通过某种策略转发至服务的提供方!

  • 进程式 LB

    将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。

    Ribbon 就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址!

6.2、集成Ribbon

  • springcloud-consumer-dept-80向pom.xml中添加Ribbon和Eureka依赖

    <!--Ribbon-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-ribbon</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--Eureka: Ribbon需要从Eureka服务中心获取信息-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    
  • 在application.yml文件中配置Eureka

    # Eureka配置
    eureka:
      client:
        register-with-eureka: false # 不向 Eureka注册自己
        service-url: # 从三个注册中心中随机取一个去访问
          defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/
    
  • 主启动类加上@EnableEurekaClient注解,开启Eureka

    @SpringBootApplication
    @EnableEurekaClient     //开启Eureka 客户端
    public class DeptConsumer_80 {
        public static void main(String[] args) {
            SpringApplication.run(DeptConsumer_80.class, args);
        }
    }
    
  • 自定义Spring配置类:ConfigBean.java 配置负载均衡实现RestTemplate

    @Configuration
    public class ConfigBean {
    
        @Bean
        @LoadBalanced   //配置负载均衡实现RestTemplate
        public RestTemplate getRestTemplate(){
            return new RestTemplate();
        }
    
    }
    
  • 修改conroller:DeptConsumerController.java

    // Ribbon:我们这里的地址,应该是一个变量,通过服务名来访问
    // private static final String REST_URL_PREFIX = "http://localhost:8001";
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
    

6.3、使用Ribbon实现负载均衡

图示:实线为Ribbon做的事情

  1. 新建两个服务提供者Moudle:springcloud-provider-dept-8003、springcloud-provider-dept-8002

  2. 参照springcloud-provider-dept-8001 依次为另外两个Moudle添加pom.xml依赖 、resourece下的mybatis和application.yml配置,Java代码

  3. 启动所有服务测试(根据自身电脑配置决定启动服务的个数)

  4. 查看监控页

  5. 测试访问http://localhost:8080/consumer/dept/list (我的80端口被占用),多次刷新将访问不同的服务提供者

    每次访问http://localhost:8080/consumer/dept/list随机访问集群中某个服务提供者,这种情况叫做轮询,轮询算法在SpringCloud中可以自定义。

6.4、切换/自定义负载均衡策略

  • 切换策略 (默认负载均衡方式是轮询)

    在springcloud-provider-dept-80模块下的ConfigBean中进行配置,切换使用不同的规则

    此时访问消费者接口查询列表,将随机调用3个提供者

    @Configuration
    public class ConfigBean {
    
        @Bean
        @LoadBalanced   // 配置负载均衡实现 RestTemplate
        public RestTemplate getRestTemplate(){
            return new RestTemplate();
        }
    
        /**
         * IRule:
         * RoundRobinRule               轮询策略
         * RandomRule                   随机策略
         * AvailabilityFilteringRule    会先过滤掉,跳闸,访问故障的服务~,对剩下的进行轮询~
         * RetryRule                    会先按照轮询获取服务~,如果服务获取失败,则会在指定的时间内进行,重试
         */
        @Bean
        public IRule myRule() {
            return new RandomRule();
        }
    }
    
  • 自定义规则

    • 在myRule包下自定义一个配置类CraterRule.java,该包不要和主启动类所在的包同级,要跟启动类所在包同级

      package com.kuang.myrule;
      
      ...
      
      @Configuration
      public class CraterRule {
      
          @Bean
          public IRule myRule() {
              return new MyRandomRule();	// 自定义策略
          }
      
      }
      
    • 自定义的规则(参考Ribbon中默认的规则代码自己稍微改动):MyRandomRule.java

      package com.kuang.myrule;
      
      import ...
      
      public class MyRandomRule extends AbstractLoadBalancerRule {
          /**
           * 每个服务访问5次则换下一个服务(总共3个服务)
           * <p>
           * total=0,默认=0,如果=5,指向下一个服务节点
           * index=0,默认=0,如果total=5,index+1
           */
          private int total = 0;          //被调用的次数
          private int currentIndex = 0;   //当前是谁在提供服务
          public Server choose(ILoadBalancer lb, Object key) {
              if (lb == null) {
                  return null;
              }
              Server server = null;
              while (server == null) {
                  if (Thread.interrupted()) {
                      return null;
                  }
                  List<Server> upList = lb.getReachableServers();     //获得当前活着的服务
                  List<Server> allList = lb.getAllServers();          //获取所有的服务
                  int serverCount = allList.size();
                  if (serverCount == 0) {
                      return null;
                  }
                  //=====================自定义代码=========================
                  if (total < 5) {
                      server = upList.get(currentIndex);
                      total++;
                  } else {
                      total = 0;
                      currentIndex++;
                      if (currentIndex >= upList.size()) {
                          currentIndex = 0;
                      }
                      server = upList.get(currentIndex);  //从活着的服务中,获取指定的服务来进行操作
                      total++;
                  }
                  //======================================================
                  if (server == null) {
                      Thread.yield();
                      continue;
                  }
                  if (server.isAlive()) {
                      return (server);
                  }
                  server = null;
                  Thread.yield();
              }
              return server;
          }
          protected int chooseRandomInt(int serverCount) {
              return ThreadLocalRandom.current().nextInt(serverCount);
          }
          @Override
          public Server choose(Object key) {
              return choose(getLoadBalancer(), key);
          }
          @Override
          public void initWithNiwsConfig(IClientConfig clientConfig) {
              // TODO Auto-generated method stub
          }
      }
      
    • 主启动类开启负载均衡并指定自定义的MyRule配置类

      @SpringBootApplication
      // 开启Eureka 客户端
      @EnableEurekaClient
      // 在微服务启动的时候就能加载自定义的Ribbon类(自定义的规则会覆盖原有默认的规则)
      // 开启负载均衡,并指定自定义的规则
      @RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = CraterRule.class)
      public class DeptConsumer_80 {
          public static void main(String[] args) {
              SpringApplication.run(DeptConsumer_80.class, args);
          }
      }
      
    • 此时请求,则每请求5次,换下一个服务提供者

7、Feign:负载均衡(基于服务端)

7.1、Feign简介

Spring Cloud 集成了Ribbon 和 Eureka,同时也可以使用Feign提供负载均衡的 http 客户端。

Feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,Feign 是一个使用起来更加方便的 HTTP 客戶端,使用起来就像是调用自身工程的方法,而感觉不到是调用远程方法,类似于 controller 调用 service。

在Spring Cloud中,使用Feign非常简单:创建一个接口,并在接口上添加一些注解即可

Feign,主要是社区版,java编程都习惯面向接口编程。这个是很多开发人员的规范。调用微服务访问两种办法:

1. 微服务名字【ribbon】
2. 接口和注解【feign】

Feign能干什么?

  • Feign旨在使编写Java Http客户端变得更容易

  • 前面在使用Ribbon + RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一个客户端类来包装这些依赖服务的调用。

    所以,Feign在此基础上做了进一步的封装,由他来帮助我们定义和实现依赖服务接口的定义,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它 (类似以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解),即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon 时,自动封装服务调用客户端的开发量。

Feign默认集成了Ribbon

  • 利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。

7.2、Feign的使用步骤

  1. 修改springcloud-api模块

    pom.xml添加Feign依赖

    <!--Feign的依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    

    新建service包,并新建DeptClientService.java接口

    @Component
    // @FeignClient:微服务客户端注解,value:指定微服务的名字,这样就可以使Feign客户端直接找到对应的微服务
    @FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
    public interface DeptClientService {
    
        @GetMapping("/dept/get/{id}")
        public Dept queryById(@PathVariable("id") Long id);
    
        @GetMapping("/dept/list")
        public Dept queryAll();
    
        @GetMapping("/dept/add")
        public Dept addDept(Dept dept);
    
    }
    
  2. 创建springcloud-consumer-fdept-feign模块

    拷贝springcloud-consumer-dept-80模块下的pom.xml,resource,以及java代码到springcloud-consumer-feign模块,并添加feign依赖(暂时去掉自定义负载均衡)

    <!--Feign的依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    

    修改DeptConsumerController.java

    @RestController
    public class DeptConsumerController {
    
        /**
         * 通过Feign实现:—改造后controller:DeptConsumerController.java
         * Feign和Ribbon二者对比,
         * 前者显现出面向接口编程特点,代码看起来更清爽,
         * 而且Feign调用方式更符合我们之前在做SSM或者SprngBoot项目时,Controller层调用Service层的编程习惯!
         */
    
        @Autowired
        private DeptClientService deptClientService;
    
    
        @RequestMapping("/consumer/dept/add")
        public boolean add(Dept dept) {
            return deptClientService.addDept(dept);
        }
    
        @RequestMapping("/consumer/dept/get/{id}")
        public Dept get(@PathVariable("id") Long id) {
            return deptClientService.queryById(id);
        }
    
        @RequestMapping("/consumer/dept/list")
        public List<Dept> list() {
            return deptClientService.queryAll();
        }
    
    }
    

    加入自定义负载均衡算法后,依然生效。

7.3、Feign和Ribbon如何选择?

根据个人习惯而定,如果喜欢REST风格使用Ribbon;如果喜欢社区版的面向接口风格使用Feign.

Feign 本质上也是实现了 Ribbon,只不过后者是在调用方式上,为了满足一些开发者习惯的接口调用习惯!

Feign使代码可读性变高,但是性能低了(增加了一层)

8、Hystrix:服务熔断

分布式系统面临的问题

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的 “扇出”,复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免失败!

8.1、服务雪崩

分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为雪崩效应

对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几十秒内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以达到单个依赖关系的失败而不影响整个应用程序或系统运行

综上所述,如果一个应用不能对来自依赖的故障进行隔离,那该应用本身就处在被拖垮的风险中。 因此,为了构建稳定、可靠的分布式系统,我们的服务应当具有自我保护能力,当依赖服务不可用时,当前服务启动自我保护功能,从而避免发生雪崩效应。

8.2、Hystrix是什么

Hystrix是一个应用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性。

断路器”(Circuit Breaker)本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控 (类似熔断保险丝) ,向调用方返回一个服务预期的,可处理的备选响应 (FallBack) ,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

8.3、Hystrix能干什么

  • 服务降级
  • 服务熔断
  • 服务限流
  • 接近实时的监控

8.4、服务熔断

熔断机制是赌赢雪崩效应的一种微服务链路保护机制

当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阀值缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是:@HystrixCommand

服务熔断解决如下问题:

  • 当所依赖的对象不稳定时,能够起到快速失败的目的;
  • 快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复。

入门案例:

  1. 新建springcloud-provider-dept-hystrix-8001模块,并拷贝springcloud-provider-dept–8001内的pom.xml、resource和Java代码进行初始化并调整。

  2. 导入hystrix依赖

    <!--导入Hystrix依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    
  3. 修改controller,查不到的数据模拟报错,

    @RestController
    public class DeptController {
    
        @Autowired
        private DeptService deptService;
    
        /**
         * 根据id查询部门信息
         * 如果根据id查询出现异常,则走hystrixGet这段备选代码
         */
        @HystrixCommand(fallbackMethod = "hystrixGet")
        @GetMapping("/dept/get/{id}")
        public Dept get(@PathVariable("id") Long id){
    
            Dept dept = deptService.queryById(id);
            if(null == dept){
                throw new RuntimeException("这个id=>" + id + "不存在该用户,或者信息无法找到~");
            }
            return dept;
        }
    
        /**
         * 根据id查询备选方案(熔断)
         */
        public Dept hystrixGet(@PathVariable("id") Long id){
            return new Dept()
                    .setDeptno(id)
                    .setDname("这个id=>"+id+",没有对应的信息,null---@Hystrix~")
                    .setDb_source("在MySQL中没有这个数据库");
    
        }
    
    }
    
  4. 为主启动类添加对熔断的支持注解@EnableCircuitBreaker

    @SpringBootApplication
    @EnableEurekaClient     // 开启Eureka客户端注解,在服务启动后自动向注册中心注册服务
    @EnableDiscoveryClient  // 开启服务发现客户端的注解,可以用来获取一些配置的信息,得到具体的微服务
    @EnableCircuitBreaker   // 添加对熔断的支持注解
    public class HystrixDeptProvider_8001 {
        public static void main(String[] args) {
            SpringApplication.run(HystrixDeptProvider_8001.class, args);
        }
    }
    
  5. 测试

    使用熔断后,当访问一个不存在的id时,前台页展示数据如下:

    而对不使用熔断的springcloud-provider-dept–8001模块,修改为判空抛异常,则访问相同地址会出现下面状况:

    因此,为了避免因某个微服务后台出现异常或错误而导致整个应用或网页报错,使用熔断是必要的

8.5、服务降级

服务降级是指 当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略地不处理,或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。说白了,就是尽可能的把系统资源让给优先级高的服务

资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机,导致某些重要的服务不可用。所以,一般在高峰期,为了保证核心功能服务的可用性,都要对某些服务降级处理。比如当双11活动时,把交易无关的服务降级,如查看蚂蚁森林,查看历史订单等等。

降级的方式可以根据业务来,可以延迟服务,比如延迟给用户增加积分,只是放到一个缓存中,等服务平稳之后再执行 ;或者在粒度范围内关闭服务,比如关闭相关文章的推荐。

服务降级需要考虑的问题

  • 哪些服务是核心服务,哪些服务是非核心服务
  • 哪些服务可以支持降级,那些服务不能支持降级,降级策略是什么
  • 除服务降级之外是否存在更复杂的业务放通场景,策略是什么?

自动降级分类

  • 超时降级:主要配置好超时时间和超时重试次数和机制,并使用异步机制探测回复情况
  • 失败次数降级:主要是一些不稳定的api,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
  • 故障降级:比如要调用的远程服务挂掉了(网络故障、DNS故障、http服务返回错误的状态码、rpc服务抛出异常),则可以直接降级。降级后的处理方案有:默认值(比如库存服务挂了,返回默认现货)、兜底数据(比如广告挂了,返回提前准备好的一些静态页面)、缓存(之前暂存的一些缓存数据)
  • 限流降级:秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。

入门案例

  • 在springcloud-api模块下的service包中新建降级配置类DeptClientServiceFallBackFactory.java

    /**
     * Hystrix服务降级 ~
     */
    @Component
    public class DeptClientServiceFallBackFactory implements FallbackFactory {
        @Override
        public DeptClientService create(Throwable cause) {
            return new DeptClientService() {
                @Override
                public Dept queryById(Long id) {
                    return new Dept()
                            .setDeptno(id)
                            .setDname("id=>"+id+"没有对应的信息,客户端提供了降级的信息,这个服务现在已经被关闭")
                            .setDb_source("没有数据~");
                }
                @Override
                public List<Dept> queryAll() {
                    return null;
                }
                @Override
                public boolean addDept(Dept dept) {
                    return false;
                }
            };
        }
    }
    
  • 在DeptClientService中指定降级配置类DeptClientServiceFallBackFactory

    @Component
    // @FeignClient:微服务客户端注解,value:指定微服务的名字,这样就可以使Feign客户端直接找到对应的微服务
    // fallbackFactory指定降级配置类
    @FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT", 
                 fallbackFactory = DeptClientServiceFallBackFactory.class)
    public interface DeptClientService {
    
        @GetMapping("/dept/get/{id}")
        public Dept queryById(@PathVariable("id") Long id);
    
        @GetMapping("/dept/list")
        public List<Dept> queryAll();
    
        @GetMapping("/dept/add")
        public boolean addDept(Dept dept);
    
    }
    
  • springcloud-consumer-dept-feign模块中开启降级

    
    # 开启降级feign.hystrix
    feign:
      hystrix:
        enabled: true
    
  • 开启Eureka7001、provider8001、dept-feign,正常访问,关闭8001后,返回期望类型的值(而不是异常页面)。

8.6、服务熔断和降级的区别

  • 服务熔断—>服务端:某个服务超时或异常,引起熔断~,类似于保险丝 (自我熔断)
  • 服务降级—>客户端:从整体网站请求负载考虑,当某个服务熔断或者关闭之后,服务将不再被调用,此时在客户端,我们可以准备一个 FallBackFactory ,返回一个默认的值(缺省值)。会导致整体的服务下降,但是好歹能用,比直接挂掉强。
  • 触发原因不太一样:服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
  • 管理目标的层次不太一样:熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始
  • 实现方式不太一样:服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断

熔断,降级,限流

限流:限制并发的请求访问量,超过阈值则拒绝;

降级:服务分优先级,牺牲非核心服务(不可用),保证核心服务稳定;从整体负荷考虑;

熔断:依赖的下游服务故障触发熔断,避免引发本系统崩溃;系统自动执行和恢复

8.7、Dashboard流监控

  • 新建 springcloud-consumer-hystrix-dashboard 模块

  • pom依赖(是一个消费端的服务,拷贝80的依赖,并添加hystrix和dashboard依赖)

    <dependencies>
        <!--导入Hystrix依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--dashboard依赖(流监控)-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    
        <!--Ribbon-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--Eureka: Ribbon需要从Eureka服务中心获取信息-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    
        <!--实体类-->
        <dependency>
            <groupId>com.kuang</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--Web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--热部署工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
    
  • 配置yml

    server:
      port: 9001
    
  • 主启动类

    @SpringBootApplication
    @EnableHystrixDashboard     // 开启DashBoard
    public class DeptConsumerDashboard_9001 {
    
        public static void main(String[] args) {
            SpringApplication.run(DeptConsumerDashboard_9001.class, args);
        }
    
    }
    
  • 给springcloud-provider-dept-hystrix-8001模块下的主启动类添加如下代码,添加监控

    @SpringBootApplication
    @EnableEurekaClient     // 开启Eureka客户端注解,在服务启动后自动向注册中心注册服务
    @EnableDiscoveryClient  // 开启服务发现客户端的注解,可以用来获取一些配置的信息,得到具体的微服务
    @EnableCircuitBreaker   // 添加对熔断的支持注解
    public class HystrixDeptProvider_8001 {
        public static void main(String[] args) {
            SpringApplication.run(HystrixDeptProvider_8001.class, args);
        }
    
        /**
         * 注册一个Servlet
         */
        @Bean
        public ServletRegistrationBean hystrixMetricsStreamServlet() {
            // 创建Servlet注册Bean
            ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>();
    
            // 创建自定义的Servlet对象(流监控Servlet)
            HystrixMetricsStreamServlet servlet = new HystrixMetricsStreamServlet();
    
            // 注册Servlet
            registrationBean.setServlet(servlet);
    
            // 设置Servlet配置规则,访问该页面就是监控页面
            registrationBean.addUrlMappings("/actuator/hystrix.stream");
    
            return registrationBean;
        }
    }
    
  • 访问 http://localhost:9001/hystrix

  • 键入监控信息,

    效果如图

    各个量的含义

9、Zuul路由网关

9.1、Zuul概述

  • Zuul包含了对请求的路由和过滤两个最主要的功能
  • 路由功能负责将外部请求转发到具体微服务实例上,是实现外部访问统一入口的基础。
  • 过滤器功能负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。
  • Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka获得其他微服务的消息,也即以后访问微服务都是通过Zuul跳转后获得。

注意:Zuul服务最终还是会注册进Eureka

提供:代理 + 路由 + 过滤 三大功能。

9.2、入门案例

  1. 新建springcloud-zuul模块,并导入依赖

    <dependencies>
        <!--导入zuul依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    
        <!--Eureka:Zuul服务会注册进Eureka-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    
        <!--实体类-->
        <dependency>
            <groupId>com.kuang</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--Web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--热部署工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
    
  2. 配置application.yml

    server:
      port: 9527
    
    spring:
      application:
        name: springcloud-zuul
    
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/
      instance:
        instance-id: zuul-9527
        prefer-ip-address: true # 显示ip
    
    info:
      app.name: crater-springcloud
      company.name: 打工人公司
    
    
  3. 在主启动类开启Zuul

    @EnableZuulProxy:声明一个Zuul代理

    该代理使用Ribbon来定位注册在Eureka Server中的微服务;

    同时,该代理还整合了 Hystrix,从而实现了容错,所有经过Zuul的请求都会在Hystrix命令中执行。

    @SpringBootApplication
    @EnableZuulProxy        // 开启Zuul
    public class ZuulApplication_9527 {
        public static void main(String[] args) {
            SpringApplication.run(ZuulApplication_9527.class, args);
        }
    }
    
  4. 开启Eureka集群、服务提供者集群、Zuul服务,访问Eureka监控页面,可以看到Zuul注册进来

  5. 没有Zuul路由网关配置时,可以直接用微服务(服务提供方)名称去访问(也可以直接访问),但是会暴露微服务名称,不安全

  6. 修改application.yml配置后重启,此时可以访问路由http://localhost:9527/mydept/dept/get/1,效果与上图相同

    # zuul 路由网关配置
    zuul:
      routes:
        mydept.serviceId: springcloud-provider-dept   # eureka注册中心的服务提供方路由名称,mydept可自定义,下同
        mydept.path: /mydept/**                       # 将eureka注册中心的服务提供方路由名称,改为自定义路由名称
    
    
  7. 以上配置后,使用http://localhost:9527/springcloud-provider-dept/dept/get/1仍可以访问,配置不能使用原路径访问。

    # zuul 路由网关配置
    zuul:
      routes:
        mydept.serviceId: springcloud-provider-dept   # eureka注册中心的服务提供方路由名称,mydept可自定义,下同
        mydept.path: /mydept/**                       # 将eureka注册中心的服务提供方路由名称,改为自定义路由名称
      ignored-services: "*" 						  # 不能再使用这个路径访问了,*: 忽略,隐藏全部的服务名称~
    

    配置后重启,原路径将不能再访问,只有http://localhost:9527/mydept/dept/get/1可以访问

  8. 还可以配置一个访问的前缀,配置后的完整zuul配置如下

    # zuul 路由网关配置
    zuul:
      # 路由相关配置
      # 原来访问路由            http://localhost:9527/springcloud-provider-dept/dept/get/1
      # zuul路由配置后访问路由   http://localhost:9527/crater/mydept/dept/get/1
      routes:
        mydept.serviceId: springcloud-provider-dept   # eureka注册中心的服务提供方路由名称
        mydept.path: /mydept/**                       # 将eureka注册中心的服务提供方路由名称,改为自定义路由名称
      ignored-services: "*"                           # 不能再使用这个路径访问了,*: 忽略,隐藏全部的服务名称~
      prefix: /crater                                 # 设置公共的前缀
    

    此时再访问,需要加/crater前缀,http://localhost:9527/crater/mydept/dept/get/1

10、Spring Cloud Config 分布式配置

分布式系统面临的配置文件问题

微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务,我们每一个微服务自己带着一个application.yml,上百配置文件的修改过于麻烦。由于每个服务都需要配置信息才能运行,所以一套集中式的,动态配置管理设施必不可少,SpringCloud提供了ConfigService来解决这个问题,

什么是SpringCloud config分布式配置中心

Spring Cloud Config 为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环节提供了一个中心化的外部配置

Spring Cloud Config 分为服务端客户端两部分;

服务端也称分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息、加密、解密信息等访问接口。

客户端则是通过指定的分布式配置中心来管理应用资源,以及业务相关的配置,并在启动时从配置中心获取和加载配置信息。配置服务器默认采用Git来存储配置信息,这样有助于对配置环境进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。

spring cloud config 分布式配置中心能干嘛?

  • 集中式管理配置文件
  • 不同环境,不同配置,动态化的配置更新,分环境部署,比如 /dev /test /prod /beta /release
  • 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
  • 当配置发生变动时,服务不需要重启,即可感知到配置的变化,并应用新的配置
  • 将配置信息以REST接口的形式暴露

spring cloud config 分布式配置中心与GitHub整合

由于spring cloud config 默认使用git来存储配置文件 (也有其他方式,比如自持SVN 和本地文件),但是最推荐的还是git ,而且使用的是 http / https 访问的形式。

10.1、Git环境搭建

  • 在码云新建仓库,并克隆到本地

  • 在本地新建文件application.yml

    spring:
        profiles:
            active: dev
    
    ---
    spring:
        profiles: dev
        application:
            name: springcloud-config-dev
    
    ---
    spring:
        profiles: test
        application:
            name: springcloud-config-test
    
  • 将新建文件提交到远程仓库

  • 查看远程仓库已经提交

10.2、服务端连接Git配置

  1. 新建springcloud-config-server-3344模块,导入依赖

    <dependencies>
        <!--Web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--config-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
    </dependencies>
    
  2. 新建yml文件,Spring Cloud Config服务器从git存储库(必须提供)为远程客户端提供配置:

    server:
      port: 3344
    
    spring:
      application:
        name: springcloud-config-server
      cloud:
        config:
          # 通过 config-server可以连接到git,访问其中的资源以及配置
          server:
            # 连接码云远程仓库
            git:
              # 注意是https的而不是ssh
              uri: https://gitee/Crater/springcloud-config.git
    
    
  3. 新建主启动类

    @SpringBootApplication
    @EnableConfigServer     // 开启spring cloud config server服务
    public class Config_server_3344 {
        public static void main(String[] args) {
            SpringApplication.run(Config_server_3344.class, args);
        }
    }
    
  4. HTTP服务具有以下格式的资源:

    /{application}/{profile}[/{label}]				/application/dev/master		
    /{application}-{profile}.yml					/application-dev.yml
    /{label}/{application}-{profile}.yml			/master/application-dev.yml
    /{application}-{profile}.properties
    /{label}/{application}-{profile}.properties
    

    访问:

    连接/效果
    http://localhost:3344/application/dev/master
    http://localhost:3344/application-dev.yml
    http://localhost:3344/master/application-dev.yml
    访问不存在的配置:http://localhost:3344/master/application-aaa.yml

|

10.3、客户端连接服务端访问远程

  1. 新建配置文件 config-client.yml,并上传git

    spring:
      profiles:
        active: dev
        
    ---
    server:
      port: 8201
    
    # spring的配置
    spring:
      profiles: dev
      application:
        name: springcloud-provider-dept
    
    # Eureka的配置,服务注册到哪里
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7001:7001/eureka/
          
    ---
    server:
      port: 8202
    
    # spring的配置
    spring:
      profiles: test
      application:
        name: springcloud-provider-dept
    
    # Eureka的配置,服务注册到哪里
    eureka:
      client:
        service-url:
          defaultZone: http://eureka7001:7001/eureka/
    
  2. 新建微服务 springcloud-config-client-3355,导入依赖

    <dependencies>
        <!--config-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
        <!--Web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    
  3. resources目录下,配置系统级别配置bootstrap.yml、用户级别配置application.yml

    # 系统级别配置
    spring:
      cloud:
        config:
          uri: http://localhost:3344
          label: master
          name: config-client  # 需要从git上读取资源名称,不需要后缀
          profile: dev
    
    # 用户级别配置
    spring:
      application:
        name: springcloud-config-client-3355
    
  4. 编写一个controller,用来获取信息,一个启动类

    package com.kuang.springcloud.controller;
    
    import ...
    
    @RestController
    public class ConfigClientController {
    
        /**
         * 获取微服务名称
         */
        @Value("${spring.application.name}")
        private String applicationName;
    
        /**
         * 获取Eureka服务
         */
        @Value("${eureka.client.service-url.defaultZone}")
        private String eurekaServer;
    
        /**
         * 获取服务端的端口号
         */
        @Value("${server.port}")
        private String port;
    
        @RequestMapping("/config")
        public String getConfig(){
            return "applicationName:"+applicationName + "," +
                    "eurekaServer:"+eurekaServer + "," +
                    "port:"+port;
        }
    
    }
    
    @SpringBootApplication
    public class Config_client_3355 {
        public static void main(String[] args) {
            SpringApplication.run(Config_client_3355.class, args);
        }
    }
    
  5. 先启动3344服务,再启动3355服务,控制台显示端口号为8201,浏览器访问,可见bootstrap.yml指定的application.name优先级高于用户配置的application.name

  6. 如果修改 bootstrap.yml 文件的 profile 属性为 test,则重启3355服务,端口号为8202。

以上实现了分布式配置

10.4、远程配置实战测试

1、准备
  1. 新建文件 config-eureka.yml、

    spring:
      profiles:
        active: dev
    
    ---
    server:
      port: 7001
    
    # spring的配置
    spring:
      profiles: dev
      application:
        name: springcloud-config-eureka
    
    # Eureka配置
    eureka:
      instance:
        hostname: eureka7001 
      client:
        register-with-eureka: false 
        fetch-registry: false 
        service-url: 
          defaultZone: http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka
          
    ---
    server:
      port: 7001
    
    # spring的配置
    spring:
      profiles: test
      application:
        name: springcloud-config-eureka
    
    # Eureka配置
    eureka:
      instance:
        hostname: eureka7001 
      client:
        register-with-eureka: false 
        fetch-registry: false 
        service-url: 
          defaultZone: http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka
    

    注意,test配置连接db02数据库:

    spring:
      profiles:
        active: dev
    
    ---
    server:
      port: 8001
    
    # mybatis配置
    mybatis:
      type-aliases-package: com.kuang.springcloud.pojo
      config-location: classpath:mybatis/mybatis-config.xml
      mapper-locations: classpath:mybatis/mapper/*.xml
    
    # Spring配置
    spring:
      profiles: dev
      application:
        name: springcloud-provider-dept      # 3个服务名称一致
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource  # 数据源
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db01?serverTimezone=GMT%2b8&useSSL=false&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
    
    # Eureka配置:配置服务注册中心地址
    eureka:
      client:
        service-url:
          # 注册中心地址7001-7003
          defaultZone: http://eureka7001:7001/eureka/
      instance:
        instance-id: springcloud-provider-dept-8001 #修改Eureka上的默认描述信息
    
    # info配置
    info:
    # 项目的名称
      app.name: crater-springcloud
    # 公司的名称
      company.name: dev配置
      
    ---
    server:
      port: 8001
    
    # mybatis配置
    mybatis:
      type-aliases-package: com.kuang.springcloud.pojo
      config-location: classpath:mybatis/mybatis-config.xml
      mapper-locations: classpath:mybatis/mapper/*.xml
    
    # Spring配置
    spring:
      profiles: dev
      application:
        name: springcloud-provider-dept      # 3个服务名称一致
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource  # 数据源
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db02?serverTimezone=GMT%2b8&useSSL=false&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
    
    # Eureka配置:配置服务注册中心地址
    eureka:
      client:
        service-url:
          # 注册中心地址7001-7003
          defaultZone: http://eureka7001:7001/eureka/
      instance:
        instance-id: springcloud-provider-dept-8001 #修改Eureka上的默认描述信息
    
    # info配置
    info:
    # 项目的名称
      app.name: crater-springcloud
    # 公司的名称
      company.name: test配置
    
2、Eureka
  1. 根据springcloud-eureka-7001,新建springcloud-config-eureka-7001,引入依赖

    <!--config-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
        <version>2.1.1.RELEASE</version>
    </dependency>
    
  2. 清空该模块的application.yml配置,并新建bootstrap.yml连接远程配置

    spring:
      cloud:
        config:
          name: config-eureka # 仓库中的配置文件名称
          label: master
          profile: dev
          uri: http://localhost:3344
    
  3. 主启动类

    @SpringBootApplication
    @EnableEurekaServer     //服务端的启动类
    public class ConfigEurekaServer_7001 {
        public static void main(String[] args) {
            SpringApplication.run(ConfigEurekaServer_7001.class,args);
        }
    }
    
  4. 启动ConfigEurekaServer_7001,访问http://localhost:7001/,出现监控页面,则成功。

3、provider-dept
  1. 新建springcloud-config-dept-8001,内容复制springcloud-provider-dept-8001,引入依赖

    <!--config-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
        <version>2.1.1.RELEASE</version>
    </dependency>
    
  2. 清除application.yml,新建bootstrap.yml,

    spring:
      cloud:
        config:
          name: config-dept # 仓库中的配置文件名称
          label: master
          profile: dev
          uri: http://localhost:3344
    
  3. 启动springcloud-config-dept-8001,访问http://localhost:7001/,出现监控页面,成功注册

  4. 访问http://localhost:8001/dept/get/1,查询db02

  5. 线上修改git的文件,dev下数据源改为db01,重启后重新访问,

    成功!!!

本文标签: 笔记教程最新SpringCloudIDEA