Spring Cloud Alibab 入门搭建,包含Nacos中心,注册服务发现服务,Feign请求,GateWay网关,sentinel限流

news2024/11/18 6:48:20

一、安装Nacos注册中心

1.1查看Nacos官网,安装Nacos服务,下载源码或者安装包

1.2启动服务,默认端口为8848,

二、创建服务注册&发现

2.1使用脚手架,创建注册服务和发现服务项目,我用的版本是2.6.13,

2.2增加启动类配置,用于支持注册服务和发现服务和Feign请求等

2.2.1注册服务增加配置类注解

2.2.2发现服务配置,激活Feign

2.2.3创建包nacosdiscoveryprovider,包下创建接口EchoServiceController作为注册服务接口, 用于后续Feign请求测试,启动服务

2.2.4创建发现服务普通RestTemplate接口,测试通过RestTemplate请求是否可以转发到注册服务端

2.2.5创建Feign接口

三、创建网关服务

3.1 脚手架创建网关服务

3.2启动类增加注解

3.3创建Controller接口测试服务是否可以正常访问

4.网关服务作为统一入口,需要配置断言Predicate机制拦截,一个请求在抵达网关层后,首先就要进行断言匹配,在满足所有断言之后才会进入Filter阶段转发至发现服务nacos-discovery-provider-sample,断言配置采用Nacos中心动态配置。

4.1在Nacos中心创建配置

4.2 网关服务application.properties增加配置获取Nacos中心的配置

4.3动态刷新中心配置

4.4增加配置类事件推送类,动态更新配置DynamicRouteServiceImpl

4.5项目创建DynamicRouteServiceImplNacos类,用于接收动态刷新。

4.5启动网关服务,Postman调用网关

5.网关配置JTWtoken鉴权

5.1自定义过滤器鉴权规则

 5.2修改Nacos中心nacos-gateway-sample.json断言配置,增加过滤器

5.3增加模拟登录接口获取token

四、为服务增加sentinel限流熔断配置

4.1下载按照运行sentinel

4.2网关服务配置sentinel

4.3配置限流规则

4.3.1选择API管理,新增API规则

4.3.2新增网关流控规则

4.3.3配置熔断规则

4.3.4配置限流和熔断统一返回值,此例是返回值,

五、限流熔断配置Nacos持久化

5.1Nacos控制台新建一个限流和熔断配置

5.1.1限流配置:

5.1.1熔断配置

5.2项目配置文件增加nacos的数据源配置

5.3创建config类,初始化加载配置参数属性

5.4创建限流熔断工厂类,加载sentinel相关内容。加载限流、熔断规则,自定义限流熔断错误返回处理,

5.5 Postman测试

5.5.1限流生效返回

5.5.2熔断生效返回

六 结束语


一、安装Nacos注册中心

1.1查看Nacos官网,安装Nacos服务,下载源码或者安装包

Nacos 快速开始

1.2启动服务,默认端口为8848,

进入文件夹,cd nacos/bin

以单机模式启动 服务

sh startup.sh -m standalone

登录注册中心查看服务是否正常

二、创建服务注册&发现

2.1使用脚手架,创建注册服务和发现服务项目,我用的版本是2.6.13,

项目名称nacos-discovery-provider-sample

脚手架创建的服务包含注册和发现

Cloud Native App Initializer (aliyun.com)

选择组件

项目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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>nacos-discovery-provider-sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos-discovery-provider-sample</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
        <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
    </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>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            <version>3.1.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>3.1.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <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>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.alibaba.cloud.nacosdiscoveryprovidersample.NacosDiscoveryProviderSampleApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

application.properties内容

默认服务名

spring.application.name=nacos-discovery-provider-sample

# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=nacos-discovery-provider-sample
# Nacos认证信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=192.168.100.115:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=23963657-2b3b-49db-be69-f2517dc3695c
#ribbon.nacos.enabled=true
server.port=8082

2.2增加启动类配置,用于支持注册服务和发现服务和Feign请求等

2.2.1注册服务增加配置类注解

@EnableDiscoveryClient 开启服务提供者或消费者,客户端的支持,用来注册服务

@EnableFeignClients 开启Feign功能

配置类注解@Configuration

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
package com.alibaba.cloud.nacosdiscoveryprovidersample.nacosdiscovery;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;

/**
 * @author <a href="mailto:chenxilzx1@gmail.com">theonefx</a>
 */
@EnableDiscoveryClient
@Configuration
@EnableFeignClients
public class NacosDiscoveryConfiguration {
}

2.2.2发现服务配置,激活Feign

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
 package com.alibaba.cloud.nacosdiscoveryprovidersample.demos.nacosdiscoveryconsumer;

import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@EnableFeignClients // 激活 @FeignClient
@Configuration
public class NacosDiscoveryConsumerConfiguration {

}

2.2.3创建包nacosdiscoveryprovider,包下创建接口EchoServiceController作为注册服务接口, 用于后续Feign请求测试,启动服务

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
package com.alibaba.cloud.nacosdiscoveryprovidersample.demos.nacosdiscoveryprovider;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class EchoServiceController {

    @GetMapping("/echo/{message}")
    public String echo(@PathVariable String message) throws InterruptedException {
        System.out.println(message);
        return "[ECHO] : " + message;
    }
    @GetMapping("/test")
    public String test() {
        return "[ECHO] : " +"test";
    }
}

2.2.4创建发现服务普通RestTemplate接口,测试通过RestTemplate请求是否可以转发到注册服务端

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
 package com.alibaba.cloud.nacosdiscoveryprovidersample.demos.nacosdiscoveryconsumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.Delayed;


@RestController
public class RestTemplateController {

