微服务的优缺点

微服务设计思想优点

  • 单一进程 => 多进程: 服务自治性
    1. 部署不会影响其他服务
    2. 降低测试难度
    3. 方便性能评估与系统监控
  • 通过网络传输(RESTful API,消息总线等)进行服务间通信(也可以直接对外提供服务)
    1. 有利于多语言多技术生态
  • 大项目 => 小项目
    1. 减少代码冲突
    2. 方便维护(升级,重构,迁移)

微服务的限制

  • 完善的运维架构
  • 严格的接口一致性管理
  • 完善的服务状态监控
  • 分布式架构设计
  • 要真正发挥微服务的优势,需要完整的微服务架构体系
  • 如何拆服务是做好微服务的关键

微服务的缺陷

  • 太过复杂的多层次服务调用,会增加很多网络开销和传输时间,不利于性能
  • 只有合理的微服务设计,才能提高用户对性能的体验
  • 全局改动需要部署所有服务

服务拆分与集成

如何拆分服务

  • 专注于两个重要概念:松耦合,高内聚
  • 按照功能点划分服务
    1. 基础设施服务比如:数据库服务,缓存服务,用户信息服务等被所有服务使用
    2. 这样的拆分可以减少其它服务关于这些方面的开发工作,减少耦合方便服务更新,比如数据库替换不需要修改所有服务
    3. 增加服务调用层次带来的少许网络传输性能开销,如果服务部署在同一网络,10ms以内的http请求可以接受
  • 按照业务模块划分服务
    1. 业务之间松耦合,高内聚,不相互影响
    2. 对于业务模块划分过细的,业务模块过多的系统会产生过多的服务,消耗过多的资源,比如JVM自身所需的内存
    3. 对于太小的业务模块带来太微的服务,有时候你就会想要合并这些太小的/业务流程又相同的服务,但这样就违反了你松耦合,高内聚的思想,如何抉择?
  • 同一个模块按功能点划分
    1. 比如DataSource采集处理数据,Service提供数据给用户,Kafka消费者
    2. 这样划分有的时候是必要的,但有的时候显得太细
  • 参考领域界限上下文概念

服务集成

  • 基于事件的协作方式,可以有效减少耦合度
    1. 比如kafka
    2. 参考消息队列的好处与使用场景
  • 使用REST
    1. HTTP对REST有很好的支持
    2. HTTP适用于大流量通讯,但是性能比较低,信息尺寸大,请求频繁
    3. 低延迟通讯可以选择TCP协议,比如WebSockets,在初始的HTTP握手之后,就通过TCP连接
    4. UDP协议的尺寸相对小很多,性能要高一点
  • 使用客户端的RPC[远程过程调用]
    1. 使用客户端很方便,但是如果你将逻辑过多的放在客户端,就会增加耦合度
    2. 会迷惑使用者,不知道哪些调用实际上是远程调用
    3. 所以如果要使用客户端,保证其中只有底层传输协议相关代码
    4. Feign client做的很好,是一种基于REST API的客户端
  • DRY : 共享库
    1. 共享库的修改,需要重新部署其他所有的服务方或消费者,不同步的部署是灾难
    2. 但是如果把共享库复制到每个服务,那么虽然可以独立部署,但是有可能会增加维护成本
    3. 两者需要取舍

不好的做法

  • 不要共享数据库
    1. 共享数据库/表=再见!松耦合,高内聚
    2. 数据库的外建依赖可能要用代码维护
  • 不要做服务代劳
    1. 不好的做法: 服务A调用服务B获取信息,发送给服务C,这样会带来一致性问题
    2. 应该 :服务A通知服务C去调用服务B获取信息

服务变动

无论是哪种方式的服务接口,都需要注重一些原则

  • 开闭原则
  • Postel法则,宽进严出
  • 不要做破坏性修改
  • 测试所有客户端
  • 客户注册和通知

版本管理

  • 语义化版本管理
    1. MAJOR.MINOR.PATCH
    2. 向后不兼容.新增功能.兼容
  • 接口版本共存
    1. 一个好的RESTful API会在URL中包含版本信息
    2. 某个接口的不同版本同时存在,允许消费者逐步迁移
    3. 对V1的请求转向V2,转向V3
  • 服务版本共存
    1. Scooby的做法
    2. 应该只适用于特殊项目/情况

API跟踪

  • 如果系统加入了API跟踪功能,就可以在淘汰某些API之前通知用户
  • API跟踪还可以通过访问量优化API性能

需要寻找更好的应对服务变动的策略

服务聚合

什么是服务聚合

  • 当一个业务流程需要访问多个API接口,并且他们之间有先后顺序,如何提高性能
  • 比如用户访问页面需要先查看用户权限,再决定显示哪些内容,但是两次调用需要两次网络开销,如果调用者和服务再同一地区还好,如果相隔万里,那么网络开销就不容忽视
  • 服务聚合是通过将两次调用合并成一次解决此类问题

创建第三个接口整合多个接口的业务逻辑

  • 这种方式最直观,对原来的接口没有任何影响
  • 需要开发第三个接口,个别接口的聚合推荐方案,如果所有接口都需要和另一个接口聚合,那么你的系统接口数量将成倍增长,这就不合适了。
  • 需要合并参数与返回值

通过Filter进行请求调用

  • 拦截请求,在Filter中调用另一个请求,适合所有接口都需要和另一个接口聚合的情况
  • 需要合并参数与返回值,还需要提供一个参数来表示是否聚合,否则对原来接口造成侵入,强行耦合
  • 缺点是不够直观,你明明调用一个接口,却拥有了多个接口的功能
  • Filter需要做成共享库jar给其它服务使用,否则每个服务都需要这样的Filter
  • Spring Cloud可以在网关中使用Filter
    1. 无需共享库jar
    2. 缺点是必须通过网关调用才能聚合

创建一个公共的API进行请求调用

  • 如果你的项目并没有网关服务,你可以自己创建一个公共的API,模拟网关功能
  • 调用者需要在请求中提供至少三个请求路径与参数,属于野路子方案