对于网关Zuul
来说,因为官方已经不再维护了,所以直接记录Gateway
如何使用,这是Spring
自己开发的网关。
Gateway
一些基本概念
Gateway
用了很多的Spring5.0
的新特性。
因为网关是挡在在前面的一面墙,所以他就能对请求做很多的事情,比如说鉴权,日志监控,这些在请求的时候和响应的时候能做的事情,最重要的就是鉴权了,有了鉴权就用不着每个服务里面去写鉴权功能了。
一般情况下,Nginx
在最前面,然后通过Nginx
访问到Gateway
,然后通过Gateway
访问每一个微服务。
Gateway
的优点:
-
动态路由:能够匹配任何请求属性
-
可以对路由执行
Predicate
和Filter
-
集成
Hystrix
的断路器功能 -
集成
SpringCloud
的服务发现功能 -
易于编写的
Predicate
和Filter
-
请求限流功能
-
支持路径重写
三个概念:
Route
:路由时构建网关的基本模块,它由ID
,目标URI
,一系列的断言和过滤器组成,如果断言为true
,则匹配该路由。
Predicate
:开发人员可以匹配HTTP
请求中的所有内容,例如请求头或者请求参数,如果请求与断言相匹配则进行路由。
Filter
:指的是Spring
框架中的GatewayFilter
实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
做一个网关路由
依然是同样的步骤:
新建一个Module
,然后导入坐标:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
千万不能导入spring-boot-starter-web
否则启动的时候会报错,因为Gateway
默认使用的WebFlux
然后就是配置文件了。
server:
port: 9527
启动类略,写一个最普通的启动类就可以了。
可以看到只需要一个端口就可以启动了,但是它并不能完成请求的路由。还需要其他配置
在其他服务中添加一些接口,比如说8001端口的服务中添加如下代码:
@GetMapping("router01/{id}")
public String router01(@PathVariable("id") Integer id) {
return String.valueOf(id) + " router-01";
}
@GetMapping("router02/{id}")
public String router02(@PathVariable("id") Integer id) {
return String.valueOf(id) + " router-02";
}
使用网关来路由到这两个接口上:
spring:
cloud:
gateway:
routes:
- id: router-01
uri: http://localhost:8001
predicates:
- Path=/router01/**
- id: router-02
uri: http://localhost:8001
predicates:
- Path=/router02/**
id
:和普通的id
一样,不能重复,可以自己起名字
uri
:映射到目标服务的uri
predicates
:就是断言了,这里用到了一个Path
断言
这个时候访问接口:http://localhost:9527/router01/154发现可以访问了,这就完成了路由功能
编码方式配置网关路由
使用编码的方式实现和上面配置文件中一样的功能:
@Configuration
public class RouteConfig {
@Bean
public RouteLocator routes01(RouteLocatorBuilder builder) {
return builder.routes().route("router-01",
r -> r.path("/router01/**").uri("http://localhost:8001")
).build();
}
@Bean
public RouteLocator routes02(RouteLocatorBuilder builder) {
return builder.routes().route("router-02",
r -> r.path("/router02/**").uri("http://localhost:8001")
).build();
}
}
网关+注册中心
首先第一步导入坐标:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
配置文件稍作修改:
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: router-01
uri: lb://cloud-provider-paymanet
predicates:
- Path=/router01/**
- id: router-02
uri: lb://cloud-provider-paymanet
predicates:
- Path=/router02/**
discovery:
locator:
enabled: true
consul:
host: localhost
port: 8500
discovery:
register: false
上面配置文件中加入了基本的注册中心的配置,并且启用了网关的注册中心功能,然后还将uri
改成了lb:// + serviceId
的形式。
然后启动类上面加上@EnableDiscoveryClient
注解就可以了。
访问9527
端口,可以发现网关对目标微服务也做了负载均衡,就意味着成功了。
Gateway默认的Predicate
官方文档上有11中Predicate可以供使用,前面的例子中就使用了一种Path
After
:在某个时间之后才可以访问,例如:2017-01-20T17:42:47.789-07:00[America/Denver]
,这里的时间是ZonedDateTime
类生成的Before
:在某个时间之前才可以访问Between
:在某个时间中间才可以访问Cookie
:携带规定内容的Cookie才可以访问,例如:chocolate, ch.p
Header
:携带规定内容头才能访问,例如:X-Request-Id, \d+
Host
:当host
为规定内容的时候才能访问,例如:**.somehost.org
Method
:与指定方法匹配才能访问,例如:GET,POST
Path
:这个前面用过,例如:/red/{segment},/blue/{segment}
,里面的segment
是可以获取的,官网有说Query
:携带规定内容的查询字符串才能访问RemoteAddr
:规定的IP
才能访问Weight
:给访问分配权重
Gateway的Filter
Filter
的生命周期有pre
和post
两种,过滤器的种类有GatewayFilter
和GlobalFilter
两种。
GatewayFilter
官方文档上列举了30种之多,GlobalFilter
官方文档上列举了10种
重要的不是怎么使用官方的这些Filter
,使用的时候查查文档就行了,重要的是学会怎么样自己定义过滤器,定义GlobalFilter
和GatewayFilter
的过滤器,和Zuul
的概念一样,过滤器也有优先级,所以实现一个全局的过滤器需要实现GlobalFilter
和Ordered
接口。
这里需要有一些WebFlux
的基础,其他文章中会记录。
实现代码如下:
@Component
@Slf4j
public class IpFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
if (remoteAddress == null || "0:0:0:0:0:0:0:1".equals(remoteAddress.getHostName())) {
log.info("不符合要求...");
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return response.setComplete();
}
log.info("经过了过滤器...");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
上面这个代码写到gateway
项目的filter
包中就可以了,这个代码的含义是不让本机访问。过滤器的写法不管多么麻烦,起点都是这里。