淘宝千万级并发分布式架构的14次演进

一、概述

本文以淘宝作为例子,介绍从一百个并发到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原则。

二、基本概念

在介绍架构之前,为了避免部分读者对架构设计中的一些概念不了解,下面对几个最基础的概念进行介绍:

1)分布式

系统中的多个模块在不同服务器上部署,即可称为分布式系统,如Tomcat和数据库分别部署在不同的服务器上,或两个相同功能的Tomcat分别部署在不同服务器上。

2)高可用

系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性。

3)集群

一个特定领域的软件部署在多台服务器上并作为一个整体提供一类服务,这个整体称为集群。如Zookeeper中的Master和Slave分别部署在多台服务器上,共同组成一个整体提供集中配置服务。

在常见的集群中,客户端往往能够连接任意一个节点获得服务,并且当集群中一个节点掉线时,其他节点往往能够自动的接替它继续提供服务,这时候说明集群具有高可用性。

4)负载均衡

请求发送到系统时,通过某些方式把请求均匀分发到多个节点上,使系统中每个节点能够均匀的处理请求负载,则可认为系统是负载均衡的。

5)正向代理和反向代理

  • 系统内部要访问外部网络时,统一通过一个代理服务器把请求转发出去,在外部网络看来就是代理服务器发起的访问,此时代理服务器实现的是正向代理;

  • 当外部请求进入系统时,代理服务器把该请求转发到系统中的某台服务器上,对外部请求来说,与之交互的只有代理服务器,此时代理服务器实现的是反向代理。

简单来说,正向代理是代理服务器代替系统内部来访问外部网络的过程,反向代理是外部请求访问系统时通过代理服务器转发到内部服务器的过程。

三、架构演进

单机架构

淘宝千万级并发分布式架构的14次演进

以淘宝作为例子。在网站最初时,应用数量与用户数都较少,可以把Tomcat和数据库部署在同一台服务器上。浏览器往www.taobao.com发起请求时,首先经过DNS服务器(域名系统)把域名转换为实际IP地址10.102.4.1,浏览器转而访问该IP对应的Tomcat。

随着用户数的增长,Tomcat和数据库之间竞争资源,单机性能不足以支撑业务。

第一次演进:Tomcat与数据库分开部署

淘宝千万级并发分布式架构的14次演进

Tomcat和数据库分别独占服务器资源,显著提高两者各自性能。

随着用户数的增长,并发读写数据库成为瓶颈。

第二次演进:引入本地缓存和分布式缓存

淘宝千万级并发分布式架构的14次演进

在Tomcat同服务器上或同JVM中增加本地缓存,并在外部增加分布式缓存,缓存热门商品信息或热门商品的html页面等。通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。

其中涉及的技术包括:使用memcached作为本地缓存,使用Redis作为分布式缓存,还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题。

缓存抗住了大部分的访问请求,随着用户数的增长,并发压力主要落在单机的Tomcat上,响应逐渐变慢。

第三次演进:引入反向代理实现负载均衡

淘宝千万级并发分布式架构的14次演进

在多台服务器上分别部署Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个Tomcat中。

此处假设Tomcat最多支持100个并发,Nginx最多支持50000个并发,那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。

其中涉及的技术包括:Nginx、HAProxy,两者都是工作在网络第七层的反向代理软件,主要支持http协议,还会涉及session共享、文件上传下载的问题。

反向代理使应用服务器可支持的并发量大大增加,但并发量的增长也意味着更多请求穿透到数据库,单机的数据库最终成为瓶颈。

第四次演进:数据库读写分离

淘宝千万级并发分布式架构的14次演进

把数据库划分为读库和写库,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据场景,可通过在缓存中多写一份,通过缓存获得最新数据。

其中涉及的技术包括:Mycat,它是数据库中间件,可通过它来组织数据库的分离读写和分库分表,客户端通过它来访问下层数据库,还会涉及数据同步,数据一致性的问题。

业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能。

第五次演进:数据库按业务分库

淘宝千万级并发分布式架构的14次演进

把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,对于访问量大的业务,可以部署更多的服务器来支撑。

这样同时导致跨业务的表无法直接做关联分析,需要通过其他途径来解决,但这不是本文讨论的重点,有兴趣的可以自行搜索解决方案。

随着用户数的增长,单机的写库会逐渐会达到性能瓶颈。

第六次演进:把大表拆分为小表

淘宝千万级并发分布式架构的14次演进

比如针对评论数据,可按照商品ID进行hash,路由到对应的表中存储;针对支付记录,可按照小时创建表,每个小时表继续拆分为小表,使用用户ID或记录编号来路由数据。

只要实时操作的表数据量足够小,请求能够足够均匀的分发到多台服务器上的小表,那数据库就能通过水平扩展的方式来提高性能。其中前面提到的Mycat也支持在大表拆分为小表情况下的访问控制。

这种做法显著的增加了数据库运维的难度,对DBA的要求较高。数据库设计到这种结构时,已经可以称为分布式数据库,但是这只是一个逻辑的数据库整体,数据库里不同的组成部分是由不同的组件单独来实现的。

如分库分表的管理和请求分发,由Mycat实现,SQL的解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现等等,这种架构其实是MPP(大规模并行处理)架构的一类实现。

