从 Spring Cloud 开始,聊聊微服务架构实践之路
背景
随着公司业务量的飞速发展,平台面临的挑战已经远远大于业务,需求量不断增加,技术人员数量增加,面临的复杂度也大大增加。在这个背景下,平台的技术架构也完成了从传统的单体应用到微服务化的演进。
系统架构的演进过程
单一应用架构(第一代架构)
这是平台最开始的情况,当时流量小,为了节约成本,并将所有应用都打包放到一个应用里面,采用的架构为.net+sqlserver:
表示层位于最外层(最上层),最接近用户。用于显示数据和接收用户输入的数 据,为用户提供一种交互式操作的界面,平台所使用的是基于.net的web形式。业务逻辑层业务逻辑层(Business Logic Layer)无疑是系统架构中体现核心价值的部分。它的关注点主要集中在业务规则的制定、业务流程的实现等与业务需求有关的系统设计,也即是说它是与系统所应对的领域(Domain)逻辑有关,很多时候,也将业务逻辑层称为领域层。业务逻辑层在体系架构中的位置很关键,它处于数据访问层与表示层中间,起到了数据交换中承上启下的作用。由于层是一种弱耦合结构,层与层之间的依赖是向下的,底层对于上层而言是“无知”的,改变上层的设计对于其调用的底层而言没有任何影响。如果在分层设计时,遵循了面向接口设计的思想,那么这种向下的依赖也应该是一种弱依赖关系。对于数据访问层而言,它是调用者;对于表示层而言,它却是被调用者。数据层数据访问层:有时候也称为是持久层,其功能主要是负责数据库的访问,可以访问数据库系统、二进制文件、文本文档或是XML文档,平台在这个阶段使用的是hibernate.net+sqlserver。
第一代架构看似很简单,却支撑了平台的早期业务发展,满足了网站用户访问量在几万规模的处理需求。但是当用户访问量呈现大规模增长,问题就暴露出来了:
维护成本不断增高:当出现故障时,有可能引起故障的原因组合就会比较多,这也会导致分析故障、定位故障、修复故障的成本相应增高,故障的平均修复周期会花费很多时间,并且任何一个模块出现故障将会影响其它应用模块;在开发人员对全局功能缺乏深度理解的情况下,修复一个故障,经常引入其他的故障,导致该过程陷入“修复越多,故障越多”的恶性循环。
可伸缩性差:应用程序的所有功能代码都运行在同一个服务器上,将会导致应用程序的水平扩展非常困难,只能使用垂直扩展。
交付周期变长:应用程序做任何细微的修改以及代码提交,都会触发对整个应用程序进行代码编译、运行单元测试、代码检查、构建并生成部署包、验证功能等,这也就版本的反馈周期变长,单位时间内构建的效率变得很低。
新人培养周期变长:随着应用程序的功能越来越多,代码变得越来越复杂的同时,对于新加入团队的成员而言,了解业务背景、熟悉应用程序、配置本地开发环境,这些看似简单的任务,却会花费了更长的时间。
垂直应用架构(第二代架构)
为了解决第一代架构面临的问题,团队制定了如下的策略,并形成了第二代应用架构(垂直应用架构)
应用拆成独立的应用模块。
各个应用模块独立部署,并在负载均衡通过 session 保持解决应用模块的水平扩展问题。
Sticky 就是基于 cookie 的一种负载均衡解决方案,通过cookie实现客户端与后端服务器的会话保持, 在一定条件下可以保证同一个客户端访问的都是同一个后端服务器。请求来了,服务器发个cookie,并说:下次来带上,直接来找我!。在项目中,我们使用了taobao开源的tengine中的session_sticky模块。
数据库拆分成不同数据库,由对应应用访问。
域名拆分。
动静分离。
可以看到第二代架构解决应用级别的水平扩展扩展,经过优化后,该架构支撑了几十万用户的访问需求,在这一阶段有部分应用已经使用java 完成了mvc架构的重写。当然也存在一些问题。
应用之间耦合度高,相互依赖严重。
应用模块之间交互复杂,有时直接访问对方模块数据库。
数据库涉及过多的关联查询与慢查询,数据库优化困难。
数据库单点访问严重,出现故障无法恢复。
数据复制问题严重,造成大量数据不一致。
我们曾经尝试使用 sql server AlwaysOn 解决扩展问题,但是实验发现在复制过程中出现至少10s的延迟,因此放弃了这个方案。
系统扩展困难。
各个开发团队各自为战,开发效率低下。
测试工作量巨大,发布困难。
微服务化架构(平台现状:第三代架构)
为了解决第一代与第二代架构存在的问题,我们对平台进行了梳理优化。根据平台业务需要以及对第一二代架构的总结,我们确定了第三代架构的核心需求:
核心业务抽取出来,作为独立的服务对外服务。
服务模块持续独立部署,减少版本交付周期。
数据库按服务分库分表。
大量使用缓存,提高访问。
系统间交互使用轻量级的rest协议,摒弃rpc协议。
去.net化,开发语言使用java来实现。
并以此为基础进行了平台的第三代架构的重构工作。
看第三代架构里面的组成,主要分为八个部分:
CDN:CDN 系统负责实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet 网络拥挤的状况,提高用户访问网站的响应速度。
平台在选择 CDN 厂商时,需要考虑经营时间长短,是否有可扩充的带宽资源、灵活的流量和带宽选择、稳定的节点、性价比;综合前面几个因素平台采用了七牛的 CDN 服务。
LB层:平台包括很多个业务域,不同的业务域有不同的集群,LB层(Load Balancer)是对多台业务服务器进行流量分发的负载均衡服务,通过流量分发扩展应用系统对外的服务能力,并消除单点故障提升了应用系统的可用性。
选择哪种负载,需要综合考虑各种因素(是否满足高并发高性能,Session保持如何解决,负载均衡的算法如何,支持压缩,缓存的内存消耗),主要分为以下两种:
LVS:工作在4层,Linux实现的高性能高并发、可伸缩性、可靠的的负载均衡器,支持多种转发方式(NAT、DR、IP Tunneling),其中DR模式支持通过广域网进行负载均衡。支持双机热备(Keepalived或者Heartbeat)。对网络环境的依赖性比较高。
Nginx:工作在7层,事件驱动的、异步非阻塞的架构、支持多进程的高并发的负载均衡器/反向代理软件。可以针对域名、目录结构、正则规则针对 http 做一些分流。
通过端口检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点,不过其中缺点就是不支持url来检测。
对于session sticky,我们通过基于 cookie 的扩展 nginx-sticky-module 来实现。这种也是平台目前所采用的方案。
业务层:代表平台某一领域的业务提供的服务,对于平台而言,有商品、会员、直播、订单、财务、论坛等系统,不同的系统提供不同的领域服务。
网关与注册中心:提供了统一的底层微服务 api 入口与注册管理。封装了内部的系统架构并向每个客户端提供 Rest API,同时实现了监控、负载均衡、缓存、服务降级、限流等职责。目前平台采用 nginx+consul 来实现。
服务层:该层为一些协同工作的小而自治的服务,平台根据业务的边界来确定了服务的边界,每个服务只专注自己边界之内。该层基于 spring cloud 来构建。
基础设施层:该层为上层服务提供基础设施服务,主要为以下几类:
redis 集群:以高响应速度、内存操作为上层提供缓存服务。
mongodb 集群:由于 mongodb 具有灵活文档模型 、高可用复制集 、可扩展分片集群等特性,平台基于此为上层提供如文章、帖子、链路日志等存储服务。mongodb 集群采用了复制+分片的架构解决可用性与扩展性问题。
MySQL 集群:存储会员、商品、订单等具有事务性要求的数据。
Kafka:支撑了平台的所有的消息服务。
ES(elasticsearch):提供了平台的商品、会员、订单、日志等搜索服务。
集成层:这个特点是整个平台中的最大亮点,包括实践持续集成CI、持续交付CD、DevOps文化,让每个人都参与交付,在规范的流程和标准的交付下,完成一个服务的自动部署发布,进而提高了版本交付链路的整体效率。
监控层:将系统拆分为更小的、细粒度的微服务给平台带来了很多好处,但是,它也增加了平台系统的运维复杂性。
给最终用户提供的任务服务都是有大量的微服务配合完成,一个初始调用最终会触发多个下游的服务调用,如何才能重建请求流,以重现与解决这个问题?
为此部署开源的 open-falcon 平台提供应用级以上监控、使用ELK提供应用日志的分析、使用自建服务提供链路日志跟踪以及基于spring config server实践统一配置服务。
微服务团队的工作方式
康威定律:任何组织在设计一套系统时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。
工作方式
在实践第三代架构时,我们对团队组织做了几个调整:
按照业务边界进行了划分,在一个团队内全栈,让团队自治,按照这样的方式组建,将沟通的成本维持在系统内部,每个子系统就会更加内聚,彼此的依赖耦合能变弱,跨系统的沟通成本也就能降低。
作者:java菜
链接:https://www.jianshu.com/p/6267807e91b3
共同学习,写下你的评论
评论加载中...
作者其他优质文章