系统架构之高可扩展系统设计与实现

可扩展性是衡量架构设计的一个因素,也常常被开发者提到。可是,一个系统要设计出比较好的可扩展性是有必定难度的,并且可扩展性体如今不一样层次上,有大的可扩展性,也有小的可扩展性,本文从可扩展的本质出发,经过平时经常使用的框架来印证,最后经过实际案例说明如何设计高可扩展性系统。java

imagegit

1、可扩展的本质是什么?

可扩展的意思是在面对变化时,用最少的代价去实现,平时咱们听得最多的是面向抽象 (接口) 编程,若是只是把这里的抽象理解成接口,那么就有些狭隘了,抽象是通式通法,而接口只是其中一个,因此在谈可扩展实现以前必定要讲清楚可扩展的本质是什么,连本质都不知道,怎么提出系统性解决方案。程序员

1.1 扩展的本质github

扩展的本质就是占位符,明确告诉你这里被占了,具体谁占了不清楚。那么问题来了:占位符究竟是什么?它是怎么表达的?又要如何实现的?若是能够把这三个问题理清楚,就能够想到不少可扩展性方案,而再也不是单一的面向接口编程。数据库

占位符究竟是什么:占位符仅仅是一个标识,标志这里会有变化,一句话能够归纳:凡是能够表达变化的就是占位符,然而具体的变化实现又没有给出,真正体现了作什么和怎么作的分离编程

占位符怎么表达:要回答这个标识是用什么来表达,变量、接口、配置项…这些均可以表达占位符,变量能被赋值同一类型的数据;接口能够有不一样的实现;配置项也能够被赋予不一样的值…因此,实现可扩展的思路一下就打开了。缓存

如何实现:再往深层次思考,实现一个接口,如何在执行时动态找到实现类?若是把这个问题想清楚,在实际中实现可扩展又会有一套系统性解决方案。整个过程就两点:识别和执行,识别的意思就是要找到对应目标,接下来就是执行。性能优化

综上,到这里可能已经有本身应对可扩展的方法,上面已经给了从不一样角度看可扩展性的示例,接下来就是系统化提出应对可扩展的方法。架构

结论一:扩展的本质就是占位符,凡是能够表达变化的就是占位符并发

1.2 应对可扩展的方法

先给出应对可扩展的方法:规范、识别、注册、使用,这 4 点都是从上面可推导出来的,下面一一进行详细说明。

规范:规范是从占位符推导出来的,既然是标志有变化,必定要遵循必定的规范表达,不然别人是不知道的,如接口,就是很直接地表达这里是有变化的,具体的实现还不知道;变量自然地表达这里是变化的数据。

识别:有了规范定义以后,接下来就是识别,以前为何可扩展一直对咱们来说很虚,那是由于规范和识别都是系统帮咱们作的,咱们只是知道而没有真正实践。规范是定义,识别是找出有哪些实现了规范。

注册:识别出来以后,就要把信息存储起来,能够存储在本地,也能够存储在远程,若是存储在远程就是一个注册的过程,这里的注册就是存储的意思。简单理解就是识别出来以后要集中管理。

使用:使用就很简单,找到具体实现并执行逻辑处理。

上面四个单词看起来简单,除了使用是终极目标外,其它三个都是抽象的表达,好比规范如何定义、怎么识别、如何注册?经过上面的表述能够看到具体要怎么实践,这里再总结下:

规范如何去定义:凡是能够表达变化的就能用它来定义,常见的有配置项、变量、接口、注解等;

怎么去识别:这个要具体去看如何定义规范,如配置项的变化有一个监听变化;注解是要扫描类来识别 annotation;

如何去注册:若是系统的交互只是一个,那么存储在本地就行,若是系统的交互是多个,那么要注册到一个注册中心上去。

结论二:应对可扩展的方法:规范、识别、注册、使用

1.3 扩展的经典案例

此处使用一个经典案例来讲明可扩展性,并从其原理上印证上述方法。

在 Java 中,SPI 对于大部分人来说并不陌生,最典型的加载数据库驱动就是经过 SPI 来实现的。若是你看了 SPI 的原理,再去看上面写的,会感受两个思路很类似。

SPI 有它的规范,要到指定目录下加载对应文件;找到文件后进行解析、识别并加载;最后就是使用。整个流程能印证上面所提到的:规范、识别、注册、使用。因此,方法的提出有一个点就是从具体案例中进行抽象,提炼共性的东西,再去推演其它案例看能不能也知足。

2、可扩展性系统实践之路

此处以优惠券业务平台为例讲解可扩展性系统设计与实现,在上一篇文章中已经讲了优惠券系统是一个平台型的业务系统,要作到业务与业务的隔离、业务与平台的隔离。

2.1 识别变化