    @LoadBalanced
    @Lazy
    @Autowired
    public RestTemplate restTemplate;
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @GetMapping("/call/echo/{message}")
    public String callEcho(@PathVariable String message) throws InterruptedException {
        // 访问应用 nacos-discovery-provider-sample 的 EchoServiceController 中的/echo/{message}
        return restTemplate.getForObject("http://nacos-discovery-provider-sample/echo/" + message, String.class);
    }
}

Postman 调用本服务接口 /call/echo/{message},正常访问到接口之后,RestTemplate通过路由

http://nacos-discovery-provider-sample/echo/  路由前缀为注册服务名称 转发的上述

nacosdiscoveryprovider包下接口EchoServiceController。

2.2.5创建Feign接口

创建发现服务Feign 接口,用于接收Feign请求转发只服务端,接口增加注解

@FeignClient("nacos-discovery-provider-sample") 指向服务提供者应用

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
package com.alibaba.cloud.nacosdiscoveryprovidersample.demos.nacosdiscoveryconsumer;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient("nacos-discovery-provider-sample") // 指向服务提供者应用
public interface EchoService {

    @GetMapping("/echo/{message}")
    String echo(@PathVariable("message") String message);
}

创建FeignController 测试Feign请求

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
 package com.alibaba.cloud.nacosdiscoveryprovidersample.demos.nacosdiscoveryconsumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OpenFeignController {

    @Autowired
    private EchoService echoService;

    @GetMapping("/feign/echo/{message}")
    public String feignEcho(@PathVariable String message) throws InterruptedException {
        return echoService.echo(message);
    }
}

通过Postman 请求接口/feign/echo/{message}

服务根据EchoService 中的FeignClient注解,请求会转发至nacos-discovery-provider-sample服务的/echo/{message}接口

至此,Nacos中心搭建完成,注册服务和发现服务注册成功,并且服务支持Fdign请求。

三、创建网关服务

3.1 脚手架创建网关服务

Cloud Native App Initializer (aliyun.com)

项目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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>nacos-gateway-sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos-gateway-sample</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
        <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>3.1.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-gateway-server</artifactId>
            <version>3.1.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            <version>3.1.5</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-gateway-server</artifactId>
            <version>3.1.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-crypto</artifactId>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.alibaba.cloud.nacosgatewaysample.NacosGatewaySampleApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

application.properties

# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=nacos-gateway-sample
server.port=9080
# Nacos认证信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
## 整合sentinel,配置sentinel控制台的地址## 指定控制台的地址,默认端口8080
spring.cloud.sentinel.enabled=true

spring.main.allow-bean-definition-overriding: true
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=192.168.100.115:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=23963657-2b3b-49db-be69-f2517dc3695c
#sentinel控制台
spring.cloud.sentinel.transport.dashboard=192.168.100.115:9090
##限流或者熔断返回
## 一种是redirect,重定向跳转,需要同时配置redirect(跳转的uri)
spring.cloud.sentinel.scg.fallback.mode=response
## 响应的状态
spring.cloud.sentinel.scg.fallback.response-status=200
## 响应体
spring.cloud.sentinel.scg.fallback.response-body='{"code": 200,"message": "限流/熔断,稍后重试!"}'


nacos.gateway.route.config.data-id=nacos-gateway-sample.json
nacos.gateway.route.config.group=DEFAULT_GROUP
murg.gateway.jwt.enabled=true
murg.gateway.jwt.secret=fjkfaf;afa
murg.gateway.jwt.header=Authorization
murg.gateway.jwt.expiration=3000000000000000
murg.gateway.jwt.userParamName=username
murg.gateway.jwt.pwdParamName=password




3.2启动类增加注解@EnableDiscoveryClient

启动服务,登录Nacos注册中心,查看网关服务是否正常注册。

3.3创建Controller接口测试服务是否可以正常访问

JwtAuthController
  /**
     * 测试
     */
    @RequestMapping("/aaa/test")
    public String testaaa(@RequestBody Map<String,String> map) throws InterruptedException {
        Thread.sleep(3000);
        return "aaaaaa";
    }

Postman测试请求/aaa/test

正常返回数据。

4.网关服务作为统一入口,需要配置断言Predicate机制拦截,一个请求在抵达网关层后,首先就要进行断言匹配,在满足所有断言之后才会进入Filter阶段转发至发现服务nacos-discovery-provider-sample,断言配置采用Nacos中心动态配置。

4.1在Nacos中心创建配置

本例采用Json格式,配置详情如下

[
    {
        "id":"nacos-gateway-sample",   
        "order":0, 
        "predicates":[ 
            {
              "args":{
                  "pattern":"/nacos-discovery-provider-sample/**" //路由匹配内容
              },
              "name":"Path"  //通过路由方式拦截
              }
        ],
        "uri":"lb://nacos-discovery-provider-sample",//匹配成功转发到的服务地址
    }
]

注:中心配置内不能有注释,在中心把注释删除

4.2 网关服务application.properties增加配置获取Nacos中心的配置

nacos.gateway.route.config.data-id=nacos-gateway-sample.json
nacos.gateway.route.config.group=DEFAULT_GROUP

启动网关服务,会自动加载Nacos中心中的网关断言配置

4.3动态刷新中心配置

Nacos中心的断言配置如有变化,为了不重新服务,可以创建动态监听配置类,动态刷新

首先需要创建一个网报配置类GatewayConfig,读取Nacos相关访问配置,为配置监听做准备

package com.alibaba.cloud.nacosgatewaysample.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
 * 配置类:读取Nacos相关的配置项,用于配置监听器
 */
