微服务中的常见代码问题

大家好!

最近,在莫斯科的PGConf会议上,一位演讲者演示了一种“微服务”架构,并顺便提及了所有微服务都继承自一个通用基类。 尽管没有实现的任何解释,但似乎该公司对“微服务”一词的理解与经典教给我们的方式不同。 今天,我们将处理一个有趣的问题-微服务中的通用代码是什么,以及它是否可以存在。

什么是微服务? 这是一个独立的应用程序。 不是模块,不是过程,不是简单地单独部署的东西,而是成熟,真实,独立的应用程序。 它具有其自己的主要功能,其在git中的存储库,其测试,其API,其Web服务器,其README文件,其数据库,其版本,其开发人员。

像容器一样,当硬件的计算能力和网络的可靠性达到这样的水平时,微服务就开始被使用:您可以承受比以前长100倍的函数调用,可以承受100倍的内存消耗,可以负担豪华的费用。不仅将每个“祖母”安置在单独的“公寓”中,而且将其安置在单独的“房屋”中。 像任何体系结构解决方案一样,微服务的体系结构再次牺牲了性能,从而为开发人员赢得了代码的可维护性。 但是由于人和他的反应速度保持不变,因此系统继续满足要求。

为什么要拆分成单独的应用程序? 因为我们已经在系统体系结构级别上分配了部分系统复杂性。 一般而言,编程过程是逐步消除大型初始“复杂性”,而分解(分解为类,模块,函数,在我们的情况下为整个应用程序)就是以结构形式实现这种复杂性的一部分。 当我们将系统拆分为微服务时,我们做出了架构决策(成功与否),开发人员在将来实现功能的特定部分时将不再需要该架构。 众所周知,这种特定的微服务负责发送电子邮件,并且已经建立了这个微服务(用于授权),因此我的所有新功能都“落入”了这种模式而无需讨论。

微服务的关键方面是差的连接性。 微服务应独立于“完全”一词。 它们没有通用的数据结构,每个微服务可能/应该具有自己的体系结构,技术,组装方法(等等)。 根据定义。 因为它是一个独立的应用程序。 一种微服务的代码更改不应以任何方式影响其他微服务,除非该API受到影响。 如果我有用Java编写的N个微服务,那么如果由于某种原因突然获利,则不应该有任何限制因素不使用Python编写N + 1个微服务。 它们是松散耦合的,因此开发人员开始使用特定的微服务:

a)非常敏感地监视其API,因为它是从外部可见的唯一组件;
b)完全自由地进行重构;
c)了解微服务的目的(在此我们回想起SRP)并相应地实现新功能;
d)选择最合适的持久性方法;


所有这些都是好的,听起来很合乎逻辑,就像许多意识形态和理论一样(思想理论家在这里结束并去吃晚饭),但是我们正在实践。 无需在martinfowler.com上编写代码。 迟早我们将面临所有微服务的事实:

  • 日志信息;
  • 包含授权;
  • 访问消息代理
  • 返回正确的错误消息;
  • 必须以某种方式了解系统中的一般实体(如果有);
  • 必须使用通用的消息格式(和协议);

并完全一样

在某个时候,思想体系架构师早上上班,发现晚上在系统中出现了一个“图书馆”,这是一个新的存储库,具有许多微服务中使用的通用代码。 建筑师应该被吓到吗?

这要看情况。

为了正确地评估情况,我们应该回到主要思想:微服务是通过(网络)API相互交互的独立应用程序的集合。 在这里,我们看到了架构的主要优点和简单性。 而且我们也不想在任何情况下失去这种优势。 放置在“库”中的通用代码是否会对此造成干扰? 让我们看一些例子。

