玩转SpringCloud-Alibaba微服务

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:服务网关 ⭐⭐⭐

具体学习,传送门:https://blog.csdn.net/qq_45399396/article/details/128150057

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:高吞吐消息队列 ⭐

猜你喜欢

转载自blog.csdn.net/qq_45399396/article/details/130433453