微服务设计

微服务设计

微服务

  • 微服务是协同工作的小而自治的服务
  • 微服务架构是一种将单个应用程序作为一套小型服务开发的方法,每种应用程序都在自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。这些服务是围绕业务功能构建的,可以通过全自动部署机制独立部署。这些服务的集中管理最少,可以用不同的编程语言编写,并使用不同的数据存储技术。

特点

  • 很小:专注于做好一件事。一件事:内聚,单一职责原则,业务的边界即服务的边界
    • 协同:服务之间通过进程间通信的方式进行调用,服务之间通过api通信。一个进程会暴露几个api。
  • 自治:可以独立地部署在PaaS平台上,也可以作为一个操作系统的进程存在
    • 解耦: 黄金法则-修改一个服务进行部署,不影响其他服务

主要好处

  • 技术异构性:不同的团队可以选择最适合自己的编程语言及数据存储机制
  • 弹性:舱壁,系统里的一个组件不可用了,其它的组件还可以继续工作
  • 扩展:针对需要扩展的微服务进行扩展
  • 简化部署:各个服务的部署是独立的,可以更快地针对特定部分的代码进行部署
  • 与组织结构相匹配:避免出现大的代码库,从而获得理想的团队大小和生产力
  • 可组合性:服务的可重用性
  • 对可替代性的优化:对几百行代码的微服务可以不带任何感情地删除、优化

面向服务的架构(SOA)

SOA(Service Oriented Architecture)“面向服务的架构” 是一种设计方法,其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在与操作系统进程中。各个服务之间 通过网络调用。

微服务与SOA区别

  • SOA强调按水平架构划分为:前、后端、数据库、测试等
  • 微服务强调按垂直架构划分,按业务能力划分,每个服务完成一种特定的功能,服务即产品
  • SOA将组件以library的方式和应用部署在同一个进程中运行
  • 微服务则是各个服务独立运行
  • SOA架构强调的是异构系统之间的通信和解耦合;(一种粗粒度、松耦合的服务架构)
  • 微服务架构强调的是系统按业务边界做细粒度的拆分和部署

其他分解技术

  • 共享库
  • 模块

演进式架构师

系统的成功靠不断的取舍实现

架构师的职责

  • 技术愿景、技术领导者,带领团队交付客户想要的系统
  • 从更高层次思考,权衡利弊,作出抉择

架构师的演化视角

  • 软件是持续演化的,不是一成不变的。
  • 对于架构师来讲,不要想着一开始就设计出完美产品,而是要设计一个合理的框架,在这个框架下可以逐渐演化出客户想要的系统。
  • 系统不但要满足当前的需要,还能够应对将来的变化

微服务架构师关注点

  • 应该关注

    • 分区:划分服务的边界,确定服务的粒度
    • 区域间的交互:不同的服务之间是如何交互,制定统一的接口,比如REST接口
    • 对整个系统的健康进行监控
    • 编码:架构师需要花时间和开发工程师在一起,理想状况下应该一起编码
  • 不应该关注

    • 过多关注每个区域内发生的事情:每个服务内部可以允许团队自己选择不同的技术栈或者数据存储技术

微服务架构的目标、原则与实践

业务部门的目标

  • 确保技术和业务层面能保持一致
  • 区分约束和原则:约束不能改变的,而原则是可以改变的。
  • 原则和实践相结合

实践

  • 保证原则得到实施;
  • 指导如何完成任务;
  • 是和技术相关的;
  • 比较底层的,例如:代码规范、日志数据格式、http/rest作为标准集成风格,服务部署在不同的aws账户中等。

微服务架构的要求

  • 监控
    • 原则:清晰描绘出跨服务系统的健康状态
    • 实践:每个服务主动把标准化的数据推送到一个集中的位置
  • 接口
    • 原则:选用少数几种明确的接口
    • 实践:使用一种标准的接口
  • 架构安全性
    • 原则:保证每个服务都可以应对下游服务的错误请求
    • 实践:
      • 每个下游服务使用自己的连接池
      • 返回码遵守一定的规则:正常且被正确处理的请求;错误请求;被访问的服务宕机,无法判断请求是否正常
  • 代码治理
    • 原则:使用简单的方式把事情做对
    • 实践:
      • 范例:代码范例
      • 服务代码模板:实现一个新服务时,所用实现核心属性的那些代码都是现成的,即基于模板的

如何建模服务

如何确定服务边界?

什么样的服务是好服务

  • 松耦合:服务之间的做到了松耦合,则一个服务的修改不会影响到另一个服务
  • 高内聚:相关的行为聚合在一起

