上面的伪代码probability()首先先做一个概率性的判断,例如我们做百分之一的随机判断,判断该次请求是否要进行消息的删除(请注意我们删除的逻辑是放在插入的逻辑之中的。如果每一次插入都需要判断是否要删除过期数据,会影响插入的性能)。如果通过概率性判断后,我们就优先判断某一个直播间的消息个数,如果消息个数还是比较少的话,则退出删除逻辑,如果超过消息阀值,则按照时间倒序删除已经过期的消息。
说完了http短轮询消息的存储后,我们最后再简单地说一下客户端消息查询实现逻辑。客户端通过直播间id和时间戳两个字段来请求服务端以查询直播间消息,其中"时间戳"是每一次服务端返回的,这个时间戳是渐进式的,当下一次客户端来请求服务端的数据的时候,都会带来上次服务端返回的时间戳,伪代码如下:
@Override public RoomMessage queryRoomMessages(MessageMessageReq messageMessageReq) { RoomMessage result = new RoomMessage(); long timestamp = messageMessageReq.getTimestamp(); Set<Tuple> tuples = null; if (timestamp == 0) { // 如果传递是0,说明这个客户端终端是第一次来轮询,我们只要返回一个最近最新的消息返回即可 tuples = jedisTemplate.zrevrangeWithScores(UNIQUE_ROOM_ID, 0, 0); } else // 加上一毫秒,返回后续的消息,每次返回5个,防止客户端因为低端手机原因,过多的消息渲染不出来 tuples = jedisTemplate.zrangeByScoreWithScores(UNIQUE_ROOM_ID, timestamp + 1, System.currentTimeMillis(), 0, 5); } List<EachRoomMessage> eachRoomMessages = new ArrayList<>(); long lastTimestamp = 0L; if (!CollectionUtils.isEmpty(tuples)) { for (Tuple tuple : tuples) { //最后一次循环后,会把最后一条消息产生的时间戳,返回给客户端,这样下次客户端就可以拿着这个时间戳来进行查询 lastTimestamp = new Double(tuple.getScore()).longValue(); eachRoomMessages.add(JSON.parseObject(tuple.getElement(), EachRoomMessage.class)); } } result.setTimestamp(lastTimestamp); result.setEachRoomMessages(eachRoomMessages); return result; } 复制代码上述三段比较完整地代码主要陈述了一个依赖http短轮询这种方式快速实现的直播间的能力,这种方式是比较粗糙的,不过却是一个很好的实现思路,目前我们线上部分业务也是根据这个轮询的思想进行部分模块的实现。
这样实现的思路也有一个小坑,如果有采用该思路去实现的,可以尝试去规避。如果Android客户端断网的情况下,轮询的线程是不会停止的,例如是晚上8点整断网的,8点01分恢复网络的,当网络恢复的时候,第一次轮询就会导致服务端返回大量的消息,这边是需要进行处理的,否则会返回过多的消息,服务端也会出现慢查,客户端因为渲染过期的消息也会出现部分消息展示区间出现闪跳。例如公屏区可能会"发疯"般的出现各类消息,这些可以通过客户端和服务端的双方约定进行规避,例如客户端当出现网络问题的时候,在超过5秒以上,可以把时间戳置为0,要求服务端返回最新的直播间消息即可,中间丢失掉的消息,可以在业务返回内的进行丢弃。
四、小结本文主要是想让大家对直播有一个初步的了解,了解直播基本的概念模型,一些基础的概念,后续我们会深入直播具体的模块的学习,进一步去了解直播的原理,也能够帮助我们更好的做好直播的业务。