分布式对象存储Ambry(4)Ambry-Server模块源代码解析(启动与整体通信工作篇)

Ambry一共有三个主模块:Ambry-Server,Ambry-Frontend还有Ambry-Admin。其中,Ambry-Server为其核心,我们从核心开始,一部一部剖析其源码,并会分析它的设计模式。

一个提供各种服务的服务器框架源代码,我们一般从它的Facade类开始入手进行第一步分析。Facade类就是指设计模式中的外观模式的核心类,这个类会包含这个框架几乎所有的模块。一般的,像Ambry这种服务器型框架,都会在整体设计模式上采用单例、桥接还有外观设计模式结合,我们寻找到这个单例Facade类,就能发现这个系统中的所有模块。

Ambry的主要Facade类就是是AmbryServer这个类,它的成员包括:

其中:

  1. shutdownLatch:用于关闭AmbryServer,这是一种比较巧妙的运用CountDownLatch来实现Server启动和优雅退出的方式。
  2. networkServer:这个类管理NIO通信连接还有工作调度分配方式,包括NIO通信,各种工作线程池,调度流程等等
  3. requests:这个类保存着处理requests的方法,对于不同的request如何处理,就在这个类里面。
  4. requestHandlerPool:执行上面处理requests的方法的线程池
  5. scheduler:定时任务调度线程池
  6. storeManager:存储与持久化对象管理类
  7. replicationManager:备份管理类
  8. properties:用于读取配置验证的工具类
  9. clusterMap:集群拓扑管理类
  10. registry,reporter:监控指标类
  11. connectionPool:连接池,管理所有连接
  12. notificationSystem:下游提醒系统
  13. metrics:server监控指标
  14. time:Ambry特定监控时间类

程序启动和优雅退出的关键设计 - shutdownLatch

Ambry运用了Java中的CountDownlatch来实现启动和优雅退出。首先,在AmbryServer启动之后(所有配置载入完毕,各种资源,比如线程池等等,初始化完毕),主线程会在这个shutdownLatch上执行await()方法。

同时,主线程会使用Runtime.getRuntime().addShutdownHook(shutdownHook);这个方法增加一个钩子来监听程序关闭事件,例如(kill -9,ctrl+c):

Runtime.getRuntime().addShutdownHook(new Thread() {
  public void run() {
    logger.info("Received shutdown signal. Shutting down AmbryServer");
    ambryServer.shutdown();
  }
});

主线程主要操作是调用AmbryServer的启动还有等待方法:

     //载入相关配置
      final InvocationOptions options = new InvocationOptions(args);
      final Properties properties = Utils.loadProps(options.serverPropsFilePath);
      final VerifiableProperties verifiableProperties = new VerifiableProperties(properties);
      final ClusterMap clusterMap =
          new ClusterMapManager(options.hardwareLayoutFilePath, options.partitionLayoutFilePath,
              new ClusterMapConfig(verifiableProperties));
      logger.info("Bootstrapping AmbryServer");
      //利用配置新建AmbryServer
      ambryServer = new AmbryServer(verifiableProperties, clusterMap, SystemTime.getInstance());
      // 增加一个钩子来监听程序关闭事件,例如(kill -9,ctrl+c)
      Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
          logger.info("Received shutdown signal. Shutting down AmbryServer");
          ambryServer.shutdown();
        }
      });
      //启动AmbryServer
      ambryServer.startup();
      //主线程进入阻塞状态
      ambryServer.awaitShutdown();

这里的ambryServer.startup();是启动方法,ambryServer.awaitShutdown();是主线程进入阻塞状态。这个方法代码:

public void awaitShutdown() throws InterruptedException {
  shutdownLatch.await();
}

在检测到ctrl+c或者kill信号时,ambryServer.shutdown();会被执行,这个方法关闭各个模块,清理资源,在方法的最后:

    try {
      //关闭各个模块,清理资源
    } catch (Exception e) {
      logger.error("Error while shutting down server", e);
    } finally {
      shutdownLatch.countDown();
      long processingTime = SystemTime.getInstance().milliseconds() - startTime;
      metrics.serverShutdownTimeInMs.update(processingTime);
      logger.info("Server shutdown time in Ms " + processingTime);
    }

shutdownLatch.countDown();总会被执行,这时shutdownLatch归零,主线程阻塞停止,由于主线程接下来没有代码,程序结束。

整个Server的通信设计 - networkServer

之前Ambry在这里是BIO设计,如今改成了NIO设计。

未来可能还会设计成AIO的模式,所以在这里networkServer是一个接口。

