我们如何成功实现微服务迁移?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Picnic前一段时间进行了微服务迁移。迁移与技术团队的成长几乎同时发生,以前的单一技术团队被分成了专门的产品团队。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事实上,在我们的案例中有以下三方面改变:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"员工被重新组织到不同产品团队中"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代码被分割到不同的 git 仓库中"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"应用部署的粒度更细"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"迁移后一开始,每个产品团队维护大约一个微服务。但公司一直在成长,随着成长业务范围也越来越广。因此,我们开始发现更多的业务分解机会。我们自己的Jakob"},{"type":"link","attrs":{"href":"https:\/\/blog.picnic.nl\/from-monolith-to-microservices-9b2ef44401e3","title":"","type":null},"content":[{"type":"text","text":"曾经写过涉及到的原则"}]},{"type":"text","text":",但是这个过程看起来是什么样的呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"定义用例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们的研究案例是Picnic的"},{"type":"link","attrs":{"href":"https:\/\/blog.picnic.nl\/react-native-at-picnic-eff17e8c51d5","title":"","type":null},"content":[{"type":"text","text":"Runner应用"}]},{"type":"text","text":",我们的驾驶员在旅途中使用它来接收导航指令,管理从客户那里取到的可回收物品,此外它为我们的客户提供ETA等附加功能。应用程序开发工作始于2018年,自此以后,“附加功能”部分显著增长,其中一个值得注意的附加功能是一项"},{"type":"link","attrs":{"href":"https:\/\/blog.picnic.nl\/programming-a-safer-drive-a41550575c8b","title":"","type":null},"content":[{"type":"text","text":"安全功能"}]},{"type":"text","text":",我们从我们的Runners(送货车辆驾驶员)那里收集GPS和加速度数据,并据此为他们提供驾驶反馈。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过审视Runner应用程序的后端架构,我们意识到我们原本简洁的服务现在有了另外一个功能。一方面,我们实现了最初的功能,即"},{"type":"text","marks":[{"type":"italic"}],"text":"旅途状态"},{"type":"text","text":"管理和用户交互管理。另一方面,我们围绕各种传感器数据开发了一些重要的功能,这些功能统称为“遥测”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"应用程序有两个不同部分,并不意味着我们必须引入一个新的服务,事实上我们有很多理由不采用微服务。事实上,"},{"type":"link","attrs":{"href":"https:\/\/blog.picnic.nl\/cultivating-microservices-b28403be8138","title":"","type":null},"content":[{"type":"text","text":"在迁移过程中,我们花了一段"}]},{"type":"link","attrs":{"href":"https:\/\/blog.picnic.nl\/cultivating-microservices-b28403be8138","title":"","type":null},"content":[{"type":"text","text":"时间准备,然后才能正常开始工作。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"尽管如此,我们还是编写了一个内部RFC。在这个RFC中,我们详细描述了计划,考虑了各种选择,并得出了一个结论:我们需要另一个单独的服务。让我们一起来看看我们的理由。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"行为特征"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"应用程序两个部分的行为方式并不相同。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"管理行程状态是一个相对繁重的事务性过程。向后端发出的请求相对较少,因为我们主要是在追踪驾驶员执行的具体操作。每个请求都极其重要,必须对其完整性进行验证——失败对客户和驾驶员都意味着业务被立即中断。在这里,我们大量使用我们的"},{"type":"link","attrs":{"href":"https:\/\/blog.picnic.nl\/open-sourcing-jolo-87cea6079108","title":"","type":null},"content":[{"type":"text","text":"开源Jolo库"}]},{"type":"text","text":"来实例化对象图,这意味着我们有很多内存操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与此同时,处理实时传感器数据是完全不同的。它们会导致较为频繁的请求,但每个请求的处理都是短暂的且不太可能被单独跟踪,涉及的数据类型虽然很多,但也相对简单。在这里,我们可以更广泛地使用流处理,充分利用"},{"type":"link","attrs":{"href":"https:\/\/spring.io\/reactive","title":"","type":null},"content":[{"type":"text","text":"reactive Spring技术栈"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这不仅仅是后端实现的简单区别,这意味着需要使用不同的应用程序框架。因此这就意味着需要拆分应用程序。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"数据库很重要"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"数据库很少出现在微服务相关的讨论中,这类讨论更多的是关于服务本身的结构、它们如何通信以及如何确保性能。数据库相关讨论的缺失让我感到很吃惊。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"单体应用往往调用优化良好的数据库层,而独立部署微服务意味着其会调用网络中的其他服务来获取数据,而这类网络调用往往是非常困难的。在这种情况下,确定我们可以在数据库级别上拆分应用程序,而不会破坏我们在网络请求方面的现有行为是决定新服务边界在何处的关键。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一个需要考虑的问题是,不同的数据模型可能需要使用不同的持久性策略。最初的服务是建立在Postgres上的,但是新的独立服务的流特性使得响应式MongoDB和地理空间查询成为一个很好的备选方案。最后,我们没有想出一个可以信赖的MongoDB文档结构,所以我们继续使用值得信赖的Postgres + Postgis。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我认为数据库设计是设计和确定微服务范围的一个重要部分。如果要考虑分割微服务,一个好的出发点是考虑如何分割数据库。如果功能不能在数据库级别上分割,那么在应用程序级别上分割代码能获得多少好处呢?事实上,我建议将数据库分割作为必要的第一步。如果我现在设计一个新系统,并且希望获得松散耦合和接口隔离等经典微服务优势,而不需要触及它们的所有困难,我将研究一个带有多数据库设置的模块化应用程序。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"独立失效"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微型服务有点像猫。它们有时会让你的生活更艰难,但一旦你拥有了它们,你就难以想象没有它们的生活了。它们的行为很难预测。互联网上讨论这些问题的内容非常多。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/6b\/06\/6b6fa771f258ef026e1yy2066f038306.jpg","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我心爱的猫有不同的大小,就像我的微服务。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,与我的猫不同的是,我不需要同等地对待我所有的微服务。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果Runner应用程序后端的遥测收集功能出现故障,这对我们的司机来说只会带来些微不便。但是,如果管理行程状态的服务出现哪怕一个小问题,它就会使我们的司机完全无法工作,并且毁掉我们的客户的一天。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们真的不希望“有则更好”的功能破坏那些操作上至关重要的“必须有”功能。而这正是独立运行的微服务能够带给我们的,而单体部署无法做到的一点。所有的服务都可能宕机,但是对于微服务,我们有一个工具可以进行优雅地降级。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"这就是全部"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们认为这是充分的。注意,在做出这个决定时,我们并没有遵循独立的部署流程、开发工作流或代码存储库。我们仍然是一个团队,在一个发布周期内致力于一个产品。在团队中,新分离的微服务只是技术实现的一个方面。我们不会从独立管理依赖关系、版本声明或拆分代码库中获得其他任何好处,所以我们根本没有触及那些方面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果您打算采用微服务体系结构,我建议您仔细考虑您实际想要的和需要从中得到的东西,而不是将自己束缚在其他人关于“正确的”微服务设置的概念中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"成功原因"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这是一个成功的案例,此时产品团队已经通过采用微服务模式获得了我们所想要的一切。它有可能会向另一个糟糕方向发展,如果那样的话这会是一篇“为什么我们放弃微服务”的文章。相反我们获得了成功,下面是一些我认为有助于成功的主要因素。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"没有急剧转变"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"团队不仅将遥测功能和行程状态功能拆分为单独的微服务。实际上,到目前为止,团队管理着四个不同的微服务。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们只是在权衡和考虑我们的选择时才添加微服务。我们没有例行公事地增加服务作为我们增长的一部分。我们可以扩大现有的服务,但公司的增长并不需要进行新的部署。只有在应用程序需要时,我们才增加我们微服务的数量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"任务决定技术"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"将原来的应用替换为分布式应用意味着您现在有一大堆新问题需要处理。您不仅仅是用HTTP调用替换了数据库调用,这对您所开发的应用程序的类型有着深远的影响。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"幸运的是,在最初的微服务迁移过程中,我们已经在Picnic中解决了这些问题。我们有一个内部的Kubernetes集群,我们搭建了一个消息队列并准备就绪,部署流水线很顺畅,身份验证系统可以处理分布式系统。这些都需要大量汗水和泪水才能运行成功。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对技术进行投资,使微服务能够实际使用,这将成为您的技术团队将承担的最耗费人力的项目。确保你有充足的理由这样做。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"发布和依赖管理的复杂性并未增加"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很容易发现自己处在这样一种情况中,即花费大量时间进行繁琐的多步骤部署,必须处理依赖版本,并为实现单个功能而在多个代码库中工作。微服务意味着技术上更复杂的系统,但这并不意味着开发人员的日常工作流会受到影响。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在大多数情况下,这是一个以适合开发人员的方式构建项目的问题,这个话题与微服务本身无关。更新您的部署流程,对依赖项进行逻辑分层,根据工作流程的需要将代码拆分为多个仓库。在迈向微服务模式时,请不要忽视单体应用程序中那些非常方便的特性,有可能您可以保留其优秀的方面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"下一步"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里讨论的软件仍在发展,任务仍然存在。我们的测试可以改进,在代码库的这一部分中,我们有自动化的单元测试、集成测试和组件测试,但是端到端测试尚未开始。客户希望与之交互的服务数量使我们开始研究API网关和通过GraphQL进行跨服务数据聚合。我还不知道这个微服务的故事将如何结束,但是只要我们不断仔细地反复研究并保持清醒,我相信这个故事将有一个圆满的结局!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文链接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/blog.picnic.nl\/a-microservice-success-story-fb29aa059caa","title":"xxx","type":null},"content":[{"type":"text","text":"A Microservice Success Story"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章