@SuppressWarnings("all")
@Configuration
public class GatewayConfig {
        /**
         * 读取配置的超时时间
         */
        public static final long DEFAULT_TIMEOUT = 30000;
        /**
         * Nacos 服务器地址
         */
        public static String NACOS_SERVER_ADDR;
        /**
         * Nacos 命名空间
         */
        public static String NACOS_NAMESPACE;
        /**
         * nacos 配置列表中的dataid
         */
        public static String NACOS_ROUTE_DATE_ID;
        /**
         * nacos分组id
         */
        public static String NACOS_GROUP_ID;
        @Value("${spring.cloud.nacos.discovery.server-addr}")
        public void setNacosServerAddr(String nacosServerAddr) {
            NACOS_SERVER_ADDR = nacosServerAddr;
        }
        @Value("${spring.cloud.nacos.discovery.namespace}")
        public void setNacosNamespace(String nacosNamespace) {
            NACOS_NAMESPACE = nacosNamespace;
        }
        @Value("${nacos.gateway.route.config.data-id}")
        public void setNacosRouteDateId(String nacosRouteDateId) {
            NACOS_ROUTE_DATE_ID = nacosRouteDateId;
        }
        @Value("${nacos.gateway.route.config.group}")
        public void setNacosGroupId(String nacosGroupId) {
            NACOS_GROUP_ID = nacosGroupId;
        }

}

4.4增加配置类事件推送类,动态更新配置DynamicRouteServiceImpl

package com.alibaba.cloud.nacosgatewaysample.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;
import java.util.List;

/**
 * 事件推送 Aware: 动态更新路由网关 Service
 * */
@Slf4j
@Service
@SuppressWarnings("all")
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
    /** 写路由定义 */
    private final RouteDefinitionWriter routeDefinitionWriter;
    /** 获取路由定义 */
    private final RouteDefinitionLocator routeDefinitionLocator;
    /** 事件发布 */
    private ApplicationEventPublisher publisher;
    public DynamicRouteServiceImpl(RouteDefinitionWriter routeDefinitionWriter,
                                   RouteDefinitionLocator routeDefinitionLocator) {
        this.routeDefinitionWriter = routeDefinitionWriter;
        this.routeDefinitionLocator = routeDefinitionLocator;
    }
    @Override
    public void setApplicationEventPublisher(
            ApplicationEventPublisher applicationEventPublisher) {
        // 完成事件推送句柄的初始化
        this.publisher = applicationEventPublisher;
    }

    /**
     * 从nacos读取路由配hi,写道gateway中
     * <h2>增加路由定义</h2>
     * */
    public String addRouteDefinition(RouteDefinition definition) {
        log.info("gateway add route: [{}]", definition);
        // 保存路由配置并发布
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        // 发布事件通知给 Gateway, 同步新增的路由定义
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return "success";
    }
    /**
     * <h2>更新路由</h2>
     * */
    public String updateList(List<RouteDefinition> definitions) {
        log.info("更新网关路由: [{}]", definitions);
        // 先拿到当前 Gateway 中存储的路由定义
        List<RouteDefinition> routeDefinitionsExits =
                routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst();
        if (!CollectionUtils.isEmpty(routeDefinitionsExits)) {
            // 清除掉之前所有的 "旧的" 路由定义
            routeDefinitionsExits.forEach(rd -> {
                deleteById(rd.getId());
                log.info("清除掉之前所有的 旧的 路由定义: [{}]", rd);
            });
        }
        // 把更新的路由定义同步到 gateway 中
        definitions.forEach(definition -> updateByRouteDefinition(definition));
        return "success";
    }

    /**
     * <h2>根据路由 id 删除路由配置</h2>
     * */
    private String deleteById(String id) {
        try {
            log.info("要删除的路由id: [{}]", id);
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
            // 发布事件通知给 gateway 更新路由定义
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "删除成功";
        } catch (Exception ex) {
            log.error("删除网关路由失败: [{}]", ex.getMessage(), ex);
            return "删除失败";
        }
    }

    /**
     * <h2>更新路由</h2>
     * 更新的实现策略比较简单: 删除 + 新增 = 更新
     * */
    private String updateByRouteDefinition(RouteDefinition definition) {

        try {
            log.info("更新网关路由: [{}]", definition);
            this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
        } catch (Exception ex) {
            return "更新失败,没有查到更新的网关路由id: " + definition.getId();
        }

        try {
            this.routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "成功";
        } catch (Exception ex) {
            return "更新路由失败!";
        }
    }
}

4.5项目创建DynamicRouteServiceImplNacos类,用于接收动态刷新。

package com.alibaba.cloud.nacosgatewaysample.config;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 通过Nacos下发的动态配置,监听nacos中路由配置
 */