这里我们类比下其他服务器框架的NIO通信设计(其实都是一种Reactor模式的设计):

MyCat的NIO通信模块:

这个图有点不准确,在连接完成后,其实client的request还有response就是和NIOReactor交互,这里忘了画了。。。

简单来说,就是,client在与server通信时。Server有一个NIOAcceptor线程在监听client连接事件(通过NIOAcceptor的Selctor,这个Selector就是Java NIO中的核心,负责监听多个SocketChannel的四种事件),有新的client连接时,接受连接,之后将这个SocketChannel连接转交给NIOReactorPool这个线程池中的一个线程(其实就是在这个线程上的Selector注册读写事件监听),这个线程处理链接发来的请求,并把一些重工作(例如结果集合并与复杂结果集处理等等)交给BusinessExecutor这个线程池执行,这样可以增加整体的client处理响应吞吐量。

Netty的NIO通信模块:

类似地,Netty的核心机制也是差不多这样,Boss线程池负责监听client连接事件,有client连接时,处理连接,之后将连接转交给WORK线程池处理来自于client的请求,WORK一般会做一些请求与响应的编码解码等轻量级的工作,为了不影响整体吞吐量,将耗时的业务操作(例如访问数据库等)转交给EXECUTOR线程池去做。

Ambry的NIO通信模块:

Ambry的和之前的大同小异:

  1. 首先Client连接,触发OP_ACCPET
  2. Ambry有一个Acceptor线程监听OP_ACCEPT事件,连接完成,将连接转交给Processors
  3. 之后每个Processor监听读写事件,形成Client发送Request,Processor返回Response的效果
  4. Processor其实只负责将接收到的请求封装成Request,交给RequestHandler处理

    同时接收Response,翻译后发送给Client

  5. 真正处理Request的是RequestHandler

我们来看下Acceptor还有Processor的源代码来加深下理解:

Acceptor还有Processor都继承AbstractServerThread,这个抽象类就是一个抽象线程类。包含startupLatch,shutdownLatch还有alive;

startupLatch还有shutdownLatch和其他地方的CountDownlatch类似,一个负责等待该线程启动初始化完成,一个负责等待该线程优雅关闭完成。alive是一个AtomicBoolean类型,通过这个类型还有名字我们很容易猜出它是表示这个线程是否还存活,对于监控很有用。

public AbstractServerThread() throws IOException {
    startupLatch = new CountDownLatch(1);
    shutdownLatch = new CountDownLatch(1);
    alive = new AtomicBoolean(false);
  }

  /**
   * Initiates a graceful shutdown by signaling to stop and waiting for the shutdown to complete
   */
  public void shutdown() throws InterruptedException {
    alive.set(false);
    shutdownLatch.await();
  }

  /**
   * Wait for the thread to completely start up
   */
  public void awaitStartup() throws InterruptedException {
    startupLatch.await();
  }

  /**
   * Record that the thread startup is complete
   */
  protected void startupComplete() {
    alive.set(true);
    startupLatch.countDown();
  }

  /**
   * Record that the thread shutdown is complete
   */
  protected void shutdownComplete() {
    shutdownLatch.countDown();
  }

  /**
   * Is the server still running?
   */
  protected boolean isRunning() {
    return alive.get();
  }

这里内容不少了,先小结个尾,接下来的分析会在下一篇文章中展示,敬请期待。

时间: 2024-11-05 13:33:25

分布式对象存储Ambry(4)Ambry-Server模块源代码解析(启动与整体通信工作篇)的相关文章

海量图片存储,杉岩分布式对象存储轻松应对

当今世界,互联网.大数据应用迅猛发展,物联网.人工智能.云计算 技术日新月异,随之而来的是各种企业和个人应用持续不断地产生亿级甚至是百亿级的海量小文件.这些小文件的元数据管理.存储性能以及访问效率等问题因而成为学术界和工业界公认的难题. 例如,国内目前最大的电商网站淘宝存储的商品图片超过 200 亿张,这些文件的平均大小仅为 15KB 左右,国外著名的社交网站Facebook 存储的图片总量更是超过了600亿张:在线视频播放服务中,每个视频会被切片服务器分割成 1MB 左右的分片文件,一部动画电

《转》OpenStack对象存储——Swift

OpenStack Object Storage(Swift)是OpenStack开源云计算项目的子项目之一,被称为对象存储,提供了强大的扩展性.冗余和持久性.本文将从架构.原理和实践等几方面讲述Swift. Swift并不是文件系统或者实时的数据存储系统,它称为对象存储,用于永久类型的静态数据的长期存储,这些数据可以检索.调整,必要时进行更新.最适合存储的数据类型的例子是虚拟机镜像.图片存储.邮件存储和存档备份.因为没有中心单元或主控结点,Swift提供了更强的扩展性.冗余和持久性.Swift

