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

一、安装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@Autowiredpublic RestTemplate restTemplate;@LoadBalanced@Beanpublic 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 {@Autowiredprivate 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=truespring.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;}@Overridepublic 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方法* 加载路由信息,注册监听器*/@PostConstructpublic 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*/@Overridepublic Executor getExecutor() {return null;}/*** 监听器收到接收到配置变更信息* @param configInfo nacos 中最新配置信息*/@Overridepublic 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 {@Resourceprivate 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  {@Resourceprivate JwtProperties jwtProperties;@Resourceprivate 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));}@Overridepublic 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);}}@Overridepublic 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> {@AutowiredJWTAuthCheckFilter jWTAuthCheckFilte;@Overridepublic 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令牌并returnreturn 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();}@Overridepublic 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.mzph.cn/news/744478.shtml

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

相关文章

mavros话题订阅后无法触发回调

前提 使用树莓派与pixhawk通信&#xff0c;安装好mavros&#xff0c;树莓派与pixhawk串口连接 启动节点mavros节点后&#xff0c;通过ros2 topic list可以查看到一系列话题 查看话题的类型可以去wiki mavros中查看 或者使用ros2 topic info 话题名称可以查看到 问题描述 订阅…

没有硬件基础可以学单片机吗?

没有硬件基础可以学单片机吗&#xff1f; 在开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些 电气工程师学习方法和资料&#xff0c;让我不断提升自己&#xff0c…

一.算法基础

目录 1.算法基础 2.算法概念 3.时间复杂度--用来评估算法运行效率的一个式子 如何简单快速的判断算法复杂度? 4.空间复杂度 1.算法基础 2.算法概念 --静态动态 3.时间复杂度--用来评估算法运行效率的一个式子 ----一个单位!!! 1-在什么配置下运行(机器) 2-问题的规模…

①【Docker】Linux安装Docker容器教程

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ ①【Docker】Linux安装Docker容器教程 &#x…

slf4j 打印当前类和方法

spring initializr 自动包含依赖,也可以在 pom.xml 文件中添加 slf4j 的依赖,指定版本 例如我这边默认版本是 1.7.36 通过添加依赖修改版本为 1.7.32<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version…

JavaScript数组sort自定义排序

背景 刷LeetCode时&#xff0c;遇到一道简单的数组排序题&#xff1a; 问题 想着直接用js的数组sort自定义排序即可&#xff0c;奈何测试用例运行总是不通过&#xff0c;返回的一直都是原数组。 代码排查 复制代码到Firefox浏览器控制台运行&#xff0c;结果输出的是正确结果&a…

Nginx的日志怎么看,在哪看,access.log日志内容详解

Nginx 的日志文件通常位于服务器的文件系统中&#xff0c;具体位置可能因配置而异。以下是查看 Nginx 日志的几种方法&#xff1a; 1、查看访问日志&#xff1a;在默认配置下&#xff0c;Nginx 的访问日志文件路径为 /var/log/nginx/access.log。您可以通过命令 sudo cat /var…

3、设计模式之工厂模式1(Factory)

工厂模式是什么&#xff1f;     工厂模式是一种创建者模式&#xff0c;用于封装和管理对象的创建&#xff0c;屏蔽了大量的创建细节&#xff0c;根据抽象程度不同&#xff0c;主要分为简单工厂模式、工厂方法模式以及抽象工厂模式。 简单工厂模式 看一个具体的需求 看一个…

Spring Boot 集成 WebSocket 实例 | 前端持续打印远程日志文件更新内容(模拟 tail 命令)

这个是我在 CSDN 的第一百篇原则博文&#xff0c;留念&#x1f60e; #1 需求说明 先说下项目结构&#xff0c;后端基于 Spring Boot 3&#xff0c;前端为 node.js 开发的控制台程序。现在希望能够在前端模拟 tail 命令&#xff0c;持续输出后端的日志文件。 #2 技术方案 #2.…

蓝桥杯练习:景区导游

