`
snake1987
  • 浏览: 72736 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

项目回忆(一):虚拟房间系统

    博客分类:
  • java
 
阅读更多
需求:
1.硬件环境:所有服务器都是web容器,有统一的缓存memcached,由于一些原因,不允许用中央控制服务器
2.实现一个虚拟房间系统,即包括房间内的数据可见并共享(数据竞争性),房间有超时检测,房间外部还有一个统一调度(即不是自由进入房间,由系统分派),房间有人数限制,也要求人数多的尽快分配满,人数少的尽快清空


实现思路
开始是一个很简单的思考模式:
超时检测与房间调度必然是两个服务,房间数据必然是存储在统一的缓存里面,超时检测必然是检查用户上次登录的时间戳,超时了执行用户退出房间逻辑,而房间调度则需要所有房间的信息。而用户操作,每次操作,会更新该用户的房间内的时间戳

由于房间数据是高争用数据,所以想到是把用户数据从房间数据分离,然后房间数据只保留用户索引,但用户的频繁退出进入或者超时,也就造就了房间数据有不低的争用频率,但用一些cas之类的冲突解决方案,也能基本解决问题

而超时检测,需要遍历所有房间内用户的数据,我们的用户数据量很大,最高能达到近1000w,所以即使频率很低(1分钟),高峰期每秒钟也达到了16w的数据访问量,这是不能接受的


根据需求的进一步细化,引出了第二次设计(准确说是第三次,中间的忘了):不精确模型

需求其实不明确要求人数限制要限制很死,比如说10个人,即使达到了100个人,问题也不大,所以我们的初始方案有了很大的优化余地,我们将本来的精确模型改为了不精确模型

首先,采用了租约的模型,也就是一个用户进入房间,会给他一个租约,也就是上述的时间戳,然后将每次操作更新租约改为了由客户端采集用户操作,定时更新租约(时间较长)。

然后,把用户数据整合回房间内,用一个单一列表来保存,同时利用的memcached的特性append操作(no respond),每次续租不再更新之前的租约,而是新建一个租约,直接append在队列后面,这样,可以极大的减少了冲突(cas在分布式中应用并不是一个好办法)

第三,超时检测多做一件事情,除了检查租约(其实也就是遍历所有房间的租约,每个房间一个list),让用户退出之外,再由超时检测服务来维持一个房间人数,set操作,无视冲突

第四,房间分派服务每次只需遍历所有的房间的人数,就可以得出一个全局的状况,然后进行调度

第五,调度服务采用预调度模式,即每次查询全局信息,会预调度一批房间号,当用户请求分派时,只会从预调度队列里面去拿一个房间号,然后新建租约,这样,又极大的降低了调度的频率,注意,这时候会更新房间人数,即会与第三步的动作有冲突,解决方案是无视冲突

第六,由于极大地降低了调度的频率,我们完全可以将调度算法做得很复杂,我们提供了多种解决方案
1.预调度模型,也就是根据增长率等状况,计算每次调度人数
2.简单调度,只根据当前房间数来决定增长率和调度人数
而房间的增长方案,我们采用的是优先级调度方案,高于某个阈值,则优先调度,目标是快速填满,低于某个阈值,则极少调度或者不调度

第七,由于是分布式的,即每个服务器都会有一个调度服务跟一个超时检测服务器,调度服务还好说,可以人数/服务器数就可以得到每个服务器的调度人数,不需要协作,超时检测服务器是需要协作的,因为每个服务器做的工作是一样的,所以我们还是用了memcached做同步,cas解决冲突

第八,细节
虽然超时检测需要访问统一缓存的数据量已经降为极低,但是我们还是希望进一步优化,所以制定了在房间的上一层划分一个层次,然后在超时检测周期内均匀分布所有的检测,让数据库访问更平缓

在调度服务中进一步优化,当访问次数很多的时候,会将调度频率提高,比如说当预调度队列中少于某个阈值时,会额外新增调度线程来提高调度频率



总结
其中,由精确模型向不精确模型的转化是很关键的一步,这也告诉我们,业务系统的设计永远是立足于需求的,高精确性,高一致性,不一定是最好的解决方案

第二,租约概念的提出给整个概念模型的建立给了很大的助力,在思考上和概念上的思路变得清晰而有条理

开始的设计是每秒10w以上的访问量,到最后降低为每秒不超过10次,这不能不说是一个质的飞跃


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics