Java生鲜电商平台-高可用微服务系统如何设计?

说明:Java生鲜电商平台高可用架构往往有以下的要求:

高可用。这类的系统往往需要保持一定的 SLA,7*24 时不间断运行不代表完全不挂,而是有一定的百分比的。

例如我们常说的可用性需达到 4 个 9(99.99%),全年停机总计不能超过 1 小时,约为 53 分钟,也即服务停用时间小于 53 分钟,就说明高可用设计合格。

用户分布在全国。大规模微服务系统所支撑的用户一般在全国各地,因而每个地区的人,都希望能够就近访问,所以一般不会一套系统服务全国,而是每个地区都要有相应的业务单元,使得用户可以就近访问。

并发量大,存在波峰波谷。微服务之所以规模比较大,其实是承载的压力比较大,而且需要根据请求的波峰波谷进行弹性伸缩。

有故障性能诊断和快速恢复的机制。大规模微服务场景下,运维人员很难进行命令式手动运维来控制应用的生命周期,应该采用声明式的运维方法。

另外一旦有了性能瓶颈或者故障点,应该有自动发现定位的机制,迅速找到瓶颈点和故障点,及时修复,才能保障 SLA。

战略设计

为了满足以上的要求,这个系统绝不是运维组努力一把,或者开发组努力一把,就能解决的,是一个端到端的,各个部门共同完成的一个目标,所以我们常称为战略设计。

研发

一个能支撑高并发,高可用的系统,一定是需要从研发环节就开始下功夫的。

首先,每一个微服务都有实现良好的无状态化处理,幂等服务接口设计

状态分为分发,处理,存储几个过程,如果对于一个用户的所有的信息都保存在一个进程中,则从分发阶段,就必须将这个用户分发到这个进程,否则无法对这个用户进行处理。

然而当一个进程压力很大的时候,根本无法扩容,新启动的进程根本无法处理那些保存在原来进程的用户的数据,不能分担压力。

所以要将整个架构分成两个部分,无状态部分和有状态部分,而业务逻辑的部分往往作为无状态的部分,而将状态保存在有状态的中间件中,如缓存,数据库,对象存储,大数据平台,消息队列等。

这样无状态的部分可以很容易的横向扩展,在用户分发的时候,可以很容易分发到新的进程进行处理,而状态保存到后端。

而后端的中间件是有状态的,这些中间件设计之初,就考虑了扩容的时候,状态的迁移,复制,同步等机制,不用业务层关心。

对于数据的存储,主要包含几类数据:

会话数据等,主要保存在内存中。对于保存在内存里的数据,例如 Session,可以放在外部统一的缓存中。

结构化数据,主要是业务逻辑相关。对于业务相关的数据,则应该保存在统一的数据库中。

文件图片数据,比较大,往往通过 CDN 下发。对于文件,照片之类的数据,应该存放在统一的对象存储里面。

非结构化数据,例如文本,评论等。对于非结构化数据,可以存在统一的搜索引擎里面,例如 ElasticSearch。

但是还有一个遗留的问题,就是已经分发,正在处理,但是尚未存储的数据,肯定会在内存中有一些,在进程重启的时候,数据还是会丢一些的,那这部分数据怎么办呢?

这部分就需要通过重试进行解决,当本次调用过程中失败之后,前序的进程会进行重试,例如 Dubbo 就有重试机制。

既然重试,就需要接口是幂等的,也即同一次交易,调用两次转账 1 元,不能最终转走 2 元。

接口分为查询,插入,更新,删除等操作:

对于查询接口来讲,本身就是幂等的,不用做特殊的判断。

对于插入接口来讲,如果每一个数据都有唯一的主键,也能保证插入的唯一性,一旦不唯一,则会报错。

对于更新操作来讲,则比较复杂,分两种情况。一种情况是同一个接口,前后调用多次的幂等性。另一种情况是同一个接口,并发环境下调用多次的正确性。

为了保持幂等性,往往要有一个幂等表,通过传入幂等参数匹配幂等表中 ID 的方式,保证每个操作只被执行一次,而且在实行最终一致性的时候,可以通过不断重试,保证最终接口调用的成功。

对于并发条件下,谁先调用,谁后调用,需要通过分布式锁如 Redis,ZooKeeper 等来实现同一个时刻只有一个请求被执行,如何保证多次执行结果仍然一致呢?则往往需要通过状态机,每个状态只流转一次。

还有就是乐观锁,也即分布式的 CAS 操作,将状态的判断、更新整合在一条语句中,可以保证状态流转的原子性。乐观锁并不保证更新一定成功,需要有对应的机制来应对更新失败。