@Slf4j
@Component
//再另外一个Bean初始化之后再去初始化当前类
@DependsOn({"gatewayConfig"})
@SuppressWarnings("all")
public class DynamicRouteServiceImplNacos {
    private ConfigService configService;
    private final DynamicRouteServiceImpl dynamicRouteService;
 public DynamicRouteServiceImplNacos(DynamicRouteServiceImpl dynamicRouteService){
        this.dynamicRouteService = dynamicRouteService;
    }
 /**
     * 初始化ConfigService
     *
     * @return
     */
    private ConfigService initConfigService() {
        try {
            Properties properties = new Properties();
            properties.setProperty("serverAddr", GatewayConfig.NACOS_SERVER_ADDR);
            properties.setProperty("namespace", GatewayConfig.NACOS_NAMESPACE);
            return configService = NacosFactory.createConfigService(properties);
        } catch (Exception ex) {
            log.error("初始化配置服务:[{}]", ex.getMessage());
            return null;
        }
    }
 /**
     * bean在容器中构造完成之后会立即执行当前的init方法
     * 加载路由信息,注册监听器
     */
    @PostConstruct
    public void init(){
        log.info("网关路由初始化...");
 try {
            //初始化nacos配置客户端
            configService = initConfigService();
            if(configService==null){
                log.error("初始化配置服务异常,配置服务是null!");
                return;
            }
            //通过 nacos config 并指定路由配置路径去获取路由配置
            String configInfo = configService.getConfig(GatewayConfig.NACOS_ROUTE_DATE_ID,
                    GatewayConfig.NACOS_GROUP_ID,
                    GatewayConfig.DEFAULT_TIMEOUT);
            log.info("当前网关配置信息:[{}]:",configInfo);
            //反序列化
            List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
            if(CollectionUtil.isNotEmpty(routeDefinitions)){
                //新增
                for (RouteDefinition routeDefinition : routeDefinitions) {
                    log.info("初始化 路由定义对象 信息:[{}]:",routeDefinition);
                    dynamicRouteService.addRouteDefinition(routeDefinition);
                }

            }
        } catch (Exception e) {
            log.error("网关路由初始化失败:[{}]",e.getMessage());
        }
        //设置监听器
        dynamicRouteServiceImplNacosByListener(GatewayConfig.NACOS_ROUTE_DATE_ID, GatewayConfig.NACOS_GROUP_ID);
    }

    /**
     * 监听 Nacos下发的动态路由配置
     *
     * @param dataId
     * @param group
     */
    private void dynamicRouteServiceImplNacosByListener(String dataId, String group) {
        try {
            //给Nacos config 客户端增加一个监听器
            configService.addListener(dataId, group, new Listener() {
                /**
                 * 自己提供线程池执行操作
                 * @return
                 */
                @Override
                public Executor getExecutor() {
                    return null;
                }

                /**
                 * 监听器收到接收到配置变更信息
                 * @param configInfo nacos 中最新配置信息
                 */
                @Override
                public void receiveConfigInfo(String configInfo) {
                    log.info("接收的配置信息:[{}]", configInfo);
                    List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
                    dynamicRouteService.updateList(routeDefinitions);
                    log.info("更新后的路由配置信息:[{}]", routeDefinitions.toString());
                }
            });
        } catch (Exception e) {
            log.error("监听 Nacos下发的动态路由配置异常:[{}]", e.getMessage());
        }
    }
}

Nacos中心配置变更发布之后,监听更新,调用DynamicRouteServiceImpl updateList 实现更新配置

4.5启动网关服务,Postman调用网关

路由/nacos-discovery-provider-sample/call/echo/nnn

根据断言配置的 "pattern":"/nacos-discovery-provider-sample/**" 匹配到前缀,网关自动转发至

发现服务nacos-discovery-provider-sample的/call/echo/nnn接口,控制台输出nnn

5.网关配置JTWtoken鉴权

为网关配置鉴权过滤器

pom引入以下配置

<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-crypto</artifactId>
        </dependency>

5.1自定义过滤器鉴权规则

创建JwtProperties类

package com.alibaba.cloud.nacosgatewaysample.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@ConfigurationProperties(prefix = "murg.gateway.jwt")
@Component
public class JwtProperties {

    //是否开启JWT,即注入相关的类对象
    private Boolean enabled;
    //JWT密钥
    private String secret;
    //JWT有效时间
    private Long expiration;
    //前端向后端传递JWT时使用HTTP的header名称,前后端要统一
    private String header;
    //用户登录-用户名参数名称
    private String userParamName = "username";
    //用户登录-密码参数名称
    private String pwdParamName = "password";


}

application.properties增加 JWT配置

murg.gateway.jwt.enabled=true
murg.gateway.jwt.secret=fjkfaf;afa
murg.gateway.jwt.header=Authorization
murg.gateway.jwt.expiration=3000000000000000
murg.gateway.jwt.userParamName=username
murg.gateway.jwt.pwdParamName=password