限界上下文

  • 限界上下文: 一部分不需要与外部通信,一部分需要

模块和服务

模块边界是微服务的选择,将其作为一个单独的服务

集成

  • 避免破坏性修改:修改一个服务不会导致该服务的消费方随之发生改变。如:一个微服务在响应中添加一个字段,已有的消费方不会受到影响
  • 保证API的技术无关性
  • 易于使用、隐藏细节: 消费方可以用任何技术实现。服务内部实现细节对消费方不可见
  • 为用户创建接口
  • 共享数据库
  • 同步与异步
    • 同步是堵塞
      • 可以知道调用成功与否
      • 技术实现简单
    • 异步非堵塞
      • 基于事件的协作方式可以分布处理逻辑,低耦合
      • 处理异步通信的技术相对复杂
  • 编排与协同
    • 编排:使用某个中心大脑来指导并驱动整个流程,编排导致中心节点,系统紧耦合
    • 协同:告知系统中各个部分各自的职责,具体怎么做的细节留给自己,协同降低系统耦合度
  • 远程系统调用
    • 使用RPC
  • REST
    • HTTP: RESTful
  • 版本管理
    • 语义化版本管理
    • 不同的接口共存,同时使用多个版本的服务

分解单块系统

如何把单块系统循序渐进地分解?

  • 关键是接缝

    • 接缝:指程序中的一些特殊的点,在这些点上你无需作任何修改就可以达到改动程序行为的目的。
    • 接缝可以成为服务的边界
  • 从哪里下手

    • 把哪部分代码抽取出来收益最大,而不是为了抽取而抽取。
    • 要考虑的因素
      • 改变的速度:抽离成一个可以自治的服务后,后期开发的速度将大大加快
      • 团队结构:将异地开发的代码独立出来,由异地团队独立负责
      • 安全:敏感的信息独立出来,单独做监控
      • 技术:新的语言

部署

  • 持续集成(Continuous integration:CI): 频繁地(一天多次)将代码集成到主干。
    • CI能够保证新提交的代码和已有代码进行集成,从而让所有人保持同步。CI服务器会检测到代码已经提交并签出,然后验证代码是否通过编译以及测试能否通过

测试

  • 接受自动化文化:自动化测试

监控

监控分两类:服务监控与系统监控

  • 服务监控:
    • 对每个服务而言,最低需要监控的有:请求响应时间,错误率,下游服务的健康状况,下游服务的响应时间与错误率。标准化收集指标以及存储指标。
  • 系统监控:
    • 标准化关联标识,能够对整个请求链路做监控。比如Zipkin和Jaeger
    • 聚合CPU之类的主机指标。
    • 使用可查询工具来对日志进行聚合和存储。比如Kibana ElasticSearch

安全

  • 身份认证与授权,相关技术:SSO与OpenID
  • 服务间的身份认证与授权:
    • 隐式信任,即边界内允许一切
    • 验证调用方身份
    • 要求调用方提供原始主体的凭证
  • 静态数据的安全
    • 尽量使用众所周知的算法进行加密,比如AES-128/AES-256,还有加盐密码哈希;以确保加密算法的安全性
    • 对于数据存储与备份也需要进行加密
  • 深度防御
    • 日志:剔除敏感信息
    • 防火墙
    • 入侵检测
    • 网络隔离
    • 操作系统,定期打补丁

## 康威定律和系统设计

康威定律:任何组织在设计一套系统时,所交付的设计方案在结构上与该组织的沟通结构保持一致

微服务规模化运作

在分布式架构中,故障无处不在。本身网络就是不可靠的,所以故障难以避免。而且在规模化后,故障也成了必然事件。针对不可避免的故障,不要试图浪费太多的时间,而是应该花更多的时间去优雅的处理它们

架构性安全措施

  • 设置超时: 调用下游的超时时间
  • 断路器:调用下游失败率达到一定阈值,则自动将下游屏蔽
  • 舱壁:将自己从故障中隔离开
  • 隔离:上游服务允许下游服务离线

幂等

执行多次产生的影响,应该与执行一次产生的影响相同

系统扩展

  • 更强大的主机
  • 拆分负载:将功能拆分,更加细化,强调功能拆分
  • 分散风险:将服务部署在不同的IDC
  • 负载均衡:将请求分发到一个或者多个实例中
  • 基于Worker系统: 类似负载均衡
  • 重新设计:设计系统应该考虑10倍容量的增长,但超过100倍容量时就要重写了

伸缩数据库

  • 读伸缩:主从复制
  • 写伸缩:分片
  • 选择NoSQL

