Ambari架构源码解析

By | 03月09日
Advertisement

1.Ambari介绍

Ambari是hadoop分布式集群配置管理工具,是由hortonworks主导的开源项目。它已经成为apache基金会的孵化器项目,已经成为hadoop运维系统中的得力助手,引起了业界和学术界的关注。

Ambari充分利用了一些已有的优秀开源软件,巧妙地把它们结合起来,使其在分布式环境中做到了集群式服务管理能力、监控能力、展示能力,这些优秀的开源软件有:
(1)、agent端,采用了puppet管理节点。

(2)、在web端,采用ember.js作为前端MVC框架和NodeJS相关工具,用handlebars.js作为页面渲染引擎,在CSS/HTML方面还用了Bootstrap框架。

(3)、在Server端,采用了Jetty、Spring、JAX-RS等。

(4)、同时利用了Ganglia、Nagios的分布式监控能力。

Ambari架构采用的是Server/Client的模式,主要由两部分组成:ambari-agent和ambari-server。ambari依赖其它已经成熟的工具,例如其ambari-server 就依赖python,而ambari-agent还同时依赖ruby, puppet,facter等工具,还有它也依赖一些监控工具nagios和ganglia用于监控集群状况。其中:
1. puppet是分布式集群配置管理工具,也是典型的Server/Client模式,能够集中式管理分布式集群的安装配置部署,主要语言是ruby。
2. facter是用python写的一个节点资源采集库,用于采集节点的系统信息,例如OS信息,主机信息等。由于ambari-agent主要是用python写的,因此用facter可以很好地采集到节点信息。

2.Technology Stack技术堆栈

Ambari Server

  • Server code: Java 1.7 / 1.8
  • Agent scripts: Python
  • Database: Postgres, Oracle, MySQL
  • ORM: EclipseLink
  • Security: Spring Security with remote LDAP integration and local database
  • REST server: Jersey (JAX-RS)
  • Dependency Injection: Guice
  • Unit Testing: JUnit
  • Mocks: EasyMock
  • Configuration management: Python

Ambari Web

  • Frontend code: JavaScript
  • Client-side MVC framework: Ember.js / AngularJS
  • Templating: Handlebars.js (integrated with Ember.js)
  • DOM manipulation: jQuery
  • Look and feel: Bootstrap 2
  • CSS preprocessor: LESS
  • Unit Testing: Mocha
  • Mocks: Sinon.js
  • Application assembler/tester: Brunch / Grunt / Gulp

项目目录介绍

目录 描述
ambari-server Ambari的Server程序,主要管理部署在每个节点上的管理监控程序
Ambari-agent 部署在监控节点上运行的管理监控程序
Contrib 自定义第三方库
ambari-web Ambari页面UI的代码,作为用户与Ambari server交互的。
ambari-views 用于扩展Ambari Web UI中的框架
Docs 文档
ambari-common Ambari-server 和Ambari-agent 共用的代码

一、基本概念:

  1. Resource:Ambari把可以被管理的资源的抽象为一个Resource实例,资源可以包括服务、组件、主机节点等,一个resource实例中包含了一系列该资源的属性;
  2. Property:服务组件的指标名称;
  3. ResourceProvider和PropertyProvider分别对应Resource和Property的提供方,获取指标需要先获取Resource,然后获取Property对应的metric;
  4. Query:Query是Resource的内部对象,代表了对该资源的操作;
  5. Request:一个Request代表了对Resource的操作请求,包含http信息及要操作的Resource的实例,Request按照http的请求方式分为四种:GET、PUT、DELETE、POST;
  6. Predicate:一个Predicate代表了一系列表达式,如and、or等;