创建JTWUtile,鉴权逻辑
package com.alibaba.cloud.nacosgatewaysample.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenUtil {

    @Resource
    private JwtProperties jwtProperties;


    /**
     * 生成token令牌
     *
     * @param userId 用户Id或用户名
     * @param payloads 令牌中携带的附加信息
     * @return 令token牌
     */
    public String generateToken(String userId,
                                Map<String,String> payloads) {
        int payloadSizes = payloads == null? 0 : payloads.size();

        Map<String, Object> claims = new HashMap<>(payloadSizes + 2);
        claims.put("sub", userId);
        claims.put("created", new Date());

        if(payloadSizes > 0){
            for(Map.Entry<String,String> entry:payloads.entrySet()){
                claims.put(entry.getKey(),entry.getValue());
            }
        }

        return generateToken(claims);
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public Boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            //验证JWT签名失败等同于令牌过期
            return true;
        }
    }

    /**
     * 刷新令牌
     *
     * @param token 原令牌
     * @return 新令牌
     */
    public String refreshToken(String token) {
        String refreshedToken;
        try {
            Claims claims = getClaimsFromToken(token);
            claims.put("created", new Date());
            refreshedToken = generateToken(claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    /**
     * 验证令牌
     *
     * @param token       令牌
     * @param userId  用户Id用户名
     * @return 是否有效
     */
    public Boolean validateToken(String token, String userId) {

        String username = getUsernameFromToken(token);
        return (username.equals(userId) && !isTokenExpired(token));
    }


    /**
     * 从claims生成令牌,如果看不懂就看谁调用它
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + jwtProperties.getExpiration());
        return Jwts.builder().setClaims(claims)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret())
                .compact();
    }

    /**
     * 从令牌中获取数据声明,验证JWT签名
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(jwtProperties.getSecret()).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }
}

创建JWTAuthCheckFilter自定义过滤规则实现JTW鉴权,@Order注解配置自动注入顺序后注入,防止先加载。

/authentication模拟登录注册接口,此接口放行。
package com.alibaba.cloud.nacosgatewaysample.Filter;

import com.alibaba.cloud.nacosgatewaysample.config.JwtProperties;
import com.alibaba.cloud.nacosgatewaysample.config.JwtTokenUtil;
import com.alibaba.fastjson.JSON;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@Configuration
@Order(-101)
public class JWTAuthCheckFilter  implements GatewayFilter, Ordered  {
  @Resource
  private JwtProperties jwtProperties;
  @Resource
  private JwtTokenUtil jwtTokenUtil;



  //将JWT鉴权失败的消息响应给客户端
  private Mono<Void> writeUnAuthorizedMessageAsJson(ServerHttpResponse serverHttpResponse, String message) {
    serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
    Map reuslt=new HashMap();
    reuslt. put("code","401");
    reuslt. put("msg","鉴权失败");
    serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
    DataBuffer dataBuffer = serverHttpResponse.bufferFactory()
            .wrap(JSON.toJSONStringWithDateFormat(reuslt,JSON.DEFFAULT_DATE_FORMAT)
                    .getBytes(StandardCharsets.UTF_8));
    return serverHttpResponse.writeWith(Flux.just(dataBuffer));
  }

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    ServerHttpRequest serverHttpRequest = exchange.getRequest();
    ServerHttpResponse serverHttpResponse = exchange.getResponse();
    String requestUrl = serverHttpRequest.getURI().getPath();

    if(!requestUrl.equals("/authentication")){
      //从HTTP请求头中获取JWT令牌
      String jwtToken = serverHttpRequest
              .getHeaders()
              .getFirst(jwtProperties.getHeader());
      //对Token解签名,并验证Token是否过期
      boolean isJwtNotValid = jwtTokenUtil.isTokenExpired(jwtToken);
      if(isJwtNotValid){ //如果JWT令牌不合法
        return writeUnAuthorizedMessageAsJson(serverHttpResponse,"请先去登录,再访问服务!");
      }
      //从JWT中解析出当前用户的身份(userId),并继续执行过滤器链,转发请求
      ServerHttpRequest mutableReq = serverHttpRequest
              .mutate()
              .header("userId", jwtTokenUtil.getUsernameFromToken(jwtToken))
              .build();
      ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
      return chain.filter(mutableExchange);
    }else{ //如果是登录认证请求,直接执行不需要进行JWT权限验证
      return chain.filter(exchange);
    }
  }

  @Override
  public int getOrder() {
    return 101;
  }
}

自定义过滤器工厂,为网关加载过滤器使用

package com.alibaba.cloud.nacosgatewaysample.Filter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;

@Component
public class JWTAuthCheckFilterFactory extends AbstractGatewayFilterFactory<Object> {
    @Autowired
    JWTAuthCheckFilter jWTAuthCheckFilte;
    @Override
    public GatewayFilter apply(Object config) {
        return jWTAuthCheckFilte;
    }
}

 5.2修改Nacos中心nacos-gateway-sample.json断言配置,增加过滤器

[
    {
        "id":"nacos-gateway-sample",   
        "order":0, 
        "predicates":[ 
            {
              "args":{
                  "pattern":"/nacos-discovery-provider-sample/**" 
              },
              "name":"Path"  
              }
        ],
        "uri":"lb://nacos-discovery-provider-sample",
        "filters":[
            {
                "name":"JWTAuthCheckFilterFactory"
            },
            {
               "name":"StripPrefix", 
               "args":{
                    "parts":"1"
               }     
            }
        ]
    }
]

filters的name指定为自定义工厂JWTAuthCheckFilterFactory。

至此鉴权流程完成。

5.3增加模拟登录接口获取token

/**
     * 使用用户名密码换JWT令牌
     */
    @RequestMapping("/authentication")
    public Mono<Map> authentication(@RequestBody Map<String,String> map){
        //从请求体中获取用户名密码
        String username  = map.get(jwtProperties.getUserParamName());
        String password = map.get(jwtProperties.getPwdParamName());

        if(StringUtils.isEmpty(username)
                || StringUtils.isEmpty(password)){
            return buildErrorResponse("用户名或者密码不能为空");
        }
        //根据用户名(用户Id)去数据库查找该用户
        SysUser sysUser =new SysUser();
        sysUser.setPassword("123456");
        if(sysUser != null){
            //将数据库的加密密码与用户明文密码match
          // boolean isAuthenticated = passwordEncoder.matches("123456",sysUser.getPassword());
            boolean isAuthenticated="123456".equals(sysUser.getPassword());
            if(isAuthenticated){ //如果匹配成功
                //通过jwtTokenUtil生成JWT令牌并return
                return buildSuccessResponse(jwtTokenUtil.generateToken(username,null));
            } else{ //如果密码匹配失败
                return buildErrorResponse("请确定您输入的用户名或密码是否正确!");
            }
        }else{
            return buildErrorResponse("请确定您输入的用户名或密码是否正确!");
        }
    }

返回TOKEN之后,测试请求网关/nacos-discovery-provider-sample/call/echo/nnn

请求Header中带入参数

Authorization:eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjMwMDE3MDk3Nzc5NjIsInN1YiI6ImFkbWluIiwiY3JlYXRlZCI6MTcwOTc3Nzk2MjQ1Nn0.3pw5NVIhiMggFvPEDvUHGX_Iy8EyLT0EQGQS7Xf3bAiu5GhAFHvtSd2_3nsOMzjtAbYT2sk2ffx122cLBXQzPQ

接口正常。去掉Authorization,接口返回

{
    "msg": "鉴权失败",
    "code": "401"
}

四、为服务增加sentinel限流熔断配置

4.1下载按照运行sentinel

下载Sentinel: 官网阿里 Sentinelsentinel 

Sentinel: 官网阿里 Sentinel

启动sentinel指定管理界面端口为9092

nohup java -Dserver.port=9090 -Dcsp.sentinel.dashboard.server=localhost:9092 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.7.jar &

登录管理端 用户名密码默认 sentinel/sentinel

4.2网关服务配置sentinel

application.properties增加

## 整合sentinel,配置sentinel控制台的地址## 指定控制台的地址,默认端口8080
spring.cloud.sentinel.enabled=true
#sentinel控制台
spring.cloud.sentinel.transport.dashboard=192.168.100.115:9090

重启服务,登录sentinel管理端 右侧不显示网关服务

idea增加启动参数

Dcsp.sentinel.dashboard.server=192.168.100.115:9090 -Dproject.name=nacos-natewayn-ample  -Dcsp.sentinel.api.port=9090 -Dcsp.sentinel.app.type=1

或者启动类增加

重新启动显示正常

正常压测

4.3配置限流规则

本例以路由前缀限流

4.3.1选择API管理,新增API规则

新增API分组

匹配串 /nacos-discovery-provider-sample/**

4.3.2新增网关流控规则

限流配置完成

压测截图

4.3.3配置熔断规则

修改限流规则qps1000,停掉发现服务nacos-discovery-provider-sample测试

返回429 熔断生效

4.3.4配置限流和熔断统一返回值,此例是返回值,

 一种是redirect,重定向跳转,需要同时配置redirect(跳转的uri)

application.properties

spring.cloud.sentinel.scg.fallback.mode=response
## 响应的状态
spring.cloud.sentinel.scg.fallback.response-status=200
## 响应体
spring.cloud.sentinel.scg.fallback.response-body="{\"code\":\"429\",\"msg\":\"请求太多了\"}"
重新启动服务nacos-discovery-provider-sample测试限流返回

返回正常200数据。

五、限流熔断配置Nacos持久化

限流和熔断配置在网关服务重启之后会失效,在sentinel中会消失,改为Nacos持久化配置

引入依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
 

5.1Nacos控制台新建一个限流和熔断配置

5.1.1限流配置:

nacos-discovery-provider-sample-flow的json配置,内容如下:

[
  {
    "resource": "nacos-gateway-sample",
    "controlBehavior": 0,
    "count": 100,
    "grade": 1,
    "limitApp": "default",
    "strategy": 0
  }
]

resource:资源名称;和网关的配置nacos-gateway-sample.json id保持一致,指明来源

nacos-gateway-sample


limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。

5.1.1熔断配置

nacos-discovery-provider-sample-degrade

5.2项目配置文件增加nacos的数据源配置

## 整合sentinel,配置sentinel控制台的地址## 指定控制台的地址,默认端口8080
spring.cloud.sentinel.enabled=true
#sentinel控制台
spring.cloud.sentinel.transport.dashboard=192.168.100.115:9090
#指定限流的配置名字
sentinel.config.nacos.data-id-flow=nacos-discovery-provider-sample-flow
#指定熔断的配置名字
sentinel.config.nacos.data-id-degrade=nacos-discovery-provider-sample-degrade
#指定默认分组
sentinel.config.nacos.group=DEFAULT_GROUP

5.3创建config类,初始化加载配置参数属性

package com.alibaba.cloud.nacosgatewaysample.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@SuppressWarnings("all")
@Configuration
public class SentinelConfig {

    /**
     * Nacos 服务器地址
     */
    public static String NACOS_SERVER_ADDR;
    /**
     * Nacos 命名空间
     */
    public static String NACOS_SENTINEL_NAMESPACE;
    /**
     * Nacos 分组id
     */
    public static String NACOS_SENTINEL_GROUP;
    /**
     * nacos 限流配置列表中的dataid
     */
    public static String NACOS_SENTINEL_DATAID_FLOW;
    /**
     * nacos 熔断配置列表中的dataid
     */
    public static String NACOS_SENTINEL_DATAID_DEGRADE;

    @Value("${spring.cloud.nacos.discovery.server-addr}")
    public  void setNacosServerAddr(String nacosServerAddr) {
        NACOS_SERVER_ADDR = nacosServerAddr;
    }

    @Value("${spring.cloud.nacos.discovery.namespace}")
    public  void setNacosSentinelNamespace(String nacosSentinelNamespace) {
        NACOS_SENTINEL_NAMESPACE = nacosSentinelNamespace;
    }

    @Value("${sentinel.config.nacos.group}")
    public  void setNacosSentinelGroup(String nacosSentinelGroup) {
        NACOS_SENTINEL_GROUP = nacosSentinelGroup;
    }

    @Value("${sentinel.config.nacos.data-id-flow}")
    public  void setNacosSentinelDataidFlow(String nacosSentinelDataidFlow) {
        NACOS_SENTINEL_DATAID_FLOW = nacosSentinelDataidFlow;
    }
    @Value("${sentinel.config.nacos.data-id-degrade}")

    public  void setNacosSentinelDataidDegrade(String nacosSentinelDataidDegrade) {
        NACOS_SENTINEL_DATAID_DEGRADE = nacosSentinelDataidDegrade;
    }
}

5.4创建限流熔断工厂类,加载sentinel相关内容。加载限流、熔断规则,自定义限流熔断错误返回处理,

限流自定义返回状态码429

熔断自定义返回状态码504

package com.alibaba.cloud.nacosgatewaysample.Filter;

import com.alibaba.cloud.nacosgatewaysample.config.SentinelConfig;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.PropertyKeyConst;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;

@Component
//再另外一个Bean初始化之后再去初始化当前类
@DependsOn({"sentinelConfig"})

//再另外一个Bean初始化之后再去初始化当前类
public class SentinelFactory implements BlockRequestHandler {
    private  List<ViewResolver> viewResolvers;
    private  ServerCodecConfigurer serverCodecConfigurer;
    public SentinelFactory(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                           ServerCodecConfigurer serverCodecConfigurer) {
        //视图解析
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, SentinelConfig.NACOS_SERVER_ADDR);
        properties.put(PropertyKeyConst.NAMESPACE, SentinelConfig.NACOS_SENTINEL_NAMESPACE);
        //加载nacos配置中心的限流规则
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>
                (properties, SentinelConfig.NACOS_SENTINEL_GROUP, SentinelConfig.NACOS_SENTINEL_DATAID_FLOW,
                        source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                        }));
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());

        //加载nacos配置中心的熔断规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleDataSource = new NacosDataSource<>
                (properties, SentinelConfig.NACOS_SENTINEL_GROUP, SentinelConfig.NACOS_SENTINEL_DATAID_DEGRADE,
                        source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {
                        }));
        DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty());

    }
    //异常处理接收Handler
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    //这是执行sentinel规则的拦截器,Gateway路由之前会先执行这个拦截器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @Override
    public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
        String path = serverWebExchange.getRequest().getPath().pathWithinApplication().value();
        Map map = new HashMap();
        map.put("error","Unauthorized");
        map.put("path",path);
        map.put("status",HttpStatus.TOO_MANY_REQUESTS.value());
        map.put("imestamp",new Date());
        if (throwable instanceof FlowException){
            map.put("message","服务被限流");
            return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS.value()).contentType(MediaType.APPLICATION_JSON).body(Mono.just(map), Map.class);

        } else if (throwable instanceof DegradeException) {
            map.put("message","服务被熔断降级");
            return ServerResponse.status(HttpStatus.GATEWAY_TIMEOUT.value()).contentType(MediaType.APPLICATION_JSON).body(Mono.just(map), Map.class);
        }
         return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value()).contentType(MediaType.APPLICATION_JSON).body(Mono.just(map), Map.class);

    }

}

