美文网首页
【扯淡】Pipeline设计指北

【扯淡】Pipeline设计指北

作者: 游学者夏纳 | 来源:发表于2021-01-12 22:43 被阅读0次

梦三手游已经C++加上Unity这套东西搞了三年多了,我相信无论是client还是server都有自己的一套理解,但是在client到server、server到client、server到server的消息pipeline这一块似乎还是有一点空白,因为这里涉及到一些大家平时都接触不到的领域,比如client根本不关心server的接口为什么要这么设计,server也不会去管client如何调用自己设计的接口,反正数据我都给了。我作为一个lua和c++都有在写的老混子,不敢妄谈best practice,就随便说一下自己的理解。

为什么说通知维护模式不是一个好的设计

通知维护设计具体可以参考英雄卡和熟练度,(因为熟练度的数据结构、系统逻辑和英雄卡几乎完全相同,所以当时熟练度我就完全模仿着写),它基本上就由初始化接口,更新接口和删除接口构成(init update delete),server只发送需要变更的单元数据,client只要来什么改什么,完全不用思考逻辑。那么这么写的好处有:

  • 省流量。只有初始化会发送所有数据,然后client维护这一块内存的原子数据更新就行,打开界面也不需要请求,直接拿维护的内存渲染就行。
  • 逻辑简单。对于client来说就是纯展示,有什么就显示什么,逻辑量明显减少,数据用单独的一个mgr管理curd就行。下发的数据错误都可以归咎于server问题,就是自己完全没锅。
  • 实时更新。因为是update下发更新,所以界面上的数据都是实时的,如果是请求响应的话就必须重新打开界面或者定时刷新了,二次浪费流量。

那么为什么这么一个省流又简单的设计不好呢?其实道理很简单,因为不像是端游,手游有一个无解的问题,那就是网络经常会不好,比如切4G网络的过程什么的,此时很多请求就会收不到。比如一个删除通知下发的时候,iPhone当时刚好APP在后台,那很有可能就没有处理掉这个消息。所以通知维护模式下经常会出现界面错乱的问题,更要命的是它出现错乱以后,玩家只有重新登录才能解决,因为client没法检查数据的正确性。所以在正确性的权衡下,就不得不放弃上面的这些便利。虽然请求响应模式耗流量,client还得自己计算显示逻辑,但是请求响应模式也有自己的优点:

  • 逻辑简单。这是对于server说的,因为在通知维护模式下,server需要处理所有的错误,基本上client逻辑也完全写在server里,包括数据检查、一些需要实时操作的消息通知(比如你的在线请求被拒绝了,就需要通知发起人关闭界面)。如果是请求响应设计的话,server完成逻辑操作以后甚至都可以不需要通知,让client请求过后再自行查询都可以(当然不推荐)。同时一部分逻辑就可以转移到client,一部分逻辑错误就可以在client提前处理掉。
  • 设计自由。对于client来说,如果请求能给到最新的所有数据的话,那么就可以自由选择发起更新数据的时机,比如有的人喜欢打开界面就发起请求,然后用回调更新数据,现在游戏里大部分界面都是这么做的;有的人喜欢定时更新,比如好友界面的好友状态是会一直变的,就设计成10s请求一次;有的人喜欢在数据过期的时候才发起请求,比如开泰之前写的排行榜,就是打开界面的时候发现没数据的时候才会发送一次请求,否则就使用缓存。这些设计都是可以的,要看具体情况来灵活设计过期的时间。(但是有的设计就很差,比如初版的个人资料prefab一打开就发送了14个QueryPlayerInfo,有好多个还是重复的type,把子界面的请求全部放到主界面,也不知道当时的人是怎么想的,这也是太过自由带来的问题。)
  • 熔断机制。在数据发生错误的情况下,在ErrorHandler可以重新发一次全量查询请求来抢救,数据更新后一般会重新更新一次界面,不至于网络问题导致界面错误的情况下,没有重新更新掉错误UI的机会。

我可以找到一个例子来说明问题,就比如我和伟哥之前写的挚友。因为当时王路有一个奇葩的需求,就是要求实时结拜(你可以把挚友理解成微型的军团,但是没有军团长,一切CURD操作要投票决定- -),所以当时在说服策划不成的情况下开始使用请求响应模式设计。现在挚友还是偶尔会出现bug,就是当时设计遗留下的问题,这个属于无解的问题,因为不可能保证网络一直是良好的状态。伟哥刚开始的时候也没理解我是用维护通知的方法在写,他还是用请求响应的模式在接,所以一开始甚至浪费了很多时间,搞到最后我去看lua代码把我自己都看傻了,伟哥根本没搞懂怎么接。最后也是写了一半的设计强行改成请求响应模式,代码是惨不忍睹,到处都是触发更新就全量下发的逻辑。。

这个属于一个版本的功能从6月份做到10月份,张天直接测到奔溃,我也修bug修到奔溃,本来好友就一大堆遗留bug,在bug上面开发bug,写到人都有点生理性排斥了。(顺便一提,这个功能到现在在用的玩家1%都不到,成为挚友的条件极其苛刻,就是个吃力不讨好的sb需求。)

所以这也带来一个宝贵的经验,你遇到一个奇葩的需求,宁可不做也不要硬上,硬做出来最后被玩家喷到改,还不如自己一开始就改……