其次,根据服务重要度实现熔断降级、限流保护策略

服务拆分多了,在应用层面就会遇到以下问题:

服务雪崩:即一个服务挂了,整个调用链路上的所有的服务都会受到影响。

大量请求堆积、故障恢复慢:即一个服务慢,卡住了,整个调用链路出现大量超时,要长时间等待慢的服务恢复到正常状态。

为了解决这些问题,我们在应用层面实施了以下方案:

通过熔断机制,当一个服务挂了,被影响的服务能够及时熔断,使用 Fallback 数据保证流程在非关键服务不可用的情况下,仍然可以进行。

通过线程池和消息队列机制实现异步化,允许服务快速失败,当一个服务因为过慢而阻塞,被影响服务可以在超时后快速失败,不会影响整个调用链路。

当发现整个系统的确负载过高的时候,可以选择降级某些功能或某些调用,保证最重要的交易流程的通过,以及最重要的资源全部用于保证最核心的流程。

还有一种手段就是限流,当既设置了熔断策略,又设置了降级策略,通过全链路的压力测试,应该能够知道整个系统的支撑能力。

因而就需要制定限流策略,保证系统在测试过的支撑能力范围内进行服务,超出支撑能力范围的,可拒绝服务。

当你下单的时候,系统弹出对话框说 “系统忙,请重试”,并不代表系统挂了,而是说明系统是正常工作的,只不过限流策略起到了作用。

其三,每个服务都要设计有效探活接口,以便健康检查感知到服务状态

当我们部署一个服务的时候,对于运维部门来讲,可以监控机器的状态或者容器的状态是否处于启动状态,也可以监控到进程是否启动,端口是否监听等。

但是对于已经启动的进程,是否能够正常服务,运维部门无法感知,需要开发每个服务的时候,设计一个有效探活接口,让运维的监控系统可以通过调用这个接口,来判断进程能够正常提供服务。

这个接口不要直接返回,而是应该在进程内部探查提供服务的线程是否出去正常状态,再返回相应的状态编码。

只有这样,开发出来的服务和运维才能合作起来,保持服务处于某个副本数,否则如果一部分服务虽然启动,但是处于假死状态,会使得其他正常服务,无法承受压力。

其四,通过制定良好的代码检查规范和静态扫描工具,最大化限制因为代码问题造成的系统不可用

要保持线上代码的高可用性,代码质量是关键,大部分线上问题,无论是性能问题,还是稳定性问题,都是代码造成的,而非基础设施造成的。

而且基础设施的可用率为 99.95%,但是服务层要求的可用率高于这个值,所以必须从业务层高可用来弥补。

除了下面的高可用架构部分,对于每一个服务来讲,制定良好的代码检查规范和静态扫描工具,通过大量的测试用例,最大化限制因为代码问题造成的系统不可用,是必须的,是高可用的基础。

高可用架构设计

在系统的每一个部分,都要避免单点。系统冗余往往分管控面和数据面,而且分多个层次,往往每一个层次都需要进行高可用的设计。

 
 

在机房层面,为了高可用应该部署在多个区域,或者多个云,每个区域分多个可用区进行部署。

对于云来讲,云的管控要多机房高可用部署,使得任何一个机房故障,都会使得管控依然可以使用。

这就需要管控的组件分布于至少两个机房,管控的数据库和消息队列跨机房进行数据同步。

对于云的数据面来讲,入口的网关要和机房网络配合做跨机房的高可用,使得入口公网 IP 和负载均衡器,在一个机房故障的情况下,可以切换至另一个机房。

 
 

在云之上要部署 Kubernetes 平台,管控层面 Kubernetes 要实现高可用部署,etcd 要跨机房高可用部署,Kubernetes 的管控组件也要跨机房部署。

当然还有一种情况是机房之间距离比较远,需要在每一个机房各部署一套 Kubernetes。

这种情况下,Kubernetes 的管控依然要实现高可用,只不过跨机房的高可用就需要应用层来实现了。

在应用层,微服务的治理平台,例如注册发现,ZooKeeper 或者 Euraka,APM,配置中心等都需要实现跨机房的高可用。另外就是服务要跨机房部署,实现城市级机房故障迁移能力。

运维

运维一个大规模微服务系统也有不一样的挑战。

首先,建议使用的是 Kubernetes 编排的声明式的运维方式,而非 Ansible 之类命令式的运维方式。

另外,对于系统的发布,要进行灰度、蓝绿发布,降低系统上线发布风险。要有这样的理念,任何一个新上线的系统,都是不可靠的。

 
 

所以可以通过流量分发的模式,逐渐切换到新的服务,从而保障系统的稳定。