二、源代码结构:

  1. org.apache.ambari.server.api.services:对web接口的入口方法,处理/api/v1/*的请求;
  2. org.apache.ambari.server.controller:对Ambari中cluster的管理操作,如新增host,更新service、删除Component等;
  3. org.apache.ambari.server.controller.internal:主要存放ResourceProvider和PropertyProvider;
  4. org.apache.ambari.server.orm.*:数据库操作相关;
  5. org.apache.ambari.server.agent.rest:处理与Agent的接口的入口方法;

三、获取指标流程:

  1. jersy接口接收到请求,创建一个ResourceInstance实例;
  2. 解析http请求构造一个Request对象,然后交给reques的process()方法来处理;
  3. reques解析url或http_body得到一个Predicate对象;
  4. 根据http类型获取handler,GET请求对应ReadHandler;
  5. handler向Query对象中添加分页、Render、Predicate等属性后,然后让query.execute();
  6. 根据Resource.Type获得对应的ResourceProvider对象,调用其getResources方法得到Set;
  7. 调用对应的PropertyProvider填充Resource;
  8. 处理结果,放回json结果

Ambari-server

ambari-server是一个有状态的,它维护着自己的一个有限状态机FSM。同时这些状态机存储在数据库中,前期数据库主要采用postgres。如下图所示,server端主要维护三类状态:
1. Live Cluster State:集群现有状态,各个节点汇报上来的状态信息会更改该状态;
2. Desired State:用户希望该节点所处状态,是用户在页面进行了一系列的操作,需要更改某些服务的状态,这些状态还没有在节点上产生作用;
3. Action State:操作状态,是状态改变时的请求状态,也可以看作是一种中间状态,这种状态可以辅助Live Cluster State向Desired State状态转变。

Ambari-server的Heartbeat Handler模块用于接收各个agent的心跳请求(心跳请求里面主要包含两类信息:节点状态信息和返回的操作结果),把节点状态信息传递给FSM状态机去维护着该节点的状态,并且把返回的操作结果信息返回给Action Manager去做进一步的处理。
Coordinator模块又可以称为API handler,主要在接收WEB端操作请求后,会检查它是否符合要求,stage planner分解成一组操作,最后提供给Action Manager去完成执行操作。

因此,从上图就可以看出,Ambari-Server的所有状态信息的维护和变更都会记录在数据库中,用户做一些更改服务的操作都会在数据库上做一些相应的记录,同时,agent通过心跳来获得数据库的变更历史。

Ambari-Agent

ambari-agent是一个无状态的。其功能主要分两部分:
- 采集所在节点的信息并且汇总发心跳汇报给ambari-server;
- 处理ambari-server的执行请求。

因此它有两种队列:
- 消息队列MessageQueue,或为ResultQueue。包括节点状态信息(包括注册信息)和执行结果信息,并且汇总后通过心跳发送给ambari-server;
- 操作队列ActionQueue。用于接收ambari-server返回过来的状态操作,然后能过执行器按序调用puppet或python脚本等模块完成任务。

实现细节

Ambari-Server启动

Ambari-Server是一个WEB Server,提供统一的REST API接口,同时向web和agent开放了两个不同的端口(默认前者是8080, 后者是8440或者8441)。它是由Jetty Server容器构建起来的,通过Spring Framework构建出来的WEB服务器,其中大量采用了google提供的Guice注解完成spring框架所需要的注入功能(想一想,之前spring框架需要加载一个applicationcontext.xml文件来把bean注入进来,现在可以用Guice注解的方式就可以轻松完成)。 REST框架由JAX-RS标准来构建。

Ambari-Server接受来自两处的REST请求,Agent过来的请求处理逻辑由包org.apache.ambari.server.agent处理, 而API所的处理逻辑来自org.apache.ambari.server.api。详见如下代码:

“`

“`

Ambari-Server有一个状态机管理模块,所有节点的状态信息更改都最终提供给状态机进行更改操作,因此状态机是一个很忙的组件。在Ambari-Server里面,把每一次更改操作都把它当作是一类事件,采用事件驱动机制完成对应的任务。这种思想有点借鉴已经运用在hadoop 2.x YARN里面的事件驱动机制。事件驱动机制能够一种高效的异步RPC请求方式,直接调用需要执行相应的代码逻辑,而事件驱动只需要产生事件统一提交给事件处理器,因此事件驱动需要一个更复杂的有限状态机结合起来一同使用。

Ambari-Server处理Ambari-Agent请求

Agent发送过来的心跳请求由org.apache.ambari.server.agent.HeartBeatHandler.handleHeartBeat(HeartBeat)来处理,执行完后,同时会返回org.apache.ambari.server.agent.HeartBeatResponse给agent。 org.apache.ambari.server.agent.HeartBeat里面主要含了两类信息:节点的状态信息nodeStatus和服务状态信息componentStatus。

public class HeartBeatHandler {
  ...
    public HeartBeatResponse handleHeartBeat(HeartBeat heartbeat)
      throws AmbariException {
    long now = System.currentTimeMillis();
    if (heartbeat.getAgentEnv() != null && heartbeat.getAgentEnv().getHostHealth() != null) {
      heartbeat.getAgentEnv().getHostHealth().setServerTimeStampAtReporting(now);
    }

    String hostname = heartbeat.getHostname();
    Long currentResponseId = hostResponseIds.get(hostname);
    HeartBeatResponse response;

    if (currentResponseId == null) {
      //Server restarted, or unknown host.
      LOG.error("CurrentResponseId unknown for " + hostname + " - send register command");
      // 无responseId, 新请求,就进行注册, responseId =0
      return createRegisterCommand();
    }

    LOG.debug("Received heartbeat from host"
        + ", hostname=" + hostname
        + ", currentResponseId=" + currentResponseId
        + ", receivedResponseId=" + heartbeat.getResponseId());

    if (heartbeat.getResponseId() == currentResponseId - 1) {
      LOG.warn("Old responseId received - response was lost - returning cached response");
      return hostResponses.get(hostname);
    } else if (heartbeat.getResponseId() != currentResponseId) {
      LOG.error("Error in responseId sequence - sending agent restart command");
      // 心跳是历史记录,那么就要求其重启,重新注册,responseId 不变
      return createRestartCommand(currentResponseId);
    }

    response = new HeartBeatResponse();
    //responseId 加 1 , 返回一个新的responseId,下次心跳又要把这个responseId带回来。
    response.setResponseId(++currentResponseId);

    Host hostObject;
    try {
      hostObject = clusterFsm.getHost(hostname);
    } catch (HostNotFoundException e) {
      LOG.error("Host: {} not found. Agent is still heartbeating.", hostname);
      if (LOG.isDebugEnabled()) {
        LOG.debug("Host associated with the agent heratbeat might have been " +
          "deleted", e);
      }
      // For now return empty response with only response id.
      return response;
    }
    //失去心跳,要求重新注册, responseId=0
    if (hostObject.getState().equals(HostState.HEARTBEAT_LOST)) {
      // After loosing heartbeat agent should reregister
      LOG.warn("Host is in HEARTBEAT_LOST state - sending register command");
      return createRegisterCommand();
    }

    hostResponseIds.put(hostname, currentResponseId);
    hostResponses.put(hostname, response);

    // If the host is waiting for component status updates, notify it
    //如果主机正在等待组件状态更新,请通知它
    //节点已经进行了注册,但是该节点还没有汇报相关状态信息,等待服务状态更新
    if (heartbeat.componentStatus.size() > 0
        && hostObject.getState().equals(HostState.WAITING_FOR_HOST_STATUS_UPDATES)) {
      try {
        LOG.debug("Got component status updates");
        //更新服务状态机
        hostObject.handleEvent(new HostStatusUpdatesReceivedEvent(hostname, now));
      } catch (InvalidStateTransitionException e) {
        LOG.warn("Failed to notify the host about component status updates", e);
      }
    }

    if (heartbeat.getRecoveryReport() != null) {
      RecoveryReport rr = heartbeat.getRecoveryReport();
      processRecoveryReport(rr, hostname);
    }

    try {
      if (heartbeat.getNodeStatus().getStatus().equals(HostStatus.Status.HEALTHY)) {
        //向状态机发送更新事件,更新节点至正常状态
        hostObject.handleEvent(new HostHealthyHeartbeatEvent(hostname, now,
            heartbeat.getAgentEnv(), heartbeat.getMounts()));
      } else { // 把节点列入不健康
        hostObject.handleEvent(new HostUnhealthyHeartbeatEvent(hostname, now, null));
      }
    } catch (InvalidStateTransitionException ex) {
      LOG.warn("Asking agent to re-register due to " + ex.getMessage(), ex);
      hostObject.setState(HostState.INIT);
      return createRegisterCommand();
    }

    /**
     * A host can belong to only one cluster. Though getClustersForHost(hostname)
     * returns a set of clusters, it will have only one entry.
     *主机只能属于一个集群。 通过getClustersForHost(hostname)返回一组集群,它只有一个条目。
     *
     * TODO: Handle the case when a host is a part of multiple clusters.
     * 处理 主机是多个集群的一部分时的 情况。
     */
    Set<Cluster> clusters = clusterFsm.getClustersForHost(hostname);

    if (clusters.size() > 0) {
      String clusterName = clusters.iterator().next().getClusterName();

      if (recoveryConfigHelper.isConfigStale(clusterName, hostname, heartbeat.getRecoveryTimestamp())) {
        RecoveryConfig rc = recoveryConfigHelper.getRecoveryConfig(clusterName, hostname);
        response.setRecoveryConfig(rc);

        if (response.getRecoveryConfig() != null) {
          LOG.info("Recovery configuration set to {}", response.getRecoveryConfig().toString());
        }
      }
    }

    heartbeatProcessor.addHeartbeat(heartbeat);

    // Send commands if node is active
    if (hostObject.getState().equals(HostState.HEALTHY)) {
      sendCommands(hostname, response);
      annotateResponse(hostname, response);
    }

    return response;
  }

  ...
}

Ambari-Agent执行流程

安装ambari-agent 服务时会把相应在的python代码置于python执行的环境上下文中,例如其入口代码可能是/usr/lib/python2.6/site-packages/ambari_agent/main.py,并且进行相关初始化工作(例如验证参数,与server建立连接,初始化安全验证证书),最后会产生一个新的控制器Controller子线程来统一管理节点的状态。Controller线程里面有一个动作队列ActionQueue线程,并且开启向Server注册和发心跳服务。可以看出来,ambari-agent主要由两个线程组成,Controller线程向Server发送注册或心跳请求,请求到的Action数据放到ActionQueue线程里面,ActionQueue线程维护着两个队列:commandQueue和resultQueue。ActionQueue线程会监听commandQueue的状况。

class Controller(threading.Thread):
  def __init__(self, config, range=30):
  // 在初始化Controller之前,ambari-agent就会在main.py里面进行判断:ambari-server是否正常,正常才会初始化Controller
  // 省略初始化代码
  def run(self):
    try:
      // 初始化队列线程
      self.actionQueue = ActionQueue(self.config, controller=self)
      self.actionQueue.start()
      // 初始化注册类
      self.register = Register(self.config)
      // 初始化心跳类
      self.heartbeat = Heartbeat(self.actionQueue, self.config, self.alert_scheduler_handler.collector())

      opener = urllib2.build_opener()
      urllib2.install_opener(opener)

      while True:
        self.repeatRegistration = False
        //开始注册 并且 定时发心跳
        self.registerAndHeartbeat()
        if not self.repeatRegistration:
          logger.info("Finished heartbeating and registering cycle")
          break
    except:
      logger.exception("Controller thread failed with exception:")
      raise

    logger.info("Controller thread has successfully finished")

CommandQueue队列主要有3类command:
1. REGISTER_COMMAND:该类命令主要通知agent重新向server发送注册请求。
2. STATUS_COMMAND:该类命令主要告诉agent需要向server发送某组件的状态信息。
3. EXECUTION_COMMAND:要求agent执行puppet或者软件集升级任务

Ambari架构源码解析

Ambari架构源码解析

Ambari架构源码解析

Ambari架构源码解析

Ambari架构源码解析

Ambari架构源码解析

Ambari架构源码解析

Ambari架构源码解析

Ambari架构源码解析

Similar Posts:

  • jQuery源码解析(架构与依赖模块)

    jQuery设计理念 引用百科的介绍: jQuery是继prototype之后又一个优秀的Javascript框架.它是轻量级的js库 ,它兼容CSS3,还兼容各种浏览器(IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+),jQuery2.0及后续版本将不再支持IE6/7/8浏览器.jQuery使用户能更方便地处理HTML(标准通用标记语言下的一个应用).events.实现动画效果,并且方便地为网站提供AJAX交互.jQuery还有一个比较大的优势是,它的文档说

  • ReactiveCocoa v2.5 (RAC) 源码解析之架构总览

    ReactiveCocoa 是一个 iOS 中的函数式响应式编程框架,它受 Functional Reactive Programming 的启发,是 Justin Spahr-Summers 和 Josh Abernathy 在开发 GitHub for Mac 过程中的一个副产品,它提供了一系列用来组合和转换值流的 API . Mattt Thompson 大神是这样评价 ReactiveCocoa 的: Breaking from a tradition of covering Apple

  • Java 集合系列 10 Hashtable详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 Java 集合系列 05 Vector详细介绍(源码解析)和使用示例 Java 集合系列 06 Stack详细介绍(源码解析)和使用示例 Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和

  • Java 集合系列 05 Vector详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 Java 集合系列 05 Vector详细介绍(源码解析)和使用示例 Java 集合系列 06 Stack详细介绍(源码解析)和使用示例 Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和

  • JQuery源码解析(一)

    写在前面:本<JQuery源码解析>系列是基于一些前辈们的文章进行进一步的分析.细化.修改而写出来的,在这边感谢那些慷慨提供科普文档的技术大拿们. 要查阅JQ的源文件请下载开发版的JQ.js文档,下载地址:http://jquery.com/download/ 注意选择其中的development版本进行下载,如下图所示 开发版本的JQ.js属于非压缩的源文件,方便我们阅读和分析其代码. 下载完用Dreamweaver或其它代码编辑器打开查阅即可.我们今后分析的代码也是基于1.11.0版本的J

  • [置顶] iOS开源库源码解析之SDWebImage

    来自Leo的原创博客,转载请著名出处 我的stackoverflow 这个源码解析系列的文章 AsnycDispalyKit SDWebImage(本文) 前言 SDWebImage是iOS开发中十分流行的库,大多数的开发者在下载图片或者加载网络图片并且本地缓存的时候,都会用这个框架.这个框架相对来说,源代码还是比较少的.本文会详细的讲解这些类的架构关系和原理. 本文会先介绍类的整体架构关系,先有一个宏观的认识.然后讲解sd_setImageWithURL的加载逻辑,因为这是SDWebImage

  • 开源项目源码解析-PhotoView 源码解析

    PhotoView 源码解析 本文为 Android 开源项目源码解析 中 PhotoView 部分 项目地址:PhotoView,分析的版本:48427bf,Demo 地址:PhotoView-demo 分析者:dkmeteor,校对者:cpacm,校对状态:完成 1. 功能介绍 特性(Features): 支持Pinch手势自由缩放. 支持双击放大/还原. 支持平滑滚动. 在滑动父控件下能够运行良好.(例如:ViewPager) 支持基于Matrix变化(放大/缩小/移动)的事件监听. 优势

  • Android入门笔记之源码解析一

    Android入门笔记之源码解析一 <1>简介 今天下了很多源码,接下来几篇博客来分析这几个源码的架构. 首先是<左边按钮右边菜单>这个简单的源码. <2>关键步骤 主Activity为SlidingActivity,它使用了自己定制的布局文件. 而在这个布局文件中采用相对布局. SlidingMenu.java中主要有5个方法,setLeftView.setRightView.setCenterView主要是用来设置左.主.右界面的,其中setCenterView中有

  • NineOldAnimations 源码解析

    NineOldAnimations 源码解析 本文为 Android 开源项目源码解析 中 NineOldAnimations 部分 项目地址:NineOldAnimations,分析的版本:d582f0e,Demo 地址:NineoldAnimations Demo 分析者:Mr.Simple,校对者:lightSky,校对状态:已完成 1. 功能介绍 NineOldAndroids 是一款支持在低版本(API 11 以下)使用 Android 属性动画以及 3D 旋转动画的框架,它提供了一系

  • Tomcat源码解析系列

    学web也有一段时间了,为了从底层了解web应用在Tomcat中的运行,决定看一下Tomcat的源码参见<How Tomcat works>    和大牛博客,对大体架构有了一定的了解,但是具体细节还是不大清楚,以后随着对这方面的深入再具体看吧. 下面是解析的Tomcat系列: Tomcat源码解析(一)下载源码与导入eclipse Tomcat总体架构(Tomcat源码解析系列二) tomcat的启动过程(Tomcat源码解析(三)) Tomcat关闭过程(Tomcat源码解析四) Tomc

Tags: