前几天帮忙排查一个问题,简单记录一下。
1.背景
答题服务,近期用户答题率(答题人数/发题时的在线人数)有明显下降,需要分析这部分用户没有答题的原因。
2.初步排查
从服务端监控来看,客户端尝试连接、断连、超时比例都没有明显异常,整体也没有容量上的问题。
从没有答题的用户分析,没有明显的特征。
从没有答题的设备分析,大部分集中在IOS客户端。
从没有答题的设备网络分析,没有明显的地域或ISP分布。
3.进一步排查
在初步排查基本排除了服务端整体性能问题之后,接下来的问题是:这一部分IOS用户为什么在房间里但是没有答题?
可能的解释有几种:
发题的时候用户在线,但是没有弹出题目
发题的时候用户在线,弹出题目了,但是没有选择答案
发题的时候用户在线,弹出题目、选择答案了,但是没有提交成功
发题的时候用户其实不在线,统计有问题
想要继续统计有一个比较麻烦的地方,如何根据已有日志判断某个用户在发布题目的时候是否在线,进而得知这个用户在这段时间内究竟发生了什么?
记录的日志大概是这个画风:
time:12:30:00, uid:aaa, event:join room time:12:30:00, uid:bbb, event:join room time:12:30:30, uid:bbb, event:show question time:12:30:35, uid:bbb, event:submit answer time:12:31:00, uid:aaa, event:exit room time:12:30:00, uid:bbb, event:exit room
也就是说,需要通过事件日志获得“对于没有答题的用户来说,在发题的时间段内,哪些用户是在线状态”,进而分析“没有答题的用户的时间轴与正常用户的时间轴有什么区别”,如果是一个用户可以人肉观察的方式,针对具体case定性,但是对于几十上百万个用户来说,需要进行定量分析,从而判断出“影响答题率的关键因素究竟是哪个问题”。
在这里利用了红黑树进行辅助分析,针对每一个用户创建一颗基于事件的红黑树,key为时间戳,value则是具体事件类型。在对所有用户创建树后,则可以使用某一个具体的时间戳,获取某个指定用户“在这个时间点发生的事件“、”在这个时间之前(之后)发生的的事件”和“用户的完整时间轴”。
在此之上,如果某个用户在某个时间点之前的最后一个事件是断开连接,则是离线状态,否则为在线状态。
伪代码如下:
// 建森林 foreach(所有事件) { TreeMap userTree = allTree[uid]; userTree.put(event.time,event.event); } // 获取某个时间点在线用户列表 public List getOnlineUsers(long timestamp) { foreach(allTree) { if(!userTree.floorEntry(timeStamp).event == "断开连接") { result.add(userTree); } } }
筛出异常用户后,进一步对异常用户的时间轴作分析,获取每个时间点上的事件数量。
for(time=start;time将异常用户与正常用户的加入房间请求做对比,可以发现答题开始前有加房间请求的异常峰值。
由于红黑树可以顺序遍历,因此用程序对所有异常用户的时间轴进行分析,发现异常用户都是有两次加入房间请求,并且都是第一次连接关闭后直接发起请求,但第二次加入房间后没有任何操作,直到直播结束。
4.结论
怀疑断线后有误加房间逻辑,重新review客户端断线重连代码,发现bug,解决。