posts - 297,  comments - 15,  trackbacks - 0

本文小弟浅谈,新手看,老手拍砖,转载请注明出处http://www.cppblog.com/ziyebuboka/

    游戏服务器在网游上的作用不容考虑,游戏能做大到什么程度,还是有很大的依靠的,这篇文章先从功能模块的角度来谈一个完善的游戏服务器需要实现哪。
    一:游戏服务器的作用:连接各个网游客户端,实现各客户端的通信,连接,数据操作
    二:先从大分类上来:游戏服务器按一般架构来说具备1
            1:登陆验证注册和账号有关的所有操作的服务器  我们简称他为registerserver
            2:游戏逻辑操作服务器 我们简称他为gameserver
            不用细说大家也明白了,说一个玩家登陆进入游戏世界的流程:玩家打开游戏客户端(这之前会有更新操作,不过这只是连接更新服务器的一个文件比对和下载过程,我们不将他列为游戏服务器之内)说到这里朋友会发现游戏登陆上目前有两大类,一类是先选服务器后输入账号 一类是先输入账号后选服务器,这里说下区别
先选服务器后输入账号的一般来说都是将registerserver和gameserver配对,就是你先选择服务器,而后你连接上的就是此服务器的registerserver,通过此registerserver来进行账号验证等等。另一类先输入账号的,无非是先制定一个中心registerserver(或者是随机一个,因为register有时候会弄好多个由运维配置来做动态均衡),账号验证成功后再显示服务器列表,然后玩家选择了服务器后,则从指定服registerserver去数据库查询玩家此服的角色列表(当然了,这里如果非有人的服务器是做成查询角色列表就从gameserver走的流程,我也没意见)。返回后,客户端进入角色选择界面,客户端与registerserver断链,玩家选择角色,与gameserver连接,去数据库提取角色,注册进入游戏服游戏世界,反馈角色信息给客户端,客户端进入游戏世界。然后消息发送过来发送过去的开始了。。。。。。
            上面说的是针对一个普通的一对一架构的服务器所有的一个登陆流程,看到这里,朋友们应该对registerserver和gameserver的基本功能有所了解了。一个是登陆验证用(垃圾点的小公司没有注册账号的主页的话也会通过这个在游戏里直接注册账号。。。)到登陆进入游戏世界的过程。
             再稍微高级点的就是加个聊天服务器了,因为聊天这个功能实在是太耗性能了,特别那啥的那公聊,你发个一句,服务器得有多少人就发多少回。。。
 打个比方你发一句话 50个字那就是100 一个服有那么个几K人的话,就打比方5K人 一句话发送就得是一个全服人口大FOR循环,网络还得消耗掉5000*100字节
所以你看各游戏公聊国聊那啥的都时间限制要不就收费,他扛不住啊。
             所以这个地方就出来个性能优化方案了,加个聊天服务器,也就是玩家在登陆成功与gameserver连接成功的同时要与聊天服务器连接成功,聊天服务器有个全服在线角色表,一个大FOR循环和那啥的5000*100就让他来发吧,至少对gameserver没影响了。
             好了,一个普通服务器的基本介绍就说到这里,读者也应该有个基本了解了,具体架构上的就不细说了,赶明换个帖子发。这里还是接标题从功能上来分析服务器。
            这里就只分析游戏服务器了,registerserver就不考虑了。
            一个游戏服务器他的作用在与,所有的游戏数据操作都将在这里完成,我们将只将客户端作为显示和一个数据的完善缓存。一切的操作都必须在游戏服务器的验证之后才能完成。
           功能模块分类(本人之间,有多余或遗漏的,欢迎补充):
              1:脚本模块
              2:属性模块
              3:网络模块
              4:数据库模块
              5:日志模块
以上所列的为一个完善的游戏服务器所必须实现的功能 下面一一来说
1:脚本模块
      没有脚本模块,脚本策划就得喝西北风,LUA首选很不错。
      具体LUA的学习,推荐LUA程序设计,这玩意不用深究,会即可。
      程序员要写好几个CPP,完成的功能是---》在C++中任意的调用LUA函数(或是执行一个LUA文件),在LUA中任意调用你提供给脚本策划的C++函数接口,如此功能实现后,哈哈,程序员们,你们的工作负担就轻了,很大一部分的逻辑编程将交给策划们去完成了
     举例:程序实现一个接口例如 say(LPCSTR szSay)  ,则策划可调用此接口 function  mySay()  say("欢迎来到游戏世界") end,就可
    可用到的地方:具体物品的使用逻辑,特殊任务的逻辑,活动逻辑,特殊NPC逻辑等等等等,只要你愿意,跑步都可用LUA来
     比如C++里可实现一篮子接口:AddItem 增加个物品  ChangeMap 跳地图 甚至是AddLevel等等 而由LUA调用此类接口,而后就是C++里做些操作,比如玩家点击物品就是调用相应物品的脚本:例:点击 血瓶 调用 LUA 的血瓶脚本,执行LUA函数调用AddHp
     脚本模块很重要,可极大的方便工作,还有调试和维护,因为修改脚本,服务器是不用重编译的,则意味着可在调试中随意修改,甚至可在运营过程中随意修改某物品功能 某任务 某活动。。。,只需C++到LUA,把这个LUA函数RELOAD下即可
     具体的游戏应用脚本的例子 大家网上找找吧 一坨子开源的。
2:属性模块:呵呵,不知道我取这个名字OK不OK,我也觉得不是很合适,大家又更好的命名没?
     NPC的基本属性配置,物品的基本属性配置,叫属性配置可能更合适点,意思就是你要提供一个模块供策划可填入各个属性配置,甚至是GUI的配置又或者是任务配置。对于这个,我推荐使用EXCEL表格。
    例如:NPC的表格至少包括  NPC名字  模型编号   血量    蓝量   移动速度    AI  等等,而后有策划往里面随意填配置 这里比如AI就是一个对应的脚本文件
     这个读取表格由程序员实现,功能是要求要自由化,不然的话 策划加一条 你咋办,不至于从读取开始你的代码都得改吧,肯定能做到直接在NPCPROPERTY结构体里直接加一条和表格新加的对应就OK了。
   如果OK得话,最好再提供个编辑工具给策划,任务的相同模式也可由此来实现:任务描述  任务要求 对话1  对话2  对话3  奖励1 奖励2 奖励3
   物品:物品名字 模型编号 职业   等级   攻击  脚本  
   不然你总不至于傻到认为这些都是写代码里的吧。也可能有人想使用XML  随便了 不过我这里不想用XML 为啥  EXCEL清晰啊 就一行条目
    这个模块是数值策划 任务策划的天下
3:网络模快:这个没啥好说的  一个基本功能,游戏世界都是在这之上也能成型,做好链接维护,收发消息
4:数据库模块:对数据的保存 其实这里我热衷于使用工厂模式,本地调试服务器具体逻辑用本地文档方式,上测试服上外网则用数据库,所以这里叫数据操作模块比较妥当点,就是玩家保存数据,保存帮派啦 等等世界信息的具体操作,这个很重要
5:日志模块:我将此日志模块分为两大类,一类是游戏服务器调试维护用日志,一类是游戏运营用日志,可选择使用本地文档或是数据库模式记录
    调试维护日志没啥好说的,运营日志说说,是给客服提供的,谁消费了什么 交易流向 物品流向 等等的记录 GM操作的记录等等 比如万一某玩家装备被盗了,则可通过此来证明。

 接着一继续,其实写本文从内行技术角度来看,本身就没什么技术含量,但是俗话说的好,隔行隔山,内行看门道,外行那啥什么,反正就是想触碰这玩意,但是又没搞过的人看的。反正都是随便乱写了,爱看的看,准备写个功能模块大概 再写个架构得大概,而后就去从网络包开始搞个最简单最轻量的小架构,力图让知道编程是啥的就能在上面搞东西
      还是继续谈功能模块。
      一、还有个  AI模块,这个可不能忘啊
      不过要注意,我这里提到的AI模块和我一里面所提到的几个AI地方说指AI不是一会事情。
      这里的AI模块,哈哈,就是所谓的算法了,算法达人们NB的地方了。
      针对NPC怪物等,比如最基本的寻路算法。
      此模块达到的效果是什么呢,就是:怪物死了又活,怪物看见你知道追你,怪物知道打你 都知道寻路躲障碍
      介绍下最基本的A*算法吧 什么A B C D E F D的
      怪物要打你 得追你,但是他为啥知道跟你走呢,或者说你点击一个地方,为啥就能自动走过去,能自动的绕开障碍呢。这个模块就是实现这些基本的东西。
      2D的一般都是按格子计算,就说2D了,还有用像素玩的,3D玩坐标的,等等 其实都是一会事情
      角色身旁一共有8个格子,你点击一个地方,就等于是指明了一个方向,角色就找到正对方向的身旁最近格子,判断此格子是否有阻挡,如果无则走过去,如果有阻挡则搜索身旁另一个格子,然后就是这么一直递归,知道到达终点格子。
     基本的自动寻路算法就是上面这么一段话,当然实际操作中,肯定不会用这么费效率的算法了,这里就是简单介绍下这个活在N-》B之前的N-》A*算法。。NA啊。具体的大家可以去找本人工智能的书看看
     服务器要算一下怪物走哪了就给周围玩家同步下消息,所以服务器需要这玩意,这理由充分吧、、、
     客户端也需要,给玩家或者宠物自动寻路,内挂使用。
    为什么我说这里的AI和我在一里面说的不同呢,是因为这里是实行基本的自动功能,而后你可以在这个模块的基础上发展高级AI智能,结合脚本,表格配置,活用技能。比如一个游戏里按档次有白怪 蓝怪  紫怪 BOSS
白怪么就给他这一套最基本的会走路躲障碍会打人就可
蓝怪 稍微高级点了,在基本模块上扩展,程序里再实现血少到一定程度会逃跑 会喊同伙,

以上两种可根据属性配置表格模块根据怪物类型读取到程序,程序根据类型判断是否激活扩展AI

表格可如下:
NPC名字  类型  

紫怪  再高级点 定制AI,可在程序里预先定义一组高级AI,比如预先设想好的十种可能,打个比方,放A技能 放B技能 自动加血等等 ,而后也可在表格配置,比如预先表格设定好一种怪物最多可有4个定制AI

表格可如下:
NPC名字  类型  定制AI1 定制AI2 定制AI3 定制AI4

程序读取到相应类型而激活相应模块 或者此组AI 都用脚本预先写好 也不错 这样比写死在程序里好

BOSS  那就得完全特殊处理了不是,脚本发挥作用,完全脚本实现  程序事先一组接口,比如掉什么装备接口,放技能的接口等等,LUA里面就狂写吧,接口只要完善,写成个WOW里的一样也很OK

表格可如下:
NPC名字  类型  定制AI1 定制AI2 定制AI3 定制AI4  脚本AI

像2D游戏 下FB BOSS不够智能的话 玩家就知道卡BOSS 几个玩家把BOSS围一圈,让外面的远程玩家打,格子上玩家又是不可重复的,BOSS就出不去 有仇恨系数 他又只想杀外面打他的玩家 导致就卡那里了,想打的玩家打不到 打的到的玩家又不想打。。。。。。。怎么办呢,特殊AI处理,卡BOSS?系统判断BOSS十秒不出手,就放大技能秒杀周围的人。。。。
     
说到底,AI模块就是最基本算法,程序定制,脚本定制,属性表格配置再加脚本特殊化处理,基本就可达到需求了

二:扩展下前面说的数据库模块和日志模块
        这里大家要注意,在这类数据库 和IO操作上 尽量使用别的线程来开,不要和主线程搞到一起
 按目前流行的架构,一般都是在服务器上多开线程开启网络接口,另外在专门单开代理程序,消息发送到数据代理,让代理来实现数据操作。
       日志模块,本地调试么,就用文档记录,运营日志,单开个代理吧,这个操作挺频繁的,和登录保存角色的代理放一起影响性能 而且每什么意思,毕竟这个异步互相是不关联的  稍微提醒下就是 你要是做 物品流向的时候 切记不要所有物品都记啊 不然就SB了,这个流向日志 要是都记录的话 那一天都不知道是多少万条记录了 万?十万?百万?
   可以 表格配置:
  物品名字  物品ID  是否记录日志    //后面其他列是其他属性
每次产生物品流向时候 比如买一个装备到包里  交易一个装备 等等
if(pItemProperty->bCanSaveLog)
{
//sendmessagetologdb
}  

这样你就记录些珍贵物品就可

按我的分类 我一般将代理分为 账号代理  角色代理  游戏代理  日志代理  运维控制器代理

这里不一个个讲了 放到后面说架构的时候再说每个代理需要做的事情 
一个宗旨是 分的细 每一个得压力就小  但是要保证不要出现数据互交叉

也见过某些项目 是没有具体的数据操作代理的,直接是在服务器里直接操作,我个人认为啊,能新开进程 异步的 就开,没必要给老板省钱,全部都压一GAME上 扛不住啊,而且如果是分布式的话,你肯定得有一个统一的数据出口啊,不然的话。。我没想过会怎样,数据不统一?数据库死锁?

三:运维模块
     运维分开就是运营和维护。。  因为他们是走的同一套架构,所以这里就放一起来说
     首先说明他们的产生原因:不可能每一次服务器更新 或者再监控服务器 维护过程 或者是提取某某文件日志  都是一个个远程硬件服务器吧 那样的话 维护者工作效率就太低了
     GM也不可能每一个服务器都登陆进个客户端开着吧。。所以这个模块就产生了,对维护者是要实现他们的远程操作,对GM是要实现他们的线下操作。
    工具功能:可监控 开启  关闭服务器 可主动推送更新文件 更新脚本 
                       GM可线下操作基本命令,监控聊天,赔偿物品,发送游戏邮件等等。
                       开发者可主动提取调试日志记录
                       算帐的可主动开后台查看运营日志记录计算ARPU值,查看在线记录,等等等等
我现在是不推荐GM做线上操作的呢,就如同之前传奇那样的,都是在聊天框里输入GM命令,我个人认为内部操作还是走后门的好 不要和玩家一起从前门走了,注意的是这一块在中心控制器代理这里一定要做好监控,和操作记录,验证,来保证操作的安全性,防止违规操作。力图将工具客户端绑定到某一台机器,比如可在运维登陆工具时候 发送账号 密码  MACKEY  IP  子网掩码   某一个CODE 等等在控制中心验证 成功才可登入控制中心,工具客户端才可操作、
     这个模块主要注意的就是安全性,操作的方便,和日志模块结合在一起,日志记录 分类 挖掘 良好 


    具体架构的后面再说
    毕竟这个就是浅谈,所以没有什么实际性的代码内容,就是让不了解的朋友能够了解这是怎样的一个架构一个工作流程
    看了留言啊 ,这博客,不同IP,点了就加一阅读,没意思啊,我不知道到底有没有价值继续啊,

    我是力图用最浅的语言来表现这些玩意是怎么会事情,高深的我也不懂了,扁我吧,。觉得没啥意思的也留个言拍下砖头啊,觉得有意思的留个言让我高兴下,主要是没打草稿直接写的就发了,遗漏 不清不楚肯定还是有的 .

本文介绍一下一个应用的游戏服务器的架构和演变
      游戏服务器的作用在于满足在线玩家的需求,实现账号的验证,登陆,玩家在游戏世界的一系列逻辑操作和验证。在此基础上,一个好的架构,可以提升效率,在实现逻辑需求的情况下达到百万级的同时在线数也不是不可能。
      我们先从最搓的最简单的结构看起
      
      CLIENT ----------  SERVER   ---------   DBSYSTEM

      就是一个很简单的 C/S系统  同一个server同时处理登陆注册创建角色和游戏逻辑操作的功能,。在server上直接挂接DB操作。DB可以是一个具体的数据库也可以是一个FILESYSTEM
      这里可以看的出来,过于简单了,将登陆注册创建和具体逻辑这几个毫无关系的东西放置于一起,严重损耗了具体逻辑操作的效率,特别是在新开服阶段,完全会因为登陆验证的操作而导致逻辑爆卡。
      所以这里需要升级,将完全不同类型在玩家一次游戏操作工程中只会在登陆阶段执行一次的操作单独分开,单独进程解决,。故而可成为下面阶段
       
                 -  LOGINSERVER -
               -                                 -
CLIENT -                                   -  DBSYSTEM
               -                                -
                - GAMESERVER -

分为两个服务器,这个我在一文章里的开头就有提到过了。

我们来看下好处进阶,在LOGINSERVER里只执行账号验证  查询角色列表  创建角色的操作   而后玩家登陆进GAMESERVER  具体逻辑操作在GAMESERVER里完成

玩家的一次登陆操作

                   发送账号                                 验证                               返回角色列表                          创建角色
CLIENT---------------LOGINSERVER -----------DBSYSTEM-----------------------CLIENT ------------------
                                                                                                选择角色与LOGIN断链与GAME连接 登陆
LOGINSERVER -------DBSYSTEM  ----------CLIENT  ------------------------------------------------------GAMESERVER

如此,可有效的提升效率,玩家的验证 列表读取 创建 和GAME就毫无关系了,但是他的缺点任然存在 我们再继续看可优化的地方

首先从数据库上来提升效率(先说下,从这里开始就应该是肯定的是用数据库了,而非什么本地FILE了,),将账号库和游戏数据库分开,分离为两个独立的库,
具体理由有两个:
1:从游戏运营上来说 你不可能一直是只有一个服吧? 分成多个服后 人数越来越多,就不能所有服都共用一个数据库了吧?那你这数据库也牛逼了
      所以我们这里这么干,将 账号库独立,全游戏共用,这样是方便管理,方便管理账号的全局性的信息 经济性的 比如点卡什么的,每个服一个游戏数据库,只记录操作你这个服的玩家信息,世界信息。
2:理由类同将服务器拆分为LOGIN和GAME。

现在 结构就是这样的

                   - LOGINSERVER-
                 -                               - ACCOUNTDBSYSTEM
 CLIENT -                                                             -   
                 -                               - GAMEDBSYSTEM 
                   - GAMESERVER -


但是到了这里后 肯定还是不够的,
我们先说个基本的,在服务器里 ,你一定要记得,数据库操作,IO操作 ,文本操作这些 一定要单独进程不要和主进程搞一起,你总不希望你做了个什么查询还是什么操作 他主线程挂起吧?但是,试想下,如果能单独进程肯定还是单独进程更爽一点吧?你还能在里面做做缓存啥的,还不占服务器的资源
    所以这里还是麻烦的 从效率上来说 至少后台这块 还有很大提升空间。因此,我们加入 DBAGENT模块 ,单独进程,将数据库操作单独分离,并且可自我添加某些应用的缓存

对应ACCOUNTDBSYSTEM 增加 accountagent ,对应 GAMEDBSYSTEM 增加 gameagent

结构如下


                 -LOGINSERVER-
               -                              - accountagent - ACCOUNTDBSYSTEM
CLIENT -                                                                                                   
               -                              - gameagent   - GAMEDBSYSTEM 
                -GAMESERVER -

LOGINSERVER同时和accountagent和gameagent连接   gameserver也同时和这两个连接   服务器通过agent来对对应的数据库做操作   因为accountdbsystem可能是全服共有的 ,所以accountagent也可以是全服共有的 他连接所有的服务器。loginserver通过accountagent来验证 通过gameagent来查询列表 创建角色  gameserver通过gameagent来查询完整角色信息登陆进game,并通知accountagent此账号已进入游戏,避免重复登陆。并且定期保存。设置你可以将比如你游戏的排行榜啊,拍卖行啊的信息放置于你自己设计的gameagent的缓存中,而避免重复查询

玩家每一个对数据库的操作 server只需发送消息到agent agent来做具体操作,而后再返回到server 再到client就可。将数据操作完全的异步操作,效率有较大提升。并且安全性上也得到了些许保证。

不是我胡说或者轻视,国内一大半游戏,都用的上面这个结构。。。。。。。。。。。。。。。。。。。。。这个可以算是一个比较完善的产品化架构了。

说到这里,大家有没有发现一个共性。采用这类架构,游戏必然会是先选服务器再验证账号 登陆进游戏,这是因为服务端采用的是LOGIN和GAME一对一得处理,也就是你是登陆的什么服务器必然就是从什么服务器的LOGIN进入验证,故而他需要在客户端开启的时候就要知道需要连接的是哪个LOGIN 账号验证成功后再是哪个GAME
其实这里很好处理,就在客户端上做些处理就可以达到先登陆验证在选服务器。所以在配置上 LOGIN不再是一对一(后面会有优化的再说到多个LOGIN),全局也共用一个LOGIN。玩家开启客户端,连接LOGIN。发送账号验证。因为这里已经是把ACCOUNTDBSYSTEM做成全局的了,所以是不用担心他是哪个服的,然后返回成功,玩家客户端显示服务器列表(这个列表还有连接信息配置再客户端就可以了)选择某一个服后,则发送查询某服角色列表的请求到LOGIN,而后再返回再登陆游戏就可。小小调整和改变就可实现先验证后选服务器了。

      所以,到了这步,服务器架构就变成这个样子了


                  -LOGINSERVER-
                -                               - accountagent -ACCOUNTDBSYSTEM
CLIENT -                                 |--------                                           
                -                                |-----------
                 - 
                  - GAMESERVER  - gameagent     -GAMEDBSYSTEM 
                    -                                             |  
                   - GAMESERVER  -gameagent      -GAMEDBSYSTEM 

全局共用一个LOGIN和一个ACCOUNTAGENT还有ACCOUNTDBSYSTEM
每个独立服各自独立的gameagent 和gamesdbystem
但是LOGIN的瓶颈立马出现了,当初是一个服独立一个LOGINSERVER 现在是全服只有一个了,毫无以为,扛不住。

其实上面这个架构的不一定非得存在的,就是随便写写,让大家更清楚一点。

所以LOGINSERVER就得配置多个了。这个地方大家选择吧 尽量灵活点,可以全服单位的配置多个也可以每个服对应的配置多个LOGINSERVER。这样 在开服时候,这种大面积井喷式的玩家玩家上线,可有效解决负载。具体CLIENT开启后会是和哪个LOGIN连接呢
这里提供两种方式。一个是配置上的 一个是程序上的
1:
   程序上的好说 ,你把所有LOGINSERVER的连接信息都配置到客户端,让客户端开启后随机选择一个去连接。要是你开服后,他所有玩家都随机到一个LOGINSERVER了,那真的算你点背。背到家了
2:配置上 :
   通过基于DNS的负载均衡系统,DNS中为一个域名配置多个IP地址。通过负载,让系统来选择是对应到哪个IP

所以就是     
                 -LOGINSERVER   -|

                 - LOGINSERVER  -|

                 -LOGINSERVER   -|
                -                  -accountagent -ACCOUNTDBSYSTEM
CLIENT -                                  |--------                                           
                -                                 |-----------
                - 
                -GAMESERVER -gameagent   -GAMEDBSYSTEM 
                -                                             |  
                -GAMESERVER - gameagent  -GAMEDBSYSTEM 

或者是:

        

        - LOGINSERVER   -             
         - LOGINSERVER   -   
         - LOGINSERVER   - 
      ..................................上面可以是N个                                                                             
        -                                -gameagent -GAMEDBSYSTEM 
        -GAMESERVER  -

Client
       - LOGINSERVER   -      
         - LOGINSERVER   -   
         - LOGINSERVER   - 
      ..................................上面可以是N个                                                                                            
                                         - gameagent-GAMEDBSYSTEM 
        - GAMESERVER  -

  accountagent    --              --- ACCOUNTDBSYSTEM 是全局的

一个服对应好多个LOGINSERVER


因为LOGINSERVER使用的动态配置,故而可在登陆下线没有多大压力的情况下,关掉几个LOGINSERVER,节省运维资源


不想写了,元旦又混了三天,明天继续吧。明天把前面的再扩展下,再说下分线的和分布的。牛逼的就不说了

posted on 2010-01-31 12:10 chatler 阅读(1511) 评论(1)  编辑 收藏 引用 所属分类: misceGame

FeedBack:
# re: 浅谈游戏服务器---功能模块上来看[未登录]
2010-02-02 14:08 | cppexplore
不错 好文!! 期待博主继续  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2012年6月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿(10)

随笔分类(307)

随笔档案(297)

algorithm

Books_Free_Online

C++

database

Linux

Linux shell

linux socket

misce

  • cloudward
  • 感觉这个博客还是不错,虽然做的东西和我不大相关,觉得看看还是有好处的

network

OSS

  • Google Android
  • Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.
  • os161 file list

overall

搜索

  •  

最新评论

阅读排行榜

评论排行榜