12 因素应用程序:Java 开发人员的观点

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

十二要素应用程序 是一种用于编写 Web 应用程序的最新方法(和/或宣言),希望它会变得非常流行。尽管我不是 100% 同意这些建议,但我将快速浏览所有 12 个因素并从 Java 生态系统的角度讨论它们,提及绝对“必须”和我不同意的要点。有关更多信息,请访问 12factor.net 网站

  1. 代码库 ——一个代码库,多个部署。这意味着你不能有不同版本的不同代码库。分支机构可以,不同的回购协议则不行。我什至会走得更远,不推荐 Subversion。不是因为它不好,而是因为 git 和 mercurial 做的一样,而且做得更多。您可以像使用 SVN 一样使用 git/mercurial,但反之则不行。 DVCS 的工具(例如 SourceTree)已经相当不错了

  2. 依赖关系 ——显然,您必须在您的清单(例如 pom.xml)中放置尽可能多的依赖关系。宣言建议不要依赖预装软件,例如 ImageMagick 或 Pandoc,但我不会那么严格。如果您的部署是自动化的并且您保证给定工具的存在,您不应该花几天时间试图将它包装在您的工作语言的库中。如果它就像将可执行脚本放入 jar 文件然后解压缩一样简单,那很好。但是如果它需要安装,而且你真的需要它(ImageMagick 确实是一个很好的例子),我认为期望它被安装并没有错。只需检查启动是否存在,如果不存在则快速失败。

  3. 配置 ——这里最重要的规则是——永远不要在源代码仓库中提交你的环境特定配置(最重要的是:密码)。否则,您的生产系统可能容易受到攻击, 这些 wordpress 部署中 可能至少有三分之一(是的,mysql 可能不允许外部连接,但我打赌没有人验证过)。但从那以后,我的看法不同于 12 因素应用程序之一。不,您不应该为您的配置使用环境变量。因为当您有 15 个变量时,如果将它们放在一个文件中,管理它们会变得更加容易。您可以使用一些 shell 脚本来设置它们,但这违背了操作系统的独立性。我认为,拥有一个键值 .properties 文件(Java 对其有本机支持),并且仅将绝对路径作为环境变量(或 JVM 参数)传递到该文件是一种更好的方法。我 以前讨论过 。例如 CONFIG_PATH=/var/conf/app.properties,您在启动时加载它。在您的应用程序中,您可以保留一个空白的 app.example.properties,其中包含要配置的所有属性的列表——数据库凭证、外部系统的密钥和机密等(没有任何值)。这样您就可以将所有属性集中在一个地方,并且很容易发现在给定场景中您可能需要添加/重新配置的内容。如果您使用环境变量,则必须在 txt 文件中包含它们的列表,以便使它们“可发现”,或者让开发人员深入研究代码以找出哪些属性可用。最后但并非最不重要的一点——当我说您不应该将属性文件提交给源代码管理时,有一个非常特殊的例外。您可以选择对环境配置进行版本控制。它必须是一个私人仓库,具有有限的访问权限等等,但是 (Dev)Ops 可以有一个地方来保存每个环境的属性和其他细节,版本化。使用属性文件更容易(使用 env 变量并非不可能,但你又需要一个 shell 脚本)。 12 因素应用程序作者警告环境爆炸。如果每个环境都有一个属性文件,这些文件可能会增长。但他们不必这样做。您可以完全按照管理环境变量的方式更改属性文件中的值。

  4. 支持服务 ——它是关于平等对待您的应用程序所依赖的外部服务,无论您是否管理它们,或者是否由其他方管理它们。从应用程序的角度来看,这应该无关紧要。我可以在这里补充的是,您应该尽量减少这种情况。如果内存队列可以,请不要部署单独的 MQ。如果内存缓存可以,请不要部署 redis 实例。如果嵌入式数据库可以,请不要管理数据库安装(例如,neo4j 提供嵌入式变体)。等等。但是,如果您确实需要功能齐全的外部服务,请将它的路径/凭据设置为可配置的,就好像它是外部的一样(而不是,例如,默认指向本地主机)。

  5. 构建、发布、运行 —— 在页面上 有很好的描述。拥有这样的生命周期真是太好了。但是设置它需要时间和资源。根据您的限制,您可能没有完整的管道,并且某些阶段可能比理想情况更加手动和流畅。有时,例如在初创公司的早期阶段,能够在正在运行的生产服务器上交换类文件或网页可能比经历完整的发布过程(您还没有时间完全自动化)。我知道这听起来像是异端,人们应该努力实现一个完全自动化和分离的过程,但在实现之前,不要完全放弃在生产中手动删除固定文件的选项。只要您不一直这样做,并且您最终不会得到一个您不知道运行哪个版本的代码库的生产环境。

  6. 进程 ——这是关于无状态的,也不依赖于内存或文件系统中存在的任何状态。事实上, 状态不属于代码 。但是,有一点我不同意。打包资产的 12 因素首选方式是在构建期间(例如,将所有 css 文件合并为一个文件)。这有几个缺点——你不能动态地组合资产,例如,如果你有 6 个脚本,在一个页面上你需要 4 个,在另一个页面上你需要 2 个在第一页上使用的脚本,另外 2 个,那么你有预先构建所有这些排列。哪个很好并且有效,但为什么需要它?没有明显的好处。根据您使用的工具,如果您动态生成捆绑包,使用 CDN 可能会更容易。可以提供更多 Java 相关细节的另一件事是“粘性会话”。拥有它们不是一个好主意,但请注意,您可以使用会话将有关用户的数据存储在内存中。您只需配置您的 servlet 容器(或应用程序服务器)来共享该状态。基本上,在引擎盖下它仍然使用分布式缓存,如 memcached 或 ehcache(我猜你也可以使用会话集群的 redis 实现)。它对开发人员来说是透明的,他仍然可以使用会话存储。

  7. 端口绑定 ——这是关于让您的应用程序独立,而不是依赖于您部署的应用程序服务器的运行实例。虽然这看起来更容易管理,但事实并非如此。启动 servlet 容器和推送部署同样简单。但是为了让您的应用程序绑定到端口,您需要拥有相应的工具。他们提到了jetty,还有一个嵌入式版本的tomcat,和spring-boot(两者都包装)。虽然我不反对端口绑定,但我认为反过来也一样好。容器配置同样容易完成,无论您是删除特定于环境的 xml 文件,还是以编程方式执行并从第 3 点中提到的文件加载属性。关键是——没关系——做哪个更容易你。更不用说您可能需要一些 apache/nginx 功能。

  8. 并发 ——它是关于使用本地进程。我认为,这与 Java 运行时不太相关,它在后台使用线程并隐藏了 unix 进程。顺便说一下,另一个对 unix 的明确引用(而不是保持操作系统独立)。

  9. 一次性 ——这就是拥抱失败。即使一个或多个应用程序实例死亡,您的系统也必须正常工作。这必然会发生,尤其是 “在云中” 。他们提到了 SIGTERM,这是一个特定于 *nix 的信号,而 12 因素应用程序的总体思路是独立于操作系统。明显倾向于 Linux,但这很好。

  10. Dev/prod parity—— 你的开发环境应该与生产环境几乎相同(例如,为了避免一些“在我的机器上工作”的问题)。不过,这并不意味着您的操作系统必须是在生产环境中运行的操作系统。例如,您可以运行 Windows,并让您的数据库、MQ 等在本地虚拟机上运行( 如我的设置 )。这也强调了您的应用程序的操作系统独立性。请记住保持版本相同。

  11. 日志 ——12-factor 应用程序建议将所有日志信息写入系统。 Java 开发人员当然会不同意。使用像 loggack/slf4j 这样的工具,您可以在应用程序中管理日志记录方面,而不是依赖 3rd 方工具来做到这一点。例如,日志轮换和清理,或 发送到集中式日志记录设施 。配置一个 graylog 或 splunk 适配器比让另一个进程从系统中收集并推送它要容易得多。可以有特定于环境的日志配置,这也是与 app.properties 捆绑在一起的一个文件)。如果这看起来很复杂,请考虑设置任何要捕获输出的复杂性。

  12. 管理流程 ——普遍同意,但除此之外,我认为在部署或启动时执行迁移比手动执行迁移更可取,并且最好通过 capistrano 之类的工具在生产环境中手动更改“内容”,以确保它是正确的在所有实例上都相同。

总的来说,考虑到上述评论,这是我推荐的一套很好的建议和构建应用程序的方法。