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

  • 苹果官网购物袋怎么减少物品(苹果官网购物袋结账网页打不开)

    苹果官网购物袋怎么减少物品(苹果官网购物袋结账网页打不开)

  • 苹果13有没有耳机(苹果13有没有耳机孔)

    苹果13有没有耳机(苹果13有没有耳机孔)

  • 手机淘宝怎么用微信支付(手机淘宝怎么用邮箱登录)

    手机淘宝怎么用微信支付(手机淘宝怎么用邮箱登录)

  • 苹果面容识别一直在转(苹果面容识别一直提示抬高抬低)

    苹果面容识别一直在转(苹果面容识别一直提示抬高抬低)

  • 戴尔电脑装系统按什么(戴尔电脑装系统按f几)

    戴尔电脑装系统按什么(戴尔电脑装系统按f几)

  • 安卓手机照片怎么改名字(安卓手机照片怎么导入u盘)

    安卓手机照片怎么改名字(安卓手机照片怎么导入u盘)

  • 美图秀秀怎么把三张图片横着拼在一起(美图秀秀怎么把背景图换成白色)

    美图秀秀怎么把三张图片横着拼在一起(美图秀秀怎么把背景图换成白色)

  • apn接入点是什么意思(手机apn接入点有什么不一样的)

    apn接入点是什么意思(手机apn接入点有什么不一样的)

  • 华为p30信号不稳定怎么办(华为p30手机突然信号不好是什么情况)

    华为p30信号不稳定怎么办(华为p30手机突然信号不好是什么情况)

  • 发短信蓝色和绿色的区别(发短信蓝色和绿色)

    发短信蓝色和绿色的区别(发短信蓝色和绿色)

  • 固态硬盘要不要缓存

    固态硬盘要不要缓存

  • 苹果平板a1432是什么型号(苹果平板a1432是第几代平板)

    苹果平板a1432是什么型号(苹果平板a1432是第几代平板)

  • 为什么有4g却上不了网(为什么有4g信号上不了网)

    为什么有4g却上不了网(为什么有4g信号上不了网)

  • 三张照片怎么合成一张(三张照片怎么合并)

    三张照片怎么合成一张(三张照片怎么合并)

  • 荣耀手环3和小米手环3对比(荣耀手环3和4参数对比)

    荣耀手环3和小米手环3对比(荣耀手环3和4参数对比)

  • ipad分析共享要开吗(ipad共享分析打开还是关闭)

    ipad分析共享要开吗(ipad共享分析打开还是关闭)

  • pdf用什么手机软件打开(pdf手机用什么软件打开手机)

    pdf用什么手机软件打开(pdf手机用什么软件打开手机)

  • ie兼容性设置在哪里(ie兼容性设置在哪里找不到)

    ie兼容性设置在哪里(ie兼容性设置在哪里找不到)

  • 号码拉黑了打电话有提示吗(号码拉黑了打电话过去还有信息不)

    号码拉黑了打电话有提示吗(号码拉黑了打电话过去还有信息不)

  • 华为手机悬浮球在哪里(华为手机悬浮球怎么关掉设置)

    华为手机悬浮球在哪里(华为手机悬浮球怎么关掉设置)

  • 手机qq如何开启临时会话(手机QQ如何开启密保)

    手机qq如何开启临时会话(手机QQ如何开启密保)

  • vivo手机设置永久亮屏(vivo手机设置永久不黑屏)

    vivo手机设置永久亮屏(vivo手机设置永久不黑屏)

  • 三星s10是不是双扬声器(三星s10是不是双卡双待)

    三星s10是不是双扬声器(三星s10是不是双卡双待)

  • 金山文档小程序怎么保存(金山文档小程序怎么多人在线编辑一个表格)

    金山文档小程序怎么保存(金山文档小程序怎么多人在线编辑一个表格)

  • 快手魔筷开店收费吗(快手卖货魔筷怎样开通)

    快手魔筷开店收费吗(快手卖货魔筷怎样开通)

  • 小红书买的东西怎么查(小红书买的东西别人能看到吗)

    小红书买的东西怎么查(小红书买的东西别人能看到吗)

  • 【SpringBoot】简单的文件上传和文件下载以及图片回显(springbootssm)

    【SpringBoot】简单的文件上传和文件下载以及图片回显(springbootssm)

  • 一般纳税人减免增值税政策
  • 出口退税逾期申报说明怎样写
  • 可回收再生资源有哪些
  • 职业年金单位缴费方式
  • 报表中的存货是如何填报的
  • 办公室水电费计算公式
  • 短期借款现金流
  • 本期缴纳前期应纳税额
  • 红字发票已抵扣和未抵扣是什么意思
  • 2016 营改增 对建筑企业影响
  • 减免的教育费附加和地方教育费附加怎么做分录
  • 公司分期付款买车可以抵税吗
  • 12月份未计提的费用汇算清缴如何调整?
  • 企业收到跨年度的政府补贴款,是否计入当年企业所得税
  • 收到公司of
  • 国税零申报怎么报
  • 补缴税款会计处理
  • 员工看病报销要计税吗
  • 土地增值税清算后补缴税款如何帐务处理
  • 以库存抵债的账务处理
  • 公司的班车费用怎么入账
  • 固定资产一次性扣除账务处理
  • 代开专票交的城建税怎么申报附加税
  • 增值税普通发票税率
  • 销售使用过的板房如何缴税?
  • mac怎么分区硬盘分区
  • 潜在表决权如何影响表决权
  • 社保怎么计提
  • win10开机启动项怎么删除
  • 在php中使用什么获取文件的修改时间
  • 税盘进项税额怎么做
  • php 上传
  • 发票开具时限是怎么规定的
  • 企业需要税务登记吗
  • 经营租入的设备为什么不属于资产
  • framework教程
  • React18的useEffect会执行两次
  • yii框架入门
  • yolo v4 v5
  • windows7怎么操作
  • 织梦系统基本参数
  • 个人所得税核定征收税率
  • 房租押金不退还的账务怎么处理
  • 预付发票能入账吗
  • 纳税人企业本月纳税额
  • 收回以前年度的应收账款较多能说明什么呢?
  • 含有增值税的工作有哪些
  • 物业管理公司收入确认
  • 流动资金周转次数计算公式
  • 因自然灾害发生固定资产净损失
  • 股东以固定资产出资
  • 可供出售债权投资
  • 安全生产责任险是强制险吗
  • 蔬菜销售方式
  • 建筑升降机厂家
  • 商业会计的任务有哪些
  • 公司买支票需要带什么资料
  • sqlserver
  • 判断数据库表是否为空
  • mysql安装不成功怎么办
  • sql怎么分组查询
  • 数据库 mysql
  • centos6.5共享文件夹
  • win7没有printtopdf
  • windows十桌面
  • win7远程设置在哪
  • 虚拟机远程控制局域网
  • Windows 8 Consumer Preview 中的新热键介绍
  • js兼容ie
  • python输出代码怎么写
  • 物理引擎演示
  • python编写api接口
  • unity ui批处理
  • jquery遍历表单元素
  • jquery mobile 加载页面
  • javascript的函数调用
  • 成本费用总额在报表哪里看
  • 你认为作为一名护士应具备哪些素质
  • 车船发票什么样子
  • 防疫物资开票免税吗
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设