南充市网站建设_网站建设公司_RESTful_seo优化
2026/1/17 8:19:01 网站建设 项目流程

实用指南:【深入理解SpringCloud微服务】Gateway简介与模拟Gateway手写一个微服务网关

2026-01-17 08:16  tlnshuju  阅读(0)  评论(0)    收藏  举报

Gateway简介与模拟Gateway手写一个微服务网关

  • Gateway简介
    • 网关的作用
    • Gateway原理
      • 核心概念
        • 路由(route)
        • 断言(predicates)
        • 过滤器(Filter)
      • 工作原理
  • 模拟Gateway手写一个微服务网关
    • 架构设计
    • 源码解析
      • META-INF/spring.factories
      • GatewayConfig
      • GatewayHandlerMapping
      • FilteringWebHandler
      • GatewayFilter
        • LoadBalanceGatewayFilter
        • HttpRoutingGatewayFilter
        • WriteResponseGatewayFilter

Gateway简介

网关的作用

由于微服务架构下,我们的系统通常被拆分为多个服务,此时缺少一个统一的入口,外部客户端调用将成为一个问题。因此需要网关作为统一入口,让网关帮我们做路由转发

在这里插入图片描述

单是微服务网关一般不会作为系统最前端的入口,在微服务网关前面还有一层负载均衡

在这里插入图片描述

除了路由转发外,微服务网关还具有认证、鉴权、安全策略、防刷、流控、监控日志等。

在这里插入图片描述

Gateway原理

核心概念

Gateway有三个核心概念需要了解:路由(route)、断言(predicates)、过滤器(Filter),它们都是yml配置文件中的配置。

路由(route)

路由是yml配置文件中最基础的部分,它在yml配置文件中就是routes配置,routes配置是数组格式,数组中每个配置项是一个路由配置,每个路由配置包括一个唯一的id、目标URI、一组断言(predicates)、一组过滤器(Filter)。

在这里插入图片描述

id用于唯一定位一条路由配置,目标URI表示路由转发的目标地址。

在这里插入图片描述

断言(predicates)

断言指的是断言函数,用于与请求中的各种请求信息(比如请求路径或请求头)匹配,如果匹配结果为true,则使用该条路由配置;匹配结果为false则不使用该条路由配置。

在这里插入图片描述

过滤器(Filter)

Gateway中的过滤器,Gateway中的所有功能(路由转发、认证、鉴权等等)都在过滤器中实现,Gateway会把所有的过滤器组成一个过滤器链对请求和响应进行处理。

在这里插入图片描述

工作原理

Gateway是依赖于spring-webflux的,底层原理如下:

在这里插入图片描述

Gateway基于Spring-WebFlux,实现了WebFlux的两个核心组件HandleMappingWebHandler

  1. 首先HandleMapping会加载路由配置,并调用路由配置中的断言函数进行匹配,如果匹配成功,则使用该路由规则。
  2. WebHandler会把路由配置中指定的过滤器(GatewayFilter)和全局过滤器(GlobalFilter)组装成过滤器链,然后调用过滤器链进行请求处理。

模拟Gateway手写一个微服务网关

熟悉了Gateway的功能和原理之后,我们可以参考Gateway着手设计并实现我们自己的微服务网关。

架构设计

我们设计的微服务网关也是基于Spring-WebFlux的,我们实现了Spring-WebFlux定义的两个组件接口HandlerMapping和WebHandler,分别是GatewayHandlerMappingFilteringWebHandler

当webflux接收到请求时,会调用我们的GatewayHandlerMapping获取Handler。

在这里插入图片描述

GatewayHandlerMapping会组装拦截器链(我们这里跟Gateway不一样,拦截器链的组装放到HandlerMapping来做了),先加入是三个固定的拦截器,分别是LoadBalanceGatewayFilterHttpRoutingGatewayFilterWriteResponseGatewayFilter,然后通过SPI机制加载用户自定义的拦截器(如果有的话),然后与请求进行匹配,匹配成功的拦截器也会加入到拦截器链中,然后对拦截器链中的拦截器进行排序。

在这里插入图片描述

然后GatewayHandlerMapping返回FilteringWebHandler。WebFlux获取到GatewayHandlerMapping返回的FilteringWebHandler,然后调用FilteringWebHandler对请求进行处理。

FilteringWebHandler会按顺序调用拦截器链中的拦截器进行请求处理。

在这里插入图片描述

拦截器链中最后的三个拦截器的作用分别是:

在这里插入图片描述

源码解析

META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.huangjunyi1993.simple.microservice.gateway.config.GatewayConfig

GatewayConfig