目前开源和商用都已经有不少MPP数据库,开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等,商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibrA等等。

不同的MPP数据库的侧重点也不一样,如TiDB更侧重于分布式OLTP场景,Greenplum更侧重于分布式OLAP场景。

这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回。

也提供了诸如权限管理、分库分表、事务、数据副本等能力,并且大多能够支持100个节点以上的集群,大大降低了数据库运维的成本,并且使数据库也能够实现水平扩展。

数据库和Tomcat都能够水平扩展,可支撑的并发大幅提高,随着用户数的增长,最终单机的Nginx会成为瓶颈。

第七次演进:使用LVS或F5来使多个Nginx负载均衡

淘宝千万级并发分布式架构的14次演进

由于瓶颈在Nginx,因此无法通过两层的Nginx来实现多个Nginx的负载均衡。

图中的LVS和F5是工作在网络第四层的负载均衡解决方案,其中LVS是软件,运行在操作系统内核态,可对TCP请求或更高层级的网络协议进行转发,因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;F5是一种负载均衡硬件,与LVS提供的能力类似,性能比LVS更高,但价格昂贵。

由于LVS是单机版的软件,若LVS所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点。可使用keepalived软件模拟出虚拟IP,然后把虚拟IP绑定到多台LVS服务器上,浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器,当主LVS服务器宕机时,keepalived软件会自动更新路由器中的路由表,把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果。

此处需要注意的是,上图中从Nginx层到Tomcat层这样画并不代表全部Nginx都转发请求到全部的Tomcat。

在实际使用时,可能会是几个Nginx下面接一部分的Tomcat,这些Nginx之间通过keepalived实现高可用,其他的Nginx接另外的Tomcat,这样可接入的Tomcat数量就能成倍的增加。

由于LVS也是单机的,随着并发数增长到几十万时,LVS服务器最终会达到瓶颈,此时用户数达到千万甚至上亿级别,用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同。

第八次演进:通过DNS轮询实现机房间的负载均衡

淘宝千万级并发分布式架构的14次演进

在DNS服务器中可配置一个域名对应多个IP地址,每个IP地址对应到不同的机房里的虚拟IP。

当用户访问www.taobao.com时,DNS服务器会使用轮询策略或其他策略,来选择某个IP供用户访问。此方式能实现机房间的负载均衡,至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决,系统入口处的请求并发量不再是问题。

随着数据的丰富程度和业务的发展,检索、分析等需求越来越丰富,单单依靠数据库无法解决如此丰富的需求。

第九次演进:引入NoSQL数据库和搜索引擎等技术

淘宝千万级并发分布式架构的14次演进

当数据库中的数据多到一定规模时,数据库就不适用于复杂的查询了,往往只能满足普通查询的场景。

对于统计报表场景,在数据量大时不一定能跑出结果,而且在跑复杂查询时会导致其他查询变慢,对于全文检索、可变数据结构等场景,数据库天生不适用。

因此需要针对特定的场景,引入合适的解决方案。如对于海量文件存储,可通过分布式文件系统HDFS解决,对于key value类型的数据,可通过HBase和Redis等方案解决,对于全文检索场景,可通过搜索引擎如ElasticSearch解决,对于多维分析场景,可通过Kylin或Druid等方案解决。

当然,引入更多组件同时会提高系统的复杂度,不同的组件保存的数据需要同步,需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等。

引入更多组件解决了丰富的需求,业务维度能够极大扩充,随之而来的是一个应用中包含了太多的业务代码,业务的升级迭代变得困难。

第十次演进:大应用拆分为小应用

淘宝千万级并发分布式架构的14次演进

按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。这时候应用之间可能会涉及到一些公共配置,可以通过分布式配置中心Zookeeper来解决。

不同应用之间存在共用的模块,由应用单独管理会导致相同代码存在多份,导致公共功能升级时全部应用代码都要跟着升级。

第十一次演进:复用的功能抽离成微服务

淘宝千万级并发分布式架构的14次演进

如用户管理、订单、支付、鉴权等功能在多个应用中都存在,那么可以把这些功能的代码单独抽取出来形成一个单独的服务来管理,这样的服务就是所谓的微服务。

应用和服务之间通过HTTP、TCP或RPC请求等多种方式来访问公共服务,每个单独的服务都可以由单独的团队来管理。此外,可以通过Dubbo、SpringCloud等框架实现服务治理、限流、熔断、降级等功能,提高服务的稳定性和可用性。

不同服务的接口访问方式不同,应用代码需要适配多种访问方式才能使用服务,此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱。

第十二次演进:引入企业服务总线ESB屏蔽服务接口的访问差异

淘宝千万级并发分布式架构的14次演进

通过ESB统一进行访问协议转换,应用统一通过ESB来访问后端服务,服务与服务之间也通过ESB来相互调用,以此降低系统的耦合程度。这种单个应用拆分为多个应用,公共服务单独抽取出来来管理,并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的SOA(面向服务)架构,这种架构与微服务架构容易混淆,因为表现形式十分相似。

个人理解,微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,而SOA架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA架构中包含了微服务的思想。

业务不断发展,应用和服务都会不断变多,应用和服务的部署变得复杂,同一台服务器上部署多个服务还要解决运行环境冲突的问题,此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难。

第十三次演进:引入容器化技术实现运行环境隔离与动态服务管理

