微服务架构简介
微服务架构模式(Microservices Architecture Pattern)的目的是将大型的、复杂的、长期运行的应用程序构建为一组相互配合的服务,每个服务都可以很容易得局部改良。
微服务(micro services)这个概念不是新概念,很多公司已经在实践了,例如亚马逊、Google、FaceBook,在国内我自己知道的有:BAT、滴滴、饿了么、携程、唯品会、酷狗等公司。
一个单体应用可能是下图这样的架构,各个功能在应用内部通过模块划分,这样的应用有它自己的好处如:调试简单、部署方便等。
但是它也有明显的局限性,如:
不同模块发生资源冲突时无法解决(有些业务需要无阻塞的io操作,这明显使用nodejs之类的语音是非常棒的,但是有些业务需要做算法密集型的操作,这明显就是nodejs的短板,这时候单体应用就需要做一个妥协,而不能都取最优解)。
单体应用一般所有应用都运行在同一进程中,在某个模块产生bug后会导致整个应用挂掉。
其中最突出的是,随着时间的迁移这个应用会越来越大,会大到任何单个开发者都不可能搞懂它。
将上图单体应用拆分为微服务的架构如下:
从图中可以看到每一个模块功能都变成了一个单独的服务,各服务之间通过各自暴露的API 接口相互调用,对外的接口通过一个API GATEWAY 来统一处理。
这种架构看起来明显是稍显复杂,但是其扩展性却是非常棒的,下图很好的描述如何去构建和扩展一个微服务架构。
- X轴表示在物理层面做负载均衡、应用副本来提高吞吐能力。
- Y轴表示从功能方面拆分服务,来对应处理单一专注功能。
- Z轴表示如何通过优化路由来整合相关服务(API GATEWAY)
微服务架构的优势与不足
优点:
- 每个服务足够内聚,足够小,代码容易理解、开发效率提高
- 服务之间可以独立部署,微服务架构让持续部署成为可能;
- 每个服务可以各自进行x扩展和z扩展,而且,每个服务可以根据自己的需要部署到合适的硬件服务器上;
- 容易扩大开发团队,可以针对每个服务(service)组件开发团队;
- 提高容错性(fault isolation),一个服务的内存泄露并不会让整个系统瘫痪;
- 系统不会被长期限制在某个技术栈上。
缺点
- 开发人员要处理分布式系统的复杂性;开发人员要设计服务之间的通信机制,对于需要多个后端服务的user case,要在没有分布式事务的情况下实现代码非常困难;涉及多个服务直接的自动化测试也具备相当的挑战性;
- 服务管理的复杂性,在生产环境中要管理多个不同的服务实例,意味着开发团队需要全局统筹(docker的出现适合解决这个问题)
API Gateway
如果已经使用微服务架构系统,客户端调用各个服务的使用场景可能是这样的:
想象一下,各个服务部署在不同的服务器上,那客户端调用是否就需要记录无数个baseurl,这还算好的。如果某一项功能需要同时调用多个服务来获取一个组合数据,那是否就需要发起N个HTTP请求,这对于客户端肯定噩梦。
所以我们需要 API GATEWAY
从图中可以直接看出 API GATEWAY 的直接作用为请求转发、合成等。客户端的请求都需要经过 API GATEWAY 先进行处理。
这还只是 API GATEWAY 的基本功能,一个合格的 API GATEWAY还有许多功能需要处理:
- 统一身份认证、权限配置
- 恶意访问限制
- 日志统一管理
- 服务器健康检查
- 缓存处理
- ...
添加 API GATEWAY 前后对比的用例图:
22732-bf98e36e1dc067bf 22732-153fcf86ad6d48a9如果有对应的研发实力,如果不记成本和实力考虑的话可以自己开发一套完整的 API GATEWAY,当然开源界也有一些造好的轮子可以使用:
Kong
基于Nginx 使用lua插件方式开发的 API GATEWAY ,上面介绍的功能基本上通过配置能够完成。除了API组合,我询问了开发组,他们的回答是:
Kong致力于开发一套外部API route,只完成相关流程处理和公共组件,关于组合API建议各个项目创建一个内部API GATEWAY 来专门处理对应的服务组合,然后通过Kong来路由转发。
在另一篇介绍中将介绍如何安装配置Kong,以及会踩到的坑。传送门
通信方式
由于各个服务间是相互独立的,如果服务之间需要相互访问,那就不免要处理进程间的通信了。在微服务间进行通信业界推荐采用一下几种方式:
- 同步:RPC、REST
- 异步:AMQP、STOMP
RPC
RPC(Remote Procedure Call,远程过程调用), RPC其实也是种C/S的编程模式,有点类似C/S Socket 编程模式,但要比它更高一层,通过RPC可以充分利用非共享内存的多处理器环境,应用程序就像运行在一个多处理器的计算机上一样。可以方便的实现过程代码共享,提高系统资源的利用率,也可以将以大量数值处理的操作放在处理能力较强的系统上运行,从而减轻前端机的负担。
同样,RPC是一套标准的模式,可按照既定的协议去自主实现,但是我还是建议采用先用轮子去做会省去很多成本,这里我推荐使用 Apache Thrift
。
Apache Thrift
Thrift 是 Facebook(后提交给Apache基金会将Thrift作为一个开源项目)实现的一种高效的、支持多种编程语言的远程服务调用的框架。
在另一篇文章中将介绍如何完成跨语言实现进程建通信。传送门
REST
REST就没有什么好特别介绍的,调用过API的同学对此都应该不陌生,REST一个种数据资源获取的设计原则。
采用该原则设计的好处就是简单、统一。
微服务之间通过REST走HTTP实现相互调用。
AMQP、STOMP
AMQP是一个异步消息传递所使用的应用层协议规范。
STOMP,Streaming Text Orientated Message Protocol,是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。
两者都是适用于微服务架构的异步通讯。
微服务架构中有大量开源的系统可以选择:比如RabbitMQ、Apache Kafka、Apache ActiveMQ和NSQ。它们都支持某种形式的消息和channel,并且都是可靠的、高性能和可扩展的;然而,它们的消息模型完全不同。
这里我推荐使用kafka,后续我将写一篇kafka的安装配置使用文章:传送门
然而在微服务架构中为什么需要使用异步消息模式来处理一些请求呢?
下图是一个系统当热销补货时就需要通知顾客的REST微服务实现。
- 一个外部实体发送一个存货清单更新请求到REST网关地址。
- 网关将请求转发给存货清单管理服务。
- 存货清单管理服务基于它接收到的请求更新存货清单,随后发送请求到热销补货通知服务。
- 接着,热销补货通知服务发送请求到订阅管理服务,要求当商品有库存时所有注册用户都需要被通知。
- 然后,订阅管理服务发送邮件REST请求到邮件服务,通过邮件来通知所有用户。
- 最后,每个服务依次相应,循环回到网关并且到达客户端。
这种实现很容发现其中的一些缺陷:
- 阻塞。通过REST来实现相互调用,必须在一个服务处理完成之后再去调用另外的服务,这么一层层传递之后客户端需要等到所有服务响应完毕才会得到对应的结果。这种处理方式将会影响到所有调用的服务性能。
- 耦合严重。比如:清单管理服务的单一职责就是更新系统存货清单(存货清单聚合根),它甚至都不需要知道通知服务的存在。在这个模型中,这两个服务是的耦合度太紧。
- 服务失效。如果其中某一个服务失效会造成整个流程都无法完成。
然而使用异步消息传送流程会被重构成下图所示:
使用异步消息机制能很好的处理上面那些出现的问题,当然世界上所有的异步消息传递都不是完全美好的,还需要考虑一些缺陷:
- 设计/实现/配置的困难,如消息排序,重复消息和消息幂等性。
- 异步消息的本性,一个动作结果未立即返回同样会增加系统和用户接口的复杂性。
- 信息流的可见性,系统内很难获得一个完全清晰的消息流图。这使调试变得更加困难,而且相对于管道方式,系统的业务逻辑更加难以管理。
服务发现
未完待续...
分布式数据管理
未完待续...
单体架构迁移至微服务
策略1——停止扩大
首先停止单体应用继续扩大,如果有新的业务需求,首先考虑将其开发为独立服务,然后通过单体应用接入。
策略2——将前端和后端分离
这里的前后端分离不是指的开发过程中仅仅在代码层面去分离前后端开发,而是要求在架构方面分离,后端指处理业务逻辑,前端web app单独实现,通过调用统一REST接口获取数据。
策略3——抽出服务
模块抽取排序
- 按复杂度抽取,从简单开始,可以积累微服务化抽取经验。
- 按改动频率抽取,频繁改动的模块可以优先抽取,微服务化后可以独立部署,加速开发进程
- 按性能消耗抽取,抽出性能瓶颈的模块,通过硬件横向专项处理,解决性能问题,或者使用更加合适的技术处理。
未完待续...
网友评论