缓存

  • 三种缓存

    • 客户端缓存,即浏览器缓存,受HTTP缓存相参数的控制:cache-control,Expires,Entity Tags
    • 代理缓存,专门服务器设置在实际源服务前面,如:Squid, Varnish, Apache Traffic Server
    • 服务端缓存,比如Redis,Memcache
  • 问题

    • 隐藏源服务,大量缓存失效时,大量的请求会被放送到源服务,可能会导致源服务宕掉
      • 解决方案是:在缓存失效时,可以让原始请求快速失败,向源服务推送异步消息,由后台重建缓存
    • 缓存中毒,在Expires:Nerver情况下,缓存永远不会失效,直到浏览器缓存已满或者用户手动清理它们
      • 解决方案是:改变这些网页的URL,以便重新获取它们。

CAP理论

  • 即一致性(Consistency)、可用性(Availability)、分区容忍性(partition tolerance)只能保证其中两个。因此,分布式系统中每一个功能,只能在一致性与可用性中保证一个。
  • AP:会存在过时数据,但是可以保证可用性与最终一致性
  • CP:强一致性,快速失败,牺牲可用性

服务发现与服务注册

  • 相关技术:DNS,zookeeper, consul, eureka

文档服务

  • 相关技术:Swagger, HAL

总结

什么是微服务

就是一些协同工作的小而自治的服务。

  • 很小,专注于做好一件事:根据业务的边界来确定服务的边界。
  • 自治性:一个微服务就是一个独立的实体。服务之间均通过网络调用进行通信,从而加强了服务之间的隔离性,避免紧耦合。这些服务应该可以彼此独立进行修改,并且某一个服务的部署不应该引起该服务消费方的变动

微服务的主要好处

  • 技术异构性:可以采用不同的技术栈、语言、数据库或者框架。
  • 弹性:弹性工程学的一个关键概念是舱壁。如果系统中的一个组件不可用了,但并没有导致级联故障,那么系统的其他部分还可以正常运行。
  • 扩展:使用较小的多个服务,则可以只对需要扩展的服务进行扩展,这样就可以把那些不需要扩展的服务运行在更小的、性能稍差的硬件上。
  • 简化部署:在微服务架构中,各个服务的部署是独立的,这样就可以更快地对特定部分的代码进行部署。
  • 与组织结构相匹配:微服务架构可以很好地将架构与组织结构相匹配,避免出现过大的代码库,从而获得理想的团队大小及生产力。
  • 可组合性:可以根据不同的目的组合不同粒度的服务为客户提供功能。

