SpringCloud(H版&alibaba)之Gateway篇
服务网关
Gateway和Zuul说明:
Zuul由于内部出现重大分歧,已经没落,新一代的服务网关是Gateway,只学这个
Gateway 并非netflix开发的,而是spring 社区自己开发的。
Gateway是什么
官网:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
面试可能会问,是什么,为什么选这个
是什么?
Cloud全家桶中有个很重要的组件就是网关,在1.x版本中采用zuul;
2.x版本中,SpringCloud自己研发了一个网关代替Zuul,那就是Gateway.
Gateway: 旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断、限流、重试等。
为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty.
提供 安全,监控/指标,限流。
为什么选择Gateway
一、基于异步非阻塞模型上进行开发的,性能方面不需要担心;稳定,Zuul 2 进入了维护期。
二、特性
动态路由:能够匹配任何请求属性
可以对路由指定 Predicate (断言) 和 Filter (过滤器);
集成Hystrix 的断路器功能;
集成 Spring Cloud 服务发现功能;
易于编写的 Predicate (断言) 和 Filter (过滤器);
请求限流功能;
支持路径重写;
三、spring cloud gateway 还支持WebSocket,并且与Spring紧密集成拥有更好的开发体验,Zuul阻塞模型,性能要慢,gateway非阻塞模型,是Zuul速度的1.6倍
Gateway工作流程
三大核心概念
Route(路由) :路由是构建网关的基本模块,它由ID,目标URI, 一系列的断言和过滤器组成,如果断言为true,则匹配该路由
Predicate(断言) :参考的是Java8的java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
Filter(过滤) :指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或之后进行修改。
总体:web请求,通过一些匹配条件,定位到真正的服务节点。并且在这个转发过程的前后,进行一些精细化控制。 predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri, 就可以实现一个具体的路由了。
额外的知识:一个请求的流程:
外部请求 ——> 负载均衡——> 网关 ——> 微服务
官网总结流程
客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。
Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行 业务逻辑,然返回。
过滤器可能会在发送代理请求之前(pre)或之后(post)执行业务逻辑。
Filter在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,
在 “post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
核心逻辑
路由转发 + 执行过滤器链
入门配置
新建Module : cloud-gateway-gateway9527
POM
<dependencies>
<!--Gateway依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--需要注册进eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.huawei.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
YML
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
业务类
无
主启动类
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class, args);
}
}
9527网关如何做路由映射
cloud-provider-payment8001看看controller的访问地址,get / lb
假如我们目前不想暴露8001端口,希望在8001外面套一层9527
YML新增网关配置
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #payment_route 路由的ID,没有固定规则但要求唯一,建议配合服务名
url: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
测试
启动eureka服务注册中心 7001
启动服务提供者 cloud-provider-payment8001
启动网关9527
debug:
启动报错:
Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.
gateway 不需要 spring cloud 的web 和actuator这两个组件,去除掉
另一个bug:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'spring.cloud.gateway' to org.springframework.cloud.gateway.config.GatewayProperties Failed:
Property: spring.cloud.gateway.routes[0].uri
Value: null
Reason: 不能为null
解决方法,将yml中 routes的 url 改成uri
routes:
- id: payment_routh #payment_route 路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
访问说明
添加网关前:http://localhost:8001/payment/get/21
添加网关后:http://localhost:9527/payment/get/21
结果:成功通过9527端口访问到
yml配置说明
端口和路径都可以重新配置,这样可以隐藏系统真实的端口和路径
predicates:
- Path= 这里配置地址,对应controller里的方法上的 Mapping 里的值 ** 代表匹配符
Gateway配置路由的两种方式
Gateway网关路由有两种配置方式:
第一种:在配置文件yml中配置 例如上面的配置
第二种:代码中注入RouteLocator的Bean 配置类的方式
例如:通过9527网关访问到百度的网页
编码:cloud-gateway-gateway9527
业务实现:
@Configuration
public class GatewayConfig {
/**
* 配置了一个id 为path_route_huawei的路由规则,
* 当访问地址 http://localhost:9527/guonei 时,会自动转发到地址 http://news.baidu.com/guonei
*/
@Bean
public RouteLocator customrouteLocator (RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_huawei", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
其他参考文章:
https://blog.csdn.net/tianyaleixiaowu/article/details/83412301?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162196452916780255275894%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162196452916780255275894&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-2-83412301.pc_search_result_cache&utm_term=gateway%E7%9A%84%E8%B7%AF%E7%94%B1%E9%85%8D%E7%BD%AE&spm=1018.2226.3001.4187
Gateway配置动态路由
存在的问题:
1.地址被写死 2. 只写了一台机器的,现实中微服务不可能这样,又要做负载均衡了
地址写死,这跟之前做负载均衡的效果相反,之前做服务调用时是通过服务名找地址然后进行负载均衡。
解决:
2.默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
启动: 一个eureka7001 + 两个服务提供者 8001/8002
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
YML:
增加网关的discovery部分,再将uri地址由写死的改成从服务中心动态获取
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route 路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
测试:http://localhost:9527/payment/lb 8001/8002两个端口切换
eureka本来就整合了ribbon,这样配置后,会实现轮询 8001和8002自动切换
Gateway常用的Predicate
是什么
当启动9527时,后台打印了
Loaded RoutePredicateFactory [After]
Loaded RoutePredicateFactory [Before]
Loaded RoutePredicateFactory [Path]
Loaded RoutePredicateFactory [Weight]
之前配置的
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
这里的 path 只是其中一种配置
Route Predicate Factories这个是什么东西?
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#the-before-route-predicate-factory
查看官网4.1 到4.11部分,有如何配置及意义
可以根据不同的要求进行匹配。多种进行组合,并通过逻辑and.
常用的Route Predicate
The After Route Predicate Factory
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
This route matches any request made after Jan 20, 2017 17:42 Mountain Time (Denver).
在美国丹佛时间 xxxx 之后开始匹配 (官网)
手测案例:
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
- After=2021-05-26T11:22:00.789+08:00
表示东八区时间 2021-05-26T11:22:00.789 之后进行匹配
测试成功
时区也可以写上具体时区名字类似官网案例: 东八区时间可以写 [Asia/Shanghai]
The Before Route Predicate Factory
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
This route matches any request made before Jan 20, 2017 17:42 Mountain Time (Denver).
The Between Route Predicate Factory
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
This route matches any request made after Jan 20, 2017 17:42 Mountain Time (Denver) and before Jan 21, 2017 17:42 Mountain Time (Denver). This Could be useful for maintenance windows.
The Cookie Route Predicate Factory
predicates:
- Cookie=chocolate, ch.p
This route matches requests that have a cookie named chocolate
whose value matches the ch.p
regular expression.
打开cmd
发送请求
curl http://localhost:9527/payment/lb --cookie "chocolate=ch.p"
得到结果。 一旦配置了Cookie ,发送请求时就必须带上,否则404
The Header Route Predicate Factory
predicates:
- Header=X-Request-Id, \d+
This route matches if the request has a header named X-Request-Id
whose value matches the \d+
regular expression (that is, it has a value of one or more digits).
两个参数:一个是属性名称,一个是正则表达式,如果这个属性的值和正则表达式匹配则执行。
配置后发送curl请求
curl http://localhost:9527/payment/lb -H "X-Request-Id:123"
-H 表示请求头 后面是名称和值
测试成功 得到端口
The Host Route Predicate Factory
predicates:
- Host=**.somehost.org,**.anotherhost.org
This route matches if the request has a Host
header with a value of www.somehost.org
or beta.somehost.org
or www.anotherhost.org
.
The Method Route Predicate Factory
predicates:
- Method=GET,POST
This route matches if the request method was a GET
or a POST
.
The Path Route Predicate Factory
predicates:
- Path=/red/{segment},/blue/{segment}
This route matches if the request path was, for example: /red/1
or /red/blue
or /blue/green
.
The Query Route Predicate Factory
predicates:
- Query=green
The preceding route matches if the request contained a green
query parameter.
The RemoteAddr Predicate Factory
predicates:
- RemoteAddr=192.168.1.1/24
This route matches if the remote address of the request was, for example, 192.168.1.10
.
The Weight Route Predicate Factory
predicates:
- Weight=group1,2
This route would forward ~80% of traffic to weighthigh.org and ~20% of traffic to weighlow.org
Gateway的Filter
是什么
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
Spring Cloud Gateway的Filter
生命周期:
pre 之前
post 之后
种类:两种类型
GatewayFilter:有31个
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#gatewayfilter-factories 官网资料看着配
GlobalFilter:全局的
常用的GatewayFilter
filters: #与predicates是平级的
- AddRequestParameter=X-Request-Id,1024 #过滤器工厂会在匹配的请求头上加上一对请求头,名称为
X-Request-Id值为1024
自定义过滤器
自定义全局过滤器GlobalFilter
两个主要接口介绍:implements 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());
//获取参数 uname (用户名),检验是否为空,为空就拒绝,直接返回
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname == null) {
log.info("**************用户名为null,非法用户,o(╥﹏╥)o");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getorder() {
//表示过滤器优先级,一般数字越小,优先级越高
return 0;
}
}
如果直接在浏览器输入进行测试,要把之前配置的- Header 注释掉,浏览器地址栏输入没法配
浏览器地址栏输入:http://localhost:9527/payment/lb?uname=z123 测试成功
输入http://localhost:9527/payment/lb?uname= 也成功
输入http://localhost:9527/payment/lb 失败
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。