1.用户类(或其他业务实体)位于库中。

  • 即 一个业务实体不是封装在一个微服务中,而是以不同的方式散布(否则为什么将其放在共享代码库中?);
  • 即 微服务通过该业务实体建立连接;更改与实体合作的逻辑将影响多个微服务;
  • 这很糟糕,非常糟糕,它根本不是微服务,尽管它不是“大泥巴”,但很快,该团队的世界观将导致“大泥巴大球”;
  • 但是系统中的微服务使用相同的概念,并且概念通常是实体,或者仅仅是具有字段的结构,我该怎么办? 阅读DDD,它是关于如何将实体封装在微服务中的,这样它们就不会“飞”过API。

不幸的是,放置在共享库中的任何业务逻辑都会产生这种影响。 通用代码库趋于增长,导致系统中间形成不属于任何特定微服务的逻辑“肿瘤”,并且架构崩溃。 系统的“逻辑重心”开始进入具有通用代码的存储库中,我们得到了单片式服务和微服务的混合地狱,而我们根本不需要去那里。

2.消息格式的解析代码位于库中。

  • 如果所有微服务都是用Java编写的,那么代码很可能是用Java编写的。
  • 如果明天我要使用Python编写服务,则将无法使用解析器,但似乎根本没有问题,我将编写Python版本。
  • 关键点:如果我正在用Java编写新的微服务,是否需要使用此解析器? 是的,可能不是。 也许我没有义务,尽管它作为微服务开发人员可能非常有用。 好吧,好像我在Maven存储库中找到了有用的东西一样。

消息解析器,或改进的记录器,或用于将数据发送到RabbitMQ的包装客户端-有点像助手,辅助组件。 它们与NuGet,Maven或NPM的标准库相当。 微服务开发人员永远是国王;他决定是使用标准库,还是编写自己的新代码,还是使用共享帮助程序库中的代码。 这对他来说将是多么方便,因为他编写了一个独立的应用程序。 特定的助手可以进化吗? 也许他可能会有版本。 让开发人员在其服务中引用特定版本,没有人强迫他更新服务,而在更新助手时,这是谁支持该服务的问题。

3. Java接口,抽象基类,特征。

  • 或“破损代码段”类别中的另一段代码;
  • 即 我在这里,独立而独立,我的肝脏位于其他地方;
  • 这里出现了代码级别的微服务一致性,因此我们不推荐使用;
  • 在最初阶段,这可能不会带来任何实际问题,但是建筑设计的本质是保证未来几年的舒适(或不适)。

开始开发新产品的团队为架构奠定了基础,并且对产品的发展趋势具有最大的影响。 如果最初将SRP,成功分解,低连接性等原则纳入系统,那么它就有机会继续正确开发。 如果不是这样,那么“时间因素”(另一个团队,很少的时间,紧急的补丁程序,缺乏文档)的离心加速将使该系统以比看起来更快的速度被淘汰。

微服务中通用代码的问题仍然很困难,因为它与某种权衡相关联:我们权衡了未来将获得更多收益的因素-微服务的独立程度,代码中的重复次数更少,工程师的资格,系统的简单性等。 每次这些思考和讨论都可能导致不同的特定体系结构决策。 不过,让我们总结一些建议:

建议0:不要将微服务分解为独立存在的部分。 并非每个带有列的表都是一个矩阵,让我们正确使用这些术语。

建议1:非常希望微服务完全没有通用代码。

建议2:如果仍然有通用代码,则将其作为可选“助手”的集合(库)。 服务开发人员决定是使用它们还是编写自己的代码。

建议3:在任何情况下,通用代码中都不应包含业务逻辑。 所有业务逻辑都封装在微服务中。

建议4:让通用代码库设计为标准软件包(NuGet,Maven,NPM等),并提供版本控制(或者更好的是几个单独的软件包)。

建议5:系统的“逻辑重心”应始终保留在微服务本身中,而不是公共代码中。

建议6:如果您打算以微服务的格式编写代码,请事先调和它们之间的代码有时会重复。 在某种程度上,必须抑制我们自然的“干性本能”。

感谢您的关注和成功的微服务。

Source: https://habr.com/ru/post/zh-CN454100/


All Articles