我们有一条数据处理管道,可在其中接收来自不同来源的数据。整个管道是通过使用事件驱动的体系结构和微服务来实现的。服务之一具有三个单独的任务。它们中的两个在不同数据源之间是完全相同的,但是第三个任务范围可能会根据我们的数据源是什么而略有变化。例如,对于一个源,可以基于文件1和文件2计算唯一签名,对于另一个源,可以根据字段2和3计算唯一签名。将该服务实现为与微服务粒度原则保持一致的最佳方式是什么?
我想到了三种方法:
1)创建一个服务,并对每个源使用不同的实现(例如工厂设计模式)。根据来源,将在运行时使用其中一种实现。
优点:服务数量少。复杂度降低
缺点:由于该服务将在所有数据源之间共享,因此,通过添加任何新的数据源,应重新部署此服务,这将在该服务与负责从源收集数据的任何服务之间创建隐式依赖关系
2)将该服务分为两个服务,对所有源使用一个,并为每个数据源重新实现提取的服务。
优点:收集器与这两个服务之间没有依赖关系。通过添加新来源,需要实施新服务,并且不需要重新部署与其他来源相关的服务。
缺点:更多的服务,由于服务太小,我们将来可能会面临纳米服务的问题。
3)不要更改服务的粒度,而是在运行时创建服务的不同实例。每个都有不同的配置,以指定用于每个源的字段集。在这种情况下,代码是共享的,但是运行时实例根据它属于哪个源而有所不同。
优点:服务数量少,没有操作依赖性
缺点:将逻辑的复杂性转移到运行时,这可能会使部署和操作更具挑战性
答案 0 :(得分:0)
这取决于。仅供参考,我知道Kafka,但没有经验。
对于选项3 :您使用不同的配置管理解决方案的各种实例的能力有多成熟?会有多少个实例?您将如何观察所有实例的运行状况,行为和性能?
此选项意味着开发不那么复杂,但操作方面却更复杂:您只有一个代码库要测试,但是要测试许多不同的配置排列。
对于选项1 :在开发方面会更加复杂,并且在操作方面仍然会有些复杂。这样做的原因将取决于如何设置解决方案以识别在运行时使用哪种实现。 IOC方法可以使用,但是仍然需要对其进行配置。
对于这两种方法,您都可以使用正确的配置来设置实例并对其进行测试,这很好。
关于系统更改:理想情况下,微服务应该易于更换。确保服务之间的划分是干净的,以便您理想地替换解决方案的一部分而不破坏另一部分。在将这样的变更部署到生产中之前,它还应该易于测试-既要单独测试服务/模块,又要与解决方案的其余部分以及相关配置进行集成测试。如果您认为一种方法比另一种更适合于此,那么这就是我可能会采用的方法。
更新-选项2
这意味着拥有多个服务来处理某些请求(但不能处理其他请求),因此现在您必须管理服务之间的流(在运行时)以及它们之间的相互依赖性(在开发,部署时);很难测试。
微服务部分是关于具有独立的服务-选项2并不是真正符合精神的行列-可以,只是要意识到它并不是微服务,这取决于您对这些事情的特殊程度。
答案 1 :(得分:0)
我同意阿德里安,这取决于您的情况。
我认为最重要的是系统复杂性-它在系统的测试,支持和发展中起着至关重要的作用。 (吻)
所以我认为最好的选择是2。
当然,您还应该记住其他设计原则-创建用于重用的库,等等,但是无论如何,您的源代码设计将不是树,没有不受管理的依赖关系。
答案 2 :(得分:0)
我想我最会担心服务产生的结果类型。 如果(对于所有3个选项)处理的结果是相同的类型(或域对象),那么我肯定会赞成选项1 。
这与Sam Newman在book中写道的“松散耦合,内聚行为”原则相符。
松散耦合,因为消费者不必关心从哪个服务调用/接收事件,因此不必知道来源。
内聚行为,因为单个服务将业务逻辑应用于事件,并因此产生具有一致类型/语义的可靠内聚事件。
对于不同的来源,我想说让单个服务使用它们是可以的。在服务实现内部有很多以可维护的方式设计它的可能性。源应该选择什么任务是应该对使用者隐藏的实现细节。
缺点:由于该服务将在所有数据源之间共享,因此,通过添加任何新的数据源,应重新部署此服务,这将在该服务与负责从源收集数据的任何服务之间创建隐式依赖关系
此服务确实将依赖生产者,因为它需要它作为源。没有办法避免这种情况。对于所有其他选项在一定程度上也是如此。但是依赖性是消费者取决于生产者,而不是相反!