最近,我们的线上环境多次出现 504(Gateway Timeout) 错误。经过排查,发现是由于存在慢 SQL 查询,导致 HTTP 请求无法在服务器设定的时间内获得响应。借此机会,我们来聊聊网关在微服务架构中的重要性。
一、什么是网关
服务网关是一种在客户端(web, app,小程序)和后端各种微服务之间充当中介的软件。它负责接收来自客户端的请求,并将其转发到合适的后端服务,同时也负责将后端服务的响应返回给客户端。
二、网关在微服务架构中扮演的角色
在传统的单体架构中,客户端只需与一个服务交互。但在微服务架构中,应用被拆分为多个独立的服务,客户端需要调用不同的服务来完成一个业务流程。这带来了诸多挑战:
- 调用复杂性:客户端需要维护各个服务的地址和接口,增加了开发难度。
- 安全风险:所有微服务都直接暴露在外网,增加了被攻击的可能性。
- 性能问题:客户端可能需要多次请求才能完成一个业务操作,增加了延迟。
服务网关的出现,可以有效的解决上述问题。它提供了以下功能。
- 统一入口:网关为所有客户端提供一个统一的入口,大大简化了客户端和后端服务之间的互动。客户端只需要关注网关的地址,无需了解每个具体服务的地址。
- 路由和负载均衡:网关能够根据请求的路径、方法、请求头等信息,将请求动态路由到对应的微服务实例。它可以与注册中心(如 Eureka、Consul)集成,实现服务发现和负载均衡,确保请求被分发到可用的服务实例上。
- 请求管理:网关可以实现限流和缓存,保护后端服务免受流量突增的影响。同时,网关可以对请求和响应进行转换,比如协议转换、数据格式转换等。
- 安全性:网关提供集中式的认证和授权功能,如JWT 验证、OAuth2.0 等,防止未授权的访问,同时,避免了每个微服务都需要重复实现安全逻辑,提高了系统的整体安全性。
- 日志与监控:网关可以集中收集请求的日志、指标,方便进行系统的监控与链路追踪,从而快速定位和解决问题。
三、常用网关
在微服务架构中,有多种实现服务网关的解决方案,其中Spring Cloud Gateway和Zuul是最常用的两个:
1. zuul
Zuul是Netflix开源的一个网关解决方案,是 Spring Cloud 早期默认的网关组件,被广泛应用于生产环境。Zuul 1 基于阻塞式的 Servlet 模型,Zuul 2 进行了重写,支持了异步和非阻塞。
zuul的使用
- 引入maven依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
- 配置路由规则
Zuul 的路由功能是通过配置文件来实现的。在application.yml中可以配置路由规则。
以下是一个简单的示例,将以/customer-api开头的请求路由到名为customer-api的微服务实例上:
zuul:routes:user-route:path: /customer-api/**serviceId: customer-api
在上述配置中:
- user-route:是路由的标识,用于区分不同的路由规则。
- path:指定了请求的路径映射模式,以/customer-api开头的请求将匹配此路由。
- serviceId:表示要将请求路由到的微服务的标识(eureka中注册的标识),这里是customer-api,Zuul 会根据这个标识通过服务发现组件(如 Eureka 等)来查找微服务的实际地址。
- 添加过滤器
Zuul 支持在请求处理的不同阶段添加过滤器,实现类似身份验证、请求限流等功能。
过滤器分为前置过滤器、路由过滤器和后置过滤器。以下是一个简单的前置过滤器示例,用于检查请求头是否包含授权信息:
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.ZuulContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;@Component
public class CustomZuulFilter extends ZuulFilter {@Overridepublic String filterType() {return "pre";}@Overridepublic int filterOrder() {return 1;}@Overridepublic boolean filterByDefault() {return true;}@Overridepublic Object filter(ZuulContext zuulContext, Object input) throws ZuulException {if (!zuulContext.getRequest().getHeaders().containsKey("Authorization")) {zuulContext.getResponse().setStatusCode(401);return null;}return input;}
}
在上述示例中,CustomZuulFilter是一个前置过滤器,它检查请求头中是否包含Authorization信息,如果没有,则返回401(未授权)状态码。
4. 启动应用
在应用启动时,在主类(Main类)添加**@EnableZuulProxy**注解启动Zuul的代理功能。
2. Spring Cloud Gateway
Spring Cloud Gateway 是 Spring 官方推出的网关解决方案,旨在替代 Zuul。基于 Spring 5、Spring Boot 2 和 Project Reactor构建,支持响应式编程模型,能够高效地处理大规模并发请求。其特性包括动态路由、断路器、限流、负载均衡等。
Spring Cloud Gateway的使用
- 引入maven依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
- 配置路由
示例同zuul。这里就不再赘述了,我们来看配置有何不同。
spring:cloud:gateway:routes:id: customer_routeuri: ca://customer-apipredicates:Path=/customer-api/**
在上述配置中:
- id:是路由的唯一标识,用于区分不同的路由规则。
- uri:指定了请求要转发到的目标地址。这里使用了ca://前缀,表示通过服务发现组件(如 Eureka)来查找名为customer-api的微服务实例的实际地址。
- predicates:是一组断言条件,用于判断请求是否符合该路由规则。这里的Path=/customer-api/**表示请求路径以customer-api开头的请求将匹配此路由。
- 添加过滤器
Spring Cloud Gateway 也支持在请求处理的不同阶段添加过滤器,以实现诸如身份验证、请求参数校验、响应处理等功能。
过滤器可以通过两种方式添加:
- 全局过滤器:对所有通过网关的请求都生效。可以通过实现org.springframework.cloud.gateway.filter.GlobalFilter接口并将其注册为 Spring Bean 来创建全局过滤器。
示例代码如下:
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
@Order(1)
public class CustomGlobalFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 在这里可以进行一些全局的操作,比如检查请求头是否包含授权信息if (!exchange.getRequest().getHeaders().containsKey("Authorization")) {exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}return chain.filter(exchange);}
}
- 路由特定过滤器:只对特定路由生效。可以在路由配置中直接添加过滤器配置。
例如,在前面的路由配置基础上,添加一个对customer_route路由生效的过滤器,用于在请求转发前添加一个自定义请求头:
spring:cloud:gateway:routes:id: customer_routeuri: ca://customer-apipredicates:Path=/customer-api/**filters:name: AddRequestHeaderargs:name: custom-headervalue: custom-value
在上述配置中,添加了一个名为AddRequestHeader的过滤器,它会在将请求转发到customer-api微服务之前,为请求添加一个名为custom-header,值为custom-value的请求头。
4. 启动应用
在应用启动时,在主类(Main类)添加**@EnableGateway**注解来启动 Spring Cloud Gateway 的相关功能。
四、总结
服务网关在微服务架构中担当着举足轻重的角色。它不仅是一座连接客户端和后端微服务的桥梁,更是微服务系统的守护者。通过统一访问入口、加强安全性、优化流量管理,网关使复杂的微服务生态变得更加有序与高效。
在面对 504 Gateway Timeout 问题时,网关可以帮助我们监控请求的链路,识别出导致超时的慢请求,进而进行优化和调整。
在实践中,我们可以根据自身需求,灵活使用Spring Cloud Gateway或Zuul,来构建健壮的微服务生态。