在计算机网络中,服务网关是一种连接不同网络的设备或程序。它提供了一个连接不同网络的桥梁,它可以将数据包从一个网络传递到另一个网络。它可以是硬件设备,也可以是软件程序。服务网关则是指在微服务架构中,负责将对外提供的API请求路由到相应的微服务上,并提供负载均衡、服务降级、故障恢复等功能。在实际应用中,Gateway可以使用开源框架如Nginx、Zuul、Kong等来实现。
我们最常见的服务网关实现莫过于是springcloud gateway和zuul。而两者的区别是什么呢?
Spring Cloud Netflix Zuul是由Netflix开源的API网关,在微服务架构下,网关作为对外的门户,实现动态路由、监控、授权、安全、调度等功能。Zuul基于servlet 2.5(使用3.x),使用阻塞API。 它不支持任何长连接,如websockets。而Gateway建立在Spring Framework 5,Project Reactor和Spring Boot 2之上,使用非阻塞API。 比较完美地支持异步非阻塞编程,Gateway 中Websockets得到支持,并且它是spring开发,完美集成在spring中,使用起来体验感会更好,本文叙述的是gateway的使用方式。
Springcloud gateway有三大概念:Route(路由)、 Predicate(断言)和Filter(过滤器)。
本次配置是gateway微服务的模块配置,在启动和测试本模块之前,需要新建好注册中心模块和服务提供者微服务模块,不清楚的可以查看上几篇文章。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
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/** # 断言,路径相匹配的进行路由
@SpringBootApplication
@EnableEurekaClient
public class gatewayMain {
public static void main(String[] args) {
SpringApplication.run(gatewayMain.class, args);
}
}
没有配置服务网关之前,我们会直接访问服务方的接口,比如: http://localhost:8001/payment/get/1;当配置后,会根据配置文件的uri地址进行服务匹配,访问 http://localhost:9527/payment/get/1能够达到一样的效果。
除了上述的这种yml配置方式,还有一种较为繁琐的bean方式。
新建配置文件gatewayconfig,启动访问可以达到一样的效果。
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_atguigu", r -> r.path("/guoji").uri("http://news.baidu.com"))
.build();
return routes.build();
}
}
一般来说,路由配置基本很少去写死服务的端口号,所以gateway的动态路由使用是最为频繁的,也是最常用的。其实很简单,在上述的yml文件中修改uri的配置value值即可。
lb://cloud-payment-service:lb代表负载均衡,可以查看上本系列文章负载均衡实现方式,cloud-payment-service表示的是服务提供者的地址,这样无需关注端口。gateway会根据注册中心注册的服务列表,以注册中心上微服务为路径创建动态路由进行转发,实现动态路由功能。
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
在上述的配置中,我们已经实现了断言的最基本的方法,更多方法在项目启动时打印的断言日志可以看到断言的所有配置项。
比如:After(在配置的时间之后触发)、Before(在配置的时间之前触发)、Between(在配置的时间之间触发)等。可以查看官网文档介绍,实现方式都很简单。
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
- Before=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
- Between=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] , 2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
yml在routes下配置参数:filter参数,有由于太多种,推荐去官网可以查看具体的配置项。这里主要介绍自定义过滤器,因为这是最常用的的方式。
新建配置文件:启动后访问http://localhost:9527/payment/lb?name=111。文件中request.getQueryParams().getFirst(“name”)表示在访问接口时必须携带name参数,否则访问不到。
@Component
public class MyGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("-------------MyGateWayFilter-----------" + new Date());
ServerHttpRequest request = exchange.getRequest();
String uname = request.getQueryParams().getFirst("name");
if (uname == null) {
System.out.println("name为空!参数非法!!!");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
具体不清楚的可以查看本系列文章,或者评论留言。