精华内容
下载资源
问答
  • 论云原生架构及其应用

    千次阅读 2020-12-19 00:04:28
      本文以该系统为例,主要论述了云原生架构在项目中的具体应用。系统以Spring Cloud微服务框架开发,分为前端Web服务、平台保障服务、业务服务三部分。前端Web服务由负载均衡与服务器集群结合,实现高并发的前台...

      声明:本文为本人2020软考系统架构设计师考试中所写论文(52分)的回忆版本,不保证内容的原创性与正确性,仅供参考,请勿照抄和用于学术论文等正规场合,因不当使用产生后果一律自负。

    摘要

      2019年3月,我单位联合某高校研发了《程序在线评测比赛考试系统》。系统以程序代码在线提交自动评测功能为核心,分为题库模块、评测机模块、实验作业模块、考试模块、比赛模块、抄袭判定模块、用户管理模块等,支持对接教务平台。在项目中我担任系统架构师,负责架构设计工作。
      本文以该系统为例,主要论述了云原生架构在项目中的具体应用。系统以Spring Cloud微服务框架开发,分为前端Web服务、平台保障服务、业务服务三部分。前端Web服务由负载均衡与服务器集群结合,实现高并发的前台界面;平台保障服务以Eureka为中心,由API网关、服务注册中心、监控平台等构成,实现基础服务框架;业务服务划分为多个微服务,基于Docker容器,协同工作实现具体业务功能。最终系统顺利上线,获得用户一致好评。

    正文

      笔者在一个专为高校建设计算机专业智能教学一体化平台的单位任职,过往成果有《计算机组成原理仿真实验系统》等。2019年3月,我单位联合某大学研发了《程序在线评测比赛考试系统》项目(以下简称为“OJ系统”),以取代原有传统的编程上机考试平台。
      系统以程序代码的在线提交自动评测功能为核心,主要分为题库模块、评测机模块、实验作业模块、考试模块、比赛模块、抄袭判定模块、用户管理模块等。题库模块主要负责试题和测试用例的管理,用户根据试题要求编写程序代码提交到系统,系统将测试用例与程序代码发送给评测机模块,由评测机自动编译、执行、判分,并将结果发送给其他相关模块进行统计;实验作业模块用于在线布置作业,从题库中选取试题,设置截止日期等要求;考试模块用于学生在线考试,按教师预先设置的参数自动从题库随机抽题生成试卷,以及向教务平台上传考试成绩;比赛模块主要用于ACM竞赛的培训;抄袭判定模块用于鉴定代码与他人代码雷同率;用户管理模块负责用户信息的管理。在这个项目中,我担任了系统架构师的职务,主要负责系统的架构设计相关工作。
      云原生架构以微服务和容器技术为代表,有服务化、强韧性、可观测性和自动化四类设计原则。通过服务化的设计原则,应用被分解为多个服务,可分别选择不同的技术,单个服务模块很容易开发、理解和维护,无需协调其他服务对本服务的影响;通过强韧性的设计原则,微服务可以分布式云化部署,负载均衡管理请求的分发,避免单机失败对整体服务的影响,以及弹性调整资源容量;通过可观测性的设计原则,能够对系统进行健康检查、指标监控、日志管理和链路追踪,提高系统运维、管理和排错能力;通过自动化的设计原则,可实现系统的自动化部署、自动化扩展伸缩、自动化运维、持续交付和集成,有效减少人工操作的工作量。
      OJ系统基于Spring Cloud微服务框架开发,将平台服务划分为三类,分别为前端Web服务、平台保障服务、业务服务。下面针对这三类服务展开具体说明。

    1. 前端Web服务

      前端Web服务主要提供给用户使用的界面,分为前置Nginx负载均衡服务器、前端网站Nginx集群。当用户通过网络访问系统时,首先会访问到前置的Nginx负载均衡服务器,负载均衡服务器会将请求转发到前端网站的Nginx集群,前端网站通过发起http请求来和后端交互,具体是通过Ajax方式来调用后端REST API接口。用户访问网站通过前置的Nginx负载均衡服务器来转发到前端网站集群,以起到将用户请求进行分流的作用。当前端网站集群中的部分服务发生故障时,系统仍可正常地对外提供服务。前置Nginx负载均衡服务器使用软件反向代理的方式来实现负载均衡,部署为路由模式,系统内部网络与外部网络分属于不同的逻辑网络,以实现系统内部与外部网络的隔离。在负载均衡算法的选择上,使用最小连接法,每当用户的请求来临时,任务分发单元会将任务平滑分配给最小连接数的前端网站节点,这样的架构以廉价且透明的方式扩展了服务器和网络的带宽,可以大大提升系统的并发量,同时保证网站前端整体的稳定性和可靠性。

    2. 平台保障服务

      平台保障服务用以实现后端微服务的基础框架,包括API路由网关、服务注册中心、服务监控组件。API网关收到前端的请求,不会直接调用后端的业务服务,而是首先会从服务注册中心根据当前请求来获取对应的服务配置,随后通过服务配置再调用已注册的服务。当后端微服务存在多个实例时,将采取负载均衡的方式调用。服务注册中心是整个云原生架构体系的核心部分,由Spring Cloud的Eureka组件来实现,专门提供微服务的服务注册和发现功能,涉及三种角色:服务提供者、服务消费者和服务注册中心。API路由网关、所有业务服务,以及服务监控平台组件都注册到服务注册中心。通过服务注册中心两两互相注册、API路由网关向服务注册中心注册多个实例等方式,来实现后端整体服务的高可靠性。服务监控平台通过注册到服务注册中心,获取所有注册到服务注册中心的后端业务服务,从而监控到所有后端业务服务的运行状态信息,最后收集并展示整个微服务系统的运行状态,更进一步保证整个后端的服务质量。

    3. 业务服务

      业务服务按功能模块,相应划分为题库服务、评测机服务、考试服务、比赛服务、抄袭判定服务等。各服务单独打包,基于Docker容器,连同运行环境一起封装,根据实际情况可在一台或多台物理机同时部署多个实例,服务启动后会将自身信息注册到服务注册中心。服务间协同工作,通过松耦合的服务发现机制,动态调用对方REST API接口。对于压力较大的服务,如评测机服务、抄袭判定服务等,将部署为多实例集群。以在线考试功能为例,用户进入考试时,考试服务核验考生信息通过,调用题库服务,题库服务返回试题,由考试服务组合成试卷,返回前端显示。用户交卷时,提交的程序代码到达考试服务,考试服务拆分后分发给题库服务,题库服务将程序代码和测试用例送入MQ队列排队。然后由负载均衡机制,依次将队列中待评测程序分发给评测机服务编译、执行、判分,完成评测后,题库服务统计试题通过率,考试服务统计成绩并向前端显示。在此期间服务请求者无需了解其他服务对数据如何具体处理和分析。

    总结

      系统自2019年10月正式上线已运行一年有余,在学校的日常教学考试和竞赛培训中投入使用,至今已有3000名以上的学生用户,评测了70000份以上的程序代码,获得了单位同事领导和学校教师们的一致好评。在开发和试运行过程中,主要遇到了两个问题。一是跨域问题。OJ系统前后端分离,前端通过Ajax访问后端服务。由于浏览器同源策略的限制,导致前端UI无法正常访问不同端口和IP的后端服务。我们利用Spring Boot后端的Cors跨域机制解决了该问题。二是评测机宕机问题。评测机服务需要执行用户提交的代码,部分用户短时间内提交了大量不安全代码,导致评测机集群中所有实例全部宕机。我们引入心跳机制、快照回滚机制,以及基于机器学习技术的预判断机制,使评测机宕机时能够在10秒内自动重置恢复运行,最终解决了该问题。
      实践证明,OJ系统项目能够顺利上线,并且稳定运行,与系统采用了合适的架构设计密不可分。经过这次云原生架构的方法和实施的效果后,我也看到了自己身上的不足之处,在未来还会不断更新知识,完善本系统在各方面的设计,使整个OJ系统能够更加好用,更有效地服务于高校师生。

    展开全文
  • 快速成长期的云原生应用架构实践

    千次阅读 2017-08-02 11:20:14
    系统不再是单体架构,还会涉及系统的扩展和多系统之间的通信;高可用也不仅是服务自动拉起或者并行扩展,还需要考虑数据可靠、对用户影响,以及服务等级协议(SLA)。本文将以上述挑战为出发点,介绍如何通过引入新...

    在经过了最初的业务原型验证和上线运行期之后,用户业务进入了高速成长阶段。在这一阶段,业务重点不再是方向上的调整,而是在原来基础上的不断深挖、扩展;开发不仅是功能的实现,还需要兼顾成本和性能;系统不再是单体架构,还会涉及系统的扩展和多系统之间的通信;高可用也不仅是服务自动拉起或者并行扩展,还需要考虑数据可靠、对用户影响,以及服务等级协议(SLA)。

    本文将以上述挑战为出发点,介绍如何通过引入新的工具、新的架构,对原有系统进行升级和优化,来更好满足这一阶段需求,并为产品的进一步发展打下基础。

    关键业务需求


    随着用户业务的发展,原来的功能已经无法满足要求,需要增强或者增加新的功能。在用户数和访问量达到一定规模后,原先单体架构下的简单功能,如计数和排序,将变得复杂;随着业务深入,定期举行的秒杀、促销等活动,给系统带了巨大的压力;由于数据量的飞速增长,单纯的数据库或者内存检索已经无法满足不断增加的各种查询需求;随着业务数据量的增加,产品价值的提高,如何收集系统运行数据,分析业务运行状态也成了基本需求。接下来我们聚焦这一阶段的关键业务需求,并给出相应的解决方案。

    计数与排序

    在单体架构下,通过简单的内存数据和对应算法就可以实现计数和排序功能。但是在大量数据和多节点协作的环境下,基于单点内存操作的实现会遇到高并发、数据同步、实时获取等问题。在这一阶段,通用方法是使用Redis的原生命令来实现计数和排序。

    计数

    在Redis中可用于计数的对象有字符串(string)、哈希表(hash)和有序集合(zset)3种,对应的命令分别是incr/incrby、hincrby和zincrby。

    网站可以从用户的访问、交互中收集到有价值的信息。通过记录各个页面的被访问次数,我们可以根据基本的访问计数信息来决定如何缓存页面,从而减少页面载入时间并提升页面的响应速度,优化用户体验。

    计数器

    要实现网页点击量统计,需要设计一个时间序列计数器,对网页的点击量按不同的时间精度(1s、5s、1min、5min、1h、5h、1d等)计数,以对网站和网页监视和分析。

    数据建模以网页的地址作为KEY,定义一个有序集合(zset),内部各成员(member)分别由计数器的精度和计数器的名字组成,所有成员的分值(score)都是0。这样所有精度的计数器都保存在了这个有序集合里,不包含任何重复元素,并且能够允许一个接一个地遍历所有元素。

    对于每个计数器及每种精度,如网页的点击量计数器和5s,设计使用一个哈希表(hash)对象来存储网页在每5s时间片之内获得的点击量。其中,哈希表的每个原生的field都是某个时间片的开始时间,而原生的field对应的值则存储了网页在该时间片内获得的点击量。如图1所示。

    • 更新计数器信息示例代码。
     PRECESION = [1, 5, 60, 300, 3600, 18000, 86400]  def update_counter(conn, name, count=1, now=None): 
     ----now = now or time.time() 
     ----pipe = conn.pipeline()  ----for prec in PRECESION: 
     --------pnow = int(now / prec) * prec 
     --------hash = '%s:%s' % (prec, name) 
     --------pipe.zadd('http:/.....xxxxxx', hash, 0) 
     --------pipe.hincrby('count:' + hash, pnow, count) 
     ----pipe.execute() 

    图片描述
    图1 网页点击计数器的实现

    • 获取计数器信息示例代码。
     def get_counter(conn, name, precision):  ----hash = '%s:%s' % (precision, name) 
     ----data = conn.hgetall('count:' + hash) 
     ----to_return = [] 
     ----for key, value in data.iteritems(): 
     --------to_return.append((int(key), int(value)))  ----to_return.sort() 
     ----return to_return; 

    当然,这里只介绍了网页点击量数据的存储模型,如果我们一味地对计数器进行更新而不执行任何清理操作的话,那么程序最终将会因为存储了过多的数据而导致内存不足,由于我们事先已经将所有已知的计数器都记录到一个有序集合里面,所以对计数器进行清理只需要遍历这个有序集合,并删除其中的旧计数器即可。

    排序

    在Redis中可用于排序的有天然有序的有序集合(zset)和键(keys)类型中的SORT命令,其中SORT命令的功能非常强大,不仅可以对列表(list)、集合(set)和有序集合(zset)进行排序,还可以完成与关系型数据库中的连接查询相类似的任务,下面分别以两个例子来介绍各自的应用。

    帖子排序

    论坛中的帖子通常会有各种排序方式方便用户查看,比如按发帖时间排序、按回复时间排序、按回复数量排序、按阅读量排序等,这些TOP N场景对响应时间要求比较高,非常适宜用有序集合(zset)来缓存排序信息,其中排序字段即为分值(score)字段。

    • 例子
     127.0.0.1:6379> zadd page_rank 10 google.com 8 bing.com 6 163.com 9 baidu.com 
     (integer) 4 
     127.0.0.1:6379> zrange page_rank 0 -1 withscores 
    1)  "163.com" 
    2)  "6" 
    3)  "bing.com" 
    4)  "8" 
    5)  "baidu.com" 
    6)  "9" 
    7)  "google.com" 
    8)  "10" 

    SORT命令

    SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC | DESC] [ALPHA] [STORE destination]

    SORT命令提供了多种参数,可以对列表,集合和有序集合进行排序,此外还可以根据降序升序来对元素进行排序(DESC、ASC);将元素看作是数字还是二进制字符串来进行排序(ALPHA);使用排序元素之外的其他值作为权重来进行排序(BY pattern)。

    下面代码清单展示了SORT命令的具体功能使用。

    • 对列表(list)进行排序

    1.顺序

     127.0.0.1:6379> lpush mylist 30 10 8 19 
     (integer) 4 
     127.0.0.1:6379> sort price 
    1)  "8" 
    2)  "10" 
    3)  "19" 
    4)  "30" 

    2.逆序

     127.0.0.1:6379> sort price desc 
    1)  "30" 
    2)  "19" 
    3)  "10" 
    4)  "8" 

    3.使用alpha修饰符对字符串进行排序

     127.0.0.1:6379> lpush website www.163.com www.kaola.com www.baidu.com 
     (integer) 3 
     127.0.0.1:6379> sort website alpha 
    1)  "www.163.com" 
    2)  "www.baidu.com" 
    3)  "www.kaola.com" 
    • 使用limit修饰符限制返回结果
     127.0.0.1:6379> rpush num 1 4 2 7 9 6 5 3 8 10 
     (integer) 10 
     127.0.0.1:6379> sort num limit 0 5 
    1)  "1" 
    2)  "2" 
    3)  "3" 
    4)  "4" 
    5)  "5" 
    • 使用外部key进行排序

    可以使用外部key的数据作为权重,代替默认的直接对比键值的方式来进行排序。假设现在有用户数据如表1所示。

    uiduser_name_{uid}user_level_{uid}
    1helifu888
    2netease666
    3kaola777
    4ncr4444

    表1 用户数据示例

    以下将哈希表(hash)作为by和get的参数,by和get选项都可以用key->field的格式来获取哈希表中的域的值,其中key表示哈希表键,而field则表示哈希表的域。

    1.数据输入到Redis中

     127.0.0.1:6379> hmset user_info_1 name helifu level 888 
     OK 
     127.0.0.1:6379> hmset user_info_2 name netease level 666 
     OK 
     127.0.0.1:6379> hmset user_info_3 name kaola level 777 
     OK 
     127.0.0.1:6379> hmset user_info_4 name ncr level 4444 
     OK 

    2.by选项

    通过使用by选项,让uid按其他键的元素来排序。

    例如以下代码让uid键按照user_info_*->level的大小来排序。

     127.0.0.1:6379> sort uid by user_info_*->level 
    1)  "2" 
    2)  "3" 
    3)  "1" 
    4)  "4" 

    3.get选项

    使用get选项,可以根据排序的结果来取出相应的键值。

    例如以下代码先让uid键按照user_info_*->level的大小来排序,然后再取出

    user_info_ *->name的值。
     127.0.0.1:6379> sort uid by user_info_*->level get user_info_*->name 
    1)  "netease" 
    2)  "kaola" 
    3)  "helifu" 
    4)  "ncr" 

    现在的排序结果要比只使用by选项要直观得多。

    4.排序获取多个外部key

    可以同时使用多个get选项,获取多个外部键的值。

     127.0.0.1:6379> sort uid get # get user_info_*->level get user_info_*->name 
    1)  "1" 
    2)  "888" 
    3)  "helifu" 
    4)  "2" 
    5)  "666" 
    6)  "netease" 
    7)  "3" 
    8)  "777" 
    9)  "kaola" 
    10) "4" 
    11) "4444" 
    12) "ncr" 

    5.不排序获取多个外部key

     127.0.0.1:6379> sort uid by not-exists-key get # get user_info_*->level get user_info_*->name 
    1)  "4" 
    2)  "4444" 
    3)  "nc" 
    4)  "3" 
    5)  "777" 
    6)  "kaola" 
    7)  "2" 
    8)  "666" 
    9)  "netease" 
    10) "1" 
    11) "888" 
    12) "helifu" 
    • 保存排序结果
     127.0.0.1:6379> lrange old 0 -1 
    1)  "1" 
    2)  "3" 
    3)  "5" 
    6)  "2" 
    7)  "4" 
     127.0.0.1:6379> sort old store new 
     (integer) 5 
     127.0.0.1:6379> type new  list 
     127.0.0.1:6379> lrange new 0 -1 
    1)  "1" 
    2)  "2" 
    3)  "3" 
    4)  "4" 
    5)  "5" 

    SORT命令的时间复杂度用公式表示为O(N+M*log(M)),其中N为要排序的列表或集合内的元素数量,M为要返回的元素数量。如果只是使用SORT命令的get选项获取数据而没有进行排序,时间复杂度为O(N)。

    云环境下的实践

    在云服务中实现计数和排序,可以自己使用云主机搭建Redis服务,也可以使用云计算服务商提供的Redis服务。

    对于高可用和性能有要求的场景,建议使用云计算服务商提供的Redis服务。专业的服务商会从底层到应用本身进行良好的优化,可用率、性能指标也远高于自己搭建的Redis实例。同时,由于服务商提供了各种工具,开发运维成本也更低。

    以网易云为例,网易云基础服务提供了名为NCR(Netease Cloud Redis)的缓存服务,兼容开源Redis协议。并根据用户具体使用需求和场景,提供了主从版本和分布式集群版本两种架构。

    主从服务版

    如图2所示,主从版本实例都提供一主一从两个Redis实例,分别部署在不同可用域的节点上,以确保服务安全可靠。在单点故障时,主从服务通过主备切换来实现高可用。
    主从版本使用较低的成本提供了高可用服务,但是也存在无法并行扩展等问题,因此适合数据量有限、对高可用有要求的产品使用。

    图片描述
    图2 主从服务架构

    分布式集群

    分布式集群采用官方Redis集群方案,gossip/p2p的无中心节点设计实现,无代理设计客户端直接与Redis集群的每个节点连接,计算出Key所在节点直接在对应的Redis节点上执行命令,如图3所示,详细的过程请参考后续Redis Cluster的相关介绍。

    分布式集群采用多活模式,支持并行扩展,因此在性能、可用率方面有明显优势。但是由于分布式集群最少需要3个节点,因此成本会较高,适合对可用率、性能有较高要求的用户使用。

    图片描述
    图3 分布式集群架构

    秒杀

    把秒杀服务单列出来进行分析,主要有下面两个原因。

    • 秒杀服务的重要性:秒杀活动本身已经是很多业务推广的重要方式之一,大部分的电商类业务都会涉及这一促销方式。很多非直接秒杀的业务(如火车购票),在实际运行时也会碰到类似秒杀的场景。秒杀实际上就是在瞬时极大并发场景下如何保证系统正常运行的问题,而这种场景对很多系统都是无法避免的,因此在系统设计时,我们往往要考虑到秒杀的影响。
    • 系统实现难度:秒杀最能考验系统负载能力,瞬间涌入平时数十倍甚至数百倍的压力,对开发和运维人员来说都是噩梦,这也为系统设计带来了巨大的挑战。针对秒杀活动的处理,是一个系统性的设计,并不是单一模块或者层面可以解决的问题,需要从系统设计整体进行考量。

    处理秒杀的指导思路

    秒杀的核心问题就是极高并发处理,由于系统要在瞬时承受平时数十倍甚至上百倍的流量,这往往超出系统上限,因此处理秒杀的核心思路是流控和性能优化。

    流控

    • 请求流控

    尽可能在上游拦截和限制请求,限制流入后端的量,保证后端系统正常。

    因为无论多少人参与秒杀,实际成交往往是有限的,而且远小于参加秒杀的人数,因此可以通过前端系统进行拦截,限制最终流入系统的请求数量,来保证系统正常进行。

    • 客户端流控

    在客户端进行访问限制,较为合适的做法是屏蔽用户高频请求,比如在网页中设置5s一次访问限制,可以防止用户过度刷接口。这种做法较为简单,用户体验也尚可,可以拦截大部分小白用户的异常访问,比如狂刷F5。关键是要明确告知用户,如果像一些抢购系统那样假装提交一个排队页面但又不回应任何请求,就是赤裸裸的欺骗了。

    • Web端流控

    对客户端,特别是页面端的限流,对稍有编程知识或者网络基础的用户而言没有作用(可以简单修改JS或者模拟请求),因此服务端流控是必要的。服务端限流的配置方法有很多种,现在的主流Web服务器一般都支持配置访问限制,可以通过配置实现简单的流控。

    但是这种限制一般都在协议层。如果要实现更为精细的访问限制(根据业务逻辑限流),可以在后端服务器上,对不同业务实现访问限制。常见做法是可以通过在内存或缓存服务中加入请求访问信息,来实现访问量限制。

    • 后端系统流控

    上述的流控做法只能限制用户异常访问,如果正常访问的用户数量很多,就有后端系统压力过大甚至异常宕机的可能,因此需要后端系统流量控制。

    对于后端系统的访问限制可以通过异步处理、消息队列、并发限制等方式实现。核心思路是保证后端系统的压力维持在可以正常处理的水平。对于超过系统负载的请求,可以选择直接拒绝,以此来对系统进行保护,保证在极限压力的情况下,系统有合理范围内的处理能力。

    系统架构优化

    除了流控之外,提高系统的处理能力也是非常重要的,通过系统设计和架构优化,可以提高系统的吞吐量和抗压能力。关于通用系统性能的提升,已经超出本节的范围,这里只会提几点和秒杀相关的优化。

    • 读取加速:在秒杀活动中,数据需求一般都是读多写少。20万人抢2000个商品,最后提交的订单最多也就2000个,但是在秒杀过程中,这20万人会一直产生大量的读取请求。因此可以使用缓存服务对用户请求进行缓存优化,把一些高频访问的内容放到缓存中去。对于更大规模的系统,可以通过静态文件分离、CDN服务等把用户请求分散到外围设施中去,以此来分担系统压力。
    • 异步处理和排队:通过消息队列和异步调用的方式可以实现接口异步处理,快速响应用户请求,在后端有较为充足的时间来处理实际的用户操作,提高对用户请求的响应速度,从而提升用户体验。通过消息队列还可以隔离前端的压力,实现排队系统,在涌入大量压力的情况下保证系统可以按照正常速率来处理请求,不会被流量压垮。
    • 无状态服务设计:相对于有状态服务,无状态服务更容易进行扩展,实现无状态化的服务可以在秒杀活动前进行快速扩容。而云化的服务更是有着先天的扩容优势,一般都可以实现分钟级别的资源扩容。

    系统扩容

    这项内容是在云计算环境下才成为可能,相对于传统的IT行业,云计算提供了快速的系统交付能力(min VS. day),因此可以做到按需分配,在业务需要时实现资源的并行扩展。

    对一次成功的秒杀活动来说,无论如何限流,如何优化系统,最终产生数倍于正常请求的压力是很正常的。因此临时性的系统扩容必不可少,系统扩容包括以下3个方面。

    • 增加系统规格:可以预先增加系统容量,比如提高系统带宽、购买更多流量等。
    • 服务扩展:无状态服务+负载均衡可以直接进行水平扩展,有状态的服务则需要进行较为复杂的垂直扩展,增大实例规格。
    • 后端系统扩容:缓存服务和数据库服务都可以进行容量扩展。

    秒杀服务实践

    一般来说,流控的实现,特别是业务层流控,依赖于业务自身的设计,因此云计算提供的服务在于更多、更完善的基础设计,来支持用户进行更简单的架构优化和扩容能力。

    系统架构优化

    通过CDN服务和对象存储服务来分离静态资源,实现静态资源的加速,避免服务器被大量静态资源请求过度占用。要实现异步的消息处理,可以使用队列服务来传输消息,以达到消息异步化和流控。

    系统扩容

    云服务会提供按需计费的资源分配方式和分钟级甚至秒级的资源交付能力,根据需要快速进行资源定制和交付。

    内部系统可以通过负载均衡等服务实现并行扩展,在网易云基础服务中,用户可以直接使用Kubernetes的Replication Controller服务实现在线水平扩容。对于对外提供的Web系统,可以通过负载均衡服务实现水平在线扩展。

    对于后端系统来说,建议使用云计算服务商提供的基础服务来实现并行扩展。例如,网易云基础服务就提供了分布式缓存服务和数据库服务,支持在线扩容。

    全文检索

    搜索,是用户获取信息的主要方式。在日常生活中,我们不管是购物(淘宝)、吃饭(大众点评、美团网)还是旅游(携程、去哪儿),都离不开搜索的应用。搜索几乎成为每个网站、APP甚至是操作系统的标配。在用户面前,搜索通常只是展示为一个搜索框,非常干净简洁,但它背后的原理可没那么简单,一个框的背后包含的是一整套搜索引擎的原理。假如我们需要搭建一个搜索系统为用户提供服务,我们又需要了解什么呢?

    基本原理

    首先,我们需要知道全文检索的基本原理,了解全文检索系统在实际应用中是如何工作的。
    通常,在文本中查找一个内容时,我们会采取顺序查找的方法。譬如现在手头上有一本计算机书籍,我们需要查找出里面包含了“计算机”和“人工智能”关键字的章节。一种方法就是从头到尾阅读这本计算机书籍,在每个章节都留心是否包含了“计算机”和“人工智能”这两个词。这种线性扫描就是最简单的计算机文档检索方式。这个过程通常称为grepping,它来自于Unix下的一个文本扫描命令grep。在文本内进行grepping扫描很快,使用现代计算机会更快,并且在扫描过程中还可以通过使用正则表达式来支持通配符查找。总之,在使用现代计算机的条件下,对一个规模不大的文档集进行线性扫描非常简单,根本不需要做额外的处理。但是,很多情况下只采用上述扫描方式是远远不够的,我们需要做更多的处理。这些情况如下所述。

    • 大规模文档集条件下的快速查找。用户的数据正在进行爆发性的增长,我们可能需要在几十亿到上万亿规模下的数据进行查找。
    • 有时我们需要更灵活的匹配方式。比如,在grep命令下不能支持诸如“计算机NEAR人工智能”之类的查询,这里的NEAR操作符的定义可能为“5个词之内”或者“同一句子中”。
    • 需要对结果进行排序。很多情况下,用户希望在多个满足自己需求的文档中得到最佳答案。

    此时,我们不能再采用上面的线性扫描方式。一种非线性扫描的方式是事先给文档建立索引(Index)。回到上面所述的例子,假设我们让计算机对整书本预先做一遍处理,找出书中所有的单词都出现在了哪几个章节(由于单词会重复,通常都不会呈现爆炸式增长),并记录下来。此时再查找“计算机”和“人工智能”单词出现在哪几个章节中,只需要将保存了它们出现过的章节做合并等处理,即可快速寻找出结果。存储单词的数据结构在信息检索的专业术语中叫“倒排索引”(Inverted Index),因为它和正向的从章节映射到单词关系相反,是倒着的索引映射关系。

    这种先对整个文本建立索引,再根据索引在文本中进行查找的过程就是全文检索(Full-text Search)的过程。图4展示了全文检索的一般过程。

    图片描述
    图4 全文检索的一般过程

    首先是数据收集的过程(Gather Data),数据可以来源于文件系统、数据库、Web抓取甚至是用户输入。数据收集完成后我们对数据按上述的原理建立索引(Index Documents),并保存至Index索引库中。在图的右边,用户使用方面,我们在页面或API等获取到用户的搜索请求(Get Users’ Query),并根据搜索请求对索引库进行查询(Search Index),然后对所有的结果进行打分、排序等操作,最终形成一个正确的结果返回给用户并展示(Present Search Results)。当然在实现流程中还包含了数据抓取/爬虫、链接分析、分词、自然语言处理、索引结构、打分排序模型、文本分类、分布式搜索系统等技术,这是最简单抽象的流程描述。关于索引过程和搜索过程更详细的技术就不做更多介绍了,感兴趣的同学请参考其他专业书籍。

    开源框架

    根据上面的描述我们知道了全文检索的基本原理,但要是想自己从头实现一套搜索系统还是很困难的,没有一个专业的团队、一定的时间基本上做出不来,而且系统实现之后还需要面临生产环境等各种问题的考验,研发和维护成本都无比巨大。不过,现代的程序开发环境早已今非昔比,开源思想深入人心,开源软件大量涌现。没有特殊的需求,没有人会重新开发一套软件。我们可以站在开源巨人的肩膀上,直接利用开源软件的优势。目前市面上有较多的开源搜索引擎和框架,比较成熟和活跃的有以下几种。

    • Lucene
    • Solr
    • Elasticsearch
    • Sphinx

    我们分别介绍这几种开源方案,并比较一下它们的优劣。

    Lucene

    Lucene是一个Java语言开发的搜索引擎类库,也是目前最火的搜索引擎内核类库。但需要注意的是,Lucene本身还不是一套完整的搜索引擎解决方案,它作为一个类库只是包含了核心的搜索、索引等功能,如果想使用Lucene,还需要做一些额外的开发工作。

    优点:

    • Apache顶级项目,仍在持续快速进步。
    • 成熟的解决方案,有很多成功案例。
    • 庞大而活跃的开发社区,大量的开发人员。
    • 虽然它只是一个类库,但经过简单的定制和开发,就可以满足绝大部分常见的需求;经过优化,可以支持企业级别量级的搜索。

    缺点:

    • 需要额外的开发工作。系统的扩展、分布式、高可用性等特性都需要自己实现。

    Solr

    Solr是基于Lucene开发、并由开发Lucene的同一帮人维护的一套全文搜索系统,Solr的版本通常和Lucene一同发布。Solr是最流行的企业级搜索引擎之一。

    优点:

    • 由于和Lucene共同维护,Solr也有一个成熟、活跃的社区。
    • Solr支持高级搜索特性,比如短语搜索、通配符搜索、join、group等。
    • Solr支持高吞吐流量、高度可扩展和故障恢复。
    • 支持REST风格API,支持JSON、XML、CSV甚至二进制格式的数据;功能强大的综合管理页面、易于监控。
    • Solr已比较成熟、稳定。

    缺点:

    • 系统部署、配置及管理配置上的使用较为复杂。

    Elasticsearh

    Elasticsearch是一个实时的分布式搜索和分析引擎,和Solr一样,它底层基于Lucene框架。但它具有简单、易用、功能/性能强大等优点,虽然晚于Solr出现,但迅速成长,已成为目前当仁不让的最热门搜索引擎系统。

    优点:

    • 支持REST风格的HTTP API,并且事件完全受API驱动,几乎所有的操作都可以通过使用JSON格式的RESTful HTTP API完成。
    • 支持多租户/多索引(multi-tenancy),支持全面的高级搜索特性。
    • 索引模式自由(Schema Free),支持JSON格式数据;部署、配置简单,便于使用。
    • 强大的聚合(Aggregation)分析功能(取代Lucene传统的Facets),便于用户对数据进行统计分析。

    缺点:

    • 几乎无缺点,在性能和资源上较C++ 开发的Sphinx稍差。

    Sphinx

    Sphinx是基于C++ 开发的一个全文检索引擎,从最开始设计时就注重性能、搜索相关性及整合简单性等方面。Sphinx可以批量索引和搜索SQL数据库、NoSQL存储中的数据,并提供和SQL兼容的搜索查询接口。

    优点:

    • Sphinx采用C++ 开发,因此支持高速的构建索引及高性能的搜索。
    • 支持SQL/NoSQL搜索。
    • 方便的应用集成。
    • 更高级的相似度计算排序模型。

    缺点:

    • 社区、生态等不如Lucene系发达,可见的成功案例较少。

    应用选型

    通过上面的介绍,相信大家对各个开源搜索系统所适用的场景有了一定了解。

    如果是中小型企业,想快速上手使用搜索功能,可以选择Elasticsearch或Solr。如果对搜索性能和节省资源要求比较苛刻,可以考虑尝试Sphinx。如果有很多定制化的搜索功能需求,可以考虑在Lucene基础上做自定义开发。如果用于日志搜索,或者有很多的统计、分析等需求,可以选择Elasticsearch。

    开源方案实践

    如上述介绍,在实际开发当中已有了很多现成的开源方案可供选择,但我们还是需要额外再做一些事情。譬如集群搭建、维护和管理、高可用设计、故障恢复、最基本的机房申请及机器采购部署等。这也需要投入较高的人力和成本(虽然较自己研发已经节省很多),并且还需要配备专业的搜索开发及运维人员。

    而在网易云中,我们基于开源Elasticsearch系统提供了一套简单的方案。我们把专业、复杂的搜索系统服务化和简单化,并降低准入门槛和成本,让用户可以直接使用平台化的搜索服务。我们提供了全面的近实时、高可用、高可靠、高扩展等搜索系统强大功能,易于用户使用。用户使用网易云的云搜索后不再需要处理搜索系统的搭建与配置工作,而只需要在云搜索服务的产品管理平台申请建立服务实例并配置索引数据格式,申请完成后云搜索平台就会自动生成索引服务实例并提供全文检索服务。

    日志收集

    日志,一组随时间增加的有序记录,是开发人员最熟悉的一种数据。通常,日志可以用来搜索查看关键状态、定位程序问题,以及用于数据统计、分析等。

    日志也是企业生产过程中产生的伟大财富。日志可以用来进行商业分析、用户行为判断和企业战略决策等,良好地利用日志可以产生巨大的价值。所以,日志的收集不管是在开发运维还是企业决策中都十分重要。

    第三方数据收集服务

    在日志收集领域内,目前已经存在了种类繁多的日志收集工具,比较典型的有:rsyslog、syslog-ng、Logstash、FacebookScribe、ClouderaFlume、Fluentd和GraylogCollector等。

    rsyslog是syslog的增强版,Fedora、Ubuntu、RHEL6、CentOS6、Debian等诸多Linux发行版都已使用rsyslog替换syslog作为默认的日志系统。rsyslog采用C语言实现,占用的资源少,性能高。专注于安全性及稳定性,适用于企业级别日志记录需求。rsyslog可以传输100万+/s(日志条数)的数据到本地目的地。即使通过网络传输到远程目的地,也能达到几万至几十万条/每秒的级别。rsyslog默认使用inotify实现文件监听(可以更改为polling模式,实时性较弱),实时收集日志数据。

    rsyslog支持实时监听日志文件、支持通配符匹配目录下的所有文件(支持输出通配符匹配的具体文件名)、支持记录文件读取位置、支持文件Rotated、支持日志行首格式判断(多行合并成一行)、支持自定义tag、支持直接输出至file/mysql/kafka/elasticsearch等、支持自定义日志输出模板,以及支持日志数据流控。

    syslog-ng具有开源、可伸缩和可扩展等特点,使用syslog-ng,你可以从任何来源收集日志,以接近实时的处理,输出到各种各样的目的源。syslog-ng灵活地收集、分析、分类和关联日志,存储和发送到日志分析工具。syslog-ng支持常见的输入,支持BSDsyslog(RFC3164)、RFC5424协议、JSON和journald消息格式。数据提取灵活,内置一组解析器,可以构建非常复杂事情。简化复杂的日志数据,syslog-ng patterndb可以转化关联事件为一个统一格式。支持数据库存储,包括SQL(MySQL,PostgreSQL,Oracle)、MongoDB和Redis。syslog-ng支持消息队列,支持高级消息队列协议(AMQP)和面向简单的文本消息传递协议(STOMP)与更多的管道。syslog-ng设计原则主要包括更好消息过滤粒度和更容易在不同防火墙网段转发信息。前者能够进行基于内容和优先权/facility的过滤。后者支持主机链,即使日志消息经过了许多计算机的转发,也可以找出原发主机地址和整个转发链。

    Logstash是一个开源的服务器端数据处理管道,采集多种数据源的数据,转换后发送到指定目的源。数据通常以各种格式分散在许多孤立系统上。Logstash支持种类丰富的inputs,以事件的方式从各种源获取输入,包括日志、Web应用、数据存储设备和AWS服务等。在数据流从源到存储设备途中,Logstash过滤事件,识别字段名,以便结构化数据,更有利于数据分析和创造商业价值。除了Elasticsearch,Logstash支持种类丰富的outputs,可以把数据转发到合适的目的源。表2是几个典型产品的特性对比。

    名称开发语言性能所占资源支持I/O插件种类社区活跃度
    rsyslogC
    syslog-ngC
    LogStash/BeatsLogStash:Ruby Beats:Go
    FacebookScribeC++
    ClouderaFlumeJava
    FluentdRuby

    表2 典型日志收集产品特性比较

    技术选型

    由于日志收集的需求并非很复杂,此类工具大体上比较相似,用户只需要根据其特性选择合适自己需求的产品。

    通常来说,对于日志收集客户端资源占用要求较高的,可以选择C语音开发的rsyslog、syslog-ng。对于易用性要求较高,可以选择Logstash、Beats。对于日志收集后接入的后端有特殊需求,可以参考Fluentd是否可以满足。如果公司用的是Java技术栈,可以选用Cloudera Flume。

    架构实践


    除了基本的功能需求之外,一个互联网产品往往还有访问性能、高可用、可扩展等需要,这些统称为非功能需求。一般来说,功能需求往往可以通过开发业务模块来满足,而非功能需求往往要从系统架构设计出发,从基础上提供支持。

    随着用户的增加,系统出现问题的影响也会增大。试想一下,一个小公司的主页,或者个人开发维护的一个App的无法访问,可能不会有多少关注。而支付宝、微信的宕机,则会直接被推到新闻头条(2015年支付宝光纤被挖路机挖断),并且会给用户带来严重的影响:鼓足勇气表白却发现信息丢失?掏出手机支付却发生支付失败,关键是还没带现金!在用户使用高峰时,一次故障就会给产品带来很大的伤害,如果频繁出现故障则基本等同于死刑判决。

    同样,对一个小产品来说,偶发的延时、卡顿可能并不会有大的影响(可能已经影响到了用户,只是范围、概率较小)。而对于一个较为成熟的产品,良好的性能则是影响产品生死存亡的基本问题。试想一下,如果支付宝、微信经常出现卡顿、变慢,甚至在访问高峰时崩溃,那它们还能支撑起现在的用户规模,甚至成为基础的服务设施吗?可以说,良好的访问性能,是一个产品从幼稚到成熟所必须解决的问题,也是一个成功产品的必备因素。实际上,很多有良好创意、商业前景很好的互联网产品,就是因无法满足用户增长带来的性能压力而夭折。

    随着性能需求的不断增长,所需要考虑的因素越多,出问题的概率也越大。因此,用户数的不断增长带来的挑战和问题几乎呈几何倍数增加,如果没有良好的设计和规划,随着产品和业务的不断膨胀,我们往往会陷入“修改→引入新问题→继续调整→引入更多问题”的泥潭中无法自拔。

    在这一阶段,架构设计的重点不再是业务本身功能实现和架构的构建,而是如何通过优化系统架构,来满足系统的高可用、并行扩展和系统加速等需要。

    前端系统扩展

    可扩展性是大规模系统稳定运行的基石。随着互联网用户的不断增加,一个成功产品的用户量往往是数以亿计,无论多强大的单点都无法满足这种规模的性能需求。因此系统的扩展是一个成功互联网产品的必然属性,无法进行扩展的产品,注定没有未来。

    由于扩展性是一个非常大的范畴,并没有一个四海皆准的手段或者技术来实现,因此本节主要介绍较为通用的可扩展系统设计,并以网易云为例,来介绍基础设施对可扩展性的支持。

    无状态服务设计

    要实现系统的并行扩展,需要对原有的系统进行服务化拆分。在服务实现时,主要有两种实现方式,分别是无状态服务和有状态服务。

    特点

    无状态服务

    指的是服务在处理请求时,不依赖除了请求本身外的其他内容,也不会有除了响应请求之外的额外操作。如果要实现无状态服务的并行扩展,只需要对服务节点进行并行扩展,引入负载均衡即可。

    有状态服务

    指的是服务在处理一个请求时,除了请求自身的信息外,还需要依赖之前的请求处理结果。
    对于有状态服务来说,服务本身的状态也是正确运行的一部分,因此有状态服务相对难以管理,无法通过简单地增加实例个数来实现并行扩展。

    对比

    从技术的角度来看,有状态服务和无状态服务只是服务的两种状态,本身并没有优劣之分。在实际的业务场景下,有状态服务和无状态服务相比,有各自的优势。

    有状态服务的特性如下。

    • 数据局部性:数据都在服务内部,不需要依赖外部数据服务强并发,有状态服务可以把状态信息都放在服务内部,在并发访问时不用考虑冲突等问题,而本地数据也可以提供更好的存取效率。因此,单个有状态服务可以提供更强的并发处理能力。
    • 实现简单:可以把数据信息都放在本地,因此可以较少考虑多系统协同的问题,在开发时更为简单。

    无状态服务的特性如下。

    • 易扩展:可以通过加入负载均衡服务,增加实例个数进行简单的并行扩展易管理,由于不需要上下文信息,因此可以方便地管理,不需要考虑不同服务实例之间的差异。
    • 易恢复:对于服务异常,不需要额外的状态信息,只需要重新拉起服务即可。而在云计算环境下,可以直接建立新的服务实例,替代异常的服务节点即可。

    总体来看,有状态服务在服务架构较为简单时,有易开发、高并发等优势,而无状态服务的优势则体现在服务管理、并行扩展方面。随着业务规模的扩大、系统复杂度的增加,无状态服务的这些优势,会越来越明显。因此,对于一个大型系统而言,我们更推荐无状态化的服务设计。

    实践

    下面,我们根据不同的服务类型,来分析如何进行状态分离。

    常见的状态信息

    • Web服务:在Web服务中,我们往往需要用户状态信息。一个用户的访问过程,我们称为一个会话(Session),这也是Web服务中最为常见的状态信息。
    • 本地数据:在业务运行过程中,会把一些运行状态信息保留到本地内存或者磁盘中。
    • 网络状态:一些服务在配置时,会直接使用IP地址访问,这样在服务访问时就依赖相应的网络配置。一旦地址改变,就需要修改对应的配置文件。

    状态分离

    要把有状态的服务改造成无状态的服务,一般有以下两种做法。

    • 请求附带全部状态信息:这种做法适用于状态信息比较简单的情况(如用户信息,登录状态等)。优点是实现较为简单,不需要额外设施。缺点是会导致请求内容增加,因此在状态信息较多时并不适用。
    • 状态分离:即通过将状态信息分离到外部的独立存储系统中(一般是高速缓存数据库等),来把状态信息从服务中剥离出去。

    Web服务状态分离

    在Web服务中,两种状态分离模式都可以实现状态分离。

    • 使用Cookie:把会话信息保存在加密后的Cookie之中,每次请求时解析Cookie内容来判断是否登录。这种做法的优点是实现简单,不需要额外的系统支持。缺点是Cookie的大小有限制,不能保持较大的状态信息,还会增加每次请求的数据传输量,同时Cookie必须要使用可靠的加密协议进行加密,否则会有被人篡改或者伪造的风险。因此这种做法一般用来保持用户登录状态。
    • 共享Session:将Session信息保存在外部服务(共享内存、缓存服务、数据库等)中,在请求到来时再从外部存储服务中获取状态信息。这种做法没有状态信息大小的限制,也不会增加请求大小。但是需要可靠、高效的外部存储服务来进行支持。一般来说,可以直接使用云计算服务商提供的缓存服务。

    服务器本身状态分离

    对于依赖本地存储的服务,优先做法是把数据保存在公共的第三方存储服务中,根据内容的不同,可以保存在对象存储服务或者数据库服务中。

    如果很难把数据提取到外部存储上,也不建议使用本地盘保存,而是建议通过挂载云硬盘的方式来保持本地状态信息。这样在服务异常时可以直接把云硬盘挂载在其他节点上来实现快速恢复。

    对于网络信息,最好的做法是不要通过IP地址,而是通过域名来进行访问。这样当节点异常时,可以直接通过修改域名来实现快速的异常恢复。在网易云基础服务中,我们提供了基于域名的服务访问机制,直接使用域名来访问内部服务,减少对网络配置的依赖。

    在线水平扩展

    在线水平扩展能力是一个分布式系统需要提供的基本能力,也是在架构设计时需要满足的重要功能点。而水平扩展能力也是业务发展的硬性需求,从产品的角度出发,产品的业务流量往往存在着很大波动,具体如下。

    • 产品业务量增长:在这个信息病毒式传播的时代,一些热点应用的业务量可能会在很短时间内大量增长。
    • 周期性业务:客服服务、证券服务及春运购票等。这类活动往往都存在着很明显的周期性特征,会按照一定的周期(月、天、小时、分钟)进行波动。波峰和波谷的流量往往会有一个数量级以上的差异。
    • 活动推广:一次成功的活动推广往往会带来数倍甚至数十倍的流量,需要业务可以快速扩展,在很短的时间内提供数十倍甚至上百倍的处理能力。
    • 秒杀:秒杀活动是弹性伸缩压力最大的业务,会带来瞬时大量流量。
      为了应对这些场景,需要业务在一个很短的时间内提供强大的处理能力。而在业务低谷期,可以相应回收过剩的计算资源,减少消耗,达到系统性能和成本之间的平衡,提高产品的竞争力。

    准备工作

    产品对水平扩展的需求是一直存在的,但是受制于传统IT行业按天甚至按周计算的资源交付能力,弹性伸缩一直是一个美好的愿望。直到云计算这个基础设施完善之后,才使弹性伸缩的实现成为了可能。如果要实现弹性伸缩,需要以下几点的支持。

    • 资源快速交付能力:业务可以根据需要,动态并快速地申请、释放资源。这也是云计算提供的基础能力,根据云计算平台的不同,一般都会提供从秒级到分钟级的资源交付能力,相对于传统IT管理按天计算的交付水平有了巨大的提升,这也是弹性伸缩的基础。在云环境下,绝大部分资源交付、扩容操作都可以在分钟级别完成,从而为弹性伸缩提供了基础支撑。
    • 无状态服务设计:对于有状态服务来说,由于有着各种各样的状态信息,因此会使扩展的难度大大增加。因此,无状态话的服务设计,是弹性伸缩服务的前提。
    • 业务性能监控:只有了解业务实际的负载情况,才有弹性伸缩的可能。只有对业务承载能力、运行负载有了全面的了解和实时监控,才能制定出相应的扩展指标。对于云服务厂商来说,基本上都提供了对基础资源,如CPU、内存、网络等的监控能力。而对应的PaaS服务,还提供了应用层数据的详细分析,为更细粒度、更加精确的扩展提供了可能。
    • 统一的服务入口:只有提供了统一的服务入口,才可以在不影响用户的情况下实现后台服务的弹性伸缩。统一服务入口有两种实现机制,一种是在系统层面,通过负载均衡服务提供统一的流量入口,使用负载均衡服务统一进行管理。另一种是通过服务注册和发现机制,在服务层实现适配。对于外部访问,可以使用对外的负载均衡服务,对于内部服务,一般都会提供租户内部的负载均衡。业务方可以根据需要,使用对应的流量入口。

    实现要点

    前端系统一般都会采用无状态化服务设计,扩展相对简单。在实践中,有多种扩展方案,如通过DNS服务水平扩展、使用专有的apiserver、在SDK端分流及接入负载均衡等。其中负载均衡方案使用最广,综合效果也最好,可以满足绝大多数场景下的需要,下面就以负载均衡服务为例,介绍前端系统水平扩展的实现要点。

    协议选择

    负载均衡服务分为4层和7层服务,这两种并不是截然分开的,而是有兼容关系。4层负载均衡可以支持所有7层的流量转发,而且性能和效率一般也会更好。而7层负载均衡服务的优势在于解析到了应用层数据(HTTP层),因此可以理解应用层的协议内容,从而做到基于应用层的高级转发和更精细的流量控制。

    对于HTTP服务,建议直接采用7层负载均衡,而其他所有类型的服务,如WebSocket、MySQL和Redis等,则可以直接使用TCP负载均衡。

    无状态服务

    前端系统可扩展性需要在系统设计层面进行保证,较为通用的做法是无状态化的服务设计。因为无状态,所以在系统扩展时只需要考虑外网设施的支持,而不需要改动服务代码。对于有状态的服务,则尽量服务改造把状态分离出去,将状态拆分到可以扩展的第三方服务中去。

    高级流量转发

    对于7层负载均衡来说,由于解析到了协议层,因此可以基于应用层的内容进行流量转发。除了最常用的粘性会话(Sticky Session)外,最常用的转发规则有基于域名和URL的流量转发两种。

    • 基于域名的流量转发:外网的HTTP服务默认使用80端口提供,但经常会有多个不同域名的网站需要使用同样一个出口IP的情况。这时候就需要通过应用层解析,根据用户的访问域名把同一个端口的流量分发到不同的后端服务中去。域名流量转发是通过解析请求头的Host属性实现的,当浏览器通过域名访问时,会自动设置Host头。通过程序访问HTTP API接口时,一般的第三方库也会设置这个属性,但如果自己组装HTTP请求,则需要主动设置对应的Host头。
    • 基于URL的流量转发:对一些大型网站,或者基于REST风格的API接口,单纯通过域名进行分流已经无法满足分流要求。此外,还存在着同一个域名的服务,根据URL分流到不同后端集群的情况,这种情况就可以通过请求中的URL路径信息,进行进一步分流。一般的URL都会支持模糊匹配。

    后端系统扩展

    后端系统,一般指用户接入端(Web系统、长连接服务器)和各种中间件之后的后台系统。在这一阶段,最重要的后端系统就是两种,缓存服务和数据库服务。下面,我们分别以Redis缓存服务和MySQL数据库为例,来介绍后端系统水平扩展的技术和核心技术点。

    Redis水平扩展

    Redis去年发布了3.0版本,官方支持了Redis cluster即集群模式。至此结束了Redis没有官方集群的时代,在官方集群方案以前应用最广泛的就属Twitter发布的Twemproxy(https://github.com/twitter/twemproxy),国内的有豌豆荚开发的Codis(https://github. com/wandoulabs/codis)。

    下面我们介绍一下Twemproxy和Redis Cluster两种集群水平扩展。

    Twemporxy+Sentinel方案

    Twemproxy,也叫nutcraker,是Twitter开源的一个Redis和Memcache快速/轻量级代理服务器。

    Twemproxy内部实现多种hash算法,自动分片到后端多个Redis实例上,Twemproxy支持失败节点自动删除,它会检测与每个节点的连接是否健康。为了避免单点故障,可以平行部署多个代理节点(一致性hash算法保证key分片正确),Client可以自动选择一个。

    有了这些特性,再结合负载均衡和Sentinel就可以架构出Redis集群,如图5所示。

    图片描述
    图5 基于Twemporxy的Redis水平扩展

    • 负载均衡:实现Twemproxy的负载均衡,提高proxy的可用性和可扩张能力,使Twemproxy的扩容对应用透明。
    • Twemproxy集群:多个Twemproxy平行部署(配置相同),同时接受客户端的请求并转发请求给后端的Redis。
    • Redis Master-Slave主从组:Redis Master存储实际的数据,并处理Twemproxy转发的数据读写请求。数据按照hash算法分布在多个Redis实例上。Redis Slave复制master的数据,作为数据备份。在Master失效的时候,由Sentinel把Slave提升为Master。
    • Sentinel集群:检测Master主从存活状态,当Redis Master失效的时候,把Slave提升为新Master。

    水平扩展实现就可以将一对主从实例加入Sentinel中,并通知Twemporxy更新配置加入新节点,将部分key通过一致性hash算法分布到新节点上。

    Twemproxy方案缺点如下。

    • 加入新节点后,部分数据被动迁移,而Twemproxy并没有提供相应的数据迁移能力,这样会造成部分数据丢失。
    • LB(负载均衡)+ Twemproxy + Redis 3层架构,链路长,另外加上使用Sentinel集群保障高可用,整个集群很复杂,难以管理。

    Redis Cluster方案

    Redis Cluster是Redis官方推出的集群解决方案,其设计的重要目标就是方便水平扩展,在1000个节点的时候仍能表现良好,并且可线性扩展。

    Redis Cluster和传统的集群方案不一样,在设计的时候,就考虑到了去中心化、去中间件,也就是说,集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。

    数据的分配也没有使用传统的一致性哈希算法,取而代之的是一种叫做哈希槽(hash slot)的方式。Redis Cluster默认分配了16384个slot,当我们set一个key时,会用CRC16算法来取模得到所属的slot,然后将这个key分到哈希槽区间的节点上,具体算法是CRC16(key) % 16384。举个例子,假设当前集群有3个节点,那么:

    • 节点r1包含0到5500号哈希槽。
    • 节点r2包含5501到11000号哈希槽。
    • 节点r3包含11001到16384号哈希槽。

    集群拓扑结构如图4-3所示,此处不再重复给出。

    Redis Cluster水平扩展很容易操作,新节点加入集群中,通过redis-trib管理工具将其他节点的slot迁移部分到新节点上面,迁移过程并不影响客户端使用,如图6所示。

    图片描述
    图6 Redis Cluster水平扩展

    为了保证数据的高可用性,Redis Cluster加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点实时备份数据,当这个主节点瘫痪后,通过选举算法在从节点中选取新主节点,从而保证集群不会瘫痪。

    Redis Cluster其他具体细节可以参考官方文档,这里不再详细介绍。Redis Cluster方案缺点如下。

    • 客户端实现复杂,管理所有节点连接,节点失效或变化需要将请求转移到新节点。
    • 没有中心管理节点,节点故障通过gossip协议传递,有一定时延。

    数据库水平扩展

    单机数据库的性能由于物理硬件的限制会达到瓶颈,随着业务数据量和请求访问量的不断增长,产品方除了需要不断购买成本难以控制的高规格服务器,还要面临不断迭代的在线数据迁移。在这种情况下,无论是海量的结构化数据还是快速成长的业务规模,都迫切需要一种水平扩展的方法将存储成本分摊到成本可控的商用服务器上。同时,也希望通过线性扩容降低全量数据迁移对线上服务带来的影响,分库分表方案便应运而生。

    分库分表的原理是将数据按照一定的分区规则Sharding到不同的关系型数据库中,应用再通过中间件的方式访问各个Shard中的数据。分库分表的中间件,隐藏了数据Sharding和路由访问的各项细节,使应用在大多数场景下可以像单机数据库一样,使用分库分表后的分布式数据库。

    分布式数据库

    网易早在2006年就开始了分布式数据库(DDB)的研究工作,经过10年的发展和演变,DDB的产品形态已全面趋于成熟,功能和性能得到了众多产品的充分验证。

    图7是DDB的完整架构,由cloudadmin、LVS、DDB Proxy、SysDB及数据节点组成。

    • cloudadmin:负责DDB的一键部署、备份管理、监控报警及版本管理等功能。
    • LVS:负责将用户请求均匀分布到多个DDB Proxy上。
    • DDB Proxy:对外提供MySQL协议访问,实现SQL语法解析、分布式执行计划生成、下发SQL语句到后端数据库节点,汇总合并数据库节点执行结果。
    • SysDB:DDB元数据存储数据库,也基于RDS实现高可用。
    • RDS:底层数据节点,一个RDS存储多个数据分片。

    图片描述
    图7 网易DDB架构

    分布式执行计划

    分布式执行计划定义了SQL在分库分表环境中各个数据库节点上执行的方法、顺序和合并规则,是DDB实现中最为复杂的一环。如SQL:select * from user order by id limit 10 offset 10。

    这个SQL要查询ID排名在10~20之间的user信息,这里涉及全局ID排序和全局LIMIT OFFSET两个合并操作。对全局ID排序,DDB的做法是将ID排序下发给各个数据库节点,在DBI层再进行一层归并排序,这样可以充分利用数据库节点的计算资源,同时将中间件层的排序复杂度降到最低,例如一些需要用到临时文件的排序场景,如果在中间件做全排序会导致极大的开销。

    对全局LIMIT OFFSET,DDB的做法是将OFFSET累加到LIMIT中下发,因为单个数据节点中的OFFSET没有意义,且会造成错误的数据偏移,只有在中间件层的全局OFFSET才能保证OFFSET的准确性。

    所以最后下发给各个DBN的SQL变为:select * from user order by id limit 20。又如SQL:select avg(age) from UserTet group by name可以通过EXPLAIN语法得到SQL的执行计划,如图8所示。

    图片描述
    图8 分布式执行计划

    上述SQL包含GROUP BY分组和AVG聚合两种合并操作,与全局ORDER BY类似,GROUP BY也可以下发给数据节点、中间件层做一个归并去重,但是前提要将GROUP BY的字段同时作为ORDER BY字段下发,因为归并的前提是排序。对AVG聚合,不能直接下发,因为得到所有数据节点各自的平均值,不能求出全局平均值,需要在DBI层把AVG转化为SUM和COUNT再下发,在结果集合并时再求平均。

    DDB执行计划的代价取决于DBI中的排序、过滤和连接,在大部分场景下,排序可以将ORDER BY下发简化为一次性归并排序,这种情况下代价较小,但是对GROUP BY和ORDER BY同时存在的场景,需要优先下发GROUP BY字段的排序,以达到归并分组的目的,这种情况下,就需要将所有元素做一次全排序,除非GROUP BY和ORDER BY字段相同。

    DDB的连接运算有两种实现,第一种是将连接直接下发,若连接的两张表数据分布完全相同,并且在分区字段上连接,则满足连接直接下发的条件,因为在不同数据节点的分区字段必然没有相同值,不会出现跨库连接的问题。第二种是在不满足连接下发条件时,会在DBI内部执行Nest Loop算法,驱动表的顺序与FROM表排列次序一致,此时若出现ORDER BY表次序与表排列次序不一致,则不满足ORDER BY下发条件,也需要在DBI内做一次全排序。

    分库分表的执行计划代价相比单机数据库而言,更加难以掌控,即便是相同的SQL模式,在不同的数据分布和分区字段使用方式上,也存在很大的性能差距,DDB的使用要求开发者和DBA对执行计划的原理具有一定认识。

    分库分表在分区字段的使用上很有讲究,一般建议应用中80%以上的SQL查询通过分区字段过滤,使SQL可以单库执行。对于那些没有走分区字段的查询,需要在所有数据节点中并行下发,这对线程和CPU资源是一种极大的消耗,伴随着数据节点的扩展,这种消耗会越来越剧烈。另外,基于分区字段跨库不重合的原理,在分区字段上的分组、聚合、DISTINCT、连接等操作,都可以直接下发,这样对中间件的代价往往最小。

    分布式事务

    分布式事务是个历久弥新的话题,分库分表、分布式事务的目的是保障分库数据一致性,而跨库事务会遇到各种不可控制的问题,如个别节点永久性宕机,像单机事务一样的ACID是无法奢望的。另外,业界著名的CAP理论也告诉我们,对分布式系统,需要将数据一致性和系统可用性、分区容忍性放在天平上一起考虑。

    两阶段提交协议(简称2PC)是实现分布式事务较为经典的方案,适用于中间件这种数据节点无耦合的场景。2PC的核心原理是通过提交分阶段和记日志的方式,记录下事务提交所处的阶段状态,在组件宕机重启后,可通过日志恢复事务提交的阶段状态,并在这个状态节点重试,如Coordinator重启后,通过日志可以确定提交处于Prepare还是PrepareAll状态,若是前者,说明有节点可能没有Prepare成功,或所有节点Prepare成功但还没有下发Commit,状态恢复后给所有节点下发RollBack;若是PrepareAll状态,需要给所有节点下发Commit,数据库节点需要保证Commit幂等。与很多其他一致性协议相同,2PC保障的是最终一致性。

    2PC整个过程如图9所示。

    图片描述
    图9 两阶段提交协议

    在网易DDB中,DBI和Proxy组件都作为Coordinator存在,2PC实现时,记录Prepare和PrepareAll的日志必须sync,以保障重启后恢复状态正确,而Coordinator最后的Commit日志主要作用是回收之前日志,可异步执行。

    由于2PC要求Coordinator记日志,事务吞吐率受到磁盘I/O性能的约束,为此DDB实现了GROUP I/O优化,可极大程度提升2PC的吞吐率。2PC本质上说是一种阻塞式协议,两阶段提交过程需要大量线程资源,因此CPU和磁盘都有额外消耗,与单机事务相比,2PC在响应时间和吞吐率上相差很多,从CAP角度出发,可以认为2PC在一定程度上成全了C,牺牲了A。

    另外,目前MySQL最流行的5.5和5.6版本中,XA事务日志无法复制到从节点,这意味着主库一旦宕机,切换到从库后,XA的状态会丢失,可能造成数据不一致,MySQL 5.7版本在这方面已经有所改善。

    虽然2PC有诸多不足,我们依然认为它在DDB中有实现价值,DDB作为中间件,其迭代周期要比数据库这种底层服务频繁,若没有2PC,一次更新或重启就可能造成应用数据不一致。从应用角度看,分布式事务的现实场景常常无法规避,在有能力给出其他解决方案前,2PC也是一个不错的选择。

    对购物转账等电商和金融业务,中间件层的2PC最大问题在于业务不可见,一旦出现不可抗力或意想不到的一致性破坏,如数据节点永久性宕机,业务难以根据2PC的日志进行补偿。金融场景下,数据一致性是命根,业务需要对数据有百分之百的掌控力,建议使用TCC这类分布式事务模型,或基于消息队列的柔性事务框架,请参考第5章,这两种方案都在业务层实现,业务开发者具有足够掌控力,可以结合SOA框架来架构。原理上说,这两种方案都是大事务拆小事务,小事务变本地事务,最后通过幂等的Retry来保障最终一致性。

    弹性扩容

    分库分表数据库中,在线数据迁移也是核心需求,会用在以下两种场景中。

    • 数据节点弹性扩容:随着应用规模不断增长,DDB现有的分库可能有一天不足以支撑更多数据,要求DDB的数据节点具有在线弹性扩容的能力,而新节点加入集群后,按照不同的Sharding策略,可能需要将原有一些数据迁入新节点,如HASH分区,也有可能不需要在线数据迁移,如一些场景下的Range分区。无论如何,具备在线数据迁移是DDB支持弹性扩容的前提。
    • 数据重分布:开发者在使用DDB过程中,有时会陷入困局,比如一些表的分区字段一开始没考虑清楚,在业务初具规模后才明确应该选择其他字段。又如一些表一开始认为数据量很小,只要单节点分布即可,而随着业务变化,需要转变为多节点Sharding。这两种场景都体现了开发者对DDB在线数据迁移功能的潜在需求。

    无论是弹性扩容,还是表重分布,都可当作DDB以表或库为单位的一次完整在线数据迁移。该过程分为全量迁移和增量迁移两个阶段,全量迁移是将原库或原表中需要迁移的数据DUMP出来,并使用工具按照分区策略导入到新库新表中。增量迁移是要将全量迁移过程中产生的增量数据更新按照分区策略应用到新库新表。

    全量迁移的方案相对简单,使用DDB自带工具按照特定分区策略DUMP和Load即可。对增量迁移,DDB实现了一套独立的迁移工具Hamal来订阅各个数据节点的增量更新,Hamal内部又依赖DBI模块将增量更新应用到新库新表,如图10所示。

    图片描述
    图10 DDB增量迁移工具Hamal

    Hamal作为独立服务,与Proxy一样由DDB统一配置和管理,每个Hamal进程负责一个数据节点的增量迁移,启动时模拟Slave向原库拉取Binlog存储本地,之后实时通过DBI模块应用到新库新表,除了基本的迁移功能外,Hamal具备以下两个特性。

    • 并行复制:Hamal的并行复制组件,通过在增量事件之间建立有向无环图,实时判断哪些事件可以并行执行,Hamal的并行复制与MySQL的并行复制相比快10倍以上。
    • 断点续传:Hamal的增量应用具有幂等性,在网络中断或进程重启之后可以断点续传。

    全局表

    考虑一种场景:City表记录了国内所有城市信息,应用中有很多业务表需要与City做联表查询,如按照城市分组统计一些业务信息。假设City的主键和分区键都是CityId,若连接操作发生在中间件层,代价较高,为了将连接操作下发数据节点,需要让连接的业务表同样按照CityId分区,而大多数业务表往往不能满足这个条件。

    连接直接下发需要满足两个条件,数据分布相同和分区键上连接,除此之外,其实还有一种解法,可以把City表冗余到所有数据节点中,这样各个数据节点本地连接的集合便是所求结果。DDB将这种类型的表称之为全局表。

    全局表的特点是更新极少,通过2PC保障各个节点冗余表的一致性。可以通过在建表语句添加相关Hint指定全局表类型,在应用使用DDB过程中,全局表的概念对应用不可见。


    本文节选自《云原生应用架构实践》,网易云基础服务架构团队著。

    图片描述

    展开全文
  • 云原生(Cloud Native)的概念,由来自Pivotal的MattStine于2013年首次提出,被一直延续使用至今。这个概念是Matt Stine根据其多年的架构和咨询经验总结出来的一个思想集合,并得到了社区的不断完善,...

    云原生(Cloud Native)的概念,由来自Pivotal的MattStine于2013年首次提出,被一直延续使用至今。这个概念是Matt Stine根据其多年的架构和咨询经验总结出来的一个思想集合,并得到了社区的不断完善,内容非常多,包括DevOps、持续交付(Continuous Delivery)、微服务(MicroServices)、敏捷基础设施(Agile Infrastructure)和12要素(TheTwelve-Factor App)等几大主题,不但包括根据业务能力对公司进行文化、组织架构的重组与建设,也包括方法论与原则,还有具体的操作工具。采用基于云原生的技术和管理方法,可以更好地把业务生于“云”或迁移到云平台,从而享受“云”的高效和持续的服务能力。

    顾名思义,云原生是面向“云”而设计的应用,因此技术部分依赖于在传统云计算的3层概念(基础设施即服务(IaaS)、平台即服务(PaaS)和软件即服务(SaaS)),例如,敏捷的不可变基础设施交付类似于IaaS,用来提供计算网络存储等基础资源,这些资源是可编程且不可变的,直接通过API可以对外提供服务;有些应用通过PaaS服务本来就能组合成不同的业务能力,不一定需要从头开始建设;还有一些软件只需要“云”的资源就能直接运行起来为云用户提供服务,即SaaS能力,用户直接面对的就是原生的应用。

    应用基于云服务进行架构设计,对技术人员的要求更高,除了对业务场景的考虑外,对隔离故障、容错、自动恢复等非功能需求会考虑更多。借助云服务提供的能力也能实现更优雅的设计,比如弹性资源的需求、跨机房的高可用、11个9(99.999999999%)的数据可靠性等特性,基本是云计算服务本身就提供的能力,开发者直接选择对应的服务即可,一般不需要过多考虑本身机房的问题。如果架构设计本身又能支持多云的设计,可用性会进一步提高,比如Netflix能处理在AWS的某个机房无法正常工作的情况,还能为用户提供服务,这就是“云”带来的魔力,当然,云也会带来更多的隔离等问题。如图1-4所示,目前业界公认的云原生主要包括以下几个层面的内容。

    bb

    图1-4 云原生的内容

    敏捷基础设施
    正如通过业务代码能够实现产品需求、通过版本化的管理能够保证业务的快速变更,基于云计算的开发模式也要考虑如何保证基础资源的提供能够根据代码自动实现需求,并实现记录变更,保证环境的一致性。使用软件工程中的原则、实践和工具来提供基础资源的生命周期管理,这意味着工作人员可以更频繁地构建更强可控或更稳定的基础设施,开发人员可以随时拉取一套基础设施来服务于开发、测试、联调和灰度上线等需求。当然,同时要求业务开发具有较好的架构设计,不需要依赖本地数据进行持久化,所有的资源都是可以随时拉起,随时释放,同时以API的方式提供弹性、按需的计算、存储能力。

    技术人员部署服务器、管理服务器模板、更新服务器和定义基础设施的模式都是通过代码来完成的,并且是自动化的,不能通过手工安装或克隆的方式来管理服务器资源,运维人员和开发人员一起以资源配置的应用代码为中心,不再是一台台机器。基础设施通过代码来进行更改、测试,在每次变更后执行测试的自动化流程中,确保能维护稳定的基础设施服务。

    此外,基础设施的范围也会更加广泛,不仅包括机器,还包括不同的机柜或交换机、同城多机房、异地多机房等,这些内容也会在后续章节中逐一进行部分讨论。

    持续交付
    为了满足业务需求频繁变动,通过快速迭代,产品能做到随时都能发布的能力,是一系列的开发实践方法。它分为持续集成、持续部署、持续发布等阶段,用来确保从需求的提出到设计开发和测试,再到让代码快速、安全地部署到产品环境中。持续集成是指每当开发人员提交了一次改动,就立刻进行构建、自动化测试,确保业务应用和服务能符合预期,从而可以确定新代码和原有代码能否正确地集成在一起。持续交付是软件发布的能力,在持续集成完成之后,能够提供到预发布之类系统上,达到生产环境的条件,持续部署是指使用完全的自动化过程来把每个变更自动提交到测试环境中,然后将应用安全地部署到产品环境中,打通开发、测试、生产的各个环节,自动持续、增量地交付产品,也是大量产品追求的最终目的,当然,在实际运行的过程中,有些产品会增加灰度发布等环境。总之,它更多是代表一种软件交付的能力,过程示例请参考图1-5。

    bb

    图1-5 持续交付流程

    DevOps
    DevOps如果从字面上来理解只是Dev(开发人员)+Ops(运维人员),实际上,它是一组过程、方法与系统的统称,其概念从2009年首次提出发展到现在,内容也非常丰富,有理论也有实践,包括组织文化、自动化、精益、反馈和分享等不同方面。首先,组织架构、企业文化与理念等,需要自上而下设计,用于促进开发部门、运维部门和质量保障部门之间的沟通、协作与整合,简单而言组织形式类似于系统分层设计。其次,自动化是指所有的操作都不需要人工参与,全部依赖系统自动完成,比如上述的持续交付过程必须自动化才有可能完成快速迭代。再次,DevOps的出现是由于软件行业日益清晰地认识到,为了按时交付软件产品和服务,开发部门和运维部门必须紧密合作。总之,如图1-6所示,DevOps强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。

    bb

    图1-6 DevOps强调组织的沟通与协作

    微服务
    随着企业的业务发展,传统业务架构面临着很多问题。其一,单体架构在需求越来越多的时候无法满足其变更要求,开发人员对大量代码的变更会越来越困难,同时也无法很好地评估风险,所以迭代速度慢;其二,系统经常会因为某处业务的瓶颈导致整个业务瘫痪,架构无法扩展,木桶效应严重,无法满足业务的可用性要求;最后,整体组织效率低下,无法很好地利用资源,存在大量的浪费。因此,组织迫切需要进行变革。随着大量开源技术的成熟和云计算的发展,服务化的改造应运而生,不同的架构设计风格随之涌现,最有代表性的是Netflix公司,它是国外最早基于云进行服务化架构改造的公司,2008年因为全站瘫痪被迫停业3天后,它痛下决心改造,经过将近10年的努力,实现了从单架构到微服务全球化的变迁,满足了业务的千倍增长(如图1-7所示),并产生了一系列的最佳实践。

    bb

    图1-7 Netflix微服务化支撑业务千倍增长

    随着微服务化架构的优势展现和快速发展,2013年,MartinFlower对微服务概念进行了比较系统的理论阐述,总结了相关的技术特征。首先,微服务是一种架构风格,也是一种服务;其次,微服务的颗粒比较小,一个大型复杂软件应用由多个微服务组成,比如Netflix目前由500多个的微服务组成;最后,它采用UNIX设计的哲学,每种服务只做一件事,是一种松耦合的能够被独立开发和部署的无状态化服务(独立扩展、升级和可替换)。微服务架构如图1-8所示。

    bb

    图1-8 微服务架构示例

    由微服务的定义分析可知,一个微服务基本是一个能独立发布的应用服务,因此可以作为独立组件升级、灰度或复用等,对整个大应用的影响也较小,每个服务可以由专门的组织来单独完成,依赖方只要定好输入和输出口即可完全开发,甚至整个团队的组织架构也会更精简,因此沟通成本低、效率高。根据业务的需求,不同的服务可以根据业务特性进行不同的技术选型,是计算密集型还是I/O密集型应用都可以依赖不同的语言编程模型,各团队可以根据本身的特色独自运作。服务在压力较大时,也可以有更多容错或限流服务。

    微服务架构确实有很多吸引人的地方,然而它的引入也是有成本的,它并不是银弹,使用它会引入更多技术挑战,比如性能延迟、分布式事务、集成测试、故障诊断等方面,企业需要根据业务的不同的阶段进行合理的引入,不能完全为了微服务而“微服务”,本书第5章也会对如何解决这些问题提供对应不同方案的权衡。

    12要素
    “12要素”英文全称是The Twelve-Factor App,最初由Heroku的工程师整理起步,是集体贡献总结的智慧,如图1-9所示。根据基于云的软件开发模式,12要素比较贴切地描述了软件应用的原型,并诠释了使用原生云应用架构的原因。比如,一个优雅的互联网应用在设计过程中,需要遵循的一些基本原则和云原生有异曲同工之处。通过强化详细配置和规范,类似Rails的基于“约定优于配置”(convention over configuration)的原则,特别在大规模的软件生产实践中,这些约定非常重要,从无状态共享到水平扩展的过程,从松耦合架构关系到部署环境。基于12要素的上下文关联,软件生产就变成了一个个单一的部署单元;多个联合部署的单元组成一个应用,多个应用之间的关系就可以组成一个复杂的分布式系统应用。

    bb

    图1-9 12要素

    下面简要介绍图1-9中的这些原则。相信很多开发者在实际开发工作中已经很好地应用了其中的一些原则,只是没有意识到概念本身。对这些原则比较陌生的开发者,如果想了解更多的操作过程,请参阅《云原生时代下的12要素(12-Factor)应用与实践》一文。

    基准代码
    每一个部署的应用都在版本控制代码库中被追踪。在多个部署环境中,会有多种部署实例,单个应用只有一份代码库,多份部署相当于运行了该应用的多个实例,比如开发环境一个实例,测试环境、生产环境都有一个实例。

    实际上,在云计算架构中,所有的基础设施都是代码配置,即Infrastructure as Code(IaC),整个应用通过配置文件就可以编排出来,而不再需要手工的干预,做到基础服务也是可以追踪的。

    依赖
    应用程序不会隐式依赖系统级的类库,通过依赖清单声明所有依赖项,通过依赖隔离工具确保程序不会调用系统中存在,但清单中未声明依赖项,并统一应用到生产和开发环境。比如通过合适的工具(例如Maven、Bundler、NPM),应用可以很清晰地对部署环境公开和隔绝依赖性,而不是模糊地对部署环境产生依赖性。

    在容器应用中,所有应用的依赖和安装都是通过DockerFile来完成声明的,通过配置能明确把依赖关系,包括版本都明确地图形化展示出来,不存在黑盒。

    配置
    环境变量是一种清楚、容易理解和标准化的配置方法,将应用的配置存储于环境变量中,保证配置排除在代码之外,或者其他可能在部署环境(例如研发、展示、生产)之间区别的任何代码,可以通过操作系统级的环境变量来注入。

    实例根据不同的环境配置运行在不同的环境中,此外,实现配置即代码,在云环境中,无论是统一的配置中心还是分布式的配置中心都有好的实践方式,比如Docker的环境变量使用。

    后端服务
    不用区别对待本地或第三方服务,统一把依赖的后端作为一种服务来对待,例如数据库或者消息代理,作为附加资源,同等地在各种环境中被消耗。比如在云架构的基础服务中,计算、网络、存储资源都可以看作是一种服务去对待使用即可,不用区分是远程还是本地的。

    构建、发布、运行
    应用严格区分构建、发布、运行这3个阶段。3个阶段是严格分开的,一个阶段对应做一件事情,每个阶段有很明确的实现功能。云原生应用的构建流程可以把发布配置挪到开发阶段,包括实际的代码构建和运行应用所需的生产环境配置。在云原生应用中,基于容器的Build-Ship-Run和这3个阶段完全吻合,也是Docker对本原则的最佳实践。

    进程
    进程必须无状态且无共享,即云应用以一个或多个无状态不共享的程序运行。任何必要状态都被服务化到后端服务中(缓存、对象存储等)。

    所有的应用在设计时就认为随时随地会失败,面向失败而设计,因此进程可能会被随时拉起或消失,特别是在弹性扩容的阶段。

    端口绑定
    不依赖于任何网络服务器就可以创建一个面向网络的服务,每个应用的功能都很齐全,通过端口绑定对外提供所有服务,比如Web应用通过端口绑定(Port binding)来提供服务,并监听发送至该端口的请求(包括HTTP)。

    在容器应用中,应用统一通过暴露端口来服务,尽量避免通过本地文件或进程来通信,每种服务通过服务发现而服务。

    并发
    进程可以看作一等公民,并发性即可以依靠水平扩展应用程序来实现,通过进程模型进行扩展,并且具备无共享、水平分区的特性。

    在互联网的服务中,业务的爆发性随时可能发生,因此不太可能通过硬件扩容来随时提供扩容服务,需要依赖横向扩展能力进行扩容。

    易处理
    所有应用的架构设计都需要支持能随时销毁的特点,和状态的无关性保持一致,允许系统快速弹性扩展、改变部署及故障恢复等。

    在云环境中,由于业务的高低峰值经常需要能实现快速灵活、弹性的伸缩应用,以及不可控的硬件因素等,应用可能随时会发生故障,因此应用在架构设计上需要尽可能无状态,应用能随时随地拉起,也能随时随地销毁,同时保证进程最小启动时间和架构的可弃性,也可以提供更敏捷的发布及扩展过程。

    环境等价
    必须缩小本地与线上差异,确保环境的一致性,保持研发、测试和生产环境尽可能相似,这样可以提供应用的持续交付和部署服务。

    在容器化应用中,通过文件构建的环境运行能做到版本化,因此保证各个不同环境的差异性,同时还能大大减少环境不同带来的排错等成本沟通问题。

    日志
    每一个运行的进程都会直接标准输出(stdout)和错误输出(stderr)事件流,还可以将日志当作事件流作为数据源,通过集中服务,执行环境收集、聚合、索引和分析这些事件。

    日志是系统运行状态的部分体现,无论在系统诊断、业务跟踪还是后续大数据服务的必要条件中,Docker提供标准的日志服务,用户可以根据需求做自定义的插件开发来处理日志。

    管理进程
    管理或维护应用的运行状态是软件维护的基础部分,比如数据库迁移、健康检查、安全巡检等,在与应用长期运行的程序相同环境中,作为一次性程序运行。

    在应用架构模式中,比如Kubernetes里面的Pod资源或者dockerexec,可以随着其他的应用程序一起发布或在出现异常诊断时能通过相关的程序去管理其状态。

    云原生的内容非常广泛,目前没有系统的说明和完整的定义,上文介绍了云原生应用的基础组件和相关特点,可能读者对云原生应用的逻辑还存在一些困惑。为了更清楚地进行说明,我们总结了其依赖关系,如图1-10所示。

    bb

    图1-10 云原生内容的依赖关系

    首先,为了抓住商业机会,业务需要快速迭代,不断试错,因此,企业需要依赖拥有持续交付的能力,这些不仅包括技术需求还包括产品的需求,如何能拥有持续交付的能力,大而全的架构因为效率低下,显然是不合适的。于是演变出微服务架构来满足需求,通过把系统划分出一个个独立的个体,每个个体服务的设计依赖需要通过12要素的原则来规范完成。同样,如果系统被分成了几十个甚至几百个服务组件,则需要借助DevOps才能很好地满足业务协作和发布等流程。最后,DevOps的有效实施需要依赖一定的土壤,即敏捷的基础设施服务,现实只有云计算的模式才能满足整体要求。通过上述梳理,我们总结出面向云原生应用的3个不同层次的特点。

    高可用设计(Design for Availability),依据应用业务需求,高可用分为不同级别,比如不同区域、不同机房(跨城或同城)、不同机柜、不同服务器和不同进程的高可用,云原生应用应该根据业务的可用性要求设计不同级别的架构支持。

    可扩展设计(Design for Scale),所有应用的设计是无状态的,使得业务天生具有扩展性,在业务流量高峰和低峰时期,依赖云的特性自动弹性扩容,满足业务需求。

    快速失败设计(Design for Failure),即包括系统间依赖的调用随时可能会失败,也包括硬件基础设施服务随时可能宕机,还有后端有状态服务的系统能力可能有瓶颈,总之在发生异常时能够快速失败,然后快速恢复,以保证业务永远在线,不能让业务半死不活地僵持着。

    通过上面的基本描述及云原生应用的组成或特点,与容器技术相比可以得知,容器的特性天生就是按这些原则进行设计的。随着互联网业务的架构不断演进,从单体应用到分布式应用,甚至微服务架构应用中,12要素较好地为构建互联网化应用提供了统一的方法论和标准化,具有强大的生命力,每一条原则都是应用开发的珠玑。当然,在实践过程中,每一个原则也不是一成不变的,随着新的理念和技术出现,原有的因素会得到延伸和发展,会出现新的原则和应用,这套理论也适用于任意语言和后端服务(数据库、消息队列、缓存等)开发的应用程序,因此也作为云原生架构应用的基本指导原则之一.

    来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31558019/viewspace-2285476/,如需转载,请注明出处,否则将追究法律责任。

    转载于:http://blog.itpub.net/31558019/viewspace-2285476/

    展开全文
  • 云原生架构本身作为一种架构,也有若干架构原则作为应用架构的核心架构控制面,通过遵从这些架构原则可以让技术主管和架构师在做技术选择时不会出现大的偏差。 1.1 服务化原则 当代码规模超出小团队的合作范围时,...

    一、云原生架构原则

    云原生架构本身作为一种架构,也有若干架构原则作为应用架构的核心架构控制面,通过遵从这些架构原则可以让技术主管和架构师在做技术选择时不会出现大的偏差。

    1.1 服务化原则

    当代码规模超出小团队的合作范围时,就有必要进行服务化拆分了,包括拆分为微服务架构、小服务(Mini Service)架构,通过服务化架构把不同生命周期的模块分离出来,分别进行业务迭代,避免迭代频繁模块被慢速模块拖慢,从而加快整体的进度和稳定性。同时服务化架构以面向接口编程,服务内部的功能高度内聚,模块间通过公共功能模块的提取增加软件的复用程度。

    分布式环境下的限流降级、熔断隔仓、灰度、反压、零信任安全等,本质上都是基于服务流量(而非网络流量)的控制策略,所以云原生架构强调使用服务化的目的还在于从架构层面抽象化业务模块之间的关系,标准化服务流量的传输,从而帮助业务模块进行基于服务流量的策略控制和治理,不管这些服务是基于什么语言开发的。

    1.2 弹性原则

    大部分系统部署上线需要根据业务量的估算,准备一定规模的机器,从提出采购申请,到供应商洽谈、机器部署上电、软件部署、性能压测,往往需要好几个月甚至一年的周期;而这期间如果业务发生变化了,重新调整也非常困难。弹性则是指系统的部署规模可以随着业务量的变化自动伸缩,无须根据事先的容量规划准备固定的硬件和软件资源。好的弹性能力不仅缩短了从采购到上线的时间,让企业不用操心额外软硬件资源的成本支出(闲置成本),降低了企业的 IT 成本,更关键的是当业务规模面临海量突发性扩张的时候,不再因为平时软硬件资源储备不足而“说不”,保障了企业收益。

    1.3 可观测原则

    今天大部分企业的软件规模都在不断增长,原来单机可以对应用做完所有调试,但在分布式环境下需要对多个主机上的信息做关联,才可能回答清楚服务为什么宕机、哪些服务违反了其定义的 SLO、目前的故障影响哪些用户、最近这次变更对哪些服务指标带来了影响等等,这些都要求系统具备更强的可观测能力。可观测性与监控、业务探活、APM 等系统提供的能力不同,前者是在云这样的分布式系统中,主动通过日志、链路跟踪和度量等手段,让一次 APP 点击背后的多次服务调用的耗时、返回值和参数都清晰可见,甚至可以下钻到每次三方软件调用、SQL 请求、节点拓扑、网络响应等,这样的能力可以使运维、开发和业务人员实时掌握软件运行情况,并结合多个维度的数据指标,获得前所未有的关联分析能力,不断对业务健康度和用户体验进行数字化衡量和持续优化。

    1.4 韧性原则

    当业务上线后,最不能接受的就是业务不可用,让用户无法正常使用软件,影响体验和收入。韧性代表了当软件所依赖的软硬件组件出现各种异常时,软件表现出来的抵御能力,这些异常通常包括硬件故障、硬件资源瓶颈(如 CPU/ 网卡带宽耗尽)、业务流量超出软件设计能力、影响机房工作的故障和灾难、软件 bug、黑客攻击等对业务不可用带来致命影响的因素。韧性从多个维度诠释了软件持续提供业务服务的能力,核心目标是提升软件的 MTBF(Mean TimeBetween Failure,平均无故障时间)。从架构设计上,韧性包括服务异步化能力、重试 / 限流 / 降级 /熔断 / 反压、主从模式、集群模式、AZ 内的高可用、单元化、跨 region 容灾、异地多活容灾等。

    1.5 所有过程自动化原则

    技术往往是把“双刃剑”,容器、微服务、DevOps、大量第三方组件的使用,在降低分布式复杂性和提升迭代速度的同时,因为整体增大了软件技术栈的复杂度和组件规模,所以不可避免地带来了软件交付的复杂性,如果这里控制不当,应用就无法体会到云原生技术的优势。通过 IaC(Infrastructure as Code)、GitOps、OAM(Open Application Model)、Kubernetes operator 和大量自动化交付工具在 CI/CD 流水线中的实践,一方面标准化企业内部的软件交付过程,另一方面在标准化的基础上进行自动化,通过配置数据自描述和面向终态的交付过程,让自动化工具理解交付目标和环境差异,实现整个软件交付和运维的自动化。

    1.6 零信任原则

    零信任安全针对传统边界安全架构思想进行了重新评估和审视,并对安全架构思路给出了新建议。其核心思想是,默认情况下不应该信任网络内部和外部的任何人 / 设备 / 系统,需要基于认证和授权重构访问控制的信任基础,诸如 IP 地址、主机、地理位置、所处网络等均不能作为可信的凭证。零信任对访问控制进行了范式上的颠覆,引导安全体系架构从“网络中心化”走向“身份中心化”,其本质诉求是以身份为中心进行访问控制。

    零信任第一个核心问题就是 Identity,赋予不同的 Entity 不同的 Identity,解决是谁在什么环境下访问某个具体的资源的问题。在研发、测试和运维微服务场景下,Identity 及其相关策略不仅是安全的基础,更是众多(资源,服务,环境)隔离机制的基础;在员工访问企业内部应用的场景下,Identity 及其相关策略提供了灵活的机制来提供随时随地的接入服务。

    1.7 架构持续演进原则

    今天技术和业务的演进速度非常快,很少有一开始就清晰定义了架构并在整个软件生命周期里面都适用,相反往往还需要对架构进行一定范围内的重构,因此云原生架构本身也应该和必须是一个具备持续演进能力的架构,而不是一个封闭式架构。除了增量迭代、目标选取等因素外,还需要考虑组织(例如架构控制委员会)层面的架构治理和风险控制,特别是在业务高速迭代情况下的架构、业务、实现平衡关系。云原生架构对于新建应用而言的架构控制策略相对容易选择(通常是选择弹性、敏捷、成本的维度),但对于存量应用向云原生架构迁移,则需要从架构上考虑遗留应用的迁出成本 / 风险和到云上的迁入成本/ 风险,以及技术上通过微服务 / 应用网关、应用集成、适配器、服务网格、数据迁移、在线灰度等应用和流量进行细颗粒度控制。

    二、主要架构模式

    云原生架构有非常多的架构模式,这里选取一些对应用收益更大的主要架构模式进行讨论。

    2.1 服务化架构模式

    服务化架构是云时代构建云原生应用的标准架构模式,要求以应用模块为颗粒度划分一个软件,以接口契约(例如 IDL)定义彼此业务关系,以标准协议(HTTP、gRPC 等)确保彼此的互联互通,结合DDD(领域模型驱动)、TDD(测试驱动开发)、容器化部署提升每个接口的代码质量和迭代速度。服务化架构的典型模式是微服务和小服务(Mini Service)模式,其中小服务可以看做是一组关系非常密切的服务的组合,这组服务会共享数据,小服务模式通常适用于非常大型的软件系统,避免接口的颗粒度太细而导致过多的调用损耗(特别是服务间调用和数据一致性处理)和治理复杂度。

    通过服务化架构,把代码模块关系和部署关系进行分离,每个接口可以部署不同数量的实例,单独扩缩容,从而使得整体的部署更经济。此外,由于在进程级实现了模块的分离,每个接口都可以单独升级,从而提升了整体的迭代效率。但也需要注意,服务拆分导致要维护的模块数量增多,如果缺乏服务的自动化能力和治理能力,会让模块管理和组织技能不匹配,反而导致开发和运维效率的降低。

    2.2 Mesh 化架构模式

    Mesh 化架构是把中间件框架(比如 RPC、缓存、异步消息等)从业务进程中分离,让中间件 SDK与业务代码进一步解耦,从而使得中间件升级对业务进程没有影响,甚至迁移到另外一个平台的中间件也对业务透明。分离后在业务进程中只保留很“薄”的 Client 部分,Client 通常很少变化,只负责与Mesh 进程通讯,原来需要在 SDK 中处理的流量控制、安全等逻辑由 Mesh 进程完成。整个架构如下图所示。

     

    实施 Mesh 化架构后,大量分布式架构模式(熔断、限流、降级、重试、反压、隔仓……)都由Mesh 进程完成,即使在业务代码的制品中并没有使用这些三方软件包;同时获得更好的安全性(比如零信任架构能力)、按流量进行动态环境隔离、基于流量做冒烟 / 回归测试等。

    2.3 Serverless 模式

    和大部分计算模式不同,Serverless 将“部署”这个动作从运维中“收走”,使开发者不用关心应用在哪里运行,更不用关心装什么 OS、怎么配置网络、需要多少 CPU …… 从架构抽象上看,当业务流量到来 / 业务事件发生时,云会启动或调度一个已启动的业务进程进行处理,处理完成后云自动会关闭/ 调度业务进程,等待下一次触发,也就是把应用的整个运行时都委托给云。

    今天 Serverless 还没有达到任何类型的应用都适用的地步,因此架构决策者需要关心应用类型是否适合于 Serverless 运算。如果应用是有状态的,云在进行调度时可能导致上下文丢失,毕竟Serverless 的调度不会帮助应用做状态同步;如果应用是长时间后台运行的密集型计算任务,会得不到太多 Serverless 的优势;如果应用涉及到频繁的外部 I/O(网络或者存储,以及服务间调用),也因为繁重的 I/O 负担、时延大而不适合。Serverless 非常适合于事件驱动的数据计算任务、计算时间短的请求 / 响应应用、没有复杂相互调用的长周期任务。

    2.4 存储计算分离模式

    分布式环境中的 CAP 困难主要是针对有状态应用,因为无状态应用不存在 C(一致性)这个维度,因此可以获得很好的 A(可用性)和 P(分区容错性),因而获得更好的弹性。在云环境中,推荐把各类暂态数据(如 session)、结构化和非结构化持久数据都采用云服务来保存,从而实现存储计算分离。但仍然有一些状态如果保存到远端缓存,会造成交易性能的明显下降,比如交易会话数据太大、需要不断根据上下文重新获取等,则可以考虑通过采用 Event Log + 快照(或 Check Point)的方式,实现重启后快速增量恢复服务,减少不可用对业务的影响时长。

    2.5 分布式事务模式

    微服务模式提倡每个服务使用私有的数据源,而不是像单体这样共享数据源,但往往大颗粒度的业务需要访问多个微服务,必然带来分布式事务问题,否则数据就会出现不一致。架构师需要根据不同的场景选择合适的分布式事务模式。

    • 传统采用 XA 模式,虽然具备很强的一致性,但是性能差;
    • 基于消息的最终一致性(BASE)通常有很高的性能,但是通用性有限,且消息端只能成功而不能触发消息生产端的事务回滚;
    • TCC 模式完全由应用层来控制事务,事务隔离性可控,也可以做到比较高效;但是对业务的侵入性非常强,设计开发维护等成本很高;
    • SAGA 模式与 TCC 模式的优缺点类似但没有 try 这个阶段,而是每个正向事务都对应一个补偿事务,也是开发维护成本高;
    • 开源项目 SEATA 的 AT 模式非常高性能且无代码开发工作量,且可以自动执行回滚操作,同时也存在一些使用场景限制。

    2.6 可观测的架构

    可观测架构包括 Logging、Tracing、Metrics 三个方面,其中 Logging 提供多个级别(verbose/debug/warning/error/fatal)的详细信息跟踪,由应用开发者主动提供;Tracing 提供一个请求从前端到后端的完整调用链路跟踪,对于分布式场景尤其有用;Metrics 则提供对系统量化的多维度度量。

    架构决策者需要选择合适的、支持可观测的开源框架(比如 OpenTracing、OpenTelemetry),并规范上下文的可观测数据规范(例如方法名、用户信息、地理位置、请求参数等),规划这些可观测数据在哪些服务和技术组件中传播,利用日志和 tracing 信息中的 span id/trace id,确保进行分布式链路分析时有足够的信息进行快速关联分析。

    由于建立可观测性的主要目标是对服务 SLO(Service Level Objective)进行度量,从而优化SLA,因此架构设计上需要为各个组件定义清晰的 SLO,包括并发度、耗时、可用时长、容量等。

    2.7 事件驱动架构

    事件驱动架构(EDA,Event Driven Architecture)本质上是一种应用 / 组件间的集成架构模式,典型的事件驱动架构如下图:

     

    事件和传统的消息不同,事件具有 schema,所以可以校验 event 的有效性,同时 EDA 具备 QoS保障机制,也能够对事件处理失败进行响应。事件驱动架构不仅用于(微)服务解耦,还可应用于下面的场景中:

    • 增强服务韧性:由于服务间是异步集成的,也就是下游的任何处理失败甚至宕机都不会被上游感知,自然也就不会对上游带来影响;
    • CQRS(Command Query Responsibility Segregation):把对服务状态有影响的命令用事件来发起,而对服务状态没有影响的查询才使用同步调用的 API 接口;结合 EDA 中的 Event Sourcing 可以用于维护数据变更的一致性,当需要重新构建服务状态时,把 EDA 中的事件重新“播放”一遍即可;
    • 数据变化通知:在服务架构下,往往一个服务中的数据发生变化,另外的服务会感兴趣,比如用户订单完成后,积分服务、信用服务等都需要得到事件通知并更新用户积分和信用等级;
    • 构建开放式接口:在 EDA 下,事件的提供者并不用关心有哪些订阅者,不像服务调用的场景 —— 数据的产生者需要知道数据的消费者在哪里并调用它,因此保持了接口的开放性;
    • 事件流处理:应用于大量事件流(而非离散事件)的数据分析场景,典型应用是基于 Kafka 的日志处理;基于事件触发的响应:在 IoT 时代大量传感器产生的数据,不会像人机交互一样需要等待处理结果的返回,天然适合用 EDA 来构建数据处理应用。

    三、典型的云原生架构反模式

    技术往往像一把双刃剑,阿里在自己和帮助客户做云原生架构演进的时候,会充分考虑根据不同的场景选择不同的技术,下面是我们总结的一些典型云原生架构反模式。

    3.1 庞大的单体应用

    庞大单体应用的最大问题在于缺乏依赖隔离,包括代码耦合带来的责任不清、模块间接口缺乏治理而带来变更影响扩散、不同模块间的开发进度和发布时间要求难以协调、一个子模块不稳定导致整个应用都变慢、扩容时只能整体扩容而不能对达到瓶颈的模块单独扩容…… 因此当业务模块可能存在多人开发的时候,就需要考虑通过服务化进行一定的拆分,梳理聚合根,通过业务关系确定主要的服务模块以及这些模块的边界、清晰定义模块之间的接口,并让组织关系和架构关系匹配。

    阿里巴巴在淘宝业务快速发展阶段,就遇到过上百人维护一个核心单体应用,造成了源码冲突、多团队间协同代价高的严重问题。为了支撑不断增长的流量,该应用需要不断增加机器,很快后端数据库连接很快就到达了上限。在还没有“微服务”概念的 2008 年,阿里巴巴决定进行服务化架构拆分,当时思路就是微服务架构,第一个服务从用户中心开始建设,后续交易、类目、店铺、商品、评价中心等服务陆续从单体中独立出来,服务之间通过远程调用通信,每个中心由独立团队专门维护,从而解决了研发协同问题,以及按规模持续水平扩展的问题。

    3.2 单体应用“硬拆”为微服务

    服务的拆分需要适度,过分服务化拆分反而会导致新架构与组织能力的不匹配,让架构升级得不到技术红利,典型的例子包括:

    • 小规模软件的服务拆分:软件规模不大,团队人数也少,但是为了微服务而微服务,强行把耦合度高、代码量少的模块进行服务化拆分,一次性的发布需要拆分为多个模块分开发布和维护;
    • 数据依赖:服务虽然拆分为多个,但是这些服务的数据是紧密耦合的,于是让这些服务共享数据库,导致数据的变化往往被扇出到多个服务中,造成服务间数据依赖;
    • 性能降低:当耦合性很强的模块被拆分为多个微服务后,原来的本地调用变成了分布式调用,从而让响应时间变大了上千倍,导致整个服务链路性能急剧下降。

    3.3 缺乏自动化能力的微服务

    软件架构中非常重要的一个维度就是处理软件复杂度问题,一旦问题规模提升了很多,那就必须重新考虑与之适应的新方案。在很多软件组织中,开发、测试和运维的工作单位都是以进程为单位,比如把整个用户管理作为一个单独的模块进行打包、发布和运行;而进行了微服务拆分后,这个用户管理模块可能被分为用户信息管理、基本信息管理、积分管理、订单管理等多个模块,由于仍然是每个模块分别打包、发布和运行,开发、测试和运维人员的人均负责模块数就直线上升,造成了人均工作量增大,也就增加了软件的开发成本。

    实际上,当软件规模进一步变大后,自动化能力的缺失还会带来更大的危害。由于接口增多会带来测试用例的增加,更多的软件模块排队等待测试和发布,如果缺乏自动化会造成软件发布时间变长,在多环境发布或异地发布时更是需要专家来处理环境差异带来的影响。同时更多的进程运行于一个环境中,缺乏自动化的人工运维容易给环境带来不可重现的影响,而一旦发生人为运维错误又不容易“快速止血”,造成了故障处理时间变长,以及使得日常运维操作都需要专家才能完成。所有这些问题导致软件交付时间变长、风险提升以及运维成本的增加。

     

     

     

     

    展开全文
  • 这里写目录标题云原生的基础架构1. 微服务2. 容器3. 服务网格5. 声明式 API云原生应用的特征:云原生与“12 因素”1. 方法和核心思想2. 编码、部署和运维原则3. 具体内容小结参考 云原生的基础架构 云原生中既有...
  • 云原生架构之前(即传统飞云原生应用),底层平台负责向上提供运行资源,而应用需要满足业务需求和非业务需求。为了更好地使代码复用,通用性好的非业务需求的实现,往往会以类库和开发框架的方式提供。在SoA、 ...
  • ACNA 的概念阿里巴巴为大量各行各业的企业客户提供了基于阿里服务的解决方案和最佳实践,以帮助企业完成数字化转型,并积累了大量经验和教训。阿里巴巴将企业的核心关注点、企业组织与 IT 文...
  • 当然啦,首先你得有个域名啊(如果有域名的大佬,...一般在买阿里ECS的时候,应该就一起买了吧.好像组合买有优惠.没有买的可以去控制台->域名那里买一个,然后,买完后还得备案,不备案无法使用.这个过程大概1-2周. ...
  • 云原生架构未来发展趋势 容器技术发展趋势 趋势一:无处不在的计算催生新一代容器实现 趋势二:原生操作系统开始浮现 趋势三: Serverless 容器技术逐渐成为市场主流 趋势四:动态、混合、分布式的环境将成为新...
  • 由我(山金孝)和业内技术专家潘晓华、刘世民(Sammy Liu)共同编著的云原生架构与实践书籍《OpenShift云原生架构:原理与实践》,于2020年3月已由机械工业出版社出版,目前可在京东等各大商店购买! 本书深入剖析...
  • 快速了解云原生架构

    2021-02-01 11:09:23
    简介: 云原生架构本质上也是一种软件架构,最大的特点是在环境下运行,也算是微服务的一种延伸。 起源 1. 原生(Cloud Native)的由来 原生的概念最早开始于 2010 年,在当时 Paul Fremantle 的一篇博客中...
  • 网易云原生架构实践之服务治理

    千次阅读 2018-05-18 17:39:35
    本文由 网易 发布云原生(Cloud Native)的高阶实践是分布式服务化架构。一个良好的服务化架构,需要良好的服务发现、服务治理、服务编排等核心能力。本文为读者解析网易的服务治理策略及其典型实践。 网易...
  • 云原生是指导企业应用上云的方法和技术体系,包含应用的开发、交付、运行时等阶段, Cloud Native 可以理解为: Cloud 表示应用运行在云端,而非传统的 IDC; Native 表示应用从设计之初即考虑到的环境,...
  • 本文节选自《未来架构:从服务化到云原生》一书,张亮、吴晟、敖小剑、宋净超合著,由电子工业出版社博文视点出版,已获得授权。本书对快速演进中的云原生数据架构、典型分布式数据库...
  • 为什么说原生重构了互联网产品开发...发展至今,云原生架构已然成为互联网行业的技术热点,并在很大程度上推动了 IT 成本的降低和企业的发展。不过为便于你更好地理解和学习这部分内容,在本节课的开始,我们就先来
  • 试题二 论云原生架构及其应用论题,依次从以下三个方面进行论述: 1.概要叙述你参与管理和开发的软件项目以及承担的主要工作。 2.服务化,强性,可观测性和自动化是原生架构重复的四类设计原则,请简要对这四类...
  • 简介:在8月20日“阿里巴巴技术质量精品课”上,来自蚂蚁的经国分享了对云原生时代微服务的高可用架构设计的全面解析,为大家介绍了应用架构演进路径、云原生时代的技术福利、高可用架构的设计原则以及经典案例的...
  • 随着微服务和云原生相关技术的发展,应用程序的架构模式已从传统的单体架构或分层架构转向了分布式的计算架构。尽管分布式架构本身有一定的开发成本和运维成本,但它所带来的收益是显而易见的。 在实现分布式应用...
  • 本文根据 8 月 11 日 SOFA Meetup#3 广州站 《蚂蚁金服在云原生架构下的可观察性的探索和实践》主题分享整理。现场回顾视频以及 PPT 查看地址见文末链接。 前言 随着应用架构往原生的方向发展,传统监控技术...
  • ArchSummit2021年全球架构师峰会于4月25日-26日在上海举办,袋鼠运维开发技术专家沙章利(花名:浣熊)应邀出席此次峰会,并在4月26日下午的《弹性架构实践》专题会场上为大家带来《弹性云原生大数据系统架构实践...
  • 拿到成绩后写了一篇关于七天复习考过系统架构师的文章,作为一个自学者,深知网上系统架构师的资料搜集不易,所以将自己收集的系统架构师资料和自己做的笔记分享了出来,希望能给考友们提供一点帮助。 后来在软考群...
  • 云原生架构旨在将云应用中的非业务代码部分进行最大化的剥离,从而让设施接管应用中原有的大量非功能特性(如弹性、韧性、安全、可观测性、灰度等),使业务不再有非功能性业务中断困扰的同时,具备轻量、敏捷、...
  • 快速了解云原生架构 作者:潘义文(空易) 原文链接 简介: 云原生架构本质上也是一种软件架构,最大的特点是在环境下运行,也算是微服务的一种延伸。 1. 原生(Cloud Native)的由来 原生的概念最早开始于 ...
  • 通过云原生技术(如容器,不可变基础设施和声明式API等),使得企业在公有、私有和混合环境构建和运行应用变得更加容易,更能充分利用环境的优势,加速了企业应用迭代、降低资源成本、提高系统容错性和...
  • 在存储服务演进过程中,每一种业务类型、新技术方向都会对存储的架构、性能、可用性、稳定性等提出新的要求,而在当今技术浪潮走到云原生技术普及的时代,存储服务需要哪些特性来支持应用呢? 从本文开始,我们将用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,153
精华内容 6,861
关键字:

论云原生架构及其应用