Spring Cloud vs Spring Cloud Alibaba
Spring官网原文:
Spring Cloud Alibaba provides a one-stop solution for distributed application development. It contains all the components required to develop distributed applications, making it easy for you to develop your applications using Spring Cloud.
With Spring Cloud Alibaba, you only need to add some annotations and a small amount of configurations to connect Spring Cloud applications to the distributed solutions of Alibaba, and build a distributed application system with Alibaba middleware.
翻译:
阿里云为分布式应用开发提供了一站式解决方案。它包含了开发分布式应用程序所需的所有组件,使您可以轻松地使用springcloud开发应用程序。
有了阿里云,你只需要添加一些注解和少量的配置,就可以将Spring云应用连接到阿里的分布式解决方案上,用阿里中间件搭建一个分布式应用系统。
spring cloud alibaba 特点
维护springcloud依赖 -Hoxton.SR6
维护springcloud alibaba依赖 -2.2.1
集成springboot父项目 -2.2.5
a. 创建一个新的project (empty project)
b. 检查SDK配置版本;检查Maven配置
c. 创建父项目,并删除/src
d. 父项目依赖管理pom.xml
<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.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>SpringCloudAlibaba_parentartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.5.RELEASEversion>
parent>
<properties>
<spring.cloud-version>Hoxton.SR6spring.cloud-version>
<spring.cloud.alibaba.version>2.2.1.RELEASEspring.cloud.alibaba.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud-version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring.cloud.alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
注意
和
pom 这个写法:虽然当前项目是我们的父项目,但还要再引入一个
import spring-cloud-dependencies
父项目,所以采用这种写法。往常在引用依赖时写法就是:,
和
,这样写引入的都是jar包。而这里的写法产生的效果是:类似引入了一个pom清单(记录各种组件的版本),把
spring-cloud-dependencies
这个父项目的pom引入到当前pom。
作用:服务注册中心(重点) + 统一配置中心。
这里主要学习Nacos作为服务注册中心的使用和细节。
Nacos作为服务注册中心,和Consul一样,是一款软件。所以需要安装Nacos,安装包下载地址:
https://github.com/alibaba/nacos/releases,进入后点击Asserts
进行下载。
在bin目录中启动nacos:
sh startup.sh -m standalone
访问http://localhost:8848/nacos,查看可视化管理页面,登陆账号密码都是谁"nacos"
MacosBigSur版本nacos启动失败解决方法
@SpringBootApplication
@EnableDiscoveryClient // 可省略:原来是consul server,现在是nacos server
public class NacosClientApplication {
public static void main(String[] args) {
SpringApplication.run(NacosClientApplication.class, args);
}
}
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
dependencies>
server.port=8989
# 微服务服务名 唯一 全部大写
spring.application.name=NACOSCLIENT
# nacos server的总地址
spring.cloud.nacos.server-addr=localhost:8848
# nacos server服务注册地址(所以这行不配置也会默认使用已配置的nacos server的总地址)
#spring.cloud.nacos.discovery.server-addr=${spring.cloud.nacos.server-addr}
# 指定向nacos server注册的服务名(同上)
#spring.cloud.nacos.discovery.service=${spring.application.name}
启动入口类,登陆nacos管理页面,发现服务注册成功:
SpringCloud服务间通信回顾:
使用Rest通信的方式:
Nacos的服务间通信与之前的Eureka和Consul基本一致。
开发一个用户服务(Users)和一个商品服务(Products):
@SpringBootApplication
@EnableDiscoveryClient
实际测试中发现,不写nacos的server地址,服务同样可以被注册到服务注册中心,猜测是引入了nacos-client依赖后会默认将服务注册到localhost:8848。
商品服务
@RestController
public class ProductController {
private static final Logger log = LoggerFactory.getLogger(ProductController.class);
@Value("${server.port}")
private int port;
@GetMapping("/product")
public String product(Integer id) {
log.info("id = " + id);
return "调用商品服务,id = " + id + ",当前提供服务的端口为: " + port;
}
}
用户服务(大致框架)
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Value("${server.port}")
private int port;
@GetMapping("/invoke")
public String invokeProduct(Integer id) {
log.info("调用用户服务...");
return "调用用户服务成功!";
}
}
new RestTemplate().getForObject(url, String.class)
DiscoveryClient
LoadBalanceClient
@LoadBalance
BeansConfig.java
@Configuration
public class BeansConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
UserController.java
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Value("${server.port}")
private int port;
@Autowired
private DiscoveryClient discoveryClient; // 服务发现客户端
@Autowired
private LoadBalancerClient loadBalancerClient; // 负载均衡客户端
@Autowired
private RestTemplate restTemplate; // 这是具有负载均衡功能的RestTemplate
@GetMapping("/invoke")
public String invokeProduct(Integer id) {
log.info("调用商品服务...");
// 1. RestTemplate
// String result = new RestTemplate().getForObject("http://localhost:9090/product?id=123", String.class);
// 2. RestTemplate + Ribbon (Load Balance):Nacos client已经包含了这个依赖
// Ribbon: a.DiscoveryClient b.LoadBalanceClient c.@LoadBalance
// a.DiscoveryClient
// List instances = discoveryClient.getInstances("PRODUCTS");
// for (ServiceInstance instance : instances) {
// log.info("服务主机:{} 服务端口:{} 服务uri:{}", instance.getHost(), instance.getPort(), instance.getUri());
// }
// b. LoadBalanceClient
// ServiceInstance instance = loadBalancerClient.choose("PRODUCTS");// 已经进行负载均衡后的节点(轮询)
// String result = new RestTemplate().getForObject(instance.getUri() + "/product?id=21", String.class);
// c. @LoadBalanced注解,直接具有负载均衡
String result = this.restTemplate.getForObject("http://PRODUCTS/product?id=123", String.class);
log.info("商品服务调用结果:{}", result);
return "调用商品服务成功:[" + result + "]";
}
}
记录一个坑:
@LoadBalanced
注解使用后无法直接在uri中使用服务名找到对应集群机器的ip。报错:500, I/O error on GET request for “http://PRODUCTS/product”: Network is unreachable (connect failed); nested exception is java.net.SocketException: Network is unreachable (connect failed)
分析:在发请求时还是拿着"http://PRODUCTS/product",而不是把服务ID转化成ip地址,说明
@LoadBalanced
注解没有生效原因:Ribbon版本原因
链接:
- 在使用Eureka的时候,@LoadBalanced注解无效,无法通过服务名获取注册中心服务的URL的问题记录
- UnknownHostException: xxx restTemplate无法通过服务名调用服务/@LoadBalanced注解失效
- nacos作为注册中心,resttemplate使用负载均衡调用服务失败的问题
- 在使用Zookeeper以及Consul的时候,@LoadBalanced注解无效,无法通过服务名获取注册中心的URL的问题记录
我的解决:
为商品服务(需要进行通信的服务)的配置中加入
instance-id
:spring.cloud.discovery.client.simple.local.instance-id=PRODUCTS
上面就是就是使用RestTemplate
和RestTemplate
+Ribbon
进行服务间通信的回顾,我们发现虽然实现了负载均衡,在最后一种使用@LoadBalanced
注解的方式中也把路径中的ip改为了服务名,但依然存在路径写死的问题。
为用户服务引入openfeign依赖spring-cloud-starter-openfeign
为用户服务入口类加上注解@EnableFeignClients
在用户服务中创建接口/feignclients/ProductClient.java
@FeignClient("PRODUCTS")
public interface ProductClient {
@GetMapping("/product")
public String product(@RequestParam("id") Integer id);
}
为UserController注入ProductClient
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Value("${server.port}")
private int port;
@Autowired
private DiscoveryClient discoveryClient; // 服务发现客户端
@Autowired
private LoadBalancerClient loadBalancerClient; // 负载均衡客户端
@Autowired
private RestTemplate restTemplate; // 这是具有负载均衡功能的RestTemplate
@Autowired
private ProductClient productClient; // 注入feign客户端
@GetMapping("/invoke")
public String invokeProduct(Integer id) {
log.info("调用商品服务...");
// 1. RestTemplate
// String result = new RestTemplate().getForObject("http://localhost:9090/product?id=123", String.class);
// 2. RestTemplate + Ribbon (Load Balance):Nacos client已经包含了这个依赖
// Ribbon: a.DiscoveryClient b.LoadBalanceClient c.@LoadBalance
// a.DiscoveryClient
// List instances = discoveryClient.getInstances("PRODUCTS");
// for (ServiceInstance instance : instances) {
// log.info("服务主机:{} 服务端口:{} 服务uri:{}", instance.getHost(), instance.getPort(), instance.getUri());
// }
// b. LoadBalanceClient
// ServiceInstance instance = loadBalancerClient.choose("PRODUCTS");// 已经进行负载均衡后的节点(轮询)
// String result = new RestTemplate().getForObject(instance.getUri() + "/product?id=21", String.class);
// c. @LoadBalanced注解,直接具有负载均衡
// String result = this.restTemplate.getForObject("http://PRODUCTS/product?id=123", String.class);
// 3. OpenFeign
String result = productClient.product(999);
log.info("商品服务调用结果:{}", result);
return "调用商品服务成功:[" + result + "]";
}
测试发现可以用户服务可以成功调用商品服务,并具有负载均衡策略。
Nacos作为统一配置中心,管理配置文件的方式是在自己所在服务器上形成一个版本库(有版本控制),因此不需要再去创建远程配置仓库。
@SpringBootApplication
@EnableDiscoveryClient
public class NacosClientApplication {
public static void main(String[] args) {
SpringApplication.run(NacosClientApplication.class, args);
}
}
依赖
配置文件
server.port=8888
spring.application.name=CONFIGCLIENT
# nacos server的总地址
spring.cloud.nacos.server-addr=localhost:8848
配置文件中定义变量name
name=XiaoMing
@RestController
public class DemoController {
private static final Logger log = LoggerFactory.getLogger(DemoController.class);
@Value("${name}")
private String name;
@GetMapping("/demo")
public String demo() {
log.info("Demo OK!");
return "Demo OK! name: " + this.name;
}
}
注意Nacos中的配置文件都是独立的,没有config组件使用的公共配置文件,所以每个配置文件的文件名称必须写全。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C0kZd4RP-1650549322384)(https://cdn.jsdelivr.net/gh/zewei94yomi/ImageLoader@master/uPic/p7SeNZ.png)]
至此我们已经把原本client的配置都放到了nacos配置中心里,在client端可以删除application.properties
,但我们还需要为client配置关于配置中心的信息/配置,即告诉client如何去nacos配置中心获取自己的配置。
引入nacos-config依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
创建本地配置文件bootstrap.properties
(配置配置中心地址),不能命名为application.properties
# 方式一:
# 远程配置中心的地址
spring.cloud.nacos.config.server-addr=localhost:8848
# 去哪个组获取配置
spring.cloud.nacos.config.group=DEFAULT_GROUP
# 需要拉取的配置文件名
spring.cloud.nacos.config.name=configclient-prod
# 配置文件后缀
spring.cloud.nacos.config.file-extension=properties
# 方式二:
# 远程配置中心的地址
spring.cloud.nacos.server-addr=localhost:8848
# 去指定nacos地址读取配置
spring.cloud.nacos.config.server-addr=${spring.cloud.nacos.server-addr}
# 读取配置的分组
spring.cloud.nacos.config.group=DEFAULT_GROUP
# 指定读取文件后缀
spring.cloud.nacos.config.file-extension=properties
# 指定读取文件的前缀
spring.application.name=configclient
# 指定读取文件的具体环境
spring.profiles.active=prod
默认情况下nacos已经实现了自动配置刷新功能,如果需要刷新配置直接在控制器中加入@RefreshScope注解即可
为Controller加上注解@RefreshScope
后,远端配置修改后会自动刷新:
修改前name=XiaoMing
,我们在Nacos中修改为name=XiaoMing999
我们直接再次访问http://localhost:8888/demo,发现配置name
已经自动刷新
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qYzBHosH-1650549322386)(https://cdn.jsdelivr.net/gh/zewei94yomi/ImageLoader@master/uPic/rMMhOn.png)]
Nacos的配置中心的配置获取,主要通过 Namespace、group、Data ID能够定位到一个配置集。
在系统中,一个配置文件通常就是一个配置集,一个配置集可以包含了系统的各种配置信息,例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。每个配置集都可以定义一个有意义的名称,就是配置集的ID即Data ID。
配置集中包含的一个个配置内容就是配置项。它代表一个具体的可配置的参数与其值域,通常以 key=value 的形式存在。例如我们常配置系统的日志输出级别(logLevel=INFO|WARN|ERROR) 就是一个配置项。
配置分组是对配置集进行分组,通过一个有意义的字符串(如 Buy 或 Trade )来表示,不同的配置分组下可以有相同的配置集(Data ID)。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:可用于区分不同的项目或应用,例如:学生管理系统的配置集可以定义一个group为:STUDENT_GROUP。
注 :默认情况下使用 DEFAULT_GROUP 组。
命名空间(namespace)可用于进行不同环境的配置隔离。例如可以隔离开发环境、测试环境和生产环境,因为它们的配置可能各不相同,或者是隔离不同的用户,不同的开发人员使用同一个nacos管理各自的配置,可通过namespace隔离。不同的命名空间下,可以存在相同名称的配置分组(Group) 或 配置集。
Nacos抽象定义了Namespace、Group、Data ID的概念,具体这几个概念代表什么,取决于我们把它们看成什么,这里推荐给大家一种用法:
Namespace:代表不同环境,如开发、测试、生产环境。
Group:代表某项目,如XX医疗项目、XX电商项目
DataId:每个项目下往往有若干个工程,每个配置集(DataId)是一个工程的主配置文件
————————————————
版权声明:本文为CSDN博主「Jay-zhan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38978094/article/details/104172300
持久化:管理的配置信息的持久化
注意:Nacos默认存在配置信息的持久化,默认的持久化方式是使用内嵌数据库(Derby:不方便观察数据存储的基本情况),但是官方建议在生产情况下,将配置信息存入MySQL数据库。
数据源:在0.7版本之前,在单机模式时nacos使用嵌入式数据库(derby)实现数据的存储;0.7版本增加了支持mysql数据源能力(Nacos到目前只支持了MySQL数据库)。
关于为什么要做配置持久化:
具体步骤:
# 1.说明
- 在0.7版本之前,在单机模式时nacos使用嵌入式数据库(derby)实现数据的存储,不方便观察数据存储的基本情况。
0.7版本增加了支持mysql数据源能力,具体的操作步骤:
1.安装数据库,版本要求:5.6.5+
2.初始化mysql数据库,数据库初始化文件:nacos-mysql.sql
3.修改conf/application.properties文件,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码。
# 2.安装mysql数据库5.6.5+以上版本(略)
- 添加官方的yum源创建并编辑mysql-community.repo文件
vi /etc/yum.repos.d/mysql-community.repo
- 粘贴以下内容到源文件中
[mysql57-community]
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/RPM-GPG-KEY-mysql
- 安装mysql
yum install mysql-community-server -y
- 启动mysql数据库
systemctl start mysqld
- 修改mysql数据库密码
grep 'temporary password' /var/log/mysqld.log
mysqladmin -u root -p password 回车 输入原始密码 在输入新的密码
- 登录mysql
mysql -uroot -p'Root!Q2w'
- 修改远程连接
grant all privileges on *.* to 'root'@'%' identified by 'Root!Q2w' with grant option;
flush privileges;
# 3.修改配置文件
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?
characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=Root!Q2w
因为集群搭建太麻烦,这里略过,直接复制笔记。
# 集群 cluster : 统一种软件服务的多个节点对一个系统提供服务称之为这个软件服务集群 tomcat集群 mysql集群 redis集群 es集群...
集群解决问题:
1.并发访问压力
2.单节点故障问题
# 1.nacos集群架构图
- https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html
# 2.集群搭建注意事项
- 注意:
a.3个或3个以上Nacos节点才能构成集群。
b.要求虚拟机内存分配必须大于2G以上
# 3.集群规划
- node cluster:
10.15.0.3 8845 nacos01
10.15.0.3 8846 nacos02
10.15.0.3 8847 nacos03
10.15.0.3 9090 nginx
10.15.0.3 3306 mysql
# 4.搭建nacos集群
1).将nacos安装包从新解压缩
2).开启nacos mysql持久化
注意:数据库中不能存在原始数据
3).修改nacos conf目录中cluster.conf文件添加所有集群节点
10.15.0.3:8845
10.15.0.3:8846
10.15.0.3:8847
4).将修改后nacos复制三份
注意:修改为不同端口信息
5).分别启动三台机器
./startup.sh
# 5.安装Nginx
- 0.安装必要依赖
yum install -y gcc pcre-devel zlib-devel
- 1.下载Nginx
http://nginx.org/en/download.html
- 2.将Nginx上传到linux中,并解压缩
tar -zxvf nginx-1.11.1.tar.gz
- 3.查看Nginx安装目录
[root@localhost nginx-1.11.1]# ls
auto CHANGES CHANGES.ru conf configure contrib html LICENSE man README src
- 4.在Nginx安装目录中执行如下命令:(指定安装位置)
./configure --prefix=/usr/nginx
- 5.执行上述命令后,执行如下命令:
make && make install
# 6.配置nginx conf配置文件
a.加入如下配置:
upstream nacos-servers {
server 10.15.0.3:8845;
server 10.15.0.3:8846;
server 10.15.0.3:8847;
}
b.修改
location / {
proxy_pass http://nacos-servers/;
}
# 7.启动nginx进行测试即可
作用:Alibaba开源的组件,用来对现有的微服务系统进行保护(替换Hystrix)
解决服务雪崩==》服务熔断
Sentinel takes “flow” as the breakthrough point, and works on multiple fields including flow control, circuit breaking and load protection to protect service reliability.
Sentinel以“流量”为突破口,在流量控制、断路、负载保护等多个领域进行工作,保障服务可靠性。
特性:
资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,我们都会用"资源"来描述代码块。
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
流量控制有以下几个角度:
资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
运行指标,例如 QPS、线程池、系统负载等;
控制的效果,例如直接限流、冷启动、排队等。
Sentinel的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过线程池的方式,来对依赖(在我们的概念中对应”资源“)进行了隔离。
优点:
- 资源和资源之间做到了最彻底的隔离
缺点:
增加了线程切换的成本(thread context switching)
需要预先给各个资源做线程池大小的分配
Sentinel 对这个问题采取了几种手段:
- 通过并发线程数进行限制
和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。- 通过响应时间对资源进行降级
除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。- 系统负载保护
Sentinel 同时从系统的维度提供保护,防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
“3.2 资源-概念&原理”摘自https://zhuanlan.zhihu.com/p/64786381,更多sentinel的原理与源码解读请查看此链接。
Query Per Second:系统每秒的请求数,如5/s,代表平均每秒5个请求
Response Time:响应时间
Sentinel包含两个开源组件:
两者必须一起使用,Sentinel做流量控制,Dashboard做规则配置,这样的坏处是两者必须耦合,好处是规则配置无需硬编码,只需在dashboard上修改配置,规则就会立即生效而无需重启服务和组件。
下载Sentinel Dashboard,github:https://github.com/alibaba/Sentinel/tags,下载dashboard的jar,sentinel-dashboard-1.7.2.jar
运行sentinel-dashboard-1.7.2.jar:
java -jar -Dserver.port=9191 sentinel-dashboard-1.7.2.jar
springboot的默认启动端口为8080,如果8080被占用那么这个java程序就无法启动成功,所以最好指定一个端口。
如果在服务器上运行,服务器必须安装JDK8.0+,并配置环境变量
访问dashboard管理界面:http://localhost:9191/
账号密码都是sentinel
启动Nacos
开发创建一个demo的Springboot应用
引入依赖
配置文件
# Application
server.port=8998
spring.application.name=SENTINEL
# Nacos server
spring.cloud.nacos.server-addr=localhost:8848
暴露接口
@RestController
public class DemoController {
private static final Logger log = LoggerFactory.getLogger(DemoController.class);
@GetMapping("/demo")
public String demo() {
log.info("demo ok!!!");
return "demo ok!!!";
}
}
测试,访问http://localhost:8998/demo,成功
在微服务中引入sentinel组件
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
配置配置文件,使sentinel可以连接到dashboard
# Sentinel Dashboard
# 开启sentinel保护,默认开启
spring.cloud.sentinel.enabled=true
# 指定sentinel dashboard的web地址(连接dashboard),这里使用的是HTTP的通信方式(效率低)
spring.cloud.sentinel.transport.dashboard=localhost:9191
# 指定sentinel组件与sentinel dashboard组件的通信地址,这里使用的是TCP的通信方式(效率高),默认8719
spring.cloud.sentinel.transport.port=8719
至此,微服务启动后就可以被sentinel保护。注意,服务必须被调用后才能在dashboard中查看到。
这里重点介绍记录前三种规则,文档链接:https://github.com/alibaba/Sentinel/wiki
https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
流量控制(flow control)
其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
QPS:
并发线程数:
使用QPS作为流控规则非常容易测试,只需要频繁刷新浏览器来发送请求即可,而设置并发线程数则测试比较困难,需要借助工具JMeter。
下载:https://jmeter.apache.org/
Linux/Mac,直接打开/bin/jmeter
在Test Plan下创建一个Thread Group
配置线程组
配置HTTP请求
添加监听器(HTTP请求的结果树)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IyqWbqxB-1650549322393)(https://cdn.jsdelivr.net/gh/zewei94yomi/ImageLoader@master/uPic/vRR6zg.png)]
测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGztRYXt-1650549322394)(https://cdn.jsdelivr.net/gh/zewei94yomi/ImageLoader@master/uPic/gZhwVR.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jh7sg0NY-1650549322394)(https://cdn.jsdelivr.net/gh/zewei94yomi/ImageLoader@master/uPic/xPZysj.png)]
直接:当配置资源在运行过程中超过当前配置规则的阀值之后,它做的处理是什么
关联模式:当配置资源在运行过程中超过当前配置规则的阀值之后,它所关联的资源做什么处理。注意关联的顺序,是设置的“关联资源”影响“当前资源”。被关联的资源无论怎样接受请求,都不会被影响。比如下载面的例子中,对/demo无论发送多少请求都不会被流控,而当对/login的qps超过2时,/demo会被sentinel进行流控。
链路模式(存在问题):当配置资源在运行过程中超过当前配置规则的阀值之后,它链路中的资源做什么处理。链路模式类似关联模式,只是它的关联顺序是正向关联。
注意:这里超过阀值,指的是“超过阀值后的请求”,做什么处理
注意:流控效果只适用于QPS限流
快速失败:当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException
。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
Warm Up:即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tQo2CBlc-1650549322395)(https://cdn.jsdelivr.net/gh/zewei94yomi/ImageLoader@master/uPic/f9BeCf.jpg)]
排队等待:始终匀速通过,严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式。这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7
熔断降级(Degrade Service)
熔断:为了避免微服务架构中的服务雪崩现象
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用自动进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException
)。
慢调用比例 (SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
异常比例 (ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值 ,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0]
,代表 0% - 100%。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIHQKCdV-1650549322396)(https://cdn.jsdelivr.net/gh/zewei94yomi/ImageLoader@master/uPic/image-20200802143052470.png)]
异常数 (ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhAAWzOC-1650549322397)(https://cdn.jsdelivr.net/gh/zewei94yomi/ImageLoader@master/uPic/image-20200802143303114.png)]
@GetMapping("/sentinel/test1")
@SentinelResource(value = "aa",blockHandler = "fallBack",fallback = "fall")
public String test1(int id){
log.info("sentinel test1");
if(id<0)
throw new RuntimeException("非法参数!!!");
}
return "sentinel test1 :"+id;
}
//降级异常处理
public String fallBack(int id,BlockException e){
if(e instanceof FlowException){
return "当前服务已被流控! "+e.getClass().getCanonicalName();
}
return "当前服务已被降级处理! "+e.getClass().getCanonicalName();
}
//异常处理
public String fall(int id){
return "当前服务已不可用!";
}
https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
热点参数(Param Flow)
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。(控制颗粒度更小)
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
https://github.com/alibaba/Sentinel/wiki/%E9%BB%91%E7%99%BD%E5%90%8D%E5%8D%95%E6%8E%A7%E5%88%B6
也叫黑白名单控制
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。(类似电脑的防火墙)
参考:
- Nacos服务—配置中心
- 阿里Sentinel原理解析