一、gateway 网关子模块创建
1、右键项目 - New - Module
image.png2、bootstrap.yml
server:
port: 8008
servlet:
context-path:
tomcat:
uri-encoding: utf-8
spring:
application:
name: gateway #唯一名称
profiles:
active: dev # 运行环境
freemarker:
check-template-location: false
prefer-file-system-access: false
logging: # logback 配置
path: /usr/local/alibaba/logs/${spring.application.name} # 保存日志文件目录路径
level: # 日志级别
org.springframework.web: DEBUG # 配置spring web日志级别
3、bootstrap-dev.yml
spring:
cloud:
sentinel:
transport:
port: 8719 # 默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
dashboard: 127.0.0.1:9090 # [sentinel]管理控制台地址
eager: true
nacos:
discovery:
server-addr: 192.168.0.119:8848 # [nacos]的访问地址,根据上面准备工作中启动的实例配置
namespace: 11ba48cc-9931-4760-bf58-7d3e2c99629c # [nacos]test命名空间的ID
config:
server-addr: 192.168.0.119:8848 # [nacos]的访问地址,根据上面准备工作中启动的实例配置
namespace: 11ba48cc-9931-4760-bf58-7d3e2c99629c # [nacos]test命名空间的ID
group: DEFAULT_GROUP # [nacos]默认分组就是DEFAULT_GROUP,如果使用默认分组可以不配置
file-extension: yml # [nacos]默认properties
gateway:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
globalcors: # 网关cors跨域设置
cors-configurations:
'[/**]': # gateway网关上所有的uri都应用下面的跨域设置
allowed-credentials: true # 允许携带认证信息
allowed-origins:
- "*" # 允许所有来源进行跨域访问
allowed-headers: "*" # 允许跨域请求里的head字段,设置为*是全部
allowed-methods: # 允许跨域的方法
- GET
- POST
- PUT
- DELETE
- OPTIONS
max-age: 3600
routes:
# oauth
- id: oauth # 当前路由的标识, 要求唯一
uri: lb://oauth # lb指的是从 nacos 中按照名称获取微服务,并遵循负载均衡策略
predicates: # 断言(就是路由转发要满足的条件)
- Path=/oauth/** # 当请求路径满足Path指定的规则时,才进行路由转发
# api
- id: api # 我们⾃定义的路由 ID,保持唯⼀
uri: lb://api # ⽬标服务地址(部署多实例)
# gateway⽹关从服务注册中⼼获取实例信息然后负载后路由
# 断⾔:路由条件,Predicate 接受⼀个输⼊参数,返回⼀个布尔值结果。该接⼝包含多种默认⽅法来将 Predicate 组合成其他复杂的逻辑(⽐如:与,或,⾮)。
predicates:
- Path=/api/**
#filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
# - StripPrefix=1 # 转发之前去掉1层路径
# miniapp-oauth
- id: miniapp-oauth
uri: lb://miniapp-oauth
predicates:
- Path=/miniapp-oauth/**
# miniapp-api
- id: miniapp-api
uri: lb://miniapp-api
predicates:
- Path=/miniapp-api/**
#filters:
# - StripPrefix=1
# callback
- id: callback
uri: lb://callback
predicates:
- Path=/callback/**
#filters:
# - StripPrefix=1
4、pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.aydan</groupId>
<artifactId>ali-cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>gateway</artifactId>
<packaging>jar</packaging>
<name>gateway</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<!--GateWay 网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--引入webflux-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Actuator可以帮助你监控和管理Spring Boot应⽤-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--nacos config client 依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--链路追踪-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel 流量监控控制台 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel 网关限流 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel 持久化 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!--打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5、GatewayApplication.java
package com.aydan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.TimeZone;
/**
* @Author ds
* @Date 2023/7/27
*/
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
ConfigurableApplicationContext application = SpringApplication.run(GatewayApplication.class, args);
Environment environment = application.getEnvironment();
String applicationName = environment.getProperty("spring.application.name");
String port = environment.getProperty("server.port");
String contextPath = environment.getProperty("server.servlet.context-path");
System.out.println("---------------------------------------------------------->");
System.out.println(" :: ServletInitializer 启动:" + applicationName);
String localHost;
try {
localHost = InetAddress.getLocalHost().getHostAddress();
System.out.println("\t\t http://" + localHost + ":" + port + "" + contextPath + "");
System.out.println("\t\t http://127.0.0.1:9090");
} catch (UnknownHostException e) {
System.out.println("\t\t " + e.getMessage());
e.printStackTrace();
}
System.out.println("<----------------------------------------------------------");
}
}
6、CorsConfig.java
package com.aydan.config;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
/**
* @Author ds
* @Date 2023/7/27
*/
@Component
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
/**
* 返回一个CorsWebFilter ,构造其中需要传入连个形参,均为接口,可以直接new 接口
* 是借口可以使用它的实现类来处理
*/
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
//构建CorsConfiguration
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedMethod(HttpMethod.DELETE);
corsConfiguration.addAllowedMethod(HttpMethod.PUT);
corsConfiguration.addAllowedMethod(HttpMethod.OPTIONS);
corsConfiguration.addAllowedMethod(HttpMethod.GET);
corsConfiguration.addAllowedMethod(HttpMethod.POST);
corsConfiguration.addAllowedMethod(HttpMethod.HEAD);
corsConfiguration.addAllowedHeader("*");
// 允许cookie跨域
corsConfiguration.setAllowCredentials(true);
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(urlBasedCorsConfigurationSource);
}
}
7、GatewayConfig.java
package com.aydan.config;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
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.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.aydan.core.BaseResponse;
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.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @Author ds
* @Date 2023/7/27
*/
@Configuration
public class GatewayConfig {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfig(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolvers;
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* 初始化一个限流的过滤器
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
/**
* 配置初始化的限流参数
*/
@PostConstruct
public void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
// 分别给指定的资源 配置限流
// GatewayFlowRule 资源名称,对应路由id setCount 限流阈值 setIntervalSec 统计时间窗口,单位是秒,默认是 1 秒
// 简单来说就是 如果Count=1 IntervalSec=1 那么一秒钟只允许访问一次 (用于测试)
// 如果Count=5 IntervalSec=2 那么2秒钟只允许访问5次 (用于测试)
// 如果Count=60 IntervalSec=3 那么3秒钟只允许访问60次 也就是1秒20次 一般用于生产(一般够了)
// 如果是商城网站那么Count和IntervalSec 这个需要计算平均每天每秒最大的请求量是多少然后在设置
int count = 50;
int intervalSec = 5;
// 设置指定路由的限流
rules.add(new GatewayFlowRule("api").setCount(count).setIntervalSec(intervalSec));
rules.add(new GatewayFlowRule("miniapp-api").setCount(count).setIntervalSec(intervalSec));
GatewayRuleManager.loadRules(rules);
}
/**
* 配置限流的异常处理器
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
/**
* 自定义限流异常页面
*/
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {
BaseResponse response = new BaseResponse();
response.setStatus(429);
response.setMessage("请求过于频繁");
return ServerResponse.status(HttpStatus.OK).
contentType(MediaType.APPLICATION_JSON_UTF8).
body(BodyInserters.fromObject(response));
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
8、BaseResponse.java
package com.aydan.core;
import lombok.Data;
import java.io.Serializable;
/**
* @Author ds
* @Date 2023/7/27
*/
@Data
public class BaseResponse implements Serializable {
private static final long serialVersionUID = 1L;
protected int status;
protected String message;
protected String recvTime;
protected String respTime;
}
9、Nacos配置
image.pngimage.png
参考:
https://www.jianshu.com/p/552416053ff1
网友评论