网关可提供请求路由与组合、协议转换、安全认证、服务鉴权、流量控制与日志监控等服务。可选的网关有不少,比如 Nginx、高性能网关 OpenResty、Linkerd 以及 Spring Cloud Gateway。
如果是真的追求高性能,那肯定是选择 Nginx 或者 OpenResty 无疑了, 但是对性能要求不是很高的话,并且又在用 Spring Cloud 系列,那当然就要选择 Spring Cloud Gateway 了。
网关的基础就是路由功能,通俗解释就是地址转发,将一个请求地址转发到实际的服务地址。比如请求的是 http://xxx.com/api 的路由地址,实际上会被转发到 http://xxx.com:8888 上来,这就是个最简单的路由方式。
我们可以理解为 Spring Cloud Gateway 就是针对进来的请求做各种判断和处理,比如说判断请求的合法性、权限验证,请求地址改写,请求参数、头信息、cookie 信息的分析和改写,请求速率控制,日志留存等。而这些都可以方便的通过 Predicate 和 GatewayFilter 来组合实现。
创建 Spring Cloud Gateway 项目
Spring Cloud 版本是 Greenwich.SR2,Spring Boot 版本 2.1.6.RELEASE,JDK 1.8。
接下来正式创建一个 Gateway 项目。
首先做两个微服务,当做路由转发的目标服务
两个微服务是以 consul 作为服务注册中心的,可以看这篇文章服务注册发现、配置中心集一体的 Spring Cloud Consul
1、创建 consul-order 服务,具体可以去 github 上看代码,很简单的一个服务。创建的 RESTful Controller 如下:
@RestController
@RequestMapping(value = "order")
public class OrderController {
@Value("${spring.application.name}")
private String applicationName;
@GetMapping(value = "get")
public CustomerOrder getOrder(){
CustomerOrder customerOrder = new CustomerOrder();
customerOrder.setOrderId("9999");
customerOrder.setProductName("MacBook Pro");
customerOrder.setClient(applicationName);
return customerOrder;
}
}
总之,最后直接访问这个接口的地址为 http://localhost:5006/order/get
2、创建 consul-user 服务,具体代码可以到 github 上查看。
创建的 RESTful Controller 内容如下:
@RestController
@RequestMapping(value = "user")
public class UserController {
@GetMapping(value = "get")
public User getUserInfo(){
User user = new User();
user.setName("古时的风筝");
user.setAge(8);
user.setLocation("北京");
return user;
}
}
和上面的微服务有点区别的就是设置了 context-path
server:
port: 5005
servlet:
context-path: /user-service
之所以这样不同的设置,是因为下面要验证一个 filter。总之,最后上述接口的访问地址为:http://localhost:5005/user-service/user/get
创建一个项目,并引入 maven 包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置简单的路由转发
路由配置有两种方式。一种是配置文件,另外一种是代码方式配置,WebFlux 的反应式编程方式。所以我们 pom 文件中要引入 WebFlux 的包。这是 Spring 5 的新特性。
1、先看第一种配置文件方式配置:
server:
port: 10000
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: false # 是否将服务id转换为小写
routes:
- id: userServiceRouter
uri: lb://consul-user
predicates:
- Path=/user-service/**
- id: orderServiceRouter
uri: lb://consul-order
predicates:
- Path=/order-service/**
filters:
- StripPrefix=1
consul:
host: localhost #注册gateway网关到consul
port: 8500
discovery:
service-name: service-gateway
其中包括 Spring Boot 项目的基本配置,name、port ,还有关于 consul 的配置,要将网关服务注册到注册中心。
上面配置中创建了两条路由规则,路由规则名称通过 id 设置,分别是 userServiceRouter 和 orderServiceRouter,通过 predicates.Path 设置待转发的 url,通过 uri 设置转发后的目标地址。上面配置将以 /user-service/
开头的地址转发到 lb://consul-user
,固定格式 lb + 服务id,在有注册中心的情况下要这样写,如过没有注册中心,可以直接写目标 url。
下面的路由规则中多了一个 StripPrefix 的 filter ,这个是 Gateway 的内置 filter,作用就是去掉 Path 中的指定部分,StripPrefix=1,就是以 / 分隔,去掉第一部分,比如 /a/b/c 这个地址,在 StripPrefix=1 的作用下,就会转发到 /b/c/,当 StripPrefix=2 的时候,就会转发到 /c/。
配置好上述接口,然后启动网关服务。访问规则就会有如下对应关系:
http://localhost:10000/user-service/user/get->http://localhost:5005/user-service/user/get
http://localhost:10000/order-service/order/get->http://localhost:5006/order/get
当然这只是针对每一个目标服务只有一个实例的情况,如果有多个实例,就会按照负载策略落到对应的实例中。
2、代码方式的路由配置
@Bean
public RouteLocator kiteRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("userRouter", r -> r.path("/user-service/**")
.filters(f ->
f.addResponseHeader("X-CustomerHeader", "kite"))
.uri("lb://consul-user")
)
.route("orderRouter", r -> r.path("/order-service/**")
.filters(f -> f.stripPrefix(1)).uri("lb://consul-order")
)
.build();
}
上面的这段代码和前面的配置文件的内容是同样的作用。只要实现一个返回类型为 RouteLocator
,参数为 RouteLocatorBuilder
类型的 Bean。
你看后面那一连串的 r.path().filters().uri() 了吗,用它们就可以简单的配置出路由规则,而且可读性也比较强。另外,Gateway 还套用了 Predicate 的规则来构建更加灵活、复杂的路由规则。Predicate 是 Java 8 增加的逻辑计算库,有 negate()、and()、or()、isEqual()几个方法。具体的代码在 PredicateSpec 和 UriSpec 这两个类里,一目了然。
PredicateSpec 里有这么多方法,都可以结合 and、or 组合起来使用。
接下来就说到 filter,Gateway 内置了很多的 filter,可以在 GatewayFilterSpec 类下找到方法封装,每一个 filter 都由一个 factory 的 apply 实现,都在 org.springframework.cloud.gateway.filter.factory
包下,有必要的话可以直接看源码。 比如上面用到的 StripPrefix。还有 addResponseHeader,它的作用是在 Response 对象的 header 中添加请求头。
启动网关服务
启动网关,并访问两个接口测试,接口分别为 http://localhost:10000/user-service/user/get和http://localhost:10000/order-service/order/get,正常返回数据,则说明网关服务配置正常。
巧用 StripPrefix filter
微服务多了之后,路由的转发规则也就多了,比方说订单相关请求要转发到订单微服务集群,用户相关请求要转发到用户微服务集群,最终开放给终端的接口也要能表明是哪个微服务的,除了接口文档里说明之外,接口本身最好也能明确标识。
一种方式是在微服务的配置文件中配置上server.servlet.context-path
。
还有一种方式就是在路由规则的 path 中配置,然后加上 StripPrefix 配置,选择性的去掉请求 url 中的某些部分。比如我们请求 Gateway的地址为 order-service/order/get,则经过 StripPrefix(1) 之后,会把请求地址变为 order/get,然后根据路由规则定向到具体的微服务地址或者特定的 url。
本篇就介绍 Spring Cloud Gateway 的基本用法,后续还会有关于集成安全认证、鉴权、限流、日志等相关内容,敬请关注。