石一歌的SpringCloud笔记
SpringCloud
架构演变
单体应用架构
互联网早期,一般的网站应用流量较小,只需一个应用,将所有功能代码都部署在一起就可以,这样可以减少开发、部署和维护的成本。
- 优点
- 项目架构简单,小型项目的话,开发成本低
- 项目部署在一个节点上,维护方便
- 缺点
- 全部功能集成在一个工程中,对于大型项目来讲不易开发和维护
- 项目模块之间紧密耦合,单点容错率低
- 无法针对不同模块进行针对性优化和水平扩展
垂直架构
随着访问量的逐渐增大,单一应用只能依靠增加节点来应对,但是这时候会发现并不是所有的模块都会有比较大的访问量还是以上面的电商为例子,用户访问量的增加可能影响的只是用户和订单模块,但是对消息模块的影响就比较小.那么此时我们希望只多增加几个订单模块,而不增加消息模块. 此时单体应用就做不到了,垂直应用就应运而生了所谓的垂直应用架构,就是将原来的一个应用拆成互不相干的几个应用,以提升效率。比如我们可以将上面电商的单体应用拆分成:
- 电商系统(用户管理商品管理订单管理)
- 后台系统(用户管理订单管理客户管理)
- CMS系统(广告管理营销管理)
-
优点:
- 系统拆分实现了流量分担,解决了并发问题,而且可以针对不同模块进行优化和扩展。
- 一个系统的问题不会影响到其他系统,提高容错率
-
缺点:
-
系统之间相互独立,无法进行相互调用。
-
系统之间相互独立,会有重复的开发任务
-
分布式架构
当垂直应用越来越多,重复的业务代码就会越来越多。这时候,我们就思考可不可以将重复的代码抽取出来,做成统一的业务层作为独立的服务,然后由前端空制层调用不同的业务层服务呢这就产生了新的分布式系统架构。它将把工程拆分成表现层和服务层两个部分,服务层中包含业务逻辑。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。
- 优点
- 抽取公共的功能为服务层,提高代码复用性
- 缺点
- 系统间耦合度变高,调用关系错综复杂,难以维护
soa架构
在分布式架构下,当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心对集群进行实时管理。此时,用于资源调度和治理中心(SOA Service Oriented Architecture)是关键。
-
优点:
- 使用治理中心(ESBldubbo)解决了服务间调用关系的自动调节
-
缺点:
-
服务间会有依赖关系,一旦某个环节出错会影响较大(服务雪崩)
-
服务关系复杂,运维、测试部署困难
-
微服务架构
微服务架构在某种程度上是面向服务的架构SOA继续发展的下一步,它更加强调服务的"彻底拆分"。
- 微服务架构比SOA架构粒度会更加精细,让专业的人去做专业的事情(专注〉,目的提高效率,每个服务于服务之间互不影响,微服务架构中,每个服务必须独立部署,微服务架构更轻巧,轻量级。
- SOA架构中可能数据库存储会发生共享,微服务强调独每个服务都是单独数据库,保证每个服务于服务之间互不影响。
- 项目体现特征 微服务架构比SOA架构更加适合与互联网公司敏捷开发、快速迭代版本,因为粒度非常精细。
- 优点:
- 服务原子化拆分,独立打包、部署和升级,保证每个微服务清晰的任务划分,利于扩展
- 微服务之间采用Restful等轻量级http协议相互调用
- 缺点:
- 分布式系统开发的技术成本高(容错、分布式事务等)
- 复杂性更高。各个微服务进行分布式独立部署,当进行模块调用的时候,分布式将会变得更加麻烦
理论
CAP
一个经典的分布式系统理论。CAP理论告诉我们:一个分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时满足其中两项。
- 一致性
在分布式环境下,一致性是指数据在多个副本之间能否保持一致的特性。在一致性的需求下,当一个系统在数据一致的状态下执行更新操作后,应该保证系统的数据仍然处于一直的状态。
对于一个将数据副本分布在不同分布式节点上的系统来说,如果对第一个节点的数据进 行了更新操作并且更新成功后,却没有使得第二个节点上的数据得到相应的更新,于是在对第二个节点的数据进行读取操作时,获取的依然是老数据(或称为脏数 据),这就是典型的分布式数据不一致的情况。在分布式系统中,如果能够做到针对一个数据项的更新操作执行成功后,所有的用户都可以读取到其最新的值,那么 这样的系统就被认为具有强一致性
- 可用性
可用性是指系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。这里的重点是"有限时间内"和"返回结果"。
"有限时间内"是指,对于用户的一个操作请求,系统必须能够在指定的时间内返回对 应的处理结果,如果超过了这个时间范围,那么系统就被认为是不可用的。另外,"有限的时间内"是指系统设计之初就设计好的运行指标,通常不同系统之间有很 大的不同,无论如何,对于用户请求,系统必须存在一个合理的响应时间,否则用户便会对系统感到失望。
"返回结果"是可用性的另一个非常重要的指标,它要求系统在完成对用户请求的处理后,返回一个正常的响应结果。正常的响应结果通常能够明确地反映出队请求的处理结果,即成功或失败,而不是一个让用户感到困惑的返回结果。
- 分区容错性
分区容错性约束了一个分布式系统具有如下特性:分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。
网络分区是指在分布式系统中,不同的节点分布在不同的子网络(机房或异地网络) 中,由于一些特殊的原因导致这些子网络出现网络不连通的状况,但各个子网络的内部网络是正常的,从而导致整个系统的网络环境被切分成了若干个孤立的区域。 需要注意的是,组成一个分布式系统的每个节点的加入与退出都可以看作是一个特殊的网络分区。
既然一个分布式系统无法同时满足一致性、可用性、分区容错性三个特点,所以我们就需要抛弃一样:
选 择 | 说 明 |
---|---|
CA | 放弃分区容错性,加强一致性和可用性,其实就是传统的单机数据库的选择 |
AP | 放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,这是很多分布式系统设计时的选择,例如很多NoSQL系统就是如此 |
CP | 放弃可用性,追求一致性和分区容错性,基本不会选择,网络问题会直接让整个系统不可用 |
BASE
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。接下来看一下BASE中的三要素:
- 基本可用
基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性----注意,这绝不等价于系统不可用。比如:
(1)响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒
(2)系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
- 软状态
软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时
- 最终一致性
最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
总的来说,BASE理论面向的是大型高可用可扩展的分布式系统,和传统的事物ACID特性是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。但同时,在实际的分布式场景中,不同业务单元和组件对数据一致性的要求是不同的,因此在具体的分布式系统架构设计过程中,ACID特性和BASE理论往往又会结合在一起。
微服务概述
基于单个应用围绕业务进行拆分,拆分出来每一个服务独立项目单独部署运行自己计算机进程里面,基于分布式服务管理
什么是微服务
-
-
就目前而言,对于微服务,业界并没有一个统一的,标准的定义。
-
但通常而言,微服务架构是一种架构模式,或者说是一种架构风格,它体长将单一的应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程内,服务之间互相协调,互相配置,为用户提供最终价值,服务之间采用轻量级的通信机制(HTTP)互相沟通,每个服务都围绕着具体的业务进行构建,并且能狗被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应该根据业务上下文,选择合适的语言,工具(Maven)对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
-
-
技术维度解读
- 微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或销毁,拥有自己独立的数据库。
微服务与微服务架构
-
微服务
- 强调的是服务的大小,它关注的是某一个点,是具体解决某一个问题/提供落地对应服务的一个服务应用,狭义的看,可以看作是IDEA中的一个个微服务工程,或者Moudel。IDEA 工具里面使用Maven开发的一个个独立的小Moudel,它具体是使用SpringBoot开发的一个小模块,专业的事情交给专业的模块来做,一个模块就做着一件事情。强调的是一个个的个体,每个个体完成一个具体的任务或者功能。
-
微服务架构
-
一种新的架构形式,Martin Fowler 于2014年提出。
-
微服务架构是一种架构模式,它体长将单一应用程序划分成一组小的服务,服务之间相互协调,互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制(如HTTP)互相协作,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具(如Maven)对其进行构建。
-
微服务优缺点
-
优点
-
单一职责原则;
-
每个服务足够内聚,足够小,代码容易理解,这样能聚焦一个指定的业务功能或业务需求;
-
开发简单,开发效率高,一个服务可能就是专一的只干一件事;
-
微服务能够被小团队单独开发,这个团队只需2-5个开发人员组成;
-
微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的;
-
微服务能使用不同的语言开发;
-
易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如jenkins,Hudson,bamboo;
-
微服务易于被一个开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果,无需通过合作才能体现价值;
-
微服务允许利用和融合最新技术;
-
微服务只是业务逻辑的代码,不会和HTML,CSS,或其他的界面混合;
-
每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一的数据库;
-
-
缺点
-
开发人员要处理分布式系统的复杂性;
-
多服务运维难度,随着服务的增加,运维的压力也在增大;
-
系统部署依赖问题;
-
服务间通信成本问题;
-
数据一致性问题;
-
系统集成测试问题;
-
性能和监控问题;
-
微服务技术栈
微服务技术条目 | 落地技术 |
---|---|
服务开发 | SpringBoot、Spring、SpringMVC |
服务配置与管理 | Archaius、Diamond |
服务注册与发现 | Eureka、Consul、Zookeeper、Nacos、Etcd |
服务调用 | Rest、PRC、gRPC |
服务熔断器 | Hystrix、Envoy、 |
负载均衡 | Ribbon、Nginx |
服务接口调用 | Feign、Openfegin |
消息队列 | Kafka、RabbitMQ、ActiveMQ、RocketMQ |
服务配置中心管理 | SpringCloudConfig、Nacos |
服务路由(API网关) | Zuul、Getway、Dashboard、Sentinel |
服务监控 | Zabbix、Nagios、Metrics、Specatator |
全链路追踪 | Zipkin、Brave、Dapper |
数据流操作开发包 | SpringCloud Stream |
时间消息总栈 | SpringCloud Bus、Nacos |
服务部署 | Docker、OpenStack、Kubernetes |
SpringCloud&Alibaba选型依据
- 整体解决方案和框架成熟度
- 社区热度
- 可维护性
- 学习曲线
各微服务框架对比
功能点/服务框架 | Netflix/SpringCloud | Motan | gRPC | Thri t | Dubbo/DubboX |
---|---|---|---|---|---|
功能定位 | 完整的微服务框架 | RPC框架,但整合了ZK或Consul,实现集群环境的基本服务注册发现 | RPC框架 | RPC框架 | 服务框架 |
支持Rest | 是,Ribbon支持多种可拔插的序列号选择 | 否 | 否 | 否 | 否 |
支持RPC | 否 | 是(Hession2) | 是 | 是 | 是 |
支持多语言 | 是(Rest形式) | 否 | 是 | 是 | 否 |
负载均衡 | 是(服务端zuul+客户端Ribbon),zuul-服务,动态路由,云端负载均衡Eureka(针对中间层服务器) | 是(客户端) | 否 | 否 | 是(客户端) |
配置服务 | Netfix Archaius,Spring Cloud Config Server 集中配置 | 是(Zookeeper提供) | 否 | 否 | 否 |
服务调用链监控 | 是(zuul),zuul提供边缘服务,API网关 | 否 | 否 | 否 | 否 |
高可用/容错 | 是(服务端Hystrix+客户端Ribbon) | 是(客户端) | 否 | 否 | 是(客户端) |
典型应用案例 | Netflix | Sina | |||
社区活跃程度 | 高 | 一般 | 高 | 一般 | 2017年后重新开始维护,之前中断了5年 |
学习难度 | 中等 | 低 | 高 | 高 | 低 |
文档丰富程度 | 高 | 一般 | 一般 | 一般 | 高 |
其他 | Spring Cloud Bus为我们的应用程序带来了更多管理端点 | 支持降级 | Netflix内部在开发集成gRPC | IDL定义 | 实践的公司比较多 |
SpringCloud概述
用来帮助开发人员快速构建一套分布式应用微服务工具集(服务注册发现负载均衡路由组件统一配置管理)
SpringCloud和SpringBoot的关系
- SpringBoot专注于开苏方便的开发单个个体微服务;
- SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务,整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、为代理、事件总栈、全局锁、决策竞选、分布式会话等等集成服务;
- SpringBoot可以离开SpringCloud独立使用,开发项目,但SpringCloud离不开SpringBoot,属于依赖关系;
- SpringBoot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架;
SpringCloud版本选择
大版本说明
SpringBoot | SpringCloud | 关系 |
---|---|---|
1.2.x | Angel版本(天使)(已过时) | 兼容SpringBoot1.2x |
1.3.x | Brixton版本(布里克斯顿)(已过时) | 兼容SpringBoot1.3x,也兼容SpringBoot1.4x |
1.4.x | Camden版本(卡姆登)(已过时) | 兼容SpringBoot1.4x,也兼容SpringBoot1.5x |
1.5.x | Dalston版本(多尔斯顿)(已过时) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
1.5.x | Edgware版本(埃奇韦尔) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
2.0.x | Finchley版本(芬奇利) | 兼容SpringBoot2.0x,不兼容SpringBoot1.5x |
2.1.x | Greenwich版本(格林威治) | |
2.2.x, 2.3.x (Starting with SR5) | Hoxton版本(霍斯顿) | |
2.4.x, 2.5.x (Starting with 2020.0.3) | Ilford版本(伊尔福德) | |
2.6.x | Jubilee版本(朱比利) |
实际开发版本关系
spring-boot-starter-parent | spring-cloud-dependencles | ||
---|---|---|---|
版本号 | 发布日期 | 版本号 | 发布日期 |
1.5.2.RELEASE | 2017-03 | Dalston.RC1 | 2017-x |
1.5.9.RELEASE | 2017-11 | Edgware.RELEASE | 2017-11 |
1.5.16.RELEASE | 2018-04 | Edgware.SR5 | 2018-10 |
1.5.20.RELEASE | 2018-09 | Edgware.SR5 | 2018-10 |
2.0.2.RELEASE | 2018-05 | Fomchiey.BULD-SNAPSHOT | 2018-x |
2.0.6.RELEASE | 2018-10 | Fomchiey-SR2 | 2018-10 |
2.1.4.RELEASE | 2019-04 | Greenwich.SR1 | 2019-03 |
基础环境搭建
基础环境包括四部分
- 版本控制父项目
- 通用子项目
- 生产者子项目
- 消费者子项目
版本控制父项目
父项目为springboot项目,子项目为maven项目
- 修改父项目打包方式为pom
- 删除build标签
- 用dependencyManagement方式维护springcloud、springcloudAlibaba和mybatisplus版本
<modules>
<module>springcloud_provider_8001</module>
<module>springcloud_common</module>
<module>springcloud_consumer_9001</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.nuc</groupId>
<artifactId>springcloud_parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud_parent</name>
<description>Demo project for Spring Boot</description>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<spring.cloud.version>2021.0.1</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.7.RELEASE</spring.cloud.alibaba.version>
<mybatis.plus.version>3.5.1</mybatis.plus.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
通用子项目
构建实体
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept implements Serializable {
private static final long serialVersionUID = 708560364349809174L;
@TableId(type = IdType.AUTO)
private Long dNo;
private String dName;
private String dbSource;
}
生产者子项目
使用mybatisplus简化代码,接口参数使用@RequestBody接受restTemplate传过来的参数
<dependency>
<artifactId>springcloud_common</artifactId>
<groupId>com.nuc</groupId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/springcloud_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 15 #最大连接数,默认值10.
minimum-idle: 5 #最小空闲连接,默认值10.
connection-timeout: 60000 #连接超时时间(毫秒),默认值30秒.
idle-timeout: 600000
max-lifetime: 3000000 #连接最大存活时间
connection-test-query: select 1 #连接测试查询
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
server:
port: 8001
@EnableTransactionManagement
@MapperScan("com.nuc.mapper")
@Configuration
public class MybatisPlusConfig {
}
public interface DeptMapper extends BaseMapper<Dept> {
}
@Service
public class DeptService extends ServiceImpl<DeptMapper, Dept> {
}
@RestController
@RequestMapping("/dept")
public class DeptController {
@Resource
DeptService deptService;
@PostMapping
public boolean save(@RequestBody Dept dept) {
return deptService.save(dept);
}
@PutMapping
public boolean update(@RequestBody Dept dept) {
return deptService.updateById(dept);
}
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Integer id) {
return deptService.removeById(id);
}
@GetMapping("/{id}")
public Dept getById(@PathVariable Integer id) {
return deptService.getById(id);
}
@GetMapping
public List<Dept> get() {
return deptService.list();
}
}
消费者子项目
使用restTemplate请求生产者接口(注:虽然没使用mybatisplus但由于引入通用子项目仍然需要配置mysql)
<dependency>
<groupId>com.nuc</groupId>
<artifactId>springcloud_common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
server:
port: 9001
spring:
application:
name: CONSUMER9001
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/springcloud_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
@Configuration
public class BeanConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
@RestController
@RequestMapping("/dept")
public class DeptController {
private static final String REST_URL_PREFIX = "http://localhost:8001";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/save")
public boolean save(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/", dept, Boolean.class);
}
@RequestMapping("/update")
public void update(Dept dept) {
restTemplate.put(REST_URL_PREFIX + "/dept/", dept);
}
@RequestMapping("/delete/{id}")
public void delete(@PathVariable Integer id) {
restTemplate.delete(REST_URL_PREFIX + "/dept/" + id);
}
@RequestMapping("/get/{id}")
public Dept getById(@PathVariable Integer id) {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/" + id, Dept.class);
}
@RequestMapping("/get")
public List<Dept> get() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/", List.class);
}
}
服务注册与发现
服务注册中心就是在整个微服务架构单独抽取一个服务,这个服务不完成项目中任何业务功能,仅仅用来在微服务中记录微服务以及对整个系统微服务进行健康状态检查,以及服务元数据信息存储
- 可以对所有的微服务的信息进行存储,如微服务的名称、IP、端口等
- 可以在进行服务调用时通过服务发现查询可用的微服务列表及网络地址进行服务调用
- 可以对所有的微服务进行心跳检测,如发现某实例长时间无法访问,就会从服务注册表移除该实例。
Eureka
- Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务。springCloud将它集成在其子项目spring-cloud-netflix中,以实现Springcloud的服务注册和发现功能。
- Eureka包含两个组件:Eureka server和Eureka client。(CS架构)
- Eureka Server 提供服务注册,各个节点启动后,回在EurekaServer中进行注册,这样Eureka Server中的服务注册表中将会储存所有课用服务节点的信息,服务节点的信息可以在界面中直观的看到.
- Eureka Client 是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳 (默认周期为30秒) 。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉 (默认周期为90s).
- 三大角色
- Eureka Server:提供服务的注册与发现
- Service Provider:服务生产方,将自身服务注册到Eureka中,从而使服务消费方能狗找到
- Service Consumer:服务消费方,从Eureka中获取注册服务列表,从而找到消费服务
-
eureka-server
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
server: port: 8761 spring: application: # 应用名 推荐大写 禁止下滑线 name: EUREKASERVER8761 eureka: instance: # 应用实例主机名 hostname: 127.0.0.1 client: # 是否向 Eureka 注册中心注册 register-with-eureka: false # 是否立即注册 fetch-registry: false # Eureka监控页面 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
@SpringBootApplication @EnableEurekaServer public class SpringCloudEurekaServer8761SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudEurekaServer8761SpringBootApplication.class,args); } }
-
eureka-client
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
server: port: 8002 spring: application: name: EUREKACLIENT8002 eureka: instance: instance-id: springcloud-eureka-client-8002 client: service-url: defaultZone: http://localhost:8761/eureka/
@SpringBootApplication @EnableEurekaClient public class SpringCloudEurekaClient8002SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudEurekaClient8002SpringBootApplication.class,args); } }
-
监控信息
怀疑最新版spring-boot-starter-actuator(2.6.4)有bug,访问http://localhost:8001/actuator/info无数据,回退2.5.6版本正常
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
management: endpoints: web: exposure: include: "info" info: app: name: springcloud-provider-8001 encoding: @project.build.sourceEncoding@ java: source: @java.version@ target: @java.version@ build: artifactId: @project.artifactId@ version: @project.version@ company: name: 山西中北大学软件学院 author: 石一歌
-
微服务信息
@SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient public class SpringCloudEurekaClient8002SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudEurekaClient8002SpringBootApplication.class,args); } }
@Resource private DiscoveryClient client; @GetMapping("/discovery") public Object discovery() { List<String> services = client.getServices(); System.out.println("discovery=>services:" + services); List<ServiceInstance> instances = client.getInstances("PROVIDER8001");//使用应用名称获取 for (ServiceInstance instance : instances) { System.out.println( instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri() + "\t" + instance.getServiceId() ); } return instances; }
-
自我保护机制
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。Eurekaserver在运行期间会去统计心跳失败比例在15分钟之内是否低于85%,如果低于85%,Eureka Server会将达些实例保护起来,让这些实例不会过期。这种设计的哲学原理就是""宁可信其有不可信其无!"。自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。
- 注意:自我保护机制默认是开启的
- 现象:在自我保护模式下,eureka服务器将停止逐出所有实例,
- 机制:这样做是为了确保灾难性的网络事件不会清除eureka注册表数据,并将其传播到下游的所有客户端触发自我保护机制
- 客户端在服务注册中心被清除的条件
- 心跳的次教高于预期阈值
- 自我保护被禁用
关闭自我保护机制
eureka: server: # 关闭自我保护机制(不建议关闭) enable-replicated-request-compression: false # 设置自动清除时间(默认60000) eviction-interval-timer-in-ms: 3000
eureka: instance: # 续约到期期限(默认90) lease-expiration-duration-in-seconds: 10 # 续约时间间隔(默认30) lease-renewal-interval-in-seconds: 5
-
eureka 集群
以自己为客户端向其余的注册中心注册,构成eureka 集群
-
复制项目配置,编写VM参数
-Dserver.port=8762
覆盖项目端口号 -
手动建立N个项目
server: port: 8761 spring: application: name: EUREKASERVER8761 eureka: instance: # 应用实例主机名 hostname: 127.0.0.1 client: # 是否向 Eureka 注册中心注册 register-with-eureka: false # 是否立即注册 fetch-registry: false # Eureka监控页面 service-url: # defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ defaultZone: http://${eureka.instance.hostname}:8762/eureka/,http://${eureka.instance.hostname}:8763/eureka/ server: # 关闭自我保护机制 enable-replicated-request-compression: false # 设置自动清除时间(默认60000) eviction-interval-timer-in-ms: 3000
-
Consul
consul是一个可以提供服务发现,健康检查,多数据中心,Key/value存储等功能的分布式服务框架,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,使用起来也较为简单。consu1用colang实现,因此具有天然可移植性(支持Linux、windows和Mac os x);安装包仅包含—个可执行文件,方便部署。
- 简介:consul基于go语言进行开发服务注册中心轻量级服务注册中心google
- 作用:管理微服务中所有服务注册发现―管理服务元数据信息存储〔服务名地址列表)心跳健康检查
-
consul-server
- 下载consul
- 在指定目录进行解压缩
- 启动服务注册中心
- 在consul安装目录中打开cmd执行
consul agent -dev
命令
- 在consul安装目录中打开cmd执行
- 访问consul管理界面
- 管理界面基本介绍
- dc1:数据中心名称datacenter(默认为: dc1)指定数据中心启动
consul agent -dev -datacenter=datacentername
- services:当前consul服务中注册服务列表(默认:client server同时启动自己注册自己会出现一个consul服务)
- nodes:用来查看consul的集群节点
- dc1:数据中心名称datacenter(默认为: dc1)指定数据中心启动
- 推荐配置环境变量
Path = consul安装目录
-
consul-client
默认开启注册健康检查需要引入spring-boot-starter-actuator依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
server: port: 8003 spring: application: name: CONSULCLIENT8003 cloud: consul: # 服务注册ip及端口 host: 127.0.0.1 port: 8500 discovery: # 服务名(默认为服务名称) service-name: ${spring.application.name} # 注册健康检查(默认开启) register-health-check: true
@SpringBootApplication @EnableDiscoveryClient public class SpringCloudConsulClient8003SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudConsulClient8003SpringBootApplication.class,args); } }
Nacos
Nacos Name Service(服务注册与发现) & Configurations Services(统一配置中心)
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理此处只介绍服务注册与发现功能
-
nacos-server
注意,Latest版本并不是最新版,要与maven维护版本一致
-
nacos-client
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
server: port: 8004 spring: application: name: NACOSCLIENT8004 cloud: nacos: # 服务器总地址 server-addr: 127.0.0.1:8848 discovery: # 注册中心地址 server-addr: ${spring.cloud.nacos.server-addr} # 注册服务名称 service: ${spring.application.name}
@SpringBootApplication // 该注解可省略 @EnableDiscoveryClient public class SpringCloudNacosClient8004SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudNacosClient8004SpringBootApplication.class, args); } }
服务调用与负载均衡
环境准备
将consul provider复制三份,分别占用8005 8006 8007三个端口,服务名共用CONSULPROVIDER,形成集群
consul consumer添加如下接口,用来测试负载均衡
@Value("${server.port}") private int port; @GetMapping("/port") public String port() { return "The port is: " + port; }
RestTemplate&Ribbon
RestTemplate http客户端
Ribbon 客户端负载均衡
- Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。
- 原理:根据调用服务服务id去服务注册中心获取对应服务id的服务列表,并将服务列表拉取本地进行缓存,然后在本地通过默认的轮询的负载均衡策略在现有列表中选择一个可用节点提供服务
- 客户端负载均衡
最新版spring-cloud-starter-consul-discovery已经移除spring-cloud-starter-netflix-ribbon,需要手动添加,添加后原有的loadbalancer用法失效。
猜测原因为版本不一致 spring-cloud-starter-netflix-ribbon2.2.10.RELEASE spring-cloud-starter-loadBalancer3.1.1,自定义规则无法实操
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> <version>2.2.10.RELEASE</version> </dependency>
-
使用方法
-
DiscoveryClient
只发现服务列表,需要手动实现负载均衡
@Resource private DiscoveryClient discoveryClient; @RequestMapping("/port") public String port() { return restTemplate.getForObject(discoveryClient.getInstances("CONSULPROVIDER").get(new Random().nextInt(discoveryClient.getInstances("CONSULPROVIDER").size())).getUri() + "/dept/port", String.class); }
-
LoadBalancerClient
发现服务列表并自动实现负载均衡
@Resource private LoadBalancerClient loadBalancerClient; @RequestMapping("/port") public String port() { return restTemplate.getForObject(loadBalancerClient.choose("CONSULPROVIDER").getUri() + "/dept/port", String.class); }
-
@LoadBalanced
加在RestTemplate上,使其具有负载均衡功能,直接解析服务名
@Configuration public class BeanConfig { @LoadBalanced @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
@RequestMapping("/port") public String port() { return restTemplate.getForObject("http://CONSULPROVIDER/dept/port", String.class); }
-
-
负载均衡策略
- 源码分析
- 查看loadBalancerclient . choose ( ""ORDERS"")源码
- 得知serviceInstanceChooser是LoadBalanceclient父接口
- serviceInst anceChooser 的choose方法默认实现 RibbonLoadBalancerClient
- 在RibbonLoadBalancerclient有一个choose方法带有两个参数这里面进行负载均衡实现
- 查看getserver方法实现
- 得知IRule是底层负载均衡父接口
- 源码分析
-
IRule
-
常见规则
-
RoundRobinRule 轮询策略
按顺序循环选择server -
RandomRule 随机策略
随机选择Server -
AvailabilityFilteringRule 可用过滤策略
会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问 -
weightedResponseTimeRule 响应时间加权策略
根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够会切换回来 -
RetryRule 重试策略
先按照RoundRobinRule的策略获取服务,如果获取失败则在制定时间内进行重试,获取可用的服务。 -
BestAviableRule 最低并发策略
会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务 -
NacosRule Nacos策略
优先选择同集群服务实例列表;本地集群找不到提供者,才去其它集群寻找,并且会报警告;确定了可用实例列表后,再采用随机负载均衡挑选实例
-
-
设置规则
-
代码配置
/** * Ribbon的配置类 * 注意:该类不能放在主应用程序上下文@ComponentScan所扫描的包中,否则配置将会被所有Ribbon Client共享。 * @IgnoreScan 该注解可解决上述问题 */ @Configuration @IgnoreScan public class RibbonConfiguration { @Bean public IRule ribbonRule() { return new RandomRule(); } }
/** * 指定Ribbon的配置类 */ @RibbonClient(name = "serviceId", configuration = RibbonConfiguration.class) @ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value =IgnoreScan.class)}) @SpringBootApplication @EnableDiscoveryClient public class SpringCloudConsulConsumer9002SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudConsulConsumer9002SpringBootApplication.class,args); } }
-
属性配置
serviceId: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
-
全局配置
/** * 定义一个空类,使用@RibbonClients */ @RibbonClients(defaultConfiguration = DefaultRibbonConfig.class) public class RibbonClientDefaultConfigurationTestsConfig { }
/** * 定义一个config类,指定使用的负载均衡策略 */ @Configuration public class DefaultRibbonConfig { @Bean public IRule ribbonRule() { return new RandomRule(); } }
-
-
自定义规则
将设置规则的实例化改为自定义的规则类即可
public class MyRandomRule extends AbstractLoadBalancerRule { /** * 每个服务访问5次则换下一个服务 * <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);//从活着的服务中,获取指定的服务来进行操作 } //======================================================= 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) { } }
-
Feign/OpenFeign
OpenFegin由Feign发展而来,两者使用方法一致。
penFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。
优点
- 路径灵活
- 自动转换响应结果为对应对象
- 集成ribbon实现负载均衡
-
使用方法
创建与远程服务一致的接口绑定服务进行调用
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@FeignClient(value = "CONSULPROVIDER")
public interface ConsulProviderClient {
@GetMapping("/dept/port")
String port();
@PostMapping("/dept")
boolean save(@RequestBody Dept dept);
@PutMapping("/dept")
boolean update(@RequestBody Dept dept);
@DeleteMapping("/dept/{id}")
boolean delete(@PathVariable Integer id);
@GetMapping("/dept/{id}")
Dept getById(@PathVariable Integer id);
@GetMapping("/dept")
List<Dept> get();
}
@RestController
@RequestMapping("/dept")
public class DeptController {
@Resource
private ConsulProviderClient consulProviderClient;
@RequestMapping("/port")
public String port() {
return consulProviderClient.port();
}
@RequestMapping("/save")
public boolean save(Dept dept) {
return consulProviderClient.save(dept);
}
@RequestMapping("/update")
public boolean update(Dept dept) {
return consulProviderClient.update(dept);
}
@RequestMapping("/delete/{id}")
public boolean delete(@PathVariable Integer id) {
return consulProviderClient.delete(id);
}
@RequestMapping("/get/{id}")
public Dept getById(@PathVariable Integer id) {
return consulProviderClient.getById(id);
}
@RequestMapping("/get")
public List<Dept> get() {
return consulProviderClient.get();
}
}
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SpringCloudOpenFeignConsumer9003SpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudOpenFeignConsumer9003SpringBootApplication.class,args);
}
}
-
形参
-
零散类型
-
queryString传参
@GetMapping("/test") public String test(@RequestParam("name") String name, @RequestParam("id") Integer id) { return name + id; }
@GetMapping("/dept/test") String test(@RequestParam("name") String name, @RequestParam("id") Integer id);
@GetMapping("/test") public String test() { return consulProviderClient.test("石一歌", 20); }
-
路径传参
@GetMapping("/test2/{id}/{name}") public String test2(@PathVariable("id") Integer id, @PathVariable("name") String name) { return name + id; }
@GetMapping("/dept/test2/{id}/{name}") String test2(@PathVariable("id") Integer id, @PathVariable("name") String name);
@GetMapping("/test2") public String test2() { return consulProviderClient.test2(20, "石一歌"); }
-
-
对象
@PostMapping("/test3") public String test3(@RequestBody Dept dept) { return dept.toString(); }
@PostMapping("/dept/test3") String test3(@RequestBody Dept dept);
@GetMapping("/test3") public String test3() { return consulProviderClient.test3(new Dept(1L, "保卫部", "springCloudDb")); }
-
数组
@GetMapping("/test4") public String[] test4(@RequestParam("ids") String[] ids) { return ids; }
@GetMapping("/dept/test4") String[] test4(@RequestParam("ids") String[] ids);
@GetMapping("/test4") public String[] test4() { return consulProviderClient.test4(new String[]{"1", "2", "3"}); }
-
列表
@GetMapping("/test5") public String test5(@RequestParam("ids") ArrayList ids) { return ids.toString(); }
@GetMapping("/dept/test5") String test5(@RequestParam("ids") String[] ids);
@GetMapping("/test5") public String test5() { return consulProviderClient.test5(new String[]{"1", "2", "3"}); }
-
-
返回值
-
对象
@GetMapping("/{id}") public Dept getById(@PathVariable Integer id) { return deptService.getById(id); }
@GetMapping("/dept/{id}") Dept getById(@PathVariable Integer id);
@RequestMapping("/get/{id}") public Dept getById(@PathVariable Integer id) { return consulProviderClient.getById(id); }
-
列表
@GetMapping public List<Dept> get() { return deptService.list(); }
@GetMapping("/dept") List<Dept> get();
@RequestMapping("/get") public List<Dept> get() { return consulProviderClient.get(); }
-
Map
实战场景是手写分页,用map传输泛型会发生泛型擦除,需要消费者需要手动反序列化
@GetMapping("/test6/{page}/{rows}/{dName}") public Map<String, Object> test6(@PathVariable Integer page, @PathVariable Integer rows, @PathVariable String dName) { Page<Dept> resPage = deptService.page(new Page<>(page, rows), Wrappers.<Dept>lambdaQuery().like(Dept::getDName, dName)); Map<String, Object> map = new HashMap<>(); map.put("total",resPage.getTotal()); map.put("records",resPage.getRecords()); return map; }
@GetMapping("/dept/test6/{page}/{rows}/{dName}") Map<String,Object> test6(@PathVariable Integer page, @PathVariable Integer rows, @PathVariable String dName);
@GetMapping("/test6/{page}/{rows}/{dName}") public Map<String, Object> test6(@PathVariable Integer page, @PathVariable Integer rows, @PathVariable String dName) { Map<String, Object> objectMap = consulProviderClient.test6(page, rows, dName); List<DeptDto> deptList = JSONUtil.toList(JSONUtil.parseArray(objectMap.get("records")), DeptDto.class); deptList.forEach(System.out::println); return objectMap; }
-
-
超时处理
默认的调用超时:使用openFeigm组件在进行服务间通信时要求被调用服务必须在1s内给予响应,一旦服务执行业务逻辑时间超过1s , openFeign组件将直接报错:
Read timed out executing GET http://PRODUcT/product
feign: client: config: # 配置服务名超时时间 CONSULPROVIDER: # 连接超时时间 connectTimeout: 5000 # 等待响应时间 readTimeout: 5000
feign: client: config: # 配置默认超时时间 default: connectTimeout: 5000 readTimeout: 5000
-
日志展示
openFeign伪HttpClient客户端对象,用来帮助我们完成服务间通信(底层用http协议完成服务间调用)
日志:OpenFeign 为了更好方便在开发过程中调试openFeign数据传递,和响应处理,openFeign在设计时添加了日志功能,默认openFeign曰志功能需要手动开启的日志水平
NONE
不记录任何日志BASIC
仅仅记录请求方法,url,响应状态代码及执行时间HEADERS
记录Basic级别的基础上,记录请求和响应的headerFULL
记录请求和响应的header,body和元数据
feign: client: config: # 配置日志水平 CONSULPROVIDER: loggerLevel: FULL logging: level: com: nuc: # 客户端包名开启日志并指定debug级别 feignclient: debug
服务熔断与服务降级
服务雪崩
- 现象:在一个时刻微服务系统中所有微服务均不可用的这种现象称之为服务雪崩现象
- 根本原因:在调用链路中链路中某一服务因为执行业务时间过长,或者是大规模出现异常导致自身服务不可用,并把这种不可用放大情况
服务熔断
- 作用:防止在微服务系统中发生服务雪崩现象
- 熔断机制:微服务中引入服务熔断组件后,在满足熔断要求(10秒内存20请求调用失败&&10秒内超过50%的请求失败)后打开断路器。断路器在满足一定要求后会自动关闭(循环周期为5秒的单次请求通过)
服务降级
- 定义:服务压力剧增的时候根据当前的业务情况及流量对一些边缘服务和页面有策略的关闭,以此缓解服务器的压力,以保证核心任务的进行,同时保证部分甚至大部分任务能能得到正确的响应。
总结
熔断必会触发降级,所以熔断也是降级一种,区别在于熔断是对调用链路的保护,而降级是对系统过载的一种保护处理
- 共同点
- 目的一致,都是从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃。采用的技术手段
- 最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用;
- 粒度一般都是服务级别,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增删改);
- 自治性要求高,熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段
- 异同点
- 触发原因不一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
- 管理目标的层次不一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务边缘服务开始)
Hystrix
Hystrix是一个应用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性。
断路器本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控 (类似熔断保险丝) ,向调用方返回一个服务预期的,可处理的备选响应 (FallBack) ,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
断路器开启条件
- 10秒内存20请求调用失败
- 10秒内超过50%的请求失败
Hystrix监控流程
-
服务熔断(客户端)
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.10.RELEASE</version> </dependency>
@SpringBootApplication // 开启Hystrix支持 @EnableHystrix public class SpringCloudHystrixProvider7001SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudHystrixProvider7001SpringBootApplication.class, args); } }
@RestController public class HystrixController { @GetMapping("/demo/{id}") // 配置自定义应变计划方法、默认应变计划方法 @HystrixCommand(fallbackMethod = "demoFallback",defaultFallback = "defaultFallback") public String demo(@PathVariable("id") Integer id) { if (id <= 0) { throw new RuntimeException("无效id"); } return id + "号员工已离职"; } public String demoFallback(@PathVariable("id") Integer id) { return "网络繁忙。。。"; } public String defaultFallback() { return "服务已熔断。"; } }
-
服务降级(服务端)
feign: # 开启服务降级 circuitbreaker: enabled: true # 开启服务降级(旧版本) hystrix: enabled: true
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableHystrix public class SpringCloudHystrixConsumer9004SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudHystrixConsumer9004SpringBootApplication.class,args); } }
@FeignClient(value = "HYSTRIXPROVIDER8008",fallback = HystrixClientFallBack.class) public interface HystrixClient { @GetMapping("/demo/{id}") String demo(@PathVariable("id") Integer id); }
@Component public class HystrixClientFallBack implements HystrixClient{ @Override public String demo(Integer id) { return "服务已熔断。"; } }
-
流监控(DashBoard)
最新版没有发现bug,无需注入Bean和修改jQuery
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.2.10.RELEASE</version> </dependency>
@SpringBootApplication @EnableHystrixDashboard public class SpringCloudHystrixDashboard9999SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudHystrixDashboard9999SpringBootApplication.class, args); } }
server: port: 9999 spring: application: name: HYSTRIXDASHBOARD9999 hystrix: dashboard: proxy-stream-allow-list: 127.0.0.1
management: endpoints: web: exposure: include: hystrix.stream
- http://127.0.0.1:port/hystrix中访问http://127.0.0.1:port/actuator/hystrix.stream
Sentinel
Sentinel 是面向分布式服务架构的高可用防护组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助用户保障微服务的稳定性。
-
特征
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
-
概念
-
资源
一切皆资源,rest服务接口也是资源
-
规则
- 流量规则
- 熔断规则
- 负载规则
-
QPS
Query-Per-Second 每秒请求数
-
RT
Response-Time 单个请求响应时间
-
-
dashboard
-
指定端口运行
java -jar -Dserver.port=9191 sentinel-dashboard-1.8.4.jar
-
访问管理界面
-
sentinel客户端使用
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
server: port: 9898 spring: application: name: SENTINEL9898 cloud: nacos: server-addr: 127.0.0.1:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} service: ${spring.application.name} sentinel: # 开启sentinel enabled: true transport: # dashboard web地址 dashboard: 127.0.0.1:9191 # dashboard 通信端口 port: 8719
-
五大规则
-
流控规则
流量控制(Flow Control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
QPS:当每秒请求数超过指定阈值之后对当前请求进行限流
并发线程数:当服务器中创建线程数超过指定阙值之后对当前请求进行限流-
普通规则
- QPS,直接刷新浏览器测试
- 并发线程数,使用压测工具jmeter测试
-
高级
-
流控模式
-
直接模式
配置资源在运行过程中超过规则配的阈值后的自身请求的处理
-
关联模式
配置资源在运行过程中超过规则配的阈值后的关联资源请求的处理,反向关联。
-
链路模式
配置资源在运行过程中超过规则配的阈值后的链路资源请求的处理,类似正向关联。
-
-
流控效果(只适用QPS)
-
快速失败
直接拒绝请求,并抛出响应异常
-
Warm Up
冷启动/预热
-
排队等待
始终匀速通过
-
-
-
-
降级规则
熔断降级 (Degrade Rule),其原理时监控应用中资源调用请求,达到指定阈值时自动触发熔断降级。
-
熔断策略
-
慢调用比例
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(
statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。- 最大响应时间
- 比例阈值
- 熔断时长
- 最小请求数
- 统计时长
-
异常比例
当单位统计时长(
statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。- 比例阈值
- 熔断时长
- 最小请求数
- 统计时长
-
异常数
当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
- 异常数
- 熔断时长
- 最小请求数
- 统计时长
-
-
-
热点规则
热点参数(ParamFlow Rule),热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
- 参数索引
- 单机阈值
- 统计窗口时长
必须使用资源别名。
-
blockHandler sentinel不同规则默认处理方案
-
fallback 自定义业务异常处理方案
-
defaultFallback 默认业务异常处理方案
@GetMapping("/demo") @SentinelResource(value = "aaa", blockHandler = "blockHandler", fallback = "fall", defaultFallback = "defaultFall") public String demo(Integer id) { if (id < 0) { throw new RuntimeException("id错误"); } return "demo is ok! id = " + id; } public String blockHandler(Integer id, BlockException e) { if (e instanceof FlowException) { return "请求火爆,已流控"; } if (e instanceof DegradeException) { return "请求火爆,已降级"; } if (e instanceof ParamFlowException) { return "请求火爆,已热点限流"; } return "服务器宕了,别™请求了"; } public String fall(Integer id) { return "服务器出错"; } public String defaultFall(Integer id) { return "服务器出错"; }
- 高级
- 参数额外项
- 参数类型
- 参数值
- 限流阈值
- 参数额外项
-
系统规则
系统自适应(ParamAdaptive Rule),Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
-
授权规则
来源访问控制(Authority Rule),来源访问控制根据资源的请求来源(
origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
-
服务网关
网关统一服务入口,可方便实现对平台众多服务接口进行管控
- 网关可以实现服务的统一管理
- 网关可以实现请求路由转发(router dispatcher)以及请求过程负载均衡
- 访问服务的身份认证、防报文重放与防数据篡改、功能调用的业务鉴权、响应数据的脱敏、流量与并发控制,甚至基于AI调用的计量或者计费
Zuul
Gateway
项目提供了一个在springmvc之上构建API网关的库。springcloudgateway旨在提供一种简单而有效的方法来路由到api,并为api提供横切关注点,比如。安全性、监控/度量和弹性。
- 响应式异步非阻塞Io模型(spring webFlux 和 Reactor)
- 动态路由
- 请求过滤
-
网关配置
注意spring-boot-starter-web需要被排除
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
-
yaml
网关规则支持通配符,也支持
,
分割。spring: application: name: GATEWAY7001 cloud: consul: host: 127.0.0.1 port: 8500 discovery: service-name: ${spring.application.name} register-health-check: true gateway: routes: # 路由对象唯一标识 - id: 9003 # 服务地址 # uri: http://localhost:9003 # 负载均衡 uri: lb://OPENFEIGNCONSUMER # 断言 predicates: - Path=/dept/port
-
java
@Configuration public class GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("9003", r -> r.path("/dept/port").uri("http://localhost:9003")) .build(); } }
-
-
细节
-
断言
请求到达网关时,网关前置处理,满足断言放行请求,否则立即返回
-
Path
spring: cloud: gateway: routes: - id: path_route uri: https://example.org predicates: # 路径断言 - Path=/red/{segment},/blue/{segment}
-
After
spring: cloud: gateway: routes: - id: after_route uri: https://example.org predicates: # 时间后断言 - After=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]
-
Before
spring: cloud: gateway: routes: - id: before_route uri: https://example.org predicates: # 时间前断言 - Before=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]
-
Between
spring: cloud: gateway: routes: - id: between_route uri: https://example.org predicates: # 时间区间断言 - Between=2017-01-20T17:42:47.789-07:00[Asia/Shanghai], 2017-01-21T17:42:47.789-07:00[Asia/Shanghai]
-
Cookie
spring: cloud: gateway: routes: - id: cookie_route uri: https://example.org predicates: # cookie断言,可配合正则表达式 - Cookie=chocolate, ch.p
-
Header
spring: cloud: gateway: routes: - id: header_route uri: https://example.org predicates: # 请求头断言 - Header=X-Request-Id, \d+
-
Method
spring: cloud: gateway: routes: - id: method_route uri: https://example.org predicates: # 方法断言 - Method=GET,POST
-
-
过滤
当请求满足断言的所有条件时,会向后端服务转发,进行一系列的过滤操作
-
AddRequestHeader
spring: cloud: gateway: routes: - id: add_request_header_route uri: https://example.org filters: # 加入请求头 - AddRequestHeader=X-Request-red, blue
-
AddRequestParameter
spring: cloud: gateway: routes: - id: add_request_parameter_route uri: https://example.org filters: # 加入请求参数 - AddRequestParameter=red, blue
-
AddResponseHeader
spring: cloud: gateway: routes: - id: add_response_header_route uri: https://example.org filters: # 加入响应头 - AddResponseHeader=X-Response-Red, Blue
-
PrefixPath
spring: cloud: gateway: routes: - id: prefixpath_route uri: https://example.org filters: # 加入指定前缀 - PrefixPath=/mypath
-
StripPrefix
spring: cloud: gateway: routes: - id: nameRoot uri: https://nameservice predicates: - Path=/name/** filters: # 去掉2级前缀,常用于区分微服务的同名接口 - StripPrefix=2
-
GlobalFilter
@Bean public GlobalFilter customFilter() { return new CustomGlobalFilter(); } // 自定义全局filter public class CustomGlobalFilter implements GlobalFilter, Ordered { // 封装了Request和getResponse @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getReques(); ServerHttpResponse response = exchange.getResponse() ; log.info("custom global filter"); return chain.filter(exchange); } // 排序filter,指定执行顺序,默认按自然数字排序,-1在所有filter前执行 @Override public int getOrder() { return -1; } }
-
-
网关web展示
management: endpoints: web: exposure: include: "*"
-
统一配置&消息总线
统一配置中心实现微服务系统中服务配置统一管理,消息总线实现配置自动更新
Config
config统一配置中心将配置统一管理,配置统一管理的好处是在日后大规模集群部署服务应用时相同的服务配置一致,日后再修改配置只需要统―修改全部同步,不需要一个一个服务动维护。
-
config-server
提前创建gitee仓库,配置文件以properties、json、yaml结尾
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
server: port: 7777 spring: application: name: CONFIGSERVER cloud: consul: host: 127.0.0.1 port: 8500 discovery: service-name: ${spring.application.name} register-health-check: true config: server: # 配置gitee仓库地址和分支,私有仓库还需要配置账号密码 git: uri: https://gitee.com/Faetbwac/config.git default-label: master
@SpringBootApplication @EnableDiscoveryClient @EnableConfigServer public class SpringCloudConfigServer7777SpringBootApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudConfigServer7777SpringBootApplication.class,args); } }
-
config-client
配置客户端引导文件必须为bootstrap.yaml,最新版springcloud移除spring-cloud-starter-bootstrap(需要添加)
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency>
server: port: 7878 spring: cloud: config: discovery: # 配置服务端注册中心id service-id: CONFIGSERVER # 开启配置客户端 enabled: true # 配置文件分支 label: master # 配置文件名 name: configclient # 配置文件环境 profile: prod # 注册中心地址(无法放在远端仓库) consul: host: 127.0.0.1 port: 8500
@RestController public class DemoController { @Value("${name}") private String name; @GetMapping("/demo") public String demo() { return name+ " is ok!"; } }
-
配置config手动刷新
实现单个手动刷新
@RestController // 不重启服务前提下,将scope中的配置信息刷新 @RefreshScope public class DemoController { @Value("${name}") private String name; @GetMapping("/demo") public String demo() { return name+ " is ok!"; } }
spring: cloud: config: discovery: service-id: CONFIGSERVER enabled: true label: master name: configclient profile: prod consul: host: 127.0.0.1 port: 8500 management: endpoints: web: exposure: include: "*"
curl -X POST http://localhost:7878/actuator/refresh
Bus
springcloud bus 利用轻量级消息中间件将分布式系统中所有服务连接到一起
利用bus 广播特性当某一个状态(配置文件)发生改变时通知到bus中所有服务节点更新当前状态(更新自身配置)
-
搭建rabbitmq
前面已经学习过rabbitmq,不再赘述,参考链接
-
配置bus手动刷新
实现批量手动刷新
注意,config-cilent的rabbitmq配置在远端,本地需要开启快速失败
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
server:
port: 7777
spring:
application:
name: CONFIGSERVER
cloud:
consul:
host: 127.0.0.1
port: 8500
discovery:
service-name: ${spring.application.name}
register-health-check: true
config:
server:
git:
uri: https://gitee.com/Faetbwac/config.git
default-label: master
# 配置rabbitmq
rabbitmq:
host: 47.93.254.136
port: 5672
username: guest
password: guest
virtual-host: /
management:
endpoints:
web:
exposure:
include: "*"
spring:
cloud:
config:
discovery:
service-id: CONFIGSERVER
enabled: true
label: master
name: configclient
profile: prod
# 允许快速失败
fail-fast: true
consul:
host: 127.0.0.1
port: 8500
management:
endpoints:
web:
exposure:
include: "*"
# 测试无法使用,yml文件可以拉取,刷新无效,怀疑最新版问题
# 全部刷新
curl -X POST http://localhost:7777/actuator/busrefresh
# 指定id刷新
curl -X POST http://localhost:7777/actuator/busrefresh/CONFIGCILENT-7878
-
配置webhooks自动刷新
注意,webhooks必须提供外网地址,测试依旧无法使用,怀疑最新版问题
Nacos
nacos会在自己服务器形成版本库,无须远程创建。
-
登录nacos配置中心创建客户端需要的配置
-
客户端配置(自动刷新)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--新版本默认不再优先加载bootstrap文件,需要引入以下依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
# 注意文件名称为bootstrap.yml
# 名称配置
spring:
cloud:
nacos:
config:
# 配置中心地址
server-addr: 127.0.0.1:8848
# 配置组
group: DEFAULT_GROUP
# 配置名称
name: nacosconfig-prod
# 配置后缀
file-extension: yaml
# 前缀配置
spring:
profiles:
active: prod
cloud:
nacos:
config:
# 配置中心地址
server-addr: 127.0.0.1:8848
# 配置组
group: DEFAULT_GROUP
# 配置后缀
file-extension: yaml
# 配置前缀
prefix: nacosconfig
@RestController
@RefreshScope
public class DemoController {
@Value("${username}")
private String username;
@GetMapping("/demo")
public String demo() {
return username+" demo ok!";
}
}
-
细节
-
data id
因代表不同,有两种配置方式
-
代表配置项
spring.cloud.nacos.config.name + spring.cloud.nacos.config.file-extension
-
代表配置文件
${prefix}-${spring.profile.active}.${file-extension}
-
-
三大概念
-
命名空间
nacos安装完成会有默认命名空间 public。
项目角度隔离配置文件
-
组
nacos安装完成会有默认组 DEFAULT_GROUP
服务角度隔离配置文件
-
文件id
配置文件唯一标识
-
-
-
mysql持久化
注意:默认nacos存在配置信息持久化,默认的持久化方式为内嵌数据库derby缺点:无法友好的展示数据
官方建议:在生产情况下推荐将配置存入mysql数据库注意: nacos到目前为止仅仅支持mysql-
安装mysql
-
创建源文件
vim /etc /yum.repos.d/mysql-community.repo
-
编写mysql下载源
[mysql57-commuinity] name=MySQL 5.7 Community Server baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/7/$basearch/enabled=1 gpgcheck=0 gpgkey=file:///etc/pki/rpm-gpg/RPH-GPG-KEY-mysql
-
下载mysql
yum install mysql-community-server -Y
-
启动mysql
systemctl start mysqld
-
获取临时密码
grep 'teporary password' /var/log/mysqld.log
-
修改root密码
mysqladmin -u root -p password # 回车 原始密码 新密码
-
登录mysql
mysql -uroot -p password
-
开启远程权限并刷新
grant all privileges on *.* to 'root'@'%' identified by 'password' with grant option; flush privileges
-
创建nacos数据库,执行nacos2.0.3/conf/nacos_mysql.sql
-
-
修改配置文件
vim application.properties
spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true db.user=root db.password=root
-
-
nacos集群
nacos集群,持久化mysql不能有原始数据
-
修改配置文件
vim naocs/conf/cluster.conf
127.0.0.1:8848 127.0.0.1:8858 127.0.0.1:8868
-
数据mysql持久化
-
nginx负载均衡
-
安装nginx
-
安装依赖
yum install -y gcc pcre-devel zlib-devel
-
解压缩
tar -zxvf nginx-1.11.1.tar. gz
-
编译安装
./configure --prefix=/usr/nginx make && make insta11
-
启动nginx
cd sbin ./nginx
-
关闭nginx
./nginx -s stop
-
-
配置负载均衡
vim nginx/conf/nginx.conf
# 新建代理服务器,代理nacos http { upstream nacos-servers { server 127.0.0.1:8848 server 127.0.0.1:8858 server 127.0.0.1:8868 } server { listen 80; server_name loaclhost; location / { proxy_pass http://nacos-servers/; } } }
# 指定配置文件启动 ./nginx -c /usr/nginx/conf/nginx.conf
# 修改客户端文件中nacos的服务地址 spring: cloud: nacos: server-addr: 127.0.0.1
-
-
总结
毕业版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.cloud.version>Hoxton.SR12</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.7.RELEASE</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>{spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
参考链接
- 从分布式一致性谈到CAP理论、BASE理论
- 【编程不良人】2021年最新SpringCloud微服务实战教程
- 【狂神说Java】SpringCloud最新教程IDEA版
- 狂神说SpringCloud学习笔记
- 编程不良人Spring Cloud Alibaba
- springcloud四个注册中心的比较
- consul(一)什么是consul
- springboot创建父子工程、聚合工程及问题解决
- Spring Cloud Eureka 之常用配置解析
- Eureka监控页面 http://localhost:8001/actuator/info 无法显示
- Eureka自我保护机制
- 负载均衡之ribbon
- Ribbon的负载均衡策略及使用方法
- 浏览器地址栏访问是get还是post请求
- SpringCloud Hystrix dashboard2.2.7使用和配置,SpringCloud Hystrix dashboard服务监控