最近,在HW的项目结束,回到成都趁着空档,做一个小小的经历总结。 (本篇文章涉及少部分技术,更多的是一些自己的思考和总结)
技术入门
以充满激情的想法、热情逐渐入门,逐渐增加广度,逐渐寻找自己的方向
在全球蛙电子商务公司我是一个对技术充满期待、充满热情的人,看到每个新技术,就在想为什么这么神奇,比如当时Redis的setNX指令为什么能作为分布式锁,他和Zookeeper的临时节点分布式锁又有什么区别,优缺点以及怎么使用的;垃圾回收器的思想,怎么调优,当然,还有我的黑白棋算法等......每天晚上,每个周末不是很忙的情况下,都在钻研着自己所谓的“技术”;在公众号、博客、CSDN等地方学习,每个技术都花不少时间,每个技术都能说出一部分来,每个技术都来者不拒,因为有大把时间研究和线下学习,但是现实中工作的部分就是完成业务逻辑,都和当前技术都只有少部分关联,甚至不关联,工作每次都是技术负责人安排小功能,性能优化也遇不到太高瓶颈。加上其他制度等原因,因为没有指导,没有老师带我真正实战入门,我带着这样的困惑,加入了TW
(仔细看了自己的经历,可能也没有错,技术学习怎么会有错呢,只不过技术很多,要花很多时间去学习,是否能跟上发展,就要和技术的发展赛跑了)
思考技术
软件更多的是服务业务,创造更多的行为价值
2018年10月刚开始加入TW,我没有直接去HW的电子交易中台而是去了某个小项目救火(简单说就是去加班的),初步了解到TW的工作方式:扁平化、自主,没有人分配工作,(在此时刚加入公司,思想还很保守并没有及时在关键问题站出来而是旁听)这里的人都很优秀,在技术选型上都能说出很多我都没听说过的技术,他们可能在某一项技术的深度上不如我,但又能根据不同的场景想到不同的技术实现,这时候就会不由自主的问他们选型的方向:某项技术只是一种实现,我们需要关注整体业务的价值。是的,这句话影响了我这两年的方向,之前很容易扎进技术的细节,技术怎么实现并不那么重要,而是他的思想,比如某技术涉A不能使用,我们还会找出与他相同近似的技术,而此时,最需要的就是快速学习和上手能力。(因为之前深入理解过一些算法【博客算法及其感悟】,在学习新的技术面前让我理解到主要先分析其思想,为解决什么样的问题而产生的,与此时还会对比相同技术,最后实践为王的理论,让我也能很快上手目前项目中使用到的技术。)
接着遇到了勋哥在HW的一个小项目中实践领域驱动设计DDD,这次更加是站在业务的角度,分析代码的实现,将核心逻辑内聚,最后把技术放在了技术支撑层,选择这个技术只是因为这个技术符合我们的业务,而把它放在支撑层的意义是:它是可变化的,我们的业务才是核心逻辑不会关心基础支撑层。之前学到的技术,很可能只是支撑这个项目发展的基础,让我深深地理解到:技术是服务业务的,我们在更多的时候应该关注它的行为价值(创造什么业务价值),而不是关注技术本身高大上什么的(简单说可以理解为:做有价值的事)。
随着此时技术的进步,公司的文化也在不断促进着我去反思工程师文化,一个人是会用技术写完代码就可以了吗?因为软件是变化的,会有源源不断的需求随之而来,我们需要不断维护自己的代码,这个阶段我也学习了《重构》、《代码整洁之道》、《大话设计模式》等整洁代码之术,让代码排列整齐,写出易于扩展、易于维护的代码是工程师发展技术的同时需要追求的东西。
技术思想
架构演进模式,复杂系统不是一蹴而就的,是逐渐演进的
2019年初接触到Flink的一个自动化测试工具项目,接触到了一个不同的TL玉山,这个项目不同的点就在于每个模块的技术方案是我们开发设计的,而我负责的部分恰恰是核心模块测试工具的平台搭建,因为疫情爆发,那时TL在北京过来在项目方案设计初期刚好被隔离。起初在不了解大数据业务的情况下,我根据自己的理解写了一个版本,这个版本在和TL过了一遍后,因为不理解业务就设计,完全也不是项目需要的,最后也进行了重写,自己的理解、封装、抽象能力第一次受到了深深的打击,痛定思痛通过自己的努力在项目快结束时也得到了TL和团队的认可(项目最负责开发)。工具框架的最大目标就是在现有的业务基础上还支持未来类似业务的扩展,也就是在这里,我真正意识到了:分层和拆分就是对可变和不可变的剥离,让可变的适配层逐渐完善,进而最大化的降低开发成本。
经过这个项目之后,我对Spring这个框架的理解进而加深了,因为这个项目的初期设计没有使用Spring这个优秀的框架,想让自己快速学习某个技术,尝试着剥离它,自己去实现一套,如果最后实现一部分,你就会很快发现你真的理解它了,学习一个框架时,不要急于扎进技术细节,站在作者的角度思考问题,这样才会深入理解框架的含义
(后来在小组内也根据自己的理解分享Spring框架,上图为Spring中的Bean的形态)
2020年7月依然是跟着勋哥加入了电子交易中台物流中心开发,一个真正业务复杂、高并发、较高数据量的项目,我刚开始的时候对领域驱动建模也只是一点点简单的理解,后来在项目中根据TL、BA、甲方等小组成员,一起事件风暴建模、落地、实现,根据业务建模使用MongoDB,这些技术只是因为符合业务模型的展现形式,是技术的类型适合我们而不是我们盲目的选择了某个技术。随着业务的发展,不同的承运商接入,业务的复杂度也随之增加,DDD无法解决当前的问题时我们又借鉴六边形架构思想,拆分下游适配Access服务;随后也逐渐接触到一些软件演进的方法:绞杀者模式,通过逐步替换遗留系统的功能,新的服务最终会替换旧服务的所有功能。物流中心微服务改造无疑是成功的,在21年初期,已经完成了80%以上的承运商切换。
(上图来自《架构整洁之道》的整洁架构)
后来我们都转入了库存一致性开发小组,此时接触的业务复杂性和并发性与之前都有指数的上升,让我真正意识到没有什么技术是可能真正解决所有问题的,数据库锁的压力太大,那么就将现有的锁上升到应用层,使用Redis的锁,但随着业务的发展发现仍然无法满足时,又引入了聚类分批、排队蓄水的队列等功能,逐步降低压力最后用其他手段解决了数据库锁高并发的问题;指令的诞生,也是同理:对于某一个入库操作,我们要修改库存、通知下游进货等,这个过程可能是很耗时的,于是就根据当前的业务现状封装出来一个指令的模型,每次根据当前状态操作库存,操作完成之后修改当前阶段的状态,通过逐步更新、逐阶段修改库存完成整个指令的复杂逻辑操作。最后想了下确实是非常有幸的参加了HW电子交易中两个核心模块的开发:技术只是解决问题的手段,我们并不是需要很强的技术、算法能力,才能完成某个功能,切合实际的分析当前问题,逐步拆分复杂问题为子问题,思考、反思技术的本质,才是程序员学习技术的真正意义
展望技术
技术没有银弹,我们需要合适的时候选择最适合我们的技术
技术是Trade-Off,我们无法选择所有想得到的东西,我们选择某项技术的同时都对应的一种舍弃,比如想让不同的服务资源互斥,因为各种方案决策选择使用Redis实现分布式锁,那么它就真的安全了吗?(推荐阅读:Redis分布式锁到底安全吗?),ThreadLocal的开发者为了降低多线程产生的并发影响,设计时将缓存的Map放入了线程中,虽然解决了并发抢占的问题,但是同时引入了内存泄露的风险... 为什么JVM的设计者不断在改进GC垃圾回收器,从Serial->Parallel->CMS->G1->ZGC不断演进过程中,也没有完美解决GC的问题,我们的目标是降低系统延迟提升整体吞吐量,但是GC是不可避免的,因为业务逻辑每时每刻都在创建对象,不同的GC收集器,旨是在能够在短时间内回收更多的垃圾,调整他们的比例,分代回收只是为了让朝生夕死的对象更多的停留在年轻代,使用垃圾回收快速回收他们,让系统中稳定存活的对象停留在老年代,分区回收是为了有价值的回收垃圾更多的部分,降低停顿时间必然增加GC频率,降低GC的频率必然增加GC的停顿时间,我们需要取舍我们需要的是什么,JVM的这些努力,就是在告诉我们:因为技术没有银弹,我们需要合适的时候选择最适合我们的技术去解决我们的问题。在我们做出技术选择时,了解到足够深度时,逐渐发现技术没有银弹,任何的选择都意味着放弃——当你要去获得一个东西的时候,你总是需要放弃一些东西。我们做软件设计或算法设计一样,用时间换空间,用空间换时间,用正确性换效率,还有CAP理论,总是有很多的Trade-Off,正如这个短语的原意一样——你总是要用某种东西去交易某种东西,你永远无法找到同时满足空间和时间的完美方案一样
虽然每个人都很聪明,但是软件的发展无法按照最优的想法逐渐发展下去,因为什么方案都有两面性,随着业务复杂度的提升,问题也逐渐暴露出来。类似的问题就仿佛是指令的设计一样,增加了指令让所有业务都能分阶段逐步执行,但是又带来了业务复杂度、操作复杂度,开发成本和维护成本呈指数级提高,仿佛又回到了《架构整洁之道》中序言所说,没有能立马具体解决问题的方案,只有当碰到足够多的壁、掉足够多的坑,经历过这些痛苦后再来看软件设计,才会发现其真正的价值。正因为是这样的现状,目前也打算根据行为价值推导架构价值,也是自己的长期方向:从更深层次的思考软件设计,从技术的本质思考他们之间的联系。
为什么要写这些呢,就好比之前在某个公众号看到过的话一样,写这些的时候可能某些点不是正确的,但是我们成长的过程中,会经历一个又一个阶段,在每个阶段我至少应该留下一些东西,证明当时的自己,曾今也为一些东西努力过。在多年之后,随着时间的流逝,这些经历可能也愈发模糊,但是记录下来,多年之后再次回首,我肯定会感谢当初的自己。
饮水思源
最后还是感谢培养过、指导过我的琰哥、山哥、勋哥,在我技术前进、思考的道路上,不断进步自然也少不了贵人的帮助,最后还是祝福各位大佬:身体健康、一帆风顺、事业有成!