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

  • 淘宝店推广何不从模仿开始(淘宝店推广何不是骗局)

    淘宝店推广何不从模仿开始(淘宝店推广何不是骗局)

  • 支付宝显示网络出错(支付宝显示网络不可用怎么回事)

    支付宝显示网络出错(支付宝显示网络不可用怎么回事)

  • 苹果手机麦克风启用不成功怎么办(苹果手机麦克风权限怎么打开)

    苹果手机麦克风启用不成功怎么办(苹果手机麦克风权限怎么打开)

  • Excel怎么复制很长一列数据(excel怎么快速复制很多行)

    Excel怎么复制很长一列数据(excel怎么快速复制很多行)

  • 指纹手机怎么设置指纹锁(指纹手机设置)

    指纹手机怎么设置指纹锁(指纹手机设置)

  • 淘宝会警告几次后封号(淘宝收到警告会影响流量吗?)

    淘宝会警告几次后封号(淘宝收到警告会影响流量吗?)

  • 设置微信朋友圈视频不压缩(设置微信朋友圈权限怎么设置)

    设置微信朋友圈视频不压缩(设置微信朋友圈权限怎么设置)

  • qq空间举报多久恢复(qq空间举报人成功了那个人会知道吗)

    qq空间举报多久恢复(qq空间举报人成功了那个人会知道吗)

  • 华为桌面锁定布局设置在哪里(华为桌面锁定布局什么意思)

    华为桌面锁定布局设置在哪里(华为桌面锁定布局什么意思)

  • excel银行卡号后面变为0000了(excel银行卡号后面变0怎么调回来)

    excel银行卡号后面变为0000了(excel银行卡号后面变0怎么调回来)

  • 华为电源键在哪里(华为手机电源键在哪里)

    华为电源键在哪里(华为手机电源键在哪里)

  • 如何让关联qq不收消息(怎么让关联的qq号不让他登)

    如何让关联qq不收消息(怎么让关联的qq号不让他登)

  • 用友软件怎样删除凭证(用友软件怎样删除记账凭证)

    用友软件怎样删除凭证(用友软件怎样删除记账凭证)

  • 怎样删除微信视频记录(怎样删除微信视频号里的点赞)

    怎样删除微信视频记录(怎样删除微信视频号里的点赞)

  • 苹果x原装耳机怎么分辨(苹果X原装耳机多少钱一副)

    苹果x原装耳机怎么分辨(苹果X原装耳机多少钱一副)

  • 微信云空间怎么进入(微信云空间怎么关闭)

    微信云空间怎么进入(微信云空间怎么关闭)

  • p30pro怎么用夜视模式(华为p30pro夜景摄像)

    p30pro怎么用夜视模式(华为p30pro夜景摄像)

  • 怎样强行取消故事锁屏(如何取消强行停止使用)

    怎样强行取消故事锁屏(如何取消强行停止使用)

  • 网段与lan口ip地址冲突(网段与lan口ip地址冲突怎么解决)

    网段与lan口ip地址冲突(网段与lan口ip地址冲突怎么解决)

  • ppt2016版暗香扑面在哪(ppt2010暗香扑面主题)

    ppt2016版暗香扑面在哪(ppt2010暗香扑面主题)

  • 陌陌天天抢车位怎么关闭(陌陌天天抢车位永久魅力车哪来的)

    陌陌天天抢车位怎么关闭(陌陌天天抢车位永久魅力车哪来的)

  • 华为手环3怎么开启微信信息提醒(华为手环3怎么测心率)

    华为手环3怎么开启微信信息提醒(华为手环3怎么测心率)

  • 途牛旅游如何与微信解除授权(途牛旅游订单怎么退款)

    途牛旅游如何与微信解除授权(途牛旅游订单怎么退款)

  • word2003安全模式怎么解除(word2003安全模式解决方法)

    word2003安全模式怎么解除(word2003安全模式解决方法)

  • 增值税专用发票可以开电子发票吗
  • 专用发票和普通票有区别吗
  • 购买办公用品为什么不能用现金
  • 水利基金的计税税率6
  • 物业费按年交有优惠吗
  • 增值税17-16-13的时间
  • 发票加盖公章有用吗
  • 电子银行承兑汇票已承兑已锁定
  • 装修保证金怎么记账
  • 存货盘亏损失可以扣除吗
  • 零申报企业年报资产总额怎么填
  • 购入农产品的增值税率
  • 顾客办理会员卡要退怎么办
  • 含税价与不含税价哪个便宜
  • 7月1号出台的政策
  • 小规模销项负数怎么报税
  • 进口应税消费品所支付的金额不包括
  • 土地增值税进项税
  • 免税进口种子要符合哪些条件?
  • 出租固定资产收取租金
  • windows10如何设置输入法
  • win7为什么现在不能用了
  • 贴现利息由谁承担
  • 无形资产入账摊销
  • 出差的住宿费较高的原因
  • 台式电脑机箱怎么选
  • windows10闹钟不响
  • 周转材料属于什么科目
  • php提高性能
  • 如何设置电脑从硬盘启动
  • php+ mysql教程
  • win10 热键
  • isahelp是什么进程
  • 认缴出资没到位违法吗
  • 最小的的照片
  • 银行存款利息是按月结还是按年
  • 增值税检查调整科目还在用吗
  • 现金流量表的作用和意义
  • php获取post请求参数
  • 如何用java写接口
  • php高并发api接口怎么处理
  • 不构成业务的反义词
  • wordpress部署到github
  • js对象模型是什么
  • 经营成本包括五项内容
  • 利润分配未分配利润在报表里怎么体现
  • 发票复核人一定要填吗
  • 母子公司之间无偿划转固定资产
  • 小规模减免税收入
  • 原材料保险公司赔偿会计分录怎么写
  • 小规模收到专票可以当普票用吗
  • 银行付款退回重付怎么办
  • 企业收到委托开具发票
  • 免征的教育费附加怎么做账
  • 开票必须填写开户银行吗
  • 发票入账有效期是多长时间
  • 如何理解应收账款平均余额
  • 会计调账是什么意思
  • 如何建立一个
  • win8.1鼠标没反应
  • 启动npm服务
  • vcspawn.exe
  • win10无法安装软件怎么解决
  • linuxcut命令详解
  • linux文件latin1转换为utf
  • js表单验证实例怎么写
  • javascript例题
  • fiddler如何抓取app接口的
  • python上传本地文件的方法
  • python标准库参考手册
  • javascript基于什么的语言
  • js中的
  • javascript 自动执行
  • 公司开票限额怎么增加
  • 在深圳,企业房贷怎么办
  • 交了增值税还用交税吗
  • 安置残疾人即征即退
  • 领完发票后 怎么读取
  • 江苏电子口岸卡邮寄大概需要多久
  • 北京地税查询官网
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设