当前位置: 首页 > 后端技术 > Java

2013年开始喜欢上编程

时间:2023-04-01 17:03:10 Java

知乎有一个热点问题:你的编程能力是从什么时候开始突飞猛进的?刚看到这个问题的时候,我的嘴角微微上扬。记忆闪回到2013年,那一年,命运给了我一点正反馈,让我有点喜欢上了编程。这篇文章,想跟大家聊聊勇哥的阅读,阅读源码,重构,解决线上问题。1.初心2011年,我在一家互联网彩票公司工作。坦白说,选择程序员这个职业就是为了生存。那时候对缓存、消息队列、分发、JVM知之甚少,背了一些八字的文章,但对ibatis、velocity的使用非常熟练,写简单的业务代码。我负责用户中心系统,提供用户注册、查询、修改等基本功能。所有服务均以HTTP接口形式提供,数据传输格式为XML。虽然工作看似简单,但那时候不懂设计模式,写的业务代码非常臃肿,难以维护。人生中的第一个重大BUG也发生了。我负责的用户中心上线一段时间后就内存溢出了。我站在运维同学身边,看着他调整tomcatjvm参数,不知所措。后来发现我在用ibatis的时候用了一个类似的SQLMap,前端没有校验。数据库执行全表查询,导致JVMOOM。从t_lottery_usert中选择*,其中1=1user_name=#userName#id=#id#....经历了这次生产环境事故后,我的内心一直被一个声音折磨着:“遇到技术问题,能不能冷静一点,不要慌张,别人能做到的,我为什么要做?不行。””于是我开始疯狂的买书看书,毕璇老师的《分布式Java应用》这本书对我影响很深,通过这本书深入学习了JVM内存模型,核心集合类原理,并发等知识重点,尤其是jstack、jmap等JVM命令,边看书边练习。同时,为了开阔自己的视野,我疯狂地在javaeye和开源中国两大社区中搜索热门帖子进行学习。我会一遍又一遍地阅读一篇文章数十遍。时不时也会和运维的同学,或者说DBA聊天,因为和他们聊天可以从另一个角度审视公司系统存在的问题。有时他们的一句话能给我一些启发。2.第一次重构经过2011-2012两年的学习,彩票业务在2013年迎来小爆发,我也迎来了技术人生的第一次重构。奖品计算服务是一项非常核心的服务。开奖服务包含多个子服务,其中开奖开奖系统是用C#版本开发的。结果发现,当彩票订单数量较少时,计算奖品服务还算稳定,但一旦量级增大,C#版的计算奖品服务就会挂掉,计算奖品时间从半小时变成两小时到三个小时,严重影响订单的退货。奖。当时满脑子都是想争取一口气,证明自己不再是两年前的弱鸡了,于是主动请缨重建奖励计算系统。但领导半信半疑,不敢说实话。当时情况比较紧急,他就答应了。技术团队比较草率,没有统一的基础框架,每一个新项目都是按照研发的喜好来搭建。因为之前接触过京东的基础框架,所以第一次使用京东框架搭建自己的作品。奖金计算的整体逻辑比较简单。服务接收抽奖活动号,查询抽奖子订单,根据活动结果判断彩票是否中奖,修改子订单中奖信息,最后将中奖信息发送到消息队列,最后派遣中心返还奖品。还记得开始写代码那是一个周末。不知疲倦地工作了两天,没日没夜,指尖在键盘上敲击,代码显示在屏幕上,流畅流畅。这种感觉是美好而美妙的。两天重构完成,如何验证是否正确?在测试环境中简单运行了一下,没有发现任何问题。领导们也觉得不可思议,但这能上线吗?我的心怦怦直跳。每天的彩票计算涉及数十万人民币。如果计算有误,影响会很大,我要承担相应的责任。领导没有给我任何指导和建议,让我灵机一动:“生产环境不就是一两年的历史数据做订单计算吗?对比一下重构版本的计算结果和重构版本的计算结果”生产环境,你可以验证它是正确的。率?”。于是,我对代码做了一些微调,去掉了最后的数据写入操作,并将重构版本计算的量和c#版本计算的量进行了比较。如有金额差异,订单数据将写入文本并发送邮件提醒。让我意外的是:在近千万的历史订单中,重构版的计算结果非常准确,只有两次计算异常,而且计算速度非常快(快了将近10倍)。修复BUG后,与C#版本并行运行20天左右,计算结果全部准确。于是,领导同意老系统下线,上线重构版本。这次成功的重构给了我信心:我能在这个行业生存下去。技术层面:自己搭建项目,接触到消息队列作为消息总线的架构模型,体会到应用基础框架的重要性。同时,我也隐约感觉到:“写代码还是比较容易的,验证代码的正确性也是很考验工程能力的。”3.阅读源码2013年,是我阅读源码的起点。看了Druid、Cobar、Xmemcached等的源码。3.1数据源连接池Druid奖励计算系统重构后,有个小插曲。发现每天第一个请求的数据库连接有问题,于是写邮件给Druid的作者文少。邵文回复了我的邮件,我马上打开源码,发现我配置的数据库连接池的心跳有问题。核心点是连接池需要定时向oracle服务器发送心跳包,因为数据库为了节省资源,会关闭长时间没有读写的连接。所以客户端必须定时向服务器发送心跳包。这种对源码的简单探索给了我长久的动力,也让我更加关注技术背后的原理。精神层面:向别人请教是会上瘾的;技能水平:了解连接池的实现原理;架构层面:客户端和服务端的请求需要考虑心跳。3.2数据库中间件Cobar还是2013年的时候,我接触cobar的震撼无以复加。当时,互联网大潮汹涌,各大互联网公司的数据呈爆发式增长。曾经在javaeye上看到淘宝订单技术员分享分库分表的帖子。解决办法,可惜限于篇幅,文字始终是文字,总感觉离题太远。没想到cobar是开源的,我还记得用navicat配置cobar的信息,可以像单独一个mysql一样使用,数据会均匀分布到多个数据库,就像变魔术一样。对于我当时技术思维薄弱的人来说,就像三体遭遇战人类舰队的一滴水,降维打击。由于对分库分表原理的渴望,一直没有很好的学习方法。大概用了3个月的时间,把整个cobar的核心代码copy了一遍。真的是智商不够,还需要体力。但是光靠体力实在是不够,常常陷入疑惑。这个我看不懂,那个我也看不懂。一边抄代码一边学习,好像并没有那么大的进步。那么,我们必须找到突破口。网络通信是非常重要的一环。当时我做了一个决定,我要剥离cobar的网络通信层,深入理解使用nativenio实现通信的模式。剥离的过程也很痛苦,但是我有一个目标,这样我就不会像无头苍蝇一样,后来我有了人生第一个github项目。在追cobar的过程中,好像和阿里的大牛来了一次面对面的交流。虽然我不够格,但是大牛耐心的教导我,耐心的解答我,打通了我的任督二脉。我很感激。毫不夸张地说,它是我一生中最重要的开源项目。“你想学?我教你。”4、实战:知行合一2013年下半年,我参与了很多系统改造,解决了很多生产环境的问题。我经常思考如何将所学知识应用到实际场景中,也做了很多有趣的尝试。4.1多级缓存开奖系统中有游戏分析服务。页面非常复杂。采用的方案是静态nginx页面策略。每天通过定时任务将游戏分析数据写入磁盘NFS共享目录,配置Nginx访问。静态nginx页面的优缺点非常明显。静态页面访问速度极快,性能非常好;维护成本高,定时任务定时生成相关页面,经常延迟更新,代码是N年前的古董,php写的。领导决定用Java重构,核心指标是:性能。看过红薯哥写过一篇文章:oschina上的一个双缓冲思路。https://www.oschina.net/quest...我大胆采用了Ehcache+memcached的双缓存架构。重构过程也很顺利,上线后性能还不错,维护也比较简单。4.2消防彩票系统业务量增长极快,生产环境经常会遇到一些莫名其妙的问题。每次遇到问题,我都把每一个问题都当成自己的问题,尽自己最大的努力去解决,成为一名救火队员。发生了很多故事,举两个例子。▍调度中心不能消费某一天的数据,调度中心不能消费消息队列中的数据。消息总线处于发送状态,没有接收状态。整个技术团队都处于极度焦急的状态,“如果不能出票,那将是几百万的损失,如果用户中了两个双色球?那就是几千万了。”每个人都急得像热锅上的蚂蚁。这也是整个技术团队第一次遇到消费积累,大家都没有经验。首先想到的是多部署几个调度中心服务。部署完成后,调度中心消费了上千条消息还是挂了。这时架构师只能采用重启策略。你没看错,就是重启大法。说起来真的很惭愧,但当时真的是只能这样了。调度中心重启后,花了1万到2万元又挂了。只能重新开始。来回20多次,像挤牙膏一样。而随着开票期限的临近,这种精神上的紧张和恐惧也变得更加强烈。最后,经过1个小时的手动重启,消息终于被消费了。当时刚好在看毕轩老师的《分布式java应用基础与实践》,怀疑是不是线程阻塞了,于是用Jstack命令查看堆栈状态。果然不出所料,线程阻塞在提交数据的方法上。我们第一时间与DBA沟通,发现oracle数据库执行了很多大事务,每个大事务执行需要30多分钟,导致调度中心的调度票线程阻塞。为了避免堆积问题,技术部门后来采用了以下解决方案:生产者发送消息时,将超大消息拆分成多批消息,以降低调度中心执行大事务的概率;数据源配置参数,如果事务执行超过一定时间后,会自动抛出异常并回滚。▍实时比分页面卡顿同事开发了一个实时比分系统,所有请求从缓存中获取后直接响应。一般情况下,从缓存中查询数据是很快的,但是如果在线用户多一点,整个系统就会特别卡。通过jstat命令,发现GC频率极高,newgeneration被几个request占满,CPU消耗都在GC线程上。初步判断是缓存值太大。正如预期的那样,缓存大小约为300k到500k。求解过程还是比较曲折的,分为两步:修改新生代的大小,由原来的2G变为4G,并简化缓存数据大小(由平均300k左右变为80k左右);将缓存分成两部分,第一部分是全量数据,第二部分是增量数据(少量数据)。页面请求第一次拉取全量数据,当分数变化时,通过websocket推送增量数据。经过这次优化,我明白了缓存虽然可以提升整体的速度,但是在高并发场景下,缓存对象的大小仍然是一个需要注意的点,一不小心就会发生意外。另外,我们还需要合理控制读策略,尽量减少GC的频率,从而提高整体性能。5、文末特别喜欢毕淑敏对命运的解释:渐渐地,我终于发现命运是我懦弱时的挡箭牌。每当我大声呼喊命运的不公,就是我准备逃跑的前奏。命运就像一个篮子,我把我的宽容、原谅和我所有的懒惰都装进去,然后给它盖上一层命运的面纱,我背着它慢慢向前走,带着一种自欺欺人的坦诚。我当初选择程序员这个职业,一开始就是为了生存。我不聪明,学什么都慢,但我只想证明自己,所以只能一直学习。一点点的满足。于是,在2013年,那个年轻人找到了自己的世界。喜欢一个人,是藏不住眼睛的。如果你喜欢编程,那你的眼睛也是藏不住的。