@Configuration
@EnableConfigurationProperties({GatewayProperties.class})
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class GatewayConfig {
@Bean
public GatewayHandlerMapping gatewayHandlerMapping() {
return new GatewayHandlerMapping();
}
@Bean
public FilteringWebHandler filteringWebHandler() {
return new FilteringWebHandler();
}
@Bean
public LoadBalanceGatewayFilter loadBalanceGatewayFilter() {
return new LoadBalanceGatewayFilter();
}
@Bean
public HttpRoutingGatewayFilter httpGatewayFilter() {
return new HttpRoutingGatewayFilter();
}
@Bean
public WriteResponseGatewayFilter writeResponseGatewayFilter() {
return new WriteResponseGatewayFilter();
}
}

我们通过SpringBoot的自动装配机制加载我们的配置类GatewayConfig,通过GatewayConfig向Spring容器注册我们的核心类型:GatewayHandlerMapping、FilteringWebHandler以及三个固定的拦截器。

在这里插入图片描述

GatewayHandlerMapping

/**
* @author huangjunyi
* @date 2024/1/5 18:09
* @desc
*/
public class GatewayHandlerMapping extends AbstractHandlerMapping {
...
public GatewayHandlerMapping() {
setOrder(1);
}
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {// 1、通过SPI加载并收集与当前请求匹配的所有filter,保存到ServerWebExchangebuildFilterChain(exchange);// 2、返回FilteringWebHandlerreturn Mono.just(webHandler);}private void buildFilterChain(ServerWebExchange exchange) {// 组装拦截器链List<GatewayFilter> filterList = new ArrayList<>();// 加入三个固定拦截器filterList.add(loadBalanceGatewayFilter);filterList.add(httpRoutingGatewayFilter);filterList.add(writeResponseGatewayFilter);// 通过SPI机制加载用户自定义的拦截器ServiceLoader<GatewayFilter> gatewayFilters = ServiceLoader.load(GatewayFilter.class);for (GatewayFilter gatewayFilter : gatewayFilters) {// 判断是否与本次请求匹配if (gatewayFilter.match(exchange)) {filterList.add(gatewayFilter);}}// 拦截器排序filterList.sort(Comparator.comparing(GatewayFilter::order));// 拦截器链保存到ServerWebExchange的attributes中exchange.getAttributes().put(FILTER_LIST, filterList);}}

GatewayHandlerMapping继承了AbstractHandlerMapping并实现了它的抽象方法getHandlerInternal(ServerWebExchange),getHandlerInternal(ServerWebExchange)方法中首先会构建拦截器链,然后返回FilteringWebHandler。

拦截器链的构建就如我们上面架构设计中描述的那样:

  1. 加入三个固定拦截器
  2. 通过SPI机制加载自定义的拦截器,判断是否与本次请求匹配,匹配则放入拦截器链中
  3. 拦截器排序
  4. 拦截器链保存到ServerWebExchangeattributes

在这里插入图片描述

FilteringWebHandler

/**
* @author huangjunyi
* @date 2024/1/5 19:23
* @desc
*/
public class FilteringWebHandler implements WebHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange) {// 从ServerWebExchange的attributes中获取过滤器链List<GatewayFilter> filterList = (List<GatewayFilter>) exchange.getAttributes().get(GatewayHandlerMapping.FILTER_LIST);Mono<Void> mono = Mono.empty();// 遍历并执行过滤器for (GatewayFilter gatewayFilter : filterList) {mono = gatewayFilter.filter(exchange, mono);}return mono;}}

FilteringWebHandler实现了WebFlux定义的WebHandler接口并实现了它的handle(ServerWebExchange) 方法。handle方法中从ServerWebExchange的attributes中获取过滤器链,然后遍历并执行过滤器。

在这里插入图片描述

GatewayFilter

GatewayFilter是我们定义的过滤器接口。

/**
* 过滤器
* @author huangjunyi
* @date 2024/1/5 18:01
* @desc
*/
public interface GatewayFilter {
/**
* 过滤器排序
* @return
*/
int order();
/**
* 过滤器逻辑
* @param exchange
* @return
*/
Mono<Void> filter(ServerWebExchange exchange, Mono<Void> mono);/*** 判断当前过滤器是否与当前请求匹配* @param exchange* @return*/boolean match(ServerWebExchange exchange);}

GatewayFilter#order() 方法用于排序,GatewayFilter#match(ServerWebExchange) 方法用于判断当前过滤器是否与当前请求匹配,GatewayFilter#filter(ServerWebExchange, Mono<Void>) 方法是过滤器的处理逻辑。

LoadBalanceGatewayFilter
/**
* 负责均衡过滤器
* @author huangjunyi
* @date 2024/1/5 19:25
* @desc
*/
public class LoadBalanceGatewayFilter implements GatewayFilter {
public static final String RECONSTRUCTED_URL = "reconstructedUrl";
@Autowired
private LoadBalanceClient loadBalanceClient;
@Override
public int order() {
return Integer.MAX_VALUE - 2;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, Mono<Void> mono) {// 1、从url中取到serviceName,// 比如http://localhost:8080/userService/xxxxx,则取到userServiceServerHttpRequest request = exchange.getRequest();String url = request.getURI().toString();String serviceName;boolean startsWithHttps = url.startsWith("https");String temp = url.replace(startsWithHttps ? "https://" : "http://", "");int firstSeparatorIndex = temp.indexOf("/");temp = temp.substring(firstSeparatorIndex + 1);serviceName = temp.contains("/") ? temp.substring(0, temp.indexOf("/")) : temp;// 2、重新组装调用后端微服务的url,// 比如http://localhost:8080/userService/xxxxx,则重新组装为http://userService/xxxxxurl = (startsWithHttps ? "https://" : "http://") + temp;// 3、调用loadBalanceClient重写url,里面会进行负载均衡,根据负载均衡结果重写url,保存到重写后的url到ServerWebExchange中String reconstructedUrl = loadBalanceClient.reconstructUrl(serviceName, url);exchange.getAttributes().put(RECONSTRUCTED_URL, reconstructedUrl);return Mono.empty();}@Overridepublic boolean match(ServerWebExchange exchange) {return true;}}

LoadBalanceGatewayFilter的filter方法处理流程:

  1. 首先从url中取到serviceName,比如http://localhost:8080/userService/xxxxx,那么取得的serviceName就是userService。
  2. 重新组装调用后端微服务的url,比如http://localhost:8080/userService/xxxxx,则重写组装为http://userService/xxxxx。
  3. 调用loadBalanceClient重写url,里面会进行负载均衡,根据负载均衡结果重写url,保存到重写后的url到ServerWebExchange中。

在这里插入图片描述

HttpRoutingGatewayFilter
/**
* 向后端微服务发起http请求
* @author huangjunyi
* @date 2024/1/5 19:26
* @desc
*/
public class HttpRoutingGatewayFilter implements GatewayFilter {
public static final String CONNECTION = "connection";
@Override
public int order() {
return Integer.MAX_VALUE - 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, Mono<Void> mono) {// 取得LoadBalanceGatewayFilter负载均衡重写后的urlString url = (String) exchange.getAttributes().get(LoadBalanceGatewayFilter.RECONSTRUCTED_URL);// 取得ServerWebExchange中的请求头信息Map<String, String> headerMap = exchange.getRequest().getHeaders().toSingleValueMap();// 请求方法(GET、POST)final io.netty.handler.codec.http.HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue());// 取得ServerWebExchange中的请求体信息Flux<DataBuffer> body = exchange.getRequest().getBody();// 响应对象ServerHttpResponse response = exchange.getResponse();// 使用Netty的HttpClient重新组装请求信息并发送return HttpClient.create()// 请求头.headers(headers -> {for (Map.Entry<String, String> entry : headerMap.entrySet()) {headers.add(entry.getKey(), entry.getValue());}})// 请求方法.request(method)// 请求url.uri(url)// 发送请求.send((req, nettyOutbound) -> nettyOutbound.send(body.map(this::getByteBuf)))// 处理后端的返回结果.responseConnection((res, connection) -> {// 状态码保存到response对象HttpStatus status = HttpStatus.resolve(res.status().code());if (status != null) {response.setStatusCode(status);}// 响应头保存到response对象HttpHeaders headers = new HttpHeaders();res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));response.getHeaders().putAll(headers);exchange.getAttributes().put(CONNECTION, connection);return Mono.just(res);}).then();}@Overridepublic boolean match(ServerWebExchange exchange) {return true;}}

HttpRoutingGatewayFilter干的事情就是把客户端发送过来的请求信息(请求头、请求体)重新封装一下,然后使用NettyHttpClient发送请求给后端微服,再把后端返回的结果重新封装一下返回。

在这里插入图片描述

WriteResponseGatewayFilter
/**
* 返回结果给客户端
* @author huangjunyi
* @date 2024/1/9 19:46
* @desc
*/
public class WriteResponseGatewayFilter implements GatewayFilter {
@Override
public int order() {
return Integer.MAX_VALUE;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, Mono<Void> mono) {return mono.then(writeResponse(exchange));}private Mono<Void> writeResponse(ServerWebExchange exchange) {return Mono.defer(() -> {// 取得ServerWebExchange中的响应对象ServerHttpResponse response = exchange.getResponse();Connection connection = exchange.getAttribute(HttpRoutingGatewayFilter.CONNECTION);// 取得HttpRoutingGatewayFilter中的后端返回结果,做一下转换Flux<DataBuffer> responseBody = connection.inbound().receive().retain().map(byteBuf -> wrap(byteBuf, response));// 响应结果写回给客户端return response.writeWith(responseBody);});}@Overridepublic boolean match(ServerWebExchange exchange) {return true;}}

WriteResponseGatewayFilter的作用就是把将HttpRoutingGatewayFilter中取得的后端返回结果,做一下转换,然后写回客户端。

整体流程图:
在这里插入图片描述

gitee仓库地址:simple-microservice-gateway

在这里插入图片描述

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询