视频 UP主的博客 暴力做法&#xff0c;能过 42%数据。如果内存开 1e410 能过 40%&#xff0c;如果开 2e510就只能过 25% #include<bits/stdc.h> #define int long long #define endl \n const int N 1e410; using namespace std; //存两点的距离 typedef pair<in…

豆瓣书影音存入Notion

使用Python将图书和影视数据存放入Notion中。 &#x1f5bc;️介绍 环境 Python 3.10 &#xff08;建议 3.11 及以上&#xff09;Pycharm / Vs Code / Vs Code Studio 项目结构 │ .env │ main.py - 主函数、执行程序 │ new_book.txt - 上一次更新书籍 │ new_video.…

全球首个 AI 软件工程师 Devin它来了!

如果您想每日获取AI最新新闻,欢迎关注文章底部的公众号 Cognition AI 发布 AI 软件工程师 Devin 初创公司 Cognition 近日发布公告,宣布推出全球首个 AI 软件工程师 Devin,并号称会彻底改变人类构建软件的方式。Devin 擅长长期推理能力,可以自主规划和完成软件项目,并在此…

ElasticSearch深度分页问题如何解决

文章目录 概述解决方法深度分页方式from size深度分页之scrollsearch_after 三种分页方式比较 概述 Elasticsearch 的深度分页问题是指在大数据集上进行大量分页查询时可能导致的性能下降和资源消耗增加的情况。这种情况通常发生在需要访问大量数据的情形下&#xff0c;比如用…

【OpenBayes 官方教程】数据读写绑定功能

本教程主要为大家介绍怎样在 OpenBayes 上进行数据的绑定以及如何使用已绑定的数据&#xff0c;新朋友点击下方链接注册后&#xff0c;即可获得 4 小时 RTX 4090 5 小时 CPU 的免费使用时长哦&#xff01; 注册链接 注册 - OpenBayes 首先&#xff0c;创建一个新的容器。 然…

非光滑非凸规划

目录 一&#xff0c;非凸函数的近端梯度下降 1&#xff0c;凸函数的近端梯度下降 2&#xff0c;非凸函数的近端梯度下降 一&#xff0c;非凸函数的近端梯度下降 1&#xff0c;凸函数的近端梯度下降 参考近端梯度下降 2&#xff0c;非凸函数的近端梯度下降

11.17定时调度(血干JAVA系类)

定时调度 11.17.1 Timer 类11.17.2 TimerTask 类11.17.3范例——定时操作【例11.52】建立TimerTask的子类【例11.53】建立测试类&#xff0c;进行任务调度 11.17.1 Timer 类 11.17.2 TimerTask 类 要想执行具体的任务&#xff0c;则必须使用Tim erTas k类。Tim erTas k类是一个…

工业界真实的推荐系统(小红书)-离散特征处理、矩阵补充模型、双塔模型

课程特点&#xff1a;系统、清晰、实用&#xff0c;原理和落地经验兼具 b站&#xff1a;https://www.bilibili.com/video/BV1HZ421U77y/?spm_id_from333.337.search-card.all.click&vd_sourceb60d8ab7e659b10ea6ea743ede0c5b48 讲义&#xff1a;https://github.com/wangsh…

vid2vid(Video-to-Video Synthesis)论文详读和理解

论文&#xff1a;https://arxiv.org/abs/1808.06601 代码&#xff1a;https://github.com/NVIDIA/vid2vid

北斗卫星推动数智油田建设

北斗卫星推动数智油田建设 中国石油大港油田采油三厂深入推动北斗智能终端在智能巡检、安全监督、油井导航、坐标测绘等多场景应用&#xff0c;实现了人工查井向智能巡检的变革。截至2月下旬&#xff0c;场景覆盖率达100%&#xff0c;高效助推大港南部“双高”老区数智油田建设…

史上最牛Linux详解,看完直接带你由入门到精通!

第一部分&#xff1a;入门 第二部分&#xff1a;成为一名linux高级用户&#xff1a; 第三部分&#xff1a;成为一名Linux系统管理员 第四部分&#xff1a;成为一名Linux服务器管理员 因文章内容过长&#xff0c;目录先放这些&#xff0c;因为接下来还要放一些内容 小编13年上海…