spring cloud gateway源码分析,一个请求进来的默认处理流程

1.前言

spring cloud gateway的基本组成和作用就不细赘述,此篇适合对此有一定了解的人阅读。
spring cloud gateway版本: Hoxton.SR1

spring cloud gateway的配置使用yml配置:

server:port: 9527y#根据微服务名称进行动态路由的配置
spring:application:name: cloud-gatewaycloud:gateway:discovery:locator:enabled: true                     #开启从注册中心动态创建路由的功能,利用微服务名称进行路由routes:                - id: config-clienturi: lb://config-clientpredicates:- Path=/config/**filters:- RewritePath=/config/?(?<segment>.*),/config/v1/$\{segment}

2. 流程图

在这里插入图片描述
先看一张官网文档给的图,此图大概描述了请求的处理原理,各个组件大致的位置。

3.源码剖析

在这里插入图片描述
http底层处理是基于netty,netty是一个高性能异步事件驱动的通讯框架,对于netty的处理流程可以查阅其源码。netty读取完数据经过pipeline管道处理后,最终调用到reactor.netty.http.server.HttpServerHandle#onStateChange方法。然后经过层层方法调用到核心类org.springframework.web.reactive.DispatcherHandler#handle

	public Mono<Void> handle(ServerWebExchange exchange) {if (this.handlerMappings == null) {//没有合适的handler返回失败return createNotFoundError();}return Flux.fromIterable(this.handlerMappings)//mapping.getHandler是关键方法,根据handlerMapping找到对应的handler.concatMap(mapping -> mapping.getHandler(exchange)).next().switchIfEmpty(createNotFoundError())//invokeHandler是关键方法,调用处理逻辑.flatMap(handler -> invokeHandler(exchange, handler))//处理结果,写出.flatMap(result -> handleResult(exchange, result));}private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {if (this.handlerAdapters != null) {for (HandlerAdapter handlerAdapter : this.handlerAdapters) {if (handlerAdapter.supports(handler)) {//查找合适的handlerAdapter处理,默认会调用到SimpleHandlerAdapter#handlereturn handlerAdapter.handle(exchange, handler);}}}return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));}private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {return getResultHandler(result).handleResult(exchange, result).checkpoint("Handler " + result.getHandler() + " [DispatcherHandler]").onErrorResume(ex ->result.applyExceptionHandler(ex).flatMap(exResult -> {String text = "Exception handler " + exResult.getHandler() +", error=\"" + ex.getMessage() + "\" [DispatcherHandler]";return getResultHandler(exResult).handleResult(exchange, exResult).checkpoint(text);}));}private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {if (this.resultHandlers != null) {for (HandlerResultHandler resultHandler : this.resultHandlers) {if (resultHandler.supports(handlerResult)) {return resultHandler;}}}throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());}

handlerMappings的注入类看下图,最后通过RoutePredicateHandlerMapping找到合适的处理类。handlerMappings中的其他几种Mapping方式,是别的策略或者配置时会用到,可以思考是怎么用的。

在这里插入图片描述

先来看看mapping.getHandler的处理逻辑,默认会调用到org.springframework.web.reactive.handler.AbstractHandlerMapping#getHandler

	@Overridepublic Mono<Object> getHandler(ServerWebExchange exchange) {//getHandlerInternal,根据exchange真正去查找合适的处理handler,根据上面解释,//getHandlerInternal调用到RoutePredicateHandlerMapping类中去return getHandlerInternal(exchange).map(handler -> {if (logger.isDebugEnabled()) {logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);}//跨域处理if (hasCorsConfigurationSource(handler)) {ServerHttpRequest request = exchange.getRequest();CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null);CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);config = (config != null ? config.combine(handlerConfig) : handlerConfig);if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {return REQUEST_HANDLED_HANDLER;}}return handler;});}

现在调用到了org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping#getHandlerInternal方法中,这里一个关键点就来了。Predicates断言,是路由配置的关键,根据predicates的结果,满足的话就会转发请求到对应的Router配置的uri上。

	@Overrideprotected Mono<?> getHandlerInternal(ServerWebExchange exchange) {// don't handle requests on management port if set and different than server portif (this.managementPortType == DIFFERENT && this.managementPort != null&& exchange.getRequest().getURI().getPort() == this.managementPort) {return Mono.empty();}exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());//lookupRoute(exchange)去查找满足断言条件的路由Routerreturn lookupRoute(exchange)// 满足的router会被组装到exchange中,然后返回webHandler.flatMap((Function<Route, Mono<?>>) r -> {exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);if (logger.isDebugEnabled()) {logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);}exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);return Mono.just(webHandler);}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);if (logger.isTraceEnabled()) {logger.trace("No RouteDefinition found for ["+ getExchangeDesc(exchange) + "]");}})));}protected Mono<Route> lookupRoute(ServerWebExchange exchange) {return this.routeLocator.getRoutes()// 遍历所有的Router,此行r.getPredicate().apply(exchange)是验证是否满足断言要求,// 满足的Router会被返回.concatMap(route -> Mono.just(route).filterWhen(r -> {// add the current route we are testingexchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());return r.getPredicate().apply(exchange);})// instead of immediately stopping main flux due to error, log and// swallow it.doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(),e)).onErrorResume(e -> Mono.empty()))// .defaultIfEmpty() put a static Route not found// or .switchIfEmpty()// .switchIfEmpty(Mono.<Route>empty().log("noroute")).next()// TODO: error handling.map(route -> {if (logger.isDebugEnabled()) {logger.debug("Route matched: " + route.getId());}validateRoute(route, exchange);return route;});/** TODO: trace logging if (logger.isTraceEnabled()) {* logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }*/}

apply调用进入org.springframework.cloud.gateway.handler.AsyncPredicate.DefaultAsyncPredicate#apply方法,查看delegate.test(t)调用的实现类,可以发现所有断言的调用,此处根据我们配置的断言规则调用对应的断言,返回Boolean。
在这里插入图片描述
通过断言拿到对应的handler后回到DispatcherHandler#handle方法接下来调用invokeHandler(exchange, handler)

//这个handler 是 FilteringWehHandler
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {if (this.handlerAdapters != null) {for (HandlerAdapter handlerAdapter : this.handlerAdapters) {if (handlerAdapter.supports(handler)) {//使用SimpleHandlerAdapter来处理return handlerAdapter.handle(exchange, handler);}}}return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));}

然后调用org.springframework.web.reactive.result.SimpleHandlerAdapter#handle

	@Overridepublic Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {WebHandler webHandler = (WebHandler) handler;//handler是FilteringWehHandler,所以调用到FilteringWehHandler.handleMono<Void> mono = webHandler.handle(exchange);return mono.then(Mono.empty());}

org.springframework.cloud.gateway.handler.FilteringWebHandler#handle

	@Overridepublic Mono<Void> handle(ServerWebExchange exchange) {Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);//取出之前匹配的Router,取出filters,如果配置了的话List<GatewayFilter> gatewayFilters = route.getFilters();//GatewayFilter和globalFilter合并,并按order排序List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);combined.addAll(gatewayFilters);// TODO: needed or cached?AnnotationAwareOrderComparator.sort(combined);if (logger.isDebugEnabled()) {logger.debug("Sorted gatewayFilterFactories: " + combined);}//进入过滤链调用filtersreturn new DefaultGatewayFilterChain(combined).filter(exchange);}@Overridepublic Mono<Void> filter(ServerWebExchange exchange) {return Mono.defer(() -> {if (this.index < filters.size()) {GatewayFilter filter = filters.get(this.index);//index 每次+1,设置到chain中,传递到下一次filter,下一次filter时取就是next的filterDefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this,this.index + 1);//filter链执行return filter.filter(exchange, chain);}else {return Mono.empty(); // complete}});}

filter链很重要,是spring cloud gateway的扩展点,可以做扩展逻辑,比如权限校验,登录认证,日志等。默认情况下的filter链如下,需要关注一下LoadBalancerClientFilter和NettyRoutingFilter
在这里插入图片描述

org.springframework.cloud.gateway.filter.LoadBalancerClientFilter#filter

	@Override@SuppressWarnings("Duplicates")public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);//使用协议 http还是lbString schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);if (url == null|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {return chain.filter(exchange);}// 保存原始请求urladdOriginalRequestUrl(exchange, url);if (log.isTraceEnabled()) {log.trace("LoadBalancerClientFilter url before: " + url);}//根据注册中心的信息,使用负载均衡算法,找一个可用的服务final ServiceInstance instance = choose(exchange);if (instance == null) {throw NotFoundException.create(properties.isUse404(),"Unable to find instance for " + url.getHost());}URI uri = exchange.getRequest().getURI();// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,// if the loadbalancer doesn't provide one.String overrideScheme = instance.isSecure() ? "https" : "http";if (schemePrefix != null) {overrideScheme = url.getScheme();}//替换成真实服务器的地址,后续调用使用URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);if (log.isTraceEnabled()) {log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);}exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);return chain.filter(exchange);}

org.springframework.cloud.gateway.filter.NettyRoutingFilter#filter 处理http和https请求的发送

	@Override@SuppressWarnings("Duplicates")public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);String scheme = requestUrl.getScheme();if (isAlreadyRouted(exchange)|| (!"http".equals(scheme) && !"https".equals(scheme))) {return chain.filter(exchange);}setAlreadyRouted(exchange);ServerHttpRequest request = exchange.getRequest();final HttpMethod method = HttpMethod.valueOf(request.getMethodValue());final String url = requestUrl.toASCIIString();HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();filtered.forEach(httpHeaders::set);boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);//发送请求Flux<HttpClientResponse> responseFlux = httpClientWithTimeoutFrom(route).headers(headers -> {headers.add(httpHeaders);// Will either be set below, or later by Nettyheaders.remove(HttpHeaders.HOST);if (preserveHost) {String host = request.getHeaders().getFirst(HttpHeaders.HOST);headers.add(HttpHeaders.HOST, host);}}).request(method).uri(url).send((req, nettyOutbound) -> {if (log.isTraceEnabled()) {nettyOutbound.withConnection(connection -> log.trace("outbound route: "+ connection.channel().id().asShortText()+ ", inbound: " + exchange.getLogPrefix()));}return nettyOutbound.send(request.getBody().map(dataBuffer -> ((NettyDataBuffer) dataBuffer).getNativeBuffer()));}).responseConnection((res, connection) -> {// Defer committing the response until all route filters have run// Put client response as ServerWebExchange attribute and write// response later NettyWriteResponseFilterexchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);ServerHttpResponse response = exchange.getResponse();// put headers and status so filters can modify the responseHttpHeaders headers = new HttpHeaders();res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);if (StringUtils.hasLength(contentTypeValue)) {exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR,contentTypeValue);}setResponseStatus(res, response);// make sure headers filters run after setting status so it is// available in responseHttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(getHeadersFilters(), headers, exchange, Type.RESPONSE);if (!filteredResponseHeaders.containsKey(HttpHeaders.TRANSFER_ENCODING)&& filteredResponseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH)) {// It is not valid to have both the transfer-encoding header and// the content-length header.// Remove the transfer-encoding header in the response if the// content-length header is present.response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);}exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES,filteredResponseHeaders.keySet());response.getHeaders().putAll(filteredResponseHeaders);return Mono.just(res);});Duration responseTimeout = getResponseTimeout(route);if (responseTimeout != null) {responseFlux = responseFlux.timeout(responseTimeout, Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout))).onErrorMap(TimeoutException.class,th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT,th.getMessage(), th));}return responseFlux.then(chain.filter(exchange));}

以上,就是请求进来的处理过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/207916.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Amazon】通过代理连接的方式导入 AWS EKS集群至KubeSphere主容器平台

文章目录 一、设置主集群方式一&#xff1a;使用 Web 控制台方式二&#xff1a;使用 Kubectl命令 二、在主集群中设置代理服务地址方式一&#xff1a;使用 Web 控制台方式二&#xff1a;使用 Kubectl命令 三、登录控制台验证四、准备成员集群方式一&#xff1a;使用 Web 控制台…

linux上编写进度条

目录 一、预备的两个小知识1、缓冲区2、回车与换行 二、倒计时程序三、编写入门的进度条四、编写一个正式的五、模拟实现和下载速度相关的进度条 一、预备的两个小知识 1、缓冲区 首先认识一下缓冲区&#xff1a;先写一个.c文件如下&#xff1a; 我们执行一下这个程序时&…

如何在vs2019及以后版本(如vs2022)上添加 添加ActiveX控件中的MFC类

有时候我们在MFC项目开发过程中&#xff0c;需要用到一些微软已经提供的功能&#xff0c;如VC使用EXCEL功能&#xff0c;这时候我们就能直接通过VS2019到如EXCEL.EXE方式&#xff0c;生成对应的OLE头文件&#xff0c;然后直接使用功能&#xff0c;那么&#xff0c;我们上篇文章…

索尼PMW580视频帧EC碎片重组开启方法

索尼PMW580视频帧EC碎片重组开启方法 索尼PMW-580摄像机生成的MXF文件存在严重的碎片化&#xff0c;目前CHS零壹视频恢复程序MXF版、专业版、高级版已经支持重组结构体正常的碎片&#xff0c;同时也支持对于结构体破坏或者覆盖后仅存在音视频帧EC数据的重组&#xff0c;需要注…

次世代建模纹理贴图怎么做?

在线工具推荐&#xff1a; 三维数字孪生场景工具 - GLTF/GLB在线编辑器 - Three.js AI自动纹理化开发 - YOLO 虚幻合成数据生成器 - 3D模型在线转换 - 3D模型预览图生成服务 1、什么是次时代建模&#xff1f; "次世代建模"是一个术语&#xff0c;通常用来描述…

边缘数据中心和5G的融合彻底改变数据传输和物联网

伴随着数字化时代的飞速发展&#xff0c;边缘数据中心和5G技术的联袂崛起&#xff0c;正深刻塑造着人们对数据的创造、传输和处理方式。据Gartner公司的预测&#xff0c;到2025年&#xff0c;企业数据的三分之二将在边缘计算设施中涌现&#xff0c;而非传统的集中式数据中心。这…

抑郁症中西医治疗对比?

抑郁症是一种常见的心理障碍&#xff0c;治疗方法包括中医和西医两种。下面就抑郁症中西医治疗进行对比&#xff1a; 治疗方法&#xff1a;中医治疗抑郁症强调整体观念和辨证论治&#xff0c;通过调理身体各部分的功能&#xff0c;达到治疗抑郁症的目的。中医治疗抑郁症多采用天…

【数据结构】循环链表和双向链表

【循环链表】 (有头结点) pR1->next; R1->nextR2->next->next; free(R2->next); R2->nextp; 例&#xff1a;对于两个单循环链表a&#xff0c;b&#xff0c;将其连接起来&#xff0c;变成一个单循环链表 #include<stdio.h> #include<stdlib.h> …

【动态规划】LeetCode-62.不同路径

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

数据结构:图文详解单链表的各种操作(头插法,尾插法,任意位置插入,删除节点,查询节点,求链表的长度,清空链表)

目录 一.什么是链表 二.链表的实现 节点的插入 头插法 尾插法 指定位置插入 节点的删除 删除第一次出现的关键字节点 删除所有关键字节点 节点的查找 链表的清空 链表的长度 前言&#xff1a;在上一篇文章中&#xff0c;我们认识了线性数据结构中的顺序表&#xff0…

K8s 中 Pod OOMKilled 原因

目录 Exit Code 137 解决方案 JVM 感知 cgroup 限制 使用 JDK9 的容器感知机制尝试 问题分析 容器内部感知 CGroup 资源限制 在 Java10 中&#xff0c;改进了容器集成 JVM 参数 MaxDirectMemorySize -XX:MaxDirectMemorySize 的默认值是什么&#xff1f; 其他获取 ma…

SQL server 2016安装

1、关系数据库的基本概念。 行&#xff1a;每行成为一条“记录”或“元组”&#xff0c;用于描述一个对象的信息。 列&#xff1a;每列称为一个“字段”或“属性”&#xff0c;用于描述对象的一个属性。 2、主键与外键。 主键&#xff1a;键&#xff0c;即关键字。主键由一个或…

【高效开发工具系列】驼峰下划线互转

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

微信小程序自定义tabBar简易实现

文章目录 1.app.json设置custom为true开启自定义2.根目录创建自定义的tab文件3.app.js全局封装一个设置tabbar选中的方法4.在onshow中使用选中方法最终效果预览 1.app.json设置custom为true开启自定义 2.根目录创建自定义的tab文件 index.wxml <view class"tab-bar&quo…

SQL Server 2016(创建数据表)

1、需求描述。 在名为“class”的数据库中创建表&#xff0c;表名称为“course”&#xff0c;其中要包含序号、课程、课程编号、学分、任课教师、上课地点、开始时间、结束时间、备注等列。 设置各个字段的数据类型。其中&#xff0c;"序号"列为标识列&#xff0c;从…

scrapy介绍,并创建第一个项目

一、scrapy简介 scrapy的概念 Scrapy是一个Python编写的开源网络爬虫框架。它是一个被设计用于爬取网络数据、提取结构性数据的框架。 Scrapy 使用了Twisted异步网络框架&#xff0c;可以加快我们的下载速度。 Scrapy文档地址&#xff1a;http://scrapy-chs.readthedocs.io/z…

如果你想成为一名提示词工程师(Prompt Engineer),这款工具你不能错过

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 前言 我们知道&#xff0c;如果想要通过AI得到更好更精确的答案&#xff0c;那么提示词Prompt的好坏至关重要。 因此&#xff0c;提示词工程师这个岗位应运而出。…

优化问题,详解静态优化

优化问题&#xff0c;尤其静态优化问题&#xff0c;在控制系统设计中随处可见&#xff0c;例如基于燃油经济性和驾驶体验的多目标优化的汽车发动机 MAP 标定&#xff0c;基于性能指标优化的飞行器结构设计参数优化&#xff0c;以实验数据与模型输出匹配为目标的电池 RC 等效电路…

创建腾讯云存储桶---上传图片--使用cos-sdk完成上传

创建腾讯云存储桶—上传图片 注册腾讯云账号https://cloud.tencent.com/login 登录成功&#xff0c;选择右边的控制台 点击云产品&#xff0c;选择对象存储 创建存储桶 填写名称&#xff0c;选择公有读&#xff0c;私有写一直下一步&#xff0c;到创建 选择安全管理&#…

TCP三次握手过程

什么是TCP tcp是一个面向连接的、可靠的、基于字节流的传输层通信协议 面向连接&#xff1a;TCP连接是一对一的&#xff0c;不能实现一对多或多对一&#xff0c;TCP在通信前要首先建立连接&#xff0c;连接成功后才能开始进行通信可靠的&#xff1a;TCP连接要保证通信过程的可靠…