1.springcloud全家桶中有个很重要的组件就是网关,在1.x版本中采用的都是Zuul网关;但在2.x版本中,zuul的升级一直跳票,springcloud最后自己研发了一个网关代替zuul,那就是springcloud Gateway,Gateway是原zuul1.x版的替代。springcloud Gateway是基于WebFlux框架实现的,而WebFlux框架的底层则使用了高性能的Reactor模式通信框架Netty。
2.springcloud Getaway特性:
基于Spring Freamwork5,Project Reactor和Spring Boot2.0进行构建
动态路由:能够匹配任何请求属性;
可以对路由指定Predicate(断言)和Filter(过滤器);
集成Hytrix的断路器功能;
集成SpringCloud的服务发现功能;
易于编写的Predicate和Filter;
请求限流功能;
支持路径重写。
3.Gateway的三大核心概念
Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为ture则匹配该路由
Predicate(断言):参考的是Java8的java.util.funcation.predicate。开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言匹配则进行路由
Filter(过滤器):指的是spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或路由后对请求进行修改
4.Gateway工作流程
客户端向Gateway发出请求,然后在Gateway Handler Mapping中找到于请求相匹配的路由,将其发送到Gateway Web Handler。Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间的虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。Filter在“pre”类型的过滤器可以做参数的校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改、日志的输出,流量监控等有着非常重要的作用。
5.新建项目cloud-gateway-8093
pom
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.5.RELEASE
com.yinuo
cloud-gateway-8093
0.0.1-SNAPSHOT
cloud-gateway-8093
Demo project for Spring Boot
1.8
Hoxton.SR9
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
yml
server:
port: 8093
spring:
application:
name: cloud-gateway
eureka:
client:
fetchRegistry: true #是否从eurekaServer中抓取已有的注册信息,默认为ture,单节点无所谓,集群必须为ture才能配合ribbon使用负载均衡
register-with-eureka: true #注册进eurekaServer
service-url:
defaultZone: http://eureka-8091:8091/eureka/,http://eureka-8092:8092/eureka/
instance:
instance-id: gateway-8093
prefer-ip-address: true #显示IP地址
主启动类
package com.yinuo.gateway8093;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class CloudGateway8093Application {
public static void main(String[] args) {
SpringApplication.run(CloudGateway8093Application.class, args);
}
}
6.把8005项目加入到路由,修改yml,增加以下代码
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: hytrix-8005-1 #路由的id,没有固定规则但要求唯一,建议结合服务名
uri: http://localhost:8005 #匹配后提供服务的路由地址
predicates:
- Path=/consumer/feignFallback/** #断言,路径相匹配的进行路由
- id: hytrix-8005-2
uri: http://localhost:8005
predicates:
- Path=/consumer/circuitBreaker/get/**
7.访问http://localhost:8093/consumer/circuitBreaker/get/1和http://localhost:8093/consumer/feignFallback/get发现可以全部的返回数据。
8.实际工作中,如果接口很多,一般会采用通配符“*”来代替,如下面的配置也可以访问
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
# - id: hytrix-8005-1 #路由的id,没有固定规则但要求唯一,建议结合服务名
# uri: http://localhost:8006 #匹配后提供服务的路由地址
# predicates:
# - Path=/consumer/feignFallback/** #断言,路径相匹配的进行路由
#
# - id: hytrix-8005-2
# uri: http://localhost:8006
# predicates:
# - Path=/consumer/circuitBreaker/get/**
- id: hytrix-8005-3
uri: http://localhost:8005
predicates:
- Path=/**/**/**/**
9.路由的配置也可以通过配置类来进行,但是一般工作中不会使用该方法,因为太麻烦,一下就是类配置的相关代码,新建GatewayConfig类
package com.yinuo.gateway8093.cpnfig;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GatewayConfig {
/**lambda可以随便起名,叫a也行,叫b也行
* @param builder
* @return
*/
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("hytrix-8005-4",
lambda -> lambda.path("/consumer/**")
.uri("http://localhost:8005")).build();
return routes.build();
}
}
10.访问http://localhost:8093/consumer/get
11.上面的yml配置中,URI为固定的,但是在实际工作中,可能有多台服务器,所以就要用到动态路由,修改配置文件
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
# - id: hytrix-8006-1 #路由的id,没有固定规则但要求唯一,建议结合服务名
# uri: http://localhost:8006 #匹配后提供服务的路由地址
# predicates:
# - Path=/consumer/feignFallback/** #断言,路径相匹配的进行路由
#
# - id: hytrix-8006-2
# uri: http://localhost:8006
# predicates:
# - Path=/consumer/circuitBreaker/get/**
- id: hytrix-8006-3
# uri: http://localhost:8006
uri: lb://hytrix-consumer-8006 #微服务名
predicates:
- Path=/**/**/**/**
12.predicates的其他配置请参考官网https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gateway-request-predicates-factories
13.简单过滤器的实现,新建GateWayLogFilter
package com.yinuo.gateway8093.filter;
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;
@Component
@Slf4j
public class GateWayLogFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("======================进入GateWayLogFilter过滤器=========================");
log.info("======================访问路径是:{}=========================", exchange.getRequest().getPath());
log.info("======================访问URI是:{}=========================", exchange.getRequest().getURI());
log.info("======================访问参数是:{}=========================", exchange.getRequest().getQueryParams().toString());
log.info("======================访问方法类型是:{}=========================", exchange.getRequest().getMethod());
//过滤访问的参数必须有一个为id,没有就停止访问
if(exchange.getRequest().getQueryParams().get("id")==null){
log.info("======================访问参数中没有id=========================");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 数字表示加载过滤器的优先级顺序,数字越小,优先级越高
* **/
@Override
public int getOrder() {
return 0;
}
}
14.访问http://localhost:8093/consumer/circuitBreaker/get/333
后台日志打印