微服务应该怎样服务后端业务信息系统业务安全服务

1号店交易系统架构如何向「高并发高可用」演进
声明:本文内容来自于TOP100Summit旗下技术沙龙品牌into100沙龙第17期:&&。
嘉宾:张立刚,1号店架构部-OMS订单管理平台负责人,负责1号店订单、库存、拆单、运费、第三方平台订单等电商核心交易系统。2012年7月加入1号店,作为负责人及项目经理,主导并参与了1号店SOA治理、订单Service化、订单水平拆库&去Oracle迁Mysql、无线性能优化及拆pool、运费体系重构、库存准确率优化等重要项目,负责1号店与Tmall、百度、当当、B2B2C平台等第三方平台订单业务。
以下为分享整理正文:
轻量级电商的架构和痛点
大家看上图,一个轻量级的电商网站应用架构就是这样的,比如说你现在想做一个电商网站,你是创业公司,两三个人开始做,估计架构就是这样的。前端有PC、App和H5,有表现层、业务逻辑层和数据访问层等。
重量级的电商网站应用架构是怎么重的呢?很简单,随着业务的扩展,业务量多了、代码量多了、数据量大了、并发量高了。1号店是一家电商网站,但严格来讲并不是百分之百的互联网公司,我们更多是业务驱动型而不是技术驱动型公司。因为技术是为业务服务的,反过来说,技术也可以驱动业务,如果我们的技术能力支持不到1号店的业务体量的增长,支持不了那么高的并发量的话,网站很容易就挂了。
一些轻量级电商网站的架构痛点是什么?
首先说它的特点,业务高速发展,业务形式多样,人员规模爆增。这里所说的人员更多是技术人员,一开始是两三个人,可能是面对面的,后来变成几十人、几百人甚至是几千人上万人的规模。当然它的请求量、并发量、数据存储量都非常的高。
痛点也很多,首先是代码耦合,一开始可能就一个人就全部搞定了。因为一开始就一、两个人,不需要那么多。但从业务端的增长来看,一、两个人进行业务支持就有问题了,业务响应慢,业务互相影响。还有在出现问题时定位很难,有的时候还会责任不清,遇到一些互相扯皮的情况,表现出来的就是系统不稳定。
表结构也非常混乱,今天来一个业务加一个,明天来个业务又加一个。数据库单点,也是我们非常大的痛点,一旦数据库挂了系统就彻底崩溃了。
还有监控预警的问题,这是一个非常大的痛点。我们知道类似支付宝红包、微信红包等的监控预警甚至是秒级的,分钟级确实是不够用,不可能说发一个红包,1分钟还没有抢到,用户会一直不停地戳屏幕,对系统带来的压力反而更大。
所以,这个时候我们就需要做一些扩展性的工作。最简单的就是按业务扩展,一个小公司大概到十个、几十个人左右,大概会做这样的工作。如网站上的表现层,一开始页面代码都耦合在一起,然后我们会把详情页、搜索页、团购区分,包括域名也会区分开。慢慢的业务多了之后,会把普通购物、团购、虚拟业务等剥离开来,从数据库层来说,慢慢会把产品、用户、订单及其他一些业务数据等都进行分拆,主要是从物理上隔离开来。这个时候总体来说,架构本身变化不大,只是简单的切分一下。当然这个时候产品、用户、订单等这个层面的划分要有一定的规则和边界。
电商网站演进之路
一个小网站变成一个大网站是一个架构演进的过程。举例子来说,现在有一个老师,他讲课的时候有很多学生都在同一个教室,这边是一年级的,这边是二年级的,这边是三年级的,先给一年级讲课二三年级的自己复习做功课,再给二年级的讲课一三年级的复习做功课,在前期还能忙过来。但到了一定的时候,学生开始多了,教室坐不下了。业务也多了,我不光教语文、数学,我还要教体育、音乐、美术了,这个时候学校的规模大了。学校的规模大了之后,相关的配套设施就要跟着上去。这个时候要有校长、主任、班主任、音乐英语老师…有后勤的,甚至是保洁阿姨,甚至还有食堂了,和大学一样了。
这个例子可以类比我们的架构变化。首先是业务变化,这相当于课程的变化;第二,架构变化,就相当于学校的规模大了之后,相关的配套设施都要跟上;第三个就是人员扩大了,就是我们的开发维护人员多了,这个时候我们不可能还像以前那样,几百号人在那里弄一个工程,你必须要进行分割剥离。
那么我们怎么把它做大?没有别的路,就是拆分扩展,而且扩展不能是简单的横向扩展,不能说来一个业务就简单地按业务来拆。因为可能一个业务量就非常的大,比如说我们的团购体量就非常大,仅仅团购业务的订单量已经很大了,自己本身不能再扩了那怎么办?从我的总结来说就是拆,把大的拆成小的,不断地拆。包括现在提到的微服务,当然现在微服务还没有明确的定义。这和我们的SOA架构是分不开的,只不过是一种形式而已。
现在IT界有很多的概念,就好像敏捷一样,两个星期做一个迭代,以前是一个项目做好几个月。一开始我不太理解什么是敏捷,有什么变化呢?我觉得大概就是把大的拆成小的,以前是做两个月的,我们把它分成N个两个星期的,在两个星期的迭代当中,你该做的还是逃不了,你还是要做需求分析,做设计,做开发,做测试,做上线。这个过程没有变,只是说把任务给拆分小了。
在拆的时候,从表现层来看,可以从UI展示和UI逻辑上进行拆分。在逻辑层上,比如说你要提交订单,在提交订单后面的操作包括有订单的服务、接口,这是我们的后端业务逻辑控制层。拆的时候,原来业务逻辑和数据访问都在一层,大家知道,以前在JSP上可以在页面上直接去连数据库进行DB操作。 我们从业务逻辑层进行拆分,拆分成控制层和Service层及数据库操作层。Service层就涉及到刚刚讲到的微服务,而Service层也分为复杂的Service和简单的Service。基础Service层相对来说业务逻辑比较单一,但是又相对比较完整,聚合Service则包括了完整的业务逻辑。
我们的技术团队也是从一个小团队发展到上千人的规模,网站也经历这样一个变革,刚开始的时候就是一个简单的MVC架构,后来这个架构不适应业务的发展和人员规模了。在三五百人的时候我们还是那个架构,很多人在维护一个大的工程项目,经常出问题。
而且刚刚开始的时候是没有无线端的,无线端也是近几年才开始做起来的。
后来要做无线端,怎么办?把PC端的代码包复制一份给无线端用,这个时候问题就不断的来了,因为PC端的逻辑不可能完全符合无线端的要求。
架构演进的准备工作
这样我们开始做比较大的架构层面的规划,&最大的一个就是业务逻辑层的拆分-SOA服务化,另一个是DB层的水平拆库&。拆分之后,理论上它就具有了无限的扩展能力,比如说订单库,我可以把它按照一定的维度,去拆成很多的订单库。
拆分和解耦是分不开的,一方面是代码和业务的解耦,另一方面对人员和工作来说也是一种解耦。同样还要做一些异步工作,因为以前业务特别重,下单流程非常长,操作步骤非常多,但是很多东西并不是用户都要马上关注的。比如说下单给用户送积分,这个积分并不是说下单的时候必须马上就要给到客户的,可以稍微延迟几秒甚至是几分钟,它不应该影响下单业务。但是如果因为积分出现问题,导致下单出现问题那就是本末倒置了。因此我们做了异步,它挂了,我们可以做补偿。
当然有一些是不能做异步的,比如说积分兑换商品的订单,下单时要去扣积分,因为积分就是钱,我们还有礼品卡支付,这个是不能做异步的。我记得以前有过一个例子,好像是一个网站用积分可以充话费,结果话费充成功了,积分没扣,就变成了可以无限的充。现在是互联网时代,信息散布的很快,据说一两个小时就是几个亿的损失。还有读写分离,读和写是可以完全分开的。&为了保证下单的流畅,我们把读和写分开&,在不同的库里进行读和写,这样可以很大地减轻下单压力。
核心Service规划
这张图是我们核心Service的规划。大家想象一下一个电商网站有哪些基本特点,哪怕你的网站上只有一个页面一个商品,比如说你只卖苹果手机或者是小米手机,网站就一个页面显示这个商品,你点商品就可以直接进行购物。首先它要有商品的描述信息,第二是商品的价格,第三是商品的库存,当然库存数字你可以不显示,有就卖,没有就不卖,以上这些是商品基本的信息。
有了商品的信息,如果你想买这个商品,你要先注册用户,注册用户之后要登陆,这是用户信息;然后你就可以下单了,生成一个订单,订单之后是对订单进行相应的物流信息跟踪。同时也可以做线上支付,当然你也可以只做货到付款不做线上支付。
这是一个电商网站最基本的核心要素。一个是产品,第二个是用户&支付,再就是订单。这三大块构成了一个电商网站最核心的三个部分,这就是&我们核心Service的架构规划,抓住核心是服务化的重要理念&。从Service角度来说,产品服务、价格服务相对来说比较简单和单一,对产品的价格来说用户主要是查;但是对订单来说,我们把库存放到订单这个层面来,而不是放到产品上,是因为库存和订单是息息相关的,生成订单的时候要扣库存的,库存不足的话,订单是无法完成的。
刚开始的时候我们没有服务化,&订单有两个问题:第一个是写,生成订单。&写的业务是很多的,比如说加购物车生成订单,充值是一种业务的订单,电子卡是一种业务的订单,都在写;&第二个是读,订单写了之后,用户要来读、后端的客服商家也在读&。
读不是简单的仅仅读订单表,还有很多其他的关联表。比如说抽奖有抽奖系统有自己的很多表,抽完奖给用户发一个奖品,就生成一个订单,但抽奖系统要知道哪一个用户是通过什么样的方式获得这个订单、订单里有什么东西,必然要关联查询。所以订单查询是非常复杂的,有无数的点可以查,而且这个查询一定是有无数张表可以关联查的,甚至是跨表、跨库的。
那么Service怎么做呢?如果说我把所有的订单相关表都关联起来,都纳入到Service范围的话,基本上可以把70%-80%的业务都纳入其中了。因为几乎所有的业务都要围绕订单来转,所以Service化一定要有一个边界。边界是什么?比如说我刚刚说的,你抽奖的信息我关不关心呢?如果说我关心的话,你的抽奖业务就纳入进来了。这就是一个边界,所以我只能只关心我的订单,这就是订单的边界。
上图这句话我觉得说的非常好。一个出色的演讲一定要很短,一定也要很长。这对我们来说是非常有意义的。这一块来讲怎么样很短,怎么样又很长?
回到核心Service的规则。既要很短,又要很长,比如说订单的生成,它有很多的业务,要给客户积分,要生成订单表数据,要把抵用券扣掉,还有相关的支付信息,这些业务是不能剥离开来,必须要融合在一起,这当中要包含所有的能想到的业务场景,因此在订单生成业务上,我们要做到足够长,把所有的业务都包含进来。有一些是需要同步的,&有一些是需要异步的,但是无论是同步还是异步的,我们都要纳入进来&。
但对查询来说,业务是可以分开的,比如商品详情页,可能先是调商品的基本信息Service,然后再去调价格信息Service,然后再调用库存服务Service,生成一个页面需要很多的服务,这些服务可以是各自独立的,所以在查询Service上,我们可以做的很短。价格、库存等都可以做成独立的业务Service单元。
当然独立并不一定是最简单,它也要有自己完整的业务逻辑。比如库存并不是简单地看库存表里的数字是大于0还是小于0的问题,比如是说某一个地方销售不销售,或者说我们尽管有库存,我们现在是不卖它的,这些都是库存,库存不是一个简单的数字,如果说这个商品暂时不卖,我就显示说无库存,但是我的仓库里是有实物库存的。
我理解所谓的微服务,在底层这一块更多像微服务,&微服务是不是拆分的越多越好,也不一定。比如说库存,如果看这个商品有没有库存,首先你调一个服务看这个商品是不是在这个区域里卖,再调一个服务看商品是不是上架,再调一个服务看库存是不是大于0,那就太多了。
举个例子,打开一个详情页会调用很多的服务,类目信息、商品信息、价格信息、库存信息、评论等等。这么多服务,怎么保证性能呢,如果拆的非常细的话,仅一个库存服务就可以拆成7、8个子服务,这样的话服务就要调七八次,网络交互也是七八次,单个Service性能再好又怎么样呢?哪怕你的服务性能达到1毫秒,够快了吧,你调用10次要10毫秒,调用100次要100毫秒,你的性能还是在下降,所以并不是越微越好,这个长和短的粒度要划分好。&记住两个关键词:边界和粒度&。
订单水平拆库
接下来我们谈谈数据库。我们最早用的Oracle,很庞大,支持的量也很大,一般的业务量是没有问题的。但是什么情况会出问题呢?一个是单点故障,数据库一挂了,整个网站就全挂了,另外不支持水平扩展,包括它的存储、性能、数据量,Oracle再厉害,它不可能几百亿、几千亿的数据都放进去。所以我们后来选择了Mysql,对数据库进行了水平拆分,这样的话单点故障率会小一点,这么多的物理数据库,挂一个,其他的还可以运行,不至于影响全局。同时做了水平拆分之后,扩展能力非常强,从理论上来说可以无限扩展,因为它无非就是加服务器,你只要加一些硬件就可以了。
那么水平拆分怎么拆?要考虑哪些因素?
比如说订单,你第一要考虑业务场景,查询订单是哪些&用户&:其一是前端的用户;其二是后端的用户商家和客服。
第二,它的存&储量&,订单的数据量是非常大的。但对商品和库存来说,它是有一定的范围的,不会无限的大,因为一个网站或者一个商店,你卖的SKU数量是有限的。一个大超市可能是几万个SKU,一个小门店可能是几百个,它不会无限扩展的。
数据增量也是如此,一个大超市卖的SKU也就是几万个,电商平台可能是百万级千万级,但是它也不是无限增长的,这更多取决于商家的体量,所以它的数据量即使有增长也是非常缓慢的。这和订单不一样,订单是几何式的增长。
再看&读和写&,订单、库存的读和写频率都很高。但是对于商品、价格来讲,读肯定是很高的,因为不停地在浏览,但是写是很少的,改价格的机率很低,不停地改商品信息的机率也是很低的。
另外是&事务的一致性&。对于订单和库存一定是要保持一致性,商品信息写的话比较少,不太涉及到事务,除非是批量修改,相对来说事务性一致性稍微弱一些。
还有&缓存&,库存可以有缓存,但是缓存的时间是很短的,库存的缓存时效不可能是以天、以小时为级别的,几分钟级别已经是不错了。很多时候前端显示还是有库存,后面可能已经没有了,所以库存有时效性的要求。但为了减轻数据库压力,在前端展示会有库存的缓存,比如有时候大家会遇到,在浏览的时候发现它是有库存的,但是下单就没有了,那就是因为前端是缓存的,但是下单的是实时的库存,已经没有了。但对商品和价格信息来说,缓存时效就可以长一些,可以通过缓存技术减轻数据库的压力。
热点数据也是一样的。数据库的水平拆分怎么拆,从哪些维度去拆,比如说订单,可以有几个维度,你可以根据订单号去拆,根据产用户、商家去拆。对响应速度来说,用户要求响应速度是最高的;而对商家来说,用户下完订单之后,稍微延迟一会儿他也能接受。
如果按照用户去拆,热点数据的概率就很低,很难出现一个用户一下子出现几千个几万个订单;但是如果按对商家来拆,有一些大的商家,一个双十一可能几个小时就有上千万的单,这个量就非常大。
而对商品信息来说,如果说你的量没有像天猫、淘宝级别的话,并且主要是靠缓存来读,一般的电商网站,是不需要拆分的。
拆库时怎么做压测
在做订单水平拆库的时候,不可能网站停下来去做这个项目,所以我们说是飞机开的时候换发动机,在汽车跑的时候换轮胎。我们在做数据库拆库的时候要做压测。怎么做压测呢?
Oracle改Mysql的时候,当时我们对性能是没有绝对的信心的,因为Mysql的健壮性没有Oracle强大,有一次一个badsql直接把我们的一个mysql数据库给搞挂了,对性能要求特别高,但是在业务层,我们很难去模拟。我们可以在Service或sql上一步一步的分析,一步一步的优化,但是毕竟有很多业务场景是模拟不出来的。
当时我们做了Tcpcopy压测,原理就是把线上请求的包抓下来,放到测试环境中,测试的数据库尽量保持和线上一致,保持环境一致。&压测会动态调整流量,把原来的流量比如说一小时千万级的提升到亿级的,提升了很多倍,主要是测试看能不能把数据库压垮,会不会出现问题。
当然这个场景也不可能完全覆盖我们的现实应用场景,因为在线上抓包的时候,我们抓了一天,但这一天中数据库的数据是不断变化,不断有insert和update,而线下的测试数据是一个静态的数据,所以还有一些业务场景我们是模拟不到的。因此模拟结果和线上还是有一定的差距,但还是给我们吃了一颗很大的定心丸。
我刚刚说了两个,一个是Service化做了技术架构上的拆分,一个是做了数据库的水平拆分。这是刚刚提到的准备工作,Service化和水平拆库的同时,我们的很多中间件技术也发展起来了,因为你的量上来了、架构调整了,配套设施也要上来,不是说简单的教室一拆分就完了,学校没有保安,要上体育课没有操场是不行的,因此没有相应的中间件没有是不行的。
SOA中间件本身也是一个有意思的发展,包括分布式服务SOA中间件、数据库中间件、缓存平台、消息中间件、任务调度中间件和全局配置中心等。日志和监控系统也非常有必要,这都是系统稳定的基础。
还有&实时的分析系统&,比如说双十一,大家都关注着淘宝的数字,那个数字是怎么出来的,一定是实时出来的,你不能说到了第二天才告诉人家前一天晚上1点的时候是什么样的数据,一定是刚过1点就马上就都出来了。
同样还要做&灰度发布&,什么叫高可用,就是不出问题系统一直处于可用状态。但我们还要发布啊,发布的时候怎么办,所以灰度发布的价值就体现出来了,有了它我们的系统就有了100%可用的理论可能。
这是我们的一个简单的架构图。提交订单的时候,可以同步也可以异步提交,异步走的是秒杀系统,它不是提交之后马上生成订单,而是要有排队系统进行排队的。我刚刚在前面还说过负载均衡,我们开发了自己的&SOA中间件做负载均衡,它有自己的逻辑控制,购物流程到到订单服务是通过SOA中间件做负载均衡和调用的&。
同时我们还有&数据库中间件&,我们和数据库的交互怎么办?一个订单查询,如何定位到它所在的数据库。如果是根据用户维度拆库,用户来查询马上可以定位到相应的数据库,但是商家来查询怎么办?他的订单可能是覆盖所有的数据库,这个时候需要做一些聚合、排序。这就要通过数据库中间件,它对前端是透明的,它去做一些排序等,应用层只须常规的写自己的SQL就行了。
同样,我们还有消息中间件,比如前面提到的下单后送积分,就可以通过消息异步处理。
这是我们的核心交易架构,我们如何让它更完美一些,怎么让它的稳定性更高一些?我们有前台用户,前台用户作为普通消费者去下单、查询,同时也有很多后台的操作。
比如对消费者来说,下完单后要做支付,当然他可能会订单取消,要把订单变成取消状态;再一个他会修改收货地址,也就是这些简单的几个update操作。而后端的,运营也好,客服也好,对这个订单是有很多的操作的。可能还有审核系统,还有发货、出库等等的系统都要对订单进行操作,因此我们后端的反而是更复杂的。后端的操作必然会影响到数据库,如果不注意也会出现很多的问题,把数据库夯住了,影响了前端的交易。
Service层可以不断的拆,但从用户层角度来说,还是要考虑前端和后端的拆开。比如代码可以前端一套,后端一套,把它物理隔离开。&我们主要目的是保前端的交易,后端系统稍微延迟一点没有问题的&,但是大家看到,前后端代码物理上虽然隔开了,但是DB还是在一起,后端写的代码把数据库搞挂了,前端还是照样挂,这是一个很大的问题。
多活机房架构
最后说一下多活,多活的架构比较大,可以专门作为一个主题来,我这里只是给大家引申一下。
刚刚讲到,前端用户、后端用户尽管代码什么的可以隔开,但是DB这一层还是在一起。因此我们也要想办法把DB分开,但这个时候,两个DB的数据要保持同步,当然我今天说的只是一个思路而不是解决方案。
这是我在网上搜来的一张双活的图,想想双活和多活其实是一样的。我们可以有不同的机房,也可以在一个机房内部有多个独立的单元,多个机房或单元物理独立。这样的话,一定要&有一个统一的数据中心&,这两个数据要同步,因为前端用户下完单之后,商家在看订单的时候,不可能要看这边的订单到这个系统,要看那边订单到另外一个系统去看,因此必须要有数据中心。如果说我们有多个机房,可能是三个五个,像淘宝的级别就是非常多的机房。我理解它一定要有一个数据中心把数据汇总起来。
当然我这是从应用层来说,从应用隔离的角度去看的。&多活的目的不是简单的隔离,它考虑的是一旦发生地震、灾害等如何保证不出问题&,这个时候数据中心对多活也是必要的。
但数据中心我是不是可以作为后端应用来使用呢?后端的应用走数据中心,因为它对数据的实时性要求相对不是特别高,而前端只保证核心交易业务,后端保证非核心交易业务,这是多活应用架构拆分的思路。
& 开源中国(OSChina.NET) |
开源中国社区(OSChina.net)是工信部
指定的官方社区中国领先的IT技术网站
51CTO旗下网站
深度剖析微服务架构的九大特征
微服务架构这个术语在过去几年渐成热门,它把一种特定的软件应用的设计方法描述为能够独立部署的服务的套件。尽管缺乏对这一架构类型的准确定义,但是在业务能力、自动化部署、智能端点、语言和数据的去中心化控制等方面,已经形成了某些普遍特征。
作者:James Lewis
Martin Fowler来源:| 16:23
微服务架构这个术语在过去几年渐成热门,它把一种特定的软件应用的设计方法描述为能够独立部署的服务的套件。尽管缺乏对这一架构类型的准确定义,但是在业务能力、自动化部署、智能端点、语言和数据的去中心化控制等方面,已经形成了某些普遍特征。
微服务,另一个在软件架构领域津津乐道的新词。尽管我们本能上倾向于对它不屑一顾,然而这一专业术语描述了一种目前越来越吸引人的软件系统的风格。我们已经看到近年来有许多项目使用了此类型,结果很鼓舞人心;因而对于我的诸多同事来说,这也就成为他们构建企业级应用时候的首选。然而,当前并没有很多信息来描述什么是微服务,以及如何使用它。
简而言之,微服务架构把一个应用作为一套微服务来开发,这些微服务能运行自己的进程,采用 HTTP Resource API
这样的轻量级机制进行通信。这些微服务围绕着业务能力构建,能够通过完全自动化的部署体系独立部署。这些服务仅有最低限度的中心化管理,用不同的编程语言写成,使用不同的数据存储技术。
要说明微服务,与一体化应用做比较能有助于理解:一体化应用是被当作一个单元来构建的。企业级应用的构建通常包括三部分:客户端用户界面(由 HTML
页面和运行在用户机器上的浏览器里的 javascript
构成)、数据库(由各种表格构成,这些表格被插入到一个通用的关系数据库管理系统里),以及服务器端应用。服务器端应用处理 HTTP
请求,执行域名逻辑、从数据库提取并更新数据,以及选择并填充要被发送到浏览器的 HTML
视图。这个服务器端应用就是个庞然大物&&逻辑单一、可执行。系统有任何修改,都需要重新构建并部署新版本的服务器端应用。
构建这样的系统,一体化服务就是非常适合的方法。所有处理请求的逻辑都运行在单个进程中,能够让你使用语言的基本功能从而把应用划分成不同门类、功能和命名空间。出于某种原因,你能够在开发者的电脑上运行和测试应用,使用部署管道来确保各种修改被正确地测试并部署到生产中。借助负载均衡器,你能够通过运行更多实例来横向扩展这一巨型应用。
一体化应用获得了成功,但是渐渐地,随着更多的应用被部署到云平台,人们的挫败感渐增。更新周期被紧紧绑定&&即便是应用中一个很小部分的改变,也需要整个应用的重构和部署。随着时间推移,保持一个良好的模块化架构也日益困难,牵一发而动全身。一旦要进行扩展,就必须整体扩展,而不能仅仅扩展其中到一部分模块,也就需要更多的资源。
图 1:一体化 vs 微服务
这种情况最后进化为微服务架构:把应用作为一组服务来构建。这些服务能够独立部署和扩展,每个服务提供一个坚实的模型边界,甚至可以用不同的程序语言来写这些服务。当然他们也能被不同的团队来管理。
我们无意鼓吹微服务是新事物或者具有创新性,它植根于 Unix
的设计主旨。不过我们相信并没有很多人思考过微服务架构;如果许多软件开发使用了微服务,那它们的境况就会更好。
微服务架构的特征
我们不能给微服务架构下准确的定义,但是我们可以尝试总结一些通用特征。并不是所有的微服务架构完全符合下文列出的这些特征,不过我们可以预计大部分微服务架构能符合大多数。作为这个松散社区的活跃分子,我们(指两位作者)将尝试描绘我们在工作和我们熟悉的团队中看到的情况。
通过服务实现组件化
我们已经在软件行业浸淫多年,一直期望能够通过拼插组件的方式来构建系统,而不是采用我们在物理世界里常见的方式。在过去的几十年里,我们已经见证了多数语言平台的公共库的汇编已经取得了长足的进步。
谈及组件,要给它下定义并非易事。我们认为,一个组件就是软件的一个单元,能够被独立替换和升级。
微服务架构也会用到库,不过组件化软件的方式是把软件分解为服务。我们把库定义为一个程序中互相连接的组件,调用内存函数,同时这些服务是进程外部的组件,通过网页服务请求或者远程调用通信的方式进行通信。(这与面向对象编程中的服务对象的概念并不相同)
把服务当作组件(而非库)来使用的一大原因在于服务能够独立部署。如果你的应用在单个进程中包含多个库,那对其中任何一部分的改动都会导致重新部署整个应用。如果该应用分解为多个服务,那么可以预计,单个服务改变后可能只需要重新部署该服务即可。当然事无绝对,某些改动可能会改变服务接口,需要进行某些协调。但是,好的微服务架构的目标在于通过紧密结合的服务边界和进化机制,尽可能降低这些影响。
把服务当作组件使用的另一成果就是更为明确的组件接口。大多数语言缺乏良好机制去定义一个准确的发布接口。通常只有文档和规则来阻止客户端去中断组件的封装,结果导致组件的过度耦合。通过使用准确的远程调用机制,服务能轻松避免这些问题。
使用服务也有不足之处。远程调用比进程内调用更昂贵,远程 API
也需要粗粒度,这往往更加难以使用。如果你需要更改组件之间的职责分配,那么在跨越进程边界时就很难进行这样的操作。
乍一看,我们能观察到服务映射到运行时的过程,不过也仅仅是个大概。一个服务可能包含多个进程,它们会永远被开发和部署在一起,那么这样的应用进程和数据库也就只能被这一服务使用。
围绕业务能力组织
要把大型应用拆分为零件,管理人员通常聚焦在技术层面,拆分成 UI
组、服务器端逻辑组和数据库团组。当这些组被这样垂直分割,非常简单的改动就会导致跨组项目,而这需要时间和预算批准。聪明的团队会围绕这点进行优化,两害相权取其轻&&强化逻辑到任意有访问权限的应用。
任何试图设计一个系统(广义定义)的组织将会衍生出一种设计,其结构正是该组织的通信结构的复刻。
& Melvyn Conway, 1967
图 2: Conway 效应在运行
而微服务采用的方法则大不一样,围绕着业务能力拆分并组合。这些服务使用与业务范围相符的软件而实现广泛的技术栈,包括用户界面、持续存储,以及任意的外部协作。因此这些组之间是跨功能的,包括开发所要求的所有技能:用户体验、数据库和项目管理。
图 3:通过团队界限强化服务界限
就采用了这种组织方式。跨功能的团队对构建和运行每个产品负责,每个产品又被拆分为大量的单个服务,这些服务通过信息总线通信。
大型的一体化应用也能够围绕业务能力模块化,然而并不常见。当然我们会敦促这些构建一体化应用的大型团队按照业务线来进行分工。然而问题在于,这些业务线倾向于根据诸多环境进行组织。一旦大型应用横跨许多模块,那么对于团队中的个体而言,很难融入他们的工作记忆中。此外我们也看到,这些模块线需要大量的训练去执行。服务组件所需的更为精细的分割也能让团队边界更清晰。
产品而非项目
大多数我们常见的应用开发会使用项目模式:交付软件的部分然后再考虑组合完整。完成后的软件被交付到维护机构,构建此项目的团队不被解散。
微服务的支持者则易于避免此模式,倾向于「在产品的整个生命周期里,开发团队应该拥有此项目」。这一灵感来自于亚马逊的「你构建,你运行」概念。在亚马逊,开发团队对生产环境中的软件负有全部责任。这让开发者每日都能了解自己的软件如何在生产环境运行,增强与用户的接触,也能承担部分支持职责。
这种产品意识与业务能力紧紧联系。与其把软件看作一套需要完成的功能,不如把它们看作一段进行中的关系,其中的关键问题是软件如何帮助用户增强业务能力。
并没有理由表明这一方法不能用于一体化应用,不过颗粒度更小的服务能够更容易地在服务开发者和用户之间建立起个人关系。
智能终端和哑管道
要在不同进程之间构建通信结构,我们已经见过许多产品和方案,它们强调在通信机制内部注入智能,其中优秀案例如 ESB(企业服务总线)。ESB
产品包含复杂的设施,用于信息路由、编排、转化,以及应用业务规则。
微服务社区则喜欢另一种方案:智能终端和哑管道。使用微服务架构的应用致力于在尽可能地解耦合的同时保持关联性&&他们拥有自己的域名逻辑,从经典 Unix
的视角看来更像过滤器&&接收请求,恰当地应用逻辑,生成反应。这些编排使用了简单的 REST 协议而非 WS-Choreography 或者 BPEL
,也没有采用使用中心化工具进行编配。
两种广为使用的协议分别是 HTTP 请求-反应与资源 API,以及轻量级消息。对于前者,最佳描述莫过于
成为 web ,不要隐藏其后
&& 伊恩&罗宾逊
微服务团队使用万维网(往大了说,Unix)依赖的原则和协议。开发者或者运维人员能够以很小的代价缓存经常使用的资源。
第二种方法的通常用法是利用轻量级信息总线进行通知。选定的基础设施是典型的 「哑管道」&& 就像 RabbitMQ 或 XeroMQ
这样无需提供稳定的异步组织的简单实施;而智能终端则在服务内生成并消耗消息。
在大型应用里,组件联机执行,它们之间的通信或者采用方法调用,或者调用函数。在大型应用到微服务的转变中,最大的问题就是通信模式的改变。从内存调用函数的本地对话转为
RPC,可能会变成性能不佳的聊天式通信。并且,你还需要用粗粒度的方法代替原来的精细通信。
去中心化治理
中心化治理的一大后果就是单一技术平台的标准化倾向。经验显示这一方式非常狭隘&&每个问题各有特色,而「马斯洛的锤子」并非万能。我们更喜欢针对工作使用正确的工具,在特定情境下,一体化应用能够发挥不同语言的优势。这并不常见。
把大型应用的组件拆分为服务,那么当我们构建每个部分时就有选择。想用 Node.js 构建一个单个报告页面?用起来!用 C++
写一个格外粗糙的近实时组件?没问题。想交换不同数据库类型,从而更好地适应某个组件的阅读习惯?我们也有重构技术。
当然,能做并不等于要那样做&&不过这种系统切割方法给了你选择权。
构建微服务的团队也愿意使用别的方法达到标准。与其使用纸面上的现成标准,他们更愿意使开发有用的工具,从而别的开发者也能够用来解决相似问题。这些工具通常通过实施收获成果,以包括内部开源模式在内的方式更广泛的群体中分享。现在
git 和 github 已经成为事实上的版本控制系统,开源实践也在机构内部越来越常见。
就是这一理念的最佳践行者。通过库的形式分享有用的、经过时间考验的代码,鼓励别的开发者以相似方法解决相似问题,同时也给别的必要的方法留有机会。共享库关注常用问题,比如数据存储、进程间通信,以及下文将讨论的基础设施自动化。
对微服务社区来说,额外的开销格外讨厌。社区并非不重视服务协议;与之相反,他们使用不同的方式来管理这些协议。Tolerant Reader 和
Consumer-Driven Contracts
这样的模型经常被用于微服务。他们帮助服务协议进行独立进化。通过把执行消费者驱动协议作为构建的一部分,强化了信心,也能就其它微服务是否工作提供快速反馈。我们知道澳大利亚的一支团队就使用消费者驱动协议来推动新服务的构建。他们使用能够定义单个服务协议的简单工具。在新服务的代码写好之前,就成为自动化构建的一部分。服务仅在满足协议时被构建,以优雅的方式避免了「YANGI」(
You Aren&t Going To Need It )悖论。这些技巧和工具围绕着它们成长,降低了对中心化协议管理的需求,减少了服务之间的暂时耦合。
或许去中心化治理的最高点是流行于亚马逊的谁构建谁运行的理念。每个团队都为自己构建的应用的所有方面负责,包括 24*7 不间断地运营软件。
这种层次的责任转变显然不同寻常,然而我们看到越来越多的公司给开发团队灌输这种层次的责任心。Netflix
是另一家采用这种理念的公司。不要在每个凌晨三点被寻呼机吵醒,这绝对是开发者关注代码质量的强大动力。这些理念已经与传统的中心化治理模型相去甚远。
去中心化数据管理
去中心化数据管理的方式多种多样。在最为抽象的层级,这意味着各个系统之间关于世界的概念模型大相径庭。在大型企业进行整合时,这一现象很常见。对客户的看法,销售人员的视角与支持人员的视角不同。销售认为可称为「客户」的某些方面,支持人员却并不认同。他们可能只是具有一些在语言描述上差异很细微的不同属性。
这一现象在应用之间也很普通,也会发生在应用内,特别是当应用被拆分为单独的组件时。 领域模型驱动设计(Domain-Driven Design) 的
Bounded Context 概念非常有助于思考这一问题。DDD
将一个大模型分解为几个较小的模型,并且能够投射出它们之间的关系。这一过程对于一体化架构和微服务架构都非常有益。不过在服务和 context
的边界之间存在自然关系,当我们在描述业务能力单元、强化分离时,这一自然关系有助于明朗化。
除了下放有关模型概念的决策,微服务也下放了数据存储的决策。由于一体化应用喜欢为持续性数据采用单一逻辑的数据库,企业也往往在一系列应用中采用单一数据库。这些决策大多数受厂商的授权商业模式驱动。微服务倾向于让每个服务管理自己的数据库,或者不同的数据库系统,即
Polyglot Persistence。你也可以在一体化应用中使用 Polyglot Persistence,不过它更多见于微服务。
图4:一体化设计:单一数据库 vs 微服务:应用数据库
把跨微服务的数据下放影响了对更新的管理。处理更新的常见方式是在更新多个资源时,通过使用事务来保证一致性。这种方式通常也被用于一体化应用内。
使用诸如这样的事务有助于保持一致,但是带来了显著的短时耦合,对跨多个服务产生问题。分布式事务因难以实施而闻名,随之而来,微服务架构强调了服务之间的事务和谐,明确了一致性只可能为最终一致性,各种问题通过补偿运算来解决。
选择通过该方法来管理不一致对于许多开发团队来说是项新的挑战,不过很符合商业惯例。通常商家为了快速响应需求会对不一致进行不同程度的处理,其中会存在一些处理错误导致的逆转。只要修复错误的代价低于因为一致性而导致业务损失的成本,这种权衡就是值得的。
基础设施自动化
基础设施自动化已经在过去的几年里取得了巨大的进步。云特别是 AWS 的进化格外地降低了构建、部署和运行微服务时的复杂度。
许多采用微服务构建的产品或系统是由在持续交付和其前身&&持续集成方面经验丰富的团队构建的。使用这种方法构建软件的团队需要大量使用基础设施自动化技术。下图展示了构建流程。
图5:基本的构建流程
考虑到本文并不针对持续交付,我们这里只提及几个关键特性。我们需要大量信心来认可自己开发的软件可行,因此会跑大量的自动化测试。要推广可行的软件到流程之上,意味着我们需要把部署每个新环境自动化。
一体化应用能够非常愉快地在这些环境中构建、测试和推送。事实证明,一旦你给一体化应用投入了自动化路径,那部署更多应用也并不那么可怕了。谨记,持续交付的目的之一就是让部署变得单调,所以不管是部署一个还是三个应用,只要依然单调就没有关系。
另一个常见的使用大规模基础设施自动化的领域就是在生产环境中管理微服务。前文我们认为只要部署一如既往地单调,那在一体化和微服务之间相差不会太大;恰恰与此相反,在运行阶段,二者却是相去甚远。
图6:模块部署经常不同
为故障而生
把服务用作组件的一个结果是应用在设计之初就要能容忍技术故障。任何服务调用可能会由于供应商的不可用而失败,而客户端需要尽可能优雅地做出响应。与一体化设计相比,由于引入了额外的复杂性来处理,这是一大不足。其结果是微服务团队不断反省服务故障如何影响用户体验。
Netflix 的 Simian Army 通过测试应用的弹性和监控,减少了工作日的服务故障,甚至数据中心的故障。
这种生产环境中的自动化测试足以让大多数的运营团队望而生畏,通常后者需要提前一周的时间。这并非说一体化架构风格不能尽兴这种精密的监控设置,只是不常见于我们的经验。
既然服务可能随时发生故障,所以能够快速监测并尽可能地自动恢复服务就非常重要。微服务应用侧重于应用的实时监控,检查架构因素(数据库每秒获得多少请求)和业务相关指标(每分钟收到多少订单)。语义监控也可提供早期预警系统,一旦出错就触发开发团队去跟进和调查。
对于微服务架构来说这尤为重要,因为微服务更偏好编配和事件协作导致的自发行为。尽管许多专家认可偶发价值,事实上意外行为并非好事。监控对于发现糟糕的意外行为至关重要,从而能够尽快修复。
一体化也可以像微服务那样透明构建,事实上,他们也应如此。区别在于你必须要了解运行在不同进程的服务们是何时断开的。考虑到相同进程内的库,这种透明度不太可能有用。
微服务团队希望能为每个单独的服务设置精密的监控和记录,这些服务包括在 dashboard
上显示服务启用/宕机状态,以及各种相关的运营和业务指标。与断路器状态、当前吞吐量和延迟的详情都是我们经常遇到的其它例子。
进化的设计
微服务从业者通常具有进化设计的背景,把服务分解视作一个长远的工具,让应用开发者们能够控制应用内的改动,无需让改动慢下来。改动控制并不一定意味着减少&&借助正确的态度和工具,你能够经常快速、有节制地修改软件。
当你试图把一个软件系统分为组件,你要作出如何划分的决定&&哪些是我们切分应用时需要遵守的原则?组件的关键属性是独立替换和升级的概念,也就意味着我们要找到一些立足点,当需要重写某个组件时,也不会影响它的协作者。
按照一体化来设计并构建,却演化为微服务,卫报网站给这样的应用树立了典范。网站的核心仍然是一体化,不过他们更愿意通过调用一体化应用的 API
来构建微服务,从而添加新功能。对于体育赛事的特定页面这样注定短暂的功能来说,这样的方法非常方便。网站上的类似部分能够通过快速开发语言而被迅速地组织起来,一旦赛事结束则可以快速移除。我们在一家金融机构也看到了类似办法,增加新服务以对应新的市场机会,几个月甚至几周后就被放弃。
这种对替代性的强调,也是模块化设计通用原则的一个特例,通常推动模块性来实现改变的方式。你可能想保留同一模块内同一时间的改变。系统内发生更改的部分很少在不同的当前大量流失的服务内。如果你发现自己重复在同时修改两个应用,那表明它们应该被合并。
把组件集成到服务,让更精细的发布计划大有可为。采用一体化,任何修改都需要对整个应用进行一次全面的构建和部署。采用微服务后只需要重新部署修改过的服务。这能够简化并加快发布过程。缺点是你得担忧对一个服务的修改可能破坏它的用户。传统的集成方法是采用版本控制来处理错误,而在微服务的世界里,则把版本控制当作最后的补救办法。通过给服务设计得尽可能强的修改宽容度,我们能够避免大量的版本控制。
微服务是未来吗?我们撰写此文的主要目的是解释微服务的主要思路和原则。通过此篇论述,我们认为微服务的架构风格是一个重要概念,值得企业级应用去认真考虑。我们最近已经采用此风格构建了多个系统,也知道有人也使用并赞同此方法。
我们所知的微服务架构的先驱包括亚马逊(Amazon)、网飞(Negflix)、卫报(Guardian)、英国政府数字化服务部门(UK Government
Digital Service)、.au、Forward 和
2013 年的 The Conference Circuit 大会充满了各种公司案例,他们正在迁移到微服务类别的产品和服务,其中包括 Travis
CI。此外也有大量机构一直在做类似微服务的事情,但是并未采用此名称。(通常被标记为 SOA,不过 SOA 以各种矛盾的形态出现)
尽管有这些切实的经验,但是我们并不能坚决肯定微服务就是软件架构未来的发展方向。在微服务方面积累积极经验(与一体化应用相比)的同时,我们仍保持清醒&&微服务还没有经过足够长时间的检验,因而还不能做出完整判断。
我们的同事 Sam Newman 2014年的时候花费了大量时间写书,记录了我们构建微服务的经历。如果你想深入研究此主题,那你下一步也应该这么做。
微服务架构决定的实际效果需要多年后才能显现。我们已经看到一些由对模块化有强烈需求的优秀团队构建的一体化架构的项目,在多年后衰退。由于服务边界明确,且难以修补,许多人认为微服务不可能有此衰退。除非我们能看到足够多上年头的系统,否则还是不能完全评价微服务的成熟度。
当然也有其它原因让人们认为微服务不够成熟。在各种组件化的努力中,成功依赖于软件与组件的相符程度。要弄清组件的边界在哪里,这非常难。自我进化的设计认识到了让边界正确的难度,以及由此而来的让重构保持简单的重要性。不过一旦你的组件是需要远程通讯的服务,那重构要比采用联机库的服务更难。跨服务边界的代码迁移也很困难,任何界面变化都需要在参与者之间协调,也要添加对后端兼容的层,测试也会更复杂。
另一个问题就是,假如组件不能干净地组合,那么你所做的不过是把复杂性从组件内部转移成组件之间的联结。这样做不仅仅是复杂性的迁移,同时也变得更不明确,也更难以控制。如果只是查看一个小而简单的组件内部,而忽略服务之间混乱的联结,那你很容易就觉得更好。
最后,团队技能也是一大因素。新技术很容易被技术熟练的团队采用。不过一项对熟练团队来说更有效的技术,可能并不适合稍逊一筹的团队。我们已经见证了很多技术水平稍逊的团队构建的凌乱的一体化架构;采用微服务会发生怎样的混乱,也需要时间观察。水平不佳的团队会一直创建不太好的系统,在这种情况下,很难说微服务是会减少混乱,还是会让情况更糟。
我们听到的一个合理的说法是你不应该一开始就用微服务架构。相反,以一体化开始,保持模块化,一旦一体化变得麻烦,就将其拆分为微服务。(不过这一建议不甚理想,因为一个好的联机接口通常并不是一个好的服务接口)
我们以谨慎乐观的态度写下此文。到目前为止,我们已经足够了解微服务的风格,也认为值得踏上此路。我们不能肯定地说终点何在,不过软件开发中的一大挑战就是你只能基于当下所掌握的不完整的信息做决定。【编辑推荐】【责任编辑: TEL:(010)】
大家都在看猜你喜欢
热点热点热点热点热点
24H热文一周话题本月最赞
讲师:1人学习过
讲师:3人学习过
讲师:1人学习过
精选博文论坛热帖下载排行
JBuilder 2006是一款强大的Java企业级开发平台,其集成了几乎所有的Java技术,涵盖了软件开发生命周期的各个过程。本书深入浅出地介绍了JBu...
订阅51CTO邮刊}

我要回帖

更多关于 后端系统灰度发布方案 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信