为什么会出现?
SpringCloud Netflix进入维护模式,意味着不再向模块添加新的功能和组件
能干嘛?
服务降级限流、服务注册与发现、分布式配置管理、消息驱动能力、阿里云对象存储、分布式任务调度
介绍
Nacos是注册中心+配置中心的组合,即Nacos = Eurake + Config + Bus
下载
https://github.com/alibaba/nacos/releases/download/1.1.4/nacos-server-1.1.4.zip
解压,进入bin目录,双击sartup.cmd启动!访问 http://localhost:8848/nacos:
新建module:cloudalibaba-provider-payment9001
POM
父POM
子POM
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
YML
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
主启动
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9001.class,args);
}
}
业务类
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/nacos/{id}")
public String getPayment(@PathVariable("id") Integer id){
return "nacos registry, serverPort: "+serverPort+"\t id: " + id;
}
}
新建9002:效果如下:
新建Module:cloudalibaba-consumer-nacos-order83
POM
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
YML
server:
port: 83
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
#消费者将要去访问的微服务名称(注册成功进nacos,的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain9002 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9002.class,args);
}
}
业务类
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplete(){
return new RestTemplate();
}
}
@RestController@Slf4jpublic class OrderNacosController {
@Resource private RestTemplate restTemplate; @Value("${service-url.nacos-user-service}") private String serverURL; @GetMapping(value = "/consumer/payment/nacos/{id}") public String paymentInfo(@PathVariable("id") Long id){
return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class); }}
测试
http://localhost:83/consumer/payment/nacos/13 实现了负载均衡
Nacos支持AP和CP的切换:
新建module:cloudalibaba-config-nacos-client3377
POM
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
YML
Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application。
bootstrap
#naco配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yamL格式的配置
# $fspring.application.name}-${spring.profile.active]}.${spring.cLoud.nacos,config.file-extension}
application
spring:
profiles:
active: dev #表示开发环境
主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class NacosConfigClientMain3377 {
public static void main(String[] args) {
SpringApplication.run(NacosConfigClientMain3377.class,args);
}
}
业务类
@RestController
@RefreshScope //支持nacos动态刷新
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo(){
return configInfo;
}
}
在NACOS中添加配置信息
配置规则:Nacos中的dataid的组成格式及与SpringBoot配置文件中的匹配规则
自带动态刷新
namespace、group、dataid三者关系
默认情况:
Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT
Nacos默认的命名空间是public,Namespace主要用来实现隔离。
比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。
Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去
Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。比方说为了容灾,Service微服务分别部署在了杭州机房和广州机房,这时就可以给杭州机房的Service微服务起一个集群名称(HZ),给广州机房的Service微服务起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。
最后是Instance,就是微服务的实例。
DataId配置方案
指定spring.profile.active和配置文件的DatalD来使不同环境下读取不同的配置
默认空间+默认分组+新建dev和test两个dataid,通过spring.profile.active属性就能进行多环境下配置文件的读取,测试:
group分组方案
namespace空间方案
集群架构说明
持久化切换配置
Nacos默认自带的是嵌入式数据库derby
derby到mysql切换配置步骤
nacos的conf目录下找到sql脚本:nacos-mysql.sql,在数据库中执行
nacos的conf目录下找到application.properties:在最后添加以下内容
spring.datasource.platform=mysql
db.num=1
db.url.O=jdbc:mysql://127.0.0.1:3306/nacos_config?
characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=admin
最后进入nacos测试,发布配置文件,发现在数据库中有:
在Linux上Mysql数据库配置,跟在windows上一致,需要新建nacos_config库并生成表
application.properties配置,跟windows上一致
Linux服务器上nacos集群配置cluster.conf
编辑nacos的启动脚本startup.sh,使他能够接受不同的启动端口:
测试:启动3333、4444、5555,启动nginx,测试网址:http://192.168.200.130:1111/nacos/#/login,登录密码均为nacos!
(一定要注意如果访问失败很有可能是端口号没有打开!!!)
新建一条配置测试:
微服务cloudalibaba-provider-payment9002注册进nacos集群:
修改配置文件:
server:
port: 9002
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
#server-addr: localhost:8848 #配置Nacos地址
#换成nginx的1111端口,做集群
server-addr: 192.168.200.130:1111
management:
endpoints:
web:
exposure:
include: '*'
是什么
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel以流量为切入点,从流量控
制、熔断降级、系统负载保护等多个维度保护服务的稳定性。一句话,优化版的Hystrix。
特征
下载地址:https://github.com/alibaba/Sentinel/releases/download/1.7.0/sentinel-dashboard-1.7.0.jar
sentinel分为两部分:
安装:
新建一个Module:cloudalibaba-sentinel-service8401
POM
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-sentinelartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
YML
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719端口+1,直至找到未被占用的端口
port: 8719
management:
endpoints:
web:
exposure:
include: '*'
主启动
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class,args);
}
}
业务类FlowLimitController
@RestController
public class FlowLimitController {
@GetMapping("testA")
public String testA(){
return "---------testA";
}
@GetMapping("testB")
public String testB(){
return "---------testB";
}
}
启动Sentinel和微服务8401,查看控制台:
Sentinel执行的是懒加载机制,执行一次访问即可!http://localhost:8401/testA或http://localhost:8401/testB:
基本介绍
资源名∶唯一名称,默认请求路径
针对来源: Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
阈值类型/单机阈值:
是否集群:不需要集群
流控模式:
流控效果
QPS直接失败
线程数直接失败
QPS:如果超过阈值,其余的请求直接拒绝,御敌于国门之外
线程数直接失败:如果超过阈值,其余的请求无法访问,报错,但是并没有拒绝,关门打狗!
简介
当关联的资源达到阈值时,就限流自己,即当与A关联的资源B达到阀值后,就限流A自己,B惹事,A挂了
Postman模拟并发访问testB:
此时A又挂了:
介绍
多个请求调用了同一个微服务
介绍
快速失败:默认的流控效果,直接提示失败消息!
公式:阈值除以coldFactory(默认值为3),经过预热时长以后才会达到阈值!
默认coldFactory为3,即请求QPS从threshod/3开始,经预热时长逐渐升高设定的QPS阈值
应用:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放迸来,慢慢的把阀值增长到设置的阀值。
介绍
匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。(漏桶算法)
简介
异常数:当资源近1分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若tiiewindow小于60s,则结束熔断状态后仍可能再进入熔断状态。
介绍
配置实现
Controller
@RestController
public class FlowLimitController {
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey") //随便叫啥都可以,只是为了编码的统一和规范和上面一直
public String testhotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p1",required = false) String p2){
return "------testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception){
return "兜底┭┮﹏┭┮"; //sentinel默认提示为Blocked by sentinel (flow Limiting)
}
}
新增热点限流规则:方法testHotkey中的第一个参数只要QPS超过每秒1次,马上降级处理
测试:
如果,没有使用兜底方法,即去掉了blockHandler = “deal_testHotKey”,当QPS超过阈值,直接调到ERROR PAGE,对用户不友好!
参数例外项
在上述的情况下,当p1参数在1s内的QOS超过某个阈值的时候,p1马上就会被限流,现在我们期望,当p1是某个特殊值的时候,它的限流值和平时不太一样,例如当p1值为5的时候,它的阈值可以达到200,这就要用到参数例外项了。
配置:当p1为5的时候,限流阈值为200,当不为5的时候,限流阈值为1!
当方法中出现异常的时候,无论是否违背热点规则,都无法进行兜底了。
@SentinelResource处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理,但运行时异常他不管,即它只管配置出错,运行出错该走异常走异常!
介绍
系统保护规则是从应用级别的入口流量进行控制,从单台机器的load、CPU使用率、平均RT、入口
QPS和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳
定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流星指的是进入
应用的流量((EntryType.IN ),比如Web服务或Dubbo服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
配置:
testA、testB每秒QPS大于1的时候,都会直接失败,对整个系统的所有服务都是这样!
按资源名称限流+后续处理
修改8401:
POM:
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
业务类:
@RestController
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public CommonResult byResource(){
return new CommonResult(200,"按资源名称限流测试ok",new Payment(2020L,"serial001"));
}
public CommonResult handleException(BlockException exception){
return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
}
}
配置
问题:关闭8401,sentinel控制台,流控规则消失了!
按url地址限流+后续处理
问题:
创建CustomerBlockHandler类用于自定义限流处理逻辑
public class CustomerBlockHandler {
public static CommonResult handerException(BlockException exception){
return new CommonResult(4444,"按客户自定义,global handerException ---- 1");
}
public static CommonResult handerException2(BlockException exception){
return new CommonResult(4444,"按客户自定义,global handerException ---- 2");
}
}
RateLimitController
//customerblockhandler
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class,
blockHandler = "handerException2")
public CommonResult customerBlockHandler(){
return new CommonResult(200,"按客户自定义测试ok",new Payment(2020L,"serial003"));
}
限流后测试:
三个核心API:
服务熔断无配置
如果程序运行出错,直接404
只配置fallback
程序运行出错,直接调用fallback指定的方法内容返回。
只配置BlockHandler
当程序运行出错,会用BlockHnadler来兜底!
两种都配置
在没被限流降级时,调用fallback,在被限流降级之后,调用blockhandler!
exceptionToIngore
如果这样配置了,就不走fallback了!
熔断框架对比
介绍
一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化,因此将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
步骤
修改8401步骤:
POM
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
YML
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719端口+1,直至找到未被占用的端口
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
新建nacos业务规则配置:
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。
启动8401,发现Sentinel控制台有流控规则了!关闭8401,发现流控规则没有了,重启8401并多次刷新,流控规则又回来了!!!!!!!!!
背景
单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。
总结
一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题!
简介
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务!
分布式事务模型:1个ID+三组件
1个ID:Transaction ID ------ XID 即全局唯一的事务ID
三组件概念:
处理过程:
下载地址:https://github.com/seata/seata/releases/download/v1.0.0/seata-server-1.0.0.zip
安装:
mysql5.7新建数据库seata
seata里面新建表:建表的db_store.sql在**\seata-server-0.9.0\seata\conf**目录里面
drop table if exists `global_table`;
create table `global_table` (
`xid` varchar(128) not null,
`transaction_id` bigint,
`status` tinyint not null,
`application_id` varchar(32),
`transaction_service_group` varchar(32),
`transaction_name` varchar(128),
`timeout` int,
`begin_time` bigint,
`application_data` varchar(2000),
`gmt_create` datetime,
`gmt_modified` datetime,
primary key (`xid`),
key `idx_gmt_modified_status` (`gmt_modified`, `status`),
key `idx_transaction_id` (`transaction_id`)
);
drop table if exists `branch_table`;
create table `branch_table` (
`branch_id` bigint not null,
`xid` varchar(128) not null,
`transaction_id` bigint ,
`resource_group_id` varchar(32),
`resource_id` varchar(256) ,
`lock_key` varchar(128) ,
`branch_type` varchar(8) ,
`status` tinyint,
`client_id` varchar(64),
`application_data` varchar(2000),
`gmt_create` datetime,
`gmt_modified` datetime,
primary key (`branch_id`),
key `idx_xid` (`xid`)
);
drop table if exists `lock_table`;
create table `lock_table` (
`row_key` varchar(128) not null,
`xid` varchar(96),
`transaction_id` long ,
`branch_id` long,
`resource_id` varchar(256) ,
`table_name` varchar(32) ,
`pk` varchar(36) ,
`gmt_create` datetime ,
`gmt_modified` datetime,
primary key(`row_key`)
);
修改seata-server-0.9.0\seata\conf目录下的registry.conf配置文件:指明注册中心为nacos及修改连接信息
测试:先启动nacos,再启动seata server.bat,成功!
怎么玩?
本地@Transactional
全局@GlobalTransactional
Seata的分布式交易解决方案:
AT模式如何做到对业务的无侵入
一阶段加载:在一阶段,Seata会拦截“业务SQL",
解析SQL语义,找到“业务SQL"要更新的业务数据,在业务数据被更新前,将其保存成"before image” ,执行“业务SQL"更新业务数据,在业务数据更新之后,其保存成“after image" ,最后生成行锁。
以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。
二阶段回滚:即Seata就需要回滚一阶段已经执行的“业务SQL",还原业务数据。回滚方式便是用"before image"还原业务数据;但在还原前要首先要校验脏写,对比”数据库当前业务数据”和"after image",如果两份数据完全一致就说明没有脏写, 可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。
二阶段提交:因为“业务SQL"在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。