聊聊微服务架构

 

 

1. 微服务架构概念解析

2. 构建微服务架构:使用 API Gateway

3. 深入微服务架构的进程间通信

4. 服务发现的可行方案以及实践案例

5. 微服务的事件驱动数据管理

6. 选择微服务部署策略

7. 将单体应用改造为微服务

首先让我们了解为何要将微服务纳入考量。

构建单体应用

假设我们要开发一款全新的与 Uber 和 Hailo 竞争的打车软件。在前期的会议和需求整理后,你要么需要手动创建一个新项目,要么可以使用 Rails、Spring Boot、Play 或者 Maven 来生成。这个新应用可能采用了六边形架构模块,如下图所示:

Richardson-microservices-part1-1_monolithic-architecture

应用的核心是商业逻辑,它由定义服务、域对象和事件各模块来完成。各种适配器围绕核心与外部交互。适配器包括数据库访问组件、生成和 consume 信息的消息组件,以及提供 API 或者 UI 访问支持的 web 模块。

尽管拥有逻辑缜密的模块化设计,整个应用仍然以整体打包和部署,实际格式依赖于应用的语言和框架。譬如,许多 Java 应用被打包为 WAR 文件,部署在 Tomcat 或者 Jetty 这样的应用服务器。有些 Java 应用本身就是包涵 JARs 的软件包。与此类似,Rails 和 Node.js 应用也通过目录层级打包。

采用此种风格的应用非常普遍。由于 IDE 和其他工具擅长构建单一应用,这类应用也易于部署。这类应用也非常容易测试。你可以非常轻松地进行端到端测试,使用 Seleniu 测试 UI 。整体应用也便于部署,只需将软件包复制到服务器。你也可以通过运行多个包和负载均衡实现扩展。在项目早期这么做非常有效。

踏入单体架构的地狱

很不幸,这一简单的方法有着巨大的局限。成功的应用最终会随着时间变得巨大。在每个 sprint 阶段,开发团队都会新加许多行代码。几年后,原本小而简单的应用会变得臃肿。举个极端的例子,我最近与一位开发者交流,他正在开发一款小工具,来分析他们应用(包括几百万行代码)中的几千个 JARs 的依赖。我相信每年都会有大量开发者不遗余力地对付这种麻烦。

一旦你的应用变得庞大、复杂,你的开发团队将饱受折磨,苦苦挣扎于敏捷开发和交付。一大原因就是应用已经格外复杂,庞大到任何一个开发者都无法完全理解。最后,修复 bug 和实施新功能也就极其困难且耗时颇多。更可怕的是,这是一个向下的螺旋发展。代码库越难理解,正确的修改就越难。最后你会深陷庞大的、无法估量的泥淖之中。

而这种应用的尺寸也会拖慢开发进度。应用越大,启动时间越长。譬如在最近的调查中,不少开发者指出启动时间长达 12 分钟。我也听说有的应用启动时间居然得 40 分钟。如果开发者不得不频繁重启应用服务器,那大量时间就被浪费,生产效率也饱受其害。

庞大且复杂的单体应用的另一大问题就是难以进行持续部署。现在, SaaS 应用的发展水平足以在单日内多次将修改推送到生产环境。然而要让复杂的单个应用达到此水平却极为棘手。想更新应用的单个部分,必须重新部署整个应用,漫长的启动时间更是雪上加霜。另外,由于不能完全预见修改的影响,你不得不提前进行大量人工测试。结果就是,持续部署变得不可能。

如果单体应用的不同模块在资源需求方面有冲突的话,那应用的扩展也很难。比如,模块之一需要执行 CPU-intensive 图像处理逻辑,最好部署到 AWS 的 EC2 Compute Optimized instances;而另一模块需要内存数据库,最好适配 EC2 Memory-optimized instances。由于这两个模块需要共同部署,你不得不在在硬件选择方面做妥协。

单体应用的另一问题就是可靠性。由于所有模块都运行在同一进程中,任何模块中的一个 bug,比如内存泄漏都可能弄垮整个进程;此外,由于应用中的所有实例都是唯一,这个 bug 将影响整个应用的可用性。

