微服务架构的两大解耦利器与最佳实践

这几年,微服务架构这个术语渐成热门词汇,但它不是一个全新架构,更不是一个包治百病的架构。那么,微服务架构究竟可以解决什么问题,又带来哪些痛点?
nginx

本文将与你们谈谈这个问题,以及微服务架构的两大解耦利器配置中心和消息总线的最佳实践。web

微服务架构解决的问题与带来的痛点数据库


后端

互联网高可用架构为何要服务化?浏览器


上图是互联网典型的高可用架构,大部分公司若是没有使用微服务,正在使用这样的架构:缓存

  • 用户端是浏览器 browser,APP 客户端服务器

  • 后端入口是高可用的 nginx 集群,用于作反向代理微信

  • 中间核心是高可用的 web-server 集群,研发工程师主要在这一层进行编码工做网络

  • 后端存储是高可用的 db 集群,数据存储在这一层。更典型的公司,web-server 层是经过 DAO/ORM 等技术来访问数据库。架构


最初的架构都没有服务层,这样的架构会遇到怎样的痛点?对于没有使用微服务架构的公司来讲,要不要升级到微服务架构呢?


58 同城和 58 到家的架构痛点


回答这个问题以前,先来看看您是否遇到和 58 同城及 58 到家相似的架构痛点:


图一,代码拷贝。A、B、C   业务线,若是没有微服务架构,可能要直接访问数据库里的数据来实现本身的业务需求。拿访问用户数据举例,用户中心包括全部公司必备的业务,好比登录、注册、查找用户信息等。如某业务线须要访问用户信息,须要经过封装用户访问代码模块实现。如业务繁多,每一个业务线都须要访问用户信息,潜在的会存在代码拷贝问题。


图二,底层复杂性扩散。随着流量的增加,须要加入缓存,对数据的访问模式和流程都会带来影响。从直接访问数据库,变到先访问缓存再访问数据库。这样的复杂性,全部的业务都需关注,代码都要从新作一遍。包括数据量增大后,要进行的水平线切分、分库、分表,存储引擎的变化等复杂性,要扩展到业务线。


图三,代码库耦合。58 同城遇到图一和图二问题,最初想到的方案并非微服务,而是将相互拷贝的复杂性代码封装到一个代码库(DLL 或 jar 包),实现统一的相关功能,屏蔽复杂性。

拷贝代码的好处是代码独立演化,作改动互不影响。弊端是一旦用上库,业务就会耦合在一块儿,因共用jar包,一旦其中某个业务升级,其余的业务就可能受影响。


图四,数据库耦合。业务线不仅访问 user 数据,还会结合本身的业务访问本身的数据:典型的状况是经过 join 数据表来实现各自业务线的一些业务逻辑。这样的话:

  • 业务线 A 的 table-user 与 table-A 耦合在了一块儿;

  • 业务线 B 的 table-user 与 table-B 耦合在了一块儿;

  • 业务线 C 的 table-user 与 table-C 耦合在了一块儿;


结果就是:table-user,table-A,table-B,table-C都耦合在了一块儿。随着数据量的愈来愈大,业务线 ABC 数据库没法进行垂直拆分,必须使用一个大库(疯了,一个大库 300 多个业务表 =_=)。


图五,SQL 质量得不到保证,业务之间互相影响。由业务方拼装的 SQL 语句调用方式,经过 ORM(对象关系映射)的方法生成 SQL 语句数据库,这个库是共用的,会影响全部的业务线。一旦某业务有慢 SQL 出现,其余业务就会受影响。


回到要不要作微服务升级的问题,若是你们所负责的系统、模块或公司也存在以上的这些问题,建议考虑作服务化,在中间加一个服务层,全部调用不容许直接链接底层库。服务化还有一个很重要的特色就是数据库私有化,任何人不能跨越服务程序,干预数据库。想调用要经过接口来实现,当数据库性能变差,直接加一台机器,把数据库迁移,对调用方不会产生影响。


服务化解决了哪些问题


在 58 同城,用户中心由专门的部门负责,是全公司、全业务依赖比较重的服务,它对代码要求和稳定性要求比较高。整个 SQL 语句是服务层控制,向上提供有限的服务接口和无限的性能

工程师要保障虽然提供用户基础数据的接口数是有限的,但调用方不须要关心底层细节,能够认为性能是无限的。至于如何扩容,就是服务层的事情了。

下图是互联网典型的服务化架构。以用户中心为例,用户中心服务向上屏蔽底层技术的复杂性,上层经过 RPC 接口来调用服务,如同调用本地函数同样,不须要关注分库、分表、缓存。

业务方须要数据,把数据拼装出来返回 APP/PC 端便可,能够不关心数据存在哪里,底层的复杂性也由用户层来承担。这样一来,用户库只有用户服务依赖,任何人不得跨越用户服务来直接调用数据库,就不会存在代码拷贝、代码库、数据库耦合的状况。


