最近在维护一个三年前的旧代码,用的是laravel框架。php
从某些方面来说,这个代码算是比较标准为了实现“在规定的时间内完成相关功能”,同时“程序员水平不高”、“通过大量优化”以后,变地特别烂的。可是其中,程序员的水平和态度是最主要的,其余相对于而言都是次要的。java
固然,我就是那几个程序员之一,因此我能够放心大胆地说本身的坏话。laravel
另外本文会屡次提到语言间的对比,固然本文的目的并不在此。程序员
不管是当年来看仍是如今来看,Laravel框架思想仍是结构,都算得上是“Modern PHP”的典范。spring
Laravel之于php,就至关于springboot之于java。docker
Laravel针对http请求引入了中间件,稍微配置一下即可以很方便地使用相似servlet的拦截器,功能还远比servlet强大。数据库
针对ORM类的需求,自创了eloquent框架,使用的简洁性上也算得上是一流。安全
至于安装、配置、部署、依赖等等,laravel也都提供了彻底通用的方案,这就很可怕了。springboot
能够这么说,若是咱们彻底按照laravel的架构,彻底遵守laravel的文档,写出来的代码即使不会很优雅,可是也绝对不会特别坑。php7
固然,laravel缺点也是很明显的。最重要的一个缺点,性能。
做为PHP框架,laravel的性能无心识特别拖后腿的地方。咱们这里已经没有详细数据,可是大概的数据咱们能够提供一下:一个最简单的路由,里面只有Redis::set这一个操做,而且没有任何中间件或者计算逻辑,24C64G的机器,只能支撑到大约300+QPS,即使开了opcache等也没有质的提高。至于php7,第一个7.0版本是发布在15年12月,项目上线四个月后,不要说还要等laravel支持PHP7,更不要说php7也知足不了性能需求。
这也就是咱们初版代码就已是两种语言异构的缘由。
PHP部分用于处理正常业务请求,Go部分用于处理心跳等其余请求。
在后续的几年内咱们也在反思这个问题。
若是咱们当初作的是采用tcp协议进行传输数据,服务端也用Go,那么咱们还能够作不少“看上去很酷”的事情,例如:咱们能够实现并处处宣扬C10K、C100K,能够处处宣扬实现了十万百万MPS(Message per second),能够将全部的任务结果流式传输到服务端,能够作到同步返回结果,可让用户体验上更好。
可是若是这样作的话,无状态、流量、日志存储等即是要考虑的新问题。这可能会把咱们培养成技术流,可是也可能把咱们的项目变成新的“技术瘤”。
如今回想起当年,更重要的问题在于,咱们肯定当时参与者没有人敢提出这种方案,更没有人能驾驭住这个方案。
维护成本无疑是后期最大的成本。
早期咱们依赖supervisord,在最先期咱们经历过supervisord和docker的supervisord冲突的故障,后期咱们也经历过其余项目也依赖supervisord、由于配置缘由致使其余项目被中止的故障。故障么,本身的锅本身背,也没什么好说的。
可是其余的维护成本是比较多的。
你所能想到的,例如agent的保活,算是比较常见的问题,几十上百个agent总会有一两个出问题,这个咱们也都习觉得常了,甚至本身作一作自动修复也能解决问题。
你所不能想到的,有些人将环境相关的任务也给算到你的头上。有些人会由于“你这个系统怎么在这个环境出现了这个问题”,查了半天,对方端口没有打开。
这点在咱们中间件相关的项目比较常见。一般他们会上来就问这个中间件怎么出这种问题了,实际上呢,让他们把堆栈完整地发出来以后,告诉他们“caused by里写明了,unknown host,就是你的XXX域名没配嘛”。天天都有四五我的问这种问题,也会给答疑方带来很大的压力。
在咱们的其余工做中,也在不断地探索如何减小教育成本,FAQ、培训彷佛收效都不高。
伪装喷人彷佛有效,例如“你XX的是否是又开远程调试了(此处请脑补意大利炮)”,可是这种操做也不能天天都能作的。
如何下降教育成本、让开发者本身拥有本身解决问题的能力,一直是咱们工做的重点。可是目前看起来,咱们在这方面收效甚微。
这个项目咱们也算是顶住了很大压力,没有接新的需求,也没有再去增长新的功能。
咱们当时的理由是“他仅仅是个任务的服务端”,“你只要如此这般写这个任务即可以实现这个功能了”。
可是后续系统变迁是咱们当时所没有考虑的。
后续咱们有了一个新的任务管理界面,有了新的统一登陆接口,CMDB的接口也几经变化。
最终这个系统只剩下API天天不辞辛苦地工做着。
按理来讲没人访问的接口和界面就应该直接下掉。但是至少这也是亲生的bug,我也心软,没办法下手。
根据其余系统的经验,有时候咱们是不得不添加部分功能的,而这部分功能咱们可能会引入不少问题。
举个例子,某些人吐槽为何大公司的代码如此之烂,一个项目中httpclient就有四个版本。
这件事情能够理解,例如早期可能只用了HttpURLConnection进行get请求,中期为了支撑post请求,支持参数,支持超时,分别对HTTPClient了封装,后期由于引入JWTs,又封装了一次。代码冗余度变高,可是既然“系统跑得很好”,也就“没有精简的必要”。
能够理解,可是不表明能够接受。
代码冗余一直是内部项目重构时常见的问题,一般表现为为了避免影响原有代码的执行,把现有的代码拷贝一份,换个名称,修改一下交给新接口来调用。
Java等静态语言合并冗余代码比较简单,编译成功便可保证大部分功能可用。可是php等动态语言咱们则不敢这么作。PHP作不到“编译成功便保证基本没问题”。
就这个例子来看,一方面是开发对HttpClient的认知不足,另外一方面则是开发对代码的抽象能力不够,也未留下适当的接口知足将来的需求,才会出现“一个项目中httpclient就有四个版本”的噩梦。
有些内部系统也会和早期的咱们同样,首先为了作出成果,而后才是追求更高层次。
可是这并非一个作技术的人应该有的态度。
优秀程序员的价值,不在于其所掌握的几招屠龙之术,而是在细节中见真著。
若是咱们能够一次把事情作对,而且作好,在容许的范围内尽量追求卓越,为何不去作呢?
成果是要有的,可是一个作技术的人,应该有对职业的自我尊重、对自我价值的追求和对卓越的理解和渴求。
完美有多远?不知道,可是我之后确定会多走几步。
并发控制其实是个蛋疼的问题,夸张一点说,当时的PHP并不能特别轻松地实现并发,甚至不能实现并发。咱们目前的服务端实际上只是作了任务转发,采用了一些取巧的方法实现并发(curl_multi),可是咱们并不能实现并发控制等功能。至于说多线程(pthreads)和多进程(pcntl)的方案,实测下来也并不稳定,测试阶段便会产生coredump。
而且通过屡次调优,咱们也最终解决了curl_multi的性能问题,能够达到成千上万的并发,而且性能还算能够。
如今复盘一下,若是用的是Go的话,能够很轻松地用5-6行代码增长并发控制。Go语言自身性能不错,并发也很好。
Go语言的功能之一就是自带单元测试。这点和maven差很少,可是Go是少数几个语言层提供测试工具链的语言之一。
相比于动态语言,静态语言的优点之一即是安全。
能够稍微夸张点讲,静态语言一旦编译成功,除非有RuntimeExcetion,否则基本不会出问题。
而PHP这种动态类型的语言,就比较蛋疼了:不只写的时候可能会有问题,不少IDE也没法意识到你究竟是不是写了个bug,甚至过几年回来阅读代码,即使是本身参与过的项目,读起来代码也很蛋疼。PHP也意识到了这一点,从PHP7引入了类型声明,也能缓解这个问题。
用Go语言以前,个人习惯是不写单元测试。用了Go语言以后,我开始养成对全部函数都写单元测试的习惯。
咱们本文中提到了不少次Go语言,实际上语言对项目的影响并不大,真正起主导做用的,仍是人。
运维规范对本项目的影响并不大,主要是开发规范。
后续的工做中,我不止一次·告诫业务开发,咱们目前全部的规范,不管是运维规范、数据库开发规范,或者任何代码开发规范,都是咱们一次一次地踩坑铺出来的路。
若是当初咱们有数据库开发规范的话,表结构也不会这么坑。就像laravel框架同样,咱们按照规范来写,不至于让代码上升一侧层次,可是也不至于让代码烂出水平。
在此咱们强烈对小公司和开发人员推荐《阿里巴巴Java开发手册》,不只有开发规范、还有表结构规范,不管是对开发或是对公司都有好处。
语言对项目的影响并不大,真正起主导做用的,仍是人。
若是人的平均素质并不能达到优秀的话,那么完善的流程和规范将能很大程度上影响一个项目的质量。
教育成本是后期维护的主要成本之一,咱们也一直尝试赋予开发者本身解决问题的能力,虽然很难。
人的素质无疑能直接决定一个项目的质量。
固然,对于普通公司、新人这种平均素质达不到优秀的状况,完善的流程和规范将很大程度上保障一个项目的质量。
静态语言、单元测试等手段是保障项目稳健性的重要方式。