系统架构初探
第一篇文章好兴奋,好兴奋~
开题:
架构这东西,对于我这种一直在单个服务器上写程序的小白来说一直觉得非常高大上,想要学习却又感觉触摸不到。最近听了一些讲座和师兄那里讲解才慢慢揭开了一些面纱。当然现在依然只是知道些皮毛,并没有什么实践基础。我将通过不断的学习和整理博文来一点点来扩展在这方面知识。如果读者阅读过程中有什么疑问、建议,欢迎留言一起探讨。
什么是架构
先来谈谈架构是个什么东西。架构在我看来就是对系统的抽象和规范。对于一个庞大的项目而言,我们不可能在一台服务器由一个人去搞定它。因此我们要对项目的逻辑进行抽象和拆分,把系统拆分成模块或者组件交付给不同的人去开发、放在不同服务器去运行。当把逻辑划分下去之后,为了负责不同部分的程序员们可以高效调用别人的代码,我们定义了接口。为了不同计算机之间屏蔽系统和语言的差别并进行高效信息传输,我们定义了网络协议。接口和网络协议就是规范,规范使得系统不同的模块合理的整合在一起。
架构的目标:
- 高复用性:通过工具类、组件、服务等减少重复工作
- 高扩展性:使程序解耦、不同环境语言组件服务扩展、数据库扩展
- 容错性(Partition tolerance):可靠、数据不丢失
- 一致性(Consistency):数据一致更新,所有数据变动都是同步的
- 可用性(Availability):好的响应性能
其中容错性、一致性、可用性就是分布式领域中的CAP。有个非常著名的理论就是CAP理论:即任何分布式系统只可同时满足二点,没法三者兼顾。不要将精力浪费在如何设计能满足三者的完美系统,而是应该进行取舍。如何取舍,就要根据项目具体的业务逻辑要求来衡量了。
架构衍化:
没有适合所有系统的架构。写一个项目时,不应按照过于复杂的架构来进行。而是应该项目现有的情况及短时间未来的考量来进行选择合适的架构。随着系统的访问量逐渐加大,再慢慢改进架构才是合适之道,不然会有杀鸡用牛刀之感。
最开始我们学习写项目的时候往往是一片混沌,把所有代码都堆到一起。我们发现很多功能性代码是重复的,比如时间、字符串的处理类等等。于是我们把提取出了工具类,提高了代码的重用性。
再然后,项目变的越来越复杂,很多界面是类似的但是展现的数据却不一样、很多界面是不同的,但是却可能使用了部分相同的数据。如果还是按之前写法,我们只能复制粘贴,使得代码变的越来越臃肿,并且一旦有地方发现写错了就要修改很多地方。于是乎,我们开始把代码进行分离。比较经典的模式就是MVC架构。将代码分为模型(model)、视图(view)、控制器(controller),对代码进行解耦、重用。
再然后,我们发现一台服务器已经支撑不了服务器正常运行了,于是乎我们准备把项目部署到多台服务器上。这时候就会出现两个问题,首先是哪台服务器ip应该和域名绑定,如何把请求分发到多台服务器上、多台服务器以后,我们维持的session数据要怎么办。现在来说,通常的习惯就是加入Ngnix服务器来做负载均衡。session问题可以通过配置ngnix以及借助redis等分布式内存缓存数据库来处理。如果害怕ngnix挂掉,我们还可以做ngnix的双机热备(说白了系统提高可靠性稳定性,就是进行备份,只是用到不同地方备份策略不同罢了)
当公司不止一个项目的时候,而公司一般都是主营一个方向的产品及扩展。此时项目之间又有很多相似的逻辑点和功能,我们把它抽取出来作为组件和服务,并交由一部分人来专门的进行这部分的开发,避免功能的冗余。我们将这部分服务层分隔出来,单独部署在一些服务器上。为了以较小开发代价保证服务器之间交互效率,我们会采用RPC(Remote Procedure Call Protocol)框架来进行服务器之间交互。比较常用的RPC框架有Thrift 、Hessian等,以Thrift为例,它是由 Facebook 开源的一个 RPC 框架,用来进行可扩展且跨语言的服务的开发,支持C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些语言等。另外现在经常用的还有Dubbo,它是阿里开发的分布式服务框架,感觉是在RPC之上的东西,支持Thrift、Avro、RMI等多种协议。
当然此时数据库我们也肯定不能放在一台服务器上了。首先是要有负责数据备份的数据库。再然后就是部署在多台服务器上时,数据库如何进行横向、纵向扩展。对于一些条件查询条件相对固定且少的数据表,我们可以用HBase之类分布式NoSql数据库存储,不仅效率高,也减轻了头疼的去划分传统数据库的痛苦。缓存之类的交给Redis之类专业的缓存数据库。
除此之外,随着访问量加大,我们肯定要做一些数据统计。其中一部分是来自日志,因此,日志采集这部分需要加在每一层。由于日志数据量较大,我们一般根据对日志实时性的要求,分为实时和离线两部分。对于实时性统计不高的这部分代码,我们可以把它提取出来,做离线的数据采集和统计。例如当产生日志时,我们把这部分数据按固定格式(文件夹、文件命名)存在发生事件的本地硬盘中,等待日志采集系统去取(例如Flume),然后交给分布式文件系统(HDFS)。如果日志实时写入HDFS,有可能会出现速度不匹配问题,突然大量数据写入HDFS,这时可以用分布式消息队列(如kafaka),来缓冲这种速度的不平衡。
采集成功后,我们在服务器相对空闲的时间(例如半夜12点)开始进行统计和数据的挖掘。常用的分布式统计工具有Hadoop、Spark、Storm。其中Hadoop效率较低(因为过程要写硬盘),Spark、Strom走的是内存,因此实时性比较高。但这并不意味着Hadoop就被淘汰,现在很多公司中Hadoop应用依然比Spark多。因为一方面spark作为新生物,依然在开发过程中会遇到许多问题,另一方面Hadoop是一个较为庞大的体系,Spark比Hadoop快,说的是比Hadoop中的MR快。像是分布式数据存储,一般还是用Hadoop中的HDFS、HBase这些,Spark一般也会基于这些来存储。
现在用的应用多来越多,不同应用的环境不尽相同。为了便于环境安装。现在有了一个比较热门的应用容器引擎Docker。云计算就好比大货轮。Docker就是集装箱。常规来讲,我们可以在服务器上创建不同的虚拟机在不同的虚拟机上放置不同的应用,但是虚拟机开销比较高。docker可以实现虚拟机隔离应用环境的功能,并且开销比虚拟机小,部署速度快。相比于虚拟机来说,虚拟机会占用空闲内存,而Docker就可以利用起来。
小结:
上面对框架和大概的体系结构有了个初步的介绍。当然关于架构衍化这部分,只是讲了主流的架构大的框架。对于不同的系统,它的架构也肯定不尽相同。之后的文章会对架构这几部分,尤其是其中的技术进行划分讲解。