• spring cloud


    一、前言

    1.应用分类

    1.1单体应用框架

    • 复杂性高:整个项目包含的模块非常多、模块的边界模糊,依赖关系不清晰,代码混乱地堆砌到一起,修改 Bug 或新增功能都可能带来隐含的缺陷;
    • 技术债务:随着时间的推移、人员的变更,会逐渐形成应用程序的技术债务,而且越积越多,Not broken,dont fix(不坏不修),这在软件开发中非常常见,已使用的系统设计或代码难以被修改,因为应用程序中的其它模块可能会以意料之外的方式在使用它;
    • 部署效率低:随着代码的增多,构建和部署的时间也会增多,每次修复 Bug 或新增功能都要重新部署整个应用,全部署的方式耗时长、影响范围大、风险高,这儿使得单体应用上线部署效率很低;
    • 可靠性差:某个应用 Bug,可能会导致整个应用崩溃;
    • 扩展能力受限:单体应用只能作为一个整体扩展,无法根据业务模块的需要进行伸缩,例如:应用中有的模块是计算密集型的,需要强劲的 Cpu,有的模块是 IO 密集型的,需要更大的内存,由于这些模块都部署在一起,不得不在硬件的选择上做妥协;
    • 阻碍技术创新:单体应用往往使用统一的技术平台或方案解决所有问题,团队中的每个人都必须使用相同的语言和技术框架,要想引入新的东西比较困难,例如:一个 Struts2 构建,有 100w 行代码的单体应用,如果想换成 Spring Boot,毫无疑问,切换的成本是非常高的;

    1.2分布式服务架构

    • 将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常为 HTTP 资源 Api),这些服务通过全自动部署机制独立部署,共用一个集中式的管理,服务可以使用不同语言开发,使用不同的数据存储技术;
    • 搜索引擎、电商网站、微博、微信、O2O 平台……凡是涉及到大规模用户、高并发访问的,无一不是分布式,“大系统小做”,对于一个大的复杂系统,首先想到的就是对其分拆,拆成多个子系统,每个子系统自己的存储、Service、接口层,各个子系统独立开发、测试、部署、运维;
    • 优点:易于扩展、部署简单、技术异构性、从团队管理角度讲,也可以不同团队用自己熟悉的语言体系,团队之间基于接口进行协作,职责清晰,各司其职;

    2、基础理论

    2.1分布式和集群

    • 集群是多台计算机完成一样的工作,提高效率和安全性,需要考虑一致性的问题;
    • 分布式是多台计算机协同完成工作,提高效率和扩展性,需要考虑事务的问题;

    2.2 一致性

            强一致性、弱一致性、最终一致性;

    2.3 ACID:

    • 事务的四大特性,原子性、一致性、隔离性、持久性,MySQL 通过 InnoDB 日志和锁来保证事务的强一致性;

    2.4 CAP 理论;

    • 1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标:Consistency(一致性)、Availability(有效性)、Partition tolerance(分区容错性:单个组件不可用,依然能完成操作,分区容错性是分布式系统的根本);
      • 在 G1 和 G2 通讯中,有可能无法成功(断电、出错、网络延迟等),有可能此时 Client 已经发起查询操作;
      • 在同步服务器时候,需要停掉 G2 的读写操作,无法保证有效性;
      • 允许 G2 查询得到未同步之前的数据,无法保证一致性;
    • 两个分支;
    • CP 架构:放弃可用性,追求一致性和分区容错性;
    • AP 架构:放弃强一致,追求分区容错性和可用性,这是很多分布式系统设计时的选择;
    • BASE 理论:BA(Basic Available 基本可用)、S(Soft State 柔性状态,同一数据的不同副本的状态,不需要实时一致)、E(Eventual Consisstency 最终一致性);
    • 酸碱平衡:ACID && BASE 理论平衡,比如交易系统需要强一致性,遵循 ACID,注册成功后发送邮件只需遵循 BASE 理论即可;

    3.系统架构

    3.1物理相关

    • 负载均衡器;
    • Linux HA:又称“双机热备”,是两台机器装上同样的系统,配好后通过“心跳线”互相监测,数据存放在共享阵列库中,当主服务器出现问题时,从服务器会接管主服务器上的所有服务;
    • 多机房:同城灾备,异地灾备;

    3.2 存储

    • 存储分类
      • Nosql:内存数据库(Redis)、文件型数据库(MongoDB);
      • Sql:Mysql、Sql Server、Oracal,会设计到分库分表的几个关键性的问题:切分维度,Join 的处理,分布式事务;
      • 读写分离:对传统的单机 Mysql 数据库,读和写是完全同步的,写进去的内容,立马就可以读到,但在高并发情况下,或者读和写并不需要完全同步的情况,就可以分开存储,采用读写分离;
        • 缓存:缓存大家都不陌生,遇到性能问题,大家首先想到的就是缓存;
        • 重写轻读 VS 重读轻写;
        • 重写轻读:本质就是“空间换时间“,你不是计算起来耗时,延迟高吗,那我可以提前计算,然后存储起来,取的时候,直接去取;
        • 重读轻写:我们通常对 Mysql 的用法,写的时候,简单,查的时候,做复杂的 Join 计算,返回结果,这样做的好处是容易做到数据的强一致性,不会因为字段冗余,造成数据的不一致,但是性能可能就是问题;
    • 冷热分离:比如定期把 Mysql 中的历史数据,同步到 Hive;

    3.3 Service

    • 负载均衡;
    • 分布式事务;
    • 并发:通过设计保证系统能够同时并行处理很多请求,垂直扩展(提升单机处理能力)、水平扩展(增加服务器数量,线性扩充系统性能);
    • 同步 VS 异步:因为非实时,我们就可以做异步,比如使用消息队列,比如使用后台的 Job,周期性处理某类任务;
    • Push vs Pull
    • 在所有分布式系统中,都涉及到一个基本问题:节点之间(或者 2 个子系统之间)的状态通知,比如一个节点状态变更了,要通知另外一个节点;
    • Pull: 节点 B 周期性的去询问节点 A 的状态;
    • Push: 节点 A 状态变了, Push 给节点 B;
    • 批量:批量其实也是在线/离线的一种思想,把实时问题,转化为一个批量处理的问题,从而降低对系统吞吐量的压力,比如 Kafka 中的批量发消息,比如广告扣费系统中,把多次点击累积在一起扣费;
    • 限流:现在很多电商都会有秒杀活动,秒杀的一个特点就是商品很少,但短时间内流量暴增,服务器完全处理不了这么多请求,那索性不要放那么多人进去;
    • 服务熔断与降级:服务降级是系统的最后一道保险,就是当某个服务不可用时,干脆就别让其提供服务了,直接返回一个缺省的结果,虽然这个服务不可用,但它不至于让整个主流程瘫痪,这就可以最大限度的保证核心系统可用;
    • 前端
    • 动静分离:动态的页面放在 Web服务器上,静态的资源如 Css/Js/Img,直接放到 CDN(内容分发网络)上,这样既提高性能,也极大的降低服务器压力;
    • 其他
    • 故障监控:系统监控、链路监控、日志监控;
    • 自动预警;

    4.数据库分库分表

    4.1 数据分片

    • 离散式分片:按照数据的某一字段哈希取模后进行分片存储;
    • 跨库操作需要由数据分库分表中间件来完成,影响数据的查询效率;
      • 当数据存储能力出现瓶颈需要扩容时,离散分片规则需要将所有数据重新进行哈希取模运算,产生数据迁移问题;
    • 连续分片:数据按照时间或连续自增主键连续存储;
    • 大量的读写往往都集中在最新存储的那一部分数据,会导致热点问题;

    4.2 数据库扩展

    • 垂直分库:将一个完整的数据库根据业务功能拆分成多个独立的数据库,这些数据库可以运行在不同的服务器上,从而提升数据库整体的数据读写性能;
    • 垂直分表:如果一张表的字段非常多,将一张表中不常用的字段拆分到另一张表中,从而保证第一张表中的字段较少,提升查询效率,而另一张表中的数据通过外键与第一张表进行关联;
    • 水平分表:如果一张表中的记录数过多(超过 1000 万条记录),那么会对数据库的读写性能产生较大的影响,将一张含有很多记录数的表水平切分,拆分成几张结构相同的表,根据某字段进行哈希取模后均匀地存储在切分表中;
    • 水平分库分表:水平数据分片首先将数据表进行水平拆分,然后按照某一分片规则存储在多台数据库服务器上;
    • 跨库操作
      • 跨库 Join 变得非常麻烦,而且基于架构规范,性能,安全性等方面考虑,一般是禁止跨库 Join 的;
      • 在业务系统层面,通过多次 SQL 查询,完成数据的组装和拼接;
      • 中间件:Cobar(分库)、MyCat;

    5.分布式事务

    • 目前数据库不支持跨库事务,我们就需要在数据库之上通过某种手段,实现支持跨数据库的事务支持,比如:商品系统、订单系统、支付系统、积分系统、库存系统等,用户下单,2、3、4、5需要在一个事务控制;

    5.1 分布式事务协议

    • 2PC:两阶段提交协议
    • 有一个事务管理器的概念,负责协调多个数据库的事务,事务管理器先问各个数据库你准备好了吗?如果每个数据库都回复 OK,那么就正式提交事务,在各个数据库上执行操作,如果任何其中一个数据库回答 NO,那么就回滚事务;
      • PreCommit 阶段
      • 节点:协调者 Coordinator、参与者 Cohorts;
    • 协调者向所有参与者询问是否可以执行提交操作 Vote;
      • 参与者执行询问,Undo 信息和 Redo 信息写入日志;
      • 参与者向协调者发起询问,返回“成功”(事务操作成功)或“中止”(事务操作失败);
    • DoCommit 阶段
      • 协调者获得参与者“同意”时,发出“Commit”请求,参与者完成请求,释放资源,向协调者返回“完成”消息,协调者收到消息后完成事务;
        • 协调者获得参与者“中止”消息时,发出“Rollback”请求,参与者完成请求,释放资源;
        • 缺点
        • 参与者节点事务都是阻塞型的;
          • 协调者发生故障,参与者会一直阻塞;
          • 第二阶段,在协调者发出“Commit”后,协调者和其中的参与者都宕机,没人知道事务是否提交;
    • 3PC:三阶段提交协议
    • 在 2PC 的基础上,引入了协调者和参与者的超时机制,并将 Precommit 阶段分成了两个准备阶段;
    • CanCommit 阶段、PreCommit 阶段、DoCommit 阶段;

    5.2 分布式事务模式

    • AT:两阶段型,是一种无侵入的分布式事务解决方案,阿里的 Seata 实现了该模式;
    • TCC:两阶段型、补偿型;
    • Try 阶段:对各个服务的资源做检测以及对资源进行锁定或者预留;
      • 这种方案使用较少,因为事务回滚实际上是严重依赖于你自己写代码来回滚;
      • Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作;
      • Confirm 阶段:在各个服务中执行实际的操作;
    • Saga:两阶段、补偿型;
    • 每个 Saga 由一系列 sub-transaction Ti 组成
      • 和 TCC 相比,Saga 没有“预留”动作,它的 Ti 就是直接提交到库;
      • 每个 Ti 都有对应的补偿动作 Ci,补偿动作用于撤销 Ti 造成的结果;
    • XA:分布式强一致性的解决方案,性能低而使用较少;

    二、分布式框架

    1.Dubbo + Zookeeper

    以bubbo来调用的

    dubbo:阿里程序员,自创,一个技术,

    zookeeper:注册中心功能

    类似于我们的controller调用service,通过RPC去调用远程接口

    1.1RPC

    • Remote Procedure Call Protocol,远程过程调用协议,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议;
    • 该协议允许运行于一台计算机的程序调用另一台计算机的程序,通过一个双向的通信连接(Socket 套接字),实现数据的交换;
    • 关键是创建“客户端存根”,存根就像是代理模式中的代理,生成代理后,代理跟远程服务端通信;

    2.spring cloud

    Netflix

    注册中心,负载均衡器,网关,远程调用。

    3.spring cloud Alibaba

    使用了spring cloud的组件,另外添加了三大组件:

    1.Nacos

    2.Sentinel

    3.Seata

    三、分布式架构

    • Provider:生产者,接口提供方;
    • Consumer:消费者,调用接口方;
    • Registry:注册中心,服务注册与发现;
    • Monitor:监控中心,统计服务的调用次数和时间;
    • Container:服务运行容器;
    • ----------------
    • Container 负责启动,加载,运行服务;
    • Provider 在启动时,向 Registry 注册自己提供的服务;
    • Consumer 在启动时,向 Registry 订阅自己所需的服务;
    • Registry 返回 Provider 地址列表给 Consumer,如果有变更,Registry 将基于长连接推送变更数据给 Consumer;
    • Consumer 从 Provider 地址列表中,基于软负载均衡算法,选一台 Provider 进行调用,如果调用失败,再选另一台;
    • Provider 和 Consumer,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到 Monitor;

    四、简介

    1.特点

    • 官方文档:https://spring.io/projects/spring-cloud;
    • 在 Spring Boot 基础之上构建,用于快速构建分布式系统的通用模式的工具集;
    • 集大成者,Spring Cloud 包含了微服务架构的方方面面;
    • 约定优于配置,基于注解,配置文件少;
    • 轻量级组件、开发简便、灵活;
    • 项目结构复杂,每一个组件或者每一个服务都需要创建一个项目;
    • 部署门槛高,项目部署需要配合 Docker 等容器技术进行集群部署;

    2.版本

    Maven 中央库搜索 Spring Cloud Dependencies,查看 Spring Cloud 版本;

    • Spring Boot 版本以伦敦地铁站命名,对非英语母语国家开发者并不友好,2020 版本变更了命名规则;

    Spring Boot 和 Spring Cloud 版本对应关系;

    2020 版本变更

    • Spring Cloud 一直以来把 Netflix OSS 套件作为其官方默认的一站式解决方案,可是 Netflix 公司在 2018 年前后宣布其核心组件 Hystrix、Ribbon、Zuul、Archaius 等均进入维护状态,迫于无奈,Spring Cloud 做了阻断式升级,发布了 2020 版本;

    • Bootstrap 父上下文配置默认不再启动,参见 BootstrapApplicationListener.java,对 BootStrap 做了判断;
    • 全新的配置方式,可以使用 spring.config.import 导入其它组件的配置,如:spring.config.import=configserver:xxx,这么做更具模块化,更符合云原生环境的要求;

    3.组件

    • 注册中心:Netflix Eureka;
    • 负载均衡:Netflix Ribbon(2020 版本前)、Spring Cloud Loadbalancer(2020 版本后);
    • 熔断器:Netflix Hystrix(2020 版本前)、Resilience4j(2020 版本后);
    • 声明式服务调用组件:Feign(最初属 Netflix 公司,后来移交给 OpenFeign 组织);
    • 网关:Netflix Zuul(2020 版本前)、Spring Cloud Gateway(2020 版本后);
    • 配置中心:Spring Cloud Config;
    • 事件、消息总线:Spring Cloud Bus;
    • 安全组件:Spring Cloud Security;

    五、公共项目搭建

    搭建一个spring cloud项目

    目标:将之前写的spring boot项目拆解到spring cloud 项目中

    一下市搭建分布式项目的步骤:

    entity项目问题?

    问题1:为什么要创建一个entity项目?

    微服务之间涉及到跨模块的beanentity的调用,所以我首先将所有的分布式系统里的bean抽取出来,形成单独的模块,其他微服务以jar包的方式引入

    问题2:微服务会不会跨服务调用 bean? 不会

    举个栗子

    其他模块会不会调用 账户模块 的user 对象? 会

    对啊,既然会用到,那么我微服务 B 怎么去调用 微服务 A 里面的 bean?

    问题3: 每个微服务就是一个项目? 是的

    问题4: 是不是将每个微服务的 bean 抽取出来,形成一个单独的项目,其他微服务只要添加该项目依赖就可以引用到了

    问题5: 那以后是不是还要把每一个service之类的都抽出来?
    不用啊,我们通过resttemplate调用接口的方式实现微服务接口调用

    1、Entity 项目

    1.1.简介

    在 Spring Cloud 项目中,每个微服务都可能用到的内容,比如 Entity、Util 等,我们将这些内容单独放到一个项目内,成为其余微服务的依赖;

    1.2.创建entity

    1.创建骨架

    创建 Java Maven 项目 java_spring_cloud_entity;

    这时骨架就搭建好了,

    2.导入依赖

    • Pom 添加依赖
    1. <dependency>
    2. <groupId>javax.servletgroupId>
    3. <artifactId>javax.servlet-apiartifactId>
    4. <version>4.0.1version>
    5. dependency>
    6. <dependency>
    7. <groupId>javax.persistencegroupId>
    8. <artifactId>javax.persistence-apiartifactId>
    9. <version>2.2version>
    10. dependency>
    11. <dependency>
    12. <groupId>com.fasterxml.jackson.coregroupId>
    13. <artifactId>jackson-coreartifactId>
    14. <version>${jackson.version}version>
    15. dependency>
    16. <dependency>
    17. <groupId>com.fasterxml.jackson.coregroupId>
    18. <artifactId>jackson-databindartifactId>
    19. <version>${jackson.version}version>
    20. dependency>
    21. <dependency>
    22. <groupId>com.fasterxml.jackson.coregroupId>
    23. <artifactId>jackson-annotationsartifactId>
    24. <version>${jackson.version}version>
    25. dependency>
    26. <dependency>
    27. <groupId>com.fasterxml.jackson.datatypegroupId>
    28. <artifactId>jackson-datatype-jsr310artifactId>
    29. <version>${jackson.version}version>
    30. dependency>
    31. <dependency>
    32. <groupId>org.apache.commonsgroupId>
    33. <artifactId>commons-lang3artifactId>
    34. <version>3.12.0version>
    35. dependency>

     
    

    2.11.3

    3. 包装:将之前项目各个模块中 Entity 移植到该项目;

    1.在基础包下创建与之前项目模块各个相同的包名

    2.复制之前的entity过来放在对应的包中

    3.复制过来后,需要修改entity中的引用路径

    全部修改完后就,没错了,下一步打成jar包

    4.打包jar

    • Maven 打包项目,在本地仓库生成 Jar;
    • 命令:mvn clean install -Dmaven.test.skip=true

    这里会出现问题情况:

    这里是你的maven环境变量没有配置,步骤如下:

    • 环境变量
    • MAVEN_HOME:D:\Program Files\apache-maven-3.6.3
    • Path:%MAVEN_HOME%\bin;
    • cmd:mvn --version

    d30d56e381a36a83ac96adff4fb36aa2.png

    这样直接点确定,就好了,然后去测试,打开cmd

    测试结果是正常这样后,就配好了,但是idea还没有感应到,重启idea,然后打开entity项目,重新输入打包命令

    5.找到jar包,在target下面

    这样就打好了,然后就可以把你的项目上传到远程仓库里面了

    2.创建注册中心

    2.1Netflix Eureka

    简介

    • Spring Cloud 提供了多种注册中心的支持,如:Eureka、Consul、ZooKeeper 等,Netflix Eureka 本身是一个基于 REST 的服务,包含两个组件:Eureka Server 和 Eureka Client;
    • Eureka Server 提供服务注册服务,各个节点启动后,会在 Eureka Server 中进行注册,这样 Eureka Server 的服务注册表将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到;
    • Eureka Server 之间通过复制的方式完成数据的同步,Eureka 还提供了客户端缓存机制,即使所有的 Eureka Server 都挂掉,客户端依然可以利用缓存中的信息消费其他服务的 API;
    • Eureka Client:生产者或消费者;
    • 在应用启动后,Eureka Client 将会向 Eureka Server 发送心跳,默认周期为 30 秒,如果 Eureka Server 在多个心跳周期内(默认 90 秒)没有接收到某个节点的心跳,Eureka Server 将会进入自我保护机制;

    Eureka Server(注册中心)

    • 骨架搭建
    • 创建 Spring Boot 项目 java_spring_cloud_register,选择 Eureka Service 组件;

    • 配置
    • application.properties
    1. # for server
    2. server.port=8760
    3. # for Eureka server
    4. eureka.instance.hostname=localhost
    5. eureka.client.registerWithEureka=false
    6. eureka.client.fetchRegistry=false
    7. eureka.client.servic

    • 启动类
    • 添加 @EnableEurekaServer 注解;
    • @EnableEurekaServer :范围大
    1. @SpringBootApplication
    2. @EnableEurekaServer
    3. public class SpringCloudRegisterApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(SpringCloudRegisterApplication.class, args);
    6. }
    7. }
    • 启动项目,访问注册中心:http://127.0.0.1:8760

    3.Eureka Client Test微服务

    3.1.Eureka Client Test (test微服务)

    • 创建 Spring Boot 项目 java_spring_cloud_test,选择 Eureka Discovery Client、Spring Web 组件;

    • pom,xml
    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-webartifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>org.springframework.cloudgroupId>
    7. <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    8. dependency>
    9. <dependency>
    10. <groupId>mysqlgroupId>
    11. <artifactId>mysql-connector-javaartifactId>
    12. <version>5.1.47version>
    13. dependency>
    14. <dependency>
    15. <groupId>org.mybatis.spring.bootgroupId>
    16. <artifactId>mybatis-spring-boot-starterartifactId>
    17. <version>2.1.0version>
    18. dependency>
    19. <dependency>
    20. <groupId>com.github.pagehelpergroupId>
    21. <artifactId>pagehelper-spring-boot-starterartifactId>
    22. <version>1.4.1version>
    23. dependency>
    24. <dependency>
    25. <groupId>org.apache.commonsgroupId>
    26. <artifactId>commons-lang3artifactId>
    27. dependency>
    28. <dependency>
    29. <groupId>com.sfacgroupId>
    30. <artifactId>spring_cloudc_entityartifactId>
    31. <version>1.0-SNAPSHOTversion>
    32. dependency>
    • application.properties
    1. #for server
    2. server.port=8761
    3. # for eureka client
    4. spring.application.name=client-test
    5. eureka.instance.hostname=localhost
    6. eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:8760/eureka

    • 启动类,添加 @EnableEurekaClient 注解;
    • @EnableEurekaClient只针对与eureka-client
    1. @SpringBootApplication
    2. @EnableDiscoveryClient
    3. public class SpringCloudTestApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(SpringCloudTestApplication.class, args);
    6. }
    7. }
    • 将之前项目中 Test 模块 RestFul 接口移植到该项目;

    • 启动项目;

    • 访问注册中心:http://127.0.0.1:8760 ---- 新启动的微服务 client-test 已经被注册;

    访问微服务 client-test 接口:http://127.0.0.1:8761/api/contry/522

    3.2 Eureka Client Account(Account微服务)

    • 创建 Spring Boot 项目 java_spring_cloud_account,选择 Eureka Discovery Client、Spring Web 组件;

        pom.xml:
    
    1. org.springframework.boot
    2. spring-boot-starter-web
    3. org.springframework.cloud
    4. spring-cloud-starter-netflix-eureka-client
    5. mysql
    6. mysql-connector-java
    7. 5.1.47
    8. org.mybatis.spring.boot
    9. mybatis-spring-boot-starter
    10. 2.1.0
    11. com.github.pagehelper
    12. pagehelper-spring-boot-starter
    13. 1.4.1
    14. org.apache.commons
    15. commons-lang3
    16. com.sfac
    17. spring_cloudc_entity
    18. 1.0-SNAPSHOT

    ​​​​​​​

    • application.properties
    1. #for server
    2. server.port=8763
    3. # for eureka client
    4. spring.application.name=client-account
    5. eureka.instance.hostname=localhost
    6. eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:8760/eureka
    7. # for data source
    8. # mysql 5
    9. #spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    10. spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    11. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test_city?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    12. spring.datasource.username=root
    13. spring.datasource.password=123456
    14. # hikari pool
    15. spring.datasource.hikari.maximum-pool-size=20
    16. spring.datasource.hikari.minimum-idle=5
    17. spring.datasource.hikari.idle-timeout=180000
    18. spring.datasource.hikari.auto-commit=true
    19. # for mybatis
    20. mybatis.configuration.map-underscore-to-camel-case=true

    启动类,添加 @EnableEurekaClient 注解;

    • 将之前项目中 Account 模块 RestFul 接口移植到该项目,剔除不用的部分:Shiro、Redis 登录次数验证;

    • 启动项目;

    这里如果出现了异常,找不到,执行mvn idea idea

    • 访问注册中心:http://127.0.0.1:8763 ---- 新启动的微服务 client-account 已经被注册;

    • 访问微服务 client-account 接口:http://127.0.0.1:8763/api/user/1

    4.Eureka Client 相互调用,resttemplate,openfeign

    1.Eureka Client 互相调用

    1.1.1 需求

    • user接口,根据id查询user,需要返回对应的城市信息
    • uservo bean 没有城市信息
      • userVo对象,需要设置城市信息
      • 需要提供一个查询userVo的接口-----再service层,user对象从account微服务本身调用,city通过远程调用,访问test微服务,再组装为userVo对象。
    • client-account 接口需要调用 client-test 数据,此时,client-account 既是生产者又是消费者;

    1.1.2 新建UserVo

    • 再spring_cloud_entity中新建一个 UserVo 对象,继承自 User,并添加 City 属性,在获取 UserVo 时候,根据默认 cityId 获取 City 信息;
    1. public class UserVo extends AbstractEntity {
    2. private static final long serialVersionUID = 1L;
    3. private String userName;
    4. private String email;
    5. private String password;
    6. private String userImage;
    7. private boolean rememberMe;
    8. private List roles;
    9. private City city;
    10. }

    然后重新打包.......其他引用了entity微服务的地方就可以加载出userVo类。

    1.1.3 消费者注册 RestTemplate;

    可以新建一个包,也可以写再启动类中,注册成为bean

    1. @SpringBootApplication
    2. @EnableEurekaClient
    3. public class SpringCloudAccountApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(SpringCloudAccountApplication.class, args);
    6. }
    7. //消费者注册 RestTemplate;
    8. @Bean
    9. @LoadBalanced
    10. public RestTemplate restTemplate(){
    11. return new RestTemplate();
    12. }
    13. }

    1.1.4实现 RestFul 接口

    • Entity ---- Dao ---- Service ---- Controller;
    1. //entity:上面写了,再entity微服务中
    2. //userDao:不需要写,只需要调用写好的即可。
    3. //userService:
    4. UserVo getUserVoByUserIDAndCityId(int userId,int CityId);
    5. //userServiceImpl:
    6. @Override
    7. public UserVo getUserVoByUserIDAndCityId(int userId, int CityId) {
    8. UserVo userVo = new UserVo();
    9. User user = userDao.getUserById(userId);
    10. BeanUtils.copyProperties(user,userVo); //copy
    11. //调用test微服务,获取城市信息
    12. City city = restTemplate.getForObject(
    13. "http://client-test/api/city/{cityId}", City.class, CityId);
    14. userVo.setCity(city);
    15. return userVo;
    16. }
    17. //userController:
    18. /**
    19. * 127.0.0.1/api/userVo/1/1890 ---- get
    20. */
    21. @GetMapping(value = "/userVo/{userId}/{cityId}")
    22. public UserVo getUserVo(@PathVariable int userId,@PathVariable int cityId) {
    23. return userService.getUserVoByUserIDAndCityId(userId,cityId);
    24. }

    1.1.5 启动

    1.启动项目,先调用注册中心,加载进去

    2.调用接口:http://127.0.0.1:8762/api/userVo/1/1890

    2.Eureka 用户认证

    • 简介:用户无需登录便能访问 Eureka Server 注册中心,这样并不安全,我们需要为 Eureka 添加安全认证依赖;
    • Eureka Server
      • Pom 引入 Jar;
    1. org.springframework.boot
    2. spring-boot-starter-security

    ​​​​​​​

    • 配置
    • application.properties
    1. # for spring security
    2. # 高版本 spring cloud 舍掉该配置
    3. #spring.security.basic.enable=true
    4. spring.security.user.name=root
    5. spring.security.user.password=root

    添加配置类 WebSecurityConfig.java

    1. @Configuration
    2. @EnableWebSecurity
    3. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    4. @Override
    5. protected void configure(HttpSecurity http) throws Exception {
    6. // Basic 认证是一种较为简单的 HTTP 认证方式,客户端通过明文(Base64编码格式)传输用户名和密码到服务端进行认证
    7. http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
    8. http.csrf().disable();
    9. http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
    10. }
    11. } @Bean
    12. ReactorLoadBalancer randomLoadBalancer(
    13. Environment environment,
    14. LoadBalancerClientFactory loadBalancerClientFactory) {
    15. String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
    16. // 在此也可返回自定义负载均衡器
    17. return new RandomLoadBalancer(
    18. loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    19. }
    • ureka Client
    • Pom 无需引入 Jar;
    • 配置
      • application.properties
        • eureka.client.serviceUrl.defaultZone=http://root:root@${eureka.instance.hostname}:8760/eureka
    • 启动项目,访问接口;

    3.Eureka 自我保护机制

    • 简介
    • 微服务正常退出,比如用 Idea 停掉微服务,控制台输出 Completed shut down of DiscoveryClient,此时,注册中心立即注销该微服务;
    • 微服务非正常退出,比如使用 Eclipse 停掉微服务、拔掉网线、taskkill 等,Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,让这些实例不会过期,此时消费者就会拿到一个无效的服务实例,导致调用失败,需要服务消费者端有一些容错机制,如重试,断路器等;
    • 触发条件:Renews(last min) < Renews threshold;
      • Renews (last min):Eureka Server 最后 1 分钟收到客户端实例续约的总数;
        • this.expectedNumberOfRenewsPerMin = count * 2; ---- 微服务数量,包括注册中心 * 每分钟心跳数;
        • this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold()); ---- getRenewalPercentThreshold 默认 85%;
      • Renews threshold:Eureka Server 期望每分钟收到客户端实例续约的总数;
        • count * 2 ---- 微服务数量,包括注册中心 * 每分钟心跳数;
    • 禁用自我保护机制
    • Eureka Server
      • application.properties
    1. # 关闭注册中心自我保护机制
    2. eureka.server.enable-self-preservation=false
    3. # 清理间隔
    4. eureka.server.eviction-interval-timer-in-ms=20000

    ​​​​​​​

    • Eureka Client
      • application.properties
    1. # 告诉服务端,如果我 10s 之内没有给你发心跳,就代表我死了,将我踢出掉,默认 90s
    2. eureka.instance.lease-expiration-duration-in-seconds=10
    3. # 每间隔 3s,向服务端发送一次心跳,证明自己依然存活,默认 30s
    4. eureka.instance.lease-renewal-interval-in-seconds=3

    5.负载均衡(微服务添加集群,引入负载均衡器组件)

    1.Netflix Ribbon

    1.1简介

      • 适用于 Spring Cloud 2020 之前的版本;
      • Ribbon 是 Netflix 发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起,Ribbon 客户端组件提供一系列完善的配置项如连接超时,重试等,简单的说,就是在配置文件中列出 Load Balancer 后面所有的机器,Ribbon 会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用 Ribbon 实现自定义的负载均衡算法;
    • 负载均衡策略
    • RoundRobinRule ---- 轮询、默认;
    • RandomRule ---- 随机;
    • RetryRule ---- 重试机制;
    • BestAvailableRule ---- 选择最小并发 server;
    • AvailabilityFilteringRule ---- 过滤高并发、失败 server;
    • WeightedResponseTimeRule ---- 根据响应时长比重选择中间值;
    • ZoneAvoidanceRule ---- 判断 server 性能;
    • 查看 com.netflix.loadbalancer.IRule 接口;

    1.2 实现

    • 启动 Eureka Server;
    • Eureka Client Test,不同端口启动多个实例;
    • Eureka Client Account
      • Pom 无需引入 Jar;
        • spring-cloud-starter-netflix-eureka-client 中已经包含了 spring-cloud-starter-netflix-ribbon;配置
        • application.properties,配置策略(可选配置);
          # for ribbon 
          client-test.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
        • 注册 RestTemplate 时,添加 @LoadBalanced 注解;
      • 应用
        • 启动 Client-Account,调用端口测试:http://127.0.0.1:8762/api/userVo/1;
        • 断点调试 LoadBalancerInterceptor、RibbonLoadBalancerClient;
          • 拦截器 LoadBalancerInterceptor 获取请求中注册的服务名 CLIENT-TEST,在 RibbonLoadBalancerClient 中获取服务器列表,通过负载均衡算法得到调用地址;

    2.Spring Cloud Loadbalancer

    2.1 简介

      • 适用于 Spring Cloud 2020+;
      • Spring Cloud Loadbalancer 负载均衡器,它并不是一个独立的项目,而是 Spring Cloud Commons 其中的一个模块,项目中用了 Eureka 相关的 Starter,想完全剔除 Ribbon 的依赖是不可能的,2020 版本默认关闭 Ribbon(spring.cloud.loadbalancer.ribbon.enabled=false),启用 Spring Cloud Loadbalancer;
      • Spring Cloud 2020 版本内置了轮询、随机的负载均衡策略,默认轮询,这点比 Ribbon 要少,我们可以通过 LoadBalancerClient 注解来指定负载均衡器;
      • 接口结构
        • ReactiveLoadBalancer
        • ReactorLoadBalancer
        • ReactorServiceInstanceLoadBalancer
        • RandomLoadBalancer || RoundRobinLoadBalancer

    2.2 实现

    • 启动 Eureka Server;
    • Eureka Client Test,不同端口启动多个实例;
    • Eureka Client Account
      • Pom 无需引入 Jar;
        • spring-cloud-commons 和 spring-cloud-loadbalancer 已经被引入;
      • 配置
        • application.properties
          • 无需配置;
        • CustomLoadBalancerConfiguration.java
    public class CustomLoadBalancerConfiguration {
    
        @Bean
        ReactorLoadBalancer randomLoadBalancer(
                Environment environment,
                LoadBalancerClientFactory loadBalancerClientFactory) {
            String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
            // 在此也可返回自定义负载均衡器
            return new RandomLoadBalancer(
                    loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
        }
    }
    • 应用
      • 启动 Eureka Client Account,调用接口 http://127.0.0.1:8762/api/userVo/1/1890 测试;
      • 从控制台日志记录中可看出,当 Client Account 选择不同策略时,访问 Client Test 微服务集群会按照既定策略访问;

    6.熔断器

    分区熔断性,就是当集群中的一个服务挂掉后,给他一个默认值,不让他影响整个集群的运行。

    1.Resilience4j
    1.1简介

    • 推荐 Spring Cloud 2020 之后版本使用;
    • Resilience4j 是 Spring Cloud G 版本推荐的容错方案,是一个轻量级的容错库,专为 Java 8 和函数式编程而设计,借鉴了 Hystrix 的设计,提供了断路器(CircuitBreaker)、并发调用隔离(Bulkhead)、限流(RateLimiter)、重试(Retry)、超时(Timeout) 等功能;
    • 断路器 CircuitBreaker
      • 断路器一般通过 3 个有限状态机来实现:CLOSED、OPEN、HALF_OPEN,此外,还有 2 个特殊的状态机:DISABLED 和 FORCED_OPEN,状态的存储更新必须是线程安全的,即只有一个线程能够在某个时间点更新状态;
      • 关闭 ----> 打开:当故障率等于或大于可配置的阈值时,CircuitBreaker 的状态将从“关闭”更改为“打开”。
      • 打开 ----> 半开:当 CircuitBreaker 打开时,它会拒绝带有 CallNotPermittedException 的调用,经过一段等待时间后,CircuitBreaker 状态从 OPEN 变为 HALF_OPEN,并允许可配置数量的服务调用是否仍然不可用或再次变为可用,用 CallNotPermittedException 拒绝其他调用,直到所有允许的调用完成,如果故障率或慢呼叫率等于或大于配置的阈值,则状态会变回 OPEN;
      • 半开 ----> 关闭:如果故障率和慢呼叫率低于阈值,则状态将变回“已关闭”;
      • DISABLED:始终拒绝调用;
      • FORCED_OPEN:始终允许调用;
    • 并发调用隔离 Bulkhead
      • 在系统设计中,需要预期故障的发生,将应用程序拆分成多个组件,通过资源隔离确保一个组件的故障不会影响其他的组件,就像轮船用隔板(Bulkhead)分成多个小隔间,每个隔间都被隔板密封,这样可以防止进洪水时整艘船沉没;
      • 两个服务 A 和服务 B,A 的某些 API 依赖 B,当服务 B 运行速度非常慢的时候,A 调用 B 的请求变多时,A 的性能会受到影响,服务 A 中那些不依赖于服务 B 的功能也无法处理,因此,需要隔离 A 中依赖 B 的请求,Resilience4j 提供了 SemaphoreBulkhead 和 FixedThreadPoolBulkhead 来实现 Bulkhead;
    • 限流 RateLimiter
      • 微服务在给定的时间内设置可以处置的最大请求数,控制吞吐量来帮助保护服务器免于过载;
    • 重试 Retry
      • 微服务体系中,多个服务互相依赖,当被依赖的服务出现问题而无法按预期响应时,就会级联到下游服务,导致不良的用户体验,通常我们会为每个微服务部署多个实例,如果其中一个实例有问题,无法响应我们的请求,我们则重试该请求,负载均衡器可以将请求发送到运行状况良好的节点并正确获得响应,通过重试,有更多机会获得正确的响应;
    • 超时 Timeout
      • 在微服务体系中,微服务相互依赖,可能因为网络的原因,导致消费者阻塞,在设计时需要设置超时来应对服务缓慢 、不可用性问题;

    1.2 实现

    • 消费者 Pom 导入 Jar
        	
            
                io.github.resilience4j
                resilience4j-spring-boot2
            

    实现方式一:AOP 式;

      • 消费者 application.properties 配置不同的策略;
    • RecordFailurePredicate && IgnoredException
    public class RecordFailurePredicate implements Predicate {
        @Override
        public boolean test(Throwable throwable) {
            return true;
        }
    }
    public class IgnoredException extends Exception {
    }
    • 在消费者 Service 层添加相应的策略来启动配置项;
      • @Retry(name = "retryBackendA") ---- 启动重试策略;
      • @CircuitBreaker(name = "backendA") ---- 启动断路器策略,没法指定断路器默认返回值;
      • @RateLimiter(name = "backendA") ---- 启动限流策略;

    实现方式二:编程式

    无需配置,改造消费者 Service;

        @Override
        public UserVo getUserVoByUserIDAndCityId(int userId, int cityId) {
            UserVo userVo = new UserVo();
            User user = userDao.getUserById(userId);
            BeanUtils.copyProperties(user,userVo);   //copy
    
    //        调用test微服务,获取城市信息,均衡负载方式
    //        City city = restTemplate.getForObject(
    //                "http://client-test/api/city/{cityId}", City.class, cityId);
    //        userVo.setCity(city);
    //        return userVo;
    
    
    //        熔断器:就是一个服务挂掉后,给一个默认的值
            CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                    .failureRateThreshold(50)
                    .waitDurationInOpenState(Duration.ofMillis(1000))
                    .build();
            CircuitBreaker circuitBreaker = CircuitBreaker.of("circuitBreakerPloy", circuitBreakerConfig);
            Try circuitBreakerSupplier = Try.ofSupplier(CircuitBreaker.decorateSupplier(
                    circuitBreaker,
            () -> restTemplate.getForObject("http://client-test/api/city/{cityId}", City.class, cityId)
                    )).recover(Exception.class, new City());
            userVo.setCity(circuitBreakerSupplier.get());
            return userVo;
    }

    • 调用接口 http://127.0.0.1:8762/api/userVo/1 测试;
    • 挂掉test1.test2

    7.远程调用

    1.RestTemplate

    • 参见 Netflix Eureka ---- Eureka Client 互调;

    2.OpenFeign

    2.1 简介

      • Feign:是 Netflix 开发的声明式、模板化的 HTTP 客户端,用于访问注册中心接口,2019 年停更;
      • OpenFeign:Spring Cloud 在 Feign 的基础上进行了增强,使 Feign 支持了 Spring MVC 的注解,提高了 Feign 的易用性,Spring Boot 2.0 以上基本使用 OpenFeign;

    2.2 实现

    • 消费者 Pom 导入 Jar;
        	
            
                org.springframework.cloud
                spring-cloud-starter-openfeign
            
    • 配置;
      • 启动类上添加 @EnableFeignClients 注解,Spring 启动后会扫描标注了 @FeignClient 的接口,然后生成代理类,并注入到 Spring IOC 容器中,才可以被注入使用;
    • 应用;
    • 在 service 包下创建 TestFeignClient 接口,用于调用 Client-Test 微服务接口;
    @Component
    @FeignClient(name = "client-test")
    public interface TestFeignClient {
        /**
         *
         * /api/city/2259 ------put
         */
        @GetMapping(value = "/city/{cityId}")
        City selectCityById(@PathVariable int cityId);
    
    }
    

    改造消费者 Service,注入 TestFeignClient,替换掉 RestTemplate;

    @Override
        public UserVo getUserVoByUserIDAndCityId(int userId, int cityId) {
            UserVo userVo = new UserVo();
            User user = userDao.getUserById(userId);
            BeanUtils.copyProperties(user,userVo);   //copy
    
    //        调用test微服务,获取城市信息,均衡负载方式
    //        City city = restTemplate.getForObject(
    //                "http://client-test/api/city/{cityId}", City.class, cityId);
    //        userVo.setCity(city);
    //        return userVo;
    
    
    //        熔断器:就是一个服务挂掉后,给一个默认的值
    //        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
    //                .failureRateThreshold(50)
    //                .waitDurationInOpenState(Duration.ofMillis(1000))
    //                .build();
    //        CircuitBreaker circuitBreaker = CircuitBreaker.of("circuitBreakerPloy", circuitBreakerConfig);
            Try circuitBreakerSupplier = Try.ofSupplier(CircuitBreaker.decorateSupplier(
    //                circuitBreaker,
    //        () -> restTemplate.getForObject("http://client-test/api/city/{cityId}", City.class, cityId)
    //                )).recover(Exception.class, new City());
    //        userVo.setCity(circuitBreakerSupplier.get());
    //        return userVo;
    
    
            //远程调用
            CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                    .failureRateThreshold(50)
                    .waitDurationInOpenState(Duration.ofMillis(1000))
                    .build();
            CircuitBreaker circuitBreaker = CircuitBreaker.of("circuitBreakerPloy", circuitBreakerConfig);
    
            Try circuitBreakerSupplier = Try.ofSupplier(CircuitBreaker.decorateSupplier(
                    circuitBreaker,
                    () -> testFeignClient.selectCityById(cityId)
            )).recover(Exception.class, new City());
            userVo.setCity(circuitBreakerSupplier.get());
            return userVo;
    
        }
    }
    
    • 调用接口 http://127.0.0.1:8762/api/userVo/1/1890 测试;

    8.网关服务

    1.Netflix Zuul( 开源的微服务网关 )

    1.1 简介

      • 推荐 Spring Cloud 2020 之前版本使用;
      • 未加网关的微服务架构
        • 通过上面的学习,分布式架构基本成型,内部服务集群 Service A 和 Service B,它们都会到注册中心注册和订阅服务,而 Open Service 集群专门对外提供接口,这样的实现是否合理?是否有更好的方法?
        • 这种架构破坏了服务无状态特点:为了保证对外服务的安全性,我们需要实现对服务访问的权限控制,而 Open Service 的访问权限机制会贯穿并污染整个开放服务的业务逻辑,破坏了集群中 Rest Api 无状态的特点,从具体的开发来说,工作中除了要考虑实际业务逻辑之外,还要额外持续对接口的访问做权限处理;
        • 无法直接复用接口:当我们需要一个已有的集群内部接口,我们不得不在原有的接口上做校验逻辑,或者增加一个代理来做权限控制,无法直接复用原有的接口;
      • 网关服务
        • 将权限控制、日志收集从服务单元中抽离出去,最适合的地方是服务集群的最外端,我们需要一个功能更强大的负载均衡器,网关服务;
        • 网关服务是微服务架构中不可或缺的一部分,它具备统一向外提供 Rest Api、服务路由、负载均衡、权限控制等功能,为微服务架构提供了门前保护,除了安全之外,还能让服务集群具备高可用性和测试性;
        • Zuul 是 NetFlix 开源的微服务网关,可以和 Eureka、Ribbon、Hystrix 等组件配合使用,其核心是一系列的过滤器;
          • 身份认证和安全:识别每个资源的验证要求,拒绝不符合要求的请求;
          • 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图;
          • 动态路由:动态地将请求路由到不同的后端服务集群;
          • 压力测试:逐渐增加指向集群的流量;
          • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求;
          • 静态响应处理:在边缘位置直接建立部分响应,从而避免其转发到集群内部;
          • 多区域弹性:跨越 AWS Region 进行请求路由,实现 ELB(AWS Elastic Load Balancing ---- 负载均衡服务)使用的多样化,以及让系统的边缘更贴近系统的使用者;
      • 使用 Zuul 后的微服务架构
        • 客户端请求微服务时,先经过 Zuul,再调用微服务集群,这样对于校验、负载均衡等功能移到了 Zuul 中实现,而微服务只需关注自身的业务逻辑则可;

    2.2 Zuul 工程

      • 创建 Spring Boot 项目 java_spring_cloud_gateway,选择 Eureka Discovery Client、Zuul 组件;
      • application.properties

    • 启动类添加 @EnableZuulProxy 注解;
    • 启动服务,进行测试;
      • Test 服务
        • 原生接口:http://127.0.0.1:8761/api/cities/522
        • 通过 Gateway 接口:http://127.0.0.1:8759/testService/api/cities/522
      • Account 服务
        • 原生接口:http://127.0.0.1:8762/api/user/1
        • 通过 Gateway 接口:http://127.0.0.1:8759/accountService/api/user/1
    • Zuul Filter
    • 简介
      • Zuul Filter 是一个抽象类,是 Zuul 的重要组件,自定义过滤器需实现以下四个方法;
        • shouldFilter:是否执行该过滤器;
        • run:过滤器业务逻辑;
        • filterType:返回过滤器类型;
          • pre:请求在被路由之前执行;
          • routing:在路由请求时调用;
          • post:在 routing 和 errror 过滤器之后调用;
          • error:处理请求时发生错误调用;
        • filterOrder:过滤器优先级,数字越小优先级越高;
      • 执行流程
    • 自定义过滤器

    2.Spring Cloud Gateway

    2.1.简介

      • Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0、Spring Boot 2.0 和 Project Reactor 等技术开发,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流;
      • Spring Cloud Gateway 目标是替代 Zuul,为了提升网关的性能,Spring Cloud Gateway 是基于 WebFlux 框架实现的,而 WebFlux 框架底层则使用了高性能的 Reactor 模式通信框架 Netty;

    2.2.相关术语(断言)

      • Filter 过滤器:和 Zuul 的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理,过滤器为 org.springframework.cloud.gateway.filter.GatewayFilter 类的实例;
      • Predicate 断言:这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数,断言的输入类型是一个 ServerWebExchange;
      • Route 路由:网关配置的基本组成模块,和 Zuul 的路由配置模块类似,一个 Route 模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义,如果断言为真,则路由匹配,目标 URI 会被访问;
        • spring.cloud.gateway.routes[0].id=testServer
        • # 指向外部地址
        • spring.cloud.gateway.routes[0].uri=https://www.baidu.com
        • # 指向注册中心注册的服务
        • spring.cloud.gateway.routes[0].uri=lb://client-test
        • # 断言绑定各种过滤器,匹配成功则跳转到目标地址
        • # 请求 Path 匹配
        • spring.cloud.gateway.routes[0].predicates[0]=Path=/api/**
        • # 请求 Host 匹配
        • spring.cloud.gateway.routes[0].predicates[0]=Host=**.foo.org
        • # 请求方式匹配
        • spring.cloud.gateway.routes[0].predicates[2]=Method=GET
        • # 请求参数匹配
        • spring.cloud.gateway.routes[0].predicates[4]=Query=foo, ba.
        • # 请求 header 匹配
        • spring.cloud.gateway.routes[0].predicates[3]=Header=X-Request-Id, \d+
        • # 请求 Cookie 匹配
        • spring.cloud.gateway.routes[0].predicates[6]=Cookie=chocolate, ch.p
        • # 在该时间之前的请求都转到目标地址
        • spring.cloud.gateway.routes[0].predicates[7]=Before=2018-01-20T06:06:06+08:00[Asia/Shanghai]
        • # 在该时间之后的请求都转到目标地址
        • spring.cloud.gateway.routes[0].predicates[7]=After=2018-01-20T06:06:06+08:00[Asia/Shanghai]

    2.3.实现

    1.重构 client-test、client-account 接口;

        • 我们用 Path 断言来设置 Route,那么不同的微服务 Api Path 应该有明显的区别,故而做如下改变;
        • client-test:/api/** ---- /api/test/**;
        • client-account: /api/** ---- /api/account/**;
        • 修改 TestFeignClient.java 中 Feign 接口;

    2.创建 Spring Boot 工程 java_spring_cloud_gateway,选择 Eureka Discovery Client、Gateway 组件;

    2.配置application.properties,配置account,test的路由和端口

    #for  server
    server.port=8888
    
    # for eureka client
    spring.application.name=client-gateway
    eureka.instance.hostname=localhost
    eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:8760/eureka
    
    # for gateway route
    spring.cloud.gateway.routes[0].id=testServer
    # lb load balance 缩写
    spring.cloud.gateway.routes[0].uri=lb://client-test
    spring.cloud.gateway.routes[0].predicates[0]=Path=/api/test/**
    
    # for gateway route
    spring.cloud.gateway.routes[1].id=accountServer
    # lb load balance 缩写
    spring.cloud.gateway.routes[1].uri=lb://client-account
    spring.cloud.gateway.routes[1].predicates[0]=Path=/api/account/**
    
      • 端口详情:

    注册中心: 8760

    test微服务: 8761 8762

    account微服务:8763 8764

    gateway网关:8888

      • 启动类添加 @EnableEurekaClient 注册服务;扫描微服务。

    4.启动服务测试

      • Client-Test 微服务
        • 原生接口:127.0.0.1:8762/api/test/city/1890
        • 网关接口:127.0.0.1:8888/api/test/city/1890

      • Client-Account 微服务
        • 原生接口:127.0.0.1:8763/api/account/userVo/1/1890
        • 网关接口:127.0.0.1:8888/api/account/userVo/1/1890

    2.4 过滤器

    • 局部过滤器:针对单个路由的过滤器,
      • Gateway 内置局部过滤器
        • spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1
        • spring.cloud.gateway.routes[0].filters[1]=SetStatus=250
      • 自定义局部过滤器
        • 参照内置过滤器书写;
    • 全局过滤器:针对所有路由,无需配置;
      • Gateway 内置全局过滤器;
      • 自定义全局过滤器

    2.5 跨域配置

    • 方式一:application.properties
    # for Gateway Cros
    spring.cloud.gateway.globalcors.cors-configurations.[/**].allowCredentials=true
    spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedOriginPatterns=*
    spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedMethods=*
    spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedHeaders=*
    spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping=true

    方式二:配置类

    package com.sfac.springCloudGateway.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.web.cors.reactive.CorsUtils;
    import org.springframework.web.server.ServerWebExchange;
    import org.springframework.web.server.WebFilter;
    import org.springframework.web.server.WebFilterChain;
    import reactor.core.publisher.Mono;
    
    @Configuration
    public class CorsAutoConfiguration {
        @Bean
        public WebFilter corsFilter() {
            return (ServerWebExchange ctx, WebFilterChain chain) -> {
                ServerHttpRequest request = ctx.getRequest();
                if (CorsUtils.isCorsRequest(request)) {
                    ServerHttpResponse response = ctx.getResponse();
                    HttpHeaders headers = response.getHeaders();
                    headers.set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, request.getHeaders().getOrigin());
                    // 允许的 header
                    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
                            "X-Token,Token,Authorization,x-requested-with,Content-Type");
                    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "PUT,POST, GET, OPTIONS, DELETE");
                    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
                    headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
                    headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");
                    if (request.getMethod() == HttpMethod.OPTIONS) {
                        response.setStatusCode(HttpStatus.OK);
                        return Mono.empty();
                    }
                }
                return chain.filter(ctx);
            };
        }
    }

    9.配置中心

    将微服务的配置文件全部整合一起,然后通过注册中心接口调用然后配置文件。

    1.Spring Cloud Config

    简介

      • 之前配置文件是否存在的问题?微服务各自拥有自己的配置文件,当配置修改之后,我们需要重启项目,如果数量庞大,维护成本增加,针对这个问题,我们采用 Spring Cloud Config 来做配置;
      • Spring Cloud Config 为分布式系统外部化配置提供了服务端和客户端的支持,它包含 Config Server 和 Config Client 两个部分,实现了 Spring Environment 和 PropertySource 抽象的映射,非常适合 Spring 应用程序;
      • Config Server 是一个可横向扩展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,支持 Git、SVN、本地文件存储,默认使用 Git;
      • Config Client 是 Config Server 的客户端,用于操作存储在 Config Server 中的配置内容,微服务在启动时会请求 Config Server 获取配置文件的内容,请求到后再启动容器;

    架构图

    1.1 Config Server

    • 创建仓库
      • 创建 Git 远程仓库,并将微服务所有配置文件上传到仓库中,配置文件命名规则:

      • 环境配置:application-{profile}.properties;
      • 配置中心:{application}-{profile}.profiles;
        • 此案例只将 Client-Test、Client-Account 配置移植到配置中心;
        • applicationTest-dev.properties
        • applicationAccount-dev.properties
    • 创建工程
      • 创建 Spring Boot 工程 java_spring_cloud_config,选择 Eureka Discovery Client、Config Server 组件;
    • 导入pom:server config
    	
        
            org.springframework.cloud
            spring-cloud-config-server
        
    
    • application.properties
    注册中心: 8760
    test微服务: 8761 8762
    account微服务:8763 8764
    gateway网关:8888
    配置中心:8766
    #for  server
    server.port=
    
    # for eureka client
    spring.application.name=client-config
    eureka.instance.hostname=localhost
    eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:8760/eureka
    
    # for config server
    # 使用本地配置文件,并在工程 resources 下添加相应的文件即可
    # spring.profiles.active=native
    # 配置文件 Git 仓库地址
    spring.cloud.config.server.git.uri=https://gitee.com/ljr666666/config.git
    # 配置文件在仓库中文件夹路径
    spring.cloud.config.server.git.search-paths[0]=clazz
    # 仓库用户名和密码,公有仓库无需设置
    #spring.cloud.config.server.git.username=***
    #spring.cloud.config.server.git.password=***
    # 仓库分支
    spring.cloud.config.label=master
    • 启动类添加 @EnableEurekaClient、@EnableConfigServer 注解;
    • 启动服务,进行测试,配置文件访问规则如下;
      • /{application}/{profile}/[label] ----
      • {application}:
      • http://127.0.0.1:8766/applicationTest/dev/master
      • /{label}/{application}-{profile}.properties ----
      • http://127.0.0.1:8766/applicationAccount/dev/master
      • /{application}-{profile}.properties ----
      • http://127.0.0.1:8766/applicationAccount-dev.properties
      • /{label}/{application}-{profile}.properties ---- http://127.0.0.1:8766/master/applicationAccount-dev.properties

    1.2 Config Client

    1.简介

      • 在微服务中添加 Config Client 支持,用来读取 Config Server 中配置文件的内容;
      • Spring Cloud 2020+ 版本默认不再启动 Bootstrap 父上下文配置,需要在微服务 Pom 中添加 spring-cloud-starter-bootstrap 依赖,并在配置中开启 spring.cloud.bootstrap.enabled=true;

    2.流程

      • 微服务启动,到注册中心中去注册,才能获取配置中心提供的功能,去读取git仓库中的配置文件
      • 注册中心配置,就需要从application.properties移到bootstrap.properties
        ■ 加入bootstrap支持,手动添加,pom
        ■ 创建bootstrap.properties
        ■ 拆分application.properties
        ■ 注册部分,拆解到bootstrap.properties,其余拆解到git仓库对应的配置文件上。

    3.实现(改造微服务 Client-Test/Account,其余微服务类似)

    • Pom 导入
    1. org.springframework.cloud
    2. spring-cloud-config-client
    3. org.springframework.cloud
    4. spring-cloud-starter-bootstrap

    4.重构配置

    • 配置改动思路
    • 将本地 application.properties 中的配置分成两个部分
      • Eureka 相关配置、引入的其余配置文件配置(比如 logback.xml),移植到 bootstrap.properties 配置文件中,注册微服务;
      • 删除 application.properties 配置(我对之进行重命名备份);
      • 其余配置项,移植到远程配置仓库中对应的配置文件中;
    • 配置加载顺序:读本地 bootstrap.properties---- 到注册中心注册服务 ---- 连接 Config Server,加载远程配置 applicationTest-dev.properties ---- 加载本地 application.properties;
    • bootstrap.properties
    1. Test:bootstrap.properties
    2. #for server
    3. server.port=8762
    4. # for eureka client
    5. spring.application.name=client-test
    6. eureka.instance.hostname=localhost
    7. eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:8760/eureka
    8. #start bootstrap
    9. spring.cloud.bootstrap.enabled=true
    10. # for config discovery 开启注册发现
    11. spring.cloud.config.discovery.enabled=true
    12. # 指定配置中心 id
    13. spring.cloud.config.discovery.serviceId=client-config
    14. # 配置中心中配置文件的名字:{application}-{profile}.properties 中 {application}
    15. spring.cloud.config.name=applicationTest
    16. # 配置文件“外观”,可以理解为“配置环境占位符”
    17. spring.cloud.config.profile=dev
    18. # 配置文件在 git 远程仓库的分支
    19. spring.cloud.config.label=master
    1. Account:bootstrap.properties
    2. #for server
    3. server.port=8763
    4. # for eureka client
    5. spring.application.name=client-Account
    6. eureka.instance.hostname=localhost
    7. eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:8760/eureka
    8. #start bootstrap
    9. spring.cloud.bootstrap.enabled=true
    10. # for config discovery 开启注册发现
    11. spring.cloud.config.discovery.enabled=true
    12. # 指定配置中心 id
    13. spring.cloud.config.discovery.serviceId=client-config
    14. # 配置中心中配置文件的名字:{application}-{profile}.properties 中 {application}
    15. spring.cloud.config.name=applicationAccount
    16. # 配置文件“外观”,可以理解为“配置环境占位符”
    17. spring.cloud.config.profile=dev
    18. # 配置文件在 git 远程仓库的分支
    19. spring.cloud.config.label=master
    • applicationTest-dev.properties
    1. # for data source
    2. # mysql 5
    3. #spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    4. spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    5. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test_city?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    6. spring.datasource.username=root
    7. spring.datasource.password=123456
    8. # hikari pool
    9. spring.datasource.hikari.maximum-pool-size=20
    10. spring.datasource.hikari.minimum-idle=5
    11. spring.datasource.hikari.idle-timeout=180000
    12. spring.datasource.hikari.auto-commit=true
    13. # for mybatis
    14. mybatis.configuration.map-underscore-to-camel-case=true
    • applicationAccount-dev.properties
    1. # for data source
    2. # mysql 5
    3. #spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    4. spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    5. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test_city?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    6. spring.datasource.username=root
    7. spring.datasource.password=123456
    8. # hikari pool
    9. spring.datasource.hikari.maximum-pool-size=20
    10. spring.datasource.hikari.minimum-idle=5
    11. spring.datasource.hikari.idle-timeout=180000
    12. spring.datasource.hikari.auto-commit=true
    13. # for mybatis
    14. mybatis.configuration.map-underscore-to-camel-case=true

    删除 application.properties 或重命名备份;

    5. 测试

    • 微服务启动顺序:
      • Java_Spring_Cloud_Register
      • Client_Config
      • Client_test
      • Client_Account
      • Client_Gateway;
    • 访问注册中心:
      • http://127.0.0.1:8760
    • 访问配置中心文件:
      • http://127.0.0.1:8766/applicationTest/dev/master
      • http://127.0.0.1:8766/applicationAccount/dev/master

      • =========================================
      • http://127.0.0.1:8766/applicationAccount-dev.properties
      • http://127.0.0.1:8766/master/applicationAccount-dev.properties
    • 访问 Client_test 接口;
    •   原生接口:127.0.0.1:8762/api/test/city/1890
      • 网关接口:127.0.0.1:8888/api/test/city/1890
    • 访问 Client_Account 接口;
    • 原生接口:127.0.0.1:8763/api/account/userVo/1/1890
      • 网关接口:127.0.0.1:8888/api/account/userVo/1/1890

    •  

    10. 将页面移植到微服务中进行联调

    1.启动接口

    1.1 端口列表

    注册中心:8760

    配置中心:8766

    test.微服务:8761、8762.

    accounL 微服务:8763、8764

    前端wcb项目:8765

    网关:8888

    1.2.测试 接口

    • 访问 Client_test 接口;
      • 原生接口:127.0.0.1:8762/api/test/city/1890
      • 网关接口:127.0.0.1:8888/api/test/city/1890
    • 访问 Client_Account 接口;
      • 原生接口:127.0.0.1:8763/api/account/userVo/1/1890
      • 网关接口:127.0.0.1:8888/api/account/userVo/1/1890

    1.3.启动步骤

    启动注册中心,访问注册中心页面

    启动配置中心,访问注册中心页面,查看注册是否成功,调用配置中心接口,读取配置文件是否成功

    启动test微服务,访问注册中心页面,查看注册是否成功

    --------

    启动网关微服务,访问注册中心页面,查看是否注册成功

    对各个微服务接口进行测试

    前端文本web项目:

    spring mvc

    2.创建web微服务:spring web、注册客户端

    3.移植前端页面过来

    将thmeleaf引进了:

    pom,配置

    静态资源、页面、页面控制器一直到web微服务,移植拦截器、webMVC配置类(只留下拦截器)

    启动项目,检查是否报错,页面能否访问。

    修改Ajax请求

    页面测试,检查并修复。

    spring cloud阶段就打上了句号!继续加油哟!

    ​​​​​​​

     

  • 相关阅读:
    数位dp。。。
    树的直径&
    SpringMVC(八):SSM整合
    软件的命令安装备份
    python笔记(四)--封装
    MAML:User Diverse Preference Modeling by Multimodal AttentiveMetric Learning
    51单片机控制智能家居监控系统设计仿真
    【网络安全】国家专利局专利办理系统存在信息泄漏风险
    XmlDocument 解决 Clone、CloneNode、ImportNode 等节点克隆后的标签自闭合问题
    1916. 统计为蚁群构筑房间的不同顺序 费马小定理+快速幂+DFS
  • 原文地址:https://blog.csdn.net/weixin_46048259/article/details/126667172