Spring Cloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架
1、什么是路由网关
网关是系统的唯一对外的入口,介于客户端和服务器端之间的中间层,处理非业务功能 提供路由请求、鉴权、监控、缓存、限流等功能。它将"1对N"问题转换成了"1对1”问题。
通过服务路由的功能,可以在对外提供服务时,只暴露 网关中配置的调用地址,而调用方就不需要了解后端具体的微服务主机。
2、为什么要使用微服务网关
不同的微服务一般会有不同的网络地址,而客户端可能需要调用多个服务接口才能完成一个业务需求,若让客户端直接与各个微服务通信,会有以下问题:
(1)客户端会多次请求不同微服务,增加了客户端复杂性
(2)存在跨域请求,处理相对复杂
(3)认证复杂,每个服务都需要独立认证
(4)难以重构,多个服务可能将会合并成一个或拆分成多个
微服务网关介于服务端与客户端的中间层,所有外部服务请求都会先经过微服务网关客户只能跟微服务网关进行交互,无需调用特定微服务接口,使得开发得到简化
gateway三大核心概念
1。Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
2.Predicate(断言):参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
3.Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
案例如下:
我们需要新建一个专门用于网关的项目,项目中都是一些配置没有业务类。这个也需要注册到注册中心。
新建gateway项目
pom.xml
<dependencies>
<!--新增gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.at.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<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>
这个案例是别的微服务的controller中的路径进行通用port的修改,比如一起我们访问项目时是http://localhost:8001/payment/get/31这个就已经具体到我们的某个微服务,这样可能会出现被攻击的危险,现在我们利用网关的配置再访问这个微服务时用网关的端口号就能直接访问这个地址,这样就不用暴露我们自己的微服务端口已经信息。http://localhost:9527/payment/get/31访问的结果是一样的,因为在网关项目的yml文件中配置了自己微服务的ip和端口号所以可以直接替换为网关的ip和端口号,意义也是去访问我们这个接口,只不过是进行了包装。
yml文件
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes:
- id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
#匹配后提供服务的路由地址
uri: http://localhost:8001
#uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由这里的路径就是业务微服务的接口地址
- id: payment_route2
uri: http://localhost:8001
#uri: lb://cloud-payment-service
predicates:
- Path=/payment/getAll/** #断言,路径相匹配的进行路由
# - After=2020-03-12T15:44:15.064+08:00[Asia/Shanghai]
#- Cookie=username,eiletxie #带Cookie,并且username的值为eiletxie
#- Header=X-Request-Id,\d+ #请求头要有 X-Request-Id属性并且值为整数的正则表达式
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册消息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
#集群版
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
#单机版
#defaultZone: http://localhost:7001/eureka/
instance:
instance-id: cloud-gateway-service
prefer-ip-address: true #访问路径可以显示ip
#Eureka客户端向服务端发送心跳的实际间隔,单位为秒(默认为30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端收到最后一次心跳后等待时间上线,单位为秒(默认为90秒) 超时将剔除服务
lease-expiration-duration-in-seconds: 2
启动类
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain9527.class,args);
}
}
启动Eureka集群后再启动我们的业务微服务,之后启动网关微服务。
原来没有网关之前我们访问业务微服务的方式http://localhost:8001/payment/get/31
有了网关微服务后我们访问业务微服务的方式http://localhost:9527/payment/get/31
返回的结果是一样的,只不过将8001端口替换成了网关的9527端口提高安全。
可以在一个网关配置文件中配置不同微服务的接口,这是第一种的配置方式后面还有第二种配置方式
第二种配置网关的方式
第一种我们是在yml文件中配置的,第二种需要我们自己写一个配置类,在配置类中一个一个的写转发地址。
routes.route(“path_rote_atguigu"代表yml文件中的- id: payment_route
r -> r.path(”/guonei").uri(“http://news.baidu.com/guonei”)).build();代表用网关的访问可以跳转到具体的业务路径
http://localhost:9527/guonei只需要用这个就可以访问原来的http://news.baidu.com/guonei
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
//http://localhost:9527/guonei只需要用这个就可以访问原来的http://news.baidu.com/guonei
routes.route("path_rote_atguigu", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
@Bean
public RouteLocator customRouteLocator1(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
//http://localhost:9527/guoji只需要用这个就可以访问原来的http://news.baidu.com/guoji
routes.route("path_rote_atguigu1", r -> r.path("/guoji").uri("http://news.baidu.com/guoji")).build();
return routes.build();
}
}
其实还是yml看着舒服,不过这两种都对用哪个都行。
由于我们后期会有同一个业务项目的集群,就比如8001,8002他们都是一个项目但是为了高可用就把它弄成集群,如果是集群的情况下那么原来yml配置中用ip+端口号就不能实现负载均衡,这时候要用spring注册到注册中心的名字作为一个动态的地址,因为同一项目的集群注册名称都是一样的,这样就可以实现负载均衡。
修改如下discovery:
locator:
enabled: true开启从注册中心动态创建路由的功能,利用微服务名称进行路由
修改uri,将原来的ip+端口的形式改为微服务名称
yml文件
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes:
- id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
#匹配后提供服务的路由地址
#uri: http://localhost:8001
#spring:applicatio:nname
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_route2
# uri: http://localhost:8001
uri: lb://cloud-payment-service
predicates:
- Path=/payment/getAll/** #断言,路径相匹配的进行路由
# - After=2020-03-12T15:44:15.064+08:00[Asia/Shanghai]
#- Cookie=username,eiletxie #带Cookie,并且username的值为eiletxie
#- Header=X-Request-Id,\d+ #请求头要有 X-Request-Id属性并且值为整数的正则表达式
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册消息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
#集群版
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
#单机版
#defaultZone: http://localhost:7001/eureka/
instance:
instance-id: cloud-gateway-service
prefer-ip-address: true #访问路径可以显示ip
#Eureka客户端向服务端发送心跳的实际间隔,单位为秒(默认为30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端收到最后一次心跳后等待时间上线,单位为秒(默认为90秒) 超时将剔除服务
lease-expiration-duration-in-seconds: 2
在yml中配置路径时会有在某个时间后才生效或者在某个时间直接才生效,这些都可以看官网很详细。
geteway官网。
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
Filter的使用:
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
官网5和6有配置全局和局部过滤的案例可以自行观看
过滤书写位置
自定义过滤:
自定义过滤需要新建一个类需要实现GlobalFilter, Ordered 这两个类
@Component
@Slf4j
public class MyLogGateWayFilter 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(StringUtils.isEmpty(uname)){
log.info("*****用户名为Null 非法用户,(┬_┬)");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);//给人家一个回应
return exchange.getResponse().setComplete();
} return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0; }
}
访问是在后面加上需要的参数即可