读《Chris Richardson 微服务系列》
阅读文章来自DaoCloud:http://blog.daocloud.io/microservices-1/
微服务架构概念解析
单体应用释义:
应用的核心是商业逻辑,它由定义服务、域对象和事件各模块来完成。各种适配器围绕核心与外部交互。适配器包括数据库访问组件、生成和 consume 信息的消息组件,以及提供 API 或者 UI 访问支持的 web 模块。
优点:
- 易于构建、部署
- 容易测试
- 横向拓展,负载均衡方便
缺点:
- 系统复杂:内部多模块紧密耦合,关联依赖复杂
- 运维困难:代码量巨大,没有人能理解整个应用,bug修复困难。任何修改都必须重启整个应用。
- 无法拓展:不能拆分部署,出现性能瓶颈往往只能增加服务器或集群节点。但是DB问题无法解决。
微服务释义:
一个微服务一般==完成某个特定的功能== ,比如订单管理、客户管理等。每个微服务都是一个微型应用,==有着自己六边形架构==,包括商业逻辑和各种接口。有的微服务通过暴露 API 被别的微服务或者应用客户端所用;有的微服务则通过网页 UI 实现。
与SOA区别:
- 不包含网络服务说明(WS-*)
- 不包含Enterprise Service Bus(ESB)
优点:
- scale cube的3D模型
- Y轴:应用分解,即将传统的单体应用分解为多个微服务应用
- X轴:负载均衡实现水平弹性拓展,但DB问题无法解决,引入3
- Z轴:DB弹性拓展,引入数据库拆分和Daas
- 引用分解后,单应用复杂度降低。每个服务都有一个RPC 或者消息驱动API,边界清晰。
- 单应用可以自由选择开发技术,不必受某些早起技术影响。甚至重构代码也相对简单。
- 服务单独部署,不需要协调其他服务部署。加快部署速度。
- 单服务对于性能消耗更明确,利于优化硬件资源配置。
缺点:
- 微服务应用是分布式系统,由此带来固有复杂性
- 通讯机制:RPC或消息传递
- 通讯速度慢
- 服务不可用
- 通讯超时
- 通讯机制:RPC或消息传递
- CAP原则:分布式事务一致性,业务实现复杂度提高
- 集成复杂:任何吃滴的分解都将带来集成的复杂度
- 部署:部署节点多,还涉及部署后的配置、拓展、监控问题
使用API网关构建微服务
客户端与微服务交互方式。
单体应用只需要发起一次请求,就能拿到所有需要的信息;
微服务需要发起多次请求,调用不同微服务提供的API。
发现问题:
- 客户端需求和每个微服务暴露的细粒度API不匹配
- 部分服务使用的协议对web并不友好
- 重构困难:系统拆分、合并,涉及外部改造。困难重重
使用API网关的架构:
API网关职责:请求路由、组合、协议转换
- 外部调用API,网关调用多个微服务并合并结果
- 处理内部服务,与外部调用协议不匹配问题
- 根据外部不同设备,设计不同API
优点:
- 封装了应用程序内部,简化外部调用
缺点:
- 增加了一个必须开发、部署、维护的高可用组件
- API网关变成了开发瓶颈
实现API网关
- 性能和可拓展性
- 构建在一个支持异步、I/O非阻塞的平台
- JVM上的NIO框架,例如:netty、vertx、spring reactor等
- node.js
- 另一种方法,使用nginx plus提供的成熟的、可拓展的、高性能web服务器和一个易于部署的、可配置、可编程的反向代理
- 身份验证
- 访问控制
- 负载均衡
- 缓存响应
- 提供程序可感知的健康检查、监控
- 构建在一个支持异步、I/O非阻塞的平台
- 使用响应式编程模型
- 独立请求
- 组合请求:代码复杂
- 响应式抽象概念的例子有 Scala 中的 Future、Java 8 中的 CompletableFuture 和 JavaScript 中的Promise,还有最初微软为 .NET 平台开发的 Reactive Extensions(RX)。
- 服务调用:多协议支持
- 异步,基于消息传递的机制==响应可能非即时==
- jms
- amqp
- 同步通信==由于等待而阻塞==
- http
- thrift
- 异步,基于消息传递的机制==响应可能非即时==
- 服务发现:应用程序服务的位置是动态分配的,而且,单个服务的一组实例也会随着自动扩展或升级而动态变化。
- 服务端发现
- 客户端发现
- 服务版本问题:
- 特别是RestAPI调用,由于json本身无Schema返回,更容易忽视对服务的版本控制
- 小版本变更,直接覆盖。升级受影响消费端
- 大版本升级,则视为增加一个服务;逐步迁移和替代旧版本服务
- 特别是RestAPI调用,由于json本身无Schema返回,更容易忽视对服务的版本控制
- 处理局部失败:Netfilix的服务解决方案
- 超时设置
- 断路器机制
- 流量控制
- 缓存数据或默认返回值
- 最终目的:减少对最终用户影响
进程间通信
一对一 | 一对多 | |
---|---|---|
同步 | 请求/响应 | |
异步 | 通知(客户端不期望服务端响应) | 发布/订阅(通知消息) |
异步 | 请求/异步响应(客户端不阻塞,默认响应不会立即到达) | 发布/异步响应(请求消息,等待从感兴趣的服务发回响应) |
REST包含以下四个层次:
- Level 0:本层级的 Web 服务只是使用 HTTP 作为传输方式,实际上只是远程方法调用(RPC)的一种具体形式。SOAP 和 XML-RPC 都属于此类。
- Level 1:Level 1 层级的 API 引入了资源的概念。要执行对资源的操作,客户端发出指定要执行的操作和任何参数的
POST
请求。 - Level 2:Level 2 层级的 API 使用 HTTP 语法来执行操作,譬如
GET
表示获取、POST
表示创建、PUT
表示更新。如有必要,请求参数和主体指定操作的参数。这能够让服务影响 web 基础设施服务,如缓存GET
请求。 - Level 3:Level 3 层级的 API 基于 HATEOAS(Hypertext As The Engine Of Application State)原则设计,基本思想是在由
GET
请求返回的资源信息中包含链接,这些链接能够执行该资源允许的操作。例如,客户端通过订单资源中包含的链接取消某一订单,GET
请求被发送去获取该订单。HATEOAS 的优点包括无需在客户端代码中写入硬链接的 URL。此外,由于资源信息中包含可允许操作的链接,客户端无需猜测在资源的当前状态下执行何种操作。
优点:
- HTTP 非常简单并且大家都很熟悉。
- 可以使用浏览器扩展(比如 Postman)或者 curl 之类的命令行来测试 API。
- 内置支持请求/响应模式的通信。
- HTTP 对防火墙友好。
- 不需要中间代理,简化了系统架构。
缺点:
- 只支持请求/响应模式交互。尽管可以使用 HTTP 通知,但是服务端必须一直发送 HTTP 响应。
- 由于客户端和服务端直接通信(没有代理或者缓冲机制),在交互期间必须都保持在线。
- 客户端必须知道每个服务实例的 URL。如前篇文章“API 网关”所述,这也是个烦人的问题。客户端必须使用服务实例发现机制。
Thrift
服务发现的可行方案以及实践案例
客户端发现模式:
Netflix OSS 是客户端发现模式的绝佳范例。Netflix Eureka 是一个服务注册表,为服务实例注册管理和查询可用实例提供了 REST API 接口。Netflix Ribbon 是 IPC 客户端,与 Eureka 一起实现对请求的负载均衡。我们会在后面深入讨论 Eureka。
优点:
- 除了服务注册,其他部分无需变动
- 能针对特定应用实现负载均衡
缺点:
- 客服端与服务注册绑定,要针对服务端用到的每个编程语言和框架,实现发现服务逻辑
服务端发现模式
HTTP 服务器与类似 NGINX PLUS 和 NGINX 这样的负载均衡起也能用作服务端的发现均衡器。Graham Jenson 的 Scalable Architecture DR CoN: Docker, Registrator, Consul, Consul Template and Nginx一文就描述如何使用 Consul Template 来动态配置 NGINX 反向代理。Consul Template 定期从 Consul Template 注册表中的配置数据中生成配置文件;文件发生更改即运行任意命令。在这篇文章中,Consul Template 生成 nginx.conf 文件,用于配置反向代理,然后运行命令,告诉 NGINX 重新加载配置文件。在更复杂的实现中,需要使用 HTTP API 或DNS 来动态配置 NGINX Plus。
———————–未完待续————————-