Java实践团队负责人Zurab Bely讲述了他在一家大公司的项目中工作的故事,并分享了他的经验。
我如何定居...
我于2017年夏末作为普通开发人员加入该项目。 我不能说当时我非常喜欢它:项目中使用的技术过时,团队内部的沟通被最小化,与客户的沟通既困难又无济于事。 所以这个项目遇见了我。 那时,我只有一个愿望:快速摆脱困境。
我将介绍整个项目。 这是一家大公司的官方门户网站,提供一般信息,新闻,促销和其他内容。 所有市场新闻通讯都包含指向该网站某些页面的链接,即负载是稳定的平均负载,但在某些时间点它可以达到很高的价值。 Web应用程序的稳定性和可访问性需要特别注意-服务中断的每一分钟都会给客户带来巨大损失。
在风中quin着眼睛的棚户区
首先,我主要研究了该项目的技术条件,修复了一些小错误,并做了一些小的改进。 从技术角度来看,该应用程序看起来很糟糕:围绕过时的dotCMS商业版本构建的整体式体系结构,用Java 6th版本编写的代码,此时Velocity框架上第九个服务器端客户端部分的渲染,那时这还不是几年前。得到支持。 每个实例都是在JBoss AS中启动的,并使用Nginx进行路由。 内存泄漏导致节点不断重启,缺少常规缓存导致服务器负载增加。 但是最大的分裂是对CMS代码的更改。 他们排除了无痛升级到最新版本的可能性。 一个很好的例子就是从3.2版到3.7版的过渡,当时该过渡才刚刚结束。 过渡到下一个次要版本花费了一年多的时间。 毫无疑问,Spring框架,React.js,微服务体系结构,Docker等流行的解决方案都不成问题。 深入到项目中,这种技术条件的后果变得显而易见。 其中最严重的是无法在本地运行应用程序进行开发和调试。 整个团队由8个人组成,在一个开发台上工作,在那里部署了该应用程序的生产版本的副本。 因此,只有一个开发人员可以同时调试其代码,滚动更新的代码会阻塞整个团队。 最高点是一次失败的销售,在此期间,数以百万计的信件,SMS和推送通知通过数以十计的渠道发送给了不同的用户-数以万计的会话同时打开。 服务器无法忍受它,并且大多数时候门户不可用。 该应用程序无法很好地扩展。 只有一种方法:并排部署另一个副本,并使用Nginx平衡它们之间的负载。 每次交付生产代码都涉及大量的手工工作,并且花费了几个小时。
在我参与该项目的六个月后,当局势已经开始失控时,就决定从根本上改变局势。 过渡过程已经开始。 更改影响了所有领域:团队组成,工作流程,应用程序的体系结构和技术组件。
我们建造,建造...
首先,人员发生了变化。 由数名开发人员代替,他们使我成为团队领导。 向现代解决方案的过渡始于团队经验丰富的人员的参与。
程序更改更具全局性。 到那时,敏捷+ Scrum方法已经开始进行开发,为期两周的sprint,每次迭代结束时都会交付。 但是实际上,这不仅没有增加工作速度,反而减慢了工作速度。 每日集会持续了一个半到两个小时,但没有产生任何结果。 规划和修饰变成了争执,宣誓或简单的交流。 与此有关。 最初很难以这种方式进行任何更改-代表客户,团队几乎失去了信心,尤其是在销售不成功之后。 每个更改都必须经过长时间的证实,讨论和证明。 奇怪的是,但是主动权来自客户。 在他们方面,scrum-master参与了控制方法和方法学的正确应用,调试过程并使团队工作的工作。 尽管他只被几个冲刺所吸引,但这有助于我们正确地组装基础。 与客户沟通的方式发生了很大变化。 我们开始更频繁地讨论流程中的问题,回顾开始更有效地进行,开发人员更愿意提供反馈,并且就客户而言,客户前进并以各种方式支持过渡过程。
但是,老实说,我要老实地说:在很多时候,团队中的一些变更是“盲目”进行的,并且在取得积极成果之后,这已经报告给了客户。 六个月以来,态度已转变为舒适的工作沟通。 随后进行了几次团队建设,整个开发团队与客户团队(营销人员,分析师,设计师,产品所有者,内容经理等)的一天和两天的会议,以及对酒吧的联合访问。 一年之后,直到今天,沟通可以被称为友好的。 气氛变得友好,轻松和舒适。 当然,这并非没有冲突,但即使在最幸福的家庭中,有时也会发生争吵。
在此期间,应用程序代码,其体系结构和使用的解决方案中也发生了不少有趣的变化。 如果您不精通技术,则可以安全地跳过全文以得出结论。 如果您像我一样幸运,欢迎您! 整个过渡可以分为几个阶段。 关于每个更详细。
阶段1.确定关键问题区域。一切都尽可能简单明了。 首先,有必要摆脱对第三方商业产品的依赖,切割整体,并使其在本地调试成为可能。 我想将客户端和服务器代码分开,以在体系结构和物理上进行分发。 另一个问题是资格。 该项目完全没有任何自动测试。 这使过渡有些困难,因为必须手动检查所有内容。 鉴于从来没有针对该功能的技术任务(此处会影响项目的细节),因此很可能会丢失某些内容。 绘制了问题区域后,我们再次查看了列表。 它看起来像一个计划。 是时候建造摩天大楼了!
阶段2。更新代码库。运行时间最长的阶段。 这一切都始于向服务架构的过渡(不要与微服务相混淆)。 这个想法是这样的:将应用程序分成几个单独的服务,每个服务将处理其特定任务。 服务本来不是“微型”的,但我也不想将所有东西都放在一个锅炉中。 每个服务都应该是用Java SE 8编写并在Tomcat上运行的Spring Boot应用程序。
首先是所谓的。 “内容服务”已成为未来应用程序和CMS之间的一层。 它已成为内容获取方式的一种抽象。 假定我们以前直接在CMS中直接发出的所有请求都将通过此服务执行,但已经使用了HTTP协议。 这样的解决方案使我们能够减少连接性,并有可能随后用更现代的模拟产品代替dotCMS,甚至消除了对商业产品的使用,并编写了针对我们的任务量身定制的解决方案(展望未来,我会说这就是我们的方法)。
立即为前端代码和后端代码的分离奠定了基础。 他们创建了一个前端服务,该前端服务负责分发在React上编写的代码。 我们拧紧了npm,配置了节点并调试了程序集-一切都按照客户端部件的现代趋势进行。
通常,功能是根据以下算法分配给服务的:
- 使用所有必需的依赖项和设置创建了一个新的Spring Boot应用程序;
- 移植了所有基本逻辑(通常是从头开始编写,指的是旧代码,只是为了确保您不会忘记任何细微差别),例如,对于缓存服务,这些选项可以添加到缓存,从中读取并禁用它;
- 所有新功能始终都是使用新服务编写的;
- 逐步将应用程序的旧部分重写为新服务,以重要性顺序显示。
一开始,我们有几个:
- 内容服务。 充当应用程序和CMS之间的一层。
- 缓存服务。 Spring Cache上的简单存储库。
- AA服务。 一开始,他只从事有关授权用户的信息分发。 其余的保留在dotCMS中。
- 前台服务。 负责分发客户端代码。
第三阶段。自动测试。考虑到项目的所有经验,我们认为功能测试的存在极大地简化了应用程序的寿命并可能进一步进行更新。 现在是将它们引入项目的时候了。 可悲的是,代码的单元测试几乎立即停止。 他们花了很多时间来编写和支持,而我们却花了很少的时间,因为除了重写代码之外,关于新功能的当前任务还悬而未决,并且经常出现错误。 决定只专注于使用Selenium测试应用程序接口。 一方面,这使我们更容易在交付生产之前执行回归测试,另一方面,可以在服务器端进行重构,监视客户端的状态。 团队没有自动化器,编写和维护自动化测试的相关性需要额外的费用。 他们没有再培训一名测试人员,而是将另一个人添加到了团队中。
阶段4.部署自动化。现在我们有了单独的服务,当前端与后端分离时,当主要功能开始被自测所覆盖时,是时候打开一罐啤酒并自动化在演示和生产服务器上本地部署和支持应用程序的所有手动工作了。 将整体切割成碎片并使用Spring Boot为我们打开了新的视野。
开发人员能够在本地调试代码,仅运行此功能所需的那部分功能。 最终,测试台架开始用于其预定用途-已经或多或少地调试了代码,准备进行初始和资格测试。 但是仍然有很多手工作品浪费了我们宝贵的时间和精力。 在研究了问题并找到了解决方案之后,我们选择了Gradle + TeamCity。 由于该项目是由Gradle构建的,因此添加新内容没有任何意义,而且所编写的脚本都与平台无关,因此可以在任何OS上远程或本地运行。 这样,不仅可以使用CI / CD的任何解决方案,还可以轻松地将平台更改为其他平台。 之所以选择TeamCity,是因为其强大的内置功能,庞大的社区以及适用于所有场合的一长串插件,以及与Intellij IDEA开发环境的集成。
目前,CI系统中有100多个优化脚本和300多个任务以不同的参数运行它们。 这不仅是测试台的部署和交付到生产中,而且还与日志,服务器管理,路由以及用于相同类型常规任务的解决方案一起使用。 有些任务已经从我们的肩上卸下了。 内容管理员可以自己刷新缓存。 来自技术支持部门的人员有机会自己动手提供个别服务,以执行主要的复苏行动。 开发人员的睡眠变得越来越沉稳。
阶段5。拥有CMS。在可以从商业CMS中提取数据之后,就可以从另一个来源接收数据,而无需重写应用程序代码。 内容服务决定了从何处获取该数据或该数据。 在寻找现成的解决方案并进行分析之后,我们决定编写自己的内容管理系统,因为它们都无法完全满足我们的需求。 编写自己的CMS是一个无休止的过程,不断出现新的需求和愿望。 我们选择了一些基本功能,然后进入了美丽的开发世界。 为了发布产品的第一个版本,我们花了一个半月的时间。 随着新CMS中功能的就绪,我们将内容从旧的CMS转移到新的CMS。 所有新页面都不再与dotCMS有关。 随着时间的流逝,这使得放弃付费版本并切换到社区版本成为可能,并且将来完全放弃第三方。
阶段6。校对。卷起裤子,我们开始了进入时髦编程世界的旅程。 我项目的这个阶段是重组过程的最后阶段。 一直持续到今天。 此阶段通常出现的主要区域是缩放。 Docker + Kubernetes + Consul捆绑包不仅使您可以方便地部署到不同的服务器并分别管理每个服务,而且还可以灵活地扩展整个应用程序,仅在需要的地方进行此操作,并且只在需要的时间进行。 更详细地讲,只有在生产中完全切换到此解决方案时,我才能绘画。
...最后建成。 万岁!
自应用程序更新开始以来已经过去了一年。 这些现在是用Spring Boot编写的26种REST服务。 每个工具都有Swagger UI中的详细API文档。 客户端是用React.js编写的,并与服务器端分开。 门户网站的所有主页均包含测试。 进行了几次重大销售。 向现代技术的过渡,摆脱旧代码和服务器的稳定运行,极大地激励了开发人员。 从“如我们所说,我们做到了”,该项目进入了主流,每个人都对成功感兴趣,并为改进和优化提供了自己的选择。 客户对团队的态度发生了变化,营造了友好的氛围。
这是整个团队,每个开发人员和测试人员,经理和客户的工作成果。 这非常艰难,紧张,有时甚至犯规。 但是团队的凝聚力,有能力的计划和对结果的了解使克服所有困难成为可能。