淘宝千万级并发分布式架构的14次演进

目前最流行的容器化技术是Docker,最流行的容器管理服务是Kubernetes(K8S),应用/服务可以打包为Docker镜像,通过K8S来动态分发和部署镜像。

Docker镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,运行环境根据实际的需要设置好。把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,直接启动Docker镜像就可以把服务起起来,使服务的部署和运维变得简单。

在大促的之前,可以在现有的机器集群上划分出服务器来启动Docker镜像,增强服务的性能,大促过后就可以关闭镜像,对机器上的其他服务不造成影响(在3.14节之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)。

使用容器化技术后服务动态扩缩容问题得以解决,但是机器还是需要公司自身来管理,在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低。

第十四次演进:以云平台承载系统

淘宝千万级并发分布式架构的14次演进

系统可部署到公有云上,利用公有云的海量机器资源,解决动态硬件资源的问题,在大促的时间段里,在云平台中临时申请更多的资源,结合Docker和K8S来快速部署服务,在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本。

所谓的云平台,就是把海量机器资源,通过统一的资源管理,抽象为一个资源整体。在之上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上提供通用的操作系统,提供常用的技术组件(如Hadoop技术栈,MPP数据库等)供用户使用,甚至提供开发好的应用。用户不需要关系应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)。在云平台中会涉及如下几个概念:

  • IaaS:基础设施即服务。对应于上面所说的机器资源统一为资源整体,可动态申请硬件资源的层面;

  • PaaS:平台即服务。对应于上面所说的提供常用的技术组件方便系统的开发和维护;

  • SaaS:软件即服务。对应于上面所说的提供开发好的应用或服务,按功能或性能要求付费。

至此,以上所提到的从高并发访问问题,到服务的架构和系统实施的层面都有了各自的解决方案,但同时也应该意识到,在上面的介绍中,其实是有意忽略了诸如跨机房数据同步、分布式事务实现等等的实际问题,这些问题以后有机会再拿出来单独讨论。

四、 架构设计总结

架构的调整是否必须按照上述演变路径进行?

不是的,以上所说的架构演变顺序只是针对某个侧面进行单独的改进,在实际场景中,可能同一时间会有几个问题需要解决,或者可能先达到瓶颈的是另外的方面,这时候就应该按照实际问题实际解决。

如在政府类的并发量可能不大,但业务可能很丰富的场景,高并发就不是重点解决的问题,此时优先需要的可能会是丰富需求的解决方案。

对于将要实施的系统,架构应该设计到什么程度?

对于单次实施并且性能指标明确的系统,架构设计到能够支持系统的性能指标要求就足够了,但要留有扩展架构的接口以便不备之需。对于不断发展的系统,如电商平台,应设计到能满足下一阶段用户量和性能指标要求的程度,并根据业务的增长不断的迭代升级架构,以支持更高的并发和更丰富的业务。

服务端架构和大数据架构有什么区别?

所谓的“大数据”其实是海量数据采集清洗转换、数据存储、数据分析、数据服务等场景解决方案的一个统称。

在每一个场景都包含了多种可选的技术,如数据采集有Flume、Sqoop、Kettle等,数据存储有分布式文件系统HDFS、FastDFS,NoSQL数据库HBase、MongoDB等,数据分析有Spark技术栈、机器学习算法等。

总的来说大数据架构就是根据业务的需求,整合各种大数据组件组合而成的架构,一般会提供分布式存储、分布式计算、多维分析、数据仓库、机器学习算法等能力。而服务端架构更多指的是应用组织层面的架构,底层能力往往是由大数据架构来提供。

有没有一些架构设计的原则?

1)N+1设计

系统中的每个组件都应做到没有单点故障。

2)回滚设计

确保系统可以向前兼容,在系统升级时应能有办法回滚版本。

3)禁用设计

应该提供控制具体功能是否可用的配置,在系统出现故障时能够快速下线功能。

4)监控设计

在设计阶段就要考虑监控的手段。

5)多活数据中心设计

若系统需要极高的高可用,应考虑在多地实施数据中心进行多活,至少在一个机房断电的情况下系统依然可用

6)采用成熟的技术

刚开发的或开源的技术往往存在很多隐藏的bug,出了问题没有商业支持可能会是一个灾难。

7)资源隔离设计

应避免单一业务占用全部资源。

8)架构应能水平扩展

系统只有做到能水平扩展,才能有效避免瓶颈问题

9)非核心则购买

非核心功能若需要占用大量的研发资源才能解决,则考虑购买成熟的产品。

10)使用商用硬件

商用硬件能有效降低硬件故障的机率。

11)快速迭代

系统应该快速开发小功能模块,尽快上线进行验证,早日发现问题大大降低系统交付的风险。

12)无状态设计

服务接口应该做成无状态的,当前接口的访问不依赖于接口上次访问的状态。

来源:https://segmentfault.com/a/1190000018626163

 IT大咖说  |  关于版权 

由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!

感谢您对IT大咖说的热心支持!

技术人具备“结构化思维”意味着什么?

技术人具备“结构化思维”意味着什么?

