热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

低风险整体式微服务演进第二部分

让我们潜入吧!在上一篇文章(第一部分)中,我们为该博客设置了上下文。基本上,随着我们引入将微服务引入我们的体系

让我们潜入吧! 在上一篇文章(第一部分)中,我们为该博客设置了上下文。 基本上,随着我们引入将微服务引入我们的体系结构的策略,我们不能也不应破坏当前的请求流。 我们的“整体”应用程序通常为业务提供很多价值,并且我们必须降低迭代和扩展时对这些系统造成负面影响的风险。 这使我们想到了一个经常被忽视的事实:当我们开始探索从整体到微服务的旅程时,我们将Swift遇到我们无法忍受的不良,有时令人讨厌的部分。 如果您还没有,我鼓励您回过头阅读第一部分 。 还可以阅读有关何时不做微服务的部分。

在Twitter或http://blog.christianposta.com上关注( @christianposta ),以获取最新更新和讨论。

在上一部分中,这是我们要解决的一些注意事项:

  • 我们需要一种可靠且一致的方式来构建我们的服务。 我们需要一个持续交付系统。
  • 我们需要一种方法来测试我们的服务/整体/等
  • 我们需要一种方法来安全地将任何变更投入生产,包括黑暗发射,金丝雀等
  • 我们希望有一种方法将流量路由到我们的新更改,或启用更改(或取消切换)任何新功能/更改
  • 我们将应对许多令人讨厌的数据集成挑战

技术领域

我们将用来帮助指导我们前进的技术:

  • 开发人员服务框架( Spring Boot , WildFly , WildFly Swarm )
  • API设计( APICur.io )
  • 数据框架( Spring Boot Teiid , Debezium.io )
  • 集成工具( Apache Camel )
  • 服务网格( Istio服务网格 )
  • 数据库迁移工具( Liquibase )
  • 暗启动/功能标记框架( FF4J )
  • 部署/ CI-CD平台( Kubernetes / OpenShift )
  • Kubernetes开发人员工具( Fabric8.io )
  • 测试工具( Arquillian , Pact / Arquillian Algeron , Hoverfly , Spring-Boot测试 , RestAssured , Arquillian Cube )

如果您想继续,我正在使用的示例项目基于http://developers.redhat.com上的TicketMonster教程,但已进行了修改,以显示从单片到微服务的演变。 您可以在github上找到代码和文档(文档仍在进行中!): https : //github.com/ticket-monster-msa/monolith

让我们逐步学习第一部分 ,看看如何解决每个步骤。 我们还将引入上一个博客中的注意事项,并在此情况下重新进行考虑。

遇见巨石

重新考虑注意事项

  • 整体式(代码和数据库架构)很难更改
  • 变更需要完全重新部署并在团队之间进行高度协调
  • 我们需要进行大量测试以赶上回归
  • 我们需要一种完全自动化的部署方式

这可能并不总是可能的,但是如果可以的话,请为整体编写大量测试。 当我们通过添加新功能或替换现有功能来发展整体时,我们需要对更改的影响有充分的了解。 迈克尔·费瑟斯(Michael Feathers)在有关重构旧版代码的书中将“旧版代码”定义为未进行测试。 使用JUnit和Arquillian之类的工具会极大地帮助您。 您可以使用Arquillian随意选择细粒度或粗粒度,然后打包应用程序,但是需要(使用适当的模拟等)来练习要测试的应用程序部分。 例如,在我们的整体模型(TicketMonster)中,我们可以定义一个微部署,该微部署将数据库存入内存中,并预先将示例数据加载到数据库中。 Arquillian非常适合Spring Boot应用程序,Java EE等。在这种情况下,我们正在测试Java EE整体:

public static WebArchive deployment() { return ShrinkWrap .create(WebArchive. class , "test.war" ) .addPackage(Resources. class .getPackage()) .addAsResource( "META-INF/test-persistence.xml" , "META-INF/persistence.xml" ) .addAsResource( "import.sql" ) .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml" ) // Deploy our test datasource .addAsWebInfResource( "test-ds.xml" ); }

甚至更有趣的是,您可以运行运行时中嵌入的测试来验证所有组件在内部是否正常工作。 例如,在上述测试之一中,我们可以将BookingService注入我们的测试中并直接运行它:

@RunWith (Arquillian. class ) public class BookingServiceTest { @Deployment public static WebArchive deployment() { return RESTDeployment.deployment(); } @Inject private BookingService bookingService; @Inject private ShowService showService; @Test @InSequence ( 1 ) public void testCreateBookings() { BookingRequest br = createBookingRequest(1l, 0 , new int []{ 4 , 1 }, new int []{ 1 , 1 }, new int []{ 3 , 1 }); bookingService.createBooking(br); BookingRequest br2 = createBookingRequest(2l, 1 , new int []{ 6 , 1 }, new int []{ 8 , 2 }, new int []{ 10 , 2 }); bookingService.createBooking(br2); BookingRequest br3 = createBookingRequest(3l, 0 , new int []{ 4 , 1 }, new int []{ 2 , 1 }); bookingService.createBooking(br3); }

对于一个完整的例子,看看在BookingServiceTest 从TicketMonster整体模块 。

但是部署呢?

Kubernetes已成为容器化服务/应用程序的实际部署平台。 Kubernetes处理诸如运行状况检查,扩展,重新启动,负载平衡等操作。对于Java开发人员,我们甚至可以使用fabric8-maven-plugin之类的工具自动构建我们的容器/ docker映像并生成任何部署资源文件。 OpenShift是RedHat的Kubernetes的产品版本,除其他功能外,还添加了开发人员功能,包括CI / CD管道之类的功能。

Kubernetes / OpenShift是您的应用程序/服务的部署平台,无论它们是微服务,整体还是两者之间的其他任何东西(具有处理持久性工作负载(例如数据库等)的能力)。 借助Arquillian,容器和OpenShift管道,我们有可靠的方法将变更不断交付到生产中。 顺便说一句…检出openshift.io ,可通过自动CI / CD管道,SCM集成, Eclipse Che开发人员工作区,库扫描等使开发人员体验更加深入 。

在这一点上,生产负荷指向整料。 如果转到其主页,则会看到类似以下内容的内容:

让我们开始进行一些更改...

提取用户界面

重新考虑注意事项

  • 第一步,请勿修改整体。 只需将UI复制/粘贴到单独的组件中
  • 我们需要在UI和整体之间建立一个合理的远程API-并非总是如此
  • 安全面增加
  • 我们需要一种以受控方式将流量路由/拆分到新用户界面和/或整体的方式,以支持黑暗发射/金丝雀/滚动释放

如果我们看一下TicketMonster UI v1代码,我们会发现它非常简单。 我们已经将静态HTML / JS / CSS组件移至其自己的Web服务器,并将其打包到一个容器中。 这样,我们可以将其与整体分开部署,并独立进行更改/版本化。 该UI项目仍将需要与Monolith对话以执行其功能,因此,此演变的一部分应该是公开UI可以与之交互的REST接口。 对于某些整体而言,这说起来容易做起来难。 如果您在围绕旧式整体代码包装好的REST API时遇到了挑战,我强烈建议您看一下Apache Camel,尤其是其REST DSL 。

关于此步骤的一个有趣的部分是,我们实际上并未更改整体中的任何内容。 该代码保持不变,但我们的新UI也已部署。 如果我们查看Kubernetes,我们将看到两个单独的Deployment对象和两个单独的pod:一个用于整体,一个用于UI。

ceposta @postamac $ kubectl get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE mysql-backend 1 1 1 1 4d ticket-monster 1 1 1 1 4d tm-ui-v1 1 1 1 1 4d

即使我们已经部署了tm-ui-v1 UI,我们也看不到任何新的TicketMonster UI组件的流量。 为简单起见,即使此部署没有占用生产流量(虽然ticket-monster整体目前占用了全部生产流量),我们仍然可以将其视为简单的暗发射。 如果我们向前移植到UI,我们仍然可以到达它:

kubectl port-forward tm-ui-v1- 3105082891 -gh31x 8080 : 80

我们使用kubectl cli工具将本地盒中的端口转发到特定的Pod(端口80上的tm-ui-v1-3105082891-gh31x其映射到本地端口8080 。现在,如果我们导航到http:// localhost :8080我们应该使用UI的新版本(请注意突出显示的文本指示这是一个不同的UI,但它直接指向整体)

如果我们对这个新版本感到满意,就可以开始将流量引向这个新版本。 为此,我们将使用Istio服务mesh 。 Istio是用于管理由入口点和服务代理组成的网格的控制平面。 我已经写了一些有关服务网格和数据平面(例如Envoy)的文章 。 我强烈建议您看一下它的全部功能。 在接下来的几节中,我们将迭代该项目,以探索Istio功能。 如果您对控制平面/数据平面的区别进一步感到困惑, 请看Matt Klein撰写的博客,其中谈到了

我们将从使用Istio Ingress Controller开始 。 该组件使您可以使用Kubernetes Ingress规范控制进入Kubernetes集群的流量 。 安装Istio之后 ,我们可以创建一个这样的Ingress资源,将流量指向Ticket Monster UI的Kubernetes服务 tm-ui :

apiVersion: extensions/v1beta1 kind: Ingress metadata: name: tm-gateway annotations: kubernetes.io/ingress. class : "istio" spec: backend: serviceName: tm-ui servicePort: 80

有了入口之后,就可以开始应用Istio路由规则了 。 例如,这是一条路由规则,上面写着“任何时候有人试图与Kubernetes中运行的tm-ui服务进行对话,将其定向到该服务的v1 ”:

apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: tm-ui- default spec: destination: name: tm-ui precedence: 1 route: - labels: version: v1

这将使我们能够更好地控制进入集群甚至集群内部的流量。 一点点更多。 在此步骤的最后,我们所有流量都流向了tm-ui-v1部署,后者又直接与整体通信。

从整体中删除UI

重新考虑注意事项

  • 我们正在从整体中删除UI组件
  • 这要求(希望)对整体进行最小的更改(不建议使用/删除/禁用UI,可能需要更新REST API)
  • 再次,我们使用受控的路由/整形方法来引入此更改而无需停机

这一步很简单。 我们正在通过从中删除静态UI组件(这些组件现已移至tm-ui-v1部署)来更新整体。 我们还可以对此部署进行一些API更改,因为我们现在已经释放了应用程序,使其成为具有UI可以使用的API以及其他应用程序可能的独占服务。 由于我们可能会对API进行了一些更改,因此我们可能还希望部署UI的新版本。 在此步骤中,我们将部署backend-v1服务以及一个新的UI tm-ui-v2 ,该UI将在我们的backend服务中利用此新API。

让我们看看Kubernetes集群中部署了什么:

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE backend-v1 1 1 1 1 4d mysql-backend 1 1 1 1 4d ticket-monster 1 1 1 1 4d tm-ui-v1 1 1 1 1 4d tm-ui-v2 1 1 1 1 4d

此时, ticket-monstertm-ui-v1部署正在进行实时负载。 backend-v1和指向它的用户界面tm-ui-v2不加负载。 需要注意的一件事是, backend-v1部署与ticket-monster部署共享相同的数据库,因为它几乎相同,但是面向外部的API稍有不同。

我们新的backend-v1tm-ui-v2组件已部署到生产中。 现在是时候专注于一个简单但至关重要的事实了:我们已将更改部署到生产中,但尚未发布给任何人。 turbolabs.io上的好人有一个很棒的博客,它更详细地阐明了这一点 。 我们有机会进行非正式的黑暗发射。 也许我们想先将此部署缓慢地展开给我们的内部用户,或者也许先展开给特定设备上特定区域中的一部分用户,等等。

既然现在有了Istio,让我们看看它可以为我们做些什么。 我们只想对内部用户进行暗启动。 可以识别那些内部用户,但是我们喜欢(标头,IP等),但是对于本示例,我们将说带有HTTP标头为x-dark-launch: v2任何请求x-dark-launch: v2将被路由到新的backend-v1tm-ui-v2服务。 istio路由规则如下所示:

apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: tm-ui-v2-dark-launch spec: destination: name: tm-ui precedence: 10 match: request: headers: x-dark-launch: exact: "v2" route: - labels: version: v2

当我们以任何用户身份访问主页时,我们应该看到当前的部署( tm-ui-v1ticket-monster Monolith对话):

现在,如果我们更改浏览器中的标头(例如使用Firefox的Modify Headers工具或类似工具 ),则应将其路由到暗中启动的服务集( tm-ui-v2backend-v1对话):

然后单击“开始”以开始标题修改并刷新页面:

我们看到我们现在已被重定向到我们的服务的黑暗启动。 从这里开始,我们可以进行金丝雀发布(也许将1%的实时流量发送到我们的新部署中),然后慢慢增加流量负载(5%,10%,50%等),从而释放给客户群看到没有不良影响。 这是一个Istio路由规则示例,该规则将v2流量限制为1%:

apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: tm-ui-v2-1pct-canary spec: destination: name: tm-ui precedence: 20 route: - labels: version: v1 weight: 99 - labels: version: v2 weight: 1

能够“看到”或“观察”此版本的效果至关重要,我们稍后将进一步讨论。 还要注意,这种金丝雀释放方法目前正在我们的体系结构的边缘进行,但是服务间通信/交互也可以使用istio来控制金丝雀。 在接下来的几个步骤中,我们将开始看到这一点。

推出一项新服务

重新考虑注意事项

  • 我们想重点关注提取服务的API设计/边界
  • 这可能是对巨石中存在的东西的重写
  • 确定API后,我们将为该服务实现一个简单的脚手架/占位符
  • 新的订单服务将拥有自己的数据库
  • 目前,新的订单服务将不会产生任何流量

在这一步中,我们将开始为新的Orders服务设计所需的API,这很可能与我们通过某些域驱动的设计练习确定的边界更加一致。 我们可以投入大量精力来使用API​​建模工具来设计API,部署它的虚拟化实现并与我们的消费者进行迭代,而不必花大量精力在前面构建API只是后来发现需要不断更改。

在我们的TicketMonster重构的情况下,我们可能希望保持与整体中类似的API,以使分解最初尽可能轻松,低风险。 无论哪种情况,我们都可以利用两个很棒的工具来帮助我们:一个名为apicur.io的基于Web的API设计器和一个名为Hoverfly的测试/ API虚拟化工具。 Hoverlfy是用于模拟API或捕获现有API流量的出色工具,因此可用于模拟模拟端点。

如果我们正在构建未开发的API,或者试图想象使用域驱动的设计方法进行迭代后,API的外观,则可以使用apicur.io工具来构建Swagger / Open API规范。

对于TicketMonster,我们通过在proxy模式下启动hoverfly并捕获流量,使用hoverfly捕获了从应用程序到后端服务的流量。 我们可以在浏览器设置中轻松设置HTTP代理,以通过hoverfly发送所有流量。 它将每个请求/响应对的模拟存储在JSON文件中。 从这里开始,我们在模拟甚至更好的情况下使用请求/响应对,并使用它们开始编写测试,以对我们实现中想要的行为进行编码。

对于我们关心的请求/响应对,我们可以创建一个JSON模式(签出https://jsonschema.net/#/editor并在我们的测试中使用它。

例如,结合使用Rest Assured和Hoverfly,我们可以调用hoverfly模拟并断言响应符合我们期望的JSON模式:

@Test public void testRestEventsSimulation(){ get( "/rest/events" ).then().assertThat().body(matchesJsonSchemaInClasspath( "json-schema/rest-events.json" )); }

从新的Orders服务中检出HoverflyTest.java测试。

有关测试Java微服务的更多信息,请从曼宁的这本很棒的书中找到我的同事Alex Soto Bueno , Jason Porter和Andy Gumbrecht的 “测试Java微服务” 。

由于这篇博客文章已经很长了,我决定将最后一部分划分为第三部分,该部分涉及在整体和微服务之间管理数据,消费者合同测试以及如何进行功能标记/更复杂的istio路由,本系列的第四部分将展示所有这些的录制演示,以及负载模拟测试和故障注入的演示。 请继续关注并关注Twitter !

翻译自: https://www.javacodegeeks.com/2017/10/low-risk-monolith-microservice-evolution-part-ii.html




推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 前面刚有AWS开战MongoDB,双方“隔空互呛”,这厢又曝出2亿+简历信息泄露——MongoDB的这场开年似乎“充实”得过分了些。长期以来,作为“最受欢迎的NoSQL数据库”,M ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了一些Java开发项目管理工具及其配置教程,包括团队协同工具worktil,版本管理工具GitLab,自动化构建工具Jenkins,项目管理工具Maven和Maven私服Nexus,以及Mybatis的安装和代码自动生成工具。提供了相关链接供读者参考。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • AstridDAO 专访:波卡稳定币黑马 BAI
    加入Pol ... [详细]
  • 用户视图(查看运行状态或其他参数)系统视图(配置设备的系统参数)system-viewEntersystemview,returnuservi ... [详细]
  • java.lang.Class.getDeclaredMethod()方法java.lang.Class.getDeclaredMethod()方法用法实例教程-方法返回一个Met ... [详细]
  • 原文:http:blog.linjunhalida.comblogpjaxgithub:https:github.comdefunktjquery-pjax ... [详细]
  • 于2012年3月份开始接触OpenStack项目,刚开始之处主要是与同事合作共同部署公司内部的云平台,使得公司内部服务器能更好的得到资源利用。在部署的过程中遇到各种从未遇到过的问题 ... [详细]
author-avatar
一个萝卜一个坑
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有