关于微信触发2次请求的问题【已解决】【原】

关于微信触发2次请求的问题  [终极方案请直接点此跳转]

现象描述

测试组在测试环境验证问题的时候,发现在手机微信页面点击某个按钮,却触发了2次ajax请求.
于是开发组小伙伴在修正这个bug的过程中, 一会怀疑前端js问题, 一会又怀疑后端java代码问题, 却没考虑到网络链路转发机制的问题.
后来笔者提议不改前台,只改后台,注释全部业务代码,改成让线程直接睡眠 Thread.currentThread(N * 1000 ) , 动态修改N参数来模拟业务时间, 发现 sleep 10s以下的请求只会触发一次, 当sleep 10s以上时,请求会无缘无故触发2次.

于是有了如下猜想

微信在请求其它服务器时, 如果目标服务器需要10秒以上才能响应,那么微信会自动再次触发相同请求. 时序图如下:

 

我的现象

于是开始验证, 找各种机型和APP试验,得出下表:

类型 客户端 请求类型 服务端 服务端 Sleep 秒数 服务端收到请求次数 补充说明
手机android 微信 post 阿里云ECS主机 2 1次  
手机android 微信 post 阿里云ECS主机 18 2次  
手机android qq浏览器 post 阿里云ECS主机 2 1次  
手机android qq浏览器 post 阿里云ECS主机 18 2次  
手机android uc浏览器 post 阿里云ECS主机 2 1次  
手机android uc浏览器 post 阿里云ECS主机 18 1次  
手机android 傲游浏览器 post 阿里云ECS主机 2 1次   
手机android 傲游浏览器  post 阿里云ECS主机 18 1次  
苹果iphone 微信 post 阿里云ECS主机 2 1次  
苹果iphone 微信 post 阿里云ECS主机 18 1次   
苹果iphone qq浏览器 post 阿里云ECS主机     未试验
苹果iphone qq浏览器 post 阿里云ECS主机     未试验
苹果iphone uc浏览器 post 阿里云ECS主机     未试验
苹果iphone uc浏览器 post 阿里云ECS主机     未试验
联想/华硕笔记本PC 微信 post 阿里云ECS主机 2 1次  
联想/华硕笔记本PC 微信 post 阿里云ECS主机 18 1次  
联想/华硕笔记本PC qq浏览器 post 阿里云ECS主机 2 1次  
联想/华硕笔记本PC qq浏览器 post 阿里云ECS主机 18 1次  
联想/华硕笔记本PC 傲游浏览器 post 阿里云ECS主机 2 1次  
联想/华硕笔记本PC 傲游浏览器 post 阿里云ECS主机 18 1次  

最终得出结论为: android手机端通过微信或手机QQ浏览器 ,  用ajax触发post请求, 调用任意目标服务器,如果服务器业务处理需要10秒以上才能响应, 会导致腾讯(微信服务器)自动再次触发相同请求. 

普通方案

在请求地址后追加 &connect_redirect=1 即可让请求不再重发.

怎么找到?非常感谢下链接中的9楼, 真是茫茫人海 , 眼前一亮 , 微信开放破平台都没提及该问题, 真是蛋疼.

微信公众号网页授权时,回调两次?--https://developers.weixin.qq.com/blogdetail?action=get_post_info&lang=zh_CN&token=&docid=b8f9f09573e92ffb0e23308d54bcdcf7

而在第二页中还有人提及"加&connect_redirect=1 可以解决80%左右,但极小部分android手机加上后仍旧无效"的说法, 但在笔者目前的自测用例中尚未发生 , 忽略不计了.

如何自测

笔者在阿里云一台ECS服务器, 很早前就专门搭建了一个网页版的接口对接工具,地址为http://www.kingtool.top/kingtool (注ECS服务器2018年12月底到期)

延迟返回接口地址: http://www.kingtool.top/kingtool/httptest?delay=18&connect_redirect=1

  • delay=18 是我自己配置的服务端动态参数,意为让我的java后台sleep(18)秒,即18秒后返回响应报文
  • connect_redirect=1 是腾讯(微信)服务器配置的动态参数, 意为只触发一次请求, 超时也不重发。
  • 为了解决ajax跨域问题, 请求链路为 前台页面-->触发ajax请求-->阿里云ecs-->阿里云ecs java代码调用目标服务器真实地址

此时post请求一次我的阿里云延迟返回接口, 18秒结束后, 后台日志只打印一次,成功解决.

我的请求报文"天行健,君子以自强不息,地势坤,君子以厚德载物"在后台日志中的的确确只打印一了一次.

 

终极方案, 使用redis或memcache缓存【推荐】at 2018/05/04

不知为何之前方案的connect_redirect=1参数突然MMP失灵了,只能另寻出路。