响应模式的请求时机

比如我写的征战令client,页签1需要info接口数据,页签2需要info和task接口数据,页签3需要info和exchange数据,那我打开主界面的时候咋办呢?

exchange数据是server通知维护的,但是左边toggle上单独有个红点的需求,所以不得不在打开界面的时候,query所有的数据 info/task,虽然默认页签1,不会显示页签2的内容,但是还是需要task的数据来显示/消除页签2上的红点。然后打开页签2的时候又再次请求刷新task的数据,相当于更新了2次。这个属于server设计接口没和界面统一的情况,正常情况下一个界面就应该对应一个接口。这个是为了保证数据最新和正确性做出的流量牺牲,因为征战令上的小UI特别多,不全量更新的话测试又会提bug过来了。我怕麻烦……

但是这就是属于设计上的问题,你也不能单纯的归咎于server问题或者client问题。之前我和丽林对功能的时候,我就说我靠你这个接口怎么这么设计,你不看文档的吗,这个界面是这样的,这个接口一大堆数据我就只用这一个字段。然后他说我为什么要看文档,这个界面不是你们看的吗。我想了想其实也无话可说,因为你不可能去强行要求一个写接口的人去看UI,他能把数据全量给你已经是大发慈悲了。我写GM接口的时候,我也不可能去教web说你这个界面应该怎么写,缓存结果应该用我的查询而不是你自己的请求缓存(web还是无视设计模式自行处理,有段时间我真的想砍死他,我都想自己来写web了)。虽然我是这么设计的,但是如果你对面的人不用的话,那还不如就按照他的风格去定义接口,不然就是自讨苦吃。

总而言之,你请求的越频繁,流量耗费越多,但是刷新也就越快,数据就越准确。如果server有下发刷新通知的设计,那当然是最好的,最好连红点提示也单独用通知告诉我那就更好了。如果没有,那就尝试下说服策划和测试降低更新频率,因为你不可能完全按照他们的期望来。比如他们当然恨不得所有的排行榜都tm的实时刷新,虽然它是个周榜,然后却要你几秒钟刷新一次,这就是sb的需求,坚决要打回去。太听策划的话,最后吃苦的一定是你自己。

WorkFlow

我在写client的时候,我就会思考什么server要怎么搞,我才是写的是最舒服的,当然人各有异,以下纯属个人观点。

第一点就是client如果要接的舒服,那一定是参与设计的。如果server逻辑已经写完了,你再让他大改,那你过一个力量对抗吧。所以一开始的时候要说清楚自己的要求,比如你明显需要一个红点的设计,那此时如果有一个单独的红点接口(有道具可领),那城镇icon和未打开的界面就不需要query全量数据,特别是有些接口的全量数据量还是比较大的。

(但是现在的现实情况下就是,client大多数情况下甚至不把server当成是一个部门的人,就等着接口下来然后开始撸UI。实际上你是可以有更多要求的,而且server如果有明确client设计文档的情况下,能够把逻辑理得更直观,否则他就是按照自己对client的理解来设计的。但是设计这个东西没有client经验的话,是不一定能和对接的client风格相同的,到时候你就会很迷惑,哇这个人怎么这么接,sb吧。

第二点server开发时间不宜过长,server拿到文档以后应该尽快设计好接口雏形,然后把新增的接口和枚举都更新到tapd上。然后每个接口如果预估复杂的话可以return假数据。就是首先保证每个接口有效,然后继续迭代开发。

第三点client拿到接口雏形以后,应该尽早写一个示意性UI让server参与,而不是埋头苦写。这样server如果也能早点参与client部分的话。他就能做出早期的适应性修改。你做为一个client如果不这么做的话,那我只能理解为你认为我写的api很完美,那我下次还是会这么写。这也不利于彼此的成长。

总而言之,程序这里开发应该是互相迭代的,如果是完全demand->server->client->unittest的流程的话,这出了问题永远也定位不到,也没人愿意承担问题,谁都会说我上面那个人就是这么搞的,我也没办法。

(顺便我吐槽一下我们的tapd流程,我觉得一个单子的负责人永远不应该指定到个人。我们的节点永远是两个对接的人,而不是一个个体。我觉得tapd的还是有很大的改进过程的,以需求单子为例:

阶段 说明 原版负责人 新版负责人
规划中 需求->文档 策划 策划,(程序)
实现中 文档->功能 程序(server & client) 策划,程序
策划待验收 compare(功能,实现) 策划 程序,策划
测试中 compare(功能,实现) 测试 策划,测试
已实现 功能->版本 策划 测试,策划

我觉得转移节点的时候,上一个人必须要留在节点里。一个节点的延迟很有可能不只是这个人的原因(比如测试提了一个很难复现的bug),这样如果你下一个人迟迟未解决,那这个单子就还在你的待处理列表里,你也会有一定的压力。比如我们很多策划提出了需求以后就不管了,对自己功能的热情程度甚至不如同期的测试们高。比如测试提了很多bug,但是很有可能就忘了下发的单子有没有解决,然后热情的太热情,冷淡的很冷淡,把转移掉的单子暂时寄存在自己的checklist里,至少是对下一个人的负责。)

相关文章

网友评论

      本文标题:【扯淡】Pipeline设计指北

      本文链接:https://www.haomeiwen.com/subject/nqneaktx.html