ZStack源码剖析:如何在百万行代码中快速迭代

本文首发于泊浮目的专栏: https://segmentfault.com/blog...

前言

ZStack是下一代开源的云计算IaaS(基础架构即服务)软件。它主要面向的是将来的智能数据中心,经过提供的API来管理包括计算、存储和网络在内的数据中心的各类资源。跟OpenStack相比,ZStack具备易用、稳定、灵活、超高性能等特色。其单管理节点能够管理1万台物理机规模集群,多个管理节点构建的集群能够作到使用一个数据库、一套消息总线管理10万台物理机、数百万个虚拟机节点、并发处理数万个API。java

如下是ZStackV2.2的服务架构图
官网地址: http://www.zstack.io/

核心开源引擎ZStack GitHub:https://github.com/zstackio/z...git

ZStack-Utility GitHub:https://github.com/zstackio/z...github

阅读源码若是不想使用IDE,建议配合https://github.com/buunguyen/...web

本文将对核心引擎-ZStack的源码进行剖析。在ZStack官网上咱们能够看到其每一个版本的发布都是携带了许多的新特性。在笔者看来,可以快速迭代的缘由首先是来自于每位工程师的辛勤付出。除此以外,因其还有些软件工程领域中沉淀下来的最佳实践:算法

  • 良好的架构设计
  • 覆盖较为全面的测试
  • 恰当好处的使用设计模式

良好的架构设计

异步架构

Iaas的核心应该作的是管控层,而不是数据层。故ZStack仅仅也是作出一些“决策”而已——在设计系统的时候,应不考虑在这些决策的执行上消耗大量的资源。在面对大量请求或者“决策”的时候,若是使用多线程来处理阻塞式IO模型时会遇到一些问题:数据库

  • 阻塞模型的吞吐量受到线程池大小的限制;
  • 建立并使用许多线程会耗费额外的时间用于上下文切换,影响系统性能。

而非阻塞、异步的消息驱动系统能够只运行少许的线程,而且不阻塞这些线程,只在须要计算资源时才使用它们。这大大提升了系统的响应速度,而且可以更高效地利用系统资源。segmentfault

故,ZStack采用了异步架构,分别由三个部分组成:设计模式

  • 异步消息
  • 异步方法
  • 异步HTTP 请求

若是在系统中的一部分采用异步设计,是不行的。这样仍是会由于同步而无法享受异步带来的“福利”。故此整个系统都得采用异步架构。网络

  • 相对的,开发者们在编写异步代码的时候得格外当心。
  • 在系统设计中,异步调用能够减小系统在IO上出现瓶颈的可能性。
扩展连接 : ZStack--可拓展性的秘密武器1:异步架构

无状态服务

ZStack中,每个服务都是独立存在的。为了方便的管理更多的物理机,ZStack推荐采用集群部署MN。但这样就会遇到一个问题,不一样MN下面有着不一样的几个服务存在,在这里咱们设其为X个服务。在10个MN部署的状况下,可能就是10X个服务。那么在一个资源须要操做时,我须要发送向对应的MN。那么如何找到那个MN呢?最直观的想法就是在各个MN中保存相应的“服务表”,这便是一种状态。那么在分布式系统中,采用有状态的服务绝对不是一个好的选择,它会严重影响系统的扩展性。ZStack巧妙的采用了一致性哈希算法+MQ解决了这个问题。多线程

  • 这在系统设计中实为是一种使用一致性hash技术的负载均衡
扩展连接: ZStack—可拓展性秘密武器2:无状态的服务

无锁架构

解决并发的问题不必定要用显式的锁,也能够对同一资源作操做的任务作成队列使其串行执行。

注意:并发 != 并行
扩展连接: ZStack--可拓展性秘密武器3:无锁架构

松耦合架构

项目模块化

Intellij中打开ZStack的代码,会发现大多数目录底下都会有一个pom.xml文件,ZStack采用了模块化项目。模块化的好处在工程实践中不言而喻的,好比:

  • 能够在不影响整个系统的状况下替换某个模块
  • 开发者只要专心的在本身的模块中工做便可
  • 减小系统耦合度,提升内聚,减小资源循环依赖,加强系统框架设计
  • ...

下面来看一下ZStack中代码的结构:

代码结构

截图于2017.9.22
名称 简介
build 用于Java部分的编译、打包、部署等
conf 配置文件及SQL文件的放置;Spring Service配置存放;持久化文件配置
core 核心模块。实现系统的核心功能——包括数据库、消息总线、工做流实现等等
coregroovy ZStack的最新测试采用了Groovy,这里是对测试库作的支持
header 消息以及Entity的定义
plugin 顾名思义。其中很多组件都以插件化开发,提供较高的灵活性
sdk 测试库使用的SDK
simulator 对于测试库支持的又一模块,主要用户simulator agent的行为
testlib 测试库
test 测试模块
工具类 工具包。目前仅仅支持了doc生成
utils 代码中使用的工具类
其余 功能实现模块

经过MQ来解耦合

ZStack中,每一个功能实现模块都会被称为服务——一个独立的服务。各个服务之间的通讯由MQ来承担。这就像是传统的CSE,C和E是不耦合的,经过S来交互。一样的,一个服务须要向另外一个服务发起调用,只需往消息总线发送消息,并指定这个服务ID(Service ID)便可。若是某个服务的代码须要大量重构或者作成微服务,只要提供相同的服务并注册到MQ上就能够了。这就是事件驱动架构(Event Driven Architecture)的一种典型实现。

CSE:Controller、Service、Entity。注:称做Domain或者Model都是不专业的。Domain是一个领域对象,每每咱们再作传统Java软件web开发中,这些Domain都是贫血模型,是没有行为的,或是没有足够的领域模型的行为的,因此,以这个理论来说,这些Domain都应该是一个普通的entity对象,并不是领域对象,因此请把包名改成:com.xxx.entity。

举个简单明了的例子。若是每一个对象的行为都是经过消息来决定的(好比一个方法须要message获得回复后才能do something...),那么这个对象仅仅对消息总线产生了依赖。在测试中,将会发挥巨大的威力——咱们只须要改变handle message处的行为,就可使一个对象行为作出相应的变化。这样可使mock的单位变得更小,同时也能够变得更加灵活。

试想若是经过函数调用:

//方法a中的代码
xxService.method1();
xx2Service.method2();

在测试中该如何解耦?但若是经过MQ——即一个消息来调用xxService.method1(),那么方法a对xxService就没有了直接的依赖。

使用Spring

不了解Spring的人能够看: 看起来很长但仍是有用的Spring学习笔记

在代码中,每当咱们New出一个对象时,这个模块便对这个对象产生了依赖。当咱们须要测试的时候就不得不去Mock它。当依赖的对象or Field 有成千上万个的时候,这就是一场灾难了。代码变得愈发不可测,坑就越多,开发者在扩展or维护项目的时候就会愈发的乏力。这就像是咱们以前提到的MQ,服务1->MQ->服务2,因为中间隔了一个MQ,因而服务1和服务2没有必然的关系。一样的,从对象1->调用->对象2对象1->调用->Spring提供的IOC容器->对象2,这样使对象与对象之间也没有了直接调用关系,对象1只要知道它要调用的对象实现了其须要的Interface就是能够调用的。

除了Autowired的正确使用姿式。在ZStack中,还有一类颇有意思的代码,通常称之为xxxExtensionPoint。其本质就是定义一个接口,而后其实现类做为Bean经过XML注册到IOC中。在须要使用的时候,经过Spring获取到全部实现该接口的对象,调用其函数。这样就会使代码变得很是的灵活。

例如,ZStack分为多个版本——开源版、企业版、混合云版等。若是一个服务在不一样版本中的处理逻辑须要稍许不一样,那么就能够在开源版的代码中注册一个接口,在另外一个版本的服务中实现该接口。这样也不会影响到开源版的原有逻辑。从模块上看咱们代码的是松耦合而且没法直接调用的,可是在内存中,倒是能够调用获得的。

覆盖较为全面的测试

ZStack

  • 开发者Fix每个Bug都是须要补充相应的Case;
  • 每个Feature在进去以前更会由开发工程师与QA工程师同时制定测试场景并Cover;
  • 每个PR(pull request)进去以前都会经过PR系统跑过全部的Case,以防止Bug的Regression;

因为ZStack源码作到了必定的解耦合(上述提到)与无状态,使得集成测试得以进行。

其首席架构师Frank.Zhang曾说过:咱们开发者在写代码的时候每每就应该考虑该怎么写测试了。

想了解ZStack的测试框架,能够看: ZStack WiKi :管理节点基于模拟器的Integration Test框架

恰当好处的使用设计模式

ZStack中,设计模式有较为良好的实践。笔者有机会将会在以后的系列文章分析其中的典型案例以及在代码中使用极其频繁的核心工具。

相关文章
相关标签/搜索