一、搭建环境
1、创建父项目 initializer
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lian</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway</name>
<description>网关</description>
<packaging>pom</packaging>
<modules>
<module>eureka1</module>
<module>eureka2</module>
<module>product-server</module>
<module>order-service</module>
<module>gatewaytest</module>
</modules>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
<spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
eureka搭建
2、搭建eureka1环境
配置eureka1和eureka2,搭建eureka集群
eureka1 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>
<parent>
<groupId>com.lian</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.lian</groupId>
<artifactId>eureka1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
eureka1 yaml
spring:
application:
name: eureka
#端口
server:
port: 7001
#配置eureka注册中心
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka7002.com:7002/eureka/
instance:
hostname: eureka7001.com #主机名
prefer-ip-address: false #是否使用ip地址注册
instance-id:
eureka1 主启动
开启eureka注解
@EnableEurekaServer
@SpringBootApplication
public class Eureka7001 {
public static void main(String[] args) {
SpringApplication.run(Eureka7001.class, args);
}
}
eureka 测试
3、商品服务
注册到eureka服务中心
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>
<parent>
<artifactId>gateway</artifactId>
<groupId>com.lian</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.lian</groupId>
<artifactId>product-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>product-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
yml
server:
port: 7070
spring:
application:
name: product-server
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
主启动
@EnableEurekaClient
@SpringBootApplication
public class ProductServerApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServerApplication.class, args);
}
}
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product implements Serializable {
private Integer id;
private String productName;
private Integer productNum;
private Double productPrice;
}
service接口
public interface ProductService {
//根据主键查询商品
Product selectProductById(Integer id);
}
service实现类
@Service
public class ProductServiceImpl implements ProductService {
@Override
public Product selectProductById(Integer id) {
return new Product(1,"egg",2,2.0);
}
}
测试
controller
@RestController
public class ProductController {
@Autowired
ProductService productService;
@GetMapping("/product/{id}")
public Product selectProductById(@PathVariable("id") Integer id){
return productService.selectProductById(id);
}
}
4、订单服务
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>
<parent>
<artifactId>gateway</artifactId>
<groupId>com.lian</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.lian</groupId>
<artifactId>order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>order-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
yaml
server:
port: 9090
spring:
application:
name: order-service
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
主启动
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
实体类 order
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order implements Serializable {
private Integer id;
private String orderNo;
private String orderAddress;
private Double totalPrice;
private List<Product> productList; //商品列表
}
feign远程调用商品服务
@Component
@FeignClient(value = "PRODUCT-SERVER",fallbackFactory = ProductServiceFallbackFactory.class)
public interface ProductServiceFeign {
@GetMapping("/product/{id}")
Product selectProductById(@PathVariable("id") Integer id);
}
feign备胎类
@Component
public class ProductServiceFallbackFactory implements FallbackFactory<ProductServiceFeign> {
//获取日志
Logger logger = LoggerFactory.getLogger(ProductServiceFallbackFactory.class);
@Override
public ProductServiceFeign create(Throwable throwable) {
return new ProductServiceFeign() {
@Override
public Product selectProductById(Integer id) {
logger.error("服务的方法出现异常,异常信息如下" + throwable);
return new Product(id,"备胎托底",2,100.0);
}
};
}
}
service 接口
public interface OrderService {
//根据主键查询订单
Order selectOrderById(Integer id);
}
service 接口实现类
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
ProductServiceFeign productServiceFeign;
@Override
public Order selectOrderById(Integer id) {
return new Order(1,"10","namoban",10.0,
Arrays.asList(productServiceFeign.selectProductById(1)));
}
}
controller
@RestController
public class OrderController {
@Autowired
OrderService orderService;
@GetMapping("/order/{id}")
public Order selectOrderById(@PathVariable("id") Integer id){
return orderService.selectOrderById(id);
}
}
测试
二、nginx实现api网关
1、下载地址
http://nginx.org/en/download.html
2、启动nginx
下载windows版nginx,双击 nginx.exe
,nginx所在目录路径必须是英文,中文启动会有异常,端口默认是80
3、配置文件
nginx.conf 配置文件解释
只要请求nginx,默认就会跳转到 index.html页面
# 1、http://localhost:80/ == location /,跳转到nginx页面
# 2、转到 html 下的 index.html 页面
location / {
root html; #根目录root
index index.html index.htm; #跳转页面 index.html
}
index.html 默认配置
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
4、配置路由规则
# 1、location / = http://localhost:80/,跳转到nginx页面
# 2、转到 html 下的 index.html 页面
location / {
root html;
index index.html index.htm;
}
#配置路由规则
#路由到商品服务
# location /api/product = http://localhost:80/api/product
location /api/product {
proxy_pass http://localhost:7070/;
}
# 路由到订单服务
# location /api/order = http://localhost:80/api/order
# http://localhost:80/api/order = http://localhost:9090/
# http://localhost:9090/order/1 = http://localhost:80/api/order/order/1
location /api/order {
proxy_pass http://localhost:9090/;
}
5、重启nginx
6、测试nginx网关
测试商品服务
测试订单服务
三、gateway
1、gateway概念
//gateway官网
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
2、工作原理
The following diagram provides a high-level overview of how Spring Cloud Gateway works:
客户端向Spring云网关发出请求。如果网关处理程序映射确定一个请求与路由匹配,它将被发送到网关Web处理程序。这个处理程序通过特定于请求的筛选器链运行请求。过滤器被虚线分割的原因是,过滤器可以在发送代理请求之前和之后运行逻辑。执行所有“pre”过滤逻辑。然后发出代理请求。发出代理请求后,运行“post”筛选器逻辑。
3、搭建网关服务
创建项目
添加依赖
<?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.lian</groupId>
<artifactId>gateway-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<!--父类-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置文件
server:
port: 9000
spring:
application:
name: gateway-server
主启动
@SpringBootApplication
public class GtewayServerApplication{
public static void main(String[] args) {
SpringApplication.run(GtewayServerApplication.class,args);
}
}
配置跨域配置
网关测试过,不添加跨域配置也正常运行
package com.lian.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.cors.reactive.CorsWebFilter;
/**
* 解决跨域问题
* 跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。
* 所谓同源是指,域名,协议,端口均相同,不明白没关系,举个栗子:
*
* http://www.123.com/index.html 调用 http://www.123.com/server.php (非跨域)
*
* http://www.123.com/index.html 调用 http://www.456.com/server.php (主域名不同:123/456,跨域)
*
* http://abc.123.com/index.html 调用 http://def.123.com/server.php (子域名不同:abc/def,跨域)
*
* http://www.123.com:8080/index.html 调用 http://www.123.com:8081/server.php (端口不同:8080/8081,跨域)
*
* http://www.123.com/index.html 调用 https://www.123.com/server.php (协议不同:http/https,跨域)
*
* 请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。
* 浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
*/
@Configuration
public class GatewayCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
配置路由规则
server:
port: 9000
spring:
application:
name: gateway-server
cloud:
gateway:
routes: #路由规则
- id: product-server #路由id,唯一
uri: http://localhost:7070/ #目标 uri,路由到微服务的地址
predicates: #断言(判断条件)
- Path=/product/** #匹配对应 url 的请求,将匹配到的请求追加在目标 uri 之后
filters:
- RewritePath=/(?<segment>/?.*),/$\{segment}
- id: order-server #路由id,唯一
uri: http://localhost:9090/ #目标 uri,路由到微服务的地址
predicates: #断言(判断条件)
- Path=/order/** #匹配对应 url 的请求,将匹配到的请求追加在目标 uri 之后
filters:
- RewritePath=/(?<segment>/?.*),/$\{segment}
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
四、路由规则
1、path
2、Query
配置文件
- id: product-server #路由id,唯一
uri: http://localhost:7070/ #目标 uri,路由到微服务的地址
predicates: #断言(判断条件)
- Query=token #指定参数名,不指定参数值
- Query=token,pipi #指定参数名,指定参数值,参数值必须是 pipi 才可通过网关访问
测试
必须携带参数才行,参数值任意均可 token=xxx
也可以规定参数必须搭配指定的参数值
3、Method
网关配置断言规则,必须是 post 请求
- id: product-server #路由id,唯一
uri: http://localhost:7070/ #目标 uri,路由到微服务的地址
predicates: #断言(判断条件)
- Method=POST #匹配任意 POST 请求
商品服务get测试,正常
商品服务post测试,正常
网关测试get请求,因为网关配置了断言规则,要求请求为 post,所以get请求不到
网关测试post请求,正常
4、DateTime
5、RemoteAddr
ipconfig查看tcp/ip网络配置,所有的ipv4均可等于本机localhost,作为ip地址使用
网关配置断言规则,设置请求地址必须为 192.168.56.1
- id: product-server #路由id,唯一
uri: http://localhost:7070/ #目标 uri,路由到微服务的地址
predicates: #断言(判断条件)
- RemoteAddr=192.168.56.1/0 #匹配远程地址请求是 remoteAddr的请求,0代表子网掩码
配置了断言规则,此时localhost已经无法访问了
6、Header
- id: product-server #路由id,唯一
uri: http://localhost:7070/ #目标 uri,路由到微服务的地址
predicates: #断言(判断条件)
- Header=X-Request-Id, \d+ #匹配请求头,其值匹配正则表达式 \d+
必须配置 请求头,请求值符合正则表达式数字规则
五、动态路由(服务发现的路由规则)
1、网关添加依赖
网关注册到eureka注册中心
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、动态添加uri
server:
port: 9000
spring:
application:
name: gateway-server
cloud:
gateway:
routes: #路由规则
# - id: product-server #路由id,唯一
# uri: http://localhost:7070/ #目标 uri,路由到微服务的地址
# predicates: #断言(判断条件)
# - Path=/product/** #匹配对应 url 的请求,将匹配到的请求追加在目标 uri 之后
# filters:
# - RewritePath=/(?<segment>/?.*),/$\{segment}
- id: order-server #路由id,唯一
uri: http://localhost:9090/ #目标 uri,路由到微服务的地址
predicates: #断言(判断条件)
- Path=/order/** #匹配对应 url 的请求,将匹配到的请求追加在目标 uri 之后
filters:
- RewritePath=/(?<segment>/?.*),/$\{segment}
- id: product-server #路由id,唯一
uri: lb://product-server #lb://根据服务名称从注册中心获取服务请求地址 lb:load balance 负载均衡
predicates: #断言(判断条件)
- Path=/product/** #将匹配的请求添加到目标uri之后
eureka:
instance:
prefer-ip-address: true #是否使用ip地址注册
instance-id: ${spring.application.name}:${server.port} #ip:端口号
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
测试网关
3、服务名称转发
服务名称转发方法 替代 动态添加uri方法,省去了大量重复的路由配置,但是请求路径必须要加上 服务名,这是约定大于配置
http://localhost:9000/order-service/order/1
server:
port: 9000
spring:
application:
name: gateway-server
cloud:
gateway:
discovery:
locator: #是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例
enabled: true #是否开启基于服务发现的路由规则
lower-case-service-id: true #是否将服务名小写
eureka:
instance:
prefer-ip-address: true #是否使用ip地址注册
instance-id: ${spring.application.name}:${server.port} #ip:端口号
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
六、过滤器
1、网关过滤器GatewayFilter
Path路径过滤器(路径过滤器)
配置网关filter过滤器重写路径
- id: product-server #路由id,唯一
uri: lb://product-server #lb://根据服务名称从注册中心获取服务请求地址
predicates: #断言(判断条件)
- Path=/product/**, /api-gateway/** #将匹配的请求添加到目标uri之后
filters:
#将 /api-gateway/product/1 重写为 /product/1
- RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
正常访问:localhost://9000/product/1 会路由到 localhost://7070/product/1
localhost://9000/api-gateway/product/1 添加 api-gateway 后并不会路由成功,只是为了提高安全性,所以为了可以请求到就需要设置filter过滤器重写路径
PrefixPath(路径过滤器)
网关过滤器配置了前缀 product,会路由到 localhost:7070/product/1
- id: product-server #路由id,唯一
uri: lb://product-server #lb://根据服务名称从注册中心获取服务请求地址
predicates: #断言(判断条件)
- Path=/** #将匹配的请求添加到目标uri之后
filters:
# 将 /1 重写为 /product/1
- PrefixPath=/product
StripPrefix(路径过滤器)
网关顾虑器配置了 裁剪前缀后,会自动路由到 http://localhost:7070/product/1
- id: product-server #路由id,唯一
uri: lb://product-server #lb://根据服务名称从注册中心获取服务请求地址
predicates: #断言(判断条件)
- Path=/** #将匹配的请求添加到目标uri之后
filters:
# 将请求 /api/123/product/1 重写为 /product/1
- StripPrefix=2 #将前2个路径修剪掉
setPath(路径过滤器)
- id: product-server #路由id,唯一
uri: lb://product-server #lb://根据服务名称从注册中心获取服务请求地址
predicates: #断言(判断条件)
- Path=/api/product/{segment} #将匹配的请求添加到目标uri之后
filters:
# 将 /api/product/1 重写为 /product/1
- SetPath=/product/{segment}
Parameter参数过滤器
- id: product-server #路由id,唯一
uri: lb://product-server #lb://根据服务名称从注册中心获取服务请求地址
predicates: #断言(判断条件)
- Path=/api-gateway/** #将匹配的请求添加到目标uri之后
filters:
# 将 /api-gateway/product/1 重写为 /product/1
- RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
#在下游请求中添加 flag=1
- AddRequestParameter=flag,1
商品服务添加参数
@RestController
public class ProductController {
@Autowired
ProductService productService;
@RequestMapping("/product/{id}")
public Product selectProductById(@PathVariable("id") Integer id, String flag){
System.out.println("flag: "+flag);
return productService.selectProductById(id);
}
}
,启动网关服务后,商品服务打印结果为
flag: 1
Status状态过滤器
可以将所有返回的状态码,例如 200、405、500、403等都返回 指定状态码,此处是统一返回404
- id: product-server #路由id,唯一
uri: lb://product-server #lb://根据服务名称从注册中心获取服务请求地址
predicates: #断言(判断条件)
- Path=/api-gateway/** #将匹配的请求添加到目标uri之后
filters:
# 将 /api-gateway/product/1 重写为 /product/1
- RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
#在下游请求中添加 flag=1
- SetStatus=404
2、全局过滤器GlobalFilter
七、限流
好比旅游景点,每到节假日就要限制人流量,也就是Qps
1、限流算法之计数器算法
2、限流算法之漏桶算法
一次进来100个流量,网关自己都扛了下来,对于下面的微服务,总是1秒固定允许过1个请求,增加了网关自己的压力,也浪费了其他微服务的
3、限流算法之令牌桶算法(最优)
SpringCloudGateway官方提供了 RequestRateLimiterGatewayFilterFactory
过滤器工厂,配合使用Redis
和Lua
脚本实现了令牌桶算法
基于令牌桶算法的URI限流
SpringBoot网关整合redis
第1步 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.lian</groupId>
<artifactId>gateway-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<!--父类-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--整合redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
第2步 配置限流规则
/**
* 限流规则配置类
*/
@Configuration
public class KeyResolverConfiguration {
@Bean
public KeyResolver pathKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
}
第3步 yaml配置redis
server:
port: 9000
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: product-server
uri: lb://product-server
predicates:
- Path= /product/**
filters:
- name: RequestRateLimiter #限流过滤器
args:
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 2 #令牌桶总容量
key-resolver: "#{@pathKeyResolver}" #使用SpEl表达式按名称引用bean
#配置redis
redis:
port: 6379
timeout: 1000
host: 8.131.84.120
database: 0
lettuce:
pool:
max-active: 1024 #最大连接数,默认8
max-wait: 10000 #最大连接阻塞等待时间,单位毫秒
max-idle: 200 #最大空闲连接,默认8
min-idle: 5 #最小空闲连接,默认0
#配置eureka注册中心,注入网关服务
eureka:
instance:
prefer-ip-address: true #是否使用ip地址注册
instance-id: ${spring.application.name}:${server.port} #ip:端口号
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
第4步 测试(结果和sentinel的限流效果一样)
1秒1次均匀的发送请求,网关正常可接受
短时间发送大批量请求,网关报错拦截
基于令牌桶算法的参数限流
第1步 配置参数限流
/**
* 限流规则配置类
* 注意:若干bean中,只可以有一个生效,其他注释掉 ,否则会报错
*/
@Configuration
public class KeyResolverConfiguration {
//基于令牌桶算法的 uri限流
// @Bean
// public KeyResolver pathKeyResolver(){
// return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
// }
//基于令牌桶算法的 参数限流
@Bean
public KeyResolver parameterKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
}
第2步 配置yaml
server:
port: 9000
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: product-server
uri: lb://product-server
predicates:
- Path= /product/**
filters:
- name: RequestRateLimiter #限流过滤器
args:
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 2 #令牌桶总容量
#key-resolver: "#{@pathKeyResolver}" #使用SpEl表达式按名称引用bean,基于uri限流
key-resolver: "#{@parameterKeyResolver}" #使用SpEl表达式按名称引用bean,基于参数限流
#配置redis
redis:
port: 6379
timeout: 1000
host: 8.131.84.120
database: 0
lettuce:
pool:
max-active: 1024 #最大连接数,默认8
max-wait: 10000 #最大连接阻塞等待时间,单位毫秒
max-idle: 200 #最大空闲连接,默认8
min-idle: 5 #最小空闲连接,默认0
#配置eureka注册中心,注入网关服务
eureka:
instance:
prefer-ip-address: true #是否使用ip地址注册
instance-id: ${spring.application.name}:${server.port} #ip:端口号
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
第3步 测试
必须添加参数才会正常执行,否则就会报错
不添加参数就会报错
基于令牌桶算法的IP限流
第1步 配置ip限流
/**
* 限流规则配置类
* 注意:若干bean中,只可以有一个生效,其他注释掉 ,否则会报错
*/
@Configuration
public class KeyResolverConfiguration {
//基于令牌桶算法的 uri限流
// @Bean
// public KeyResolver pathKeyResolver(){
// return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
// }
//基于令牌桶算法的 参数限流
// @Bean
// public KeyResolver parameterKeyResolver(){
// return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
// }
//基于令牌桶算法的 ip限流
@Bean
public KeyResolver ipKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
}
第2步 yaml
server:
port: 9000
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: product-server
uri: lb://product-server
predicates:
- Path= /product/**
filters:
- name: RequestRateLimiter #限流过滤器
args:
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 2 #令牌桶总容量
#key-resolver: "#{@pathKeyResolver}" #使用SpEl表达式按名称引用bean,基于uri限流
#key-resolver: "#{@parameterKeyResolver}" #使用SpEl表达式按名称引用bean,基于参数限流
key-resolver: "#{@ipKeyResolver}" #使用SpEl表达式按名称引用bean,基于ip限流
#配置redis
redis:
port: 6379
timeout: 1000
host: 8.131.84.120
database: 0
lettuce:
pool:
max-active: 1024 #最大连接数,默认8
max-wait: 10000 #最大连接阻塞等待时间,单位毫秒
max-idle: 200 #最大空闲连接,默认8
min-idle: 5 #最小空闲连接,默认0
#配置eureka注册中心,注入网关服务
eureka:
instance:
prefer-ip-address: true #是否使用ip地址注册
instance-id: ${spring.application.name}:${server.port} #ip:端口号
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
基于 Sentinel限流
第1步 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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lian</groupId>
<artifactId>gateway-server-sentinel</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway-server-sentinel</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--单独使用-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
第2步 yaml
server:
port: 9001
spring:
application:
name: gateway-server-sentinel
cloud:
sentinel:
filter:
enabled: false
gateway:
discovery:
locator: #是否与服务发现组件进行结合,通过 serviceId 转发到具体服务实例
enabled: true #是否开启基于服务发现的路由规则
lower-case-service-id: true #是否将服务名称转小写
# routes: # discovery.locator 可替代 routes
# - id: product-server
# uri: lb://product-server
# predicates:
# - Path=/product/**
#配置eureka注册中心,注入网关服务
eureka:
instance:
prefer-ip-address: true #是否使用ip地址注册
instance-id: ${spring.application.name}:${server.port} #ip:端口号
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
具体sentinel看sentinel章节