最近研究一些架构思想,看看李云华老师的专栏(从0开始学架构),做些笔记有些个人的思考。
一.基础架构
1.1简介
架构设计面临复杂度:高可用,可扩展,低成本,安全,规模。
架构设计主要目的为了解决软件系统复杂度带来的问题。
1.2高性能
1.2.1简介
水平维度和垂直维度分析。
单台计算机
升级软、硬能力实现性能提升。
- 增加内存减少I/O操作。
- 更换为固态硬盘(SSD)提升I/O访问速度。
- 使用RAID增加 I/O吞吐能力。
- 置换服务器获得更多的处理器或分配更多的虚拟核。
- 升级网络接口或增加网络接口。
多台计算机
1.利用合理的任务分配与任务分解实现性能的提升。
2.主要针对集群系统
- 功能分解:基于功能将系统分解为更小的子系统。
- 多实例副本:同一组件重复部署到多台不同的服务器。
- 数据分割:在每台机器上都只部署一部分数据。
多进程多线程虽然让多任务并行处理的性能大大提升,本质还是分时系统,并不能做到时间上真正的并行,目前解决方案:SMP(对称多处理器结构),NUMA(非一致存储结构),MPP(海量并行处理结构)。
SMP:系统中所有资源共享(cpu,内存,I/O),这种原因扩展能力有限。
NUMA:具备多个cpu模块,每个cpu模块由多个cpu(4)组成,并且具有独立的本地内存,I/O槽口等。由于其节点之间可以通过互联模块(Crossbar Switch)进行·连接和信息交互,因此每个CPU可以访问整个系统内存(NUMA系统与MPP系统的重要差别)。显然,访问本地内存速度将远远高于访问远地内存(系统内其它节点的内存)的速度,非一致存储访问NUMA的由来。
MPP:在MPP系统中,每个SMP节点也可以运行自己的操作系统、数据库等。但和NUMA不同的是,它不存在异地内存访问的问题。换言之,每个节点内的CPU不能访问另一个节点的内存。节点之间的信息交互是通过节点互联网络实现的,这个过程一般称为数据重分配(Data Redistribution)。
1.3高可用
本质:通过冗余来实现高可用
核心思想:高可用主要技术手段是服务于数据的冗余备份和失效转移。
高性能增加机器目的在于“扩展”处理能力;高可用增加机器的目的在于“冗余”的处理的单元。
计算高可用架构同存储高可用架构,都需要考虑故障发生时的分发,但是存储高可用架构,还需要考虑如何保证数据一致性,也就是说有状态的,所以复杂度更高。而无状态计算高可用,机器之间不需交互,只需要考虑分发和重试机制。
1.3.1高可用状态决策
基础:状态决策,即系统需要能够判断当前的状态。
矛盾:通过冗余来实现的高可用系统,状态决策本质上就不可能做完全正确。
独裁式:
协商式:主备互相通信
民主式:选举,例如zookeeper(半数选举为主)。
1.4可扩展
两个基本条件:正确预测变化,完美封装变化。
预测变化复杂性:
- 不能每个设计点考虑可扩展性。
- 不能完全不考虑可扩展性。
- 所有的预测都存在出错的可能性。
第一种应对变化的常见方案:
将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”。
复杂性
- 系统需要拆分出变化层和稳定层。
- 需要设计变化层和稳定层之间的接口。
第二种应对变化的方案:提炼一个“抽象层”和一个“实现层”
抽象层是稳定的。
实现层可以根据具体业务需要定制开发。
典型的实践就是设计模式和规则引擎,比如装饰模型。
装饰模型:
/**
* 形状
*
*/
public interface Shape {
void draw();
}
/**
* 圈
*
*
*/
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Circle draw");
}
}
/**
* 长方形
*
*
*/
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle draw");
}
}
/**
* 实体装饰类
*
*
*/
public abstract class ShapeDecorator implements Shape {
protected Shape shape;
public ShapeDecorator(Shape shape) {
this.shape = shape;
}
@Override
public void draw() {
shape.draw();
}
}
/**
* RedShapeDecorator 来装饰Shape对象
*
*
*/
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape shape) {
super(shape);
}
@Override
public void draw() {
shape.draw();
setRedBorder(shape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
/**
* 入口
*/
public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
Shape redCircle = new RedShapeDecorator(new Circle());
Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
1.5低成本
- 引用新技术和创造新技术,才能达到低成本目标。
- Nosql的出现是为了解决关系型数据库无法应对高并发访问带来的访问压力。
- (Sphinx、Elasticsearch、Solr)全文搜索引擎的出现为了解决关系型数据库like搜索的低效问题。
- hadoop为了解决传统文件系统无法应对海量数据存储和计算的问题。
1.6安全
从技术角度,安全分为两类:
一类是功能上的安全:
常见的XSS攻击、CSRF攻击、SQL注入、Windows漏洞、密码破解等等,本质上系统实现由漏洞。
一类是架构上的安全:
防火墙最基本功能就是隔离网络。
互联网系统架构更多依靠运营商或者云服务商强大的带宽和流量清洗的能力。
1.7规模
规模带来的复杂度的主要原因就是“量变引起质变“。
1.8架构设计三原则
合适原则,简单原则,演化原则。
合适优于先进》演化优于一步到位》简单优于复杂。
1.9设计备选方案
而恰恰需要把可能性大的变化点一一罗列出来,分维度打分,维度包括 可能性大小?长期还是短期会变化?如果发生变化,目前的组织和系统要花多大成本适应变化。这些变化正是李老师之前说过的各种复杂度上的变化,比如用户量激增带来的性能要求。此外还包括一个业务功能逻辑上的变化。
在经过上面分析后往往会给出“上中下”策的设计方案,下策一般考虑的变化少,短视,但迅速,修改小,立竿见影。上策一般看重远期,但成本高很高,也很可能预测不中。
最后还要分析,如果决定采用下中策,如果预测的变化发生了,系统修改为中上策的代价有多大,有些代价几乎是无穷大的,比如必须中断服务进行升级。如果代价小,那可以放心采用下策或中策。如果答案是否,可上策当前的代价又真的不可接受,那又要返回头重新分析了
实践发现这个方法挺好用,尤其当有人来咨询架构方案时,往往对给出的结果比较满意。
技术短期被压缩,长期被短视,但我一直本着极客精神,无论公司如何,背后有思考和意识,完成很好的工程。