微服务架构的两大解耦利器

微服务虽然看上去很好,但也给系统带来不少问题,如部署方面,愈来愈复杂,分层愈来愈多,处理时间也随之增长。如网络交互方面,运维负载性、追查问题等等。那么:

  • 面对架构的耦合及复杂性如何来优化

  • 结构如何配置


接下来,咱们介绍配置中心最佳实践与消息总线最佳实践这两大解耦利器


微服务架构解耦利器-配置中心最佳实践


放弃 IP 链接服务,选择内网域名。58  到家是创业公司,痛点和不少公司都很类似。其中一个场景是 IP 的变化。最初,IP 写在配置文件中,经过某个 IP   或端口访问数据与服务。当某台机器出现问题,DB 同事会在新机器作部署,更换 IP。当某个服务或 IP 发生变化,就在配置文件中修改,重启。


这里的经验分享是千万不要用 IP 链接服务或数据库,要选择内网域名。这二者的区别在于:

  • 使用 IP 链接服务或数据库的方式,全部的库都和一个表有关联,一旦机器挂掉或升高配,几乎全部的业务都须要修改 IP。即使只是升级一个业务,都会严重影响其余业务。

  • 选择内网域名的方式后,若是换 IP,在运维层面能够进行统一切断,自动向上连接,上游的业务就不用动,也不受下层变更的影响。


配置私藏。以下图是 58 到家早期改为内网域名以后的配置文件。底层用户服务或数据库,是个高可用集群,从 IP1 到 IP3。上游有三个依赖,两个服务器,一个 Web 调用这个高可用集群。Web 包含 WBE2.conf,调用 IP1,IP2,IP3。

在实践过程当中,这种配置私藏的方式遇到两个痛点:


  1. 升级时不知道被那个服务调用。当遇到流量愈来愈大,须要添加服务器时,如上图,把  IP1 去掉,增长 IP4 和 IP5   的时候,须要通知上游。但问题在于流量不大时,由于对业务很是熟悉,工程师可以准确的找到服务器对应的负责人。随着业务愈来愈复杂,工程师遇到出现了问题,不知道模块被谁依赖的状况。

  2. 升级时须要上游配合重启。当增长 IP 时,须要找到对应的上游服务器负责人,通知他进行服务器重启。公司成百上千的服务天天都有人在升级,当时的作法是采用建群,随时作通知,但这样很影响研发同事写代码的效率。


全局配置。最开始底层的通用基础服务,配置是写在每一个站点;并且每一个应用私藏在配置文件里,在升级过程当中,不知道谁私藏了这个配置。

面对这两个痛点,58到家采用了下图的解决方案:全局配置

全局配置也就是升级,只须要作流程与规范上的优化,对原有系统架构不产生任何影响,成本低且可平滑的慢慢迁移。

下图的实现原理是把最初放在每一个服务器中的配置文件,抽取一个全局配置文件,作好目录结构  global.conf。全部基础服务配置若是由多个 global.conf 上游来读取,必须经过 global.conf   来读取。这样全部的业务都在 global.conf,就能够保障下一次升级可链接到最新。


那么,在作扩容的时候,能不能实现调用方不须要升级呢?固然能够,两个小组件就能够实现:

  • 监控全局文件的变化状况,发生变化就进行回调,这样用户中心要配置修改的是全局配置。

  • 动态连接池组件。这是一个自身及调整流程成本都很低的组件,负载均衡也会在其中实现。

配置中心。全局配置对于服务提供方而言,问题依然没有所有解决,扩容不须要重启,却仍不知道被谁依赖,不知道被谁访问,就没办法作服务治理、限流等操做。这时,工程师就要引入配置中心,来解决这个问题。

配置中心思路是部署用户中心承载全部配置,取代全部全局配置文件。这样一来,全部都依赖配置中心上游,服务1,服务2,服务3,都再也不访问global.conf,而是经过配置中心来拉取相关配置,配置变动,配置中心反向回调,调用方也不要重启。

配置中心最佳实践总结。配置中心是微服务架构中一个逻辑解耦但物理不解耦的利器。它原来在逻辑上依赖于本身的配置文件,依赖于下游,如今再也不向配置文件索要配置,而是全部调用方逻辑上只依赖于配置中心。物理上不解耦,是从配置文件拿到配置之后该连谁仍是连谁。


微服务架构解耦利器-消息总线最佳实践


消息总线(Message Queue),后文称 MQ,是一种跨进程的通讯机制,用于上下游传递消息。它也是微服务架构中很常见的解耦利器之一,在数据驱动的任务依赖、调用方不关注处理结果、关注结果的长时间调回等场景下使用。