由于面向互联网的接口,多次相同报文的请求的事实终究无法避免,要从根本上解决该问题, 还是得从服务方的角度出发:

  • 要么精简自身系统以缩短处理时间绕开微信2次触发问题,
  • 要么采用锁机制控制第2次请求

于是又画了以下时序图,核心思想在于让第2次的处理从第1次的请求结果中去拿。

而在集群模式下,A系统集群中的各server无法感知其它server的处理情况,所以解决重点在于使用独立的redis或memcache缓存,并配以自动失效时间以保证内存的稳定性(防缓存无限增大),采用该方案后可看到第2次绿条的处理时间明显比第1次红条短多了。

 (在下图样例中 假设A系统处理业务需要11秒,  那么第2次不会再处理业务,最多比第一次多等1~2秒左右 , 最终就算2次触发响应时间最多也不会超过12秒,)

 

简要流程说明

  1. 以第一次红条运行时间11秒为前提
  2. 首先计算请求报文的md5值, 作为缓存的key,也就是查询要用的唯一识别码,
  3. 0.5秒时,第一次的红条请求到达, 以md5为key去缓存中查询对应的value,必然为空,1秒时,设置对应value为0,作为正在处理的标志.
  4. 然后第一次红条请求一直在处理中,时间往下嘀嗒嘀嗒走到10秒,
  5. 此时时间超过了10秒,
  6. 10.1秒时,第二次的绿条请求到达,还是以md5为key去缓存中查询对应的value,10.8秒去查询的时候会查到md5对应的value为0,发现该条数据正在被第一次的红条处理中,好吧,那绿条每隔1秒再去查询md5对应的value, 判断是否已处理完(非0)了
  7. 11秒时,第一次的红条请求顺利结束,把缓存中md5对应value从0更新成返回的业务报文(非0),作为处理完成的标志.
  8. 11.8秒(相当于12秒),第二次的绿条发现,缓存中md5为key对应的value,已从0更新成了返回业务报文(非0),那么就把该返回业务报文作为真正需要的业务报文返回出去,经历时间大约12秒左右.
  9. 于是,当第一次请求如果需要17秒处理时间,那么每二次会在17+1秒返回, 当第一次如果需要23秒处理时间,第二次请求会在23+1秒返回,依次类推.

 补充说明

  • 至于是否用0作为正在处理标志位,依据你的业务而定, 要是业务本身就返回0或1,那肯定需要换成true,false或其它特殊字符作为正在处理标志了.
  • 另外如果您的业务本身请求就很频率(比如平均每隔个5秒就会有请求过来, 而每次请求时间又需要个20来秒)那么本方法由于第二次请求需要线程阻塞在那,等着第一次处理结束,所以会不太适合, 可能会浪费大量线程资源 , 此时最好从业务本身角度出发看看能不能缩短处理时间, 要想鱼和熊掌都能兼得,那各位哥请把自家的服务器和网络硬件设备镀层金吧.

 

 

 

 

 

 

 

 

他人现象

求助微信一次ajax请求,会访问两次 [问题点数:20分,无满意结帖,结帖人showbo]--https://bbs.csdn.net/topics/391816470       24楼说:

我也遇到这样的问题,微商城提交订单,超过10秒钟,微信浏览器重发,导致订单重复提交的问题。拦截器里看到确实有两次提交,确定我们代码里不做超时重试,然后这种情况似乎跟手机有关,我的手机就没有出现这样的问题,其他两个同事的手机就出现,巨坑

 

后台收到微信重复请求问题--https://blog.csdn.net/gotohomebye/article/details/78508741  作者说:

都困扰快2周了,网上各种查理不出头绪,假如真是查出来问题这么严重的话,微信浏览器研发团队不可能不重视,现在运行中的这么多微信公众号不可能没遇到类似问题。其他浏览器都是正常的,肯定代码没问题,微信浏览器的问题也不大

 

作者:whatlonelytear
本文地址:https://www.cnblogs.com/whatlonelytear/p/8934738.html
欢迎转载,请在明显位置给出出处及链接。

(1)  热部署:就是容器状态在运行的情况下重新部署整个项目.在这种情况下一般整个内存会清空,重新加载.简单来说就是Tomcat或者其他的web服务器会帮我们重新加载项目.这种方式可能会造成sessin丢失等情况.  (2)热加载:就是容器状态在运行的情况下重新加载改变编译后的类.在这种情况下内存不会清空,sessin不会丢失,但容易造成内存溢出,或者找不到方法。因为内存无法转变成对像.一般改变类的结构和模型就会有异常,在已经有的变量和方法中改变是不会出问题的
posted @ 2019-03-25 17:36  鳳舞九天  阅读(1480)  评论(0编辑  收藏  举报