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

  • vivos12与12pro区别(vivos10pro和苹果12参数对比)

    vivos12与12pro区别(vivos10pro和苹果12参数对比)

  • 苹果id名可以修改吗(苹果id的名字能改吗)

    苹果id名可以修改吗(苹果id的名字能改吗)

  • 华为p40耳机如何插(华为p40耳机如何控制暂停)

    华为p40耳机如何插(华为p40耳机如何控制暂停)

  • 微信文件另存为就死机(微信文件另存为桌面)

    微信文件另存为就死机(微信文件另存为桌面)

  • win10桌面黑屏闪烁(win10桌面黑屏一闪一闪是什么情况)

    win10桌面黑屏闪烁(win10桌面黑屏一闪一闪是什么情况)

  • 我国的手机号为什么是11位(我国使用的手机号码为多少位)

    我国的手机号为什么是11位(我国使用的手机号码为多少位)

  • ios13.1.3建议更新吗(ios13.1.3要升级ios14吗)

    ios13.1.3建议更新吗(ios13.1.3要升级ios14吗)

  • 小红书直播结束后可以看回放吗(小红书直播结束忘截图怎么办)

    小红书直播结束后可以看回放吗(小红书直播结束忘截图怎么办)

  • 手机腾讯视频怎么下载电影到本地(手机腾讯视频怎么退出登录)

    手机腾讯视频怎么下载电影到本地(手机腾讯视频怎么退出登录)

  • 打电话老是自己挂断(电话总是自己打出去)

    打电话老是自己挂断(电话总是自己打出去)

  • 店铺受限买家无法付款(店铺受限买家无法退货)

    店铺受限买家无法付款(店铺受限买家无法退货)

  • 微信夜览怎么设置(微信怎样打开夜间)

    微信夜览怎么设置(微信怎样打开夜间)

  • rx580配什么电源(rx580搭配什么电源)

    rx580配什么电源(rx580搭配什么电源)

  • 手机为什么会停机(手机为什么会停机打电话对方停机怎么回事)

    手机为什么会停机(手机为什么会停机打电话对方停机怎么回事)

  • 头条买东西怎么看订单(头条买东西怎么发视频)

    头条买东西怎么看订单(头条买东西怎么发视频)

  • wps怎么删除空白页(wps怎么删除空白表格框)

    wps怎么删除空白页(wps怎么删除空白表格框)

  • 小度在家怎么切换wifi(小度在家怎么切换成人模式)

    小度在家怎么切换wifi(小度在家怎么切换成人模式)

  • wifi5和wifi6差别(电视wifi5和wifi6差别)

    wifi5和wifi6差别(电视wifi5和wifi6差别)

  • 联想小新13和14的区别(联想小新13和14充电器一样嘛)

    联想小新13和14的区别(联想小新13和14充电器一样嘛)

  • waf和防火墙的区别(wifi防火墙是什么)

    waf和防火墙的区别(wifi防火墙是什么)

  • 苹果xr有微信分身功能吗(苹果XR有微信分身功能吗)

    苹果xr有微信分身功能吗(苹果XR有微信分身功能吗)

  • 方舟编译器怎么用(方舟编译器怎么安装)

    方舟编译器怎么用(方舟编译器怎么安装)

  • 西瓜视频如何投屏(西瓜视频如何投放广告)

    西瓜视频如何投屏(西瓜视频如何投放广告)

  • 如何清理IE浏览器的鼠标右键菜单?(怎样清理ie)

    如何清理IE浏览器的鼠标右键菜单?(怎样清理ie)

  • day53-马踏棋盘(马踏棋盘游戏规则)

    day53-马踏棋盘(马踏棋盘游戏规则)

  • 宁波财税网会计招聘
  • 发给员工的福利费可以全部税前抵扣吗
  • 交易性金融资产的账务处理
  • 政府发放的人才补贴,企业可以增加条款要求离职返还吗
  • 过路费是来回收费还是单向
  • 向个人借款计入什么会计科目
  • 保障房异地建设费缴纳契税吗?
  • 一般纳税人每个月需要报哪些税
  • 工厂出租厂房税率
  • 承包方给发包方付费
  • 个人所得税更正申报有滞纳金吗
  • 公司购进的商品自己用的,税金怎么走账
  • 普通支票如何转账
  • 上个月的流量这个月可以用吗
  • 通用pe工具箱安装教程
  • 水利工程水费怎样计算
  • 路由器登录密码忘了怎么设置
  • phpstorm ftp
  • 长期股权投资引入新投资者账务处理
  • 企业存款利息收入增值税
  • 下岗再就业有什么优惠政策
  • 公允价值变动损益在利润表哪里
  • 会计凭证附件规范要求
  • 今天是520该说什么
  • thinkphp i方法
  • js获取本机ip地址
  • 若依框架用到的技术
  • 创建ftp软件
  • pythontime模块
  • phpcms怎么用
  • 确认应付职工薪酬如何计算
  • vue新手教程
  • 投资收益的核算依据
  • 加班补贴费
  • 帝国cms8.0版
  • 印花税计入管理费用还是税金
  • 营业外收入汇算清缴时需要调增吗
  • 一次性发放的年终奖金应按工资薪金代扣代缴个税
  • 印花税实际缴纳时计入
  • 认缴意思
  • 车辆购置税的计税价格为不含增值税的全部价款
  • 利息收入的会计分录
  • 办公室搬迁工作
  • 帮客户代付保证金合法吗
  • 劳务费个人所得税核定征收
  • 新增建筑物
  • 在异地施工就要在异地交税吗
  • 存货毁损损失应计入的科目是
  • 施工人员的工资计入
  • 行政事业单位如何开发票
  • 收到红字进项发票先勾选再做转出吗
  • 外经证是干什么用的
  • 专票当普票用进项需要转出吗
  • 主营业务成本为什么借增贷减
  • 个体工商户达到多少缴税
  • executesql 存储过程
  • sql server 服务器配置
  • 如何创建桌面快捷方式电脑
  • 怎么停止u盘自动运行
  • 使用dhcp的好处有哪些
  • macbook像素能不能提高
  • java调用so库文件
  • 苹果mac操作系统版本
  • dllhost应用程序异常
  • 进程audiodg.exe
  • bboy.exe进程是病毒吗 bboy进程安全吗
  • windows无法访问指定设备路径权限
  • xp桌面浏览器图标不见了
  • win10无法启动安全服务中心怎么办
  • 日历功能在哪里设置
  • 如何解决win10系统开机一直转圈圈的问题
  • linux diy
  • django 不同app间model引用
  • opengl和directX区别
  • windows、linux
  • javascript tr
  • python自带的http模块详解
  • jq easyui
  • 不错的二本院校有哪些
  • python读dat数据
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设