最后,单体应用会让采用新框架和语言极其困难。举例来说,你有两百万行使用 XYZ 框架的代码,如果要使用 ABC 框架重写代码,无论时间还是成本都将非常高昂,即便新框架更好。这也就成为使用新技术的阻碍。

总结:这个一开始曾经成功关键业务应用,最终却变成一个臃肿的、无法理解的庞然大物。它使用老旧、陈腐、低效的技术,几乎吸引不到出色的开发者。这个应用非常难于扩展,也不稳定可靠。最终,敏捷开发和交付几乎成为不可能。

你该何去何从?

微服务——直击痛点

诸如亚马逊、eBay、Netflix 等公司已经通过采用微服务架构范式解决了上文(第一部分)提到的问题。不同于构建单一、庞大的应用,微服务架构将应用拆分为一套小且互相关联的服务。

一个微服务一般完成某个特定的功能,比如订单管理、客户管理等。每个微服务都是一个微型应用,有着自己六边形架构,包括商业逻辑和各种接口。有的微服务通过暴露 API 被别的微服务或者应用客户端所用;有的微服务则通过网页 UI 实现。在运行时,每个实例通常是一个云虚拟机或者 Docker 容器。

对于前文所述的系统,一种可能的系统分解图如下:

Richardson-microservices-part1-2_microservices-architecture

应用的每个功能区都由其自身微服务实施。此外,整个网页应用被拆分为一套简单的网页应用(比如我们的打车软件拆分为乘客应用和司机应用),从而能够轻松地针对特定用户、设备或者用户案例进行单独部署。

每个后端服务包括一个 REST API 和由其它服务提供的服务消耗 API。例如,司机管理服务使用“通知”服务器来告知司机即将的行程。UI 服务唤醒其它服务,从而呈现网页。这些服务也可能用到基于信息的异步通信。内部服务通信会在本系列文章中详述。

有的 REST API 也对司机和乘客的移动应用开放。这些应用并不能直接访问后端服务器,相反,通信由名为 API Gateway 的中间人调解。AIP Gateway 负责负载均衡、缓存、访问控制、API 计费、监控等,通过 NGINX 高效实施。本系列的后续文章将会讲解 API Gateway。

Richardson-microservices-part1-3_scale-cube

上图是 Scale Cube 的 3D 模型,来自《The Art of Scalability》 一书。微服务架构范式对应 Y 轴,X 轴由负载均衡器后端运行的多个应用副本组成,Z 轴(数据分割)将需求路由到相关服务。

