上次说到Netflix已经过时了,目前除了一些大厂使用自己内部的微服务架构外,市面上使用最广泛的就是SpringCloudAlibaba,再说说后台架构的演进
- 单体架构:整体打包为一个服务进行部署,简单,代码集中,不易于后期扩展和维护,不支持水平扩展
- 垂体架构:将一个项目拆分成多个进行部署,项目与项目之间独立,但有冗余业务代码,如:都需要用户信息业务
- 分布式架构: 使用RPC进行服务与服务之间的通信,服务治理需要自己手动管理,后期难以维护
- SOA架构:由专门的服务治理中心管理服务与服务之间的调用关系,服务之间有依赖关系(分布式--使用Dubbo搭建分布式项目)导致服务关系也很复杂,共用一套数据库可能出现同步问题
- 微服务架构:每个服务都是独立的个体,需要单独部署,强调每个服务都是独立的数据库,做到真正的解耦
SpringCloudAlibaba使用的组件如下:
组件 | 描述 |
---|---|
Nacos | 注册中心,除了提供服务治理、服务发现外,还提供服务配置 |
Sentinel | 除了熔断策略,还包含流控规则、热点规则等 |
Ribbon | 实现服务与服务之间调用的负载均衡 |
OpenFeign | 服务与服务之间调用,集成了Ribbon |
GateWay | 应用内网关,提供对外统一入口 |
Seata | 分布式事务组件 |
一、Nacos
微服务中最核心组件就是注册中心,Nacos作为服务注册中心,比Eureka多个配置中心的功能
Nacos官方文档:https://nacos.io/zh-cn/docs/quick-start.html
1. 安装Nacos
下载2.0.3稳定版本:https://github.com/alibaba/nacos/releases/tag/2.0.3,对应JDK版本需要1.8
以上
下载解压后就可以启动Nacos了,来到bin
目录下:
执行命令,standalone
表示单机模式:
startup.cmd -m standalone
浏览器访问:http://localhost:8848/nacos
:
默认账号密码均为:nacos
,登录后
2. 服务注册
SpringBoot项目中如果使用SpringCloudAlibaba,需要对应SpringBoot的版本:
Spring Cloud Alibaba Version | Spring Cloud Version | Spring Boot Version |
---|---|---|
2021.0.1.0 | Spring Cloud 2021.0.1 | 2.6.3 |
2.2.7.RELEASE | Spring Cloud Hoxton.SR12 | 2.3.12.RELEASE |
2021.1 | Spring Cloud 2020.0.1 | 2.4.2 |
2.2.6.RELEASE | Spring Cloud Hoxton.SR9 | 2.3.2.RELEASE |
2.1.4.RELEASE | Spring Cloud Greenwich.SR6 | 2.1.13.RELEASE |
2.2.1.RELEASE | Spring Cloud Hoxton.SR3 | 2.2.5.RELEASE |
2.2.0.RELEASE | Spring Cloud Hoxton.RELEASE | 2.2.X.RELEASE |
2.1.2.RELEASE | Spring Cloud Greenwich | 2.1.X.RELEASE |
2.0.4.RELEASE(停止维护,建议升级) | Spring Cloud Finchley | 2.0.X.RELEASE |
1.5.1.RELEASE(停止维护,建议升级) | Spring Cloud Edgware | 1.5.X.RELEASE |
2.1 建立项目
新建Maven聚合项目,并添加一个provider
模块:
主工程pom文件:
<?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>
<packaging>pom</packaging>
<modules>
<module>provider-8001</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-cloud-alibaba-version>2.2.7.RELEASE</spring-cloud-alibaba-version>
</properties>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.12.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.aruba</groupId>
<artifactId>SpringCloudAlibabaStudy</artifactId>
<version>1.0-SNAPSHOT</version>
<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>
</dependencies>
</dependencyManagement>
</project>
provider模块
pom文件:
<?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">
<parent>
<artifactId>SpringCloudAlibabaStudy</artifactId>
<groupId>com.aruba</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>provider-8001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<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>
</dependencies>
</project>
2.2 配置文件
配置文件中配置nacos服务
的地址:
spring:
application:
name: provider
cloud:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8001
management:
endpoint:
web:
exposure:
include:'*'
2.3 @EnableDiscoveryClient
使用@EnableDiscoveryClient
注解启动类:
@SpringBootApplication
@EnableDiscoveryClient
public class Provider8001Application {
public static void main(String[] args) {
SpringApplication.run(Provider8001Application.class, args);
}
}
启动后,nacos的控制台会出现我们启动的服务,服务名就是spring.application.name
:
2.4 测试controller
创建一个测试的处理单元:
@RestController
public class DemoController {
@Value("${server.port}")
private String port;
@RequestMapping("/demo")
public String demo() {
return "demo " + port;
}
}
2.5 集群
Nacos会自动按照spring.application.name
来辨别是否是同一个服务,并加入到一个集群中,按照上面步骤再次创建一个provider
模块,来模拟集群环境:
配置文件中只是修改了端口:
spring:
application:
name: provider
cloud:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8002
management:
endpoint:
web:
exposure:
include:'*'
启动后,控制台该服务的集群节点数目会变为2:
3. 服务消费
和Netflix一样,服务之间的调用是通过http
的,并集成了Ribbon,自带负载均衡效果
3.1 创建消费者项目
依赖是相同的:
<dependencies>
<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>
</dependencies>
配置文件中自定义一个调用服务的请求主地址:
spring:
application:
name: consumer
cloud:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 9001
# 消费者将要去访问的微服务名称(注册成功的Nacos的微服务提供者)
service-url:
nacos-user-service: http://provider
3.2 RestTemplate
RestTemplate就是一个http请求的工具,集成了Ribbon,把他注入到spring容器中,并使用@LoadBalanced
注解:
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3.3 controller层
通过RestTemplate调用服务,方法为:getForObject(请求地址,返回类型, 请求参数...)
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Value("${service-url.nacos-user-service}")
private String serverURL;
@RequestMapping("/consume")
public String consume() {
return restTemplate.getForObject(serverURL + "/demo", String.class);
}
}
3.4 启动
启动类依然使用@EnableDiscoveryClient
注解:
@SpringBootApplication
@EnableDiscoveryClient
public class Consumer9001Application {
public static void main(String[] args) {
SpringApplication.run(Consumer9001Application.class, args);
}
}
访问接口:
4. 配置中心
Nacos的特有功能就是配置中心,它能够做到动态的改变配置,而不需要重启服务
4.1 创建项目
创建一个config
模块:
依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
application.yml中配置spring.profiles.active
:
spring:
profiles:
active: dev # 表示开发环境
4.2 bootstrap.yml
创建bootstrap.yml
配置文件,该配置文件优先加载于application.yml
使用file-extension
指定支持的Nacos配置文件类型:
server:
port: 8100
spring:
application:
name: config
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml # 支持的配置文件类型,目前只有yaml和properties
4.3 Nacos中新建配置
使用Nacos控制台,创建一个配置:
填写DataID
,规则为:spring.application.name
-spring.profiles.active
.file-extension
所以我们这边为:config
-dev
.yaml
选择配置格式为yaml
,并填写配置的内容:
4.4 @RefreshScope
写一个测试controller,使用@RefreshScope
注解Class
并使用@value
注解与上面Nacos配置文件内容对应:
@RestController
@RefreshScope
public class ConfigController {
@Value("${config.info}")
private String info;
@RequestMapping("/config")
public String config() {
return info;
}
}
启动服务后,进行访问:
修改配置,重新请求,经测试后也生效
4.5 Namespace和Group
上面我们使用active
标识不同环境的文件,DataID
对应一个配置文件,Nacos还支持Namespace和Group,分组级别为:Namespace>Group>DataID。
控制台上可以创建Namespace:
项目中使用namespace
进行配置,对应创建的命名空间id:
spring:
application:
name: config
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml # 支持的配置文件类型,目前只有yaml和properties
namespace: 921785db-0999-4f7b-b1c1-7511f88ef320
Group可以在新建配置时指定一个新的:
项目中使用group
进行配置:
spring:
application:
name: config
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml # 支持的配置文件类型,目前只有yaml和properties
namespace: my
group: DEV_GROUP
5. 集群搭建
nacos本质上就是一个Jar
,所以windows和linux使用的都一样,只是linux中启动脚本使用的是shell脚本
之前我们在nacos中进行的配置都会存到它自带的嵌入式数据库derby
中,针对nacos集群,内置数据库显然已满足不了要求了,接下来搭建的nacos集群,我们将使用MySQL
数据库,使得多台nacos节点可以访问同一个数据源,要知道MySQL
也支持集群,可以有效提升实际项目中的可用性
注:nacos目前只支持MySQL
5.1 安装
将nacos压缩包传到linux系统中
执行解压命令,并移动到/usr/local/
目录:
unzip nacos-server-2.0.3.zip
mv ./nacos /usr/local/
5.2 创建数据库
在解压后的conf
目录下,有一个nacos-mysql.sql
文件
将内容复制后,创建一个数据库,我这边数据库名就为nacos
,执行sql语句:
复制的sql内容为:
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info */
/******************************************/
CREATE TABLE `config_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) DEFAULT NULL,
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`c_desc` varchar(256) DEFAULT NULL,
`c_use` varchar(64) DEFAULT NULL,
`effect` varchar(64) DEFAULT NULL,
`type` varchar(64) DEFAULT NULL,
`c_schema` text,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_aggr */
/******************************************/
CREATE TABLE `config_info_aggr` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) NOT NULL COMMENT 'group_id',
`datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
`content` longtext NOT NULL COMMENT '内容',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_beta */
/******************************************/
CREATE TABLE `config_info_beta` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_tag */
/******************************************/
CREATE TABLE `config_info_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_tags_relation */
/******************************************/
CREATE TABLE `config_tags_relation` (
`id` bigint(20) NOT NULL COMMENT 'id',
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`nid` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`nid`),
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = group_capacity */
/******************************************/
CREATE TABLE `group_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = his_config_info */
/******************************************/
CREATE TABLE `his_config_info` (
`id` bigint(64) unsigned NOT NULL,
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`data_id` varchar(255) NOT NULL,
`group_id` varchar(128) NOT NULL,
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL,
`md5` varchar(32) DEFAULT NULL,
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`src_user` text,
`src_ip` varchar(50) DEFAULT NULL,
`op_type` char(10) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`nid`),
KEY `idx_gmt_create` (`gmt_create`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = tenant_capacity */
/******************************************/
CREATE TABLE `tenant_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
CREATE TABLE `tenant_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`kp` varchar(128) NOT NULL COMMENT 'kp',
`tenant_id` varchar(128) default '' COMMENT 'tenant_id',
`tenant_name` varchar(128) default '' COMMENT 'tenant_name',
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
CREATE TABLE `users` (
`username` varchar(50) NOT NULL PRIMARY KEY,
`password` varchar(500) NOT NULL,
`enabled` boolean NOT NULL
);
CREATE TABLE `roles` (
`username` varchar(50) NOT NULL,
`role` varchar(50) NOT NULL,
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
);
CREATE TABLE `permissions` (
`role` varchar(50) NOT NULL,
`resource` varchar(255) NOT NULL,
`action` varchar(8) NOT NULL,
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
5.3 配置数据库
有了数据库,接着在nacos配置文件application.properties
中修改数据源:
cd /usr/local/nacos/conf
vi application.properties
修改内容为:
方便复制:
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://192.168.42.170:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root
5.4 配置集群
这边一会通过复制二份nacos,模拟一个集群环境
在当前目录下,将集群的配置文件cluster.conf.example
进行复制,并编辑:
cp cluster.conf.example cluster.conf
vi cluster.conf
文件末尾添加集群节点,只是端口不同:
192.168.42.4:8848
192.168.42.4:8858
192.168.42.4:8868
复制二份nacos:
cp -r nacos/ nacos2/
cp -r nacos/ nacos3/
进入复制后的配置目录,修改配置文件application.properties
的对应端口号:
改为8858
和8868
后,分别启动nacos,此时不用加上指定单机版了:
cd /usr/local/nacos/bin
./startup.sh
执行后访问一个节点的管理界面,如下,就说明启动成功了:
5.5 nginx代理集群
使用nginx作为nacos统一的集群出入口,nginx入门可以看:分布式--Nginx入门
修改nginx配置,添加一个stream
块指令:
stream {
upstream cls {
server 192.168.42.4:8848;
server 192.168.42.4:8858;
server 192.168.42.4:8868;
}
server {
listen 81;
proxy_pass cls;
}
}
启动后,通过81端口进行访问:
想要在项目中使用,SpringBoot配置文件中使用nginx的地址和端口即可:
spring:
application:
name: consumer
cloud:
discovery:
server-addr: 192.168.42.4:81
新建一个配置,数据库中也成功存入了:
二、Sentinel
Sentinel的核心思想是对资源的细粒度控制,对比hystrix,除了熔断策略外,还能针对处理单元的流量进行控制,Sentinel拥有自己的控制台,在上面可以进行各种资源配置
Sentinel控制台下载:https://github.com/alibaba/Sentinel/releases,下载后直接运行Jar即可
默认使用8080
端口,账号密码均为:sentinel
后面我们会在该控制台上进行一系列配置
1. 服务提供者使用Sentinel
创建一个项目作为服务提供者
1.1 依赖
<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>
1.2 yml中配置Sentinel
除了nacos的配置外,还需要进行sentinel的配置:
spring:
application:
name: sentinel-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,键入被占用会自动从8719+1,直到找到未被占用的端口
port: 8719
server:
port: 8003
management:
endpoint:
web:
exposure:
include:'*'
1.3 编写controller并启动
controller层:
@RestController
public class DemoController {
@RequestMapping("/demo1")
public String demo1() {
return "sentinel1";
}
@RequestMapping("/demo2")
public String demo2() {
return "sentinel2";
}
}
启动类:
@SpringBootApplication
@EnableDiscoveryClient
public class SentinelProvider8003Application {
public static void main(String[] args) {
SpringApplication.run(SentinelProvider8003Application.class, args);
}
}
Sentinel采用的懒加载,对接口进行访问后,才会在控制台出现服务名:
2. 流控规则
配置流控规则,可以对资源的访问QPS(每秒请求量)
或并发量
进行控制
可配置规则如下:
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
-
阈值类型/单机阈值:
1.QPS(每秒钟的请求数量):当调用该API的QPS达到阈值的时候,进行限流
2.并发线程数:当调用该API的线程数量达到阈值的时候,进行限流 - 是否集群:当前不需要集群
-
流控模式:
1.直接:API达到限流条件时,直接限流
2.关联:当关联的资源达到阈值时,就限流自己
3.链路:与关联相反,当自己达到阈值时,限流链路的资源 -
流控效果:阈值类型为并发线程数,只有快速失败
1.快速失败:直接失败,抛异常
2.Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFacotor,经过预热时长,才达到设置的QPS阈值
3.排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
下面使用QPS来进行配置,并发线程数只是少了流控效果
2.1 流控模式:直接
效果:当每秒请求量达到阈值时,进行限流
配置如下:
尝试快速访问demo1接口:
2.2 流控模式:关联
效果:当关联的资源达到阈值时,就限流自己
配置如下:
postman中模拟请求demo2:
此时访问demo1接口,还是被限流了:
2.3 流控模式:链路与@SentinelResource
效果:链路与关联相反,在达到阈值时,资源本身不会限流,而是限流链路的资源
除了接口外,Sentinel还可以使用@SentinelResource
注解标记一个资源:
@Component
public class ShareServiceImpl {
@SentinelResource(value = "shared")
public String shared() {
return "shared";
}
}
再定义两个接口,去访问上面的shared
资源:
@RequestMapping("/demo3")
public String demo3() {
return shareService.shared();
}
@RequestMapping("/demo4")
public String demo4() {
return shareService.shared();
}
我们针对shared
资源进行流控配置,模式为链路,并指定链路到/demo3
:
yml中配置web-context-unify
为false
:
spring:
application:
name: sentinel-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,键入被占用会自动从8719+1,直到找到未被占用的端口
port: 8719
# 链路模式需要配置为false
web-context-unify: false
重启服务后,快速访问demo4
接口可以正常访问,快速访问demo3
会出现异常:
2.4 流控效果:预热(Warm Up)
阈值类型为QPS
的流控效果除了快速失败
外,还有预热(Warm Up)
和排队等待
预热指的是QPS
的最大阈值一开始并不是我们设定的值,而是从设定阈值/coldFactor(冷加载因子,默认3)
开始,经过预热时长后达到设定阈值
,目的是防止服务器突然接收到大量请求,导致崩溃
其效果为:刚开始大量的访问资源会被限流,后面到达设定阈值
后,在阈值范围内访问不会被限流
快速访问效果:
2.5 流控效果:排队等待
排队等待是指每秒只处理设置阈值
个请求,其他的进行排队等待,后续请求到达超时时间后进行限流
1秒1次请求,后续请求超时时间为3s,访问效果:
3. 熔断
Sentinel的熔断策略有三种:
-
慢调用比例:请求到达
最大RT(超时时间)
没有返回结果,记为一次慢调用。在统计时长
内,请求数达到最小请求数
,并且慢调用次数达到最小请求数*比例阈值
,则触发熔断 -
异常比例:在
统计时长
内,请求数达到最小请求数
,并且请求返回异常次数达到最小请求数*比例阈值
,则触发熔断 -
异常数:在
统计时长
内,请求数达到最小请求数
,并且请求返回异常次数达到设定异常数
,则触发熔断
3.1 慢调用比例
编写一个接口,睡眠1.5s后返回:
@RestController
public class Demo2Controller {
@RequestMapping("/demo5")
public String demo5() throws InterruptedException {
Thread.sleep(1500);
return "demo5";
}
}
配置熔断策略,1s内请求数达到5,并慢调用达到比例阈值,触发熔断:
快速请求后,达到限流:
3.2 异常比例
编写一个接口,手动抛出异常:
@RequestMapping("/demo6")
public String demo6(Integer id) {
if (id == null) {
throw new RuntimeException("异常比例");
}
return "demo6";
}
配置熔断策略:
快速访问,被限流:
剩下的异常数策略和异常比例使用上差不多
4. 热点规则
热点规则是更加细粒度的流控,我们在流控模式:链路
中使用了@SentinelResource
注解定义了一个资源,热点规则也需要和@SentinelResource
注解配合使用
流控仅仅是对资源的阈值进行限制,而热点规则可以指定访问资源的参数阈值
定义一个接口,使用@SentinelResource
注解:
@RequestMapping("/demo7")
@SentinelResource(value = "test_hotkey")
public String demo7(@RequestParam(value = "hotkey", required = false) Integer hotkey) {
return "demo7";
}
为资源test_hotkey
配置热点规则:
不带参数和带参数的访问效果:
4.1 参数例外项
更加细粒度的操作是指定参数具体的值,以及对应的阈值:
目前支持的数据类型如下:
5. @SentinelResource自定义异常处理
通过上面使用,我们知道了可以通过@SentinelResource
自定义资源,但使用该注解时,一旦达到限流,接口返回的却是500,实际上,使用@SentinelResource
注解还需要指定异常处理的属性
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
String value() default "";
EntryType entryType() default EntryType.OUT;
int resourceType() default 0;
// 限流处理的方法
String blockHandler() default "";
//限流处理的类
Class<?>[] blockHandlerClass() default {};
//异常处理的方法
String fallback() default "";
String defaultFallback() default "";
Class<?>[] fallbackClass() default {};
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
5.1 blockHandler
blockHandler
可以指定限流异常
的处理方法,定义该方法后,达到限流就不会出现500错误了
限流异常的处理方法拥有和源资源方法相同的参数,以及多了一个BlockException
的参数:
@RequestMapping("/demo8")
@SentinelResource(value = "demo8", blockHandler = "blockHandler")
public String demo8(Integer id) {
return "demo8";
}
public String blockHandler(Integer id, BlockException e) {
return "您被限制访问" + id;
}
为demo8
配置流控规则:
快速访问效果:
5.2 blockHandlerClass
除了在当前类中定义限流异常处理方法外,还可以指定一个类
public class MyBlockHandler {
public static String myBlockHandler(Integer id, BlockException e) {
return "您被限制访问" + id;
}
}
请求接口:
@RequestMapping("/demo9")
@SentinelResource(value = "demo9",
blockHandler = "myBlockHandler",
blockHandlerClass = MyBlockHandler.class)
public String demo9(Integer id) {
return "demo9";
}
5.3 fallback
block只是针对限流异常
,其他异常除了使用SpringMVC的异常处理,也可以使用注解的fallback
属性处理
异常处理方法除了和原资源方法参数相同外,还额外需要一个Throwable
参数:
@RequestMapping("/demo10")
@SentinelResource(value = "demo10",
fallback = "fallback")
public String demo10(Integer id) {
if (id == null) throw new NullPointerException("id 不能为空");
return "demo10";
}
public String fallback(Integer id, Throwable e) {
return "服务器异常 " + e.getMessage();
}
访问效果:
6. 配置持久化
实操过的同学都会发现,每次服务重启后,之前的配置都消失了,你想的没错,Sentinel本身不带持久化功能,需要实现持久化,要和Nacos
进行配合,交由Nacos进行持久化处理
添加依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.5</version>
</dependency>
yml进行持久化配置,dataId
还是使用之前的格式:
spring:
application:
name: sentinel-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,键入被占用会自动从8719+1,直到找到未被占用的端口
port: 8719
# 配置为false
web-context-unify: false
datasource: # sentinel持久化
nacos:
nacos:
serverAddr: localhost:8848
groupId: DEFAULT_GROUP
dataId: sentinel-provider-dev.json #dataid格式还是nacos的格式
ruleType: flow
profiles:
active: dev
使用Nacos配置中心的功能,在控制台创建配置:
内容为:
[
{
"resource": "test1",
"limitApp": "default",
"grade": 1,
"count": 2,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。
访问接口后,查看Sentinel控制台,会出现对应的流控配置: