windy path

凌虚

服务器开发工程师
勤学苦练,年复一年

用爱发电项目开发两个月的心得体会

前阵子我参加了一个用爱发电的独立游戏项目,是由一批网友聚集起来想要复刻某三国单机游戏的团队。“用爱发电”,即不获取酬劳,为了自己的梦想去努力奋斗。从24年2月4日开始得知这个组,2月7日进组,到4月4日离开,共经历了两个月。 我不想过多讨论这个游戏开发组中遇到的问题,因为在这个组的问题有其特殊性,并不具有普遍性。我想探讨的是个人开发者面对用爱发电项目时需要注意的内容,和一些心得体会。 进组时间线 2.4 了解这个项目组 2.6 通过面试,成功进组 2.7-2.17 过年回家,没有学习项目知识,被轻微pua 2.18-2.24 开始学习项目知识 2.24 修复第一个bug 2.25-3.30 修复大大小小6个bug 3.30-4.4 发现自己做的很多是帮人擦屁股的活,觉得没有意义,进这个组浪费太多个人时间 4.4 提出离开 团队的问题 这个团队的问题主要是文档不齐全,大量依赖视频教程,但是视频信息密度太低,学习起来难度很大。另外,之前项目的代码中,很多功能实际仅完成了50%大概一半,另一半作为bug处理,但是这样会让处理bug的人很难下手,因为无法明确知道功能边界在哪里,改起来心里没有底。加上文档不齐全,修改难度非常之大。最后,用爱发电项目没有直接利益刺激,做一段时间就觉得是帮人打工义务劳动,甚至没有github星星之类的荣誉,让人做久了有倦意。 用爱发电的心态 经过这个项目的体验,对于我个人,有以下几点体会: 一个人的空闲时间,比想象中更多一些。有时候,高效率的一两个小时就能做很多事。如果对自己要做的事情有正确的预期,那么行动起来之后并不会有什么心理负担,并且完成一点小小的里程碑也会让人感到非常愉悦。 对于一个大的项目,千万不要马上就想着把功能都做完。一定要花时间对模块进行整理。并且,在做功能模块的时候,一定要划分好什么是“功能未开发”,什么是“Bug”。如果是未开发功能,则要对功能进行分析,评估工时,逐步推进;如果是Bug,则要对Bug的成因进行分析,分析结果正确的原因和结果错误的原因,分析后再讨论解决方案。这是一个很漫长的过程,千万不能因为没有收入没有好处而松懈。 如果是不能获得直接报酬的项目,一定要控制好预期,一定要找到自己继续下去的理由。并且,没有直接报酬的话,想放弃,也不要有任何的愧疚感。 而在选择什么样的项目组,我也有以下几点体会: 如果一个项目不直接提供报酬(如这个组或者为github公开仓库贡献),那么就一定要索取其他东西(如github星星、行业内名声、资源、人脉),又或者是这件事本身已经足够有意思、有价值、有希望。一定不能什么都不要,那样是违背客观规律的。 什么都不能给予 = 无价值 = 无用之事。 只靠画饼,一定是坚持不下来的。能坚持下来的一定是在有价值的情况下,成为习惯,一点一点做出来的。 尽量要有主业。有自己谋生的手段,才能真正支撑自己的理想。或者,这份没有报酬的项目能提供资源和人脉,那么它必然很有价值。 随时调转船头,不要一条路走到底,不要害怕试错和犯错。没有报酬,也就意味着没有责任。虽然我们普世价值中建议大家负责,但是前提是要保障自己的自身利益。 项目总结 我不能说我在组里一点错都没有。事实上我从零开始学lua,学习整套代码框架的速度比我想象中要慢,弱类型语言,游戏客户端开发,确实和游戏服务器开发略有不同。我花了比较多的时间才完成基础项目的了解。这一点让我体会到,当我们在讨论某个开源项目的时候,能真正理解开源项目代码,并且帮忙处理问题的人其实是很稀少的。阅读别人的代码是有门槛的。门槛不止在于语言、框架和设计方式,还在于当没有人能实时解答问题。当没有人能正确提供解答时,如何仅靠源码和运行环境(有的项目甚至没有运行环境)进行代码的学习。这个门槛甚至包含我们是否有足够的业余时间来分配给这些项目进行研究。 所以,“为什么我要做XXX”,其实是一个非常有价值的问题。如果是为了“找工作时简历能好看点”,那么直接背面经,刷题,学习框架细节和各种常用设计,甚至是向更有经验的人讨教吃个饭,可能会更有用;如果是觉得“有意思”,那么这个项目的有趣程度,是否达到值得替代你原有的休闲方式(出游、玩游戏、喝酒KTV等),如果没有,那么也没什么必要去做;如果是为了学习某个技术热点,那么这个技术热点真的有意义、有必要学吗?也许在开始动手之前,先想明白这些事才是首要之事。 另外,学习别人项目的代码,是一件非常乏味的事情。由于编程开发的自由性,每个人都有自己的设计方案。虽然有代码规范,但是人会经常受限于自己的思维之中。这时候务必要有一种开放的心态,尽力去理解代码。当然,更重要的是在遇到困难时能否对项目价值做出合理的判断,能否利用好每一分每一秒。因为业余时间就应该属于自己。 最后,关于远程沟通,有很多噪音在里面。一个词,一句话,说出来就有可能变味。这也是为什么技术文档是如此重要。好的文档应该是没有歧义的,可以用来传递大量信息的介质,甚至比代码本身更重要。特别是无法面对面沟通的两个人,如何处理好沟通的节奏,如何“心往一处使”,是一个非常复杂的问题。关于这一点,在这两个月的实践中,我在这方面的想法是,双方都要有很高的技术水平、沟通能力、且拥有足够的耐心和责任心,才能让这件事推进下去。所以这应该也是远程工作都倾向于招聘高级程序员的原因吧。 关于开源项目 接下来的内容并不与这两个月用爱发电项目相关,但也确实是这两个多月的心得体会。 自23年5月开始,我重新开始游玩Counter Strike反恐精英这款游戏,它的缩写是CS,刚好和Computer Science的缩写一致。所以有时候我觉得自己也挺应景的,我在大学时主动选择CS,然后工作时候从事CS的职业,现在又几乎“沉迷”CS这款游戏。 最近的体会来自于CS主播莱昂凯讨论什么样的人能成为CS职业选手,这个视频 。在这个视频中,主播莱昂凯提到了很多CS天才少年,他们游玩CSGO大概700多小时的时候就已经打到了天梯第一等成就,然后才会有职业战队邀请他们去当职业选手。而大部分带着所谓“打职业”的梦想的路人玩家,其实根本没有足够的天赋去成为CSGO的职业选手。最后,莱昂凯说,大多数职业选手是因为自己真的很喜欢CSGO,打着打着发现自己可以打得更好,可以和更厉害的人一起对决,所以他们才去当职业选手。 这一点我在开源项目的维护者身上看到了类似的影子。远的不说,Vue的作者尤雨溪正是在谷歌工作的业余时间开发了Vue,然后由于其设计简单易用而引起大部分开发者注意。但前期核心团队也只有4个人 这个视频的15:06处。也就是说,大部分人其实根本无法真正踩进github贡献的大门,因为他们并没有足够的热爱和天赋(是的,我说的就是我)。基于不同的原因,将自己的代码上传开源,并且无偿为社区维护,就类似于现在在CSGO打职业的职业哥们,他们不像LOL项目有完整的LPL、LDL赛事体系能提供足够的温饱,他们兢兢业业的练枪、学道具、跑战术、报名各个大小比赛拿奖金。这真的需要热爱和天赋。只有足够的热爱才会让他们面对一件虚无缥缈的事不断努力,只有天赋才能让他们在自己的技术上登峰造极,让全世界能看到并且提供支持。 找到有价值的项目,然后坚持下来,最后等待一个结果。结果可能有好有坏,但愿意尝试和坚持的过程也总会有收获。 为了星星而做开源的开发者,就像那些为了当职业选手而玩CSGO的玩家,一定是坚持不下来的。这种没有直接利益的事情,能坚持下来的动力来源,只有热爱和习惯。除了那些外界能提供的金钱、名声、荣誉之外,你在业余中做的事,是否真的让自己感到快乐,是否是自己特别想做特别喜欢的事?业余时间的这一秒是否真的属于自己?如果是的话,那么太好了,你没有浪费这一秒,真正做到了热爱生命。 我想起Linux的作者Linus写的自传的书名叫《Just For Fun》,我想起EDG大满贯辅助Meiko接收采访的时候说“英雄联盟真的很好玩”(大概是类似的话)。Linus不是带着“我要做一个比UNIX更好的操作系统”的想法开始编写Linux,Meiko也不是因为有“我要成为世界第一辅助”的信念才开始打LOL。但是最后他们真的慢慢做到了,这背后充满了热爱和坚持。 扪心自问,我目前既不能快速上手一个不熟悉领域的项目,又暂时没有遇到自己特别想做的、热爱的开源项目,所以我还是找个班上,然后多看看书、看看开源项目开拓视野,等待属于我的“那个项目”吧。

