Gateway 网关
1.概述简介
1.1概述
1.2用途
1.3为什么使用Gateway
具有新特性
zuul和Gateway的区别
2.三大核心概念
Gateway工作原理
客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。
Handler再通过指定的过滤器链来讲请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑
Filter 在 “pre” 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;在 “post” 类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等,有着非常重要的作用
核心逻辑 :路由转发 + 执行过滤器链
3.案例
3.1流程
创建 一个eureka服务注册中心 两个支付微服务做支付微服务集群 及 一个Gateway网关微服务
3.2创建eureka服务注册中心
pom(版本控制都在父项目中,springboot版本 2.2.2.RELEASE springcloud版本 Hoxton.SR1 其他的自行选择)
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--boot web actuator-->
<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.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
yml
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#设置与Eureka server 交互的地址查询服务和注册服务都需要依赖这个地址。
# defaultZone: http://eureka7002.com:7002/eureka/ #集群 指向其他注册中心
defaultZone: http://localhost:7001/eureka/ #单机版 指向自己
主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author Xkuna
* @date 2020/7/22 15:32.
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args) ;
}
}
3.3创建支付微服务集群
3.3.1搭建项目基础 (支付服务 8001)
pom
<dependencies>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
yml
server:
port: 8001
spring:
application:
name: cloud-payment-service
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
instance:
instance-id: payment8001
prefer-ip-address: true
主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author Xkuna
* @date 2020/7/21 23:05.
*/
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args) ;
}
}
3.3.2创建controller
package top.xkuna.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
/**
* @author Xkuna
* @date 2020/7/22 11:10.
*/
@RestController
@Slf4j
public class PaymentControler {
@Value("${server.port}")
private String serverPort ;
@GetMapping("/port")
public String getPort(){
return this.serverPort ;
}
}
然后copy该服务 修改端口为8002 则两个支付微服务搭建完成
3.3 创建Gateway网关服务
3.3.1搭建项目基础
pom
注意
不要引入 springboot的web依赖否则 和 gateway依赖冲突
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--一般基础配置类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
yml
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes: #这是一个list集合 可以配置多个路由 下面我们仅配置了一个
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址 lb代表负载均衡
predicates:
- Path=/port/** # 断言,路径相匹配的进行路由
#- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
#- Cookie=username,zzyy
#- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
eureka:
instance:
prefer-ip-address: true
instance-id: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://localhost:7001/eureka
主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author Xkuna
* @date 2020/8/11 17:58.
*/
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class, args) ;
}
}
3.3.2 测试
我们依次启动 eureka服务注册中心 支付微服务集群 8001 8002 和 gateway网关 服务9527
访问http://localhost:7001
可以看到 三个服务注册成功
访问 http://localhost:9527/port
发现 支付服务调用成功 并且实现了负载均衡
4.断言
在上面 我们再gateway中使用的断言是 path
下面是 常用的几种断言
具体可以查看 这个博客(通俗易懂):路由配置
5.拦截器
拦截器官方自带了很多种,但是一般常用的还是自定义拦截器
创建gateway 拦截器 实现 GlobalFilter, Ordered两个接口
在这里拦截条件是 请求参数是否含有 uname
这个参数
有则放行
没有 则 响应 BAD_GATEWAY
的错误码
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Date;
/**
* @author Xkuna
* @date 2020/8/11 18:12.
*/
@Component
@Slf4j
public class MyGatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("***********come in MyLogGateWayFilter: "+new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if(uname == null)
{
log.info("*******用户名为null,非法用户,o(╥﹏╥)o");
exchange.getResponse().setStatusCode(HttpStatus.BAD_GATEWAY);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
此时我们重新启动 gateway 9527 服务
访问 http://localhost:9527/port 可以看到 请求被拦截
访问 http://localhost:9527/port?uname=xkuna
请求成功
gateway的拦截器生效