基于 Java 的微服务,特别是那些使用 Spring Boot 的微服务,长期以来因其强大的功能和广泛的社区支持而闻名。Spring Boot 的约定优于配置方法简化了微服务的部署和开发,提供了大量开箱即用的功能,例如自动配置、独立功能和简单的依赖关系管理,使其成为许多人的首选开发工具。该生态系统为构建有弹性和可扩展的服务提供了成熟且有据可查的途径。
然而,尽管有这些实质性的好处,但仍然存在一些痛点,例如较长的启动时间、内存消耗以及管理基于 JVM 的应用程序带来的复杂性,这可能会阻碍云环境中的性能和成本效率。
在维护 Java 生态系统的最后努力中,我们尝试了 Quarkus,希望它优化运行时和减少内存占用的承诺能够解决我们的担忧。。Quarkus 能够将应用程序编译为本机可执行文件,这似乎是希望的灯塔。
然而,现实却是错综复杂的。使我们的应用程序本地编译的过程充满了许多调整和解决方法。更令人沮丧的是原生构建的脆弱性。
与 Quarkus 的本机镜像不完全兼容的单个第三方库可能会破坏整个设置,迫使我们重新将其作为标准 JDK 应用程序运行。性能增益和操作稳定性之间的这种不稳定的平衡凸显了对更精简、更少约束的解决方案的需求。
目标
我们需要一个能够快速构建本机镜像的解决方案:
- 包含 SDK 本身内的所有必要的微服务组件。
- 它需要拥有令人印象深刻的快速启动时间,
- 轻松打包到从头开始生成的 Docker 映像中,并保持轻量级占用空间,容器映像大小和
- RAM 使用量均低于 100 MB。
Golang:简化微服务以提高效率和可靠性
Golang 是一种为现代计算挑战而设计的语言。与 Java 相关的复杂性和开销以及 Quarkus 中本机编译的不稳定性质不同,Golang 提供了一种简单、高效的方法来构建微服务。
-
其编译为本机二进制文件消除了对 JVM 的需求,从而大大减少了内存占用和启动时间。
-
Golang 凭借其强大的并发支持和简单、干净的语法,简化了开发过程并增强了性能。
-
此外,其强大的标准库和简约的本质意味着更少的依赖性和更低的兼容性问题机会。
Golang 不仅解决了前面提到的痛点,还推动应用程序进入更高效率和可靠性的领域。
如何计划迁移
最初,我们遵循领域驱动设计 (DDD) 原则将业务分解为各个子域,旨在为每个子域分配专用的微服务。具体来说,我们专注于为人力资源管理、财务管理和资产管理创建独特的微服务。接下来,我们确定了共享内核微服务来管理多个子域使用的域实体的需求。我们共同使用白板在微服务中描绘了这些域实体,确保了清晰且逻辑的分布。
为了可视化并更好地组织我们的架构,我们制作了一个依赖关系图。该图根据微服务的相互依赖性将微服务分层,其中共享内核形成基础层,而更加孤立的、特定于子域的微服务占据较高层。
我们准备阶段的最后一步涉及为每个微服务编制完整的用例列表。我们根据测试用例和遗留后端中的现有用例精心映射这些内容,确保彻底、无缝地过渡到我们新的、更高效的微服务架构。
如何执行迁移
我们选择了 monorepo 方法,将所有微服务组织为名为"ems-backend"的单个存储库下的单独文件夹。此外,我们制作并开源了一个简单的框架式包。该包有助于实现 Golang 微服务,配有 HTTP 服务器和 Mongo 数据库,同时遵循干净架构的原则。
利用我们的框架,主要任务是将用例开发为系统内命令和查询的函数。然而,我们确实为特定的 HTTP 任务(例如文件上传)编写了自定义处理程序。在我们的设置中,用例是后端的核心方面,协调多个域实体以有效地满足用户需求。
基于我们的成功,我们还使用 Go 开发了一个异常快速的 API 网关。该网关执行符合 OWASP 微服务安全标准的边缘身份验证。每个微服务都配备了自己的中间件,这是一个简单的功能,可以根据 Passport 令牌中嵌入的角色验证传入请求 URL。这确保了服务之间安全、高效的交互。
迁移后我们的成就
我们修改了 Kubernetes 清单来部署应用程序,将 RAM 限制从 500 MB 减少到仅 50 MB。
这次转变非常显着:我们的 32 GB 后端现在可以在 8 GB 集群上顺利运行,仅使用 2 GB RAM。
如果您当前正在管理 RAM 限制为 2 GB 或更多的 JDK 应用程序,那么我们的故事可能会为您带来希望的灯塔。