文章目录
- Cloud-Alibaba简介
- 1,Nacos:注册中心&配置中心 ⭐⭐⭐
- 环境部署
- Springboot整合Nacos
- 2,Sentinel: 限流、熔断&降级 ⭐⭐
- 环境部署
- Springboot整合Sentinel
- 3,Dubbo:远程调用 ⭐⭐⭐
- Springboot整合Dubbo
- 4,Getway:服务网关 ⭐⭐⭐
- 动态路由
- 限流
- 5,Seata:分布式事务 ⭐
- 6,RockedMQ:高吞吐消息队列 ⭐
Cloud-Alibaba简介
前置基础
:学习完SpringcLoud微服务再来学习CloudAlibaba,会学习来很快哦…
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服 务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
.
依托 Spring Cloud Alibaba ,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接 入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
SpringCloud-Alibaba的分布式常用组件如下所示:
组件 | 说明 |
Nacos | 注册中心&配置中心 |
Sentinel | 限流、熔断&降级 |
Dubbo | 远程调用 |
Getway | 服务网关 |
Seata | 分布式事务 |
RockedMQ | 高吞吐消息队列 |
> 特备要注意各组件的版本和cloud的版本对应以及springboot的版本对应关系
,参考如下所示:
图一:
图二:
Pay
:注意点: 要想尽量避坑学习CloudAlibab的系列组件,推荐使用windows版本的来进行学习
,如果选择Linux版本来学习,会遇到很多坑,接下来的环境搭建就是用Linux版本来学习组件
,坑迟早都会遇到…直接发现问题直接解决!!!!
1,Nacos:注册中心&配置中心 ⭐⭐⭐
Nacos 可以作为服务注册中心、配置中心。
也是很多公司微服务项目首选的注册中心和配置中心
.
Nacos 提供了一组简单易用的操作,帮助快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos用作服务注册中心比较多,因为其中服务的管理都是控制台中可视化的去管理和操作,很人性化
,所以很多公司喜欢用这个组件
环境部署
部署采用docker-compose.yml文件在linux中部署
docker-compose.yml
注意,这里需要启动mysql,在mysql中创建好nacos_config 数据库
version: "3.1"
services:
nacos:
image: nacos/nacos-server:1.4.1
container_name: nacos
hostname: nacos
restart: always
environment:
- MODE=standalone
- TZ=Asia/Shanghai
- NACOS_SERVER_PORT=8848
- SPRING_DATASOURCE_PLATFORM=mysql
- MYSQL_SERVICE_HOST=43.136.82.176
- MYSQL_SERVICE_PORT=3306
- MYSQL_SERVICE_DB_NAME=nacos_config
- MYSQL_SERVICE_USER=root
- MYSQL_SERVICE_PASSWORD=root
- PREFER_HOST_MODE=hostname
volumes:
- ./nacos/logs:/home/nacos/logs
ports:
- "8848:8848"
创建nacos_config 数据库
/******************************************/
/* 数据库全名 = 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(20) 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(20) 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(20) 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(20) 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');
linux中查看日志,看到如下说明,说明nacos启动成功
Springboot整合Nacos
其实很简单,具体步骤总结如下:
1,导入依赖
2,application.yml文件配置信息
导入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
yml配置信息
server:
port: 8081
spring:
application:
name: service-provider
cloud:
nacos:
discovery:
server-addr: 43.136.82.176:8848
启动项目,看到控制台输出日志,可以看到,项目注册成功
.
到Nacos控制台看是否注册成功:http://ip:8848/nacos
示例:有两个服务provider和consumer,consumer通过服务名调用provider服务
首先根据服务名,找到对应的服务的ip:port列表,然后替换服务名,远程调用
Nacos用的最主要的是注册中心的功能
2,Sentinel: 限流、熔断&降级 ⭐⭐
Sentinel是阿里开源的项目,提供了
流量控制、熔断降级、系统负载保护
等多个维度来保障服务之间的稳定性。
Sentinel其实主要还是多熟悉控制台的各种限流、降级、负载等操作,用Sentinel最大的方便就是做流量控制、熔断降级、系统负载的时候减少到项目中写代码,基本都是在控制台去配置,所以,学习Sentinel的时候,最主要的就是了解一下控制台的配置规则操作就可以,具体规则这里就不多说明了,直接看官网或者学习视频看一遍差不多了,接下来主要是教大家快速搭建Sentinel环境啦…
环境部署
version: "3.1"
services:
sentinel:
image: bladex/sentinel-dashboard:1.8.0
container_name: sentinel
ports:
- "8858:8858"
控制台如下所示:可以看到很多的的规则,规则详情后续待写…
Springboot整合Sentinel
整体步骤如下所示:
1, 导入依赖
2,application.yml文件配置信息
3,资源限流降级的注解:@SentinelResource
3,其他功能待续(其实就是各种规则做一个小实践,这里不一 一 说明了。环境搭好了,自己可以去实践其他规则就很快了)
导入依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<--给予注解的形式进行资限流降级-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.0</version>
</dependency>
3,Dubbo:远程调用 ⭐⭐⭐
注意:dubbo需要配合注册中心使用,常用注册中心为zookeeper、Nacos等
Dubbo工作原理图如下所示:
关于Dubbo说明:
面向接口代理的高性能RPC调用
:提供高性能的基于代理的远程调用能力,服务以接口为粒度,为开发者屏蔽远程调用底层细节。
.智能多种负载均衡
:内置多种负载均衡策略,减少调用延迟,提高系统吞吐量。
.服务自动注册与发现
:服务实例上下线实时感知。
.运行期流量调度
:通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能。
.可视化的服务治理与运维
:提供丰富服务治理工具:例如随时查询服务元数据、服务健康状态及调用统计
,实时下发路由策略、调整配置参数
。
Springboot整合Dubbo
用起来其实超级简单,话不多说,直接看步骤如下:
1,导入依赖
2,配置application.yml
3,启动类加上@EnableDubbo
4,服务调用方采用@DubboReference替换@Autowired
5,服务提供方的service实现类,加上@DubboService
6,需要创建一个spirngboot项目定义接口映射调用方api
导入依赖
<!-- dubbo相关依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.0.2.1</version>
</dependency>
<!--引入api接口依赖:自定义依赖-->
<dependency>
<groupId>com.xzq.api</groupId>
<artifactId>CommonApi</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
application.yml文件配置dubbo
dubbo:
application:
name: service
registry:
address: nacos://43.136.82.176:8848
protocol:
name: dubbo
port: 20881
scan:
base-packages: com.xzq
服务提供方的service的api
服务调用方
创建一个spirngboot项目定义接口映射调用方api
注意:dubbo远程调用默认的负载均衡是轮训调用
!!!
4,Getway:服务网关 ⭐⭐⭐
具体学习,传送门:
GetWay作为统一配置的网关,可以实现
负载均衡,动态路由以及限流
GetWay作为SpringCloud家族中新一代网关,在性能上比Zuul高上1.5倍
Getway内部实现了负载均衡,不需要手动配置,这点了解一下即可,此在,GetWay天然适配Eureka,能够在集群中负载均衡,默认采用轮询的方式进行。
GetWay的动态路由也是很大一特点,在下面的学习中会清楚的应用起来 GetWay限流内部默认采用的时redis lua脚本+令牌桶算法进行限流,限流方式很多,例如:ip限流,路径限流等,此在限流默认系统时无法捕捉到异常原因,只会返回code码429
动态路由
1,导入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!--基于Redis实现限流-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
2,添加注解,将getway服务注册到注册中心上,getway服务端口为8083
3,配置application,yml文件
静态路由和动态路由的区别在于配置中关于uri的配置,lb://服务名 (标识动态路由,默认采用的负载均衡),如果是写死的ip:port则是静态路路由
需要了解配置文件中三个关键属性
id: 这个代表路由一个服务的标识,一般与服务名一致
uri: 路由地址
predicates: 断言,需要匹配到这个路径,才会路由到uri
filters:限流策略(默认采用令牌桶的限流策略,所以基本配置都是配置令牌桶相关的参数)
eureka:
client:
service-url:
defaultZone: http://eureka8761.com:8761/eureka/ , http://eureka8762.com:8762/eureka/ # 注册的Eureka服务地址
registry-fetch-interval-seconds: 30
instance:
appname: getway-serve
spring:
application:
name: getway-serve
redis:
host: 120.76.159.196
port: 6379
database: 2
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: serve1
uri: lb://SERVE1
predicates:
- Path=/ser1/**
filters:
- name: SelfRateLimiter # 自定义限流过滤器
args:
# 每秒允许处理的请求数量 # 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 每秒最大处理的请求数量# 令牌桶总容量
redis-rate-limiter.burstCapacity: 1
# 每次请求获取的令牌数
redis-rate-limiter.requestedTokens: 1
#限流策略,对应策略的Bean
key-resolver: "#{@pathKeyResolver}"
- id: serve2
uri: lb://SERVE2
predicates:
- Path=/ser2/**
filters:
# - name: RequestRateLimiter # 官方自带
- name: SelfRateLimiter
args:
# 每秒允许处理的请求数量 # 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 每秒最大处理的请求数量# 令牌桶总容量
redis-rate-limiter.burstCapacity: 1
# 每次请求获取的令牌数
redis-rate-limiter.requestedTokens: 1
#限流策略,对应策略的Bean
key-resolver: "#{@ipKeyResolver}"
这样通过路由8083端口去访问服务,则效果如图所示:
网关访问服务2,服务2调用服务1 的test1方法,成功!!!
限流
在配置yml文件中filter属性配置就是配置限流的策略,限流默认采用令牌桶限流策略
注意,还需要了解一个类: KeyResolver :指定具体的限流对象,例如ip限流,url限流等
在项目代码中,配置了两中限流方式:IP、URL
注意,在下面配置文件中,filter属性默认的限流name = RequestRateLimiter 这个类,下面配置我采用自定义限流继承RequestRateLimiterGatewayFilterFactory这个类
routes:
- id: serve1
uri: lb://SERVE1
predicates:
- Path=/ser1/**
filters:
- name: SelfRateLimiter # 自定义限流过滤器
args:
# 每秒允许处理的请求数量 # 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 每秒最大处理的请求数量# 令牌桶总容量
redis-rate-limiter.burstCapacity: 1
# 每次请求获取的令牌数
redis-rate-limiter.requestedTokens: 1
#限流策略,对应策略的Bean
key-resolver: "#{@pathKeyResolver}"
- id: serve2
uri: lb://SERVE2
predicates:
- Path=/ser2/**
filters:
# - name: RequestRateLimiter # 官方自带
- name: SelfRateLimiter
args:
# 每秒允许处理的请求数量 # 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 每秒最大处理的请求数量# 令牌桶总容量
redis-rate-limiter.burstCapacity: 1
# 每次请求获取的令牌数
redis-rate-limiter.requestedTokens: 1
#限流策略,对应策略的Bean
key-resolver: "#{@ipKeyResolver}"
继承RequestRateLimiterGatewayFilterFactory,限流策略没变,基本是源码复制过来,稍微重写一下,重写的目的是末尾能够将异常原因设置响应给前端
/**
* 自定义geway限流策略
*/
@Component
public class SelfRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {
private final RateLimiter defaultRateLimiter;
private final KeyResolver defaultKeyResolver;
public SelfRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {
super(defaultRateLimiter, defaultKeyResolver);
this.defaultRateLimiter = defaultRateLimiter;
this.defaultKeyResolver = defaultKeyResolver;
}
@Override
public GatewayFilter apply(Config config) {
KeyResolver resolver = getOrDefault(config.getKeyResolver(), defaultKeyResolver);
RateLimiter<Object> limiter = getOrDefault(config.getRateLimiter(), defaultRateLimiter);
return (exchange, chain) -> {
// exchange : DefaultServerWebExchange
// 示例:对路径,ip限流,
// resolve: value:"/ser1/test1"、"0:0:0:0:0:0:0:1"
// 这里获取到路径
// mono类型都是要采用反应式调用的方式,就好比steam流了,接下载就是对应的api调用,所以这里用到了flatmap
Mono<String> resolve = resolver.resolve(exchange);
return resolve.flatMap(key -> {
// 获取路由id(服务名)
String routeId = config.getRouteId();
if (routeId == null) {
// yml文件中没配置,则从当前请求上下文exchange中获取key为gatewayRoute的路由信息
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
// 获取具体的服务名
routeId = route.getId();
}
String finalRouteId = routeId;
// key = "ser1/test"
// 限流核心方法isAllowed-->redis+lua实现令牌桶算法
// 返回结果:1,是否允许、 2,剩余令牌有无,没有则-1、 3,流速、生产数、剩余数的map集合
/** 打印示例如下:
transfer-encoding--------[chunked]:分块传输编码
X-RateLimit-Remaining--------[0] :剩余量
X-RateLimit-Burst-Capacity--------[1]:桶容量
X-RateLimit-Replenish-Rate--------[1]:生产令牌速度
*/
Mono<RateLimiter.Response> allowed = limiter.isAllowed(routeId, key);
return allowed.flatMap(response -> {
response.getHeaders().entrySet();
// response中设置响应信息,响应出去
for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
}
// 放行
if (response.isAllowed()) {
return chain.filter(exchange);
}
// 不放行
System.out.println("已限流: " + finalRouteId);
ServerHttpResponse httpResponse = exchange.getResponse();
//修改code为500
httpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
if (!httpResponse.getHeaders().containsKey("Content-Type")) {
httpResponse.getHeaders().add("Content-Type", "application/json");
}
//此处无法触发全局异常处理,(默认返回429,无其它信息),手动返回response,设置code对应mes信息
HashMap<Object, Object> resultMap = new HashMap<>();
resultMap.put("code", "429");
resultMap.put("mes", finalRouteId + "服务访问人数过多,已限流");
resultMap.put("data", "Server throttling");
resultMap.put("success", "false");
//将map格式转为json
String resultJson = JSONObject.toJSONString(resultMap);
DataBuffer buffer = httpResponse.bufferFactory().wrap(resultJson.getBytes(StandardCharsets.UTF_8));
return httpResponse.writeWith(Mono.just(buffer));
});
});
};
}
private <T> T getOrDefault(T configValue, T defaultValue) {
return (configValue != null) ? configValue : defaultValue;
}
}
此在,在微服务的入口这里,还可以实现令牌的校验,检查登录,做授权服务
本质配置去全局过滤器(后续会采用其他的方式,比如springsecurity+jwt+oauth2做认证授权服务器校验token等),这里大致知道有这么一个过滤器GlobalFilter,Ordered
@Component
public class LoginFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String accessToken = request.getHeaders().getFirst("token");
if("xzq".equals(accessToken)){
return chain.filter(exchange);
}else{
// 给响应,跳到登录页面
return login(exchange);
}
}
private Mono<Void> login(ServerWebExchange exchange) {
HashMap<Object, Object> resultMap = new HashMap<>();
resultMap.put("code",401);
resultMap.put("mes","请重新登录授权");
resultMap.put("status", "401");
ServerHttpResponse response = exchange.getResponse();
byte[] bytes = resultMap.toString().getBytes(StandardCharsets.UTF_8);
response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
// 封装响应数据
DataBuffer buffer = response.bufferFactory().wrap(bytes);
return response.writeWith(Flux.just(buffer));
}
@Override
public int getOrder() {
return 0;
}
}
接下啦,用postman测试一下限流效果:
.
5,Seata:分布式事务 ⭐
待续:项目上也用得少,主要是能不用事务就尽量不用事务,因为系统性能会受限…
6,RockedMQ:高吞吐消息队列 ⭐