1、Spring Cloud Alibaba
1.1、Spring Cloud Alibaba概述
官网地址:https://spring.io/projects/spring-cloud-alibaba
前文: Spring Cloud
Spring Cloud Alibaba 为分布式应用开发提供一站式解决方案。它包含开发分布式应用程序所需的所有组件,使您可以轻松地使用 Spring Cloud 开发应用程序。
使用Spring Cloud Alibaba,您只需添加一些注解和少量配置,即可将Spring Cloud应用连接到阿里巴巴的分布式解决方案,并通过阿里巴巴中间件构建分布式应用系统。
简单来说,Spring Cloud Alibaba 本质上就是Alibab基于Java自己封装的一些组件,在前面了解Spring Cloud的时候,Spring Cloud也就是在做微服务开发所提供的一系列组件。
1.2、Spring Cloud Alibaba 与 Spring Cloud
上面说到对于Spring Cloud Alibaba提供的一系列组件本质和Spring Cloud 提供的组件差不多,如下表:
Spring Cloud | Spring Cloud Alibaba | |
---|---|---|
服务注册中心 | Eureka、Consul | Nacos |
流控(服务熔断) | Hystrix | Sentinel |
统一配置中心 | Config | Nacos |
服务通信、负载均衡 | Ribbon、OpenFeign | Dubbo RPC |
2、Hello World
首先还是和Spring Cloud 项目一致,先构建一个父项目用来统一管理cloud和boot的版本。这里的Alibaba cloud 使用2.2.1版,而cloud和boot还是和前面的spring cloud使用H SR6和2.2.5版本,
github地址:https://github.com/lizuoqun/springcloudalibaba_parent (master分支)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lzq</groupId>
<artifactId>springcloudalibaba_parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<properties>
<spring.cloud-version>Hoxton.SR6</spring.cloud-version>
<spring.cloud.alibaba-version>2.2.1.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>
</project>
3、服务注册中心、统一配置中心 Nacos
Nacos官网:https://nacos.io/zh-cn/docs/what-is-nacos.html
3.1、Nacos概述
Nacos :Name Configuration Service
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
nacos下载地址:https://github.com/alibaba/nacos/releases
3.2、Nacos的安装和启动
在github上进行下载好慢,最后还是从别人分享的网盘当中下的,这里选择2.0.3版本的Nacos,下载gz文件,部署到linux虚拟机上,首先将gz文件上传给虚拟机,之后进行解压这个gz文件,在这之前还需要安装jdk,linux安装jdk这里也不做详细赘述了,解压后进入到nacos文件下,整个项目下包含3个主要文件夹,分别是bin——启动关闭nacos的脚本目录、conf——配置文件、target——核心jar包。
而后在启动Nacos的时候,默认会以集群进行启动,但是首先还是通过单机版本进行启动,使用命令:./startup.sh -m standalone
,在启动之后返回到上一层目录下,会有一个log文件夹,也就是Nacos的日志,进入log文件夹,使用tail -f nacos.log
进行查看日志。启动完成之后直接进行访问web端地址:http://192.168.101.128:8848/nacos
之后进行登录系统,默认账号和密码都是nacos
3.3、Nacos服务注册中心
首先构建一个SpringBoot的项目,引入SpringBoot、Nacos Client的依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
之后只需要在配置文件当中指定需要注册的Nacos的服务地址即可。
# 总地址
spring.cloud.nacos.server-addr=192.168.101.128:8848
# 服务注册中心地址,默认取值${spring.cloud.nacos.server-addr}
# spring.cloud.nacos.discovery.server-addr=192.168.101.128:8848
3.4、服务通信与负载均衡
在Alibaba的Nacos当中我们需要进行服务通信,而在Alibaba里面并没有提供相关服务,但是在Nacos Client当中集成了Ribbon依赖,所以在这里使用负载均衡还是使用Spring Cloud当中的Ribbon组件,这里在之前已经阐述过了,这里就不过多赘述了。
再往后和之前使用Ribbon进行实现负载均衡一样存在问题,这里就需要引入OpenFeign组件进行避免问题。
3.5、Nacos统一配置中心
在Nacos当中还提供了统一配置中心的管理,首先构建一个SpringBoot的项目,之后将服务注册到Nacos上面,在Nacos的web页面上会有一个配置管理,添加一个配置,这里的配置也就是当前SpringBoot应用要用到的公共配置。
而原本的项目我们需要配置项目去哪里去找这个配置项,并且配置文件文件名需要修改为bootstrap,配置文件如下:
# Nacos地址
spring.cloud.nacos.config.server-addr=192.168.101.128:8848
# 配置文件的分组
spring.cloud.nacos.config.group=DEFAULT_GROUP
# 配置文件文件名
spring.cloud.nacos.config.name=config-prod
# 配置文件文件后缀
spring.cloud.nacos.config.file-extension=properties
之后进行启动项目也就可以读到Nacos上面给配置的公共配置了,在这里配置实时更新,只需要在对应的Controller类上加上@RefreshScope
注解,之后在Nacos上进行修改配置,配置就会进行自动刷新了。
3.6、Nacos统一配置中心明细
dataId:表示完整的配置文件名称,(前缀-环境.后缀)=》 config-prod.properties
服务拉取配置
# 第一种方式
spring.cloud.nacos.config.name=config-prod
spring.cloud.nacos.config.file-extension=properties
# 第二种方式
spring.cloud.nacos.config.prefix=config
spring.profiles.active=prod
spring.cloud.nacos.config.file-extension=properties
统一配置中心的三个重要概念
- 命名空间(namespace) :默认Nacos安装完成之后会有一个默认命名空间,这个命名空间名为public。这个是站在项目的角度来说,将不同的项目的配置文件进行分开。
- 组(Group) :默认Nacos中在管理配置文件时不显示执行group名称,默认组名称为DEFAULT_GROUP。这个是站在服务角度,对多个微服务的配置文件进行分开。
- 文件名(dataId) : 获取一个配置文件的唯一标识
最后在这里新建了多个命名空间、组、文件名,在前面的配置文件当中还需要添加namespace的配置,其余的配置更换为对应的group、文件名id即可。
spring.cloud.nacos.config.namespace=对应id
最后在Nacos当中还可以对修改了的配置进行回滚操作,如下所示:
3.7、Nacos的mysql持久化
在前面对Nacos的了解,对于服务注册中心来说,数据不需要进行持久化,当有服务注册上来就直接进行显示,服务停掉就不显示,而这里的持久化是Nacos作为配置中心的数据进行持久化,前面对于配置管理可以看到,配置中心的数据还是存储在本地的,这里也就将数据持久化了到本地。在Nacos当中,默认的持久化的方式是内嵌的数据库debery。但是debery数据库也是有缺点的,在官网还是支持使用mysql数据库,
首先在linux下安装Mysql服务,这里网上教程也很多,也不进行过多赘述了,直接按照网上的教程进行安装即可,在官网下载安装包属实有点太慢了,这里可以使用清华源进行下载对应的安装包:https://mirrors.tuna.tsinghua.edu.cn/mysql/downloads/MySQL-8.0/
在安装完成之后,修改远程连接并生效,使用以下命令:修改远程连接之后就可以使用sql工具进行连接就好了。
update user set host='%' where user='root';
flush privileges;
最后就是对当前这个数据库进行设置了,首先创建一个库用于Nacos的持久化,
CREATE DATABASE nacos DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
而后对于数据库初始化需要一个nacos-mysql.sql文件,这个文件就在Nacos安装包的conf目录下
直接拉出来到sql工具当中执行。也就是将Nacos需要用到的表进行创建,而后在这个conf文件下修改application.properties文件,将数据源改成mysql,以及其余的mysql配置根据自身安装时的配置更改即可。
最后重启nacos。当在配置列表当中新加了一个配置之后,在mysql当中会存放在his_config_info
表当中,这样也就完成了nacos的mysql本地持久化了。
4、Sentinel
4.1、Sentinel 概述
Sentinel 是什么?
分布式系统的流量防卫兵,类似之前Cloud的Hystrix。随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinle是用来对现有微服务系统进行保护,也是用来替换Hystrix的。
Sentinel 基本概念
-
资源
- 资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用资源来描述代码块。只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
-
规则
- 围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
4.2、Sentinel DashBoard
首先在使用Sentinel的时候,sentinel和sentinel DashBoard都需要,这里的Sentinel 是通过依赖得到的,而DashBoard是通过jar包进行启动,首先先到官网进行下载。下载得到jar包后,直接通过命令进行启动即可:默认端口是8080,当然了这里还是和前面安装nacos一样,也将这个jar包给放到虚拟机上进行管理。放在windows下也是可以的。
下载地址:https://github.com/alibaba/Sentinel/releases
java -jar sentinel-dashboard-1.8.2.jar
# 修改端口进行启动
java -jar -Dserver.port=9999 sentinel-dashboard-1.8.2.jar
启动之后直接访问8080端口,账号密码都是sentinel进行登录。
4.3、Sentinel 服务
在 启动后,创建一个微服务注册到nacos上,在这里引入sentinel依赖,
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
修改配置文件:
spring.cloud.sentinel.enabled=true
spring.cloud.sentinel.transport.dashboard=192.168.101.128:8080
spring.cloud.sentinel.transport.port=8719
对接口进行监控
4.4、熔断规则——流量控制
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
- 阈值类型/单机阀值:
- QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流。
- 线程数:当调用该api的线程数达到闽值的时候,进行限流
- 是否集群:不需要集群
- 流控模式:
- 直接:api达到限流条件时直接限流
- 关联:当关联的资源达到阈值时,就限流自己。
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【API级别的针对来源】
- 效果
- 快速失败:直接失败,抛异常
- Warm up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
- 排队等待:匀速排队,让请求以匀速通过,与值类型必须设置为QPS,否则无效。
如上图:新加了一个熔断规则对接口进行监控,首先对QPS也就是每秒请求数量设置为2,当直接在浏览器一直刷新,访问该接口,可以在下图看到,当一个时间段的访问数超过了2之后,其余的请求都会直接失败进行处理掉。
上面是QPS每秒请求数进行处理,而后改成线程数为2,这个时候就需要借助工具来进行测试了,在这里使用JMater来进行压力测试,设置线程组,每组一次发3个线程,每次限制2个线程进行访问,多余的线程也会直接失败处理。
JMeter压力测试:JMeter压力测试博文
4.5、熔断规则——熔断降级
原理:当监控到调用链路中某–个服务,出现异常(20个以上异常)自动触发培断,在触发之后对于谈服务调用不可用
- RT:平均响应时间,秒级
- 平均响应时间 超出阈值 且 在时间窗口内通过的请求>=5,两个条件同时满足后触发降级,窗口期过后关闭断路器
- RT最大为4900,更大的需要通过 -Dcsp.sentinel.statistic.max.rt=XXXX 才能生效
- 异常比例,秒级
- QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
- 异常数,分钟级
- 异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
4.6、熔断规则——热点参数限流
热点也就是经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
在业务类中添加代码
- 这里@SentinelResource注解的value属性要求唯一,一般与请求Mapping相同,将来在sentinel控制台添加热点key限流时可以用@SentinelResource注解的value作为资源名。
- @SentinelResource注解的blockHandler属性用于指定兜底方法,需要在兜底方法的参数列表中添加一个BlockException类型的参数
@GetMapping("/demo")
@SentinelResource(value = "sentinelDemo", blockHandler = "blockHandler" , fallback = "fall")
public String demo(Integer id){
if (id<0) throw new RuntimeException("id 无效");
return "demo ok";
}
public String blockHandler(Integer id, BlockException e){
if(e instanceof FlowException){
return "已被流控";
}
if(e instanceof DegradeException){
return "已被降级";
}
return "===== 其余子类异常,这里就不一一列出来了 =====";
}
public String fall(){
return "服务器异常";
}
在这里的BlockException是整个所有sentinel抛出的异常的一个父类,在这里我们直接进入到该类就可以看到他的所有的子类,