前几天帮忙排查一个问题,简单记录一下。
答题服务,近期用户答题率(答题人数/发题时的在线人数)有明显下降,需要分析这部分用户没有答题的原因。
在初步排查基本排除了服务端整体性能问题之后,接下来的问题是:这一部分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<TreeMap> getOnlineUsers(long timestamp) { foreach(allTree) { if(!userTree.floorEntry(timeStamp).event == "断开连接") { result.add(userTree); } } }
筛出异常用户后,进一步对异常用户的时间轴作分析,获取每个时间点上的事件数量。
for(time=start;time<end;time++) { foreach(allTree) { event = userTree.get(time); if (event != null) { eventStastics[timestamp][event]++; } } }
将异常用户与正常用户的加入房间请求做对比,可以发现答题开始前有很多异常的加房间请求。
由于红黑树可以顺序遍历,因此用程序对所有异常用户的时间轴进行分析,可以发现异常用户都是有两次加入房间请求,连接关闭后直接发起请求,但第二次加入房间后没有任何操作。
因此怀疑有误加房间逻辑,重新review客户端断线重连代码,发现bug,解决。