5.5 Postman测试

重启服务调用接口/nacos-discovery-provider-sample/call/echo/nnn测试

5.5.1限流生效返回

5.5.2熔断生效返回

PS:按照4.3动态刷新配置 创建监听实现动态加载,由于太懒没弄,感兴趣的同学可以自行加一下。

六 结束语

以上就是基于Nacos中心部署spring cloud的基础内容,包含JTW鉴权,网关、feign、sentinel限流熔断等基本内容,自学一周大概了解一下内容。

Nacos 文档地址

Nacos官方文档地址:Nacos 快速开始

github下载地址:Releases · alibaba/nacos · GitHub
gitee下载地址: Nacos 发行版 - Gitee.com

sentinel 文档地址:

Read Me - 《Sentinel v1.8 文档手册》 - 书栈网 · BookStackRead Me - 《Sentinel v1.8 文档手册》 - 书栈网 · BookStackRead Me - 《Sentinel v1.8 文档手册》 - 书栈网 · BookStack

脚手架地址

Cloud Native App Initializer (aliyun.com)

祝福语:

同窗共学,亦为知己。让我们携手共进,相互勉励,成为更好的自己。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1516747.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多媒体操作流程

&#xff01; 从左至右依次为&#xff1a;话筒、投影遥控器、ppt演讲笔、幕布升降遥控器、无线投屏连接器 主机箱 投影仪 二、操作流程 1、打开主机电源&#xff1a;最下面两台设备的开关打开 2、打开投影仪&#xff1a;用投影遥控器对准投影仪按开机键&#xff08;如无需用到…