应用通常同时使用这三种不同类型的扩展。Y 轴扩展将应用分解为如图一(https://www.nginx.com/wp-content/uploads/2015/05/Graph-031-e1431992337817.png)所示的微服务。在运行时维度,X 轴扩展在输出和可用性的负载均衡后运行多个实例。部分应用会使用 Z 轴扩展来对服务进行数据分割。下图展示了行程管理服务(Trip Management)是如何使用 Docker 部署到 AWS EC2 上的。

Richardson-microservices-part1-4_dockerized-application

在运行时,行程管理服务包括多个服务实例,每个服务实例都是一个 Docker 容器。为了实现高可用性,这些容器运行在多个云虚拟机上。在应用实例前面是 NGINX 这样的负载均衡,将请求分发给全部实例。负载均衡也可以处理缓存、访问控制、 API 测量和监控等。

微服务架构范式对应用和数据库的关系影响巨大。每个服务都有自身的数据库计划,而不与其它服务共享同一个数据库。一方面,这个方法类似企业级数据模型。同时,它也导致部分数据的重复。然而,要想从微服务中获益,为每个服务提供单个的数据库计划就非常必要,这能保证松散耦合。下面的图表展示了示例应用的数据库架构。

intro-microservices

每个服务都有其自己的数据库。此外,单个服务可以使用符合自己需要的特定类型的数据库,即多语言一致性架构。例如,为了发现附近乘客,驾驶员管理服务必须使用高效支持地理位置请求的数据库。

表面上看,微服务架构范式与 SOA 非常类似,这两种架构都包括一套服务。然而,微服务架构范式被看作不包含某些功能的 SOA 。这些功能包括网络服务说明( WS-* )和 Enterprise Service Bus (ESB) 的商品化和请求包。基于微服务的应用更青睐 REST 这样简单的、轻量级的协议,而不是 WS-* 。他们也极力避免在微服务中使用 ESBs 及类似功能。微服务架构范式也拒绝 SOA 的其它部分,比如 canonical schema 的概念。

微服务架构的好处

微服务架构模式有很多好处。首先,通过分解巨大单体应用为多个服务方法解决了复杂性问题。在功能不变的情况下,应用被分解为多个可管理的分支或服务。每个服务都有一个用 RPC- 或者消息驱动 API 定义清楚的边界。微服务架构模式给采用单体式编码方式很难实现的功能提供了模块化的解决方案,由此,单个服务很容易开发、理解和维护。

第二,这种架构使得每个服务都可以有专门开发团队来开发。开发者可以自由选择开发技术,提供 API 服务。当然,许多公司试图避免混乱,只提供某些技术选择。然后,这种自由意味着开发者不需要被迫使用某项目开始时采用的过时技术,他们可以选择现在的技术。甚至于,因为服务都是相对简单,即使用现在技术重写以前代码也不是很困难的事情。

第三,微服务架构模式使得每个微服务独立部署,开发者不再需要协调其它服务部署对本服务的影响。这种改变可以加快部署速度,譬如 UI 团队可以采用 AB 测试并快速部署变化。微服务架构模式使得持续化部署成为可能。

最后,微服务架构模式使得每个服务独立扩展。你可以根据每个服务的规模来部署满足需求的实利。甚至于,你可以使用更适合于服务资源需求的硬件。比如,你可以在 EC2 Compute Optimized instances 上部署 CPU 敏感的服务,而在 EC2 memory-optimized instances 上部署内存数据库。

微服务架构的不足

Fred Brooks 在 30 年前写道 “there are no silver bullets”,像任何其它科技一样,微服务架构也有不足。其中一个跟他的名字类似,“微服务”强调了服务大小,实际上,有一些开发者鼓吹建立稍微大一些的,10-100 LOC服务组。尽管小服务更乐于被采用,但是不要忘了微服务只是结果,而不是最终目的。微服务的目的是有效的拆分应用,实现敏捷开发和部署。

另外一个不足之处在于,微服务应用是分布式系统,由此会带来固有的复杂性。开发者需要在 RPC 或者消息传递之间选择并完成进程间通讯机制。此外,他们必须写代码来处理消息传递中速度过慢或者不可用等局部失效问题。当然这并不是什么难事,但相对于单体式应用中通过语言层级的方法或者进程调用,微服务下这种技术显得更复杂一些。

另外一个关于微服务的挑战来自于分区的数据库架构。同时更新多个业务主体的事务很普遍。这种事务对于单体式应用来说很容易,因为只有一个数据库。在微服务架构应用中,需要更新不同服务所使用的不同的数据库。使用分布式事务并不一定是好的选择,不仅仅是因为 CAP 理论,还因为当前高扩展性的 NoSQL 数据库和消息传递中间件并不支持这一需求。最终你不得不使用一个最终一致性的方法,从而对开发者提出了更高的要求和挑战。

测试一个基于微服务架构的应用也是很复杂的任务。比如,对于采用流行的 Spring Boot 架构的单体式 web 应用,测试它的 REST API,是很容易的事情。反过来,同样的服务测试需要启动与它有关的所有服务(至少需要这些服务的 stubs)。再重申一次,不能低估了采用微服务架构带来的复杂性。

另外一个挑战在于,微服务架构模式应用的改变将会波及多个服务。比如,假设你在完成一个案例,需要修改服务A、B、C,而 A 依赖 B,B 依赖 C。在单体应用中,你只需要改变相关模块,整合变化,部署就好了。对比之下,微服务架构模式就需要考虑相关改变对不同服务的影响。比如,你需要更新服务 C,然后是 B,最后才是 A。幸运的是,许多改变一般只影响一个服务,而需要协调多服务的改变很少。

部署一个微服务应用也很复杂,一个单体应用只需要在复杂均衡器后面部署各自的服务器就好了。每个应用实例是需要配置诸如数据库和消息中间件等基础服务。相比之下,一个微服务应用一般由大批服务构成。根据 Adrian Cockcroft 的分享,Hailo 由 160 个不同服务构成,而 NetFlix 则超过 600 个服务。每个服务都有多个实例,这就形成大量需要配置、部署、扩展和监控的部分。除此之外,你还需要完成一个服务发现机制(后续文章中发表),以用来发现与它通讯服务的地址(包括服务器地址和端口)。传统的解决问题办法并不能解决这么复杂的问题。最终,成功部署一个微服务应用需要开发者有足够的控制部署方法,并高度自动化。

自动化的方法之一是使用譬如 Cloud Foundry 这样的 PaaS 服务。PaaS 能让开发者轻松部署和管理微服务,让他们无需为获取并配置 IT 资源劳神。同时,配置 PaaS 的系统和网络专家可以采用最佳实践和策略来简化这些问题。另外一个自动部署微服务应用的方法是开发自己的基础 PaaS 系统。通常的起步方式是 Mesos 或 Kubernetes 这样的集群管理方案,配合 Docker 使用。作为一种基于软件的应用交付方法,NGINX 能够方便地在微服务层面提供缓冲、权限控制、API 统计、以及监控。我们会在后续的文章中分析它如何解决这些问题。

总结

构建复杂的应用的确非常困难。单体式的架构更适合轻量级的简单应用。如果你用它来开发复杂应用,那真的会很糟糕。微服务架构模式可以用来构建复杂应用,当然,这种架构模型也有自己的缺点和挑战。

在后续的博客中,我会深入探索微服务架构模式,并讨论多个话题,包括服务发现、服务部署选择、以及将单体应用拆分为多个服务的策略。

 

转载于:https://www.cnblogs.com/ZJOE80/p/5788351.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/470259.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AntDesign组件库的使用

一、AntDesign的介绍 AntDesign ,简称 antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。 中后台的产品 属于工具性产品,很多优秀的设计团队通过自身的探索和积累,形成了自己的设计体系。 An…

socket.io跨域踩坑

一、koa结合socket.io 后端代码: // 引入依赖 const koa require("koa"); // 初始化koa const app new koa(); // 开启 http var server require("http").createServer(app.callback()); // 初始化 socket const io require("socket…

ios 权限提示语_iOS工作室都在用按键v1.6.1(体验版)

按键精灵iOS v1.6.1(体验版)已发布到cydia新增工作室专场还支持VIP自助换绑欢迎下载使用~源地址:http://apt.mobileanjian.comVIP换绑方法如下:1、在按键精灵APP上进入“我的”分页,点击VIP信息,看到“解绑”按钮。点击后&#xf…

Redux学习(一)——Redux的使用过程

一、为什么需要redux JavaScript开发的应用程序,已经变得越来越复杂了: JavaScript需要管理的状态越来越多,越来越复杂;这些状态包括服务器返回的数据、缓存数据、用户操作产生的数据等等,也包括一些UI的状态&#x…

Redux学习(二)——封装connect函数

一、自定义connect函数 connect.js: import {PureComponent} from "react"; import store from "../store"; export function connect(mapStateToProps, mapDispatchToProps) {return function enhanceHOC(WrappedComponent) {return class extends PureC…

Redux学习(三)——redux-saga的使用、编写中间件函数、Reducer文件拆分

一、redux-devtools 我们之前讲过,redux可以方便的让我们对状态进行跟踪和调试,那么如何做到呢? redux官网为我们提供了redux-devtools的工具;利用这个工具,我们可以知道每次状态是如何被修改的,修改前后…

react-router的使用(一)——URL的hash、HTML5的history、Router的基本使用

一、阶段一:后端路由阶段 早期的网站开发整个HTML页面是由服务器来渲染的. 服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示. 但是, 一个网站, 这么多页面服务器如何处理呢? 一个页面有自己对应的网址, 也就是URL.URL会发送到服务器, 服务器会通过正…

react-router的使用(二)——NavLink的使用、Switch的作用、Redirect

一、NavLink的使用 需求:路径选中时,对应的a元素变为红色 这个时候,我们要使用NavLink组件来替代Link组件: activeStyle:活跃时(匹配时)的样式;activeClassName:活跃时…

群晖ffmpeg_群晖Video station支持DTS和EAC3

群晖video station这个套件现在经过群晖的打磨,现在还是不错的,支持硬件解码和蓝光等多媒体播放,比起PLEX和EMBY动辄好几百的会员费,这个免费的用起来还真香,但是因为种种小问题需要解决了,才能好用&#x…

react-router的使用(三)——路由的嵌套

一、路由的嵌套 在开发中,路由之间是存在嵌套关系的。 这里我们假设about页面中有三个页面内容: 企业历史、企业文化和联系我们;点击不同的链接可以跳转到不同的地方,显示不同的内容; 二、手动路由跳转 目前我们实现…

如何阅读一本书 pdf_《如何阅读一本书》:一本书,四个层次,看阅读小白如何逆袭?...

“读书不是为了雄辩和驳斥,也不是为了轻信和盲从,而是为了思考和权衡。”这是培根的一句名言,我们都曾经被这样的读书警句激励的斗志昂扬,于是立下目标一年或是一个月要读多少本书,结果发现一切是徒劳。你是否曾经斗志…

React Hooks的使用(一)——useState、useEffect解析

一、为什么需要Hook? Hook 是 React 16.8 的新增特性,它可以让我们在不编写class的情况下使用state以及其他的React特性(比如生命周期)。 我们先来思考一下class组件相对于函数式组件有什么优势?比较常见的是下面的优势&#xf…

React Hooks的使用(二)——useContext、useReducer、useCallback、useMemo解析

一、useContext的使用 在之前的开发中,我们要在组件中使用共享的Context有两种方式: 第一种方式:类组件可以通过 类名.contextType MyContext方式,在类中获取context; 第二种方式:多个Context或者在函数…

凸多边形面积_C++计算任意多边形的面积

任意多边形的面积计算_拾忆楓灵的博客-CSDN博客​blog.csdn.net计算任意多边形的面积 - tenos - 博客园​www.cnblogs.com完美解决计算3D空间任意多边形面积_Studiouss的博客-CSDN博客​blog.csdn.net求多边形面积公式(已知顶点坐标)_扬帆起航-CSDN博客​…

React Hooks的使用(三)——useRef、useImperativeHandle、useLayoutEffect解析、自定义Hook

一、useRef useRef返回一个ref对象,返回的ref对象再组件的整个生命周期保持不变。 最常用的ref是两种用法: 用法一:引入DOM(或者组件,但是需要是class组件)元素; 案例一:引用DOM …

python子类分配

原问题是将左边样式变成右边样式: 即有父类和子类,父类包括多个子类,怎样将子类匹配到父类下面的问题 代码如下 1 #!/usr/bin/python3.42 # -*- coding: utf-8 -*-3 4 arr1 ["S01","S01","S01","S02&quo…

react 网易云音乐实战项目笔记

0、项目规范 一、路由相关 npm i react-router-dom npm i react-router-config // 用于配置路由映射的关系数组1. 路由重定向 访问 /路径 》 重定向到 /discover路径 2. 二级路由: 二、网络请求相关 npm i axios页面中使用暴露 出来的request发送网络请求&#…

react项目打包

一、react项目打包 对于使用脚手架创建的项目,打包是一件非常容易的事情: yarn build其他文件没有太多要解析的,我们看一下js文件: [hash].chunk.js 代表是所有依赖的第三方库, vendor(第三方库) 的代码&#xff1b…

React SSR

一、为什么需要SSR呢? 单页面富应用的局限: 之前我们开发的应用程序,如果直接请求可以看到上面几乎没有什么内容。 但是为什么我们页面可以看到大量的内容呢? 因为当我们请求下来静态资源之后会执行JS,JS会去请求数…

Vue3 组件通信学习笔记

一、父子组件之间通信 父子组件之间如何进行通信呢? 父组件传递给子组件:通过props属性;子组件传递给父组件:通过$emit触发事件; 1.1 父组件传递给子组件 在开发中很常见的就是父子组件之间通信,比如父…