阿里妹导读:在日常工作中,我们时常会碰到这样的情况,有的人讲事情逻辑非常混乱,罗列了很多事项,却把握不到重点,无法把一件事情说清楚。这种思维混乱是典型的缺少结构化思维的表现。结构化思维非常重要,不仅仅体现在表达上,也体现在在我们分析问题的过程中。具备结构化思维,才能将问题分析地更全面、更深刻。

 

那么到底是什么是结构化思维呢?简单来说,结构化思维的定义就是:逻辑+套路。

表达要有逻辑

所谓逻辑是指我们的结构之间必须是有逻辑关系的。例如,你说话的时候用“第一、第二、第三”这个逻辑顺序是合理的,但是,用“第一,第二,第四”就会显得很奇怪。当然,即使你用了”一、二、三”,也不一定就意味着你的内容有逻辑关系。想让内容有逻辑关系,我们要学会四种组织思想的逻辑关系:

 

1)演绎(因果)顺序:“大前提、小前提、结论”的演绎推理方式就是演绎顺序。比如,经典三段论:所有人都要死,苏格拉底是人,苏格拉底要死。

2)时间(步骤)顺序:“第一、第二、第三”,“首先、然后、再者”等,很多的时间顺序同时也是因果顺序。

3)空间(结构)顺序:“前端、后端、数据”,“波士顿、纽约、华盛顿”,化整为零(将整体分解为部分)等都是空间顺序。

4)程度(重要性)顺序:比如“最重要、次重要、不重要”等。

实际上,所有的逻辑关系都在这四种顺序之内。只要我们的思想和表达在这四种逻辑顺序之内,就是有逻辑的,否则就会显得没有逻辑性。

做事要有套路

套路是指我们解决问题的方法论,这个也非常重要。比如,5W2H 分析法就是一个非常好的,可以帮助我们分析问题的一个”套路”。试想一下,面对任何一个问题,你都能从 Why、Who、When、Where、What、How 和 How much(如下图所示),七个方面去思考。是不是比不知道这个方法论的人,用点状的思考,5W2H 分析法就全面得多。

技术人具备“结构化思维”意味着什么?

例如,我们在对问题域进行分析和领域知识提炼的时候,就可以用上5W2H。5W2H模型给出了具有指导意义的约束,要求我们提炼的领域知识必须具备模型的六个要素。这就好比两位侃侃而谈的交谈者,因为有了确定的主题与话题边界,一场本来是漫无目的野鹤闲云似的闲聊就变成了一次深度交流的专题高端对话。


技术人具备“结构化思维”意味着什么?

 

逻辑是一种能力,而套路是方法论,是经验。逻辑是道的东西,而方法论是术的东西。二者都很重要,只有熟练的掌握二者我们才能更好的进行结构化思考。

如何进行结构化思考?

逻辑性和方法论是结构化思维的底层,那么如何进行结构化思考呢?这也是有方法论的,总的来说是有两个步骤,首先是“建立中心”,然后再进行“分解”。

建立中心

建立中心也就是要定义清楚要解决的问题,要明确目标。是我们结构的顶层节点,也是一种以终为始的思考方式。也就是说,我们首先要搞清楚 why,然后再进行 how。

★ 建立中心有两种方式:

  1. 自上而下:适用于问题比较明确的情况,我们只需要找到问题的核心要素即可,然后进行展开即可。

  2. 自下而上:对于问题不够明确的情况,我们需要对多种杂乱的内容,进行分类、剪枝、归纳汇总成一个中心。

建立中心通常不会是一次成型的,随着对问题理解的变化,对中心的抽象也会进行相应的调整。不同的抽象层次其面对的问题宽度是不一样的。具体要用哪个层次的抽象作为“中心”,要视具体情况而定。

比如面对“系统 bug 多”的问题,向上抽象是“提升代码质量”,向下抽象是“加强测试”,都可以作为中心,选择哪个为中心取决于你当前要解决的问题是什么。

 

技术人具备“结构化思维”意味着什么?

结构化分解

确定完中心之后,我们需要构建一个结构,使用结构化的思维对问题进行分解。分解的策略就是我们上文提到的四种逻辑顺序,即演绎顺序、时间顺序、空间顺序和程度顺序。

在做空间分解的时候,要注意满足 MECE(Mutually Exclusive Collectively Exhaustive,相互独立,完全穷尽)原则。

比如我们要对衣服进行分类,如果按照季节和风格进行分类,就会出现互相重叠,并且不能穷尽的情况,也就不满足 MECE。这种分类是逻辑混乱的。

 

技术人具备“结构化思维”意味着什么?

我们可以按季节分:春秋装,冬装,夏装。除了这3类之外,没有其他季节了,这个就是「不遗漏」。

 

技术人具备“结构化思维”意味着什么?

结构化思维应用

如何落地新团队?

想象这样一个场景,你刚刚入职一家新公司,或者转岗到一个新团队,作为一个技术人,你将如何落地开展你的工作呢?

这里,我们就能用上结构化思维来帮我们理清思路,从而有条不紊的开展工作了。我们要知道对一个企业来说,核心要素无外乎就是业务、技术和人,也就是说这三个要素是我们要建立的中心。基于这个中心,我们可以进行进一步拆解,形成子结构。然后对子结构再进行分析找到应对策略。这样一步步递进,我们就已经在用结构化思维解决如何落地新团队的问题了。

技术人具备“结构化思维”意味着什么?

★ 1. 熟悉业务

