让我们潜入吧! 在上一篇文章(第一部分)中,我们为该博客设置了上下文。 基本上,随着我们引入将微服务引入我们的体系结构的策略,我们不能也不应破坏当前的请求流。 我们的“整体”应用程序通常为业务提供很多价值,并且我们必须降低迭代和扩展时对这些系统造成负面影响的风险。 这使我们想到了一个经常被忽视的事实:当我们开始探索从整体到微服务的旅程时,我们将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

低风险整体式微服务演进第二部分相关推荐

  1. 响应式微服务_低风险整体式微服务演进第二部分

    响应式微服务 让我们潜入吧! 在上一篇文章(第一部分)中,我们为该博客设置了上下文. 基本上,当我们引入将微服务引入我们的体系结构的策略时,我们不能也不应破坏当前的请求流. 我们的"整体&q ...

  2. 响应式微服务_低风险整体式微服务演进第三部分

    响应式微服务 在第一部分(第一部分)中,我们通过看一个具体的例子介绍了一种在不破坏当前请求流和业务价值的情况下将微服务引入我们的体系结构的策略. 在第二部分中 ,我们开始研究与我们的架构策略和目标相符 ...

  3. 响应式微服务_低风险整体式微服务演进第一部分

    响应式微服务 作为为期两天的微服务研讨会的一部分,我一直在思考如何解释整体应用分解以及向微服务过渡的方式. 这只是该材料的一小部分,但我想与您分享以获取反馈(在研讨会上,我们将更详细地介绍您是否应该拆 ...

  4. 低风险整体式微服务演进第三部分

    在第一部分(第一部分)中,我们通过看一个具体的示例介绍了一种在不破坏当前请求流和业务价值的情况下将微服务引入我们的体系结构的策略. 在第二部分中 ,我们开始研究与我们的架构策略和目标相符的伴随技术. ...

  5. 低风险整体式微服务演进第一部分

    作为为期两天的微服务研讨会的一部分,我一直在思考如何解释整体应用程序分解以及向微服务过渡的方式. 这只是该材料的一小部分,但我想与您分享以获取反馈(在研讨会上,我们将更详细地介绍您是否应该拆开整体!) ...

  6. 分布式微服务架构体系详解

    课程介绍 微服务架构的技术体系.社区目前已经越来越成熟.在最初系统架构的搭建,或者当现有架构已到达瓶颈需要进行架构演进时,很多架构师.运维工程师会考虑是否需要搭建微服务架构体系.虽然很多文章都说微服务 ...

  7. Java 建模: 子整体软件开发,第二部分

    Java 建模: 子整体软件开发,第二部分 英文原文 内容: 软件不可见性 恰当过程的选择 软件需求规范 用例 功能特性 用户情景 结论 参考资料 关于作者 对本文的评价 相关内容: Java 建模系 ...

  8. 分布式微服务学习总结——分布式微服务概述

    文章目录 一.前言 二.一个传统的App发展进程 三.为什么要用分布式微服务? 四.什么是分布式.微服务? 1.微服务是什么? 2.微服务架构是什么? 3.分布式是什么? 4.微服务架构和分布式的关系 ...

  9. 从单体架构到分布式微服务架构的思考

    一.单体架构 1.什么是单体架构? 单体架构也可叫单体系统或单体应用,是一种把系统所有的功能模块耦合在一个应用的架构方式. 2.单体架构的优缺点有哪些? (1)优点 部署简单: 技术单一: 用人成本相 ...

最新文章

  1. 前端页面紫红色_谷歌正在开发一种神秘的新型移动操作系统,称为紫红色
  2. java如何监控cpu耗时_超级干货:3个性能监控和优化命令讲解
  3. android 多屏幕 设计翻译,android Supporting multiple screen翻译一
  4. VTK:图片之ImageToStructuredPoints
  5. 智能时代,企业如何“聚数为智”加速数字化转型?
  6. pytorch中unsqueeze()和squeeze()函数
  7. JAVA基础自学笔记整理(一)某些数据类型的姿势
  8. 大数据平台应用开发的痛点有哪些
  9. win10 android驱动安装失败,win10系统下小米手机驱动安装失败如何解决
  10. Mac下librdkafka下载安装
  11. php微信商家转账到零钱 发起商家转账API
  12. 1688按关键词搜索
  13. 随机搜索(Random Searching)算法
  14. CentOS 7 安装配置 k8s 1.25.3
  15. 主机序、网络序的理解
  16. pat甲级考试报名费_每日一感——记第一次PAT甲级考试之感想
  17. 动态爱心代码(pathon html)
  18. SAP EWM 交货单暂存区仓位确定(配置实现 / 前台维护)含操作实例
  19. 我喜欢你是寂静的--聂鲁达
  20. 如何在股市中赚钱 八大炒股秘诀助你巧制胜

热门文章

  1. 广州百田 技术类 笔试题
  2. 别再误会碎片化阅读了
  3. 逻辑代数的基本定律和运算规则
  4. 计算机职称考试入户,揭秘!2020年考什么职称更容易入户广州?
  5. 7、帆软填报-分页预览
  6. DVWA 之暴力破解攻击(Brute Force)
  7. 我的思维工具(三)收益半衰期
  8. CString怎么转成LPVOID的问题 vc/mfc
  9. 一阶常系数微分方程组的笔记
  10. js node.js读取excel文件返回为json文本