本系列笔记涉及到的代码在GitHub上,地址:https://github.com/zsllsz/cloud
本文涉及知识点:
-
springCloud Alibaba介绍;
-
nacos做注册中心;
-
nacos做配置中心;
-
sentinel流控;
一、springCloud Alibaba简介
1、为什么会诞生springCloud Alibaba?
通过springCloud初级篇和中级篇的学习,我们知道springCloud很多技术现在都停更了,Netflix也说明了不再维护了。一开始阿里搞出了一个Dubbo,但是后面停更了几年,spring就联合Netflix搞出了一个springCloud生态,Netflix主要技术有5个,eureka、ribbon、feign、zuul和config。但是呢Netflix内部神仙打架,意见不一,然后导致这些技术大部分停更了。这时,阿里又杀出来了,搞出了一套springCloud Alibaba,spring官方看它还不错,阿里也想推广自己,所以在2018年四月份spring又把springCloud Alibaba给收编了。所以对于我们使用者而言,无非就是从 springCloud Netflix 换成 springCloud Alibaba而已。
2、springCloud Alibaba能干嘛?
- 服务的注册与发现
- 服务的降级与限流
- 分布式配置管理
- 消息驱动能力
- 分布式任务调度
- 阿里云对象存储
- 阿里云短信服务
可以发现,springCloud能干的它几乎都能干,还多了一些springCloud不能干的。springCloud Alibaba相关网址如下,可以查阅相关文档:
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
一般我们要学怎么使用查第二个文档就好了。
二、springCloud Alibaba Nacos 代替eureka做服务注册中心
之前springCloud的服务注册可以用eureka、consul和zookeeper,配置中心用config和bus,现在springCloud Alibaba用Nacos搞定这些,简言之就是注册中心加配置中心。
1、下载安装:
- 入口网站:https://nacos.io/zh-cn/index.html
- 下载地址(本次下载1.1.4的linux版):https://github.com/alibaba/nacos/tags
- 安装运行:前提要有maven和jdk1.8及以上的环境。下载完解压,然后直接进入bin目录下执行以下命令启动单机版nacos:
bash startup.sh -m standalone
-
测试:访问 ip:8848/nacos,看到如下画面就启动成功了,默认账号密码都是nacos。
nacos
2、基于nacos的服务提供者:
- 新建名为cloudalibaba-provider-payment9001的module
- 父pom.xml中添加springCloud Alibaba依赖(一开始就添加过了)
- pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator </artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- application.yml:
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: 192.168.0.106:8848
# 端点启动和暴露(这时actuator的功能,学springcloud config时也配置过)
management:
endpoints:
web:
exposure:
include:
- "*"
- 主启动类:
@SpringBootApplication
@EnableDiscoveryClient
public class NacosMainPayment9001 {
public static void main(String[] args) throws Exception {
SpringApplication.run(NacosMainPayment9001.class, args);
}
}
- 业务类:
@RestController
@RequestMapping("/provider")
public class PaymentController {
@GetMapping("/payment/{id}")
public String payment(@PathVariable("id") Integer id) {
return "success" + id;
}
}
-
最后启动该项目,并访问测试一下,然后看看nacos控制台。
服务成功注册进nacos -
为了等下演示nacos的负载均衡,新建9002,内容几乎和9001一样。
3、基于nacos的服务消费者:
- 新建名为cloudalibaba-consumer-nacos-order80的module
- pom.xml:和9001的一样
- application.yml:
server:
port: 80
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: 192.168.0.106:8848
# 消费者将要去访问的微服务名称
service-url:
nacos-user-service: http://nacos-payment-provider
- 主启动类:和9001的一样,也是那两个注解
- 配置RestTemplate:
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
- controller:
@RestController
@RequestMapping("/consumer")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Value("${service-url.nacos-user-service}")
private String serverUrl;
@GetMapping("/order/{id}")
public String getPayment(@PathVariable("id") Integer id) {
return restTemplate.getForObject(serverUrl + "/provider/payment/" + id, String.class);
}
}
访问这个controller,就会发现一次是调用9001,一次是调用9002。因为nacos也集成了ribbon,所以自带负载均衡。
4、nacos的CAP模型:
nacos支持CP和AP,可以自由切换。这样一来,nacos可以代替所有的注册中心。如果要代替eureka,用AP,如果要代替consul或者zookeeper,用CP。
三、springCloud Alibaba Nacos 代替config做服务配置中心
1、新建名为cloudalibaba-config-nacos-client3377的module:
- pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator </artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- nacos -->
<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>
- yml:和config一样,要先搞一个bootstrap.yml从配置中心拉取配置,其他自己专有的再用application.yml。先有共性再有个性。
bootstrap.yml:
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: 192.168.0.106:8848
config:
server-addr: 192.168.0.106:8848
file-extension: yaml #指定yaml格式的配置,就是对应以前用config时GitHub上的配置文件
application.yml:
spring:
profiles:
active:
- dev #激活开发环境
- 主启动类:
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConfigMain3377 {
public static void main(String[] args) throws Exception {
SpringApplication.run(NacosConfigMain3377.class, args);
}
}
- controller:用@Value注解尝试读取nacos中的配置文件
@RestController
@RequestMapping("/config")
@RefreshScope // 动态刷新
public class NacosConfigController {
@Value("${config.info}")
private String info;
@GetMapping("/info")
public String info() {
return info;
}
}
至此工程新建完毕,接下来就要去nacos中新建配置文件。
2、在nacos上新建配置文件:
- 先看一下nacos配置管理的界面:
可以看到有一个Data Id,一个Group。
Data Id的格式:
${prefix}-${spring.profile.active}.${file-extension}
-
prefix
默认为spring.application.name
的值,也可以通过配置项spring.cloud.nacos.config.prefix
来配置。 -
spring.profile.active
即为当前环境对应的 profile,注意:当spring.profile.active
为空时,对应的连接符-
也将不存在,dataId 的拼接格式变成${prefix}.${file-extension}
。在使用的时候,还是不要让它为空,免得出现奇奇怪怪的问题。 -
file-exetension
为配置内容的数据格式,可以通过配置项spring.cloud.nacos.config.file-extension
来配置。目前只支持properties
和yaml
类型。 - 通过 Spring Cloud 原生注解 @RefreshScope 实现配置自动更新,这就是刚才controller中加这个注解的原因。
按照上面的格式,那3377这个项目的data Id就是:nacos-config-client-dev.yaml
然后在nacos上新建如下配置:
这个配置一定要注意每个冒号后面要加一个空格!
- 然后访问3377的controller,就可以成功获取到配置了:
- 动态刷新:现在修改nacos上的配置文件,然后再次访问3377的controller,会发现可以获取到最新的内容。(如果没有获取到最新的,那就是浏览器缓存的原因)
3、nacos配置管理的命名空间、data id和group的关系:
nacos- 命名空间:有一个默认的public,相当于对group进行分组管理
- 分组group:有一个默认的DEFAULT_GROUP,对data Id进行分组管理
- data Id:理解为配置文件的id
所以三者关系就是:namespace + group + data Id
可以确认一个唯一的配置文件。
这样设计的好处就是可以方便地区分不同的环境。比如现在有开发、测试和生产三个环境,那我们就新建三个不同的namespace。
使用默认命名空间默认分组读取不同data id的配置文件:
- 在nacos上新建nacos-config-client-test.yaml,当作测试环境的配置文件,目前都是public命名空间默认分组下;
- 将3377的application.yml中的dev改成test,就可以读测试环境配置文件了;
使用默认命名空间不同分组读取配置配置文件:
- 在nacos上新建两个配置文件,名字都叫nacos-config-client-info.yaml,分组分别为DEV_GROUP和TEST_GROUP;
- 然后bootstrap.yml和application.yml分别改成这样:
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: 192.168.0.106:8848
config:
server-addr: 192.168.0.106:8848
file-extension: yaml #指定yaml格式的配置,就是对应以前用config时GitHub上的配置文件
group: DEV_GROUP
spring:
profiles:
active:
#- dev #激活开发环境
- info
这样就搞定了,这样读取的就是dev_group分组下的info配置文件。
使用不同的命名空间读取不同的配置文件:
- 新建DEV和TEST命名空间;
bootstrap.yml再加一行配置,如下:
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: 192.168.0.106:8848
config:
server-addr: 192.168.0.106:8848
file-extension: yaml #指定yaml格式的配置,就是对应以前用config时GitHub上的配置文件
namespace: 4d4b4c98-2746-4ff7-8ce9-eb2837db456c
group: DEV_GROUP
这样就可以指定命名空间指定分组读取指定后缀文件了。
四、nacos集群和持久化
官网的集群架构图:VIP的意思是虚拟IP,我们可以认为vip就是nginx的集群。
nacos集群架构图nacos默认自带了一个数据库(derby)用来做持久化,当nacos集群的时候,每一台服务器上的nacos都以自己自带的数据库来搞持久化,会存在数据一致性的问题。所以干脆都别用自带的数据库了,都用MySQL。
1、用MySQL做持久化:
- 在nacos/conf目录下有一个nacos-mysql.sql的脚本,在MySQL中新建一个名为nacos_config的数据库,在此数据库中执行这个脚本即可;
- 在nacos/conf目录下有一个application.properties文件,打开修改它将官网的这段配置粘贴到其中,并将MySQL连接地址改成自己的就可以了:
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://192.168.0.106:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=你的MySQL密码
- 修改完重启nacos,重启之后立即访问可能会访问不了,毕竟人家启动也要时间嘛。启动完登陆进去,发现之前的配置都没了。然后自己新增配置,再去数据库查看,发现nacos_config数据库的config_info表中就有数据了,说明切换到MySQL成功了。
2、nacos集群:
需要的环境:
- 2个nginx(此处为了简单就只启动了一个nginx,想了解nginx集群可以参考我nginx相关文章)
- 3个nacos
- 1个mysql
nginx集群和mysql已经安装好了,nacos集群通过如下步骤搞定:
- nacos的application.properties中加上MySQL配置(之前已经加过);
- 拷贝cluster.conf.example改名为cluster.conf,然后修改其内容:
192.168.2.43:8848
192.168.2.43:8849
192.168.2.43:8850
由于这里是在一台虚拟机上启动3份nacos,所以后面要加上端口。如果有3台不同的机器,那么这里直接写那3台机器的ip就好了。并且要注意,这个ip不能填写127.0.0.1,执行hostname -i
,填写ens33后面跟着的那个IP。
- 修改nacos的startup.sh,使其能够指定端口启动:
修改有两处,第一处,增加端口启动,如下图,左边是修改前,右边是修改后:
第二次启动,启动日志增加端口打印,如下图,上面是修改前,下面是修改后:
增加端口打印这样就改完了。
- 修改nginx的conf配置文件:
upstream cluster {
server 192.168.2.43:8848;
server 192.168.2.43:8849;
server 192.168.2.43:8850;
}
server {
listen 80;
server_name 192.168.2.43;
location / {
proxy_pass http://cluster;
}
这样所有配置都整完了,接下来依次启动nginx和nacos.
- 启动nginx;
- 启动nacos集群:
./startup.sh -p 8848
./startup.sh -p 8849
./startup.sh -p 8850
启动成功后,访问:192.168.2.43/nacos
,登录后可以看到如下效果:
可能遇到的坑:
- 异常1:
unable to find local peer: 192.168.2.43:0, all peers: [192.168.2.43:8850, 192.168.2.43:8849, 192.168.2.43:8848]
解决办法:在nacos/config/application.properties
中加上如下配置:
nacos.inetutils.ip-address=192.168.2.43
- 异常2:一直启动中,nacos is starting……,可能是虚拟机内存不够,将启动命令中为jvm分配的内存设置小一点即可,如下:
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
五、springCloud Alibaba Sentinel介绍和基本使用
1、sentinel介绍:
之前我们用hystrix豪猪哥来实现服务降级熔断,sentinel也是干这事的,并且更加强大和好用。
- 官网:
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
sentinel能够解决的问题:
- 服务雪崩
- 服务降级
- 服务熔断
- 服务限流
2、优点:
- 丰富的应用场景:秒杀、消息削峰填谷,集群流量控制,实时熔断等
- 完备的实时监控:精确到秒的实时监控
- 广泛的开原生态:可以快速的与springCloud、dubbo等框架整合
- 完善的SPI扩展点:提供了完善的SPI扩展接口,可以通过实现扩展接口来定制逻辑
3、下载安装:
-
下载地址:
https://github.com/alibaba/Sentinel/releases
,选择对应版本点击进入,下载dashboard即可,比如我下载的是sentinel-dashboard-1.7.0.jar
-
使用文档:
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel
-
sentinel运行:控制台由两部分构成,前台和后台。前台(即web界面)的端口是8080,所以8080端口不能被占用,并且要jdk8及以上。下载完成后直接
java -jar
运行jar包即可。然后访问8080,出现如下界面即启动成功,默认用户名和密码都是sentinel。
相比豪猪哥,简单了很多,豪猪哥需要我们引入依赖后自己启动一个微服务当成dashboard,而sentinel不需要自己写,直接运行jar包就可以了。
4、初用sentinel:
- 启动8848的nacos(为了简单,就启动了单机版而不是集群版)
- 新建名为cloudalibaba-sentinel-service8401的module:
pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator </artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 做持久化的时候会用到的 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
application.yml:
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: 192.168.0.106:8848
sentinel:
transport:
dashboard: 192.168.0.106:8080
port: 8719 # 默认8719,如果被占用会依次加1,直至找到没有被占用的端口
# actuator图形化配置
management:
endpoints:
web:
exposure:
include:
- "*"
主启动类:
@SpringBootApplication
@EnableDiscoveryClient
public class SentinalMain8401 {
public static void main(String[] args) throws Exception {
SpringApplication.run(SentinalMain8401.class, args);
}
}
controller:
@RestController
@RequestMapping("/sentinel")
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "=========== testA ==========";
}
@GetMapping("/testB")
public String testB() {
return "=========== testB ==========";
}
}
- 启动8080的sentinel
- 启动8401的微服务
- 访问8401微服务,然后看sentinel控制台是否有相关信息
8080控制台并没有任何8401的相关信息,不要方,这是因为sentinel使用懒加载机制,没调用的不会出现在dashboard中。访问一下8401,然后再看dashboard中就有了。
sentinel正在监控8401然后访问几次8401,实时监控中就会有相关信息了,如下图:
testA的调用信息可能遇到的坑:不管怎么访问8401,实时监控中都没有调用信息,可能的原因有:
- 如果你的sentinel运行在虚拟机上,8401运行在自己电脑上,那么可能原因是虚拟机时间和本机时间不一致导致。解决办法就是更新虚拟机时间,命令如下:
yum -y install ntp ntpdate
ntpdate cn.pool.ntp.org
- 如果sentinel运行在虚拟机上,8401在本机,访问8401后,sentinel没有监控到,并且sentinel运行日志报了如下的错误:
java.util.concurrent.ExecutionException: java.net.NoRouteToHostException: 没有到主机的路由
解决办法就是在8401的application.yml中加上如下配置:
sentinel:
transport:
dashboard: 192.168.0.106:8080
client-ip: 192.168.0.104 # 这一行是新加的
port: 8719 # 默认8719,如果被占用会依次加1,直至找到没有被占用的端口
就是加上client-ip,值就是8401项目,即你要sentinel监控的项目所运行的机器的IP。
六、sentinel流控规则
1、基本介绍:
sentinel流控规则在sentinel的dashboard中有一个流控规则,如上图,下面对这些名词进行解释。
- 资源名:唯一名称,默认请求路径
- 针对来源:sentinel可以针对调用者进行限流,填写微服务名称,默认default,表示不区分来源
- 阈值类型:QPS(每秒钟的请求数):当调用该API的QPS达到阈值时,进行限流;线程数:当调用该API的线程数达到阈值时进行限流
- 是否集群:不需要集群
- 流控模式:直接:达到限流条件时直接限流;关联:关联的资源达到阈值时限流自己(当与A关联的资源B达到阈值时,就限流A自己。应用场景:支付服务达到阈值的时候,就限流下订单的服务);链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流,API级别的针对来源)
- 流控效果:快速失败:直接失败,抛异常;warm up:根据codeFactor(冷加载因子,默认是3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
- 排队等待:匀速排队,让请求以匀速通过,阈值类型必须设置为QPS,否则无效
2、流控模式:
流控模式之直接:
QPS直接快速失败:
- 添加流控规则:在簇点链路那里添加流控即可,比如我添加的如下:
这里设置的意思就是,访问testB,一秒钟超过一次,就是直接快速报错。我现在对testB连点两下,就会返回如下信息:
sentinel对testB进行流控- 存在的问题:超过QPS阈值的时候,返回的是默认信息
Blocked by Sentinel (flow limiting)
,如何自定义呢?应该有hystrix一样的兜底方法(后续再说其用法)。
线程数直接失败:
sentinel线程数直接失败我们再访问testB,发现不管点多快,都没有被流控给拦住。因为我们在一个浏览器中访问,始终是一个线程。这样配置的意思就是并发线程数超过阈值1时,就会返回失败信息。可以用jmeter模拟并发访问的情况。
流控模式之关联:
配置如下:
sentinel流控之关联这里的意思就是testA的QPS数超过1,就会导致testB不能用。测试方法:用jmeter对testA进行并发访问,然后我们在浏览器访问testB。就会发现也会返回Blocked by Sentinel (flow limiting)
。
流控模式之链路:
多个请求调用了同一个微服务超过了QPS阈值就会快速失败,配置如下:
注意入口资源的名字就是sentinel簇点链路中显示的名字。然后快速访问A,只要QPS超过1,就会返回Blocked by Sentinel (flow limiting)
。
3、流控效果:
上面用的流控效果都是快速失败,现在来认识一下这些流控效果。
- 快速失败:上面的案例都是直接失败,就是超过阈值就返回
Blocked by Sentinel (flow limiting)
- 预热( Warm Up):请求的QPS从
阈值 / 冷加载因子(默认是3)
开始,经过预热时长
,最后达到阈值。比如下图的配置意思是:初始阈值为10 / 3 = 3
,经过10秒钟的时间,阈值慢慢升到10。现在快速访问testA,因为一开始QPS阈值为3,所以你点快一点可能就失败了,但是慢慢地,你点很快都不会失败了,因为最后阈值升为10,正常情况下没有人手速能达到1秒钟点11次吧。
- 排队等待:就是不管同时有多少个请求过来,我每秒钟只处理阈值数的请求,其他老老实实排队等待去。如下图配置意思就是每秒钟只处理一个,其他的排队等着,每隔1000毫秒才放下一个请求进来。
网友评论