1)了解产品:任何一个团队都有自己要负责的产品,申请一个测试账号去用一下产品,是熟悉产品比较好的方式。

2)了解流程:任何业务都有自己的业务流程,而业务流里面最核心的是信息流。我们可以通过人员采访,了解关键节点的信息输入和信息输出;可以画一些泳道活动图,理清楚系统的主要角色,以及他们之间的交互关系。

3)客户走访:通过走访客户,我们可以更加获得业务的第一手资料,更加贴近业务和客户诉求。

★ 2. 熟悉技术

1)了解系统架构:可以让团队的技术人员介绍下他们当初系统设计和架构的思路。

2)了解领域模型:查看关键的核心表结构和系统 API,这样可以快速了解系统的领域模型。

3)了解代码结构:下载系统工程,熟悉整个工程结构和模块职责。以一个最重要的流程为入手点,阅读代码,看清楚核心的执行逻辑。做一个小需求,掌握相关的流程和权限。

★ 3. 熟悉人

1)了解组织结构:查看公司的组织树,知道公司大概是如何运作的,以及哪些是KP(Key Person,关键人)。比如,一个典型的电商公司会包括产品部、运营部、销售部、技术部、人力资源部、财务部、法务部等。

2)了解人员角色:了解公司都有哪些岗位,以及各岗位的职责范围。

3)拜山头:找到和自己工作息息相关的岗位人员,比如产品和运营。积极和他们沟通,向他们请教业务问题,多多交流。这样一方面可以建立更好的人际关系,另一方面也可以更快地熟悉业务。

打造极客文化

我最近刚刚转岗到新部门,新部门的老板抛给我一个命题:如何帮助技术团队打造极客文化?

这个问题的中心很明确,接下来,看看我是如何使用结构化思维来解这个问题的。首先我们从空间顺序进行分解,也就是打造极客文化,我们可以去做哪些事情。

 

技术人具备“结构化思维”意味着什么?
空间顺序分解

确定完要做的事情,我们还可以按照时间顺序对如何落地这些事情进行分解。

 

技术人具备“结构化思维”意味着什么?
时间顺序分解

这样把按照这两个维度进行结构化拆解的方案给到老板,老板就会很清晰地知道你的规划和落地策略了。

如何做晋升述职

作者在阿里巴巴已经做了好几年的晋升评委,发现很多同学都缺乏结构化思维,冗长的 PPT 里,却不能把价值说清楚,不能把推导过程说清楚。实际上,我们需要有一些方法论来指导我们进行关键述职。

接下来,我主要说一下述职中存在的两个典型问题:“罗列事情”和“价值的背后”

★ 1.罗列事情

对自己做过的事情进行简单罗列,也许你的确做了不少事情。但是不能体现你对问题思考的深度和做这个事情带来的价值。这样的述职很难打动评委,更结构化的表达应该是:“提出问题,定义问题,分析问题,解决问题,最后是展望未来”

这是一个经典的表述问题的结构,也是麦肯锡推荐的问题解决的框架。

 

技术人具备“结构化思维”意味着什么?

类似的框架还有 zoom in/zoom out。 我们说事情时,应该像电影镜头一样,先从远拉近,再由近拉远。zoom in 是先从宏观背景开始,首先让大家知道你的事情发生的背景,为什么这事重要?然后讲到具体细节,怎么做成的?解决了什么问题?背后的思考是什么?最后 Zoom out,再从细节调回到整体,结果是什么,带来的客户价值是什么,你对未来的思考是什么。

★ 2.价值的背后

把价值说清楚的确很重要,正所谓:路走对了,就不怕远。如果你连价值都不明确,后面做的再多也是白搭。但是,仅仅阐述价值也是不够的,会让人觉得你有邀功之嫌。

比如你说:“我主导研发的风控系统把公司的坏账率从5%降低到2%”。这样的表述是不够的,你还需要把价值背后的过程和思考说清楚。对于这个结果,评委可能会问:

  1. 之前为什么那么高?

  2. 为什么你的方法可以降低?是如何归因的?

  3. 具体解决了什么问题?

  4. 是否可以总结出一套办法,以后别人也能用这个办法解决这些问题?

如果你在评委提问之前,就能对这些问题进行深入思考和适当呈现。那么你就是既有结果又有过程了。

通过这些案例,我们可以看到具备结构化思维,可以帮助我们快速的理清处理问题的思路,提升工作效率。经常锻炼结构化思维,可以极大的提升我们职场竞争力,让工作有条不紊,事半功倍。

技术人具备“结构化思维”意味着什么?

关注「阿里技术」

把握前沿技术脉搏

终于有人把服务调用说清楚了

导读:RPC,微服务,Service Mesh这些服务之间的调用是什么原理?

作者 codedump codedump.info 博主,多年从事互联网服务器后台开发工作。可访问作者博客阅读 codedump 更多文章。

本文专注于演化过程中每一步的为什么(Why)和是什么(What)上面,尽量不在技术细节(How)上面做太多深入。

服务的三要素

一般而言,一个网络服务包括以下的三个要素:

  • 地址:调用方根据地址访问到网络接口。地址包括以下要素:IP地址、服务端口、服务协议(TCP、UDP,etc)。

  • 协议格式:协议格式指的是该协议都有哪些字段,由接口提供者与协议调用者协商之后确定下来。

  • 协议名称:或者叫协议类型,因为在同一个服务监听端口上面,可能同时提供多种接口服务于调用方,这时候需要协议类型(名称)来区分不同的网络接口。

