位置: 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使用教程)

  • 微信转账怎么加对方好友(微信转账怎么加回好友)

    微信转账怎么加对方好友(微信转账怎么加回好友)

  • 爱普生打印机二维码在哪(爱普生打印机二维码如何获取)

    爱普生打印机二维码在哪(爱普生打印机二维码如何获取)

  • word如何添加自制封面(word如何添加自定义页码)

    word如何添加自制封面(word如何添加自定义页码)

  • 微信没有网络可以计步吗(微信没有网络可以删除好友吗)

    微信没有网络可以计步吗(微信没有网络可以删除好友吗)

  • qcy左右耳配对不上(qcy耳机左右耳不匹配)

    qcy左右耳配对不上(qcy耳机左右耳不匹配)

  • xr有红外线功能吗(iphonexr有红外线)

    xr有红外线功能吗(iphonexr有红外线)

  • 微信好友点开没有朋友圈那一栏(微信好友点开没有朋友圈是否被拉黑)

    微信好友点开没有朋友圈那一栏(微信好友点开没有朋友圈是否被拉黑)

  • 京东铜牌是什么等级(手机京东铜牌在哪里看)

    京东铜牌是什么等级(手机京东铜牌在哪里看)

  • 快手背景图看不见了(快手背景图不显示怎么回事)

    快手背景图看不见了(快手背景图不显示怎么回事)

  • 快手小店订单怎么查不到下的单呢(快手小店订单怎么隐藏)

    快手小店订单怎么查不到下的单呢(快手小店订单怎么隐藏)

  • mmap用什么软件打开(mmap用什么软件打开苹果)

    mmap用什么软件打开(mmap用什么软件打开苹果)

  • 电脑开机后键盘鼠标没反应怎么办(电脑开机后键盘灯不亮怎么办)

    电脑开机后键盘鼠标没反应怎么办(电脑开机后键盘灯不亮怎么办)

  • 腾讯视频vip有什么功能(腾讯视频vip有什么好处)

    腾讯视频vip有什么功能(腾讯视频vip有什么好处)

  • 苹果7p耳机插上没声音(苹果7p耳机插上没反应怎么设置)

    苹果7p耳机插上没声音(苹果7p耳机插上没反应怎么设置)

  • 在对话框中,允许同时选中多个选项的是(如果在对话框要进行各个选项卡之间的切换,可以使用的)

    在对话框中,允许同时选中多个选项的是(如果在对话框要进行各个选项卡之间的切换,可以使用的)

  • vivos5怎么设置放大手势(vivo手机放大功能怎么设置)

    vivos5怎么设置放大手势(vivo手机放大功能怎么设置)

  • 时间设置在哪里(24小时时间设置在哪里)

    时间设置在哪里(24小时时间设置在哪里)

  • 微星主板显卡插哪个槽(微星主板显卡插槽)

    微星主板显卡插哪个槽(微星主板显卡插槽)

  • 抖音举报了对方有用吗(抖音举报了对方会有什么处罚)

    抖音举报了对方有用吗(抖音举报了对方会有什么处罚)

  • 快手两格视频怎么制作(快手两格视频怎么设置)

    快手两格视频怎么制作(快手两格视频怎么设置)

  • 小米解锁机有什么坏处(小米解锁机有什么区别)

    小米解锁机有什么坏处(小米解锁机有什么区别)

  • 电脑平车绕线器怎么修(电脑平车绕线器不转)

    电脑平车绕线器怎么修(电脑平车绕线器不转)

  • phpcms v9不能生成静态页面怎么办(phpcms程序)

    phpcms v9不能生成静态页面怎么办(phpcms程序)

  • 增值税纳税申报表在哪里查询
  • 申报个人所得税在哪里报
  • 个人所得税可以不交税吗
  • 主营业务收入增长率计算公式
  • 小微企业增值税起征点是多少
  • 机动车销售发票可以跨年抵扣吗
  • 公益性捐赠如何进行纳税调整
  • 现金日记账定金和实收怎么记
  • 确认一个会计项目应符合的基本标准有
  • 营改增后场地租赁税率
  • 房地产母公司将其土地变更到其全资子公司
  • 什么计入在途物资
  • 新建厂房装修费账务处理
  • 关于商业健康保险产品的规范和条件
  • 生物性资产是什么
  • 个税申报需要补税是什么情况
  • 租车费增值税专用发票
  • 企业筹建期间可以自己发农民工工资么
  • 财政拨款税务处理方法
  • 法律诉讼费计入什么会计科目
  • 销售商品包安装如何确认收入
  • 固定资产入账价值计算公式
  • 鸿蒙3.0手机适配名单荣耀
  • 冲销去年暂估收入
  • Linux系统中quota磁盘命令的相关使用解析
  • Win10任务栏没有wifi图标
  • linux基本命令有哪些
  • 年终奖是多发一个月工资吗
  • vue引入echarts柱状图
  • 世界上最早的数码相机
  • 运输取得的收入
  • 有限公司股权转让怎么办理流程
  • phpexcel插件
  • 如何汇算清缴,会计新手求指教
  • 金税三期税收管理系统响应异常返回信息:没有查询
  • php连接
  • vue3使用教程
  • 毕业论文笔记怎么写
  • php中array用法
  • element excel
  • 退回社保怎么做分录
  • 固定资产有金额界定吗
  • 收到专票不认证不抵扣可以吗
  • 为员工购买意外险会计处理
  • 社保可以补交吗
  • 什么情况下提供个人征信
  • 增值税报完了能改么
  • 电子发票与纸质发票具有不同的法律效力
  • 往来对账的作用
  • 一般纳税人无票收入填在哪一栏
  • 月底主营业务成本会计分录
  • 银行承兑汇票是什么意思
  • 住宿费开的增值税专用发票怎么记账
  • 预收账款冲销账户怎么做
  • 盈余公积转增实收资本对会计要素的影响
  • 工伤保险交了就可以报销吗
  • 社保退休金计算方法
  • 结转固定资产清理损益的账务处理
  • 固定资产改建支出的扣除规定
  • 为员工买的商业保险怎么做账
  • 长期待摊费用怎么做账
  • 残保金新企业用交吗
  • 旅游饮食服务企业的特点包括
  • win8电脑网络受限
  • ubuntu安装chrom
  • mac文件怎么用
  • searchnav.exe - searchnav是什么进程 有什么用
  • linux html编辑器
  • win7右下角时间怎么显示年月日
  • eclipse win7
  • A Type-Safe Event System for Unity3D
  • js经典案例代码大全
  • js查看浏览器信息
  • excel执行python脚本
  • 快速学会java
  • 建筑单位外地缴税
  • 如何网上申领税票发票
  • 税务金三系统什么意思?
  • 河南省公安厅热线电话
  • 依法征拆
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设