美文网首页
系统设计经验

系统设计经验

作者: 万福来 | 来源:发表于2020-05-25 19:41 被阅读0次

    系统设计经验

    系统架构说明

    • 整个系统首先是基于分层架构思想对系统进行了横向拆分,目前主要拆分为四层。
    • 第一层是展示层,主要由前端研发发团队负责维护开发,主要包括PC、APP、M、WX和QQ五个终端的页面展示。
    • 第二层是接入层,主要由我们后端研发团队负责,接入层的应用主要对外提供HTTP接口,供展示层的前端研发团队使用。接入层异地多活双机房集群部署,主要使用负载均衡服务器进行流量分发,并实现接入层应用的伸缩性和可用性,避免单点故障。通过nginx反向代理缓存提升接口查询性能,通过nginx限流配置提高系统的安全性。
    • 第三层是服务层,也是由我们后端研发团队负责维护开发,该服务层主要是基于京东内部分布式服务框架JSF打造的可复用业务组件。该服务层主要为上层接入层提供通用的底层服务,对外封装了底层数据库的具体实现。服务层也是异地多活双机房部署,主要是使用分布式服务框架管理平台对服务进行治理,包括服务的注册,发现,上线和下线操作,都可以通过服务注册中心进行管理。并通过服务注册中心实现了服务层应用的伸缩性和可用性,服务层也引入了redis服务作为缓存,提升服务的查询性能。同时可以通过分布式服务框架管理平台的限流和黑名单功能提供系统的安全性。
    • 最后一层是数据存储层,用到的存储数据库主要有Mysql、ES和Redis。数据库层都实现了异地多活集群部署和读写分离。Mysql基于京东内部的弹性数据实现分片和扩容来,ES和Redis集群本身就支持数据分片和主从同步并能够满足系统高可用要求。
    • 系统横向拆分完成后,我们又基于高内聚低耦合的设计原则,根据领域驱动设计思想将不同的耦合性较低的业务模块进行纵向拆分,拆分为商品领域、订单领域、下单领域和基础领域四个领域模块。各个领域模块分别由不同的后端研发独立开发集群部署,对外提供统一的微服务接口,使我们的系统更加易于开发维护,局部修改很容易部署,有利于持续集成和交付,也保证了整体系统的伸缩性。
    • 服务层右侧区域是基于单一职责原则拆分出来的一些后台服务模块,主要有异步任务模块、定时任务模块和mq消费模块,这些模块也是独立应用开发、集群部署,互不影响。可以根据每个应用的特点很方便的独立扩容。比如MQ消费模块,当有大促的时候就会有消息积压,我们可以只针对MQ消费模块进行临时扩容,大促之后在进行缩容。异步任务模块也会对异步任务的积压情况进行实时监控,一旦有任务积压报警,我们就可以针对异步任务模块进行扩容,提升系统的吞吐量。定时任务模块我们专门开发了一个分布式任务管理平台来对所有的定时任务的生命周期进行管理和监控,防止定时任务出现单点故障和重复执行问题,可以很方便的进行定时任务的动态创建,删除和测试。
    • 下单模块设计


      image.png

    遇到的设计难题

    • 订单的状态管理:
      不同订单类型,不同订单支付方式,不同订单配送方式,不同的商品类型和不同的询价方式之间的订单流转状态都不一样,而且不同状态下需要展示的订单操作按钮和订单状态展示文案也不一样,导致我们在进行订单状态修改和展示的时候需要判断的类型非常繁杂,而且这些影响订单状态流转的因素选项数量还在不断增加。为了解决所有订单状态的统一管理问题,我们设计了一套订单流程引擎,该引擎把订单流程所依赖的所有因素进行整理归纳,将这些订单状态进行串联展示,类似于一个工作流系统,通过各个因素的排列组合然后配置不同的订单状态流程图,在各个状态流程节点配置页面展示文案和操作按钮,这样我们在修改状态的时候,只需要将整个订单信息传递给订单流程引擎,他就会校验你这个状态修改操作是否合法,还可以根据当前订单查询出该订单当前状态下需要展示的操作按钮和展示文案。
    • 订单消息通知:
      订单在状态流转变化的时候,有时候会需要发送各种通知消息,发送的通知消息内容模板也会经常调整变化,而且发送通知消息的订单状态节点也在不断调整;为了快速响应业务方的需求,我们基于观察者模式设计了一套订单消息通知引擎,该通知引擎,主要分为三部分,第一部分就是基于观察者模式实现的一个监听器,用来实时监听订单的状态变化,第二部分就是一个规则引擎,将监听器监听到的订单对象传入我们的规则引擎,可以校验该订单是否需要发送消息通知,如果不需要则直接返回,如果需要则返回我们提前配置好的消息发送引擎,第三部分就是消息发送引擎,通知引擎中包括了消息通知方式和消息模板内容,根据订单信息将消息模板内容进行替换,然后调用消息发送引擎进行消息发送,消息发送可以支持邮件发送、App消息发送和短信发送。通过这套消息通知引擎的设计,我们可以直接实现业务方需求的配置化开发,不需要在修改代码进行上线即可满足业务方的不同订单状态发送不同消息内容的需求。
    • 系统PaaS化设计改造:
      比如我们的三级列表页系统在当初进行系统设计的时候,没有考虑过服务接口复用,后来在接入图书列表需求的时候,由于图书与普通的商品差异性比较大,导致原来的业务逻辑代码很多都不能复用,于是刚开始设计的时候,就基于原来的项目代码重新拷贝了一份,然后在原来的项目代码基础上进行代码逻辑修改,留下可以复用的代码逻辑,从而达到了快速开发上线。但是后来又来一个类似于图书列表页的凤凰需求,实现的根据属性聚合的列表页系统,对原有业务改造比较大,为了快速上线,又基于最初版的列表页系统代码拷贝了一个新项目,在新项目中进行开发,然后部署。但是当后面来的需求涉及到这三个系统公共逻辑需要修改时,我们就需要重复修改各个项目中的重复代码,然后分别进行测试上线,维护了很多重复代码。为了解决这个问题,我们对系统进行了PaaS化改造,增加了业务身份的概念,将这些业务身份下具有相同逻辑的业务定义为水平业务组件,然后个性化业务逻辑定义为垂直业务组件,水平业务组件有领域服务下的领域能力实现,同时领域能力对外暴露业务扩展点,这些业务扩展点由各垂直业务方来实现,然后通过SPI机制将水平业务组件和垂直业务组件进行关联组合,不同的业务方可以配置使用不同的扩展点,通过该设计可以使的各业务方系统不仅可以复用统一的水平业务组件,也可以快速开发自己的扩展点逻辑,实现自己的个性化业务逻辑。同时随着垂直业务方的扩展点使用的广泛程度的增强可以直接沉淀为水平业务组件,实现水平业务资产的积累。
    • 定时任务管理问题:
      原来主要使用spring框架开发定时任务,需要在spring配置文件中配置定时任务的cron表达式和定时任务逻辑实现类,通过该种方式部署时,单台服务器部署时,存在单点故障,如果集群部署时,有重复执行任务的问题,通常可以通过分布式锁来解决重复执行问题,但是定时任务进行测试时也很不方便,尤其是每天只能执行一次的定时任务,需要等待任务时间才能触发任务执行。每个任务的运行情况也不能进行监控和管理,也不支持动态调整定时任务的各种配置。为了解决这个问题,设计了一个分布式定时任务框架,并基于该系统设计申请了一项技术发明专利,并已经通过国家专利局授权。
      该系统主要包含三个模块,分别是任务接收模块,任务执行模块和集群管理模块。
      任务接收模块负责接收定时任务创建请求,将定时任务根据负载均衡规则进行分配任务执行器,并且将该任务和执行服务器的绑定关系持久化到数据库中保存。该模块可以提供后台管理页面供开发人员直接录入定时任务系统,并对已有定时任务进行管理。
      任务执行模块主要执行自己负责的所有定时任务,执行任务的方式有多种,支持调用远程RPC接口实现,实现了定时任务调度和定时任务逻辑的分离。
      集群管理模块主要负责监控任务接收模块和任务执行模块服务器的集群健康状态,如果有异常服务器,则进行故障转移操作。该模块主要通过zookeeper来监控集群内的所有服务器。
    • 线上业务问题定位:
      线上业务问题定位一般都靠打印日志,通过日志追踪方法的入参和出参信息来定位问题,但是为了接口性能,我们会尽量的少打印日志,但是一旦线上有问题的时候,我们又需要增加打印日志代码后然后在发布上线,然后在去根据日志排查问题,问题定位到之后还得再把打印日志的代码全都删除然后在上线,整个排查过程非常繁琐。为了解决这个问题,我基于统一配置中心开发了日志级别动态调整组件,业务系统直接引入我们的日志级别动态调整组件后,系统配置好统一配置中心的连接地址后,就可以在统一配置中心管理后台进行日志级别的动态配置了,在管理后台修改日志级别动态配置后,统一配置中心会自动通知所有的业务服务器修改日志级别,这样我们的业务代码就可以放心的打印业务参数日志了。然后通过统一配置中心可以动态调整日志的输出级别来调整是否打印业务参数日志。
    • 轻量级分布式异步任务处理:
      基于数据库和Disruptor内存队列实现的分布式异步任务处理框架,在数据库中创建异步任务表,根据任务类型来区分不同的任务事件,实现不同类型的异步任务处理,通过Disruptor生产者定时扫描任务表,将等待执行状态的任务扫描出来,并批量修改任务状态为正在执行,为了避免生产者集群部署时出现数据库锁竞争,任务生产者在扫描等待执行任务之前先根据redis获取一个自增序列号作为当前生产者的服务器编号,在根据自增服务器编号与生产者配置的并行数量求余并于任务表中的任务id取模进行任务分配,防止多台生产者扫描到相同的任务ID出现锁竞争。扫描到的任务信息添加到Disruptor内存队列,并通过事件驱动对应类型的任务handler来异步处理任务。任务执行成功或失败都会同步修改任务表中的状态,如果出现异常则设置任务为等待重试状态。重试任务生产者会专门扫描任务状态为等待重试的任务列表,然后进行异步处理,同时还有系统超时检测任务生产者,专门扫描那些一直处于处于正在执行或者正在重试任务状态的任务,如果有超过超时时间,则将任务状态重置为等待重试状态,如果一直重试不成功,超过了重试次数阈值,则进行任务告警,实现人工介入处理。

    DDD领域驱动设计

    传统方式则由底层DB模型到顶层服务接口开始设计;
    领域驱动设计由顶层领域模型置底开始设计。
    领域 -> 多级业务子域 -> 领域服务 -> 领域能力 -> 领域能力扩展点
    系统 -> 多个子应用 -> 服务接口 -> 服务方法 -> 对外暴露扩展接口
    示例:
    商品系统 -> 商品详情页 -> 商品详情服务接口 -> 查询商品基本信息 -> 根据扩展接口实现自定义查询字段
    领域能力中的实现是中台提供的水平业务能力组件,可以供各个业务方直接使用。
    领域能力扩展点由垂直业务方实现,当一个能力扩展点被多个垂直业务方都使用时,可以沉淀为水平业务能力,设置为默认实现,减少垂直业务方的配置。
    这样可以通过复用中台的水平业务能力组件实现业务的快速开发,通过领域能力扩展点实现各业务方的差异化能力。
    每个叶子域下会有多个领域服务,所谓领域服务,就是指各个领域能对外提供的原子性的业务逻辑,比如库存查询服务,扣减库存服务。领域服务和JSF接口有明确的区别,领域服务属于领域建模的概念,是描述中台内部业务逻辑的手段;JSF接口是技术实现方式,会对领域服务做编排、聚合后暴露给业务方进行调用。
    领域服务负责实现业务逻辑,并编排领域能力,一个领域服务由多个领域能力支撑,一个领域能力能支持多个领域服务。
    领域能力是PaaS化业务建模体系中描述核心逻辑最细粒度的概念,一个领域能力可以映射到一组相关扩展点的集合。中台在定义扩展点时会将一个业务场景需要同时定制的扩展点聚合在一起,前台业务可以根据领域能力来索引这些扩展点。领域能力必须有扩展点,否则会出现随便定义的情况。
    领域能力的一些业务策略,不同业务有不同的需求,这种因业务而异、无法确定的逻辑被定义成扩展点,由前台业务自行设置。
    比如订单取消服务(领域服务),由订单状态判断、订单有效期判断等多个领域能力支撑,其中订单有效期面对秒杀、预售、手机充值等不同业务场景会有不同的有效时间,这个有效时间可以做成一个扩展点:订单有效期,由前台业务进行定义。
    扩展点有版本概念,业务包一般基于一个版本开发,所以扩展点的升级需要做到多版本兼容,降低前台研发使用成本。扩展点就是SPI,只是传统的SPI没有业务身份的概念。

    相关文章

      网友评论

          本文标题:系统设计经验

          本文链接:https://www.haomeiwen.com/subject/ywjuahtx.html