需要说明在服务地址中:

  • IP地址提供了在互联网上找到这台机器的凭证。

  • 协议以及服务端口提供了在这台机器上找到提供服务的进程的凭证。

终于有人把服务调用说清楚了

都属于TCPIP协议栈的知识点,不在这里深入详述。

这里还需要对涉及到服务相关的一些名词做解释。

  • 服务实例:服务对应的IP地址加端口的简称。需要访问服务的时候,需要先寻址知道该服务每个运行实例的地址加端口,然后才能建立连接进行访问。

  • 服务注册:某个服务实例宣称自己提供了哪些服务,即某个IP地址+端口都提供了哪些服务接口。

  • 服务发现:调用方通过某种方式找到服务提供方,即知道服务运行的IP地址加端口。

基于IP地址的调用

最初的网络服务,通过原始的IP地址暴露给调用者。这种方式有以下的问题:

  • IP地址是难于记忆并且无意义的。

  • 另外,从上面的服务三要素可以看到,IP地址其实是一个很底层的概念,直接对应了一台机器上的一个网络接口,如果直接使用IP地址进行寻址,更换机器就变的很麻烦。

“尽量不使用过于底层的概念来提供服务”,是这个演化流程中的重要原则,好比在今天已经很少能够看到直接用汇编语言编写代码的场景了,取而代之的,就是越来越多的抽象,本文中就展现了服务调用这一领域在这个过程中的演进流程。

在现在除非是测试阶段,否则已经不能直接以IP地址的形式将服务提供出去了。

域名系统

前面的IP地址是给主机做为路由器寻址的数字型标识,并不好记忆。此时产生了域名系统,与单纯提供IP地址相比,域名系统由于使用有意义的域名来标识服务,所以更容易记忆。另外,还可以更改域名所对应的IP地址,这为变换机器提供了便利。有了域名之后,调用方需要访问某个网络服务时,首先到域名地址服务中,根据DNS协议将域名解析为相应的IP地址,再根据返回的IP地址来访问服务。

从这里可以看到,由于多了一步到域名地址服务查询映射IP地址的流程,所以多了一步解析,为了减少这一步带来的影响,调用方会缓存解析之后的结果,在一段时间内不过期,这样就省去了这一步查询的代价。

协议的接收与解析

以上通过域名系统,已经解决了服务IP地址难以记忆的问题,下面来看协议格式解析方面的演进。

一般而言,一个网络协议包括两部分:

  • 协议包头:这里存储协议的元信息(meta infomation),其中可能会包括协议类型、报体长度、协议格式等。需要说明的是,包头一般为固定大小,或者有明确的边界(如HTTP协议中的rn结束符),否则无法知道包头何时结束。

  • 协议包体:具体的协议内容。

无论是HTTP协议,又或者是自定义的二进制网络协议,大体都由这两部分组成。

终于有人把服务调用说清楚了

由于很多时候不能一口气接收完毕客户端的协议数据,因此在接收协议数据时,一般采用状态机来做协议数据的接收:

终于有人把服务调用说清楚了

接收完毕了网络数据,在协议解析方面却长期停滞不前。一个协议,有多个字段(field),而这些不同的字段有不同的类型,简单的raw类型(如整型、字符串)还好说,但是遇到复杂的类型如字典、数组等就比较麻烦。

当时常见的手段有以下几种:

  • 使用json或者xml这样的数据格式。好处是可视性强,表达起上面的复杂类型也方便,缺陷是容易被破解,传输过去的数据较大。

  • 自定义二进制协议。每个公司做大了,在这一块难免有几个类似的轮子。笔者见过比较典型的是所谓的TLV格式(Type-Length-Value),自定义二进制格式最大的问题出现在协议联调与协商的时候,由于可视性比较弱,有可能这边少了一个字段那边多了一个字段,给联调流程带来麻烦。

上面的问题一直到Google的Protocol Buffer(以下简称PB)出现之后才得到很大的改善。PB出现之后,也有很多类似的技术出现,如Thrift、MsgPack等,不在这里阐述,将这一类技术都以PB来描述。

与前面的两种手段相比,PB具有以下的优点:

  • 使用proto格式文件来定义协议格式,proto文件是一个典型的DSL(domain-specific language)文件,文件中描述了协议的具体格式,每个字段都是什么类型,哪些是可选字段哪些是必选字段。有了proto文件之后,CS两端是通过这个文件来进行协议的沟通交流的,而不是具体的技术细节。

  • PB能通过proto文件生成各种语言对应的序列化反序列化代码,给跨语言调用提供了方便。

  • PB自己能够对特定类型进行数据压缩,减少数据大小。

终于有人把服务调用说清楚了

服务网关

有了前面的演化之后,写一个简单的单机服务器已经不难。然而,当随着访问量的增大,一台机器已经不足以支撑所有的请求,此时就需要横向扩展多加一些业务服务器。

