跟进一个 jserror 问题带来的思考

作者:徐杰

wns-cgi的业务使用同学反馈了一个问题:在安卓手Q的空间里,打开小游戏,然后进入空间宠物,聊天窗口,发送消息。这里会注册一个jsbridge回调,发送消息后,回调后,小游戏页面会报一个js的error,比较奇怪,报错截图如下

报错信息倒是很明确,是因为方法未定义导致的异常;触发条件是因为宠物聊天用的是wns-cgi,所以最初怀疑是否和wns-cgi有关,就由我和xixinhuang一起在跟进这个问题;庆幸的是问题必现,这样我们就可以复现和构造各种场景;下面简单总结下定位的过程:A打开了B页面,B页面用了wns-cgi进行聊天(打开了输入法控制面板),B页面没出现异常,但是A页面的jserror报错了;

(1)根据报错信息最初是想确认为什么A页面会收到这个回调?明明是在B页面的操作啊?于是赶紧拉上了客户端同学和mqq.js的开发同学进行定位,可是没有什么头绪和进展,客户端同学觉得没问题啊,调用wns-cgi,客户端正常的执行了一段回调;wns-cgi的请求对象如下:

{"method":"post","url":"http://h5.qzone.qq.com/webapp/json/pet_chat/Chat?uin=2577199125&g_tk=830846970&t=0.36583463430175467","body":{"qua":"V1_AND_SQ_7.2.5_0_RDM_B","iEventID":2,"stCommInfo.lLoginUin":2577199125,"stCommInfo.lHostUin":2577199125,"stCommInfo.stGeoInfo.lLon":113933731,"stCommInfo.stGeoInfo.lLat":22540520,"stCommInfo.stGeoInfo.lAlt":0,"stCommInfo.strQua":"V1_AND_SQ_7.2.5_0_RDM_B","strText":"好红红火火恍恍惚惚","mapExtInfo.source":"h5","mapExtInfo.gpstype":"1","mapExtInfo.devicetype":"1","mapExtInfo.device_info":"","mapExtInfo.userPetId":10116,"mapExtInfo.hostPetId":10116,"mapExtInfo.first_sentence_id":"","strCookie":""},"timeout":0,"dataNeedBase64":false,"callback":"__MQQ_CALLBACK_AUTO_35"} 客户端

客户端处理后,loadurl调用的js代码如下:

(window.mqq && mqq.version > 20140616001 && mqq.execGlobalCallback||function(cb) {window[cb] && window[cb].apply(window, [].slice.call(arguments, 1));}).apply(window, ["__MQQ_CALLBACK_AUTO_35", {"status":4,"url":"http:\/\/h5.qzone.qq.com\/webapp\/json\/pet_chat\/Chat?uin=2577199125&g_tk=830846970&t=0.36583463430175467","code":0,"dataIsBase64":false,"data":"{\"ret\":0,\"msg\":\"\",\"data\":{\"stActionSet\":{\"iID\":0,\"vecActionInfo\":[{\"vecActionNode\":[{\"vecName\":[\"f_speak\"],\"iLoopCount\":1,\"fSpeed\":1}],\"stContentInfo\":{\"iContentType\":1,\"strText\":\"小宠的语文阔是数学老师教的哦~ 诗词歌赋样样精通!你想考我吗!\",\"strUrl\":\"\",\"vecChoice\":[],\"iStayTimeMs\":-1,\"stFile\":{\"iFileId\":0,\"strName\":\"\",\"strUrl\":\"\",\"strMd5\":\"\",\"iSize\":0,\"iFileType\":0,\"iWidth\":0,\"iHeight\":0},\"stUgcVideo\":{\"strVideoId\":\"\",\"iAppId\":0,\"iVideoWidth\":0,\"iVideoHeight\":0,\"iOperateMask\":0,\"iVideoState\":0,\"uiDurationTimeMs\":0,\"strVideoUrl\":\"\",\"strCoverImgUrl\":\"\",\"iCoverWidth\":0,\"iCoverHeight\":0,\"strOrgKey\":\"\",\"strCurKey\":\"\",\"strCid\":\"\",\"strUgcKey\":\"\",\"lUin\":0,\"strNick\":\"\"},\"vecPetQuickCommentPic\":[],\"stRelationChain\":{\"iTotal\":0,\"vecFriInfo\":[]},\"stFmInfo\":{\"strShowId\":\"\",\"strShowName\":\"\",\"strAudioUrl\":\"\",\"strCoverUrl\":\"\",\"strOwner\":\"\",\"iShowDuration\":0,\"strShowUrl\":\"\"}},\"strTrace\":\"\",\"iDelayMs\":0}]},\"mapExtInfo\":{},\"stValidator\":{\"lUin\":2577199125,\"strModelKey\":\"mao_phase1_20172217\"},\"strCookie\":\"chat_session_id=0A9E20724B9A4AF28A0CECA0BE2FC8CF\"}}","httpStatusCode":200,"header":{"x-powered-by":"TSW\/Node.js","cache-control":"no-cache","connection":"keep-alive","keep-alive":"timeout=10","vary":"Origin, Accept","mod-map":"webapp_json","content-type":"application\/json; charset=UTF-8","content-length":"1082","date":"Wed, 13 Sep 2017 07"}}]);