嵌入式系统中端口号的理解与分析

每当看到有人的简历上写着熟悉 tcp/ip, http 等协议时, 我就忍不住问问他们: 你给我说说, 端口是啥吧! 可惜, 很少有人能说得让人满意... 所以这次就来谈谈端口(port), 这个熟悉的陌生人. 在此过程中, 还会谈谈间接层, naming service 等概念, IoC, 依赖倒置等原则以及 TCP 协议…

Java开发从入门到精通(八):Java的面向对象编程OOP:封装、继承、多态

Java大数据开发和安全开发 &#xff08;一&#xff09;Java的封装1.1 什么是封装1.1.1 封装的设计规范1.1.2 代码层面如何控对象的成员公开或隐藏? 1.2 JavaBean(实体类)1.2.1创建实体类1.2.2 实体类有啥应用场景?1.2.3 实体类总结 1.3 static关键字1.3.1 static修饰成员变量…

Groovy语言

1 Groovy介绍 1.1 Groovy介绍 Groovy是一种编程语言&#xff0c;它结合了Java的强大功能和脚本语言的简洁性。它具有动态类型、易读的语法、与Java的紧密集成、脚本编程能力、强大的闭包等特点。 1.2 Groovy SQL介绍 Groovy SQL是 Groovy 编程语言的一部分&#xff0c;用于…

Windows,MacOS,Linux下载python并配置环境图文讲解

Windows 打开python官网 点击download 点击黄色按钮 另存为 打开文件 全选 配置安装路径 安装中 关闭路径长度限制 完成 验证 同时按住winr(win就是空格键左边的东西) 输入cmd 键入python,如果出现版本(红框)即安装成功 MacOS 同理打开python官网 点击最新版本 拖…

LeetCode - 和可被K整除的子数组

974. 和可被 K 整除的子数组 题目描述&#xff1a;一个连续的区间可以被k整除。 一个连续的区间可以被k整除&#xff0c;如果用前缀和就是(arr[r] - arr[l - 1]) / k 0;当然&#xff0c;在计算机语言里面&#xff0c;用除法判断结果是否为0不行&#xff0c;需要用%,(arr[r] -…