而前面通过域名访问服务的架构就遇到了问题:如果有多个服务实例可以提供相同的服务,那么势必需要在DNS的域名解析中将域名与多个地址进行绑定。这样的方案就有如下的问题:

  • 如何检查这些实例的健康情况,同时在发现出现问题的时候增删服务实例地址?即所谓的服务高可用问题。

  • 把这些服务实例地址都暴露到外网,会不会涉及到安全问题?即使可以解决安全问题,那么也需要每台机器都做安全策略。

  • 由于DNS协议的特点,增删服务实例并不是实时的,有时候会影响到业务。

为了解决这些问题,就引入了反向代理网关这一组件。它提供如下的功能:

  • 负载均衡功能:根据某些算法将请求分派到服务实例上。

  • 提供管理功能,可以给运维管理员增减服务实例。

  • 由于它决定了服务请求流量的走向,因此还可以做更多的其他功能:灰度引流、安全防攻击(如访问黑白名单、卸载SSL证书)等。

终于有人把服务调用说清楚了

 

有四层和七层负载均衡软件,其中四层负载均衡这里介绍LVS,七层负载均衡介绍Nginx。

终于有人把服务调用说清楚了

上图是简易的TCPIP协议栈层次图,其中LVS工作在四层,即请求来到LVS这里时是根据四层协议来决定请求最终走到哪个服务实例;而Nginx工作在七层,主要用于HTTP协议,即根据HTTP协议本身来决定请求的走向。需要说明的是,Nginx也可以工作在四层,但是这么用的地方不是很多,可以参考nginx的stream模块。

做为四层负载均衡的LVS

(由于LVS有好几种工作模式,并不是每一种我都很清楚,以下表述仅针对Full NAT模式,下面的表述或者有误)

LVS有如下的组成部分:

  • Direct Server(以下简称DS):前端暴露给客户端进行负载均衡的服务器。

  • Virtual Ip地址(以下简称VIP):DS暴露出去的IP地址,做为客户端请求的地址。

  • Direct Ip地址(以下简称DIP):DS用于与Real Server交互的IP地址。

  • Real Server(以下简称RS):后端真正进行工作的服务器,可以横向扩展。

  • Real IP地址(以下简称RIP):RS的地址。

  • Client IP地址(以下简称CIP):Client的地址。

终于有人把服务调用说清楚了

 

客户端进行请求时,流程如下:

  1. 使用VIP地址访问DS,此时的地址二元组为<src:CIP,dst:VIP>。

  2. DS根据自己的负载均衡算法,选择一个RS将请求转发过去,在转发过去的时候,修改请求的源IP地址为DIP地址,让RS看上去认为是DS在访问它,此时的地址二元组为<src:DIP,dst:RIP A>。

  3. RS处理并且应答该请求,这个回报的源地址为RS的RIP地址,目的地址为DIP地址,此时的地址二元组为<src:RIP A,dst:DIP>。

  4. DS在收到该应答包之后,将报文应答客户端,此时修改应答报文的源地址为VIP地址,目的地址为CIP地址,此时的地址二元组为<src:VIP,dst:CIP>。

做为七层负载均衡的Nginx

在开始展开讨论之前,需要简单说一下正向代理和反向代理。

所谓的正向代理(proxy),我的理解就是在客户端处的代理。如浏览器中的可以配置的访问某些网站的代理,就属于正向代理,但是一般而言不会说正向代理而是代理,即默认代理都是正向的。

而反向代理(reverse proxy)就是挡在服务器端前面的代理,比如前面LVS中的DS服务器就属于一种反向代理。为什么需要反向代理,大体的原因有以下的考量:

  • 负载均衡:希望在这个反向代理的服务器中,将请求均衡的分发到后面的服务器中。

  • 安全:不想向客户端暴露太多的服务器地址,统一接入到这个反向代理服务器中,在这里做限流、安全控制等。

  • 由于统一接入了客户端的请求,所以在反向代理的接入层可以做更多的控制策略,比如灰度流量发布、权重控制等等。

反向代理与所谓的gateway、网关等,我认为没有太多的差异,只是叫法不同而已,做的事情都是类似的。

Nginx应该是现在用的最多的HTTP 七层负载均衡软件,在Nginx中,可以通过在配置的server块中定义一个域名,然后将该域名的请求绑定到对应的Upstream中,而实现转发请求到这些Upstream的效果。

 

如:

upstream hello 
{       server A:11001; 
         server B:11001;
}

location / 
{       root   html;
         index  index.html index.htm; 
         proxy_pass http://hello;
}

这是最简单的Nginx反向代理配置,实际线上一个接入层背后可能有多个域名,如果配置变动的很大,每次域名以及对应的Upstream的配置修改都需要人工干预,效率会很慢。这时候就要提到一个叫DevOps的名词了,我的理解就是开发各种便于自动化运维工具的工程师。

有了上面的分析,此时一个提供七层HTTP访问接口的服务架构大体是这样的:

终于有人把服务调用说清楚了

服务发现与RPC

前面已经解决单机服务器对外提供服务的大部分问题,来简单回顾:

  • 域名系统解决了需要记住复杂的数字IP地址的问题。

  • PB类软件库的出现解决协议定义解析的痛点。

  • 网关类组件解决客户端接入以及服务器横向扩展等一系列问题。

然而一个服务,通常并不见得只由本身提供服务就可以,服务过程中可能还涉及到查询其他服务的流程,常见的如数据类服务如Mysql、Redis等,这一类供服务内调用查询的服务被成为内部的服务,通常并不直接暴露到外网去。