微服务的原则

  • 围绕业务概念建模
    • 经验表明,围绕业务的界限上下文定义的接口,比围绕技术概念定义的接口更加稳定。针对系统如何工作这个领域进行建模,不仅可以帮助我们形成更稳定的接口,也能确保我们能够更好地反映业务流程的变化。使用界限上下文来定义可能的领域边界。
  • 接受自动化文化
    • 微服务引入了很多复杂性,其汇总的关键部分是,我们不得不管理大量的服务。解决这个问题的一个关键方法是,拥抱自动化文化。前期花费一定的成本,构建支持微服务的工具是很有意义的。自动化测试必不可少,因为相比单块系统,确保我们大量的服务能正常工作是一个更复杂的过程。调用一个统一的命令行,以相同的方式把系统部署到各个环境是一个很有用的实践,这也是采用持续交付对每次提交后的产品质量进行快速反馈的一个关键部分。
    • 考虑使用环境定义来帮助你明确不同环境间的差异,但同时保持使用统一的方式进行部署的能力。考虑创建自定义镜像来加快部署,并且创建全自动化不可变服务器,这会更容易定位系统本身的问题。
  • 隐藏内部实现细节
    • 为了使一个服务独立于其他服务,最大化独自演化的能力,隐藏实现细节至关重要。限界上下文建模在这方面可以提供帮助,因为它可以帮助我们关注哪些模型应该共享,哪些应该隐藏。服务还应该隐藏它们的数据库,以免陷入数据库耦合,这在传统的面向服务的架构中也是最常见的一种耦合类型。使用数据泵或事件数据泵,将跨多个服务的数据整合到一起, 以实现报表的功能。
    • 在可能的情况下,尽量选择与技术无关的API。这能让你自由地选择使用不同的技术栈。请考虑使用REST,它将内部和外部的实现细节分离方式规范化,即使是使用RPC,你仍然可以采用这些想法。
  • 让一切都去中心化
    • 为了最大化微服务能带来的自治性,我们需要持续寻找机会,给拥有服务的团队委派决策和控制权。在这个过程初期,只要有可能,就尝试使用资源自助服务,允许人们按需部署软件,使开发和测试尽可能简单,并且避免让独立的团队来做这些事。 在这个过程中,确保团队保持对服务的所有权是重要的一步,理想情况下,甚至可以让团队自己决定什么时候让那些更改上线。使用内部开源模式,确保人们可以更改其他团队拥有的服务,不过请注意,实现这种模式需要很多的工作量。让团队与组织保持一致,从而使康威定律起作用,并帮助正在构建面向业务服务的团队,让他们成为其构建的业务领域的专家。一些全局的引导是必要的,尝试使用共同治理模型,使团队的每个成员共同对系统技术愿景的演化负责。
    • 像企业服务总线或服务编配系统这样的方案,会导致业务逻辑的中心化和哑服务,应该避免使用它们。使用协同来代替编排或哑中间件,使用智能端点确保相关的逻辑和数据,在服务限界内能保持服务的内聚性。
  • 可独立部署
    • 我们应该始终努力确保为服务可以独立部署。甚至当需要做不兼容更改时,我们也应该同时提供新旧两个版本,允许消费者慢慢迁移到新版本。这能够帮助我们加快新功能的发布速度。拥有这些微服务的团队,也能越来越具有自治性,因为他们不需要在部署过程中不断的做编配。
    • 当使用基于RPC的集成时,避免使用像Java RMI提供的那种使用生成的桩代码,紧密绑定客户端/服务器的技术。
    • 通过采用单服务单主机模式,可以减少部署一个服务引发的副作用,比如影响另一个完全不相干的服务。请考虑使用蓝/绿部署或金丝雀部署技术,区分部署和发布,降低发布出错的风险。使用消费者驱动的契约测试,在破坏性的更改发生前捕捉它们。
    • 请记住,你可以更改单个服务,然后把它部署到生产环境,无需联动地部署其他任何服务,这应该是常态,而不是例外。你的消费者应该自己决定何时更新,你需要适应他们。
      *隔离失败
    • 微服务架构能比单块架构更具弹性,前提是我们了解系统的故障模式,并做出相应的计划。如果我们不考虑调用下游可能会失败的事实,系统会遭受灾难性的级联故障,系统也会比以前更脆弱。
    • 当使用网络调用时,不要像使用本地调用那样处理远程调用,因为这样会隐藏不同的故障模式。所以确保使用的客户端库,没有对远程调用进行过度的抽象。
    • 如果我们的心中有反脆弱的信条,预期在任何地方都会发生故障,这说明我们正走在正确的路上。请确保正确设置你的超时,了解何时及如何使用舱壁和断路器,来限制故障组件的连带影响。如果系统只有一部分行为不正常,要了解其对用户的影响。知道网络分区可能意味着什么,以及在特定情况下牺牲可用性或一致性是否是正确的决定。
  • 高度可观察
    • 我们不能依靠观察单一服务实例,或一台服务器的行为,来看系统是否运行正常。相反,我们需要从整体上看待正在发生的事情。通过注入合成事务到你的系统,模拟真实用户的行为,从而使用语义监控来查看系统是否运行正常。聚合你的日志和数据,这样当你遇到问题时,就可以深入分析原因。而当需要重现令人讨厌的问题,或仅仅查看你的系统在生产环境是如何交互时,关联标识可以帮助你跟踪系统间的调用。

对于微服务的建议

  • 考虑首先先构建单块系统,当稳定后再进行拆分
  • 你越不了解一个领域,为服务找到合适的限界上下文就越难。服务的界限划分错误,可能导致不得不频繁地更改服务间的协作,而这种更改成本很高。所以,如果你不了解一个单块系统领域的话,在划分服务之前,第一件事是花一些时间了解系统是做什么的,然后尝试识别出清晰的模块边界。另外,对已有的东西进行分类,要比对不存在的东西进行分类要容易得多。

演进式架构理念

  • 微服务架构会给你带来更多的选择,也需要你做更多的决策。相比简单的单块系统,在微服务的世界中,做决策是一个更为常见的活动。你一定会在一些决策上出错,所以尽量缩小每个决策的影响范围。即使错了,也只会影响系统的一小部分。学会拥抱演进式架构的概念,在这种概念下,系统会在你学到一些新东西之后扩展和变化。不要去想大爆炸式的重写,取而代之的是随着时间的推移,逐步对系统进行一系列更改,这样做可以保持系统的灵活性。
  • 同样很重要的一点:微服务不是银弹。

你可能感兴趣的:(微服务设计)