使用Make Sense为YOLOv5-5.0制作自定义数据集及应用

详细可参考官方文档&#xff1a; ​​​​​​​培训自定义数据 -Ultralytics YOLOv8 文档 0.使用Make Sense标注数据集 我们在网页上随机下载五张图片用以演示如何使用Make Sense工具进行标注。下载图片存放在指定文件夹中&#xff0c;如下图所示。 Make Sense网页链接&…

【自监督学习算法】

【自监督学习算法】 什么是自监督学习 (SSL) 算法? 自监督学习 (SSL)是一种不断发展的机器学习技术,旨在解决过度依赖标记数据带来的挑战。多年来,使用机器学习方法构建智能系统在很大程度上依赖于高质量的标记数据。因此,高质量注释数据的成本是整个训练过程中的主要…

【Flutter 面试题】await for 如何使用?

【Flutter 面试题】await for 如何使用&#xff1f; 文章目录 写在前面解答补充说明完整代码示例运行结果详细说明 写在前面 &#x1f64b; 关于我 &#xff0c;小雨青年 &#x1f449; CSDN博客专家&#xff0c;GitChat专栏作者&#xff0c;阿里云社区专家博主&#xff0c;51…

01 数据结构引入 和 顺序表

阅读引言&#xff1a; 从本文开始给大家带来我在复习过程中写的数据结构的代码&#xff0c; 分享给需要的同学 一、数据结构引入 1.数据结构解决什么问题 数据结构可以将杂乱无章的数据管理起来&#xff0c; 提高数据的访问效率 计算机处理的对象&#xff08;数据&#xff09…

Jsp在Javaweb中扮演什么角色?

1.什么是Jsp JSP&#xff08;Java Server Pages&#xff0c;Java 服务器页面&#xff09;是一种动态网页技术&#xff0c;它允许在 HTML 页面中嵌入 Java 代码&#xff0c;并由 Web 服务器在请求页面时动态生成 HTML 页面。JSP 通常用于创建动态 Web 内容&#xff0c;如交互式表…

幻兽帕鲁服务器搭建运行遇到的问题详解

幻兽帕鲁服务器搭建运行遇到的问题详解 大家好我是艾西&#xff0c;在个人玩家搭建幻兽帕鲁时会遇到些小问题。今天艾西给把这些问题全部罗列出来并给到解决方案&#xff0c;需要的小伙伴可以通过本篇文章进行详细的解读。 服务器搭建无法进入/服务器搭建失败&#xff1a;这个出…

Android Kotlin知识汇总(一)编程语言

在 2019 年 Google I/O 大会上宣布今后将优先采用 Kotlin 进行 Android 开发。Kotlin 是一种富有表现力且简洁的编程语言&#xff0c;不仅可以减少常见代码错误&#xff0c;还可以轻松集成到现有应用中。如果您想构建 Android 应用&#xff0c;建议您从 Kotlin 开始着手&#x…

ComfyUI中如何自动获取当前日期

之前也介绍过&#xff0c;生成的文件夹是可以直接获取当前的日期&#xff0c;作为文件名进行保存的。但是如果你想获得当前的日期&#xff0c;然后写入到图片上的话&#xff0c;这个需求可能比较小众&#xff0c;查了半天资料&#xff0c;找到一个节点刚好能适配这个需求&#…

软件设计师16--段页式存储

软件设计师16--段页式存储 考点1&#xff1a;页式存储存储管理 - 页式存储组织存储管理 - 页面置换算法例题&#xff1a; 考点2&#xff1a;段式存储存储管理 - 段式存储组织例题&#xff1a; 考点1&#xff1a;页式存储 存储管理 - 页式存储组织 页式存储&#xff1a;将程序…

一体机电脑辐射超标整改

电脑一体机是目前台式机和笔记本电脑之间的一个新型的市场产物&#xff0c;它将主机部分、显示器部分整合到一起的新形态电脑&#xff0c;该产品的创新在于内部元件的高度集成。随着无线技术的发展&#xff0c;电脑一体机的键盘、鼠标与显示器可实现无线链接&#xff0c;机器只…

阿里云最新优惠券领取入口及使用指南

随着云计算技术的普及与服务升级&#xff0c;阿里云作为全球领先的云服务提供商&#xff0c;不断推出各类优惠活动以降低企业和个人用户上云成本。本文将详细介绍阿里云最新优惠券的领取入口以及如何有效地使用优惠券。 一、阿里云优惠券领取入口 阿里云优惠券一般可以通过阿里…

Nodejs 第五十四章(net)

net模块是Node.js的核心模块之一&#xff0c;它提供了用于创建基于网络的应用程序的API。net模块主要用于创建TCP服务器和TCP客户端&#xff0c;以及处理网络通信。 TCP&#xff08;Transmission Control Protocol&#xff09;是一种面向连接的、可靠的传输协议&#xff0c;用于…

【DataWhale学习】用免费GPU线上跑chatGLM项目实践

用免费GPU线上跑chatGLM项目实践 ​ DataWhale组织了一个线上白嫖GPU跑chatGLM与SD的项目活动&#xff0c;我很感兴趣就参加啦。之前就对chatGLM有所耳闻&#xff0c;是去年清华联合发布的开源大语言模型&#xff0c;可以用来打造个人知识库什么的&#xff0c;一直没有尝试。而…

【开源】SpringBoot框架开发公司货物订单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供应商管理模块2.4 订单管理模块 三、系统展示四、核心代码4.1 查询供应商信息4.2 新增商品信息4.3 查询客户信息4.4 新增订单信息4.5 添加跟进子订单 五、免责说明 一、摘要 1.1 项目…