看起来调用wns-cgi没啥问题啊,问题重新回到了我们这边;

(2)我和xixin同学开始了错误的重新定位过程,既然报错是来自A页面,一定是和mqq的什么事件监听有关,之前我处理过mqq.debug.js文件,先定位到和这个错误有关的代码,是在invokeClientMethod中注册的,如下图:

那我们代理上这个mqq.js,然后在这个方法中加上console.trace()看看调用栈,会不会有帮助?

同时我们在A页面有涉及到mqq.invoke等方法的地方,能加上callback的,我们统一都加上测试代码,比如类似的代码,如果A页面有执行这个callback,我们可以从对应的ns和method入手查:

var img = new Image();
img.src = 'https://h5.qzone.qq.com/proxy/domain/www.qq.com/xxx/xxx';

结果上面的img的请求根本没发出来,而且我们在invokeClientMethod中设置的错误信息,只有openUrl的日志,这个是合理的,因为A打开B页面,A调用的就是openUrl,我们感觉有些没辙了。。。客户端没问题,H5也没问题,问题在哪?明明有jserror报错,而且也很怀疑是和事件监听有关,可是我们H5能做的比较有限;

(3)峰回路转:

在和xixin重现错误的时候,碰到一个常见的联调问题,就是js缓存,经常需要重新清理缓存,而且也可能代理不到本地,给重现带来一定的困扰;比如A打开B,如果A加上了_proxy=1走到了wns-html,可能都没有js请求,这样我们设置的什么代理都没用,清理缓存也不一定行。。。很焦急的看着时间慢慢流逝。。。没办法,只能先把url地址修改下,去掉_proxy=1,在聊天消息框中打开对应的url,总算可以代理上了,可以看到正常的invokeClientMethod的日志信息,意外出现了,在AIO聊天窗口打开的页面,居然没有报错???为什么?目前来看已有的信息,就是为什么从好友动态进入,一定会报错,而从QQ聊天点击链接进去就不会报错?都是同样打开的webview,H5的代码没变,那我们唯一可以确定的就是webview有什么差别;

只能继续求助于客户端,客户端保罗大神pauloliu根据这个线索,对比了代码,结果如下:

空间和AIO里面打开url,用的都是一个activity,但是确实aio里面打开的不会有问题。
AIO里面打开url的intent:
Bundle[{qqBrowserActivityCreateTime=1505354889492, fling_action_key=2, from_aio=1, param_force_internal_browser=false, friendUin=373922647, from_aio_opt=1, preAct=QQBrowserDelegationActivity, leftViewText=返回, aio_open_web=true, fling_code_key=199961721, fromAio=true, from_aio_time=43533567, uinType=0, articalChannelId=2, useDefBackText=true, startOpenPageTime=1505354885699, uin=2577199125, url=https://h5.qzone.qq.com/h5plus/home/index/alpha?_proxy=1&_wv=3&_nav_alpha=0&from=qqnavigation, preAct_time=1505354885699, key_isReadModeEnabled=true, needSkey=true, injectrecommend=true, curtype=0}]
空间的
Bundle[{qqBrowserActivityCreateTime=1505355030872, key_isFromQZone=true, fling_action_key=2, preAct=QQBrowserDelegationActivity, leftViewText=返回, fling_code_key=158123890, articalChannelId=5, startOpenPageTime=1505355027800, source_name=QQ空间, uin=2577199125, url=https://h5.qzone.qq.com/h5plus/home/index/alpha?_proxy=1&_wv=3&_nav_alpha=0&from=qqnavigation, fromQZone=true, preAct_time=1505355027800, key_isReadModeEnabled=true, needSkey=true, injectrecommend=true, insertPluginsArray=[Ljava.lang.String;@27f8d6b, isNeedAdvReport=false, post_data=null}]

我们都发现了下面多了一个insertPluginsArray,这个是干嘛的?insertPluginsArray代码如下:

在浏览器中插入了一个Qzone的命名空间,这个是啥?Qzone是个大插件,里面东西太多了,保罗发现干掉这个插件就OK了,那证明问题就出在这个Qzone插件上;找到源头我们也感觉有了一丝丝希望;在客户端同学努力下,问题根源找到了:

输入面板关闭的时候,会发出一个广播,而这个广播的接收者在webview初始化的时候就被创建了,所以每个web页面都可以收到。。。所以问题的原因我们来回归下:

我们从小游戏页面进入宠物页面,在宠物页面聊天的时候,会调用TopicComment这个方法,同时也会注册一个回调;关闭输入框会执行一下这个callback,方法名是上面的报错的哪个匿名方法,这个回调方法命名规则就是MQQ_CALLBACKAUTO+SN(这个SN的值UUIDSeed自增生成),在A页面因为没有注册这个回调方法,但是在A页面却收到了这个广播,这个是浏览器层面的事件监听,但是没有对应的回调方法,所以报错了,不过不影响功能,只是影响了jserror的统计;客户端同学也已经修复了这个bug,在下个版本会兼容处理;

通过这次的问题定位,对我们未来碰到类似的问题进行解决的时候,提供了思路,就是客户端可能会在浏览器加载的时候,不时的插入一些包括js,事件等等,如果没有相应的代码进行辅助证明,可以从这个角度去定位,举一反三下 :)

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

10 条评论
登录 后参与评论

相关文章

来自专栏烂笔头

Django 1.10中文文档-第一个应用Part5-测试

目录[-] 本教程上接教程Part4。 前面已经建立一个网页投票应用,现在将为它创建一些自动化测试。 自动化测试简介 什么是自动化测试 测试是检查你的代码是...

3466
来自专栏Android 开发者

玩转全新的 Android 8.0 Oreo 后台策略

2034
来自专栏皮振伟的专栏

[linux][redis]bgsave引起的latency突刺问题分析

前言: redis启动的时候,可能会提示“WARNING you have Transparent Huge Pages (THP) support enabl...

792
来自专栏信安之路

锁首技术总结

在公司实习也有一个月了,学到不少东西,不知不觉就要大四了,回首漫漫安全路,不禁感慨万千:我入安全的时间比较晚,大一大二跟着老师参加 Android 移动应用开发...

682
来自专栏北京马哥教育

一秒内诊断 Linux 服务器的性能

60,000 毫秒内对 Linux 的性能诊断 当你为了解决一个性能问题登录到一台 Linux 服务器:在第一分钟你应该检查些什么? 在 Netflix,我们有...

2616
来自专栏乐沙弥的世界

MongoDB执行计划获取(db.collection.explain())

843
来自专栏CSDN技术头条

为Symfony2和Redis正名,基于PHP的10亿请求/周网站打造

【编者按】如果你还在Symfony2和Redis使用中存在这样的错误观念:不能使用Redis作为主要存储;Symfony2的功能很多,以至于它的运行很慢,那么不...

1845
来自专栏美团技术团队

Android App包瘦身优化实践

随着业务的快速迭代增长,美团App里不断引入新的业务逻辑代码、图片资源和第三方SDK,直接导致APK体积不断增长。包体积增长带来的问题越来越多,如CDN流量费用...

3293
来自专栏KaliArch

Python构建企业微信自动消息转发服务端

目前有在项目分组,就小组成员中,微信群消息回复较多的情况下,想根据组来转发特定消息,包含文字、图片、语言等。在此只是自己实现仅供参考,可以根据自身需求修改更多功...

854
来自专栏SDNLAB

SONiC-P4 image build解析

2427

扫码关注云+社区