swift对象存储

swift对象存储 简介 OpenStack Object Storage(Swift)是OpenStack开源云计算项目的子项目之一,被称为对象存储,提供了强大的扩展性.冗余和持久性.对象存储,用于永久类型的静态数据的长期存储. Swift 最初是由 Rackspace 公司开发的高可用分布式对象存储服务,并于 2010 年贡献给 OpenStack 开源社区作为其最初的核心子项目之一,为其 Nova 子项目提供虚机镜像存储服务.Swift 构筑在比较便宜的标准硬件存储基础设施之上,无需采用

对象存储VS块存储

在今天的IT环境中,云计算已经作为一个时代的代名词,而在云的存储基础设施中,对象存储和块存储是两个最基本的存储形式,也是各家云提供商最常提供的两种基础存储服务.那么对象存储与块存储有什么联系和区别呢,下面我将从基础层面为各位看官慢慢道来. 通常意义上来说,对象存储也就是键值存储,一般提供使用HTTP协议通过简单的PUT .GET等接口,适合在云环境中进行大规模的非结构化数据存储使用.而块存储主要指能够模拟或表现为计算机裸盘,能够被计算主机当做硬盘使用的存储形式.从这个角度看,对象存储和块存储并没

对象存储,未来存储新潮流

大家众说纷“云”,其中,云存储已经成为业界最为火热的概念之一.大数据时代,没有存储或存储技术,一切都将成为“浮云”! 对象存储本身是一种与传统完全不同的解决方案,类似于当前正在兴起的软件定义存储趋势.客户会利用服务器——多数情况下为商用服务器——来实现存储功能,而供应商必须理解并接受这一点.因此对于硬件供应商来说,他们需要做的不再是单纯依靠存储业务部门销售阵列或者文件存储设备,而是再加深入地推动服务器业务升级.这给新兴的软件定义存储厂商留下了很大的想象空间. 事实上,对象存储与块存储.文件存储,

4.5PB!杉岩对象存储中标天威视讯“中心存储系统建设”

日前,深圳天威视讯股份有限公司(以下简称天威视讯)公布中心存储系统中标结果,杉岩对象存储中标,项目整体裸容量存储规模达到4.5PB. 天威视讯是我国有线电视网络行业的第一家股份制企业,是深圳文化产业第一股,负责深圳地区有线电视网络的建设.经营和维护,提供有线电视收视服务.电视增值业务以及互联网接入服务等,逐步形成了以传输视频信息和开展网上多功能服务为主.向产业链的上下游***.多业务并举的文化产业发展模式,探索出一条广电事业和文化产业协调发展的新路. 为了给客户提供高端前沿的视频产品和丰富多彩的

Ceph分布式radosgw对象存储融合Swift 、S3的访问应用

**Ceph分布式radosgw对象存储融合Swift .S3的访问应用** 确保集群状态正常:前半部分配置参照 https://blog.51cto.com/jdonghong/244175 上半部分配置.安装RGW实例[[email protected] idc-cluster]# ceph-deploy install --rgw client1 client2 client3 [[email protected] idc-cluster]# ceph-deploy rgw create

MogileFS + Nginx 实现基于CentOS7平台的分布式文件存储与访问

MogileFS是一个开源的分布式文件系统,Nginx是开源的4-7层web应用服务端以及反向代理服务端.本文基于CentOS7平台,进行MogileFS + Nginx的部署 MogileFS的一些注意事项 针对于MogileFS,有如下概念需要注意一下. MogileFS属于有中心节点形式的分布式文件系统,元数据默认存储在关系型数据库(MySQL)当中,在此处于单点,因此有必要对MySQL使用主从复制或者MHA. 按功能分为tracker,database,storage.其中tracker

对象存储与块存储、文件存储等对比

看到 一篇文档, 讲 对象存储, 好奇,搜索文章,摘抄,学习记录 ! 背景: 传统存储在面对海量非结构化数据时,在存储.分享与容灾上面临很大的挑战,主要表现在以下几个方面:传统存储并非为非结构化内容设计或优化.成本过高.并非PB级的扩展.不支持永远在线.专有的一体机设备等等,非结构化数据以每年60%-80%的速率增长,从而可扩展性变成了最迫切的需求. 传统存储在面对海量非结构化数据时,在存储.分享与容灾上面临很大的挑战,主要表现在以下几个方面:传统存储并非为非结构化内容设计或优化.成本过高.并非