通过总体分析以后,已经肯定大业务流程:建券、发券、用券、退券,以及对应的子流程,接下来就是要分析出哪些内容会变化。

比较明显的变化就是领券、用券门槛的变化,由于不一样业务线有不一样的限制条件,有的要限制不一样人群,有的要限制领取次数…已经认别了变化接下来就是要处理这些变化。

结论三:找扩展点就是找系统常常变化的地方

2.2 处理变化的常见手段

2.2.1 野蛮处理

一个最简单的处理,就是在代码中写 if else,它的特色是简单直接上线,不足的点长期下去,系统会变得很难维护、可扩展性较差。

if(productId = ProductEnum.A){//具体的处理  }elseif(productId = ProductEnum.B){//具体的处理  }elseif(productId = ProductEnum.C){//具体的处理   }else{      ......  }

这种代码放到如今,不少系统仍是这么作的,并且是在业务发展初期最喜欢用这种野蛮处理方式,搞上去就能直接上线,快速支持业务。

2.2.2 面向接口设计

对上面野蛮方式的一个常见处理就是面向接口设计,抽象出一个限制条件检查的接口,不一样的业务线有对应的实现,经过配置指定业务线下全部的实现,将这些实现放到一个映射中,在程序执行过程当中,经过业务线就能够执行全部的接口实现类并依次执行。

List ruleList = RuleFacotry.getByProductId(prodcutId);

这种方法比第一种明显要好,体现了必定的可扩展性,新加一个规则限制,从新实现接口就行,而后在配置项中加上这个新的实现,代码的改动量也还好。它有一个明显的问题就是每次新加一个实现就要发布上线,有没有办法不发布上线就能知足目的呢?有,就是下面提到的一类可扩展性设计的方法。

2.3 一类可扩展性设计的方法

再来明确一下目标:系统具有可扩展性和不发布系统就能实现新增功能。

仍是使用上面说的方法:规范、认别、注册、使用,下面结合这个具体的案例来讲明。

规范:这里是用接口来做为规范描述限制条件,包含入参和出参,这里有一个开放平台,实现了一个接口后就能够提交代码。

识别:在建优惠券时,会加载业务线有哪些业务规则实现,在领取、使用时能够进行配置选择,此时只是插入一个变量标识使用某个限制条件 (如限人群,这个实现的逻辑可能会变化,经过变量名来标识变化)。

注册:系统在执行的过程当中,发现有限制条件的变量名,拿这个变量名从开放平台中拉取具体的实现存储在本地 (有一个缓存时间,具体的过时时间依业务考虑,咱们取的是 30 分钟)。

执行:拿到具体的实现后,依次执行。

再整理下流程步骤,让你们更进一步掌握该设计方法:

在开放平台提交限制条件接口的实现代码,有限制人群的实现、限制领取券次数…

在开放平台提交以后,会入库存储,数据库里会存储一个业务线对应的多个限制实现。

建立优惠券时,会加载业务下的限制规则,经过配置选择具体要使用到的限制规则 (相同业务线下的不一样优惠券能够有不一样的规则限制),配置选择后,会在规范字段中存储规则实现的 id(规则实现可能会变化,会有屡次提交),因此这里存储的是 id,在执行的时候能够拿到这个 id。

在领券、用券时,会检查规则限制有哪些,经过 id 列表从远程开放平台拉取具体实现,把 java 代码拉下来以后就能够编译,并存储到本地或者集群缓存中。

最后就是执行具体的实现逻辑。

结合这张图看就会清晰不少,总体的业务平台架构比较清晰,分为开放平台、配置平台、业务平台和数据平台,一个新业务方接进来很简单,简单配置下就可使用。

3、小结

本篇文章主要讲可扩展性系统的设计与实现,从可扩展的本质讲起,可扩展的本质就是占位符,凡是可表达变化的均可以称之为占位符,常见的有变量、接口、配置项、注解等,而后提出应对可扩展性的方法:规范、识别、注册、使用四个步骤,虽然只有 8 个字,但它包含了一套系统的处理方案,再也不是单一的面向接口编程,最后结合具体的案例进行说明如何设计可扩展性系统。

做者介绍:

高福来,前后在 Oracle、阿里工做,目前在滴滴小桔车服加油团队负责营销基础 (优惠券、奖励金),在分布式中间件和系统架构方面积累了必定的经验,擅长用通俗易懂的语言描述复杂问题。

你们能够加个人程序员交流圈子:705127209,群内有阿里技术大牛讲解的最新Java架构技术。做为给广大朋友的加群福利——分布式(Dubbo、Redis、RabbitMQ、Netty、RPC、Zookeeper、高并发、高可用架构)/微服务(Spring Boot、Spring Cloud)/源码(Spring、Mybatis)/性能优化(JVM、TomCat、MySQL)【加群备注好消息领取最新架构资料】

相关文章
相关标签/搜索