其三,完善监控及应对机制,对系统各节点、应用、组件全面地监控,能够第一时间快速发现并解决问题。

 
 

监控绝非只有基础设施的 CPU,网络,磁盘的监控,应用的,业务的,调用链的监控都应该有。

而且对于紧急事件,应该有应急预案,应急预案是在高可用已经考虑过之后,仍然出现异常情况下,应该采取的预案,例如三个 etcd 全挂了的情况。

其四,持续关注线上系统网络使用、服务器性能、硬件存储、中间件、数据库灯指标,重点关注临界状态,也即当前还健康,但是马上可能出问题的状态。

例如网关 PPS 达到临界值,下一步就要开始丢包了,数据库快满了,消息出现大量堆积等等。

DBA

对于一个在线业务系统来讲,数据库是重中之重,很多的性能瓶颈定位到最后,都可能是数据库的问题。所以 DBA 团队要对数据库的使用,进行把关。

造成数据库性能问题,一方面是 SQL 语句的问题,一方面是容量的问题。

例如查询没有被索引覆盖,或者在区分度不大的字段上建立的索引,是否持锁时间过长,是否存在锁冲突等等,都会导致数据库慢的问题。

因而所有上线的 SQL 语句,都需要 DBA 提前审核,并且要对于数据库的性能做持续的监控,例如慢 SQL 语句等。

另外对于数据库中的数据量也要持续的监控,到一定的量就需要改分布式数据库 DDB,进行分库分表,到一定的阶段需要对分布式数据库进行扩容。

故障演练和性能压测

再好的规划也比不上演练,再好的性能评估也比不上在线的性能压测。

性能问题往往是通过线上性能压测发现的。线上压力测试需要有一个性能测试的平台,做多种形式的压力测试。

例如容量测试,通过梯度的加压,看到什么时候实在不行。摸高测试,测试在最大的限度之上还能承受多大的量,有一定的余量会保险一些,心里相对比较有底。

再就是稳定性测试,测试峰值的稳定性,看这个峰值能够撑一分钟,两分钟还是三十分钟。还有秒杀场景测试,限流降级演练测试等。

只有经过性能压测,才能发现线上系统的瓶颈点,通过不断的修复和扩容瓶颈点,最终才能知道服务之间应该以各种副本数的比例部署,才能承载期望的 QPS。

对于可能遇到的故障,可以进行故障演练,故意模拟一些故障,来看系统如何反应,是否会因为自修复,多副本,容错等机制,使得这些故障对于客户端来讲没有影响。

战术设计

下面,我们就从架构的每个层次,进行战术设计。我们先来看一下高可用部署架构选型以及他们的优劣:

 
 

高可用性要求和系统的负载度和成本是强相关的。越简单的架构,部署成本越低的架构,高可用性越小,例如上面的单体应用。

而微服务化,单元化,异地多活,必然导致架构复杂难以维护,机房成本比较高,所以要使用多少成本实现什么程度的高可用,是一个权衡。

高可用的实现需要多个层次一起考虑:

 
 

首先是应用层,可以通过异地多活单元保证城市级高可用,这样使得一个城市因为灾难宕机的时候,另外一个城市可以提供服务。

另外每个多活单元采用双机房保证机房级高可用,也即同城双机房,使得一个城市中一个机房宕机,另一个机房可以提供服务。

再者每个机房中采用多副本保证实例级高可用,使得一个副本宕机的时候,其他的副本可以提供服务。

其次是数据库层,在数据中心之间,通过主从复制或 MGR 实现数据异步复制,在每个集群单元中采用 DDB 分库分表,分库分表中的每个实例都是有数据库同步复制。

其三是缓存层,在数据中心之间,缓存采用多集群单元化复制,在每个集群单元中采用多副本主从复制。

其四微服务治理平台层,平台组件异地多活单元保证了城市级高可用,平台组件每个多活单元采用双机房保证机房级高可用,平台组件每个机房中采用多副本保证实例级高可用。

当有了以上高可用方案之后,则以下的故障等级以及影响时间如下表格:

 
 

接下来,我们每个层次详细论述。

应用层

下图以最复杂的场景,假设有三个城市,每个城市都有两个完全对等的数据中心。三个城市的数据中心也是完全对等的。

我们将整个业务数据按照某个维度分成 A,B,C 三部分。这样任何一部分全部宕机,其他部分照样可以提供服务。

对于有的业务,如果省级别的服务中断完全不能忍受,市级别的服务中断要求恢复时间相当短,而区县级别的服务中断恢复时间可以相对延长。

在这种场景下,可以根据地区来区分维度,使得一个区县和另外一个区县的数据属于不同的单元。

