位置: IT常识 - 正文

(WebFlux)004、WebFilter踩坑记录

编辑:rootadmin
一、背景 使用SpringWebFlux的WebFilter时,由于不熟悉或一些思考疏忽,容易出现未知的异常。记录一下排查与解决方案,给大家分享一下。 二、问题 2.1 问题描述 在测试接口方法时,出现的错误信息如下(对一些项目路径做了修改): java.lang.IllegalStateExcep ... 一、背景

推荐整理分享(WebFlux)004、WebFilter踩坑记录,希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:,内容如对您有帮助,希望把文章链接给更多的朋友!

使用SpringWebFlux的WebFilter时,由于不熟悉或一些思考疏忽,容易出现未知的异常。记录一下排查与解决方案,给大家分享一下。

二、问题2.1 问题描述

在测试接口方法时,出现的错误信息如下(对一些项目路径做了修改):

java.lang.IllegalStateException: COMPLETEDat org.springframework.http.server.reactive.AbstractListenerReadPublisher$State.subscribe(AbstractListenerReadPublisher.java:451)Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s):*__checkpoint ⇢ springfox.boot.starter.autoconfigure.SwaggerUiWebFluxConfiguration$CustomWebFilter [DefaultWebFilterChain]*__checkpoint ⇢ com.xxx.config.LoginWebFilter$$EnhancerBySpringCGLIB$$f3da6bdf [DefaultWebFilterChain]*__checkpoint ⇢ com.xxx.config.TraceIdFilter [DefaultWebFilterChain]*__checkpoint ⇢ HTTP POST "/abc/test/testMethod" [ExceptionHandlingWebHandler]Original Stack Trace:at org.springframework.http.server.reactive.AbstractListenerReadPublisher$State.subscribe(AbstractListenerReadPublisher.java:451)at org.springframework.http.server.reactive.AbstractListenerReadPublisher.subscribe(AbstractListenerReadPublisher.java:105)2.2 解决问题

通过查看错误信息描述,checkpoint点都在webfilter中,由于对webflux也不是特别熟,所以就只有一个个测试。

通过一系列操作, 把swagger移除,细读TraceIdFilter(内容不多),主要归功于原方案是正确的,修改后错误,最后才定位问题出现在LoginWebFilter。

说说插曲,原实现方式(有阻塞逻辑,没出现上述异常),代码如下:

@Configuration@Slf4j@Order(-10)public class LoginWebFilter implements WebFilter { // 略... @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); if (!enableGateway) { String token = Optional.ofNullable(request.getHeaders().getFirst(Constants.TOKEN)) .orElse(""); // 获取用户信息 User user = getUser(token); if (user != null) { ServerHttpRequest mutateRequest = exchange.getRequest().mutate() .build(); exchange = exchange.mutate().request(mutateRequest).build(); } } return chain.filter(exchange); } private User getUser(String token) { if (StringUtils.isNotBlank(token)) { return redisTemplate.opsForValue().get("xxx:tk:" + token) .flatMap(str -> Mono.justOrEmpty(JsonUtils.toObj(str, User.class))).block(); } return null; }}

这样写,没有复杂的业务逻辑,从上到下,完全OJBK,但是调整后,就出现了上述异常。

改完后的问题代码如下:

// 错误public class LoginWebFilter implements WebFilter {/...略 @Autowired private ReactiveStringRedisTemplate redisTemplate; @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { if (!enableGateway) { ServerHttpRequest request = exchange.getRequest(); String token = Optional.ofNullable(request.getHeaders().getFirst(Constants.TOKEN)) .orElse(""); return getUser(token).flatMap(user -> { ServerHttpRequest mutateRequest = exchange.getRequest().mutate() .header(UserUtils.MEMBER_ID, user.getMemId()) .header(UserUtils.MOBILE, user.getMobile()) .build(); ServerWebExchange newexchange = exchange.mutate().request(mutateRequest).build(); return chain.filter(newexchange); // 问题点 }).switchIfEmpty(chain.filter(exchange)); } return chain.filter(exchange); }// 不在用block private Mono<User> getUser(String token) { if (StringUtils.isNotBlank(token)) { return redisTemplate.opsForValue().get("xxx:tk:" + token) .flatMap(str -> Mono.justOrEmpty(JsonUtils.toObj(str, User.class))); } return Mono.empty(); }}2.3 如何解决

对比改造前和改造后的代码,其实差异不大,那问题出现在哪呢?

由于对webflux也不是特别熟,那就只能一点点试(太蠢了)。 最后发现问题出现在了switchIfEmpty(chain.filter(exchange)),在去掉了switchIfEmpty(chain.filter(exchange)),就不会在出现上述异常。

修改后部分代码如下:

// 半正确@Overridepublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { if (!enableGateway) { ServerHttpRequest request = exchange.getRequest(); String token = Optional.ofNullable(request.getHeaders().getFirst(Constants.TOKEN)) .orElse(“”); return getUser(token).flatMap(user -> { ServerHttpRequest mutateRequest = exchange.getRequest().mutate() .header(UserUtils.MEMBER_ID, user.getMemId()) .header(UserUtils.MOBILE, user.getMobile()) .build(); ServerWebExchange newexchange = exchange.mutate().request(mutateRequest).build(); return chain.filter(newexchange); }); } return chain.filter(exchange);}

虽然现在不回在出现异常,但是去掉switchIfEmpty后,代码逻辑是不完整的,当获取不到User时,返回Mono.emtpy,那会直接结束流程,不在执行剩下的filter或其他逻辑。真是连环坑,一坑接一坑。所以对代码需要调整一番,调整后如下:

// 有点正确 但是不多@Overridepublic Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { if (!enableGateway) { ServerHttpRequest request = exchange.getRequest(); String token = Optional.ofNullable(request.getHeaders().getFirst(Constants.TOKEN)) .orElse(“”); return getUser(token).switchIfEmpty(Mono.error(() -> new BizException(ErrorCode.USER_IS_NULL_ERROR))) .flatMap(user -> { ServerHttpRequest mutateRequest = exchange.getRequest().mutate() .header(UserUtils.MEMBER_ID, user.getMemId()) .header(UserUtils.MOBILE, user.getMobile()) .build(); ServerWebExchange newexchange = exchange.mutate().request(mutateRequest).build(); return chain.filter(newexchange); }).onErrorResume(e -> chain.filter(exchange)); } return chain.filter(exchange);}(WebFlux)004、WebFilter踩坑记录

当获取用户为空后,抛出异常,然后在兜底,当异常的时候执行chain.filter(exchange)(好蠢的方式.. 但是解决问题了)。

2.4 意外之喜

各位看官,就在我写完上完上面的代码修改方案之后,读了一下修改完后的代码,突然发现问题出在哪了,所以连夜修改了代码方式。现在我听我细细道来。

2.4.1 问题点

原因点:chain.filter(exchange)重复执行

switchIfEmpty(chain.filter(exchange))这个点本意是想用在当getUser 方法为空时,执行其它WebFilter的逻辑,从而不影响主流程。

忽略了一点是:当chain.filter(newexchange)这个方法执行完后,返回的也是Mono<Void>,也是为空。所以无论如何,代码最后的逻辑都会走到switchIfEmpty(chain.filter(exchange))。

但是当getUser获取到用户后,会重复执行chain.filter(exchange),如下

return chain.filter(newexchange)switchIfEmpty(chain.filter(exchange))

由于第一次执行完chain.filter(exchange),request、response都已经关闭,所以出现了xx COMPLETE,那看来的确符合逻辑。

2.4.2 验证猜想

这个验证方式还是挺简单的,那就是分别传入正常的TOKEN和错误的TOKEN。

具体操作:.....(本人已完成)

结论:

当传入错误的token的时候,确实没有抛出异常,完美执行。但是当传入正确的token,出现了熟悉的异常。

2.4.3 代码调整

知道问题的原因,那就好调整代码了。修改后如下:

public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { if (!enableGateway) { ServerHttpRequest request = exchange.getRequest(); String token = Optional.ofNullable(request.getHeaders().getFirst(Constants.TOKEN)) .orElse(request.getHeaders().getFirst("suuid")); return getUser(token).map(user -> { ServerHttpRequest mutateRequest = exchange.getRequest().mutate() .header(UserUtils.MEMBER_ID, user.getMemId()) .header(UserUtils.MOBILE, user.getMobile()) .build(); return exchange.mutate().request(mutateRequest).build(); // 调整当getUser为空时,返回的内容 }).switchIfEmpty(Mono.just(exchange)).flatMap(chain::filter); } return chain.filter(exchange);}

至此,问题就完全解决拉!心里美滋滋!

三、总结

1、遇到问题,还是要多看看呀,细细思考一下

2、多看代码,发现问题,实现完美的解决方案

本文链接地址:https://www.jiuchutong.com/zhishi/310752.html 转载请保留说明!

上一篇:Vue2基于elementUi的多级菜单动态生成(vue+elementui)

下一篇:phpcms控制器在哪(phpcms使用教程)

  • applewatch怎么设置触感模式(applewatch怎么设置喝水提醒)

    applewatch怎么设置触感模式(applewatch怎么设置喝水提醒)

  • iphone铃声怎么设置(iphone铃声怎么设置静音)

    iphone铃声怎么设置(iphone铃声怎么设置静音)

  • 拍照广角是什么意思(广角拍照有什么好处)

    拍照广角是什么意思(广角拍照有什么好处)

  • 微信被拉黑删除是什么状态(微信被拉黑删除怎么看出来)

    微信被拉黑删除是什么状态(微信被拉黑删除怎么看出来)

  • 腾讯视频投屏怎么快进(腾讯视频投屏怎么关闭本地加速)

    腾讯视频投屏怎么快进(腾讯视频投屏怎么关闭本地加速)

  • 硬磁盘存储器三大组成(硬盘存储器的作用)

    硬磁盘存储器三大组成(硬盘存储器的作用)

  • 抖音视频不能转发是怎么回事(抖音视频不能转发是哪里设置的)

    抖音视频不能转发是怎么回事(抖音视频不能转发是哪里设置的)

  • 微信提示有信息打开却没有(微信提示有信息但是找不到)

    微信提示有信息打开却没有(微信提示有信息但是找不到)

  • 移动iphone6可用电信卡吗(移动版iphone6可以用联通卡吗)

    移动iphone6可用电信卡吗(移动版iphone6可以用联通卡吗)

  • 手机显示lte是什么网络(手机显示lte是什么情况)

    手机显示lte是什么网络(手机显示lte是什么情况)

  • 腾讯视频为什么没有投屏tv的标志了(腾讯视频为什么看一会就卡住了)

    腾讯视频为什么没有投屏tv的标志了(腾讯视频为什么看一会就卡住了)

  • 快手口令怎么答题(如何把快手口令生成短链接)

    快手口令怎么答题(如何把快手口令生成短链接)

  • 乐视手机怎么设置锁屏(乐视手机怎么设置屏幕常亮)

    乐视手机怎么设置锁屏(乐视手机怎么设置屏幕常亮)

  • 手机拨号键盘不见了怎么办(手机拨号键盘不见了怎么恢复)

    手机拨号键盘不见了怎么办(手机拨号键盘不见了怎么恢复)

  • 文字底纹颜色怎么设置(文字底纹颜色怎么统一替换颜色)

    文字底纹颜色怎么设置(文字底纹颜色怎么统一替换颜色)

  • 三星S7和S8有什么区别(三星s7和s8哪个值得买)

    三星S7和S8有什么区别(三星s7和s8哪个值得买)

  • 怎样重新设置qq相恋日(怎样重新设置苹果手机id与密码)

    怎样重新设置qq相恋日(怎样重新设置苹果手机id与密码)

  • 手机照片怎么拼图一起(手机照片怎么拼接成一张)

    手机照片怎么拼图一起(手机照片怎么拼接成一张)

  • vivox27第一次充电多长时间(vivo第一次充电需要把电用完吗)

    vivox27第一次充电多长时间(vivo第一次充电需要把电用完吗)

  • 小米6x怎么插耳机(小米6x插耳机没反应)

    小米6x怎么插耳机(小米6x插耳机没反应)

  • 手机可以当投影仪用不(苹果手机可以投影吗)

    手机可以当投影仪用不(苹果手机可以投影吗)

  • 为什么三星s9卡顿严重(为什么三星s9卡不能用)

    为什么三星s9卡顿严重(为什么三星s9卡不能用)

  • Win11 Build 22449.1000更新里哪些内容?Win11 Build 22449更新介绍与安装方法

    Win11 Build 22449.1000更新里哪些内容?Win11 Build 22449更新介绍与安装方法

  • 简易计税是否要申报个税
  • 小规模企业申报时间
  • 劳务公司交企业所得税吗
  • 专家评审费属于劳务费吗
  • 材料委托加工
  • 公司收到虚开发票谁承担
  • 固定资产预计净残值可以为0吗
  • 个人独资所得税税率表最新
  • 物业公司收小区物业费吗
  • 地税按季报还是月报
  • 电子银行承兑汇票有风险吗
  • 某大宾馆因工作需要
  • 一个季度又叫什么
  • 环保税是甲方交的吗
  • 中小五金企业如何进行成本核算
  • 作废已开具的普通发票
  • 公司的公益性捐款合法吗
  • 政府发放的稳岗补贴怎么做账
  • 苹果手机录音配音乐怎么配
  • 专用发票可以抵税是什么意思
  • prevsrv.exe - prevsrv是什么进程 有什么用
  • 应交税费在借方是进项还是销项
  • 安装win11一直转圈要多久?
  • 为什么无形资产是非流动资产
  • 基于php开发
  • php文件包含的4种方式
  • 新成立股份有限公司股本构成
  • 公司股东转账进公司账户,会计分录
  • 未能连接到一个windows 的服务win7
  • 如何解决焦虑
  • 落基山国家公园攻略
  • 发票十万元版和百万元版
  • 前端环境部署到服务器开发环境
  • word2vec使用方法
  • 完税证明可以作废吗
  • wordpress 批量添加标签
  • java初学者教程
  • 进项税和销项税的分录
  • 民非企业银行利息计入其他收入,那增值税申报吗
  • 全年多少钱就得交税
  • 免征企业所得税的有哪些行业
  • 建筑企业开票都要交税吗
  • Windows2003 SQL2005添加系统用户修改系统登录密码
  • 印花税啥样
  • 银行承兑汇票背书转让会计分录
  • 中小企业会计科目
  • 补缴年报所得税怎么填
  • 建筑业发票可以抵扣制造业进项
  • 其他业务收入如何开票
  • 行程单如何验真伪
  • 应收应付可以相互冲销吗
  • 新单位年终工作总结
  • SQL2005、SQL2008允许远程连接的配置说明(附配置图)
  • mysql5.5安装配置教程
  • windows 10 build 9888
  • WIN10系统中WPS字体颜色浅
  • 如何更改win7
  • win10防火墙打不开错误代码
  • gzip压缩慢
  • linux创建makefile
  • ubuntu 18.04怎么用
  • sqlagent.exe - sqlagent是什么进程
  • linux bas
  • win7操作技巧
  • 文件系统的类型为raw
  • linux防病毒措施
  • 简单理解贴现
  • xp系统explorer停止工作
  • 批处理替换文件
  • unity gui教程
  • jquery中什么方法用于模拟光标悬停事件
  • ubuntu修改默认桌面环境
  • jquery dom对象
  • 广东电子税务局官网登录入口
  • 怎么打印纳税申报清单
  • 生猪屠宰行业
  • 版权登记条件
  • 中国银行手机银行app官方下载
  • 什么是个税扣缴期限
  • 环保税截止时间
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设