试读《分布式服务框架原理与实践》

微服务

概述


目前,微服务得到较多的关注:论文,博文,社交媒体上的讨论,还有会议报告。他们处于期望膨胀期的顶峰。同时,在软件社区还有一群怀疑论者,他们无视微服务,认为它没什么新意。反对派们声称,这种想法就是 SOA 的马甲。但是,不管是大肆宣传还是怀疑主义,微服务架构模式具有明显的好处——尤其谈到敏捷开发和复杂企业应用交付的时候。
微服务将应用程序分割成更小的相互关联的服务,而不是构建单个可怕的整体应用程序。一个服务通常实现一组独立的特性或功能,比如订单管理、客户管理等。某些微服务将会暴露供其他微服务或者客户端使用的API。另外一些微服务可能实现web UI。在运行时,每个实例通常是一个云虚拟机或者一个Docker容器。


微服务架构模式


微服务架构模式对应扩展立方体(Scale Cube)的 Y 轴扩展,扩展立方体是《The Art of Scalability》一书中描述可扩展性的 3D 模型。此外还有两个扩展维度,X 轴扩展表示在负载均衡器后面运行多个相同的应用程序副本,Z 轴扩展(数据分割)表示使用请求中的某个属性(例如数据表主键或用户 id)来路由请求到特定服务器。

微服务架构模式显著地影响了应用程序与数据库之间的关系。每个服务有自己的数据库模式,而不是共享单个数据库模式。尽管这种方式与企业级数据模型的 想法相悖,也会造成某些数据的冗余。但是如果你想获得微服务的好处,那么每个服务一个数据库模式是很关键的,因为这确保了松散耦合。每个服务有其自己的数据库。那么服务就可以选择使用最符合需求的数据库,这就是所谓的混合持久化架构。

表面上微服务架构模式类似于 SOA。这两种架构都包含一组服务。你可以认为微服务架构模式就是不包括商业化和 Web 服务规范(WS-)、企业服务总线(ESB)的 SOA。基于微服务的应用倾向于使用更简单轻量级的协议,比如 REST 而不是 WS-。很大程度上也避免使用 ESB,取而代之的是使用微服务自己实现类似 ESB 的功能。微服务架构模式也拒绝 SOA 的其他部分,比如规范模式的概念。


微服务架构模式的优势:


第一,它解决了复杂性的问题。它将一个可怕的、庞大的整体应用分解成一组服务。在整体的功能没有改变的同时,应用 程序已经被分解成可管理的模块或服务。每个服务有以 RPC 或者消息驱动 API 形式定义清楚的界限。微服务架构模式加强了一定程度的模块化,这在整体应用程序中是很难实现的。因此单个的服务可以更快的开发,更简单的理解和维护。

第二,这种架构使得每个服务可以由单独的团队独立开发,这些团队可以专注于某个服务。开发者可以自由地选择合理的技术,只要服务遵守 API 约定即可。当然大部分组织想要避免混乱地完全无限制的技术选项。然后这种自由意味着开发者不在受限于使用可能过时的技术开始新的项目。当开始写一个新服务 的时候,他们可以选择使用当前的技术。而且因为服务相对较小,所以使用当前的技术重写老服务是可行的。

第三,微服务架构模式使得每一个微服务能被独立部署。开发者再也不需要调整本地对其服务的更变 而进行部署。各种类型的变更能在他们测试时立即部署。UI 团队也可以这样做,举例来说,当 UI 发生改变时,能执行 A|B 测试并快速迭代。微服务架构模式让持续部署成为可能。

最后,微服务架构模式使得每一个服务都可以被独立扩展。你可以部署大量恰好符合要求容量和有效约束条件的服务实例。此外,你可以使用最匹配服务资源 要求的硬件。例如,你可以在计算优化过的 EC2上部署一个密集CPU 镜像处理服务实例,还可以在内存优化的 EC2 上部署内存数据库服务实例。


微服务架构模式的缺点:

跟别的技术一样,微服务架构也有缺点。其中的一个缺点就是名字本身。微服务这个词过分强调服务的规模。尽管规模小的服务更可取,但是最好记住这只是手段而不是目的。微服务的目的是充分地分解应用程序以促进敏捷开发和部署。

微服务另外一个主要的缺点是微服务应用做为分布式系统带来的复杂性。开发者需要选择或者实现基于消息或 RPC 的进程间通信机制。而且必须编写处理部分失败的代码,因为请求的目的地可能很慢或者不可用。虽然这都不是高深莫测的事情,但是相对于整体应用程序这明显更 复杂,因为整体应用程序中模块间的调用是通过语言层面的方法/程序调用实现的。

微服务的另外一个挑战是分割的数据库架构。更新多个业务实体的事务相当普遍。这种事务在整体应用程序中很容易实现,因为只有一个数据库。然后在基于微服务的应用中,你需要更新多个属于不同服务的数据库。分布式事务通常不是最好的选择,不仅仅因为 CAP 理论,而且目前许多高扩展性的 NoSQL 数据库和消息代理就不支持。你最终不得不使用基于最终一致性的方法,这对于开发者来说更具挑战性。

 

RPC框架


RPC(Remote Procedure Call,远程过程调用)框架是分布式服务的基石,实现RPC框架需要考虑方方面面。其对业务隐藏了底层通信过程(TCP/UDP、打包/解包、序列化/反序列化),使上层专注于功能实现;框架层面,提供各类可选架构(多进程/多线程/协程);应对设备故障(高负载/死机)、网络故障(拥塞/网络分化),在框架层面具备容灾措施。


RPC 框架面临的挑战


大家知道,RPC调用结果分成功、失败、超时三种状态,其中调用失败的一般原因是Server设备故障(connect失败,我们又称这类调用失败为系统失败),这时Client可以通过挑选其他Server,进行请求重试。超时的原因可能有多种,如网络拥塞、所选Server设备过载等,Client也可以通过重试尽量让请求得到处理。

系统失败、超时都可以通过重试尽量让请求得到处理,但重试次数并非越大越好,考虑Server整体服务过载的情形,过多重试可能引发后端Server雪崩。当某个Server服务过载,大量请求处理失败时,用户行为带来的重试、客户端/后台Server的重试带来翻倍的请求,进一步加重该Server的负担。调用量翻倍上涨,但Server有效输出接近零,这是雪崩的标志现象。主要有两种措施应对雪崩,一是通过扩容提升Server服务能力,二是通过柔性措施,丢弃一部分请求,并且应该尽量在调用链的前端丢弃。


RPC调用与本地调用的比较


    本地调用一定会执行,而远程调用则不一定,调用消息可能因为网络原因并未发送到服务方。
    本地调用只会抛出接口声明的异常,而远程调用还会跑出 RPC 框架运行时的其他异常。
    本地调用和远程调用的性能可能差距很大,这取决于 RPC 固有消耗所占的比重。

正是这些区别决定了使用 RPC 时需要更多考量。 当调用远程接口抛出异常时,异常可能是一个业务异常, 也可能是 RPC 框架抛出的运行时异常(如:网络中断等)。 业务异常表明服务方已经执行了调用,可能因为某些原因导致未能正常执行, 而 RPC 运行时异常则有可能服务方根本没有执行,对调用方而言的异常处理策略自然需要区分。

 

试读第8章:服务调用

 

试读章节从几个误区、调用方式、最佳实践对于服务调用做了阐述,详细讲解了几种常用的调用方式及使用场景,佐以实例解释了其中的一些误解。并在最后做了总结。例如:对延时要求不高的,可以考虑同步调用;调用链的关键服务不可靠,一旦出现故障会导致大量线程资源被挂住,可以考虑异步服务防止故障扩散。这些都是作者的实践经验之谈,配以实例之后,很有说服力,值得我们学习。

 

你可能感兴趣的:(试读书评)