为了节约成本,模型可能会更加简化。中心节点和单元化节点不是对称的。中心节点可以实现同城双活,而异地单元化的部分只部署一个机房即可。这样是能满足大部分高可用性需求的。

这种架构要求实现中间件层和数据库层单元化,这个我们后面会仔细讲。

 
 

接入层

单元化要求 App 层或者在机房入口区域的接入层,实现中心单元和其他单元节点的流量分发。

对于初始请求没有任何路由标记的,可以随机分发给任何一个单元,也可以根据地区或者运营商在 GSLB 中分发给某个就近的单元。

应用层接收到请求以后,根据自己所在的单元生成路由信息,将路由信息返回给接入层或者 App。

接下来 App 或者接入层的请求,都会带着路由信息,选择相应的单元进行发送,从而实现了请求的处理集中在本单元。

 
 

中间件层

在中间件层,我们以 ZooKeeper 为例,分为以下两个场景:

场景一:ZooKeeper 单元化主从多活

在这种场景下,主机房和单元化机房距离相隔较近,时延很小,可以当做一个机房来对待。可以采用 ZooKeeper 高可用保障通过多 ZooKeeper 实例部署来达成。

如图所示,主机房 ZooKeeper 有 Leader 和 Follower,单元化机房的 ZooKeeper 仅为 Observer。

 
 

场景二:ZooKeeper 单元化多集群复制

两个机房相距较远,每个机房部署一套 ZooKeeper 集群,集群之间进行数据同步。

各机房应用连接机房内的 ZooKeeper 集群,注册的信息通过数据同步,能够被其他机房应用获取到。

单一机房 ZooKeeper 集群不可用,其余机房不受影响。当前不考虑做不同机房之间的集群切换。

 
 

数据库层

在数据库层,首先要解决的问题是,分布式数据库 DDB 集群多机房同步复制。

在单元内采用同城主从复制模式,跨单元采用 DTS/NDC 实现应用层数据双向同步能力。

 
 

对于数据的 ID 分配,应该采取全局唯一 ID 分配,有两种实现方式,如果主机房和单元化机房距离较近,可采用 ID 分配依然采用中心式, 所有机房的单元全部向同一中心服务申请 ID 的方式。

如果主机房和单元化机房相隔较远,可采用每个单元各自分配, 通过特定规则保证每个机房得到的最终 ID 不冲突的方式。

 
 

缓存层

在缓存层,有两种方式:

方式一是集群热备,新增 Redis 集群作为热备份集群。

 
 

主集群与备份集群之间在服务端进行数据同步,通过 Redis Replication 协议进行同步处理。

离线监听主集群状态,探测到故障则进行主备之间切换,信息通过配置中心下达客户端,类哨兵方式进行监听探活。

在这种场景下,集群之间数据在服务端进行同步,正常情况下,集群之间数据会一致。但会存在一定的复制时延。

在故障切换时,可能存在极短时间内的数据丢失。如果将缓存仅仅当缓存使用,不要做内存数据库使用,则没有问题。

第二种方式,集群多活。新增集群作为多活集群,正常情况下客户端根据 Key 哈希策略选择分发到不同集群。

 
 

客户端通过 Proxy 连接集群中每一个节点,Proxy 的用处是区分客户端写入与集群复制写入。

集群之间在服务端进行数据双向复制,数据变更通过 Redis Replication 协议获取。

离线监听主集群状态,探测到故障则进行切换,信息通过配置中心下达客户端,类哨兵方式进行监听探活。

此方案应用于单纯的集群间高可用时,同一个 Key 在同一段时间内只会路由到同一个集群,数据一致性可以保证。

在故障切换情况下,可能存在极端时间内的数据丢失。

微服务治理平台

作为大规模微服务的微服务治理平台,一方面自己要实现单元化,另外一方面要实现流量在不同单元之间的染色与穿梭。

从 API 网关,NSF 服务治理和管理中心,APM 性能管理,GXTS 分布式事务管理,容器平台的管控都需要进行跨机房单元化部署。

 
 

当请求到达一个单元之后,API 网关上就带有此单元的路由信息,NSF 服务治理与管理平台在服务之间相互调用的时候,同样会插入此单元的路由信息。

当一个单元某实例全挂的时候,可以穿梭到另一个单元进行调用,并在下一跳调用回本单元,这种方式称为流量染色。

 

联系QQ:137071249

QQ群:793305035