四月 14, 2024 · JohnathanLin

以魏延“子午谷奇谋”讨论软件需求可行性问题

摘自某百科:《魏略》蜀军大将魏延认为,长安守将夏侯楙年少,娇生惯养、怯而无谋。今日假若我魏延领精兵五千,背负粮草五千,直由山道深入,寻秦岭而向东,到达子午再往北行。不过十日就能抵达长安。夏侯楙见我军突然出现,必然乘船而逃。如此一来,长安城内只剩下御史、京兆太守而已。长安北门有散民之谷,足以供应我军粮食。而由东方相会合,需要二十日,届时丞相您在由斜谷赶来,必定可以在此与我会师,这么一来,可以一举平定咸阳以西。 子午谷奇谋乱谈 年少时期,我第一次听说魏延的“子午谷奇谋”,也曾感叹可惜诸葛亮没有采纳。十几年后,当我在办公室听着游戏策划和开发吵需求的时候,逐渐理解1800年前诸葛亮没有采纳的理由。 古时候行军打仗,是一件残酷的事。老百姓被官府征召入伍,放下自己的农活,随意培训几天就上战场成为所谓的士兵。而每征召一个士兵,就会失去一个农夫。一旦征召过多,必然出现粮草不足的情况。而没有足够的农民从事农业,必然导致粮食匮乏。为了获取粮食,就只能去“东征西讨”。所谓的“一将功成万骨枯”既是如此。而为了能在破城后获取最大的收益,部队长官一般会放任士兵进城烧杀掳掠,到了史书上就只剩两个字“屠城”。 而刘备和诸葛亮一系,在三国时期的记载中,没有一次“屠城”。客观来说刘备真正发家的地方是从荆州请到诸葛亮后,而荆州益州平原辽阔常年没有战乱,粮食充足,刘备他们不需要屠城,也可以获得足够的粮食;但主观上刘备可能是出于理想主义或者是汉室宗亲的旗号,做到了那个时期下真正的“爱民如子”。到了季汉时期,诸葛亮六出祁山时,诸葛亮没有选择持续的连年征战,而是让士兵有规律的务农,储备粮食。尽管蜀道难,难于上青天,但成都开阔的平原,让当时的成都老百姓过得不错,还有艳丽的特产“蜀锦”远销“国内外”,他们生活的水平比200年初的北方老百姓幸福多了。诸葛亮的善良在史书里没有明写,但是从一行行文字的字里行间中跨越1800年传递给了每一个热爱三国历史的人。 这样一位爱兵如子的统帅,真的愿意让那五千士兵冒险吗?那五千士兵,脱下战甲,也不过就是山间农夫。也许他们背后还有五千对父母,五千个妻子和孩子?这样冒险的决策,对那些后方的亲眷而言,是否太不负责? 可是对于魏延来说,五虎将之后,姜维(当然那时候还不认识)之前,没有人能比他实力和威望更大。对于军人而言,战争年代就是建功立业的黄金时间。倘若能一举攻下长安,他就会成为季汉武官第一名。这样的利益驱使,提出这种冒险的策略也就不难理解了。 当我年纪渐长,体会到战争残酷,感受到人心难测之后,只觉得魏延所谓的“奇谋”不过是异想天开,拿五千条生命赌一个名垂青史的微弱可能。这当然是不被诸葛亮采纳的。就算真的占领长安,那又如何?曹魏已经占领了几乎所有的北方地区,又有一批与曹操出生入死、经验丰富的老将。魏延真能守得住吗?答案也是显而易见的。 那么为什么到了二十一世纪,总会有人讨论“如果”?我想,首先是因为大家真真切切为诸葛亮和姜维连年征战失利感到惋惜;其次,魏延的策略确实有微弱的可能性,“也许”会让季汉取得一定的优势。而他们从没有仔细想过,成功了下一步怎么办,失败了又有多少损失。 软件工程学就是软件开发行业的兵法 目前几乎所有稳定运行的软件,背后都有一个维护团队。这个维护团队一般就做两件事:开发新功能和修复Bug。如何稳定地进行版本迭代、已开发的功能如何保持可用、新功能如何确保不影响旧功能,以及如何让用户对新功能的学习成本降到最低,这些都在软件工程学的讨论范围之中。如果把开发一个软件比作一场战役,那么设计软件即拟定战术,开发软件即招兵买马,上线发布软件即行军出征,软件面对用户时即战场前线。如前线告急,则需要指挥官进行调度,及时补足资源,修复问题。 为了能做到稳定度过开发、上线、维护等流程,软件工程学包含各种开发模型。如瀑布模型、快速原型模型、增量模型、敏捷原型等。初学时会觉得那些模型过于拗口难以理解,但简单来说就是如何分配人力以完成需求分析、软件设计、软件开发、软件发布、软件维护等一系列流程。由于前人使用瀑布模型时,客户无法再软件期间看到预期结果,在软件开发完成后才与客户见面,一旦客户不满意就不得不推倒重来。所以后人提出先设计原型给客户检阅,拟定术语表,确保讨论的内容和目标是一致的。(这也是DDD领域驱动设计的思想);又或者是将需求细分,完成一个需求的开发后,进行一次发布,即增量模型;又或者,提高交付的次数,使用敏捷模型,以某一个时间长度作为产出周期,每周进行一个反馈(并不要求一定要完成整个功能,而是每周完成一部分成果)。这些优化后的开发模型,思路都是让客户尽早观察到软件开发的过程。 但是目前国内外的软件行业中,大部分团队是不遵循以上的开发模型的。这背后有太多复杂的因素。如客户作为甲方,对自己想要的软件不明确;开发方作为乙方,由于是拿钱办事的一方,为了竞拍得到机会不得不夸下一些海口;而作为程序员,对于需求的理解可能也会和甲方乙方领导略有偏差;当然还有技术选型,性能指标等等一系列需要花费时间进行分析的内容。能够完全按照比较先进的开发模型,是需要甲方乙方及开发整体团队整体实力达到合格线以上(当然笔者无法定义何为“合格线”),并且进行有效的、充分的沟通。 再来说说需求变更。需求变更几乎是软件行业无法避免的事情。或者说,在地球上就没有什么需求是不会变更的。但是软件行业的需求变更,成本往往很难准确评估。软件,实际上是看不见摸不着的数据,我们不过是用显示器窥探里面的内容罢了。对于需求的理解,对于需求变动需要的工作量的评估,不同的人总有不同的评判。但领导并不一定能接受一线开发者的需求变更时间评估,这当然有多方面的因素,如客户要求、如领导和开发人员能力差距或者理解偏差等等。需求变更都会带来大量难以评估的成本。而这正是敏捷模型提出的原因之一。如需求变更,就临时改变方向即可。 关于时间评估,在充分相信程序员的情况下,接受程序员对开发时间的评估是最合理的。但是由于领导对程序员的不信任、或是希望短期内多开发更多内容,强行缩减程序员的开发时间,这在表面上会让功能上线并且看起来还不错,但长期埋的暗雷也就只有开发团队内部人员知晓。但是在这个行业中,每个开发都对着自己的显示器输入英文字母进行编程,领导并不能在短期内判断程序员是否在“摸鱼”忙里偷闲,因此一旦领导遇到几个不够称职敬业的程序员,会让他对这个行业其他程序员产生偏见。 而从这个角度看,开源软件天然接受所有人的Review代码审查,遇到问题就算主库不合并,也可以fork复制一个新仓库进行开发,天然保证这些暗雷总会被找到。这也是为什么程序员们总是更偏向于使用星星很多,知名度广,背后有高手维护的项目。 魏延不会是一个好“开发经理” 回到“子午谷奇谋”,从软件工程学的角度,即可以发现魏延想要在软件上线(大军已出征)的情况下,不顾开发成本(五千士兵),临时调整需求,并且这个需求很有可能导致软件崩溃(引发曹魏愤怒,倾全国之力讨伐蜀国)。而当前网络上关于子午谷奇谋的讨论,也总是停留在“成功了如何如何”。我想这也是为什么在软件开发过程中,领导总会提出“不太合理”的需求吧。 当然,我们目前谈论的是在软件工程学方面,为何临时修改需求且需求难度大时,需要避免做出这样冒险的决定。但这并非绝对。三国时期即有张辽带着陷阵营在孙权大营搅了天翻地覆;而进入二十一世纪前20年,商机遍地,赶着做软件先上线的公司都赚的盆满钵满。从这个角度想,软件工程学之下的合理,到了现实情况又不一定合理,团队能力,甲乙双方的需求和期望,成功的时间窗口,运气等等因素,是那样无法捉摸。一味抱着软件工程学(兵书)照本宣科,也许最后只能落得马谡失街亭的下场。 也许找到了加班的理由?(笑

三月 10, 2024 · JohnathanLin

MQTT协议中可变长度的具体计算方式(有计算过程解析)

本文介绍MQTT协议中,固定报头中的可变长度部分的计算方式。通过提供一些例子,将其他介绍MQTT协议的文章中没有仔细说明的计算部分进行解释。 参考文章 MQTT 5.0 报文(Packets)入门指南 这篇文章简要介绍了MQTT,但是没有明确提供计算过程。 MQTT简介之三(3) MQTT协议 报文的剩余长度如何计算和编码 这篇文章也介绍了MQTT,且有提供计算过程,但是是用一大段文字进行表述的,理解起来有些困难。 至于New Bing(GPT4.0)和ChatGPT 3.5,他们在做这种特殊规则下的二进制计算时,效果并不好。 如果在读完本文后你对二进制有兴趣,可以阅读这篇文章:CSAPP第二章-信息的表示与处理 如何计算可变长度? 以下是各大文章的相似描述: MQTT协议的剩余长度字段使用了一种变长编码方案,每个字节的最高位是一个进位标志位,如果为1,表示还有后续的字节;如果为0,表示这是最后一个字节。每个字节的低七位是实际的数据位,用来表示剩余长度的一部分。因此,为了得到剩余长度的完整值,需要循环读取每个字节,并用一个乘法器来计算出总和。 并且,提供了一张表,介绍剩余长度在不同的区间中时,需要使用多少个字节数: 字节数 最小值 最大值 1 0(0x00) 127(0x7F) 2 128(0x80,0x01) 16383(0xFF,0x7F) 3 16384(0x80,0x80,0x01) 2097151(0xFF,0xFF,0x7F) 4 2097152(0x80,0x80,0x80,0x01) 268435455(0xFF,0xFF,0xFF,0x7F) 读取过程分析 首先,我们明确MQTT协议读取数据的过程,是一个字节一个字节依次读取的。即读取完一个字节之后,再处理下一个字节。 那么为了可以动态控制可变长度部分的字节数,MQTT协议约定该部分的字节的第一位来标记“后续是否还有字节”。 明确这一点之后,我们抛开第一位,读取后七位的值,可以得到一个数。 想象一下,如果我们的可变长度res特别长,在2097152~2684354455之间,那么我们需要4个字节进行传输。此时我们将每个字节的第一位剥离,将剩余7位取出: 用变量a表示第一个字节后7位的数,用变量b表示第二个字节后7位的数,c,d以此类推。 那么我们会有:res = a * 1 + b * 128 + c * 128 * 128 + d * 128 * 128 * 128 或者咱们换一种写法:res = a * 1280 + b * 1281 + c * 1282 + d * 1283...

十二月 26, 2023 · JohnathanLin

关于游戏服务器配置表功能的探讨

简介 在游戏开发中,配置表是策划与程序员之间针对功能模块开发而搭建的桥梁。配置表在游戏开发中扮演非常重要的角色。策划需要向配置表填入各种数据,以完成功能数值的配置;程序员需要在项目代码中读取配置表,根据配置表和需求决定的业务逻辑来开发功能模块。 本文想探讨接触过的两个项目的配置表功能设计。在本文中,我不会泄露项目相关的具体信息,仅针对方案进行讨论。 两种配置表方案简介 此处先以表格的形式,列举两种方案之间的异同: 条目 大型SLG手游 小型MMO手游 策划配置方式 使用Excel配置 使用Excel配置 代码读取方式 将Excel文件中的内容转换成Csv文件 将Excel文件中的内容转换成Json文件 读取后存储位置 在项目启动前,先读取Csv的内容到内存中,转成二进制文件写入Zookeeper,在项目启动时加载Zookeeper中的配置表信息到内存 在项目加载时读取Json到内存中 热更新方案 修改Zookeeper中的二进制文件,通过Zookeeper发布订阅机制,分布式系统中各个系统从Zookeeper中读取新的配置表信息 先修改Json文件,然后读取Json文件到内存中 配置表检查 在读取Csv到内存后,编写检查代码,检查内存对象之间的相关逻辑依赖 无 大型SLG手游 这是一个比较大型的SLG手游项目,使用了分布式的系统,服务器数量众多,因此需要一个统一的中心服务器来管理所有的配置信息。这个项目使用的是Zookeeper进行配置表的存储。 策划配置方式 策划使用Excel进行配置。每一列包含中文名、英文名、key类型(标记客户端、服务器或是客户端和服务器都用,且标记是否是key)、字段类型(数据data还是文字text)。 一张表最多只能有两个key,一个主key,一个副key。 比如,如果有一个可以领取宝箱的活动类型,活动可能有多个id,以满足不同的活动包装;每个活动都有多个宝箱。那么这张配置表的主key是活动id,副key是宝箱id。 读取配置表 首先将Excel文件中的内容转换成Csv文件,这一步会将客户端的配置列移除,仅保留服务器用到的配置列。然后使用Java代码读取Csv文件,将Csv文件中的数据构造成Java对象,然后再将Java对象序列化成二进制文件,上传到Zookeeper中的某个节点之下。 graph LR A[Excel文件]-->|Python转换|B[Csv文件]-->|Java代码读取|C[Zookeeper节点下]-->|服务器启动读取Zookeeper|D[服务器内存对象] 服务器启动时会从Zookeeper的节点中加载配置表文件。 在后续的开发过程中,发生过因为策划失误,修改了Excel后忘记生成Csv,而导致Excel文件与Csv文件不一致的情况。在这个项目中,Csv并不是读取配置表过程中最后的输出产物,仅仅是一个中间产物,所以内部也讨论过是否可以直接从Excel文件中读取到内存中的可能。 热更新方案 当服务器启动读取完Zookeeper中的配置表后,订阅Zookeeper中配置表节点的变化事件。 当配置表Excel文件被修改后,依次执行读取流程到上传到Zookeeper节点,此时触发节点变化事件,Zookeeper会将变化事件发布给所有订阅此节点的服务器,服务器接收到配置表节点变化后,再次加载新的配置表文件信息。 配置表检查 当Csv读取到内存后,会对每一场表生成一个Java表管理对象,Java表管理对象与配置表一一对应。同时,这个Java表管理对象中,还可以进行多张配置表之间的逻辑校验。比如奖励表中的道具,必须在道具表中有配置,否则就会报错。 小型MMO手游 这是一个比较小的MMO手游,服务器进程数量比较少,配置表的数量也不多。因此它不需要使用类似Zookeeper那样的配置中心,只需要各个服务器进程读取相同内容的Json文件即可。 策划配置方式 策划使用Excel进行配置表的编辑。表头包含中文名,英文名,类型(数字,或者any任意结构)。 一张表必须要有数字id,且必须列在第一列中。其他的部分不做强制要求。 读取配置表 Excel文件会转换为Json文件。 在代码中,服务器会加载所有的Json文件。 程序员一般不需要针对某一张配置表单独编写解析代码。每一张表都有一个id,其他字段都默认读取为Golang的interface{}类型,每一张表都能读取成统一的格式,即:表名-id-其余各列数据。如果这张表是一张只有一个key的表,那么可以直接通过表名+id查找对应的数据。 但如果是不止一个key的表,那么可以在默认加载之后,再新开辟内存空间,使用自定义的结构进行存储。 热更新方案 策划修改配置表Excel文件后,转成Json文件,然后手动上传到服务器中。 通过RPC的方式,向网关服务器发送“重新读取配置表”消息,网关通知服务器集群中的所有服务器进程,重新读取Json文件到内存中,替换之前的配置表内存数据。 配置表检查 没有配置表提前检查,仅在使用配置的时候抛出err错误。 优缺点 类型 大型SLG手游 小型MMO手游 优点 (1)热更新方便,一次发布到Zk上之后,所有服务器都更新 (2)有配置表检查,在加载配置表时校验配置错误信息 (1)所有表都有默认取值的方法,方便开发 (2)Excel转Json后,Json文件方便人类阅读 缺点 (1)Excel生成的Csv可读性差,维护性差,不如XML,Json带有自己的结构信息 (2)开发繁琐,需要为每一张表定义类,并且预测配置表的关联,有可能会因为过度关联导致自由度下降 (3)在这个项目中,一个Excel对应一张配置表,导致Excel表数量过多,难以分类维护 (1)没有配置表校验,配错值只能等业务运行时抛出Error...

十二月 24, 2023 · JohnathanLin

Java并发编程中上锁的几种方式

前言:本文想要介绍Synchronized,ReentrantLock和ReentrantLock的Condition的相关用法。 Synchronized上锁 Synchronized可以修饰实例方法、静态方法和代码块。修饰代码块时,可以对具体的对象上锁,也可以对某个类(.class)上锁。 Synchronized是非公平锁 以下代码是通过给一个多线程能访问到的变量使用synchronized进行上锁,实现有序打印数字的功能。并且在最后会统计不同线程打印数字的次数: package com.windypath.lockcondition; public class Syn { int count = 0; final Object sth = new Object(); void play() { int loopTimes = 1000; SynThread t1 = new SynThread(loopTimes, "t1"); SynThread t2 = new SynThread(loopTimes, "t2"); SynThread t3 = new SynThread(loopTimes, "t3"); SynThread t4 = new SynThread(loopTimes, "t4"); t1.start(); t2.start(); t3.start(); t4.start(); } public static void main(String[] args) { Syn syn = new Syn(); syn.play(); } class SynThread extends Thread { int loopTimes; public SynThread(int loopTimes, String threadName) { super(threadName); this....

十二月 4, 2023 · JohnathanLin