《Java微服务:实用指南》的 翻译和改编。指南的前几部分:
让我们从抽象的事物开始,到具体的库结束,看看 Java 中微服务的固有问题。
如何使 Java 微服务具有弹性?
回想一下,当您创建微服务时,您实际上是将 JVM 方法调用替换为同步 HTTP 调用或异步消息传递。虽然方法调用基本上可以保证完成(除非 JVM 意外关闭),但默认情况下网络调用是不可靠的。它可能有效,但也可能因各种原因而无效:网络超载、实施了新的防火墙规则等等。要了解这有何不同,让我们看一下 BillingService 示例。HTTP/REST 弹性模式
假设客户可以在您公司的网站上购买电子书。为此,您刚刚实现了一个计费微服务,它可以调用您的在线商店来生成实际的 PDF 发票。现在,我们将通过 HTTP 同步调用此服务(尽管异步调用此服务更有意义,因为从用户的角度来看,PDF 生成不必是即时的。我们将在下一个示例中使用相同的示例)部分并查看差异)。@Service
class BillingService {
@Autowired
private HttpClient client;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
httpClient.send(invoiceRequest(user.getEmail(), invoice), responseHandler());
// ...
}
}
总而言之,以下是此 HTTP 调用的三种可能结果。
- OK:通话接通,账户创建成功。
- DELAY:呼叫已接通,但完成时间太长。
- 错误。呼叫失败,您可能发送了不兼容的请求,或者系统可能无法工作。
消息传递弹性模式
让我们仔细看看异步通信。我们的 BillingService 程序现在可能看起来像这样,假设我们使用 Spring 和 RabbitMQ 进行消息传递。为了创建帐户,我们现在向 RabbitMQ 消息代理发送一条消息,其中有几个工作人员正在等待新消息。这些工作人员创建 PDF 发票并将其发送给适当的用户。@Service
class BillingService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
// преобразует счет, например, в json и использует его How тело messages
rabbitTemplate.convertAndSend(exchange, routingkey, invoice);
// ...
}
}
潜在的错误现在看起来有点不同,因为您不再像使用同步 HTTP 连接那样立即收到 OK 或 ERROR 响应。相反,我们可能会遇到三种可能出错的情况,这可能会引发以下问题:
- 我的消息是否已被员工传递和使用?还是丢失了?(用户不会收到发票)。
- 我的消息只发送一次吗?或者多次交付但仅处理一次?(用户将收到多张发票)。
- 配置:从“我是否使用了正确的路由键/名称进行交换”到“我的消息代理是否已正确配置和维护,或者其队列是否已满?” (用户不会收到发票)。
- 如果您使用 ActiveMQ 等 JMS 实现,则可以用速度换取两阶段 (XA) 提交的保证。
- 如果您使用 RabbitMQ,请先阅读本教程,然后仔细考虑一般的确认、容错和消息可靠性。
- 也许有人精通配置 Active 或 RabbitMQ 服务器,尤其是与集群和 Docker 结合使用(有人吗?;))
哪个框架是 Java 微服务的最佳解决方案?
一方面,您可以安装非常流行的选项,例如Spring Boot。它使创建 .jar 文件变得非常容易,附带 Tomcat 或 Jetty 等内置 Web 服务器,并且可以在任何地方快速运行。非常适合构建微服务应用程序。最近,出现了一对专门的微服务框架,Kubernetes或GraalVM,部分受到反应式编程的启发。这里有一些更有趣的竞争者:Quarkus、Micronaut、Vert.x、Helidon。最后,您必须自己选择,但我们可以给您一些可能不完全标准的建议:除了 Spring Boot 之外,所有微服务框架通常都被宣传为速度快得令人难以置信,几乎可以瞬时启动,低内存占用,并且能够扩展到无限。营销材料通常具有令人印象深刻的图形,展示了庞然大物 Spring Boot 旁边或彼此之间的平台。从理论上讲,这可以减轻支持遗留项目的开发人员的紧张,这些项目有时需要几分钟才能加载。或者在云中工作的开发人员希望在 50 毫秒内启动/停止当前需要的尽可能多的微容器。 然而,问题是这些(人为的)裸机启动时间和重新部署时间几乎对项目的整体成功没有贡献。至少它们的影响远小于强大的框架基础设施、强大的文档、社区和强大的开发人员技能。所以最好这样看:如果到目前为止:- 您让 ORM 大量运行,为简单的工作流程生成数百个查询。
- 您需要无尽的千兆字节来运行中等复杂的整体应用程序。
- 您有如此多的代码并且复杂性如此之高(我们现在不是在谈论像 Hibernate 这样的潜在缓慢启动器),以至于您的应用程序需要几分钟才能加载。
哪些库最适合同步 Java REST 调用?
在低级技术方面,您可能最终会得到以下 HTTP 客户端库之一:Java 的本机 HttpClient(自 Java 11 起)、Apache 的 HttpClient或OkHttp。请注意,我在这里说“可能”是因为还有其他选项,从老式的JAX-RS 客户端到现代的WebSocket客户端。无论如何,趋势是生成一个 HTTP 客户端,而不是自己摆弄 HTTP 调用。为此,您需要查看OpenFeign项目及其文档,作为进一步阅读的起点。异步 Java 消息传递的最佳代理是什么?
您很可能会遇到流行的ActiveMQ(经典或 Artemis)、RabbitMQ或Kafka。- ActiveMQ 和 RabbitMQ 是传统的、成熟的消息代理。它们涉及“聪明的经纪人”和“愚蠢的用户”的互动。
- 从历史上看,ActiveMQ 具有易于内联(用于测试)的优点,可以通过 RabbitMQ/Docker/TestContainer 设置来缓解。
- Kafka 不能被称为传统的“智能”经纪人。相反,它是一个相对“愚蠢”的消息存储(日志文件),需要聪明的消费者来处理。
我可以使用哪些库来测试微服务?
这取决于你的堆栈。如果您部署了 Spring 生态系统,那么使用该框架的特定工具将是明智的选择。如果 JavaEE 类似于Arquillian。Docker 和非常好的Testcontainers库可能值得一看,它特别有助于轻松快速地设置 Oracle 数据库以进行本地开发或集成测试。要模拟测试整个 HTTP 服务器,请查看Wiremock。要测试异步消息传递,请尝试实现 ActiveMQ 或 RabbitMQ,然后使用Awaitility DSL编写测试。此外,还使用了所有常用工具 - Junit、TestNG for AssertJ和Mockito。请注意,这不是完整列表。如果您在这里没有找到您最喜欢的工具,请在评论部分发布。如何为所有 Java 微服务启用日志记录?
微服务中的日志记录是一个有趣且相当复杂的主题。您现在拥有 n 个日志文件,而不是可以使用 less 或 grep 命令操作的一个日志文件,并且您希望它们不要太分散。本文(英文)很好地描述了日志生态系统的特征。请务必阅读它,并注意从微服务角度进行集中日志记录部分。在实践中,您会遇到不同的方法:系统管理员编写某些脚本,收集来自不同服务器的日志文件并将其合并为一个日志文件,并将它们放在 FTP 服务器上以供下载。在并行 SSH 会话中运行 cat/grep/unig/sort 组合。这正是 Amazon AWS 所做的,您可以让您的经理知道。使用Graylog或ELK Stack(Elasticsearch、Logstash、Kibana)等工具我的微服务如何找到彼此?
到目前为止,我们假设我们的微服务彼此了解并且知道相应的 IPS。我们来谈谈静态配置。因此,我们的银行整体 [ip = 192.168.200.1] 知道它需要与风险服务器 [ip = 192.168.200.2] 通信,该服务器硬编码在属性文件中。但是,您可以使事情变得更加动态:- 使用基于云的配置服务器,所有微服务都可以从中提取其配置,而不是在其微服务上部署 application.properties 文件。
- 由于您的服务实例可以动态更改其位置,因此值得查看了解您的服务所在位置、它们的 IP 以及如何路由它们的服务。
- 既然一切都是动态的,就会出现新的问题,例如自动选举领导者:谁是执行某些任务的主人,以免重复处理这些任务?当领导者失败时谁来取代他?更换的依据是什么?
如何使用Java微服务组织授权和认证?
这个话题也值得单独讲一个故事。同样,选项范围从使用自定义安全框架的硬编码基本 HTTPS 身份验证到使用自己的授权服务器运行 Oauth2 安装。如何确保我的所有环境看起来都一样?
对于没有微服务的部署来说是这样,对于有微服务的部署来说也是如此。尝试结合使用 Docker/Testcontainers 和 Scripting/Ansible。没有问题:简单介绍一下 YAML
让我们暂时远离库和相关问题,快速浏览一下 Yaml。该文件格式实际上用作“将配置编写为代码”的格式。Ansible 等简单工具和 Kubernetes 等巨头也使用它。要体验 YAML 缩进的痛苦,请尝试编写一个简单的 Ansible 文件,并查看需要对该文件进行多少编辑才能使其按预期工作。尽管所有主要 IDE 都支持该格式!之后,再回来阅读本指南。Yaml:
- is:
- so
- great
那么分布式事务呢?性能测试?其他主题?
也许有一天,在手册的未来版本中。现在,仅此而已。和我们在一起!微服务的概念问题
除了 Java 中微服务的具体问题之外,任何微服务项目中都会出现其他问题。它们主要与组织、团队和管理有关。前端和后端不匹配
前端和后端不匹配是许多微服务项目中非常常见的问题。这是什么意思?只是在古老的巨石中,Web 界面开发人员有一个特定的来源来获取数据。在微服务项目中,前端开发人员突然有n个来源可以获取数据。想象一下,您正在使用 Java 创建某种 IoT(物联网)微服务项目。假设您管理整个欧洲的大地测量机器和工业炉。这些烤箱会定期向您发送温度等最新信息。迟早您可能想在管理 UI 中找到烤箱,也许使用“熔炉搜索”微服务。根据后端对应方应用领域驱动设计或微服务法的严格程度,“查找烤箱”微服务可能只返回烤箱 ID,而不返回其他数据,例如类型、型号或位置。为此,前端开发人员需要使用从第一个微服务收到的 ID 在“获取熔炉数据”微服务中进行一个或 n 个额外调用(取决于分页实现)。 虽然这只是一个简单的例子,虽然取自一个真实的(!)项目,但它也说明了以下问题:超市已经变得非常受欢迎。那是因为有了它们,你不必去 10 个不同的地方购买蔬菜、柠檬水、冷冻披萨和卫生纸。相反,您可以前往一个地方。这样更容易、更快捷。对于前端和微服务开发人员来说也是如此。管理层期望
管理层错误地认为,他们现在需要为一个(总体)项目雇用无限数量的开发人员,因为开发人员现在可以完全独立地彼此工作,每个人都在自己的微服务上工作。最后(发布前不久)只需要进行少量集成工作。 事实上,这种做法是有很大问题的。在下面的段落中,我们将尝试解释原因。“较小的部分”并不等于“更好的部分”
如果认为分成 20 个部分的代码必然比整个代码的质量更高,那将是一个很大的错误。即使我们从纯粹的技术角度来考虑质量,我们的各个服务仍然可能运行 400 个 Hibernate 查询来从数据库中选择用户,遍历不支持的代码层。我们再次回到西蒙·布朗的名言:如果你不能正确构建单体,那么就很难构建正确的微服务。谈论微服务项目中的容错能力往往已经为时已晚。以至于有时看到微服务在实际项目中如何工作会让人感到害怕。其原因是 Java 开发人员并不总是准备好在适当的级别上研究容错、网络和其他相关主题。“部件”本身较小,但“技术部件”较大。想象一下,您的微服务团队被要求编写一个用于登录数据库系统的技术微服务,如下所示:@Controller
class LoginController {
// ...
@PostMapping("/login")
public boolean login(String username, String password) {
User user = userDao.findByUserName(username);
if (user == null) {
// обработка варианта с несуществующим пользователем
return false;
}
if (!user.getPassword().equals(hashed(password))) {
// обработка неверного пароля
return false;
}
// 'Ю-ху, залогинorсь!';
// установите cookies, делайте, что угодно
return true;
}
}
现在你的团队可能会决定(甚至可能说服业务人员)这一切都太简单和无聊了,与其编写一个登录服务,不如编写一个真正有用的 UserStateChanged 微服务,而不需要任何实际和有形的业务需求。由于目前有些人将 Java 视为恐龙,因此让我们用时尚的 Erlang 编写 UserStateChanged 微服务。让我们尝试在某个地方使用红黑树,因为 Steve Yegge 写道,你必须彻底了解它们才能申请 Google。从集成、维护和整体设计的角度来看,这就像在单个整体中编写意大利面条式代码层一样糟糕。一个人为的普通例子?这是真实的。然而,这在现实中是可能发生的。
更少的碎片 - 更少的理解
那么自然会出现关于理解整个系统、其流程和工作流程的问题,但与此同时,作为开发人员,您只负责处理独立的微服务[95:login-101:updateUserProfile]。它与上一段一致,但根据您的组织、信任和沟通水平,如果微服务链中出现意外故障,这可能会导致很多混乱、耸肩和指责。没有人会对所发生的事情承担全部责任。这根本不是不诚实的问题。事实上,连接不同的部分并理解它们在项目整体图中的位置是非常困难的。通讯与服务
沟通和服务的水平很大程度上取决于公司的规模。然而,一般关系是显而易见的:越多,问题就越多。- 谁运行微服务#47?
- 他们是否刚刚部署了一个新的、不兼容的微服务版本?这是在哪里记录的?
- 我需要与谁联系来请求新功能?
- 在唯一懂得这种语言的人离开公司之后,谁将支持 Erlang 中的微服务?
- 我们所有的微服务团队不仅使用不同的编程语言,而且还处于不同的时区!我们如何正确协调这一切?
GO TO FULL VERSION