数据驱动的任务依赖。大部分公司都有 BI、数据部门,天天都会跑一些日志、数据库,多个任务之间每每存在依赖关系,任务1先执行,依次是任务 二、任务 3 输入,最终获得结果。在没有消息总线以前,大多公司和58到家的作法雷同,就是人工排班表。


人工排班表的弊端以下

  1. 本来执行时间是40分钟,但为保险,每一个人都会多加时间,致使任务总执行时间延长。

  2. 万一某一任务的执行时间超过预留时间,接下来的任务不知情,会致使整个业务失败。

  3. 多个业务之间可能有多重依赖,特别是在数据统计、数据分析过程当中,一些核心脚本执行完,后面一系列脚本才能执行。


以下图,这种数据驱动的任务依赖很是适合使用MQ解耦。

  • task1准时开始,结束后发一个“task1 done”的消息

  • task2订阅“task1 done”的消息,收到消息后第一时间启动执行,结束后发一个“task2 done”的消息

  • task3同理


采用 MQ 的优势是:

  • 不须要预留 buffer,上游任务执行完,下游任务总会在第一时间被执行

  • 依赖多个任务,被多个任务依赖都很好处理,只须要订阅相关消息便可

  • 有任务执行时间变化,下游任务都不须要调整执行时间


须要特别说明的是,MQ 只用来传递上游任务执行完成的消息,并不用于传递真正的输入输出数据。

调用方不关注处理结果,这样的状况也适合消息总线来作解耦。举例,58  同城的不少下游须要关注“用户发布帖子”这个事件,好比招聘用户发布帖子后,招聘业务要奖励 58 豆;×××用户发布帖子后,×××业务要送 2   个置顶;二手用户发布帖子后,二手业务要修改用户统计数据。


对于这类需求,常见的实现方式是使用调用关系:帖子发布服务执行完成以后,调用下游招聘业务、×××业务、二手业务,来完成消息的通知,但事实上,这个通知是否正常、正确的执行,帖子发布服务根本不关注。


这种方法的痛点是:

  • 帖子发布流程的执行时间增长了

  • 下游服务宕机,可能致使帖子发布服务受影响,上下游逻辑+物理依赖严重

  • 每当增长一个须要知道“帖子发布成功”信息的下游,修改代码的是帖子发布服务,这一点是最恶心的,属于架构设计中典型的依赖倒转,谁用过谁痛谁知道(采用此法的请评论留言)


采用下图的优化方案:MQ解耦

  • 帖子发布成功后,向MQ发一个消息

  • 哪一个下游关注“帖子发布成功”的消息,主动去MQ订阅


采用 MQ 的优势是:

  • 上游执行时间短

  • 上下游逻辑+物理解耦,除了与 MQ 有物理链接,模块之间都不相互依赖

  • 新增一个下游消息关注方,上游不须要修改任何代码


上游关注执行结果,但执行时间很长。有时候上游须要关注执行结果,但执行结果时间很长(典型的是调用离线处理,或者跨公网调用),也常用回调网关+MQ来解耦。

举例:微信支付,跨公网调用微信的接口,执行时间会比较长,但调用方又很是关注执行结果,此时通常怎么玩呢?


通常采用“回调网关+MQ”方案来解耦,新增任何对微信支付的调用,都不须要修改代码。

  • 调用方直接跨公网调用微信接口
       

  • 微信返回调用成功,此时并不表明返回成功
       

  • 微信执行完成后,回调统一网关
       

  • 网关将返回结果通知 MQ
       

  • 请求方收到结果通知


这里须要注意的是,不该该由回调网关来调用上游来通知结果,若是是这样的话,每次新增调用方,回调网关都须要修改代码,仍然会反向依赖,使用回调网关+ MQ 的方案。


综上所述,两个解耦利器的最佳实践场景以下:


  • 配置中心是逻辑解耦,物理不解耦的微服务的利器。它能够解决配置致使的系统耦合,架构反向依赖的问题,配置中心的演进过程,配置私藏到全局配置文件,到配置中心。
       

  • 消息总线是逻辑上解耦,物理上也解耦的微服务架构利器。它很是适合数据驱动的任务依赖,调用方不关注处理结果,或者调用方关注处理结果,可是回调的时间很长的场景。不适合调用方强烈关注执行结果的场景。


以上内容根据沈剑老师在 WOTA2017 “微服务架构实践”专场的演讲内容整理。 


沈剑,现任58到家技术委员会主席,高级技术总监,负责企业、支付、营销和客户关系等多个后端业务部门。本质,技术人一枚。互联网架构技术专家,“架构师之路”公众号做者。曾任百度高级工程师,58同城高级架构师,58同城技术委员会主席,58同城C2C技术部负责人。


相关文章
相关标签/搜索