面向公网的服务,一般都是以域名的形式提供给外部调用者,然而对于服务内部之间的互相调用,域名形式还不够,其原因在于:

  • DNS服务发现的粒度太粗,只能到IP地址级别,而服务的端口还需要用户自己维护。

  • 对于服务的健康状况的检查,DNS的检查还不够,需要运维的参与。

  • DNS对于服务状态的收集很欠缺,而服务状态最终应该是反过来影响服务被调用情况的。

  • DNS的变更需要人工的参与,不够智能以及自动化。

 

综上,内网间的服务调用,通常而言会自己实现一套“服务发现”类的系统,其包括以下几个组件:

  • 服务发现系统:用于提供服务的寻址、注册能力,以及对服务状态进行统计汇总,根据服务情况更改服务的调用情况。比如,某个服务实例的响应慢了,此时分配给该实例的流量响应的就会少一些。而由于这个系统能提供服务的寻址能力,所以一些寻址策略就可以在这里做,比如灰度某些特定的流量只能到某些特定的实例上,比如可以配置每个实例的流量权重等。

  • 一套与该服务系统搭配使用的RPC库,其提供以下功能:

    • 服务提供方:使用RPC库注册自己的服务到服务发现系统,另外上报自己的服务情况。

    • 服务调用方:使用RPC库进行服务寻址,实时从服务发现系统那边获取最新的服务调度策略。

    • 提供协议的序列化、反序列化功能,负载均衡的调用策略、熔断限流等安全访问策略,这部分对于服务的提供方以及调用方都适用。

终于有人把服务调用说清楚了

 

有了这套服务发现系统以及搭配使用的RPC库之后,来看看现在的服务调用是什么样的。

  • 写业务逻辑的,再也不用关注服务地址、协议解析、服务调度、自身服务情况上报等等与业务逻辑本身并没有太多关系的工作,专注于业务逻辑即可。

  • 服务发现系统一般还有与之搭配的管理后台界面,可以通过这里对服务的策略进行修改查看等操作。

  • 对应的还会有服务监控系统,对应的这是一台实时采集服务数据进行计算的系统,有了这套系统服务质量如何一目了然。

  • 服务健康状态的检查完全自动化,在状况不好的时候对服务进行降级处理,人工干预变少,更加智能以及自动化。

现在服务的架构又演进成了这样:

终于有人把服务调用说清楚了

ServiceMesh

架构发展到上面的程度,实际上已经能够解决大部分的问题了。这两年又出现了一个很火的概念:ServiceMesh,中文翻译为“服务网格”,来看看它又能解决什么问题。

前面的服务发现系统中,需要一个与之配套的RPC库,然而这又会有如下的问题:

  • 如果需要支持多语言,该怎么做?每个语言实现一个对应的RPC库吗?

  • 库的升级很麻烦,比如RPC库本身出了安全漏洞,比如需要升级版本,一般推动业务方去做这个升级是很难的,尤其是系统做大了之后。

可以看到,由于RPC库是嵌入到进程之中的组件,所以以上问题很麻烦,于是就想出了一个办法:将原先的一个进程拆分成两个进程,如下图所示。

终于有人把服务调用说清楚了

在服务mesh化之前,服务调用方实例通过自己内部的RPC库来与服务提供方实例进行通信。

在服务mesh化之后,会与服务调用方同机部署一个local Proxy也就是ServiceMesh的proxy,此时服务调用的流量会先走到这个proxy,再由它完成原先RPC库响应的工作。至于如何实现这个流量的劫持,答案是采用iptables,将特定端口的流量转发到proxy上面即可。

有了这一层的分拆,将业务服务与负责RPC库作用的Proxy分开来,上面的两个痛点问题就变成了对每台物理机上面的mesh proxy的升级维护问题,多语言也不是问题了,因为都是通过网络调用完成的RPC通信,而不是进程内使用RPC库。

然而这个方案并不是什么问题都没有的,最大的问题在于,多了这一层的调用之后,势必有影响原来的响应时间。

截止目前(2019.6月),ServiceMesh仍然还是一个概念大于实际的产品。

从上面的演进历史可以看到,所谓的“中间层理论”,即“Any problem in computer science can be solved by another layer of indirection(计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决)”在这个过程中被广泛使用,比如为了解决IP地址难于记忆的问题,引入了域名系统,比如为了解决负载均衡问题引入了网关,等等。然而每引入一个中间层,势必带来另外的影响,比如ServiceMesh多一次到Proxy的调用,如何权衡又是另外的问题了。

另外,回到最开始的服务三要素中,可以看到整个演化的历史也是逐渐屏蔽了下层组件的流程,比如:

  • 域名的出现屏蔽了IP地址。

  • 服务发现系统屏蔽协议及端口号。

  • PB类序列化库屏蔽了使用者自己对协议的解析。

可以看到,演进流程让业务开发者更加专注在业务逻辑上,这类的演进流程不只发生在今天,也不会仅仅发生在今天,未来类似的演进也将再次发生。

技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。转载请注明来自高可用架构「ArchNotes」微信公众号及包含以下二维码。

高可用架构

改变互联网的构建方式

终于有人把服务调用说清楚了

长按二维码 关注「高可用架构」公众号