微服务架构实践总结

jopen 5年前

    微服务架构是由Martin Fowler在他这篇microservices博客中提出来的,与之对立的是monolithic架构。

monolithic架构概念 vs. 微服务架构概念

    monolithic架构指的是应用被以单一单元构建。比如一个小型订餐网站包含菜品展示、下订单、在线支付等业务功能模块,该网站的后端系统应用实现了所有这些业务功能。

    而微服务架构则是由一组微服务组成的架构模式。每个微服务都是一个可独立部署的完整系统。一组微服务组成微服务层(注意这里的服务层不同于monolithic架构中的服务层,那个是单系统中的功能模块分层)。微服务层上面一般是应用层,应用层通过组合使用微服务层的各个微服务而向外提供接口(比如HTTP API接口)。各个微服务可以通过RPC接口供应用层调用,比如利用Thrift、Avro。

微服务拆分方法

    微服务架构中的微服务一般按照业务功能来拆分,将关联性较强的业务拆成一个微服务,比如上面订餐网站可以拆分成用户服务、订单服务、菜品服务、支付服务等。具体来说一般根据业务实体名词如订单、用户或者业务动作如登录、下载等来拆分。

数据集成 vs. 服务集成

    数据集成是一种比较传统的系统集成方式,其中心是数据。比如交易系统会操作订单表和用户表,比如生成订单。而短信通知系统也会访问订单表和用户表,根据订单的状态来发出不同的短信通知给用户:比如给金额较大的订单对应的用户发一些促销活动短信,给一些未完成订单的用户发短信提醒其完成订单。这就是一种数据集成方式,交易系统和短信通知系统依靠订单表和用户表进行集成。这种集成方式的优点在于实现简单,缺点有下面几条:

  • 业务数据库负担较大,因为多个系统都访问同一个数据库的同几张表

  • 安全性,交易系统可以修改订单表中的订单状态是理所当然的事,但由于短信通知系统也可以直接访问订单表,可能导致一些意想不到的问题

  • 扩展性问题:交易系统如果以后想把订单表从RDBMS迁到HBase可能就没那么容易了,因为还有很多其他系统也依赖于订单表,真可谓牵一发而动全身;或者各个系统可能擅自做主给表增减字段,都会带来不好后果

  • DAO代码重复,如果交易系统和短信通知系统由两个不同部门的团队开发,那么每个系统中都会有针对订单表和用户表的DAO代码

    服务集成则打破了数据集成模式,不再以数据为中心集成各系统。微服务架构天然就拥抱服务集成方式。如果用服务集成方式重新设计上面的交易系统和短信通知系统,那么就会提出两个微服务:用户微服务,负责对用户信息进行管理(具体底层是否真的操作关系数据库表,外部已经不关心,即外部不知道用户信息是存在MySQL、Redis还是二者都有,也不需要知道);和订单微服务,负责对订单信息进行管理。交易系统和短信通知系统都被提取到更上一层的应用层,他们分别调用用户微服务和订单微服务完成任务。这样用户数据状态的变化和订单数据状态的变化将不再同时受控于交易系统和短信通知系统,而只由用户微服务和订单微服务控制。

微服务与微服务之间相互调用

    一般避免微服务直接调用另外一个微服务,尤其忌讳两个微服务相互调用,如果存在两个微服务相互调用的场景,那么得慎重衡量下微服务拆分是否合理。

    当然某些情况无法避免微服务之间相互调用,这时候我们一般可以采用两种方式实现:

  • 消息队列

  • 消息总线

monolithic架构与微服务架构各自优缺点

    除了上面提到的一些优缺点外。下面再总结下两种架构的各自优缺点。

    首先看monolithic架构,它的优点是:部署方便,只需要部署一份代码。但它的缺点有很多:

  • 代码庞杂,理解困难,新人上手也困难

  • 维护困难,一般一个monolithic架构应用得由一个团队维护,如果应用越大,则维护的人越多,团队管理成本也越高,团队效率也会越低下(3-5人是最佳团队规模)

  • 启动一般较慢

  • 持续部署困难:每一次小改动都需要重新部署整个应用

  • 技术堆栈固化:尝试新技术的代价太高

  • 扩展困难:应用某些部分偏IO密集型、某些部分却偏CPU密集型,但应用却只部署在一台机器上,很难用单一硬件来满足应用各部分对硬件资源的不同要求

    微服务架构的优点自然与上面的缺点相反,列举三条:

  • 每个微服务功能简单,代码量也不多,新人上手容易

  • 每个微服务可以由不同团队开发,每个微服务一般由1-3人开发维护

  • 系统稳定性增强,单个服务的失效不会影响其他服务,可以一定程度实现服务降级

  • 容易尝试技术创新,甚至每个微服务都可以采用不同的编程语言编写,只要对外提供约定好的接口就行

    但微服务架构也有缺点,按照我个人理解的严重程度由高到低排列如下:

  • 运维成本高:原来一个应用一个进程,现在被拆分成微服务后可能有十几个进程,并且每个微服务都需要独立部署

  • 测试成本高:微服务架构带来的是一个分布式系统,分布式系统的测试比单系统测试更复杂

  • 微服务升级带来的接口不向后兼容问题

  • 每个微服务开发者都需要关注微服务开发、集成、测试、部署、上线完整流程

    为了应对微服务升级带来的接口不向后兼容问题,一般可以对接口升级做一些约定以保证接口向后兼容,假设微服务采用Thrift接口开放给应用层,那么约定如下:

一般接口升级只允许下面几种情况(这种情况能够保证Thrift服务端二进制代码向后兼容客户端,  即Thrift服务端由V1.0.9升级到V1.1.0,客户端依然可以使用V1.0.9 Thrift服务端对应的Stub  代码):  > 增加新接口方法或者新结构体(Struct)  > 在接口方法参数后面增加非必须参数  > 为结构体(Struct)增加非必须参数  > 修改返回值类型为void的接口方法,使其返回值类型为任意其他类型  > 删除接口方法签名中的throw  > 从结构体中删除field  > 重命名结构体  > 修改命名空间    不允许的情况包括:  > 删除或者重命名接口方法  > 修改非void返回值类型接口方法的返回值类型  > 修改接口方法参数类型  > 为非void返回值类型接口方法添加throw  > 通过Thrift接口开放给外部的系统在升级后必须升级pom.xml中的版本号,同时对应的inf接口    项目的版本号也要跟着升级


    全文完,转载请注明出处http://my.oschina.net/feichexia/blog/344190,谢谢!