Java生鲜电商平台-高可用微服务系统如何设计?的更多相关文章

  1. Java生鲜电商平台-如何使用微服务来架构生鲜电商B2B2C平台?

    Java生鲜电商平台-如何使用微服务来架构生鲜电商B2B2C平台? 说明:随着互联网的日益普及,人们通过手机下单买菜的人越来越多,生鲜这个行业有两个显著的特点,一个是刚需.(你每天都要吃饭,都要吃菜) ...

  2. Java生鲜电商平台-深入理解微服务SpringCloud各个组件的关联与架构

    Java生鲜电商平台-深入理解微服务SpringCloud各个组件的关联与架构 概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留 ...

  3. Java生鲜电商平台-Spring Cloud微服务架构图

  4. Java生鲜电商平台-高并发核心技术订单与库存实战

    Java生鲜电商平台-高并发核心技术订单与库存实战 一. 问题 一件商品只有100个库存,现在有1000或者更多的用户来购买,每个用户计划同时购买1个到几个不等商品. 如何保证库存在高并发的场景下是安 ...

  5. Java生鲜电商平台-高并发的设计与架构

    Java生鲜电商平台-高并发的设计与架构 说明:源码下载Java开源生鲜电商平台以及高并发的设计与架构文档 对于高并发的场景来说,比如电商类,o2o,门户,等等互联网类的项目,缓存技术是Java项目中 ...

  6. Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战

    Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战 说明:Java生鲜电商平台-小程序或者APP优惠券的设计与源码实战,优惠券是一种常见的促销方式,在规定的周期内购买对应商品类型和额度的商品 ...

  7. Java生鲜电商平台-统一格式返回的API架构设计与实战

    Java生鲜电商平台-统一格式返回的API架构设计与实战 说明:随着互联网各岗位精细化分工的普及,出现了很多的系统架构设计,比如常见的前后端分离架构,后端提供接口给前端,前端根据接口的数据进行渲染,大 ...

  8. Java生鲜电商平台-订单配送模块的架构与设计

    Java生鲜电商平台-订单配送模块的架构与设计 生鲜电商系统最终的目的还是用户下单支付购买, 所以订单管理系统是电商系统中最为复杂的系统,其作为中枢决定着整个商城的运转, 本文将对于生鲜类电商平台的订 ...

  9. Java生鲜电商平台-redis缓存在商品中的设计与架构

    Java生鲜电商平台-redis缓存在商品中的设计与架构 说明:Java开源生鲜电商平台-redis缓存在商品中的设计与架构. 1. 各种计数,商品维度计数和用户维度计数 说起电商,肯定离不开商品,而 ...

随机推荐

  1. Android Application 详细介绍

    一.先看看文档里怎么说 Base class for those who need to maintain global application state. You can provide your ...

  2. openstack网络(四)-虚机流量分析

    几种网络名词解释 使用LinuxBridge时虚机流量分析 VLAN FLAT Local VXLAN 使用OVS时虚机流量分析 几种网络名词解释 1.local网络:local网络是与其他网络和节点 ...

  3. Java修炼——文件夹的复制

    文件夹的复制用到了俩个流:缓冲流和文件字节流 缓冲流用来加快写入和读取速度. 在这里我简述一下复制文件夹的过程,当然复制文件夹都可以,复制文件更是不在话下 1.首先要明确俩点.要复制的文件夹的位置(源 ...

  4. [TimLinux] Python Django myblog启动

    1. myblog介绍 自己给自己定的一个学习项目,用于: 使用学到的Python Web开发技术,框架django 使用学到的CSS技术,实现前端功能 使用学到的Axure工具,画出前端页面功能草图 ...

  5. ZOJ 3195 Design the city (LCA 模板题)

    Cerror is the mayor of city HangZhou. As you may know, the traffic system of this city is so terribl ...

  6. HDU1224-Free DIY Tour(SPFA+路径还原)

    Weiwei is a software engineer of ShiningSoft. He has just excellently fulfilled a software project w ...

  7. 将项目部署到github的方法

    GitHub是一个面向开源及私有软件项目的托管平台,因为只支持git 作为唯一的版本库格式进行托管,故名GitHub. GitHub于2008年4月10日正式上线,除了Git代码仓库托管及基本的 We ...

  8. Python入门(一个有趣的画图例子实战)你肯定不会

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:qiu_fang  画一条可爱的python(蟒蛇): import t ...

  9. idea 上传svn忽略文件

    在idea最下面找到Version Control 默认我们svn提交的文件都在Default ChangeList中 我们创建一个新的ChangeList

  10. java—将数据库读取的list转tree

    一.引言 有时候我们从数据库中读取出了一个表的数据,比如存储的是中国的省市县的ID.名称与父节点ID,读出来的数据并不是前台想要的,这个时候我们要想法处理一下都出来的list,将它变为一个树. 比如直 ...