-------------------------------------------------------------------------------------------------------------------------------------

分布式存储

目录

分布式系统理论基础

  什么是分布式系统,这个概念我们很难用一个精准的描述方式来概括出,所有的意义来。但大体上来讲,我们可以从两个层面来描述一个分布式系统的特性。第一,分布式系统一定是,他有很多种组件,而且组件是分布在某一网络内的多个不同计算机上。第二组件之间就是各组件之间是仅仅通过消息传递。来通信并协调行动的。分布式系统对客户端来讲要注意。站在系统运维的角度或者站在系统构建的角度分布式系统的确是多个计算机。但对于最终的终端请求者来讲,他们所看到的却是只是一个计算机。因此分布式系统看起来就像是一个超级计算机。分布式系统的特性,首先要注意几个核心点,第一,一定是有多个节点组成的系统。第二,每一个节点通常指的就是一个计算机,但是这些节点彼此之间不是孤立的。而是互相连通的。第三,在这些互相连通的节点上,我们部署了很多的组件,而且组件之间通过消息传递要完成协同的效果。所以只要有了这样一种法则,或者要遵循的法则,我们的系统就是一个分布式的。为什么要用到分布式系统 

  1、系统的各组件分布于网络上多个计算机
  2、各组件彼此之间仅仅通过消息传递来通信并协调行动

  分布式系统存在的意义:
        那一般而言,我们要使用分布式系统的主要原因在于,第一,我们系统扩展可以有两种模型。所谓向上和向外对不对,而经验表明,向上扩展的这种模型,他的性价比越来越低。 第二,单机上无论怎么向上拓展,他总有那么一个时刻到达其扩展的临界点。而后再添加组件,它的性能不升反降,所以单机处理能力一定是存在一种瓶颈的。就是刚才我们所描述的临界点。第三,出性能和可用性方面的考虑,单机无论怎么扩展只要机器一出现故障,所有的扩展都无法使用,所以分布式系统存在的意义。所以在任何场景当中,目前来讲各种在性能方面的提升模型,都是采用所谓分布式系统的方案来这来解决。

        1、向上扩展的性价比越来越低;
        2、单机扩展存在性能上升临界点:
        3、出于稳定性及可用性考虑,单机会存在多方面的问题



  CPU,内存,IO
        要想理解分布式系统所能够带给我们的意义,分布式系统的目的,主要是扩展了单机处理能力的弱势,或者说瓶颈。我们计算机主要包含五大部件,根据所谓的冯诺依曼架构所构成的系统,无非就是有CPU,内存,以及有IO共同组成。那而且我们也知道摩尔定律系统集成电路芯片上所集成的电路的数目,每隔18个月就翻一番,那现在好像遇到问题了。这个但是另外一个又遵循了摩尔定律了。另外一个角度,全球的数据量每18个月翻一倍。所以大数据时代来临了就这么一道理,那这个定律其实告诉我们随着时间推移,单位成本所需要支出的购买能力在计算机能力的提升上。其实你现在花多大价钱买个计算机,18个月之后,同样价钱一年半以后就可以买到性能升一倍的产品。一般而言是这样子的。所以说在单机方面向上扩展所带来的性能提升,他的性价比是非常低的。计算机组成的5个基本要素当中,我们要想实现对其性能的提升,无非就是这几个方面进行提升。这5个部件也构成了我们计算的基本系统。那这5个部分又是怎么进行协调工作的?这个其实要完成对他的说明还是比较麻烦的。但是大体上来讲,我们可以知道,对于单个CPU来讲,我们系统运行时它一般而言在某一时刻运行的进程只能有一个,而如果说有了多颗CPU,也或者多个物理核心却可以同时运行多个线程,但是多线程开发起来是非常麻烦的。而且多线程的线程彼此之间为了完成通信。为了完成多个线程之间的同步带给我们的也是相当困难的。而且一旦我们实现了并行编程,将来在程序出现问题时进行调试也是非常麻烦。所以多线程能带我们确实有性能方面的提升。但额外的也带来了一些很多的副作用,由此要想让并行系统彼此之间运行起来。更容易的话,那么今有一种模型叫互不通信的多线程模型。而这种模型下,通常而言其性能是最好的,而且在线程内部及管理机制,所需要消耗管理开销也是最小的。各线程之间彼此间无需协调,这是最简单的模型。第二个,在我们多CPU核心模型下,程序在单机上为了能够实现系统性能更强大,我们必须要实现多线程编程。而多线程编程可能就面临了就类似于多节点内部通信一样的难题。要描述一下这些难题所存在的一些问题,以及多线程编程时所用到的一些模型,互不通信的线程模型这是第一个,第二个基于共享容器协同工作的模型,什么基于容器协同共享的模型呢,其实也很简单,我们两个节点或两个线程彼此之间各自开始运行任务时,比如其中一个线程走到某一步,可能需要等待另外一个线程完成或者请求获得另外一个线程的结果以后,他才能进而执行后面的步骤。你说这个时候怎么办呢?向另外一个线程通信时,如果说直接进行通信并启动那条线程是非常麻烦的。所以在这个场景中,我们可以基于队列的模型,我们在内部构建一个消息队列。第一个线程的请求先发往这个队列中,而另外一个线程可以从队列中取得线程的请求,并决定接下来该怎么去执行这个请求,执行完成之后,将结果再返回这个队列,从而这个线程获取到结果之后就可以继续向后运行了。而他们彼此之间很可能在有些场景中是相互协调的。第一个线程需要对第二线程发送数据,发送消息,反过来也是一样的道理。所以在这种情况之下,我通过一个队列模型这个队列我们可以称为叫共享容器。是共享的,大家都可以往里边放数据,或者拿出的一个容器,这样可以理解吧。ok这是一种模型。那不管怎么讲,这种模型就实现了让多个线程同时工作时彼此之间进行协调的一种模型。好,在这种模型下,我们需要所有保护控制以保障访问的正确性的,所以对于使用共享容器的这种通信,基本上来讲有所谓线程安全和线程不安全的模型之分。而对于线程不安全的容器和对象,一般来讲我们需要基于加锁或者别的,来实现并发访问控制。简单来讲,我们在一个线程内部放一个消息,这个消息,如果别的线程也可以修改的话,就有可能会导致第三个协调方在从种取数据时会受到影响。所以如果说这个线程这个容器是线程安全的,就意味着其他线程修改并不影响第三方或者其他线程的获取,彼此之间毫无瓜葛。这就叫线程安全的模型。而线程不安全模型指的是其中任何线程可以修改这个数据的同时,那么其他线程也去修改这个数据,这会导致一个数据被损坏了。就像我们使用的共享存储一样,这种就叫线程不安全模型。对于线程不安全模型来讲,任何一个线程在访问这个数据时必须要利用加锁的机制或者实现 Copy On Write,谁用谁复制一个,你千万别直接修改源数据,这叫Copy On Write机制,Copy On Write这个术语非常有用而且也非常流行。但是使用加锁方式时,如果数据在多线程中的读写比例很高,那么这个时候一般会采用互斥锁。而对于线程安全容器来讲,那你可以直接使用。叫基于共享容器的多线程模型。而这种线程之间是需要通信的了。而需要通信就意味着第一个线程运行过程中可能需要取得一个数据。而这个数据必须要是第二个线程或者其他线程生成的结构才能取得。那这也意味着第一个线程必须要等待其他线程送完数据之后,我们才能向后继续的。是不是接下来这个线程就处于等待状态了,处于等待那就意味着他不能完全并行运行了。所以我们说过互不通信的模型中,大家各自运行各司其职,互不相干运行速度就非常快。而对于这种需要通信的,基于共享容器就已经是一种需要协调的模式了。而这一协调势必会带来性能的下降。第三种模型可以通过事件来进行协调。好的,除了并发访问控制方面,线程之间也存在协调的需求。比方说有a,b两个线程,b 线程需要等到某个状态或某个事件发生之后,才能继续自己的工作或者是这个状态改变。这个改变跟其中第一个线程相关联的话。这时候才能完成继续向后工作的。那像这种就基于事件协调。如果说第二种基于共享容器模式仅仅是为了完成数据共享,彼此之间互不相瓜葛。只要能够保证取得自己所取得的数据,而且数据彼此之间还是无需等待的话,那这种性能的影响还是比较轻量级的。而对第三种事件协调模型来讲,这就势必会产生等待的过程。这通常意味着a线程可能需要触发某一事件。而且这个事件是有必然产生的。所以b必须要完成对a事件的触发,这也就意味着b线程的运行是依赖于a线程的某个特殊时刻到来之后才能运行,所以在这个事件被a线程触发之前,b线程就是必须要处于等待状态。所以这就是通过事件协调实现多线程模型的。而这种模型则有可能会出现死锁。有时候很有可能是互相等待。a等待b触发,b等待a触发,b要a触发以后才能真正触发事件,那a是一个道理。那结果就是死锁。我们此前在讲到mysql中的锁的时候,给大家讲到了死锁的概念,其实指的就是同一个意义。在这mysql此前的查询产生死锁的这种场景,其实就是一种基于通过事件协调的多线线程模型。当然我们也可以使用多进程模型。多进程跟多线程彼此之间他们的内部协调方式是一样的,相对于多线程的方式来讲资源控制更容易实现,因为进程和线程有什么区别,线程是可以共享进程的多种资源的。比方说打开的文件句柄,内部的很多信号等等都是。而每一个进程彼此间是互相隔离的。而仅仅通过进程间通信模式进行通信,所以多进程模型的资源控制方面比多线程模式,更为简单。而我们之所以提到这些模型,主要是因为在单机上实现扩展的多cpu这种机制是有关系的。多线程程序开发其实不容易。而摩尔定律发展到今天也很难实现。通过简单提升单个CPU频率来提升自己性能了,所以现在都是通过增加核心来提供其性能。由此就遇到问题了。所以现在的各个生产厂商就是芯片制造商都是通过所谓的。不断的增加内部的核心数,提高其处理能力。但是这种核心数的增加,要想能够共同工作起来或者同时工作起来,就只能使用多线程模型,

  多CPU,多线程编程 
        互不通信的线程模型
        基于共享容器协同工作的模型
        通过事件协调的多线程模型
              А, в
                    A:触发事件
                          В:等待事件
        多进程模型

  网络IO:

        多进程,每个进程响应一个请求
        多线程,多进程,每进程生成多个线程,每线程响应一个用户请求
              事实上,线程响应跟进程响应无非就是其内部可共享资源或者不可共享资源那种概念。但是每一个用户请求他都需要一个网络IO,一个单独的网络套接字
        
        多线程,每线程直接响应多个请求

        
        基于socket实现网络通信开发,其实现方式
              实际上这个网络IO机制大体上可以分为三类模式。当我使用Socket的套接字进行网络通信模式开发时。他的实现方式主要有三类。基于socket实现网络通信开发,其实现方式主要有三种。第一种叫 BIO模式。这种就叫做阻塞IO模式,说白了就是一个socket套接字是需要用一个线程或进程来处理。那么于是连接的建立,读数据,写数据本身都有可能产生阻塞的,这种机制就是所谓,前面所提到的每进程或每线程响应一个请求的模式。这种模式就统称为叫BIO,在这个模型下它有个最大的问题就在于每一个连接,每一个请求都需要占用一个套接字。我们此前讲到过套接字有两种模式有所谓监听套接字和连接套接字。这是第一种模型,叫做BIO模型,所以“多进程,每个进程响应一个请求”这一种,和“多线程,多进程,每进程生成多个线程,每线程响 应一个用户请求”,这两种其实他们都算是BIO模型的实现。      下面一个模型叫做NIO,这种指的是基于事件驱动(epoll)思想,采用Reactor模式,相对于BIO来讲,NIO的最大好处在于每个socket套接字分为一个线程。而这一个线程可以处理多个套接字相关的工作。      实际上还有第三重叫AIO模式,叫异步IO模式,所谓AIO和NIO的区别主要在于AIO他也是基于事件驱动的思想,但采用的是叫Proactor模式,这两种模式跟网络IO中的编程逻辑有莫大关系。我们大体知道有三种模式就ok,自上而下,一种模式比一种模式更先进,更高级。当然开发起来所依赖的技术程度也更麻烦。

              BIO:Blocking IO  
                    一个进程或一个线程处理一个请求

              NIO: Nonblocking IO
                    基于事件驱动(epoll)思想,采用Reactor模式(就是反射这种机制,说白了就是内部事件的通信模式。)

              AIO:
                    基于事件驱动思想,采用Proactor模式


  如何把应用从单机扩展至多机?
        对于现代操作系统来讲,他所做的任何生产能力的提供,都是通过所谓的进程或线程来实现的,所以真正运用到计算机中的各种资源,包括你的网络IO,磁盘IO,甚至的键盘,和鼠标输入输出的IO,以及cpu和内存都是线程或进程,所以我们如何把应用从单机扩展至多机,就涉及到,不光有网络IO,不光有CPU,还有各种其他的组件。那我们如何进行扩展?比方说从单机扩展的多机,你的输入设备是怎么变化的?如果是单机的话非常容易理解,而输入一旦是多机的话就意味着通过网络接口让彼此间能互相发信息的。所以输入模型变得非常麻烦。那第二输出设备呢。我们的产生结果都可以通过网络发送给同一个系统内的其他任意节点,输出设备也发生了变化。嗯,也可以简单的理解为这里的输入仅指键盘和鼠标,而输出设备仅指监视器。这种模式下一旦有多个主机这种输入输出,它事实上他的输入输出流,就有多个位置了,也可以这么去理解。对CPU而言,我们控制器如何发生变化?在单机中所谓控制器就是cpu有两部分组成运算器和控制器,有一个cpu内部的控制器就能够完成控制了,那要是分出去呢,在单机上控制器就是CPU中的控制器。而如果我们要实现分布式应用。基于多机来协调,控制器在分布式系统当中,它的实现模式有几种。这些负载均衡设备本身,事实上是有多种工作模型的,第一种模型我们称作透明代理模型,用户请求时,请求被直接发往一个控制器,用户就以为这个控制器是响应者,是服务的直接提供者。但他事实上不是的,所以我们称之为它叫透明代理,lvs的四层转发,他不是一种代理机制,但有时候我们也把它称之为叫透明代理,所以我们透明代理有两种模式,像lvs的四层的nat模式和haproxy,或者nginx的反代模式,这种我们都可以理解做透明代理。所以请求的地方或者客户端不关心有多少台服务器,也不需要直接知道这些提供服务的机器地址。我们只需要发请求给控制器就可以了。所以这种方式尽可能简化了客户端请求时的问题,但这种模式有两个不足,第一会增加网络开销(这种开销是,第一方面是流量,第二方面是延迟。),主要是所有的请求压力都压在了控制器的前端,是不是这样子。 第二种模式,叫做旁路模式,从服务器上直接响应的结果是直接响应客户端,不会再通过中间代理,只有请求数据包,在请求过程中实现了一次转发,所以这种模式中可以有流量的提升,这种模式我们可以理解为第二种模式叫做旁路模式。这也是一种透明代理的实现,但旁路模式中还有其他几种实现,比方说第三种机制我们基于名称服务来实现转发,各位知道DNS也可以实现转发。因此这种模式我们称之为名称服务模式,基于名称服务的实现,客户端将请求发送时,发送给某个提供名称服务的主机,有名称服务通过名称解析之后,给定向到目标节点上的这种模式,而后接下来的整个通信过程,其实都是由客户端跟主机之间直接进行的,跟我们这个名称服务就没有关系了。第四种模型叫做规则服务模型。规则服务指的是什么?这个时候中间的节点不是名称服务器。而是一个规则服务器。这个规则服务器上面定义了很多规则。他里面定义了客户端发请求时,你到底应该给予这两个节点中的每一个主机通信。取决于规则服务器,规则响应给你的结果。举个简单的例子,比如说我们mysql数据库来讲。本来一个数据库中有4张表。但是mysql服务器,它如果面临特别大的写请求的话。也有可能会扛不住了。我们可以做分库。做分库以后我们可以把4张表给他分到两个节点上,每一个节点只保留两张表,当一个用户去请求查询数据时他怎么知道哪张表位于哪个节点上?而这个规则服务器明确知道,你请求的那张表,他在 哪一个服务器上,所以客户端发请求时,先请求规则服务器说我要查询某张表,规则服务器你告诉我,我要找哪个节点或者说哪些节点。所以说规则服务器会告诉他怎么去找的。在这个过程中客户端,很有可能需要找这两个节点当中的所有节点,因为必要时我们也可能把一张表他一共有1,000万行拆成两半,其中500万行在第一个节点上。另500万行在第二个节点上,当我去查询年龄大于30的用户是,会发现两个节点都有数据。那因此这个规则服务器会告诉我们到底找一个节点还是找两个节点,以及这两个节点分别是谁的,这就是规则服务器,而这种规则服务器就是实现分库等模式中经常用到一个定义了查询规则或者请求机制的一个节点。有没有发现规则服务器的模式跟名称服务器的模式很相像,的确很相像。从优缺点方面来讲,规则服务器和名称服务器也很相似。只有某一次查询第1次请求时才需要找规则,判定好规则结果之后,就无需跟规则服务器再联系了。所谓Master机制指的是在整个分布式系统中有一个节点是主节点,而其他节点是从节点。向mysql的主从分布模型。而这种系统他不是说通过透明代理或者旁路代理的模式来实现。只不过有一个节点负责全面工作,而其他节点负责分摊一部分工作。这种称作Master/slave机制,这是我们控制器变化中的master节点。,控制器就变成了master,而slave则是运算器的表现形式之一。但是master自己也会运算。所以这是一种比较独特的模型。

        输入设备的变化? 
        输出设备的变化?

        控制器的变化?
                    控制器的变化。就实现了有多个节点来实现控制。而控制时可以通过代理的模式,旁路的模式,名称服务的模式,或者规则服务的模式。这些模式中透明代理对控制器的压力是最大的。而规则服务和名称服务对控制的压力比较小一点。所以到底采用哪一种模式来实现我们分布式系统场景中就有各种意义了。
              实现的模式:
                    透明代理
                          lvs的nat模式
                          haproxy和nginx的反代模式 
                    旁路模式(旁路代理)
                    名称服务
                    规则服务
                    Master/slave机制

        运算器的变化:
                我们实现分布式系统的主要目的就是把运算器和控制器拆离了。一个节点是控制器,真正提供服务的是什么?运算器。这些后端节点,你可以理解为。把单机系统上不宜扩展的那个运算器分散到多个节点上来单独实现的。扮演了单机模式中的运算器。对于运算器本身来讲,我们能理解控制器就容易理解同一种模型中运算器被分离出来的应用。

        存储器的变化:
              在单机系统中,存储器无非有内存和外存或者主存和辅存。内存容易理解,外存或者辅存指就是硬盘了。在单机系统中,如果数据都在内存上,一旦重启了或者系统崩溃了,那在内存中的数据就丢失了。因此我们使用外存当做持久存储设备,当然我们的外存也不是万无一失的,因为外存也有可能会发生损坏的,而在分布式系统中则需要把承担存储功能的多个节点组织在一块儿使其看起来像一个存储器一样来工作。这就是所谓存储器的变化形式。通过存储器模型应用,我们正是通过所谓控制器的不断变化,将分布式系统中的存储能力,放在了多个节点上来完成存储。而且比如web服务器集群是提供处理能力,所以也可以分布式处理能力,或者mysql的存储和处理能力。



  分布式系统实现的难点:
              比如说在同一个主机内部,你的键盘鼠标硬盘彼此之间怎么协调?比如我们在键盘上一直按字母a,它出现了6个a。那为什么不是20个a呢?其内部必须有所谓时钟同步信号来控制。多个部件他们走路的速度快慢不一样,那我们内部必须有控制器,能够协调这些部件彼此之间步调,所以非常困难。因此对于分布式系统而他的第1个难点就在于缺乏全局时钟,他没有全局的时钟信号无法做到步调一致,这是第一点。第二点,面对故障时的独立性。说白了就是当出现故障时。因为我们分布式系统是由多个节点组成的。当出现故障时,我们怎么去判定故障出现在什么地方?而这个故障本身是不是独立的,说白了就是一个节点出现问题,会不会影响全局的整个系统 的工作。一般而言,在分布式系统中整个系统一部分有问题,而不是全部有问题,这是很常见的。我怎么能实现让一个节点出故障,而不至于影响全局。所以问题来了,挖掘机到底哪家强?同样的道理。第三,如何处理单点故障。故障固然有独立性。而控制器通常本身成为了整个无法绕过去的一个独立性的节点。所以它一旦出现故障,很有可能导致整个系统都会出现故障。所以如何处理单点故障?就是我们必须要考虑的问题。因此我们必须要考虑给单点可能故障所在做冗余。对于降低单点故障的影响范围我们也可以实现以下方案。比如说数据库,我们可以实现双写模式来降低单点故障,也可以。自己不用做高可用,但是我们前端通过双写模式来实现,其中任何一节点宕机了,不至于全局宕机,这也算是一种解决机制。第四点,事务处理。是非常麻烦的。比如mysql做事务处理,一个系统能否能够支持事务,要有一个前提,看他是否够满足ACID测试,原子性, 一致性, 隔离性, 持久性,这4个单词的简写。而在单机上实现事物是比较容易的。如果要跨节点实现事务,这事儿其实是麻烦的多的多。而在分布式系统中要想实现事务的功能有多种相关的方案。比如说第1个方案叫2PC机制,我们称作叫两段式提交。第2个方案叫BASE机制。第3个方案叫CAP机制.第4个方案叫Paxos机制。这是分布式系统实现起来所面临的难点,大部分有那么多。
        
        缺乏全局时钟
        面对故障时的独立性
        如何处理单点故障
        事务处理
              ACID

              2PC,BASE,CAP,Paxos

大型网站站点的架构演进方式

        假设刚开始使用的是LAMP或者LNMP。最简单的时候就是这么一种架构。而且还有可能是构建在单机上。所以我们的网站刚开始的时候有可能只有一台主机。一个主机内部有一个所谓的application(应用) server(服务)(应用服务)就是我们tomcat可以这样理解,这个application server通过JDBC或者通过别的模式来联系我们mysql数据库。mysql也在同一个节点上。必要时我们如果不期望这个tomcat直接面向用户请求,我们还可以在前端构建一个nginx。所以用户请求先到达nginx或者apache然后被代理至本地的tomcat,tomcat需要实现数据存取时到达mysql。而在tomcat内部我们跑的是应用程序,我们想一下tomcat自己需要跟mysql打交道吗?不需要。一定是tomcat内部所运行的程序需要存取数据时,才需要跟mysql打交道。所以在这个application server上一定跑的有多个应用。这些应用才会跟mysql打交道。一定要记住这一点,很多人到头来都没有搞明白,以为我们部署PHP或者部署tomcat就必须用提供mysql,没有mysql他们跑不起来,不是这么回事儿的,一定要记得跟mysql打交道的是一些应用程序。假如说这是一个交易网站,就是一个电商站点。想象一下,一般而言一个电商站点大体上应该具备哪几个基本功能。比如你要让用户注册,那我们还要实现用户管理,比如某些用户经常做虚假购买下单以后不付钱,还有信息维护等等,这主要是跟用户相关的。那跟商品相关的,商品上架,下架,商品展示。那商品上架,下架,我们得有商品管理。还有交易,因为我们主要目的就是为了交易,那比如创建交易,交易管理等等等等,无非就是这样的功能。而这些功能都是一个一个具体应用,这里要注意。所以application server这地方可能有几个应用程序,第1个应用程序它实现用户注册和用户管理的功能,第2个应用程序实现商品展示和商品管理的。还有一个应用是实现交易创建和交易管理的等等相关的功能。所以我们这个网站主要组成有三个部分用户,商品,交易。我们就从这三个角度来进行解释。这是我们刚开始使用单机来构建这个网站的。各位想象一下,当我们单机负载出现告警的时候,用户请求来了我们服务器扛不住了,你说这时候怎么办?你在这整个应用中,我们经过分析发现。application server中应用程序,他这些应用程序要想执行起来,非常占用cpu,而mysql数据库自己无论是写入还是查询,尤其是查询,比如像排序呀。出现表的连接等等也是非常消耗CPU时间的,所以我们应用程序从资源占用上一般有两类。一个叫CPU密集型应用,一个叫IO密集型应用。注意啊,这这两个概念一定要做到张口就来这是一个基本术语。而application server中应用程序和mysql他们其实都是CPU密集型的应用。所以说考虑到这一点,我们应该将同样更多征用同一种资源的服务,给他拆分开。mysql对io占用比较大,但是application server对io占用不是特别大,他无非就是简单获取一个小文件而已,的确比mysql小的多了。那因此我们先做第2步把数据库和application server给他拆分开。所以我们再加一个节点把数据库放在这个节点上,实现所谓的处理和存储拆分各自实现独立。各位还需要注意的是,我们的应用大体上分三个交易,用户,商品,而这个数据库为了能够存储这三类数据,一定有用户表,交易表,商品表都应该有,这里只是举一个例子,事实上真正的时候可能会有很多很多。而这种架构中就已经实现了应用和存储就是mysql数据库分离。那就要说应用服务器负载可以接受中,数据库处理已然不会有太大问题的,不管怎么讲,我们已经把服务处理,把CPU密集型应用分散到两个节点上去了。那后来发现随着用户的并发访问量越来越大,之前说过,不是所有的应用都需要查询数据库。有一个28法则,你有10个程序中,大概只有2个程序需要跟mysql打交道,那剩下8个只需要在我们应用程序服务器上自己运行就行了。所以下一个面临的问题是访问量在变大时,mysql访问量只是略微缓慢上升,前端的那个应用程序服务器负载就急剧上升,所以下一布一定是应用程序服务器负债报警一般来讲是这样子的,当这个应用程序服务器报警以后该怎么办?于是就发展到了下一个阶段,对应用程序服务器要做负载均衡。所以一个应用程序服务器就变成了两个。那我们有两个应用程序服务器,每一个服务器内部有他的应用程序服务器,而应用程序服务器内部就所谓商品交易和用户,有两个应用程序服务器,这时候前端就需要一个所谓的负载均衡器,你必须要引入负载均衡设备了在这种场景中,那nginx也被提取出来,当做一个独立的节点作为前端的负载均衡器,中间是两个应用程序服务器,后端仍然是mysql数据库,现在他的工作模式是这样子的,开始引用负载均衡设备。用户请求先到达负载均衡设备,然后被负载均衡,按需分发到不同的应用程序服务器上来。在这里我们要考虑一点,一旦对应用程序服务器做了负载均衡,那么用户会话如何保持?所以此时不得不考虑负载均衡器的调度方式,以及应用程序服务器的构建方案了,而mysql依然是商品表,交易表,用户表等等都在这工作。那在这个场景中要面临的第1个最严重的,而且最应该认真考虑的就是会话的保持问题。那解决方案,第1个叫session sticky(会话粘滞)来实现,第1个方案有一个问题,一旦其中一个应用程序服务器节点挂了,会话一定会丢失,所以第2个方案叫session replication(会话复制)这个说白了就是构建这两个节点成为session集群,这叫做session复制,第3个方案叫做session server(session集中存储),第3个方案叫做cookie based(基于cookie的session分发)这种方案是影响最小的,但他也是session sticky的一种,那这种方法会使得不会导致现有互联网架构中位于同一个nat服务器背后的所有用户请求都被分发到同一个服务器上去,因此我们通常把cookie based归结之session sticky中的一种,第2种叫做ip based。 需要明白,在不同的场景中,你所面临的需求是什么就至关重要。这就是所谓引入负载均衡设备以后,session会话保持时所面临的问题。我们网站已经扩展到需要引入负载均衡设备这个高度了。下一步如果再来更多的用户访问请求,会发生什么情况?前端不会轻易面临压力的,nginx同时并发面对2万个用户连接都很轻松。而lvs可以应付400万个用户,他们轻易不会遇到问题。需要明白,如果就是遵循28法则,现在来了1万个并发,意味着什么?前端应用程序服务器,如果没有问题。那么应用程序服务器就面临1万个请求,那每一个节点就是5000个,那1万个中,那28法则有2000个人到哪去了?到数据库节点上来了。数据库节点2000个请求什么概念?一个数据库服务器通常能应付100个到几百个请求就不错了,当使用的查询模型。那么很显然,这么高的并发量,下一个面临瓶颈的一般就是数据库节点,那我们要对数据库节点做扩展应该怎么做?当数据这一层次面临访问压力时就要主从模式。可以是一主多从,需要明白,一旦对mysql做了主从,那么mysql自己这个系统就已经成为一个分布式系统。前端nginx负载均衡器节点,加两个应用程序服务器节点,这是处理能力分布式。而mysql是实现了对存储能力做分布式,当然主从不是对存储能力做分布式,而对mysql的查询响应做分布式的,所以msyql主从仅能够实现对处理做分布式,他事实上不是存储的分布式,在主从模型下,对每一个节点都要存储一模一样的数据,对存储本身没有做分布,但是有了主从节点以后从服务器也可以处理查询请求了,所以对处理本身做了分布。这个结构变化和带来两个问题。第一,数据复制的问题,主从同步是有可能步调不一致的,从节点会通常落后于主节点,尤其是对非常繁忙的主节点来讲,因为主节点无论多少个请求量,他的读写是可以并行的,而对于从节点来讲由于主节点,它所有的读写都可以并行,那么他记录到二进制日志中,这个日志事件一定只能串行存入的,所以从节点也只能串行复制,所以对于繁忙的主节点来讲,从节点落后于主节点是在所难免的。 第二,是应用程序选择数据源的问题。应用程序到哪儿去都写数据,表示他的数据源的选择问,解决方案无非就是读写分离,这东西可能需要加以控制器来实现,否则就不得不让程序自身来实现多谢分离了。那么对于我们的电商站点了,要实现读写分离,这个主库既能读也能写,而对于从库来讲,他是只能读的,即便我们有了主从节点,但是mysql对于全局搜索,其实能力不强的。而对于电商站点用户经常会在搜索引擎中搜索产品的,而要想实现全局搜索,你必须要对整个内容做索引,而mysql只对MyISAM搜索引擎支持做全文索引。但是对于商品交易来讲,我们应该是使用事务性存储引擎的。既要考虑商品自身交易过程的事务问题,还得考虑商品本身的数据问题。所以一般来讲,这种在线处理系统都要做那些事物性系统而mysql对innodb不支持全文索引。事实上,当我们站点起来后,用户搜索量还是非常大的。比如你去网站上去买东西的时候,大多情况下是搜索还是一个一个找,事实上确前面还有人统计过,作为一个电商站点中用户的交易达成其中的70%都是搜索产品进行的。那由此。像这种越来越多的搜索需求,使得我们数据库根本就没办法应付的需要。我们要想能够满足这种需求,该怎么办?自己构建搜索引擎。为了减轻mysql数据库对搜索上,面临的压力。要因为每一次搜索其实就是数据库的全面查询。尤其是全文搜索和查询幅度覆盖范围就更广。所以在这种场景中。从我们mysql数据库中,从读库上把所有数据都读出来,读出来之后换一种方式存储,存储在我们的文件中或者什么地方,而且在换一种方式存储之后给他构建一个,全文缩影,然后用户再查询是通过我们应用程序,这里加一个搜索引擎。用户通过搜索引擎这个程序来做搜索时,都被转发给搜索引擎上来了,都被这个搜索库来负责响应。搜索引擎访问数据通常都是在本地访问的。一定要记得。所有数据的写是在my mysql主库上写,主库写完以后同步到从库,然后搜索引擎再从,从库上把数据读过来存储在本地,然后在对本地这个数据做处理以后构建成索引,然后用户才能搜索到了,搜索引擎同步构建索引的速度决定了,搜索引擎是否能够立即能够搜索到新上架的商品的。进一步减轻了对大量用户访问时对数据库的压力。这个层次就要引入搜索引擎,实现全文搜索。这是固然对后端动态内容生成内容,极大的减轻了压力,但是当用户的并发请求量很大时,那些静态内容的访问压力也是很大的,那如果这些静态内容都直接由应用程序服务器自己来处理的话,事实上也是没有必要的,在这个时候,为了扩展整个系统的响应性能还应该引用动静分离,而后对数据做缓存,所以下一步我们引入缓存机制,当然引用缓存的同时,也可以做动静分离,也可以不做。这一步引入缓存,由此在前端开始构建缓存节点。我们可以在缓存节点上明确定义,只对那些指定的文件格式做缓存就可以了比如gpg,gpeg,gif或者html,css这样的文件做缓存,所以那些动态内容还是直接由缓存服务器交由后端主机直接处理,我们引入缓存机制时要考虑两点,第一其实我们有页面缓存,这个时候可以使用varnish或者squid等实现,第二,还有数据,可以使用memcached来实现,这里就不用具体的产品了,使用key-value数据库就ok,memcached具代表性的工具。对页面缓存来讲需要考虑,ersi技术,并借助于缓存静态内容尽可能多的提升系统缓存的命中率,进而降低或者缓轻后端应用程序服务器的压力。不管是哪一种缓存机制,一旦有多个了,这就叫分布式缓存,缓存一分布式就意味着数据有可能在其中一个节点上只存在一部分。那就必须尽可能保证前端用户请求过来时他能够其极大的提升其命中率的,我说过很多次,要想提升其命中率,我们这里应该使用什么方案?尤其是在这个节点出现故障时,不至于影响全局,使用一致性哈希算法。在做负载均衡器时,其实haproxy其实比nginx更专业一点。前端一旦使用haproxy,也不该让tomcat直接面向用户的请求,我们应该在每一个应用程序服务器节点上安装nginx。好了,这是引入缓存机制。那继续向下走,如果此后我们应用程序服务器在遇见瓶颈,那么我们加应用程序服务器就OK了,缓存再遇到瓶颈加缓存就行。缓存再遇到瓶颈,加入cdn技术。我们现在先不考虑那么远。假如说下一个时刻面临的问题的一定一般来讲不是缓存,也不是应用程序服务器,而是mysql的写库,随着我们访问量越来越大,那交易越来越多,那需要存数据的机会也就越来越多了,而能够写数据的只有主库,而主库还要复制数据给从库,所以主库的压力越来越大。接下来一定要记清楚,对于关系型数据库来讲,一旦面临写请求压力下面要做的一步就是数据库拆分,拆分大体上分为两种类型,第1种叫,垂直拆分,把一个库中的多张表,分散到多个物理节点上去,把数据库中不同的业务的数据拆分到不同的数据库中,比方说,我们整个数据库里面他的表无非就分为三类,大体上是用户类的,商品类的,交易类的,假如他们彼此之间没有太大的关联性的话,用户注册和用户登录,通常跟我们后续的商品交易关系并不是特别大。 所以这个时候我们可以把交易,商品,用户他的库分开。所以用户的请求先到达前端的负载均衡器,负载均衡器到达以后,接下来后面有两个甚至有多个分布式缓存节点,我们可以理解为这是页面缓存,但页面本身也是数据,我们只是为了能够区别对应的后面的内容,我们有意把它称作页面缓存,那在向后就是各应用程序服务器,应用程序服务器本身有比如Tomcat,这里面有很多具体应用,比如用户正注应用,商品应用,或者交易应用都在这里,前端还有nginx,我们应用程序服务器可能也是多台。应用程序服务器在实现数据检索时,不会先去mysql去检索,而是先去memcached,有多个,为分布式缓存。接下来就要跟数据库服务器打交道了,对mysql而言,可能我们主库就被拆分了,拆分成用户,商品,交易三个库。所以每一个应用都有可能跟这三个库打交道,如果是用户登录就找用户库,如果是商品交易就找交易库,如果是商品本身的操作的话就找商品库。我们还有一个从库,一个从库,它可以有从三个节点上来同时同步数据,这是允许的,通常就需要依赖于特殊技术。如果不打算这么做,也可以各自进行做从库,但是不管怎么讲,我们在数据库前端做读写分离器了,我们这里就是用一大堆的从库。所有的从库都是从主库上同步数据的。那么所以在这种场景中,用户的前端的服务器的读请求是有可能到从库读商品信息的。好的,这是所谓的垂直拆分。把一个库,拆分成位于多个物理节点上,所以他到交易的压力在一个节点上。用户的压力在一个节点上。商品的压力在商品节点上。而商品本身因为压力比较大,所以做了从库,当然旁边还有搜索引擎,通常只搜索商品什么的。并不是我们把所有功能都要提供给用户,这是数据库的第一步叫垂直拆分。垂直拆分以后依然会面临问题。我们仍然考虑后端数据的问题。商品频繁的读,写操作,频繁的查询操作,有可能带来的结果是,我们尽管已经按照商品,按照用,户按照交易来拆分了,依然无法满足需要,因为他的写压力对商品库来说依然非常的大。我们一旦需要完成数据修改,读写,都在主库上进行,一个节点就算有主从,面对写操作主库依然扛不住压力。那因此我们就要对主库再次做拆分。但这种拆分业务已经拆不开了,我们要做水平拆分,水平拆分就是,把一个单独的表中的数据拆分到多个不同的数据库服务器上,所以垂直拆分是拆库中的表,而水平拆分则是拆表本身,按行来拆。水平拆分与读写分离的区别在于,读写分离是解决读压力过大的问题,而水平拆分是解决写压力过大问题。比如之前说的商品库,现在我们来说用户库。说我们用户量太大了,每天都有大量的用户登录,大量的用户创建,大量用户去修改用户信息,也有这种可能,那这样我们就会面临用户量过大,导致一个库他怎么写都忙不过来,读很少,只是用户登录时读一次,而平时用户登录进来频繁的更换头像呢,修改一个资料啊,注册一个新用户,等等对大型站点来讲,依然面临的压力会非常大,所以我们使用水平拆分。水平拆分的拆分方式是一个用户库假如你有1,000万个用户,前500万的一个库上,后500万的一个库上,所以对用户的读取就到用户这两个库上来实现,所以他需要到两个库上来实现,但是到两个库上来实现,他会面临什么问题?当一个用户修改用户信息时,他怎么知道在哪个库上?这里需要提供规则服务器,规则服务器保存了哪些用户存储在哪些服务器上,按规则进行定义,比如 ID号小于500万在第1个节点上,大于500万的就在第2个节点上,这就叫规则节点。所以规则这种机制通常就是用在数据库或者数据的拆分上。如果我完成对数据库的水平拆分之后就能够尝试着去实现,把写压力分散到不同的节点上去了吗,所以我们说这个规则定义至关重要,你如果按照id小于500万或大于500万来分的话,那很有可能是新注册用户比较活跃,老用户不活跃,有可能的。导致会你仍然一个服务器压力大,另一个服务器只分担了一部分压力,是不是有这种可能性,因此水平拆分对那些经常发生变化的数据,一定要做到一个目标,就是把热区数据分散出去。否则的话拆的结果是 ,一个数据库承担90%的压力,一个数据库承担了10%的压力。因此我们对用户库来拆的话,可以这么拆,只有奇数的在一个库上,所有偶数的在另外一个库上,可以按照范围小于等于多少,大于等于多少,也可以按照基偶来进行拆分。无论是哪种方法拆的,你一定要让前端应用找到其特定的数据,到底存储在哪个节点上,这就是所谓叫规则服务器来实现这个功能,这就是前面提到过的,我们要做数据库的水平拆分,必须要选择合适的拆分键,而后我们必须有一个所谓的拆分框架,能够帮我们去完成前端的读写请求路由,数据库被拆分了以后,用户请求的时候到底分配到哪个节点上,所以框架本身既要是路由,同时它又能完成规则服务器说白了他就是一个规则服务器。淘宝曾经开源的应用叫CobAR,就是一个著名的数据拆分框架。 T推特也把它的数据拆分框架给公布出来了叫 GIzzARd。有没有发现后端服务器这一端已经变得非常复杂的结构。所以这就是为什么一个大型站点上,有专门的dba,有专门的运维,有专门的系统工程师,还有监控工程师的原因。那个数据库拆分就完成了,已经足以应付我们数据库的读写问题了。后来发现,我们前端的应用服务器面临的压力越来越大,就算你做向外扩展,加一台加一台再加一台,一样面临过大压力。分布式系统也不可能做线性扩展,并不是你随意加服务器,它的处理能力就一定是线性扩展,因为这些节点之间可能需要怎么协调,所以你加的节点越多,它内部所需要的事务就越复杂。那怎么办,应用服务器面临压力不大,通过简单的增加应用服务器,依然无法应付这种场景。没错,应用也需要拆分。            我们回头来看,我们整个数据的前端。通过引入搜索引擎可以实现对数据库访问的读压力分摊出相当一部分,而又通过实现数据库的垂直拆分,将数据库按照业务拆分以后,减轻了数据库本身的读写压力,同时对于某一数据库再次做水平拆分,又能够实现对那些写压力比较大的节点实现其负载均衡的,这整个都是在不同角度实现整个系统执行分布式。我们对整个系统实现分离之后,做了分布式之后,如果有主从架构,我们要做读写分离,如果做了水平拆分,我们要做规则服务器等等。那我们继续对这个系统本身进行扩展,这是让我们整个系统中所需要存储的数据,正是由于要将其存储为关系型数据库中的数据,才使得整个数据库的访问压力也特别的大,因为关系模型在过去那段时间,确实为计算机系统或者叫信息系统中的数据存储带来了很大的帮助,但是他也引用了很多问题,因为它不适用于并发访问量很大的场景。如果满足关系模型,那就是必要带来像数据复杂查询当中为整个系统资源过大压力,如果我们数据实现存储时可以未必非得存储为关系型中的数据的话,我们可以将它们流式化存储Nosql中的数据。所以下一点,实在搭这些应用无法满足需求,而且我们觉得自己做水平拆分,有着这样那样的问题,并且在水平拆分上有些数据的确可以不用非得遵循所谓的事务机制,也可以引用Nosql机制。无论是关系型数据库还是非关系型数据库,他们通常存储的数据要么是关系型数据,要么是kv数据,绝大多数的非关系型数据库,就是Nosql,都是以kv模式来实现存储的,当然他们有可能展示为所谓列式数据库,文档数据库等等,那最终数据存储模型都是 KV的机制,一般来讲。所以这些数据也都是流式化数据。,你也可以理解为结构化或者是半结构化数据。而有些数据可能无法存储在关系型数据库或者是Nosql中的,比如说网站的图片数据,对于一个电商站点来讲,上传我们所发布的每一个商品,通常都需要大量的图片来展示其商品本身,所以如果你有1,000万个商品需要展示那很有可能图片就有数亿个,由此在这种场景的和我如何实现这种所谓,不能够被简单存储在关系型数据库或者Nosql中的数据。就意味着三个应用程序服务器节点都在存储一模一样的图片数据,假如他们能够直接处理静态内容的,很显然这是不可能的。在这种场景中,就意味着我们的数据存储除了所谓的关系型数据,非关系型数据,还要有文件数据,文件数据像这种我们通常叫做非结构化数据。对于非结构化数据,如果我们不得不用到共享存储机制的话,我们可以使用分布式文件系统,分布式文件系统无非就是将数据存储在多个可存储的数据存储节点上而已,比如我们可以通过n个主机,多台主机同时进行数据存储,数据是分散存储在这些节点上的,当我们找数据时,到哪个节点上去找啊?比如这张图片有可能存储在这三个节点上的某一个节点上,所以他需要一个控制区,需要一个控制节点,当然并非所有的分布式系统都有控制节点,有些分布式系统本来就是无元数据的,或者元数据是跟数据本身存储在一起了,他可以自行去全局检索叫无中心节点,无控制节点的模式。当然更容易管理的,他应该是有控制节点的机制,所以控制节点可以理解成就是存储文件时的元数据的,所以这里,这个分布式文件系统可以叫做数据节点,而控制器叫做元数据节点。所以刚去找数据时,先去找控制器那里面记录着数据,在哪个个节点上,在哪个存储中存储这,找到数据并返回给用户。分布式系统还有一个好处,有些分布式系统特别适用于存储海量小数据,有些分布式系统特别适合存储单个大文件,但是数量通常很小。所以在这种场景中,当我们去检索数据时,有可能那些适用于存储大文件的分布式文件系统,把一个大数据文件分成很多的块,而把这些块分散到这三个节点上,接着我们取一个文件,需要跟多个节点进行联系,才能够取得完整的数据,像这种就适用于大型的单个文件存储的,但是不管怎么讲,这也是分布式文件系统的一种表示。所以这里我们就引用了新的机制。NoSQL:非关系数据,需要注意的是NoSQL有很多种流派,用来分别实现不同的应用场景,有文档数据库类似于mysql这样的数据存储,当然他不只是事务或者事务能力很差。 还要像列式数据库等等,他们分别适用于不同的场景中。我们也可以引入分布式文件系统,DFS来存储一些非结构化的文件数据,DFS本身也有很多种流派,像淘宝的TFS,还有MogileFS等等,这些都适用于存储海量小文件。另外还有HDFS,还有Google的GFS这些都是适用于存储少量的大文件。所所以他们分别也有各自不同的技术流派,那我们再做选型时就极为重要了。像淘宝这种站点上存储的图片,它是海量小文件,所以淘宝内部他的图片都是使用tfs来存储。所以在存储层上,我们现在引用的机制越来越多,有关系型数据存储,有非关系型数据存储,非结构化数据存储。甚至还有分布式缓存系统以及搜索,整个站点本身它是做分层的,这是数据层。我们引入分布式存储以后,结合前端的cache(高速缓冲存储器)应用,那么很显然,整个站点的响应能力或者叫承载能力已经变得非常强大。但是即便如此当用户访问到达,我们所有的用户请求都到了负载均衡器,都直接有本地cache,并结合后端应用,来响应他。前端负载均衡器和cache的压力也非常大。我没有引入CDN,他们无非都是在全球各地用户的家门口的机房中构建了缓存节点,所以当用户请求数据时,首先不是到达我们的前端的负载均衡器这。所有的请求都会到CDN,他怎么可能会到CDN上去呢?为什么不到我们站点上来?我们知道用户对于我们这里的好像都是通过域名,很少有通过IP地址。因此我们在互联网上通过DNS服务器, DNS服务器在解析我们的域名时,这个域名返回的结果不是我们入口控制器的A记录,而是CDN各节点的别名,更重要的是这个DNS在解析时,他解析的结果不是CDN中的指定一个节点,而是通过客户端的来源返回给离他最近的一个节点的记录。而这整套系统组织起来,我们叫gslb叫全局服务负载均衡系统。但是gslb未必都是DNS服务,不过DNS服务是一种常见的解决方案而已,假如可以是一个负载均衡器。假如说用户发起请求,一定是送给我们某一个支持智能解析的DNS服务器。比如我们客户端是上海电信的,那于是他就返回离上海电信的用户最近的一个或者多个服务器节点。当用户发起请求解析域名时,假设得到了很有可能是shtel.cdn.net而这个别名节点的节点是离用户最近的那个CDN节点,但是他在各地的机房中可能不止一个他是有冗余的多个节点,因此得到了这个shtel.cdn.net别名,不可能直接访问他还需要二次解析,那于解析时这个shtel.cdn.net别名所对应的A记录可能有假设有三个,那于是在DNS这里就可以做负载均衡,返回的是这三个节点中的某一个,也有可能他的确只有一个节点,就是一个主控节点,这个节点是做了高可用的,当用户请求时返回的是这么一个节点,但这个节点并不真正提供CDN功能,他是做负载均衡,它是一个负载均衡器,负责均衡到内部的三个节点上。在我们用户请求时,就从这里缓存到结果,而cdn这里的缓存,如果没有结果。就找我们后端主站点节点上的服务器,这时候就到主站点上去请求,主站点上还有缓存的,如果缓存命中就返回给用户请求,如果缓存没有命中就要由应用服务器运行,我们基于这个技术再次实现了对我们主站访问压力极大的降低了,很多时候由于我们的CDN都是按流量收费的,所以他们通常都会尽可能高你的静态内容,或者甚至是动态内容当中引入ersi技术以后的,那些内容的命中率,所以很有可能80%的流量都是由CDN承载的。如果他没有命中数据就会到我们站点来,而我们站点这时候面临的只是20%的请求,而在20%的请求中,站点内部缓存有可能又命中80%,所以到我们应用程序服务器上已经很少了,应用程序服务器本身在实现运行时,也有20%需要跟数据库打交道等等。这就是我们所谓的整个站点,层层通过缓存来减轻压力,到我们主站点上的压力已经很小的这么一种构建模型。即便如此,对于非常大的站点,尤其是支持在线交易。 偶尔还要类似于那种抢购的站点来讲,那些写数据无论如何你的缓存都不可能命中,这就需要应用程序服务器自身来负责承载。而应用程序服务器自己既要检索用户的创建,又要保证商品上线,还要保证交易系统运行,很有可能运行的服务器在很大的并发写请求压力下依然扛不住,这时候你无论在做几个负载均衡,由于他们各系统之间有可能需要内部协调,也无法实现负载均衡的。所以下一步我们要对应用本身做拆分,比如像商品服务就由商品这个应用程序来负责,把它做成单独的项目,用户服务相关就给用户这个应用程序,跟交易服务相关的,就给交易这个应用程序。对业务本身对应用做拆分。拆分应用的方式可能有很多种。第1步就是根据业务的需求来进行拆分,就是商品一个应用,用户一个应用,交易一个应用。第二,我们还可以按照用户注册,用户登录,用户信息等下进行拆分。把一个大应用拆分成几个小应用,比如说用户注册一个系统,用户登录一个系统,用户信息维护一个系统这也是一个方式。很多应用本身可能都依赖于底层的数据库,底层缓存,底层文件系统,底层搜索等等。所以我们拆的时候也可以分层去拆,把某些底层应用做成服务,根据对底层应用的调用进行拆分。有分布式文件系统就称之后分布式存储好了,有NoSQL非关系型数据库,和关系型数据库,还有搜索引擎,这是几个所谓的底层数据,那对于前端数据来讲,按照我们应用本身的特性来进行拆分,比如交易前台,登录后可以交易,还有一些店铺可以发布一些商品,所以还有一个发布后台。另外通常还有一些社区,比如评论。那这些应用他们所依赖的后端存储程序可能不一样。像交易前台,他用到的分布式存储就比较多,但是可能需要用户登录,需要验证用户的身份就需要跟我也想出去过的,用户数据库打交道,交易前台也有可能跟搜索引擎打交道。是用这种方式来拆分,你的后端有多个子系统,前端也有多个子系统,这也是一种模式。所以这就要根据底层应用的调用本身对前端进行拆分。拆分了以后会发现这个前传应用无论是,交易前台发布后台或者是社区,他们依赖于如果用户需要上传什么东西,他需要登录以后才能够进行。那因此很多应用对后端的某些调用是公共。而且后端的某些功能,他们通常情况下也是被调用之后才有可能展示给用户的。接下来我就再对整个系统做规范化,标准化,就有可能走到下一 步做服务化应用。也就意味着我们对整个服务对整个应用,尤其是后端存储本身,做出一个又一个服务,比如分布式存储,做成一种完全独立的系统。,用户请求时,前端多路由节点进行路由。或或者是跟着我们用户的请求,并且我们的业务规模做服务化。比如说不管是用户,交易,还是商品这三个系统,假如说按业务来拆分。有两个应用程序服务器是用来做商品组,后面又有两个,这两个是交易组的,所有交易都通过这两个服务器来实现。用户浏览时,通过商品服务器来浏览,一旦发生交易,它会自动跳转的交易系统。在下好就有两个服务器来实现用户登录相关的功能,比如用户修改自己的个人信息呀,这些都跟用户的行为有关系。所以这是我们把整个应用拆分之后所得到的结果。而这个结果中会发现。无论是商品,还是交易,还是用户,他们事实上都要跟用户数据库打交道。有些时候我们去浏览商品,去看商品,去交易商品,是不是需要登录以后才能操作。要想完成交易,要不要验证用户身份?要,同样的用户要想登录进来或者修改自己的个人信息做维护。要不要跟用户数据库打交道?也都要。所以这三个系统都要跟用户打交道的。所以我们干脆就把对用户认证的账号请求干脆做一个独立应用。商品也,好,交易也好,用户也好,都不会直接跟数据库打交道的。而我是在数据库前端,在用户的数据库前面放一个应用程序,这个应用程序本身负责接收无论是商品的,还是交易的,还是用户的,对用户的请求都由他来负责后续的操作。把这个功能给独立出来,大家都需要这个功能跟用户管理相关的,像用户认证,用户登录,用户修改,把它独立出来,我们把它称作叫用户管理中心,把它做成一个服务,你只要需要认证用户,只要发过来用户账号和密码就OK了,至于认证成功与否,你不用自己做,有用户中心来做,把那些所有系统公共功能抽离出来,做成一个独立的后端应用,这个独立的后端应用,他才跟数据库打交道,这个独立的后端应用就叫做一个服务,专门是负责用户管理的服务。同样的你的所有的前端的商品服务器都需要去检索商品,展示商品什么之类。那么很显然,他们也需要跟商品系统打交道,给予商品展示甚至于完成商品编辑。所以对商品的管理也不在前端中进行了,所有跟数据库中的商品打交道的时候。包括上传的图片也好,关系型数据也好,非关系型数据也好,都通通有一个专门的商品中心来管理。你只要电容说我要删除这个商品,只要把删除请求发过去,有商品中心来负责完成就行了。所以在前端管理上说,我要上架一个商品,你提供信息由商品中心帮你上架。所有的商品操作都只跟商品中心进行操作。交易要跟交易系统打交道,所以我们要构建一个交易中心。当你需要交易时,就需要跟交易中心发请求就可以了,由交易中心负责完成所有操作。前端构建应用时,只需要调用后端每一个所谓的应用中心所提供的API。这每一个应用中心都是一种程序开发时所需要的功能。它的功能可以被别人调用。调用后通过接口向外提供输出的。告诉别人我这里有多少API。当你需要发起交易时,需要把交易涉及的用户和商品本身,提交给交易中心。通过交易中心的API接口提交给交易中心。由交易中心在本地负责完成这笔交易的存储和结果。当这笔交易完成了,发现能够完成交易中心把交易事务存储下来,反馈给交易应用。所以这种公共功能部分。做成大家可以共同调用的能够完成某些功能的接口。可以理解成在大型网站或者分布式应用构建中。因为它是这个分布式应用中所用的一个功能,所以我们通常把它叫做一个服务。他是中间服务层。应用程序服务器是前端应用程。下面是数据层。这就是三层模式。中间的各种应用中心就是服务,就叫做服务化。但是走到这步之后还会面临一个问题。比方说我们做了用户中心,但是发现这三个系统都要跟用户中心来调用用户中心面临的压力就最大。关于这个应用中心,在某一个时段或者某一天。突然间做了一次抢购,有很多用户登录都要交易,都要抢购。那么很显然这个时候我们用户认证用户中心这个压力就特别的大。这时候有可能会把用户中心这个服务给 压垮。为了避免怎么办?前端的所有应用都是对用户中心所提供的功能的接口做调用,那这种调用有几种模式?同步和异步。所谓同步调用指的是,调用以后要等待对方返回结果之后才能进行。,而且整个过程中连接都是存在的。那么很显然,如果前端发来大量的同步调用。我们的用户中心是不是可能会被压垮的?那为了避免这种情况,就做异步。这整个过程以用户中心为例,可以在用户中心前面加一个中间件,也是队列服务器,所有用户的请求,所有前端的请求,我们可以异步模式进行。比如说用户认证过程通常都需要立即返回结果,但是用户的修改调用,你想象一下,用户说要修改一下个人的名字,他修改完以后,一般情况下很有可能,不会立即去查看结果,我们过个10分钟5分钟以后再存数据,把这个修改结果存储到数据库上,像这种我们就可以异步实现。 再举个例子,因为这个比较接近于现实运营模式,很多网站我们知道注册完之后,他会有一个短信通知,包括银行的交易业务的通知,但是一旦用户要实现一笔转账,用户要实现一笔转账,当你转账这笔交易发生之后,我们一定请求,这个请求对于这个短信的确认,他不会告诉你说立即发送短信,他会告诉你,过一段时间发个短信。因为短信中心,不可能说所有短信请求过来之后他直接给你发送出去,他需要一个一个的发。所以短信中心前面可以做一个队列,所有需要用户转账的用户请求都发送给这个短信网关,那是短信网关不会立即接收,因为他前面有一个队列,异步的,所以对短信网关发出的请求不是立即响应的,而这个网关是在队列里取一个发一个,取一个发一个。他是这么去实现的。所以说不是所有的系统彼此之间协调都需要实时同步调用。一旦可以一步的话,那这个过程我们就叫系统彼此之间调用解耦过程。 很多的大型站点,他们的服务调用只要无需立即得到结果,而且无需同步执行,才能完成下一步操作的话,都可以异步的模式实现。但并非所有的应用都可以异步,而异步本身是通过所谓的消息中间件进行的,我们称之为MOM:Message-oriented middleware(面向消息的中间件),消息中间件的主要功用是协调多个应用之间,通过消息传递的方式,来实现其功能协调的一种机制,而这种中间间的工作模式还是很有意思的事,他的整个过程可以通过异步实现,所以称作消息中间件,他是一个能够面向消息的系统,是在分布式系统中完成,消息接收和发送的基础软件,就叫消息中间件,交易中间件的两个好处就在两个功能异步和解耦,消息中间件怎么去应用,这一定是你的业务本身所构建出来的,所以很多大型公司有技术的公司,很有可能是自己内部开发的中间件,有些公司也可以利用现有技术做中间件,比如像redis就可以做消息中间件至少可以做消息中间件的存储,事实上他可以自己独立做中间件,另外还有一些专门的消息队列RabbitMQ,ActiveMQ,ZMQ等等,这些都是非常著名的消息队列服务的实现。这些服务可以满足大多数场景中对消息中间件的应用,但是这些兄弟中间件本身,只能服务于他们所支持的那种模型。像大公司的很多大的应用站点,他们有很多都有很强大的技术实力。在各模块之间之间协调,自己开发各种中间件。比方说运用直接和分布式后端服务之间做中间件。再比如页面缓存和背后的负载中心做中间件。另外各应用之间作消息中间件。存储层数据复制是用自己做中间键进行复制,其实作为中间件,下面的用户中心也是应用,只不过它不是直接面向用户的,所以称作叫服务。这就是对应的服务化模式或者模型怎么实现的。我们从集中式走向分布式。可能会遇到很多问题,比如说各组件之间怎么协调。分布式环境中所面临的重大的问题之一就是各组件之间怎么协调?。比如通过共享容器来协作,要让我们真正面临这种大的站点时,需要对整个服务框架进行设计,是一个所谓的软件架构师所需要完成的工作。其实我们经常谈到架构师的时候,它主要分为两类,我们所指的是你整个系统本身的架构,叫基础系统底层架构师。还有一种是软件架构师。我们整个分布式系统,所开发的各种应用。这些应用彼此之间,他实际上是一个大型的软件,这软件的特性架构。包括内部有哪些中间件,消息中间件需要自己去研发,各种应用如何去研发。哪些是服务性的东西,这些都是软件开发架构师所面临的工作。而且开发架构通常而言其身价是非常高的,尤其是大型系统的开发架构。接下来来说另外一个层次。就是在讲到数据这个层次当中,我们需要再深入了解的问题,数据访问层。比方说,当前端应用程序对我们数据库访问压力过大时,尤其是写操作压力过大时。我们需要对数据库进行拆分,拆分有两种方式,分别是垂直拆分和水平拆分。垂直拆分比较简单,水平拆分可能需要麻烦点。那我们先说说垂直拆分的问题。垂直拆分以后或者说拆分的时候,我们要考虑一下几个方面才能做起来的。第一,因为我们本身在单机上提供多个表时,为了保证事物的完成,有些查询或者读写操作需要在多张表上同时进行。那一旦把这些表拆分到不同的数据库上去,而有些表之间可能还是有这样那样的联系的,那他怎么保证事务?所以这是第1点,单机所拥有的ACID保证机制被打破。所以此时你所面临的选择是,要么放弃事务,要么引入分布式事务;第二,有些时候我们做复杂查询时候,尽管我们把他们都拆开,有用户库,商品库,交易库。我想查询某一个用户的所有交易怎么办? 还有我想查询某一天有多少个用户进行了交易怎么办?这时候我们就必须得跨库来做连接查询了,所以一些Join查询操作将变得非常困难,所以此时该怎么办呢? 第三,原来依赖于外键实现的约束将无从保证:这是垂直拆分所面临的问题。因为垂直拆分它主要是将表分散到多个数据库服务器上去了,所以面临的是这样的问题。我们再看水平拆分所面临哪些问题。水平拆分其实就是把一张表中的数据给他横向分割到不同的数据库服务器节点上来提供服务,那水平拆分他面临到哪些问题呢?第一,同样的单机所拥有的ACID保证机制被打破,第二,Join问题也有 ,第三,同样原来依赖于外键实现的约束将无从保证,所以垂直拆分所遇到的问题,水平拆分都有,而水平拆分还有额外的问题,第四,比如说依赖于单库的自增序列生成ID会有影响。甚至我们在创建一个双主模型时会有影响。因为双主模型也是也算是一种所谓的水平拆分的表现,虽然他没有真正实现拆分。第五,针对单张表的查询很有可能要跨库操作;那于是乎,单机变成多机之后,事事务怎么处理呢?这就是分布式事物的实现。如果我们这个时候不得不用到事务功能的话。那就意味着我们要做分布式事务了。分布式事务是指事务的参与者支持事务的服务器及资源服务器以及资源管理器分别位于分布式系统的不同节点上。也就是说,我们要想实现事务,通常有这么几个依赖因素。第一,事务的参与者,比如我们有多个线程参与这个事务。第二,。支持实现事务的服务器,你需要几台服务器来完成事务。第三,资源服务器。第四,事务资源管理器。一旦要想实现分布式事务,就意味着这几个组件,很有可能会在不同的节点上的。而对于单机事务来讲,对于单机模型来讲,这几个组件都在同一个节点上,所以他内部实现进程间协调就OK了。 要想了解分布式事务。我们再了解一下分布式事务的模型及规划,早期这个模型是由这个组织X/Open,提出的分布式事务叫XA,他的意思就是分布式事务,这是一个分布式事务规范,如何来实现分布式事务的,他有一个X/Open DTP的模型,DTP是分布式事务处理引用或者参考规范就这意思。而在参考规范中,他定义了三个组件,分别是第一个,简称为AP:即应用程序,说白了就是可以理解为使用DTP模型的程序。第二,RM:资源管理器,即DBMS系统,可以理解AP是要依赖于数据库中的数据的,而RM就是这个数据库管理系统。第三,TM:事务管理器,事务管理器主要是负责协调和管理事务的,提供给AP应用程序编程接口并管理这些东西。所以tm要协调AP和rm就这意思。在这三个组件中 AP可以跟 PM和rm通信。而tm和Rm之间可以相互通信。那么我们接下来看什么叫2PC:Two Phase Commitment Procotol(两段式提交协议 ):两段式提交。 两段式提交是相当于单库事务。,所以既然叫两段式,那么就有第一阶段和第二阶段的概念了。第一,我们的应用程序项事务管理器TM提起要运行一个事物,TM要想办法保证这个事物是成功的,所以第一向rm1请求资源1, RM1必须告诉TM说我这里准备,OK。好,前面是一个准备阶段TM告诉RM说我要启动一个分布式事务,现在需要RM1资源,RM1资源说OK,这就可以了,这个过程我们称作提前准备阶段叫prepare(准备),只要RM1资源说OK那就可以了。但是这一次事务需要依赖于两个资源,所以还要请求RM2资源。这是第1个叫准备阶段,所以这就是准备,OK准备,OK,把所有资源都准备OK。他才会进行第2步,第2步就是提交之前的prepare(准备)就叫做commit,提交RM1说OK,提交RM2说OK,所以同步准备同步提交,都准备完成了,才可以进入第2段,所以这就叫做两段式看懂了吧。第1阶段就是请求所有资源都准备OK的阶段。然后都准备OK了,再进入第二阶段。请求所有都提交的阶段,如果完成,那这一次失误就完成了,这就叫两段式提交。所以说如果某一次事务提交需要依赖于两个以上的资源。他必须要让每一个资源都在第1个阶段准备成功才能同时开始执行第二阶段,而且还要保证第二阶段都要提交完成。其中任何一个步骤失败了,这个事务都无法完成。比如说第1阶段准备的时候成功了但是第2个资源访问它返回的是Error这就不能继续了,那么第1个阶段出现任何问题,第一个资源还能提交吗?不能,要做回滚。第1个出现问题,第2个就一定要执行回滚。这就是所谓叫两段式提交。在分布式系统中,两段式提交是很基础的,也是很常见的概念。两段是提交是一种协议。我们再来看第2个,CAP, Cap是一个非常重要的分布式系统的相关理论。我们来看cap指的是什么?  C指的是一致性。 A叫做可用性。 P叫做网络分期容错性。比如集群之间无法通信了,网络出现分区了也可以理解为网络分裂了。  Cap就是要说任何一个分布式系统,任何一种需要在多个主机上运行的分布式系统,最多只能满足这三个中的两者。根据我们的选择不同,那么这个系统本身所达到的目标不同。 Cp理论是在2000年7月,由他的作者Eric Brewer提供的。 它的核心意义指的是,任何一种分布式系统最多只能同时满足上述三项中的两项,因此分布式系统的目标只有三种选择了。            第一,AP:指的就是满足可用性和分区容错性,选择AP意味着放弃了C,放弃了一致性,追求的是分区容错性和可用性,绝大多数分布的系统都选择AP,放弃一致性尽可能保证系统是可用的,而且即便整个网络出现了分区的场景中,也照样能够正常工作            第二,CA:指的就是满足一致性和可用性,选择CA意味着放弃了P,其实我们单机上大多数就是这功能,比如mysql在单机上工作时,他就不可能有网络分区的可能性出现的,所以放弃了P就意味着他不在多个节点上实现                  第三,CP:指的就是满足一致性和分区容错性。只有这三种可变化形式,选择CP意味着放弃了A,放弃了可用性,但是这动不动就不可以用了怎么弄?所以其此种方案一般的话用的很少。。。。 


  LAMP,LNMP

  应用从资源占用的角度分两类:
        CPU Bound(CPU密集型应用)
        IO Bound(IO密集型应用)
  
  session sticky(会话粘滞,基于IP地址的session粘滞 )
        ip based
        cookie based(基于cookie的session分发)
  session replication(会话复制,不是用大规模集群中,所以使用第3种。)
  session server(session集中存储)
  
  引出缓存:
        1、页面缓存
            varnish, squid
        2、数据緩存
             key-value(memcached)

  主库写操作压力:数据库拆分
        垂直拆分:把数据库中不同的业务的数据拆分到不同的数据库服务器中
        水平拆分,把一个单独的表中的数据拆分到多个不同的数据库服务器上

  NoSQL:非关系数据
        文档数据库
        列式数据库
        ... ...

  SFS:非结构化数据
        TFS,MogileFS:适用于存储海量小文件。
        HDFS,GFS:适用于存储少量的大文件

  应用拆分
        根据业务特性拆分
        根据用户拆分
              用户注册
              用户登录
              用户信息维护
        根据对底层应用的调用进行拆分

  异步:解耦
        消息中间件
              MOM: Message-oriented middleware(面向消息的中间件)

              RabbitMQ, ActiveMQ,ZMQ

  数据访问层:
        拆分:
              垂直拆升:
                    单机的ACID保证被打破:要么放弃事务,要么引入分布式事务;
                    一些Join查询操作将变得非常困难:
                    原来依赖于外键实现的约束将无从保证;
              水平拆分:
                    单机ACID保证被打破;
                    一些Join查询操作将变得非常困难:
                    原来依赖于外键实现的约束将无从保证
                    自增列的10号的产生会有影响:
                    针对单张表的查询很有可能要跨库操作;

        分布式事务的实现:
              事务:事务参与者、支持事务的服务器、资源服务器、事务管理器

              分布式事务的模型及规范:
                    X/open: XA (分布式事务规范)
                          X/Open DTP:定义了三个组件
                                AP:应用程序,即使用DTP模型的程字
                                RM:资源管理器,即DBMS系统
                                TM:事务管理器,负责协调和管理管理条例,提供给AP应用程序编程接口并管理资源管理器

              2PC,两段式提交
                    Two Phase Commitment Procotol

              CAP:(就算是Tomcat实验做不出来。CAP代表了什么也要背会。 ) 2000年7月, Eric Brewer 
                    一致性
                    可用性
                    网络分区容错性
                    任何一种分布式系统最多只能同时满足上述三项中的两项:因此,分布式系统的目标;
                          AP:放弃C;大多数他布式系统都选择此项:
                          CA: 故弃P; 
                          CP:

              分布式系统的目标:加强A和P,在C上进行妥协;
                    但是我们要明白一个系统中的数据,如果不能一致的话,所带来的问题也是非常严重的。那因此妥协谁并不是真正放弃谁。而是在C上实现了,额外的定义。怎么妥协的呢?BASE模型,他就是分布式系统当中选择加强a和p而妥协c的时候,我们经常可以满足的模型。ba指的是基本可用。 S指的是网状态,表示接受一段时间内的状态不一致,但是最终还是要一致的就这意思。 E就是表示最终一致,最终一致性也是弱一次性的表现。。所以我们都在c上进行妥协。

                    BASE模型
                          BA: Basically Availibale
                          S: Soft state:接受一段时间内的状态不同步:
                          E: Eventually Consistent:最终一致性:


              但我们如何在服务器上保证其一致性的这个这个公式我们要有所了解。我们有假如有这样几个相关的概念。
                    *概念
                          N:节点的个数
                          W:更新的时候需要确认已经被更新的节点个数
                          R:读数据的时候读取数据的节点个数

                                W + R > N      ---> 强一致性(通常N=3,W=R=2)
                                W=N,R=1      最佳读
                                W=1,R=N      最佳写
                                
                                
                                W + R <= N      ---> W 加 R 小于等于 N      弱一致性

                                      W表示写,要保证几个节点写完成。R表示读,保证从哪个或哪些节点上读完成。这两个就表示一次读和写,如果大于N的,就表示强一致性。就说我单机mysql服务器,单机有一个节点,都需要从至少一个节点上进行才能进行读,写是不是也是一个节点,那么很显然,1+1是不是大于1,所以这就是强一致性。同样的以主从复制为例,假如说是一组两从的节点,我写的时候是不是要写一个节点,但是我读的时候,是不是也需要读一个节点,那一共有三个节点。那么读节点数加上写节点数,大不大于整个节点数?不大于,所以他是弱一致性。在这个模型中,如果W=N,写的时候所有节点都要写才能完成,R=1这是最佳读的模型,写的时候所有节点都在写,单独的时候只需要一个节点,mysql可以做水平拆分的,水平拆分的主要目的是为了写分部,所以如果某一次写操作,只要说有4个节点,对每一个节点都要去写,这时就使用了写分布,这样的某一次查询,一般来讲,我们业务中所有的查询只需要通过一个节点,就能查到我所需要的数据,但是查哪一个不确定,但每一次只需要一次查询,这就是最有效的读模型,而且对写做到有效的负载分担,这是我们水平拆分设计的终极目标。下一个W=1写只需要一个,但读需要从所有的节点去读,这就是最佳写模型,虽然叫最佳写,但是事实上他对写负载分担没有什么意义,他只是写模型很简单。这是服务器的一致性。但是你无论怎么读怎么写,尤其是不能只从一个节点读或者一个节点写的时候,那么就不得不从多个节点中读或者写,那因此从哪些节点读,哪些节点写,就面临着两种模式。第一种模式基于模的哈希,除模取余。还有第二种是一样的一致性哈希。

分布式存储或分布式文件系统

  数据存储的趋势:
        数据增长
              目前不光这种大型站点,其实很多中型站点像美团点评,这样的企业来讲,他每天的数据访问也是非常大的,不说别的,就说他们的日志,一天几十亿条日志,我们要想集中收集并完成日志分析,我们不可能把它分散放到各种各样的主机上,最终我们可以集中收集起来,放到一个可以统一管理的存储,那这个时候我们要么是集中式存储,要么别的存储,反正是我们现在数据存储的趋势面临数据爆炸增长。全球数据也开始遵循摩尔定律的发展,每18个月数量翻一倍,而我们的数据量,数据统计有几个基本单位,GB,TB,PB,EB,ZB,YB,等等。那我们数据规模一旦达到PB以后,其实就可以称得上是海量数据了,大数据了,可以想象一下,因为1024个GB才是一1个TB,1024个TB才是1个PB,而到了pb级别基本上就是海量数据了。你想象一下,我从1024个1024GB去分析某个数据,去找某个数据,怎么找?存起来好像不是什么大问题,但是我们日积月累可以存起来。但是将来我们需要对这些数据中找一个数据怎么找?那因此我们数据存储大体上目前来讲面临这样的趋势,比如数据增长是爆炸的,是翻倍的,但是有没有想过按固定时长,以此增长一倍是什么概念。
        信息连接
              信息连接越来越没有规律可循。
        并发性
              数据访问的并发性也呈逐年增长趋势。。
        结构多样性
              数据呈现出各种各样的结构。虽然说结构有多样性,这种多样性就使得我们此前的结构化数据被打破了,从非结构化,一旦到非结构化的话,再事就麻烦了。比如访问web服务那我们就约定俗成的去访问对方的80端口,但是他说非80这事就麻烦了,到底是啥呀。所以一旦没结果,这事就麻烦了。

  大数据带来的挑战
        数据采集
              数据采集会有很大问题。假如我们前端web服务器有200个节点,我们从这200个节点上采集我们期望用的数据,怎么去采集?所以数据收集需要专门的工具来实现,这样我们需要一个信号系统行之有效的系统来实现。
        数据存储
              数据存储也面临了很大的问题。
        数据搜索
              把数据存起来以后一个PB内,我们如何获取到自己有兴趣的数据。
        数据共享
              这么大量的数据,我们如何共享给别人。
        数据传输
              PB级的数据如何做到高效传输。
        数据分析
              数据分析如何进行,把整个数据做一下分析,就像我们搜索引擎一样,当用户给一个关键字就能反馈出这个关键字,在哪些页面中有相关数据,我们如何完成倒排索引。      
        数据可视化
              数据通常都是一些信息,我们如何将分析结果用非常直观的方式呈现出来。 

  传统存储
        问题:
              纵向扩展受阵列空间限制。
                    说白了就是像我们的磁盘存储阵列,有可能是集中式的存储区域网络,当我们存储空间不够用了想扩展,其实它的空间是有限制的。你这个磁盘阵列上有多少个盘位,假如说一共有32盘位,现在是16个,你扩展到32个盘位怎么在扩展。
              横向扩展受交换设备限制。
                    指的是一个磁盘阵列不够,扩展不了了,再加一个磁盘阵列,实在不行再加一个磁盘阵列,但是最终这个阵列上前端的存储交换机,它所支持的阵列接口是有限的。
              节点受文件系统限制
                    有些文件系统在前端,像集群文件系统GFS相关的,在用户进行访问时他的单个集群文件系统,使用共享方式进行访问时,最多只能支持到16个节点。

  存储相对于分布式来讲,就有了另外一个概念,叫做集中式。所以这次两个不同的概念,一个是集中式,一个是分布式。

  集中式
        哪种存储方式叫做集中式存储呢,比如多个节点上共享存储通常都是集中式存储,比如像NAS,SAN,等等,这些都是共享式存储或者叫做集中式存储,比如说相对于nfs来讲,他是怎么工作的?我们找一个节点,它提供了存储空间,我们前端有多台主机都可以连接到这台提供了存储空间的主机上来,完成数据存取操作,站这种角度来讲,我们可以理解为这就是一种集中式存储。那么集中式存储,它所面临的问题是。这么多主机都发过来数据存取操作。这台存储主机的本地存储io压力会非常大,这叫NAS我们称作为网络附加存储。SAN我们称作为储存区域网路,他的工作方式是什么样的?任何一种存储设备其实他都涉及到存储协议的概念。比如说我们本地有一台主机在主板上有CPU,有内存这些是计算机的核心,没有错的,但是我们数据要想持久存储还需要一个辅存,所以在主板上会有一个接口,他可以接上一根线缆,然后这根线缆可以接到一块磁盘上来,大体上就是这么一种体系,或者这么一种结构。而这根线跟这个磁盘设备连接起来。这根线缆我们可以认为它就是通信的物理介质。而后这台主机主板上这个控制器也有可能是一个适配器,其实就是一个通过这根线缆这个介质通信的协议的客户端,而这块硬盘一般磁盘上也有集成的芯片,这块集成的芯片,其实他也有自己的CPU,内存等,这个集成芯片其实就是提供了这个协议的服务端,因此一旦通过线缆加电以后,这个线缆可以加电,也可以传输的,这么我们就有了客户端和服务端,二者之间通过这个专用线缆来完成通信。就像我们以太网一样,我们知道现在以太那个双脚线是目前来讲用的比较多的通信介质,那有没有想象过把双脚线换成同轴线缆,站在我们现在这个角度上,我们现在这个网络设备通信上他是没有办法工作,同样道理成光纤他也没法工作。所以某一种协议,有可能他的工作跟物理介质也是有关系的,且必须适用物理介质,你不要想IP协议,我们只需要想底层协议。比如无线通信,他用的是无线通信的底层协议。以太网是使用的CSMA/CD算法来完成通信。这种基本通信机制,他会借助于某种工业线缆来完成,早期用的同轴线缆,现在大家更多用的是双绞线缆。所以说像磁盘跟计算机之间这种他们所专用协议就叫做存储协议,计算机是存储客户端,磁盘是存储服务端。当如果有两块硬盘时,会发生什么问题?也容易理解,那就意味着这两块硬盘在这个线缆上,或者在通讯协议中必须得有地址。我们发过来任何一个存取的报文,比如我要存数据,他也是个报文,报文有前导码,有地址标识,后面才是我们要存的数据,因此这两块硬盘上的控制器,可能都能收到这个数据报文,但是只有那个报文上的磁盘地址的,那个磁盘才能把这个报文接收下来,并完成寻道,而在本地存储的。像这种它的扩展性是非常有限的,像IDE线缆一个控制器一根线缆上,最多只能接上两个磁盘,我们称作叫主从,两块一块主盘,一块从盘。而对于scsi协议(Small computer system interface 小型计算机系统接口。),从这个角度来讲,我们可以理解为他也是集成在主板上的一个芯片,或者我们通过PCI , PCI-e等槽,扩展的一个板卡也行,这个板卡与IDE所不同的地方在于,这个板卡的功能更为强大,因为它一根线缆上可以接7块或15块硬盘, 他要看你的整个scsi协议是窄带的还是宽带的,如果是崽崽的话,他有8个接口,其中有7个接口用来接对应的硬盘设备,那如果是宽带的话,它一共有16个接口,其中15个接口可以直接去接磁盘设备。那我们是不是可以理解为在一个线缆上可以连接那么多块硬盘,那我们可不可以认为这一个线缆连接就是一个网络,这就是存储网络概念,只不过对于scsi来讲他的扩展能力有限,这个线来说能够传输的距离太有限了,通常也只是集中在机箱内,或者是在你的主机同一个机架上的另外的位置,传出距离有限,再次说明。另外一个其实在scsi上对接硬盘的每一个接口,我们通常也把它称作partition他有专门的名字,而为了保证一个scsi组件连接更多的设备,我们知道一个硬盘上还可以有多分区的,每一个分区都当做独立的设备来使用,那因此每一个partition上还可以再次被划分成更多的子单位,这每一个子单位我们把它称作叫一个逻辑单元,叫lu,每一个逻辑单元用来对应一个存储设备,其实并不是一个口上接一块磁盘的,一个口上可以通过lu接n个设备,每一个设备都有一个自己唯一的编号叫做逻辑单元号,这显然就是一个存储区域网络了,但这种存储区域网络他只能通过一个scsi总线来接,而且传输距离极为有限。那既然这么一个网络上能接这么多硬盘设备,那每一个硬盘也必须有其地址,不然我存取数据时把数据包扔到哪儿或者是我发送一个请求过来,到底哪个硬盘来负责响应呢,所以说他在实现这么多复杂的功能时,也参考了TCP/IP协议站设计的风格,比如说像本地底层,他把整个协议传输报文协议分成了4层,物理层决定这个报文是如何组织成特定物理传输介质上完成报文传输的。传输层,是用来指明数据组织格式,以及如何保证数据的安全可靠送达到目的端的,数据链路层是用来使用scsi协议的通用指令,比如我们是存取那个读写寻道等等各种各样的指令,而这是特定指令不同公司所生产的不同型号的scsi设备,有可能有他的专用指令,所以大体上就分为这四层。而最底层我们称作叫物理层用于决定这个协议报文是如何在哪种设备上传输的,而默认情况下,他用的只能是scsi自己的专用线缆。scsi线缆大概是并行40根线也有并行80根线他只是个排线。任何一个协议之所以分层设计,我们说过,其中任何一层都可以按照需要替换成其他的内容。所以后来就有人把这样一个必须借助于scsi线缆传输的协议报文格式替换成了另外一种格式,它可以通过光纤,或者是以太网传输,我们借助以太网网线反正都是传输数据报文的,而且都是流式报文,那我们只要把底层的给他借助于以太网的传输子网完成就行,反正只要能够从源端到目的端就可以,如果我们要借助以太网传输就意味着什么?这个发起者只要是一个以太网的客户端服务器,或者是客户端主机,而接受者也不再局限于是一个scsi硬盘了,而可以是一个TCP/IP的主机,这个主机上只要提供了存储空间,这个主机只要能把自己模拟工作成scsi协议的服务端,他就在别人看来,他不是一台主机,而是一个总线上可能n块硬盘,看你怎么去模拟这个总线了。我们可以简单的理解为这个板卡就不再是工作在本地了,这个板卡我们可以放在另外的其他主机上了,所以中间可以不仅仅借助于scsi协议进行传输了。那么可以想象一下,如果是通过以太网来传输,那又是什么,这边有一台交换机可以连n台主机,任何一个存储设备或者任何一个主机,他能够提供自己的存储空间,那么任何工作在同一个局域网甚至跨互联网的,只要能够基于以太网协议或者是TCP/IP协议通信的主机都能够在这个设备上存取数据了,所以他就极大地延展了其地理范围的,但是最终结果还是存储在同一个设备上,像这种扩散的机制,其实以太网用的其实并不是特别的多,因为以太网带宽有限,现在万兆以太网,他的通信能力也是有限的,真正无线带宽能力应该是光纤设备,所以很多场景中都用光设备,光卡接上光缆,接上光交换机来提供存储网络,因为毕竟还是集中式存储,我们不能让通信网络本身成为整个传输瓶颈所在,而光纤传输带宽理论它是无上限,几乎不无上限,据说现在达到40gbps,80gbps应该都没什么问题。就看你能有多大能力,多快的速度来灌装数据。好了,那由此这个就是我们所谓的叫集中式存储的SAN的概念。那么要一个明白,即便是我们前端传输介质不成为性能瓶颈,但是这个存储设备后面,后面不管怎么样,还是接硬盘呀。那你这个要么是ide硬盘,要么是sata硬盘,要么是scsi硬盘,或者是sas硬盘,每一块硬盘它的存储能力是有限的,Io能力是有限的,再不然你用100块硬盘做个read 0在不考虑磁盘会出错的情况下,它的IO能力是不是依然有限,用PCI-e的固态硬盘做read,看上去是个无与伦比的性能了,对吧。但是你看有多大规模的,如果想象一下,如果是google这样的站点,每天从互联网上可以爬过来上亿张图片的,你的IO能力能承载得了,所以说在这种极端场景中,这种集中式的存储,即便是我们经过层层优化,能够为前端的诸多主机提供统一存储位置,但是他依然会面临着很大的问题,比如io能力他有上限,一旦达到上限,以后再扩展是极为不易,就像我们自己做read 0一样,本来是使用10块硬盘做read 0,现在又加了10块,有20块了,但是此前10块上的数据已经对,分散好,已经配置好了,已经存储好了,那这个时候我们就只能做Rebalance,做重新均衡,就意味着我们把此前10快上的数据已经存下来的要给他均衡到新添加的10块硬盘上,才能保证我们在访问,在读写的时候才能保证数据的io能最大化。所以说它的扩展能力是极为有限的,而且我们到底可以加多少块硬盘,这还取决于你接硬盘的接口数量,还取决于你的前端硬件设备扩展起来不便。在极端场景中,比如我们前端主机只有三五十个,这种io压力下,他的工作应该是没有问题,那三五百个呢,三五千个呢,其实现在单一节点或者单一集群的三五千个节点,在有些规模的企业中还是很常见的。
        共享存储:
              NAS:Network Attached Storage(网络附加存储)
              SAN:Storage Aera Network(储存区域网路)
        
  分布式存储
        专用的元数据节点:集中元数据存储:(数据节点只负载存储数据)
              分布式存储是一种什么样的存储,无非就是存储是由多个节点共同提供了存储空间融合而成的一个较大存储空间,比如每一个节点都是用来专门做存储的节点,而每一个节点都贡献出来一部分存储空间,我们可以把这种节点提供N个,但是这些节点上存储空间是离散的,是分散的,所以我们需要在这些节点之上抹上一层,就跟高可用集群一样抹上一个通用层,我们可以理解为这是一种存储操作系统。对通用层来讲,有了它文件系统再向客户端输出时看上去就是单一的接口,因此任何客户端再来存取数据时,他只需要扔给这个通用层,由通用层决定数据如何存储,比如说存储在哪个节点的哪个存储空间之上,当客户端请求数据时,他也无需关心这个数据到底在哪个节点上存储的,有那么一位置能告诉他。之前说过数据有元数据和数据共同组成,文件有数据和元数据,而现在我们访问数据基本上都是以文件的方式进行,有没有想象过在单机上我们使用单一存储空间,每一个文件系统它都是一个单独的存储设备或者说每一个文件系统都是一个单一管理的逻辑设备,这个逻辑设备他自己有空间存储元数据,有空间存数据,所以他的文件是完整的。那有没有想象过,一旦我们把数据给它分散到N个节点上,那数据存哪,元数据存哪儿呢?你把元数据存在哪个节点上?所以分布式系统或者分布式文件系统,要么我们文件在存储级别只表现为一个数据流,而不提供文件系统本身的功能,要么提供文件系统功能。但一般来讲,无论如何他最起码任何一个数据流,它也应该有一个名字,我们能做到按名存取,至少也有一个唯一标识,那因此元数据是必不可少的。那就是元数据存哪儿啊?一般一个分布式文件系统,会有一个集中节点,有一个专门的节点负责存元数据,有N多节点负责存数据。所以当一个客户端试图来存取数据时,先向元数据节点发请求,那么这每一个数据节点上到底如何存数据呢?假如这个客户端要存一个数据了,这一个数据有一个G,于是这个元数据节点就告诉客户端,把那一个G的数据划分成N个存储单元,比如每512兆一个那于是就分成两块,并且接下来这个元数据服务器并告诉客户端,接着你把这分成两块的第1块,自己存到第1个节点上,第2块自己存储到第3个节点上,从而他就把数据放在了1,3两个节点上,那万一第1个节点挂了怎么办?数据只剩一半儿怎么办?比如把存储到第1个节点的512兆,做冗余,而冗余到哪个节点上,这是你的元数据节点决定的,然后由这个数据节点存下来以后,它自动自己冗余到其他节点的,大体上就是这种方式来进行工作。所以如果冗余一个不太靠谱,我们可以再冗余几个。这就叫做所谓分布式文件系统,对于分布式存储来讲,这些数据节点,我们通常把它称作叫data block(数据块), 既然叫大数据存储了,按照一个G一个块儿都是不存在问题的,所以这就叫做分布式存储中的第一种模式。

        无专用的元数据节点:所有节点均完整存储元数据(存储了部分数据)
              我们这里有4个数据存储节点,但是我们没有任何一个元数据节点,也就意味着当客户端需要存取数据时,他找到这4个节点中的其中一个。所以所谓叫无集中式元数据节点的结构,指的就是整个集群当中每一个数据节点本身又都提供了整个集群完整的元数据,但是每一个节点只存储了一部分数据,为什么是一部分数据呀?因为不是一部分数据,它就不是分布式存储了。因此我们找到其中任何一个节点,他都能够知道我们的数据到底在哪些节点上存放,这种好处在于没有单点所在。

        分布式文件系统遇到的挑战:
              节点之间通信
                    节点之间的通信如何保证其可靠性。 
              数据存储
                    如何能够明白整 个数据存储的布局,我们需要一个元数据节点,还是说元数据让每一个节点都提供。
              数据空间平衡
                    元数据节点如何能够保证整个集群中所存储的数据,能尽量均衡的存储到这些数据节点上去,如果不均衡的话,我们的IO就没办法保证分散。它就发挥不了分布式存储的意义了。
              容错
                   一个节点故障呢?一个电脑一年故障率大概是365分之一.,而365个电脑,一年故障率就是100%的,就必然会故障,就必然有一个节点会发生故障,一天坏一次。所以对于我们现在的这种商用X86系列平台服务器来讲,它是出故障的可能性还是很高的。所以为了避免因为节点故障而导致数据丢失,就必须进行容错,容错的方式就是把每一个数据块多存储几个副本就可以了,但存副本也是有一些讲究。
              文件系统支持
                    他支持文件系统的功能。像存储,他提供的都是存储空间,而我们的文件系统通常是可以挂载以后,通过文件系统接口访问的,他如果不接这种文件系统接口,你就没办法挂载,就只能使用API访问,就像mysql一样,好像我们mysql从来就没有挂载过,但是他还照样能让我们去存取数据,访问方式有两种,要么第一使用版mysql专用的命令行客户端,第二 使用mysql图形化客户端,但是不管哪种客户端,都是通过API与对方进行交互。因此没有文件系统支持我们叫做分布式存储,有文件系统支持我们称作叫做分布式文件系统。

  分布式
        文件系统:有文件系统接口;
        存储:无文件系统接口,通过API访问:

              常见的实现有哪些:
                    GFS:Google File System(Google文件系统)--> MapReduce --> BgTable
                          这是最为著名的。Google文件系统其实并不是新生事物,很早之前就有了。但是Google后来发表GFS,并且把GFS整个核心研发思想,以论文的形式呈现在世人面前时,分布式存储或者分布式文件系统正式进入一个新时代,掀开新篇章。GFS的研发主要目的是在于存储Google,从互联网上通过爬虫爬来的海量网页。并且能够在这网页存储下来文件系统之上支持并行分析和索引构建以支撑其高性能搜索功能的。MapReduce我们所谓的hadoop就是山寨版的GFS和MapReduce,只不过hadoop所山寨的GFS不叫GFS,而叫DFS,只不过它是hadoop的分布式文件系统,所以也叫HDFS。 Google最后还发表了第3篇论文叫做BgTable(大表),仍然像我们mysql组织一张表一样,也是一个表结构,但是它不支持事务,而且他很多数据都是键值存储的。所以在这种就是NoSQL的表现,后来hadoop把他山寨了叫HBase。

                    HDFS: Hadoop Distributed File System (Hadoop分布式文件系统)      --> MapReduce(他也山寨了MapReduce只不过还是叫MapReduce。) --> BgTable --> HBase
                          HDFS加上MapReduce二者合起来就是hadoop

                          (适用场景:敷据不太多的大文件:            HDFS或者GFS工作方式,就是分布式集中存储的方式进行的, 首先有一个节点作为集中的元数据节点,有n个节点来做数据节点,在数据节点可以并行存取数据,他支持n个节点同时到这些节点中,以较高吞吐能力实现数据存取,而这些节点我们通过增加节点还可以提供更多的存储空间以及提供更分散的io平均能力。HDFS和GFS有一个特性,他的元数据虽然是放的一个元数据服务器节点上,但元数据是在内存中维护的,为了保证元数据节点挂了以后,元数据怡然在,所以他能够周期性的给他同步到持久存储设备。目前只考虑单点上,元数据节点一旦宕机,整个文件系统是不是都宕掉了,所以它是整个系统的单点所在,因此我们还应该对元数据节点做高可用,这个我们先不考虑,我们先想象一下,好的。他既然把所有的元数据都存储在内存中,那么内存空间是有限的,所以他们制作文件数量也有限,你要说一个文件,它的元数据需要1k的话,那1亿个数据呢?能明白什么意思吧。所以他能够存储文件数量有限,因为它是存储在内存中的,所以所能够支持文件数量有限。 因此无论GFS还是HDFS,他们通常都是用来存储有限的,就是几百万的或者几千万而已,最多,这种大数据,因为每一个文件只需要一个元数据。既然我存的量不多,那每一量对应的文件数据大一点,一个数据两个T,就算存100个。所以我们说它只是适合于存储于有限的就是百万级别的较大单个数据这样的。但是这种存储能力对有些场景讲不太实用。比如说像互联网公司存储图片的需求,一个互联网公司运作10年,或者像Facebook这样级别的企业,那不用到10年运行个5年8年,几乎每天增长的图片数量都是千万级别甚至于,算一下几年下来是什么概念,每一个图片都应该是一个单独的文件,所以说使用HDFS或者GFS来存储这个量级别的数据,显然是不太理想,它不适合于存储海量小文件,只适合于存储较少的像百万或者千万级别的单个大数据。不过,即便是海量的小文件,但是架不住量多几十亿个几百亿个小文件,其实也算是大数据,不是吗。)

                    TFS: Taobao File System(淘宝文件系统)
                          将元数据存储于关系型数据库或其它高性能存储中,从而能维护海量文件元数据:
                                淘宝在HDFS基础之上做了二次研发,能够让HDFS元数据不在存储的内存中,可以支持存储在外部的存储当中了。因此,他所支持的文件数量就急剧增长,而这个文件系统被称做叫TFS。那既然TFS还是能做到,为什么HDFS没做到把它的元数据放在一个外部的关系型数据库,或者非关系型数据库中呢。其实很容易理解。首先放到外部存储中,于是依赖的组件就多了一个,对不对,你自己还要依赖于别的组件才能实现文件系统,这就不太独立而完整这是第一点。第二点,我们把数据放在外置存储中,它的性能显然没有放在内存中高。还是那句话没有放之四海而皆准,没有普适性的原理。TFS的确适用于存储海量小文件,但是面对于数量不大的单个大文件时,它的性能表现上就不如HDFS了,但是TFS对淘宝来讲,这种战略来讲确实很必须的。因为你想象的,那淘宝加上天猫这样的站点,他得有多少个图片,所以他们是专门研发改进而来为淘宝的战点存储海量图片,淘宝可能有几十亿甚至上百亿张图片,一个站点。这么多图片别说别的,我从中找一个我想要的就很困难。

                    GlusterFS:
                          比起前三者而言。他就是满足另外一种风格的叫做去中心化设计。所谓去中心化,就是没有专门的元数据节点,而它的工作模式就跟刚才我们所描述(无专用的元数据节点:所有节点均完整存储元数据)的那种情形一样或者类似,每一个节点都存储了整个集群的元数据,而每一个节点又都存储了部分数据。这种是去中心化设计的。

                    ceph:
                         这个我们将来可能接触的就比较多。 因为这是在Linux内核级别,直接被整合进Linux内核当中所提供的分布式文件系统,而且已经被收入进Linux内核。前面讲的GlusterFS,TFS,HDFS,GFS等等他们都是在用户空间实现的文件系统,他要借助于本地文件系统来实现分布式文件系统,这是一些重量级的实现。

                    MooseFS: 简称mfs
                    MogileFS:也简称mfs
                          FastDFS
                                有人做过测试MogileFS的性能要比MooseFS好的多。所以说国内很多公司当中在存储海量的网站图片时,一般都使用轻量化的MooseFS或MogileFS。 因此我们现在去构建一个中等规模或者是中大规模的互联网站点,去存储数量较多的图片时MooseFS或MogileFS都是不错的选择。那我们不必要去每一个了解,了解实其中一个就可以。但是各位要注意的是MogileFS使用perl语言研发,不过可能有人依然满足不了这种特性表现。所以有国人就仿照MogileFS的工作模式,对MogileFS做一次完全的重写,便发布了另外一个类似于MogileFS叫FastDFS。

  分布式文件系统设计目标
        访问透明
              要保证访问透明,对于客户端来讲,哪怕使用API也罢,使用挂载也罢,其实我们真正的用户本身并不知道这个数据来自于哪的,只有API知道到底数据是在哪里存的,存储在哪个节点上。
        位置透明
        并发透明
        失效透明
              如果提供冗余能力的话,还能保证失效透明
        硬件透明
              如果提供冗余能力的话,还能保证硬件透明
        可扩展性
        复制透明
        迁移透明

MogileFS

MogileFS的架构

  MogileFS这个文件系统要想那么工作起来,他需要三种角色。第一个角色名称叫Tracker(就是我们所说的元数据节点),第二个角色叫database元数据的内容他没有存储的tracker节点的内存中,而是存储在外部的关系型数据库中,所以它适合于存储海量小文件,所以这个database要记得,他不是用存我们文件的数据的,而是存文件的元数据的。Tracker负责从database中检索元数据,或者向database中存储元数据,为了避免单个Tracker成为单点故障,于是可以多做几个Tracker,每一个Tracker都通过database获得整个文件系统全部的元数据。database故障了怎么办?所以人家MogileFS说了,我们是无单点的。至于mysql单不单点,那是mysql的问题,你要想办法把mysql部署成高可用的。每一个客户端实现数据存取时,他应该先去找Tracker获得元数据。Tracker会告诉你元数据是什么,文件在不在,以及存储在哪些存储节点上。因此Client Library我们客户端的API,并自行负责去找对应的Storage Node节点(叫存储节点),元数据的存取线路是database 和 Tracker 和 Storage Node之间进行,为什么Storage Node还要跟database打交道?因为一个用户来访问时,这个用户有没有权限访问数据,一个用户包括用户的所有的相关信息和认证信息,还有他的权限信息等等。访问权限都在database中存储。每一文件他有可能有什么读写访问权限等等。客户端请求响应线路是Client Library(客户端)于 Tracker 和 Storage Node之间进行,,节点直接完成副本冗余的线路是Storage Node 与 Storage Node之间进行的。

        MogileFS宣称MogileFS大体上有这个几种特性
              *应用层:无需特殊核心组件
                    因为他的整个文件存取协议,用的是http协议。所以说白了,MogileFS就是一个http存储,是基于http,dav协议的存储,那因此他就特别适用于放在web站点上,提供图片存储功能。第二,无单点失败。那大家说过了。
              *无单点失败
                    因为Tracker可以多做几个,Storage Node上数据是有副本的。
              *自动文件复制
                    一旦我们指明一个文件因该有多少副本以后,他会自动复制。
              *传输中立,无特殊协议
                    他是基于http协议完成数据存储,当然也支持nfs。默认用的是http。
              *简单命名空间
                    文件系统的文件在不同目录下就可以重名,在同一个目录下就不能重名。所以我们可以这么理解,每一个目录其实提供了一个名称空间。也就意味着每一个目录提供了一个可命名的空间,空间怎么取名都行,只要不一样。一个可取名的范围就叫做一个名称空间。而对于MogileFS来讲,它没有目录的概念,所以他用的是名称空间。
              *Shared-Nothing
                    表示不共享任何东西。
              *non-RAID
                    他们其实就是在文件系统级别通过副本冗余或者通过冗余一个数据分片来完成其可能性提供的或者冗余能力提供的。因此他不用RAID,而且也无需底层硬件提供RAID,任何磁盘都不用使用RAID机制,他自己能够在应用级别实现冗余的。
              *不能追加写、随机写
                    不能够追加写,不支持随机写。也就是说,一个文件创建以后,你不能说打开文件的尾部在增加几行不支持,要改你的整个文件全部都替换掉。所以说不支持追加写。他也不支持随机写,你不能拿到一个文件以后,这里边随机的去写一些数据,这也不允许的。所以他只支持顺序完整读写单一文件。
              * Tracker Client传输(miogilefsd),管理数据复制、删除、查询、修复以及监控
                    这是Tracker 的功能。
              *通过HTTP/WebDAV服务上传到Storage node(mogstored)(存储节点,而存储节点它的服务进程叫做mogstored,而用mysql来存储MogileFS的元数据)
              *MySQL存储MogileFS元数据(命名空间、位置)
                    (而用mysql来存储MogileFS的元数据,这些数据包括。命名空间、数据存储的位置,每一个数据分片它到底存储到了哪个节点上,还有哪个节点上有冗余等等。)

        MogileFS传输模式
                客户端来访问时,他的请求先到反向代理这,反向代理负责去找元数据服务器Tracker来获取元数据,然后反向代理再去找存储节点获得数据从而返回给客户端,所以对于客户端来讲,我只一次请求直接拿到结果,反向代理之后发生哪些复杂事情都不用管了。反向代理这里就成了一个统一的入口。那鱼是他带给我们的好处,我们以后就可以通过这么一个反向代理服务器向外输出这主机上存储的每一个图片的url,刚刚讲过,他本来就是http协议的,对不对。那么每一个图片我们都可以基于名称空间的方式进行遍历,比如说http://imgs.haohaoxuexi.com/下的你可以取一个名称空间。对应着每个路径就是个名称空间。比如我们取名字是shop商城的http://imgs.haohaoxuexi.com/shop/。shop低下是images图片http://imgs.haohaoxuexi.com/shop/images/1.jpg或者2.jpg就可以直接映射了。所以说我们在前端的,你的每一个主页中的每一个图形的url直接指的这个路径,最终可以直接送达给反向代理这个节点,由反向代理这个节点自动从你的分布式存储中结合来进行反馈客户端。那我各位应该明白,只要这个反向代理足够高性能,我们后端的这个存储节点就支持足够的文件io能力,我们就可以认为他是能够支撑更多的并发读写请求的。所以说这就是为什么很多站点通常会直接整合MogileFS作为其海量静态小文件不光是图片,还有像css,javascript等这种静态文件存储的一个重要原因。

        MogileFS高可用性
              MogileFS主要有两类节点,除了database之外,有两类,一类叫Tracker,一个类叫Storage Node。而Storage Node我们不用做高考用,因为他本来就能够完成通过数据副本或者通过数据块的冗余来提供其可用性。那因此Tracker如何提供其可用性?Tracker应该部署到哪些节点上?所以Tracker自己有可能成为单点故障所在, Database因为使用的是mysql,所以他单点不单点,我们先不问。对于Tracker而言我们如何去做高可用?可以这么做,比如我们有三个节点,每一个节点上都运行Storage Node进程从而他们就是数据节点,与此同时,我们把这三个节点上每一个节点都运行一个Tracker,所以他们又都是Tracker节点。所以客户端去请求时,请求任何一个节点的Tracker都能获得全局数据,因为他的所有原数据都在mysql中存放,主要买收购在就没有问题,mysql如何高可用自己解决。所以从而他就能知道这三个存储节点上哪儿存储的有数据从而在三个Tracker节点就做了高可用,这有三个是没有错的,而且找其中一个都行,但目的不是找其中任何一个,而是就找那么一个他只要能够给我提供服务就行,很容易的在前端使用nginx做反向代理。因为他们本身都是http服务,只不过用的不是80端口罢了,所以用nginx做反向代理以后,只要nginx能对后端主机的健康状态做检查,必要时不但能够对他们做高可用,还能做负载均衡。大量的原数据请求来了这三个节点都可以提供服务,任何一节点故障,另外一个节点都能提供,因为他们本身是无状态的,这里也无需保持连接,都是静态数据,还没有会话的时候。但是这时候nginx也是单点,所以也需要对nginx用keepliaved做高可用就可以了,所以这就是我们的MogileFS的使用架构。

        MogileFS核心概念
              在MogileFS中有Domain和calss概念,有Domain + fid的意思,只要理解了这三个基本元素,我们MogileFS的基本工作属性就有所了解。

              Domain(Domain我们称作一个域。这个域跟我们DNS的域是两回事儿。在MogileFS中Domain就是用来描述一个名称空间的。)
                    一个MogileFS可以有多个Domain
                          因此一个MogileFS可以有多个Domain(名称空间),比如说我们需要在MogileFS上存储三类文件,图片,css和javascript。所以我们可以建三个Domain。一个Domain中放图片。,一个Domain中放css。,一个Domain中放javascript文件,这是我们按文件类型划分的,其实我们也可以按站点划分。比如我们站点有手机站就是手机上的网页,有PC站就是正常的站点,这两种站点他们用的图片可能不一样,用的文件也可能不一样,所以我们把它分开,手机的站点用一个Domain, PC的站点用一个Domain。所以Domain到底按什么划分取决于我们的需要,但一个Domain就是一个名称空间。。。
                    用来存放不同文件(大小、类型)同一个Domain内, key必须唯
                          一个Domain内部,可以想象成一个目录,一般而言我们不会使用多级目录在MogileFS中,可以多建几个Domain,但尽量不要使用层级关系,所以一个Domain就是一个目录,所以在一个目录下,一个文件的文件名必须是唯一的,只不过在Domain内部使用的不叫文件名,而是叫文件的key,文件的数据叫文件的value,所以是key,value格式
                    不同Domain内, key可以相同
                          不同Domain内, key可以相同,同Domain内, key可以不能相同
              Class(一个Domain内部可以有多个calss,calss是实现文件属性管理的容器,主要作用是定义文件存储在不同的设备上的份数)
                    文件属性管理
                    定义文件存储在不同设备上的份数
                          说白了就是在讲,我们可以为一个数据块在多个地方提供冗余。这样讲对于数量过多的单个大方面来讲,一个文件它有10个G,我可以把10个G切成多个数据块,一个数据块可以多达1个G,或者小点128兆,但是对于存储海量小文件的程度来讲,一个图片十几k,你说怎么再切割?所以我们要反其道而行之,对单个大文件把他切割成多个数据块,来进行分散存储,每一个数据块做一个独立的单位,而且对他独立冗余。但是文件太小了,就将文件合起来成为一个数据块,只不过他不叫数据块而叫Class。所以他用于实现将多个小文件合并成一个单独被容余的复制单元。所以在一个class级别,一个class可能会存储多个副本,而不是在文件级别的副本的。因为每一个文件有副本,这个管理起来是因为不便,而且它的副本数量的太大。
              Domain + Fid定位文件
                    所以可以通过Domain + key来定义文件。只不过这个key通常在内部称作叫Fid。

安装和使用MogileFS

  使用centos7安装,这里一共准备了4个节点。node1,node2,node3,node4,我们计划把node2,node3,node4配置成MogileFS节点,而node2,node3,node4都即是Tracker又是Storage Node,node1做成nginx的反向代理节点还能支持MogileFS模块进行反代。MogileFS多数场景中,我们经常看到的应该是编译安装,这个好像是挺麻烦,
# 配置各节点用hosts文件解析;
[root@localhost ~]# vi /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.78.213  node1
192.168.78.216  node2
192.168.78.218  node3
192.168.78.215  node4

# 各server时间同步
[root@localhost ~]# grep ^server /etc/chrony.conf ;ssh node1 'grep ^server /etc/chrony.conf' ;ssh node3 'grep ^server /etc/chrony.conf'; 'grep ^server /etc/chrony.conf' ;ssh node4 'grep ^server /etc/chrony.conf';

[root@localhost ~]# chronyc sources;ssh node1 'chronyc sources';ssh node3 'chronyc sources';ssh node4 'chronyc sources'

[root@localhost ~]# date ;ssh node1 'date' ; ssh node3 'date'; ssh node4 'date'
#2020年 09月 26日 星期六 20:47:01 CST
#root@node1's password: 
#2020年 09月 26日 星期六 20:47:06 CST
#root@node3's password: 
#2020年 09月 26日 星期六 20:47:08 CST
#root@node4's password: 
#2020年 09月 26日 星期六 20:47:10 CST


# node2,3,4节点上,epel源进行安装(使用阿里云镜像下的epel)(MogileFS也是使用perl语言研发的,所以配得起来需要安装perl很多模块,而这个模块大多都在下epel源下)
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

# node2,3,4节点上,查看源仓库(epel/x86_64源已然识别)
[root@localhost ~]# yum repolist
已加载插件:fastestmirror
epel                                                                                                                   | 4.7 kB  00:00:00     
(1/3): epel/x86_64/group_gz                                                                                            |  95 kB  00:00:00     
(2/3): epel/x86_64/updateinfo                                                                                          | 1.0 MB  00:00:00     
(3/3): epel/x86_64/primary_db                                                                                          | 6.9 MB  00:00:00     
Loading mirror speeds from cached hostfile
 * base: mirrors.bfsu.edu.cn
 * extras: mirrors.163.com
 * updates: mirrors.cn99.com
源标识                                               源名称                                                                             状态
base/7/x86_64                                        CentOS-7 - Base                                                                    10,070
epel/x86_64                                          Extra Packages for Enterprise Linux 7 - x86_64                                     13,452
extras/7/x86_64                                      CentOS-7 - Extras                                                                     413
updates/7/x86_64                                     CentOS-7 - Updates                                                                  1,134
repolist: 25,069

# node2,3,4节点上,安装perl依赖,使用yum安装的时候这些依赖不会被解决,需要提前手动安装。
[root@localhost ~]# yum -y install perl-Sys-Syslog perl-Net-Netmask perl-IO-AIO

# node2,3,4节点上,centos7 查看包并安装MogileFS,并把包发送到别的节点(安装包地址http://www.qiuhom.com/d/383f34f57cf847beb296/      
和 自己的百度云盘      参考博客地址https://www.cnblogs.com/qiuhom-1874/p/13677279.html 和 自己的百度云盘)

[root@localhost ~]# ls
anaconda-ks.cfg                                         MogileFS-Utils-2.19-1.el7.centos.noarch.rpm
MogileFS-Server-2.46-2.el7.centos.noarch.rpm            perl-Danga-Socket-1.61-1.el6.rf.noarch.rpm
MogileFS-Server-mogilefsd-2.46-2.el7.centos.noarch.rpm  perl-MogileFS-Client-1.14-1.el7.centos.noarch.rpm
MogileFS-Server-mogstored-2.46-2.el7.centos.noarch.rpm  perl-Perlbal-1.78-1.el6.noarch.rpm

## 发送
[root@localhost ~]# scp ./*.rpm node3:/root/
[root@localhost ~]# scp ./*.rpm node4:/root/

## 安装
[root@localhost ~]# yum install ./*.rpm -y

### MogileFS-Server-2.46-2.el7.centos.noarch.rpm       #核心服务(tracker节点和storage节点依赖的公共组成部分)
### MogileFS-Server-mogilefsd-2.46-2.el7.centos.noarch.rpm       #tracker节点
### MogileFS-Server-mogstored-2.46-2.el7.centos.noarch.rpm       #storage节点(存储)
### MogileFS-Utils-2.19-1.el7.centos.noarch.rpm       #mogilefs的一些管理工具,如mogadm(你打算在哪个节点?做客户端配置做配置操作,就应该在哪个节点上安装这个个工具。)
### perl-Danga-Socket-1.61-1.el6.rf.noarch.rpm       #依赖包   Socket   (在epel源和base源中都没有)
### perl-MogileFS-Client-1.14-1.el7.centos.noarch.rpm       #客户端
### perl-Perlbal-1.78-1.el6.noarch.rpm      (MogileFS所依赖的,提供web服务的perl模块组件)

### mogilefs程序路径
主程序:/usr/bin/mogilefsd

命令行管理工具程序:/usr/bin/mogadm

主配置文件(Tracker):/etc/mogilefs/mogilefsd.conf

主配置文件(Storage Nodes):/etc/mogilefs/mogstored.conf

### 启动服务 (tracker 服务为 mogilefsd ) (storage服务为mogstored)

配置MogileFS

设置databases节点

# 安装mariadb数据库,MogileFS依赖mysql来实现元数据存储的。(这里没有部署mysql高可用,在这三个节点中随便找一个节点运行mysql当做mysql服务器,这里使用node2节点,)
[root@localhost ~]# yum -y install mariadb mariadb-server

# 配置mariadb数据库(首先要跳过名称解析,不然的话可能会遇到很多问题。)
[root@localhost ~]# vi /etc/my.cnf
[mysqld]
innodb_file_per_table = 1
skip_name_resolve = 1

# 启动mariadb数据库,配置开机自动启动
[root@localhost ~]# systemctl start mariadb
[root@localhost ~]# systemctl enable mariadb
Created symlink from /etc/systemd/system/multi-user.target.wants/mariadb.service to /usr/lib/systemd/system/mariadb.service.

# 授权一个root用户能够远程连接。(要授权一个账号,能够为MogileFS提供数据库,MogileFS连接数据库时,将使用这个用户账号来实现。我们这里授权的root用户做实验先无所谓)
MariaDB [(none)]> grant all on *.* to 'root'@'192.168.78.%' identified by 'fspass';
Query OK, 0 rows affected (0.00 sec)

# 创建一个数据库(这个数据库,MogileFS不会自动创建,需要我们手动创建。)
MariaDB [(none)]> create database mogilefs;
Query OK, 1 row affected (0.00 sec)

# 授权给moguser用户能访问mogilefs库中的所有数据,而且拥有所有权限,用户名和密码可以随便定义。( 让普通用户就让他访问这个库。而不要让管理员远程连接。)
MariaDB [(none)]> grant all on mogilefs.* to 'moguser'@'192.168.78.%' identified by 'mogpass';
Query OK, 0 rows affected (0.00 sec)

# 刷新授权表
MariaDB [(none)]> flush privileges;
Query OK, 0 rows affected (0.00 sec)

MogileFS指向数据库(设定数据库)

  三个节点都装了MogileFS具体哪个包提供的工具忘了,他提供了一个工具mogdbsetup他是MogileFS的数据库的安装设定工具,指明连到哪个数据库服务器上,指明数据库的密码是多少,然后他会初始化这个数据库服务器,并完成表创建。

[root@localhost ~]# mogdbsetup --dbhost=192.168.78.216 --dbrootpass='fspass' --dbuser='moguser' --dbpass='mogpass'

This will attempt to setup or upgrade your MogileFS database.
It won't destroy existing data.
Run with --help for more information.  Run with --yes to shut up these prompts.
(这将尝试设置或升级您的MogileFS数据库。它不会破坏现有数据。)
Continue? [N/y]: y(如果MogileFS已经运行了一段时间了,这个命令要慎用)


## --dbhost=        localhost(要连接的mysql数据库是谁?默认是本地localhost,我们刚才授权时没有授权localhost,我们授权的是192.168.78.%,所以他是连接不上数据库的。)
## --dbport=        dbd default (这边数据库的端口号。 默认是mysql默认端口号3306。)
## --dbname=        mogilefs(指明数据库的名字,默认mogilefs)
## --dbrootuser=    root(以哪个用户连接上数据库做初始化,默认是root用户)
## --dbrootpass=    <blank>(默认是空密码,如果有的话进行指明)
## --dbuser=        mogile(这是你连接mogilefs数据库的用户,默认是mogile,我们定义的是moguser)
## --dbpass=        <blank>(是指明moguser的密码)

# 连接到mysql上进行查看。(能看到那些表,就说明数据库已经初始化成功了。)
[root@localhost ~]# mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 5.5.65-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> use mogilefs;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mogilefs]> show tables;
+----------------------+
| Tables_in_mogilefs   |
+----------------------+
| checksum             |
| class                |
| device               |
| domain               |
| file                 |
| file_on              |
| file_on_corrupt      |
| file_to_delete       |
| file_to_delete2      |
| file_to_delete_later |
| file_to_queue        |
| file_to_replicate    |
| fsck_log             |
| host                 |
| server_settings      |
| tempfile             |
| unreachable_fids     |
+----------------------+
17 rows in set (0.00 sec)

启动MogileFS进程

  先启动Tracker进程,而后在启动各storage节点,对于Tracker进程来讲,有哪些Tracker节点,如果有一个就配置的一个,如果有多个就配置的多个,刚开始我们先配置一个。
# 修改Tracker节点配置文件(其他选项未修改。)
[root@localhost ~]# vi /etc/mogilefs/mogilefsd.conf 
db_dsn = DBI:mysql:mogilefs:host=192.168.78.216
db_user = moguser
db_pass = mogpass
listen = 0.0.0.0:7001

#################################################### 参数的注释##############
# 配置Tracker节点,定义配置文件(这是我们rpm包安装所指定的路径)
daemonize = 1      (是否运行为守护进程,那已经是服务了,那就应该运行为守护进程。设置为1表示启动为守护进程)

pidfile = /var/run/mogilefsd/mogilefsd.pid      ( PID文件)
dbdsn= DBI:mysql: mogilefs:host=172.16.100.67      (连接数据库的数据源的连接方式。基于DBI连接,是mysql数据库,数据库名字叫做mogilefs,指明主机地址:host=)
db user = moguser      (连接到mysql的用户)
db_pass = mogpass      (您觉得mysql的用户密码)

listen = 0.0.0.0:7001      (监听在本机所有地址的7001端口,mogilefs监听地址,监听在127.0.0.1表示只允许从本机登录进行管理)

conf_port = 7001
query_jobs = 5      (启动多少个查询工作线程。查询进程数量,当有大量客户端并发访问文件时,那我们就需要启动一个线程当做mysql客户端去连接mysql服务器,去检索这个文件是不是存在。如果存在的话,就把这个文件的元数据的取出来,所以query_jobs应该多一点,但是定义100个就有点夸张。我们测试定义3,5个就行。)
delete_jobs=1      (启动多少个删除工作线程。删除文件的进程数量,一般只有我们大量并行删除文件时才会用到很多。这个你按需调整就行。)
replicate_jobs = 2      (启动多少个复制工作线程。用来做复制的线程。创建一个新文件,都要给他同步地复制到其他可以复制的节点上去,这个数量按需定义就好了。如果节点非常大或者非常多了之后可以定义多。)
reaper_jobs = 1      (启动多少个用于回收资源的线程)

#min_free_space = 200      (最少空闲空间,一旦空闲空间不够了,可能会出现问题,我们还要添加额外的新空间和节点。)
#node_timeout = 2      (超时时间间隔,默认为两个秒钟。)
#conn_timeout = 2      (向存储节点发起连接请求的超时时间间隔,默认为两个秒钟。一般而言,我们可以认为,如果后端节点带宽足够大,而且是局域网内进行存储的,两秒钟这个时间其实已经不算短了,如果你是跨互联网的,是跨机房的话,有时间就可以调长一点。)

# 查看定义的信息,和安装的文件(安装所生成的文件中其脚本在/etc/rc.d/init.d/mogilefsd,所以我们直接运行mogilefsd就行。)
[root@localhost ~]# rpm  -ql MogileFS-Server-mogilefsd
/etc/rc.d/init.d/mogilefsd

# 启动MogileFS进程(第一次启动会报错,端口号已经监听,启动了两次,可能程序已经在跑了,没有报错,原因不明。注大意:systemctl stop mogilefsd停止不了,需要kill -9 掉)
[root@localhost ~]# systemctl start mogilefsd 

# 查看监听的端口(7001端口已在监听状态)
[root@localhost ~]# ss -tnl
State      Recv-Q Send-Q                          Local Address:Port                                         Peer Address:Port              
LISTEN     0      128                                         *:22                                                      *:*                  
LISTEN     0      128                                         *:7001                                                    *:*                  
LISTEN     0      100                                 127.0.0.1:25                                                      *:*                  
LISTEN     0      50                                          *:3306                                                    *:*                  
LISTEN     0      128                                        :::22                                                     :::*                  
LISTEN     0      100                                       ::1:25                                                     :::*         

node2,3,4节点上,安装storage节点,(Storage node(mogstored)存储节点)(将来真正存储时,你应该创建有一个专门的分区,然后把它挂在至某个指定路径下。然后在给他创建dev1)

  mogstored存储节点应该不止一个了,所以我们打算在三个节点上分别执行如下操作。
# 创建设备目录(建一个目录 ( 并且需要 mount 一个硬盘给这个目录 ) 给这个"设备" 使用, 我们这的例子是使用dev1在主机中建一个目录,建目录使用 dev + ID 这种格式,记的所有系统中 ID 不能重复.也必须和配置文件中的路径一样。      mogstored把每一个设备上的存储目录下创建的dev1当做一个设备,这个文件是必须叫这个名字的,而前面这个路径可以在任何位置,而且第1个节点叫dev1,第2个节点就只能叫dev1,以此类推。一个节点一般而言只提供一个存储空间,所以每一个dev就是相对于节点来讲的)

##node2
[root@localhost ~]# mkdir -pv /data/mogilefs/dev1
mkdir: 已创建目录 "/data"
mkdir: 已创建目录 "/data/mogilefs"
mkdir: 已创建目录 "/data/mogilefs/dev1"

##node3
[root@localhost ~]# mkdir -pv /data/mogilefs/dev2
mkdir: 已创建目录 "/data"
mkdir: 已创建目录 "/data/mogilefs"
mkdir: 已创建目录 "/data/mogilefs/dev2"

##node4
[root@localhost ~]# mkdir -pv /data/mogilefs/dev3
mkdir: 已创建目录 "/data"
mkdir: 已创建目录 "/data/mogilefs"
mkdir: 已创建目录 "/data/mogilefs/dev3"

# 给目录授权,修改属主,属组。(对MogileFS而言,他的运行进程是以mogilefs身份运行的。所以我们还需要把这个目录的权限,属主,属组改为mogilefs用户,和mogilefs组。)
[root@localhost ~]# chown -R mogilefs:mogilefs /data/mogilefs/
[root@localhost ~]# ll /data/mogilefs/
总用量 0
drwxr-xr-x. 2 mogilefs mogilefs 6 9月  26 23:18 dev1

# 编辑配置文件(定义参数)(只修改这一条路径即可,注意dev不要动他,就放在此处即可。其他的默认。)
[root@localhost ~]# vi /etc/mogilefs/mogstored.conf 
docroot = /data/mogilefs/

#################################################### 参数的注释##############
maxconns = 10000      (存储系统的最大连接数。定义其支持的最大并发连接数)
httplisten 0.0.0.0:7500      (可通过http访问的服务端口。监听的http端口的)
ngmtlisten = 0.0.0.0:7501      (mogilefs的管理端口。管理端口)
docroot =/data/mogilefs/      (该项决定了数据的在storage上存储的实际位置,建议使用的是一个单独挂载使用的磁盘。文档存储根目录,这个根目录就是dev文件所在的父目录)

# 查看服务脚本(他的服务脚本就叫做mogstored。)
[root@localhost ~]# rpm -ql MogileFS-Server-mogstored
/etc/rc.d/init.d/mogstored


############################################################ 这个程序包修复了需要手动创建pid文件,以下弃用##########
# 编辑服务器脚本配置文件(这个脚本早期写的时候是只适用于centos6上跑的对centos7来讲他的判断方式是有区别的,即便服务能正常启动起来,他也总是会报错,为了避免这个问题,还需要编辑一下他这个配置文件)
[root@localhost ~]# vi /etc/rc.d/init.d/mogstored

lockfile=${LOCKFILE-/var/lock/subsys/mogstored}
pidfile=/var/run/mogilefsd/mogstored.pid      (在这儿他需要一个pid文件,这个文件事实上并不存在,一旦启动成功了他会创建一个锁文件。我们这里直接给他手动创建pid文件,touch ${lockfile}这里会创建锁文件 && pidof $prog > $pidfile,而且我们停止服务器还要删除文件)
RETVAL=0

start() { 
        ulimit -n 65535
        echo -n $"Starting mogstored"
        su - mogilefs -c "$prog -c $configfile --daemon"  &> /dev/null
        RETVAL=$?
        [ $RETVAL = 0 ] && success && touch ${lockfile} && echo &(pidof mogstored)  > $pidfile || failure      (这里创建pid文件,命令替换&(pidof mogstored)取得真正运行的这个mogstored进程的pid,直接覆盖写入$pidfile中去)

stop() {
        echo -n $"Stopping mogstored"
        netstat -nlp|grep "mogstored"|grep -v grep|awk '{print $7}'|awk -F"/" '{print $1}'|xargs kill -9
        RETVAL=$?
        [ $RETVAL = 0 ] && success && rm -f ${lockfile} ${pidfile} || failure      (这里删除pid文件 rm -f ${lockfile} ${pidfile} )
############################################################ 这个程序包修复了需要手动创建pid文件,以上弃用##########

# 直接启动服务,并查看监听端口(启动无任何报错,7500端口,和7501端口以监听)
[root@localhost ~]# systemctl start mogstored
[root@localhost ~]# ss -tnl
State      Recv-Q Send-Q                          Local Address:Port                                         Peer Address:Port              
LISTEN     0      128                                         *:7500                                                    *:*                  
LISTEN     0      128                                         *:7501                                                    *:*                  
LISTEN     0      128                                         *:22                                                      *:*                  
LISTEN     0      128                                         *:7001                                                    *:*                  
LISTEN     0      100                                 127.0.0.1:25                                                      *:*                  
LISTEN     0      50                                          *:3306                                                    *:*                  
LISTEN     0      128                                        :::22                                                     :::*                  
LISTEN     0      100                                       ::1:25                                                     :::*                  

将mogstored存储节点添加进Tracker(就是我们所说的元数据节点)

  mogstored存储节点这些设备要想能够在Tracker上被使用或者被识别的话,那我们还需要将设备添加进Tracker当中去或者添加到Tracker所能够识别的节点当中去。
# 检测节点(在Tracker节点上查看)(检查MogileFS world当前的状态信息)
[root@localhost ~]# mogadm check
Checking trackers...
  127.0.0.1:7001 ... OK      (默认连接至当前的Tracker节点127.0.0.1的7001说ok)

Checking hosts...      
No devices found on tracker(s).      (不过现在没有任何devices(设备)因为我们的确没有添加任何devices)


# 指明Tracker节点是谁(检查这个主机是否加入到 MogileFS 的系统中。--trackers选项指明Tracker节点是谁,如果这个选项不给的话,他就连接127.0.0.1的7001端口,我们还可以设置多个,第1个找不到就找第2个。这是我们提供多个Tracker节点时的用法,也可以使用check检查连接到Tracker节点的状态信息)
[root@localhost ~]# mogadm --trackers=192.168.78.216:7001
[root@localhost ~]# mogadm --trackers=192.168.78.216:7001 check
Checking trackers...
  192.168.78.216:7001 ... OK      (连接至192.168.78.216节点的7001端口,也是ok)

Checking hosts...
No devices found on tracker(s).      (No devices,依然是没有发现任何设备。)

# 列出当前节点(检查这个主机是否加入到 MogileFS 的系统中。当前没有任何节点)
[root@node2 ~]# mogadm host list


# 添加主机(添加主机,将主机信息注册到数据库中。添加节点,并列出当前节点)(将mogstored存储节点添加至MogileFS集群中去)(可以使用--trackers指明Tracker节点,这个选项可以不用指,add后面主机名字是必需要给的,我们这里不使用名字就是用ip来识别它更便捷关键是当名字来用了,--status选择不指定alive它就变成down了,默认是down,同样的方式,把另外两个节点也加进来。注意,添加别的节点的时候Tracker节点地址不要改变,指定的依然需要是Tracker节点地址) 
[root@node2 ~]# mogadm --trackers=192.168.78.216:7001 host add 192.168.78.216 --ip=192.168.78.216 --status=alive
[root@node2 ~]# mogadm --trackers=192.168.78.216:7001 host add 192.168.78.215 --ip=192.168.78.215 --status=alive
[root@node2 ~]# mogadm --trackers=192.168.78.216:7001 host add 192.168.78.218 --ip=192.168.78.218 --status=alive

#################################################### 参数的注释##############
## mogadm host add <hostname> [opts]       (添加主机,将主机信息注册到数据库中。mogadm host add指定名称和选项,如果我们主机有名称,比如node1,node2,他只是一个识别符没有什么太大意义)
##  --altip=s      (说白了就是我们这台主机还有另外一个ip,有些时候一个主机确实会有多个ip地址,所以一个IP连接不到时,还有一个备用IP,但这个基本上就不用额外专门指明了)
## --ip=s      (指明真正运行了的mogstored存储提供服务的ip地址)
## --port=i      (端口如果不指,默认他有7500,7501相关的端口,他的http port使用的7500的端口)
## --status=s       (表示它的状态,默认是down,表示你的主机添加进来,他也不会被使用。如果要让它转换可用则转换为alive)


[root@node2 ~]# mogadm host list
192.168.78.216 [1]: alive      (状态是alive)
  IP:       192.168.78.216:7500      (加进来集群第1个主机。192.168.78.216的7500)

192.168.78.215 [2]: alive      (状态是alive)
  IP:       192.168.78.215:7500      (加进来集群第2个主机。192.168.78.216的7500)

192.168.78.218 [3]: alive      (状态是alive)
  IP:       192.168.78.218:7500      (加进来集群第3个主机。192.168.78.216的7500)

# 删除主机(如果报错是因为,信息保存再数据内,需要删除)
[root@node2 ~]# mogadm host delete <主机IP>

# 修改主机(暂时停止服务器      如果你需要维护一个服务器,比如更新内存,升级操作系统之类的需要关机的操作,推荐你在操作之前先设置这些为 "down". MogileFS 对这种偶然的故障可以很弹性的处理)      (如果你不小心写错了,可以修改主机IP,端口,alive或者down,只要指定好节点就行。比如把节点的状态--status=down改为down,如果单纯更改--status为down使用mogadm host mark效果是一样的)(添加完节点以后就可以管理节点上的设备)
[root@node2 ~]# mogadm host modify

[root@node2 ~]# mogadm host mark 192.168.78.215 down
[root@node2 ~]# mogadm host list
192.168.78.216 [1]: alive
  IP:       192.168.78.216:7500

192.168.78.215 [2]: down      (状态修改为down)
  IP:       192.168.78.215:7500

192.168.78.218 [3]: alive
  IP:       192.168.78.218:7500

[root@node2 ~]# mogadm host mark 192.168.78.215 alive
[root@node2 ~]# mogadm host list
192.168.78.216 [1]: alive
  IP:       192.168.78.216:7500

192.168.78.215 [2]: alive      (状态改为alive)
  IP:       192.168.78.215:7500

192.168.78.218 [3]: alive
  IP:       192.168.78.218:7500

管理节点上的设备

# 查看现今的所有存储设备      (检查我们加入的"设备"信息,这样就能见到上面这个设备了.还能显示加入的大小。      现在是没有存储设备的状态)
[root@node2 ~]# mogadm device list
192.168.78.216 [1]: alive
                    used(G)    free(G)   total(G)  weight(%)

192.168.78.215 [2]: alive
                    used(G)    free(G)   total(G)  weight(%)

192.168.78.218 [3]: alive
                    used(G)    free(G)   total(G)  weight(%)

# 添加存储设备并查看(给"设备"加入"存储的节点"当中,相当于为每个设备加入 MogileFS 的存储系统。)
[root@node2 ~]# mogadm device add
[root@node2 ~]# mogadm device add 192.168.78.216 1
[root@node2 ~]# mogadm device add 192.168.78.218 2
[root@node2 ~]# mogadm device add 192.168.78.215 3

## dev2,dev3添加错误进行修改(添加错的存储设备,从数据库中删除,否者重新添加不了)
MariaDB [mogilefs]> select * from device;
+-------+--------+--------+--------+----------+---------+------------+
| devid | hostid | status | weight | mb_total | mb_used | mb_asof    |
+-------+--------+--------+--------+----------+---------+------------+
|     1 |      1 | alive  |    100 |    51175 |    1238 | 1601200795 |
|     2 |      3 | alive  |    100 |     NULL |    NULL |       NULL |
|     3 |      2 | down   |     10 |     NULL |    NULL |       NULL |
+-------+--------+--------+--------+----------+---------+------------+
3 rows in set (0.00 sec)

MariaDB [mogilefs]> delete from device where devid=2;
Query OK, 1 row affected (0.01 sec)

MariaDB [mogilefs]> delete from device where devid=3;
Query OK, 1 row affected (0.00 sec)

MariaDB [mogilefs]> select * from device;
+-------+--------+--------+--------+----------+---------+------------+
| devid | hostid | status | weight | mb_total | mb_used | mb_asof    |
+-------+--------+--------+--------+----------+---------+------------+
|     1 |      1 | alive  |    100 |    51175 |    1238 | 1601200848 |
+-------+--------+--------+--------+----------+---------+------------+
1 row in set (0.00 sec)

##  删除主机并重新添加主机(如果需要)
[root@node2 ~]# mogadm  host delete 192.168.78.218
[root@node2 ~]# mogadm  host delete 192.168.78.215
[root@node2 ~]# mogadm host list
192.168.78.216 [1]: alive
  IP:       192.168.78.216:7500

[root@node2 ~]# mogadm --trackers=192.168.78.216:7001 host add 192.168.78.218 --ip=192.168.78.218 --status=alive
[root@node2 ~]# mogadm --trackers=192.168.78.216:7001 host add 192.168.78.215 --ip=192.168.78.215 --status=alive
[root@node2 ~]# mogadm host list
192.168.78.216 [1]: alive
  IP:       192.168.78.216:7500

192.168.78.215 [2]: alive
  IP:       192.168.78.215:7500

192.168.78.218 [3]: alive
  IP:       192.168.78.218:7500

## 重新添加(注意存储节点和Tracker节点,都需要关闭防火墙(不会配置防火墙就关闭),可以看到3个设备各有50G空间可用,可以想象一下存储数据时,是不是有150g空间可用了?不是的,要看你一个数据,冗余了几个副本。1个数据冗余了1一个副本其实你只有75G可用,一个数据冗余2个副本只有50G可用了,所以事实上有多大的空间可用,取决于我们副本冗余量而不是取决于空间总和的。)
[root@node2 ~]# mogadm device add 192.168.78.218 2
[root@node2 ~]# mogadm device add 192.168.78.218 2
[root@node2 ~]# mogadm device list
192.168.78.216 [1]: alive            (现在dev1已经在了,他会自动识别为dev1,used以用多少G,free空闲多少G,total一共多少G空间。这个total其实是,你的真正/data/mogilefs/目录,所在设备的空闲空间,因为我们是在根分区下创建的所以他这个识别的是应该是根分区的可用空闲空间)
                    used(G)    free(G)   total(G)  weight(%)
   dev1:   alive      1.213     48.763     49.976        100

192.168.78.215 [2]: alive
                    used(G)    free(G)   total(G)  weight(%)
   dev2:   alive      1.075     48.900     49.976        100

192.168.78.218 [3]: alive
                    used(G)    free(G)   total(G)  weight(%)
   dev3:   alive      1.067     48.908     49.976        100


[root@node2 ~]# df -lh
文件系统                 容量  已用  可用 已用% 挂载点
/dev/mapper/centos-root   50G  1.3G   49G    3% /      (根分区空闲空间,可用49G)
devtmpfs                 454M     0  454M    0% /dev
tmpfs                    465M     0  465M    0% /dev/shm
tmpfs                    465M   13M  452M    3% /run
tmpfs                    465M     0  465M    0% /sys/fs/cgroup
/dev/sda1               1014M  125M  890M   13% /boot
/dev/mapper/centos-home   74G   33M   74G    1% /home
tmpfs                     93M     0   93M    0% /run/user/0
tmpfs                     93M     0   93M    0% /run/user/997


[root@node2 ~]# mogadm check
Checking trackers...
  127.0.0.1:7001 ... OK      (对应的trackersok)

Checking hosts...      (三个节点ok)
  [ 1] 192.168.78.216 ... OK
  [ 2] 192.168.78.215 ... OK
  [ 3] 192.168.78.218 ... OK

Checking devices...      (每个host上的设备dev1,dev2,dev3都是ok的,而且她的ob state当前状态是writeable 可写入数据数据的,那因此我们可以在里边直接填充数据就ok,再次提醒以后的dev1,dev2,dev3应该是我们自己专门创建的一个至少是一个分区,把这个分区挂载至指定的路径下,在创建dev,她这里识别实际上是一个根分区,他这里识别的空间是一个根分区所在的那个真正的磁盘空间。)
  host device         size(G)    used(G)    free(G)   use%   ob state   I/O%
  ---- ------------ ---------- ---------- ---------- ------ ---------- -----
  [ 1] dev1            49.976      1.214     48.762   2.43%  writeable   N/A
  [ 2] dev2            49.976      1.079     48.896   2.16%  writeable   N/A
  [ 3] dev3            49.976      1.068     48.907   2.14%  writeable   N/A
  ---- ------------ ---------- ---------- ---------- ------
             total:   149.927      3.361    146.565   2.24%      (total总空间149.927,以使用总空间3.361,可使用总空间146.565)


#################################################### 参数的注释##############
  mogadm device add <hostname> <devid> [opts]        <hostname>添加哪个主机上的,devid的id号,[opts]指明选项

      <devid>              Numeric devid.  Never reuse these.      (这个ID至关重要,一定要跟你的节点和dev1,2,3相匹配,相对应。)
      <hostname>           Hostname to add a device
      --status=s           One of 'alive' or 'down'.  Defaults to 'alive'.      (选项当中最重要的就是--status,默认就是alive,这个可以不用指了,关键是添加<hostname>哪一个主机上的,<devid>哪一个设备上的id)


标记失效的设备,当硬盘坏了,设备有问题时,这时会自动在一个域内复制到最小设置的保存份数.恢复上面一样在一次 add 设备就好了

  mogadm device mark <storage_node_name> <storage_node_name> ID dead

只读模式和耗尽(Drain) 模式

  如果你想要冻结设备上所有的文件,你要使用只读模式就行了。这将停掉 MogileFS 存放新文件到这个设备上,但它也将阻止删除文件.代替的删除的操作是会给这些内容放到队列中等待为您标记为'alive'或'drain'。

  mogadm device mark mystorage 5 readonly

  mogadm device mark mystorage 5 drain

  耗尽(Drain) 模式, 在 2.40 和更高以上,告诉 MogileFS 不会有新的文件应写入设备. 但是在耗尽(Drain) 模式,文件可能被删除.所以如果你不希望写文件到这个设备上,可以设置为drain 的模式

        注:耗尽(Drain) 模式在 MogileFS 的早期版本,,将会从设备删除 FIDS.现在它已经被重新均衡的功能取代。

重新复制文件

  如果有一个硬盘坏了,MogileFS 可以自动的让请求不在访问这个设备,但是不会自动的重新复制这个硬盘的文件,你必须通过 mogadm 来手工来标志成 'dead'. 只要你这样做, MogileFS 将开始删除设备上的文件,并试图在集群间重新复制它们到其它的设备上.

mogadm domain(域)

  说白了就是定义和管理名称空间的。
# 查看域(现在没有任何域)
[root@node2 ~]# mogadm domain list
 domain               class                mindevcount   replpolicy   hashtype
-------------------- -------------------- ------------- ------------ -------

# 添加和管理域,并查看(看需要,看划分格式,比如放图片,和文件,划分两个命名空间,如果还有其他的继续创建即可。)

## 添加
[root@node2 ~]# mogadm domain add images
[root@node2 ~]# mogadm domain add files

## 检查"域",显示存在的域
[root@node2 ~]# mogadm domain list
或
[root@node2 ~]# mogadm class list
 domain               class                mindevcount   replpolicy   hashtype      (可以看到现在有两个域,一个files,一个images,而且在每一个域内自动创建了一个class名字叫default,并且还指明了这个default他的mindevcount最小复制单位MultipleHosts(),这里2个副本数,着就以为这每一个class至少有一个副本,所以他至少要存储在两个设备。replpolicy复制策略机主机做复制策略。hashtype她的hash类型是none不做哈希,直接使用id做键)
-------------------- -------------------- ------------- ------------ -------
 files                default                   2        MultipleHosts() NONE   
 
 images               default                   2        MultipleHosts() NONE 


## 使用格式
  mogadm domain add <domain>                         (指明domain名称就行,名称不要重复)

# 删除域
[root@node2 ~]# mogadm domain delete files
[root@node2 ~]# mogadm domain list
 domain               class                mindevcount   replpolicy   hashtype
-------------------- -------------------- ------------- ------------ -------
 images               default                   2        MultipleHosts() NONE   


mogadm class(管理,最小的复制单元)(class添加完了,就可以使用命令来完成文件上传了)

  class是实现文件属性管理的容器,主要作用是定义文件存储在不同的设备上的份数
# 添加class,并查看(在哪个域里添加,域必须要给,这里假如在images中添加,第一个class我们就以图片类型分类,叫jpeg,就是jpeg图片)
[root@node2 ~]# mogadm class add images jpeg
[root@node2 ~]# mogadm class add images png
[root@node2 ~]# mogadm class add images gif
[root@node2 ~]# mogadm class list
 domain               class                mindevcount   replpolicy   hashtype
-------------------- -------------------- ------------- ------------ -------
 files                default                   2        MultipleHosts() NONE

 images               default                   2        MultipleHosts() NONE            (没指明class都在defaul中)
 images               gif                       2        MultipleHosts() NONE            (指明了class就在对应的class下)
 images               jpeg                      2        MultipleHosts() NONE   
 images               png                       2        MultipleHosts() NONE 

# 在"域"中建"类",并加入最小保存份数(限制mindevcount为1个。假如我们某一个域我们不想做冗余)
[root@node2 ~]# mogadm class add files plaintext --mindevcount=1
[root@node2 ~]# mogadm class list
 domain               class                mindevcount   replpolicy   hashtype
-------------------- -------------------- ------------- ------------ -------
 files                default                   2        MultipleHosts() NONE   
 files                plaintext                 1        MultipleHosts() NONE   

 images               default                   2        MultipleHosts() NONE   
 images               gif                       2        MultipleHosts() NONE   
 images               jpeg                      2        MultipleHosts() NONE   
 images               png                       2        MultipleHosts() NONE 

# 设置至少保存的副本
[root@node2 ~]# mogadm class add files html  --replpolicy=MultipleHosts(3)
[root@node2 ~]# mogadm class list
 domain               class                mindevcount   replpolicy   hashtype
-------------------- -------------------- ------------- ------------ -------
 files                default                   2        MultipleHosts() NONE   
 files                html                      2        MultipleHosts(3) NONE   
 files                plaintext                 1        MultipleHosts() NONE   

 images               default                   2        MultipleHosts() NONE   
 images               gif                       2        MultipleHosts() NONE   
 images               jpeg                      2        MultipleHosts() NONE   
 images               png                       2        MultipleHosts() NONE

# 删除class并查看
[root@node2 ~]# mogadm class delete images jpeg
[root@node2 ~]# mogadm class delete images png
[root@node2 ~]# mogadm class delete images gif
[root@node2 ~]# mogadm class list
 domain               class                mindevcount   replpolicy   hashtype
-------------------- -------------------- ------------- ------------ -------
 files                default                   2        MultipleHosts() NONE   
 files                html                      2        MultipleHosts(3) NONE   
 files                plaintext                 1        MultipleHosts() NONE   

 images               default                   2        MultipleHosts() NONE   

# 删除域(domain)并查看(images命名空间没有了)
[root@node2 ~]# mogadm domain delete images
[root@node2 ~]# mogadm domain list
 domain               class                mindevcount   replpolicy   hashtype
-------------------- -------------------- ------------- ------------ -------
 files                default                   2        MultipleHosts() NONE   
 files                html                      2        MultipleHosts(3) NONE   
 files                plaintext                 1        MultipleHosts() NONE   
#################################################### 参数的注释##############

## --mindevcount=i       (要求必需要大于等于1)
## --replpolicy=s      (指明 MultipleHosts(3)表示至少要在三台主机上保存,相当于--mindevcount=3,这二者意义是一样。)
## --hashtype=s      (做文件的校验值检测时用哪种算法,有MD5或NNONE,none表示不检测)

上传文件(mogupload)

  class添加完了,就可以使用命令来完成文件上传了。      我们可以简单的使用 mog 开头的系列 Linux 命令, 来进行管理, 当然也可以用 Client 的 API来管理. 新的 MogileFS 的工具, 拆分成多个了. 下面这些命令, 都需要在/etc/mogilefs/mogilefs.conf 中指定 trackers , 不然就需要在下面的命令都加上 –trackers来指定.
# 上传文件(需要指定--trackers地址是多少,--key='/fstab.txt'就放在/fstab.txt这个域内,上传的文件名,可以跟本地文件名不一致,--files='/etc/fstab'把本地的/etc/下的fstab文件上传上去,--class=pilintext指定在哪个名称空间)
[root@node2 ~]# mogupload --trackers=192.168.78.216:7001 --domain=files --key='/fstab.txt' --file='/etc/fstab' --class=plaintext

# 上传图片(注意key指定的名字加不加路径,非常影响后面nginx反向代理范围)
[root@node2 ~]#  mogupload --trackers=192.168.78.216:7001 --domain=images --class=jpeg --key='/linux.jpg' --file='/root/Linux2.jpg'

#################################################### 参数的注释##############
## mogupload(命令顾名思义上传文件,--trackers=host指明trackers,--domain=foo指明向哪个域内上传文件,--key='/hello.jpg'指明key,key指的是你上传到远程这个域中以后,我们访问路径是什么?由于MogileFS是基于http协议的,所以这里给的路径这个根相当于MogileFS的根,而跟文件系统的根没有任何关系,而他的访问路径也是类似于使用http协议哪个指令对应路径的根 --file='./hello.jpg'指明file,这个file是自己本身文件路径看你放哪了) 
Usage: /usr/bin/mogupload --trackers=host --domain=foo --key='/hello.jpg' --file='./hello.jpg'

## --class=<class>      (指明class,如果不指的话,默认使用default)

下载文件(mogfetch)

查看文件相关信息(mogfileinfo)

# 查询文件。例如查询一个叫 /fstab.txt的 key .在指定的 files 中.      查看/fstab.txt文件相关信息
[root@node2 ~]# mogfileinfo --trackers=192.168.78.216:7001 --domain=files --key='/fstab.txt'
- file: /fstab.txt
     class:            plaintext
  devcount:                    1      (我们设定了,这个plaintex类的最小devcount为1,所以他只存了一份。)
    domain:                files
       fid:                    4
       key:           /fstab.txt
    length:                  541
 - http://192.168.78.215:7500/dev2/0/000/000/0000000004.fid      (访问路径,在浏览器中可以直接访问,前提注意防火墙,指明了文件在192.168.78.215这台主机上,这自动内部映射存储在dev2上,这可能是内部的均衡策略,这因为我们设定了,这个plaintex类的最小devcount为1,存储了一份,所以显示一个fid,如果存储三个会显示三个fid。)

# 查看/linux.jpg图片相关信息
[root@node2 ~]# mogfileinfo --trackers=192.168.78.216:7001 --domain=images --key='/linux.jpg'
- file: /linux.jpg
     class:                 jpeg
  devcount:                    1
    domain:               images
       fid:                    6
       key:           /linux.jpg
    length:                10530
 - http://192.168.78.218:7500/dev3/0/000/000/0000000006.fid

#################################################### 参数的注释##############
## 查看哪个--trackers内的,查看哪个--domain=foo域的--key='/hello.jpg'key是什么
Usage: /usr/bin/mogfileinfo --trackers=host --domain=foo --key='/hello.jpg'

列出所有文件fid(moglistfids)

  这个 fromfid 是指 mogileFS 内部的文件 id , 这个是自增的, 你可以指定一个开始的位置, 指定显示多少文件, 这个用于查询指定时间段内上传的文件时很有用. 比如我们在 8 点上传了一个文件, 是 100 的 id, 我们可以查询到从这个之后的所有的 id 的文件.
# 列出所有fid
[root@node2 ~]# moglistfids --trackers=192.168.78.216:7001 
fid 5
class plaintext
devcount 1
domain files
key /fstab.txt
length 541

fid 6
class jpeg
devcount 1
domain images
key /linux.jpg
length 10530

# 列出所有fid,指定只显示1个
[root@node2 ~]# moglistfids --trackers=192.168.78.216:7001 --count=1
fid 5
class plaintext
devcount 1
domain files
key /fstab.txt
length 541

#  列出从fid 5以后的
[root@node2 ~]# moglistfids --trackers=192.168.78.216:7001 --fromfid=5
fid 6
class jpeg
devcount 1
domain images
key /linux.jpg
length 10530

#################################################### 参数的注释##############
指明--fromfid=123从哪个fid开始,列出所有,如果有一个文件那就麻烦大了,--count=5000可以设定只显示多少
Usage: /usr/bin/moglistfids --trackers=host --fromfid=123 --count=5000

列出指定域下可用的文件(key)(moglistkeys列出所有的文件 key)(--key_prefix='bar/'可以做过滤,比如指明哪些开头的)

# 列出来在images命名空间下所有文件(所有key)
[root@node2 ~]# moglistkeys --trackers=192.168.78.216:7001 --domain=images
/linux.jpg

# 列出来在files命名空间下所有文件(所有key)
[root@node2 ~]# moglistkeys --trackers=192.168.78.216:7001 --domain=files
/fstab.txt

#################################################### 参数的注释##############
Usage: /usr/bin/moglistkeys --trackers=host --domain=foo --key_prefix='bar/'

删除文件(mogdelete)

# 删除--domain=images这个命名空间下的/linux.jpg这个图片并查看(没有文件了,报错找不到了)
[root@node2 ~]# mogdelete --trackers=192.168.78.216:7001 --domain=images --key='/linux.jpg'
[root@node2 ~]# moglistkeys --trackers=192.168.78.216:7001 --domain=images
Error listing files: none_match No keys match that pattern and after-value (if any).

# 删除class

直接针对MogileFS数据库计算慢速统计信息的实用程序(mogstats)

  它实际上是以连接mysql服务器的客户端工具。
# 显示指定devices,files状态信息(首先通过DBI连到mysql数据库上对应的数据库叫做mogilefs,服务器节点是192.168.78.216,数据库用户名moguser,数据库用户密码mogpass)
[root@node2 ~]# mogstats --db_dsn="DBI:mysql:mogilefs:host=192.168.78.216" --db_user="moguser" --db_pass="mogpass" --verbose --stats="devices,files"
Fetching statistics... (devices,files)
... files stats...
... done
... per-device stats...
... done

Statistics for devices...
  device     host                   files     status
  ---------- ---------------- ------------ ----------
  dev3       192.168.78.218          2      alive      (这里会显示存储数据的节点。这里可以看到在这里节点上存储了两个文件,如果有多个节点存储数据,这里会显示多个节点)
  ---------- ---------------- ------------ ----------

Statistics for files...
  domain               class           files    size (m)  fullsize (m)
  -------------------- ----------- ---------- ----------- -------------
  files                plaintext           1           0             0      (文件在files命名空间下的plaintext有一个文件)
  images               jpeg                1           0             0      (文件在images命名空间下的jpeg有一个文件)
  -------------------- ----------- ---------- ----------- -------------

done

# 显示全部的状态信息(包括主机的,域的,等等等)
[root@node2 ~]# mogstats --db_dsn="DBI:mysql:mogilefs:host=192.168.78.216" --db_user="moguser" --db_pass="mogpass" --verbose --stats="all"Fetching statistics... (all)
... replication stats...
... done
... replication queue stats...
... done
... delete queue stats...
... done
... files stats...
... done
... per-device stats...
... done
... fid stats...
... done
... queue stats...
... done

Statistics for devices...
  device     host                   files     status
  ---------- ---------------- ------------ ----------
  dev3       192.168.78.218          2      alive
  ---------- ---------------- ------------ ----------

Statistics for file ids...
  Max file id: 6

Statistics for files...
  domain               class           files    size (m)  fullsize (m)
  -------------------- ----------- ---------- ----------- -------------
  files                plaintext           1           0             0
  images               jpeg                1           0             0
  -------------------- ----------- ---------- ----------- -------------

Statistics for replication...
  domain               class        devcount      files
  -------------------- ----------- ---------- ----------
  files                plaintext           1          1
  images               jpeg                1          1
  -------------------- ----------- ---------- ----------

Statistics for replication queue...
  status                      count
  -------------------- ------------
  deferred                        1
  -------------------- ------------

Statistics for delete queue...
  status                      count
  -------------------- ------------
  -------------------- ------------

Statistics for general queues...
  queue           status                      count
  --------------- -------------------- ------------
  --------------- -------------------- ------------

done

#################################################### 参数的注释##############
能够显示整个MogileFS统计数据,用法--db_dsn="DBI:mysql:mfs:host=mfshost"指明怎么去连接mysql数据库,所以他是从mysql数据库中检索出数据并显示,--db_user="mfs"指明数据库用户名。--db_pass="mfs"指明数据库用户密码。--verbose表示显示详细信息,--stats="devices,files"指明显示哪些状态,指明devices,files表示只显示devices和files
mogstats --db_dsn="DBI:mysql:mfs:host=mfshost" --db_user="mfs" \
                      --db_pass="mfs" --verbose --stats="devices,files"

指明--stats="all"表示显示所有状态。
mogstats --stats="all"

Nginx 作为 MogileFS的前端客户端

  使用Nginx 反向代理MogileFS,说白了就是用户向Nginx发请求,Nginx 能够把请求转给MogileFS,然后还能自动从MogileFS作为客户端取得数据之后通过Nginx的本地路径来进行展示。这个需要使用到一个模块,这个模块叫nginx-mogilefs-module,这个模块不是Nginx自带的,事实上它是属于一个第三方模块,因此我们需要自己去编译Nginx。并装载这个第三方模块才可以。

  我们使用Nginx来获取文件,做前端的查询代理时需要使用到mogilefs的这个棋块,可以下载这个棋块编译进Nginx就行了,直接使用/configure -add-module=这个参数就可以了最新的这个模块的下载地址是:https://sithub.com/vkholodkov/nginx-mogilefs-module使用这个需哭考虑到网站原来的url是什么样的。比如:

  http://www. e.com/uploads/front page/A6Be0135E24AB17E043B985453762438.png这个URL中的UUTD是A6B00135E24AB17E643B9B5453762438.png。这时我们使用这个做key来存成MogileFS中就行。再结合rewrite,只要key在url里有,就能直接代理到后端的mogilefs。的,直接取A6B00135E24AB17E643B9B5453762438.png来做查询用的key.

原码编译安装Nginx

# 安装开发包组(这里使用centos7)
[root@node1 ~]# yum -y groupinstall "开发工具"

# 下载tengine源码包(编译安装提供nginx)(已上传到百度云 tengine官方网址:http://tengine.taobao.org/)
[root@node1 ~]# wget http://tengine.taobao.org/download/tengine-2.1.1.tar.gz

# 下载nginx_mogilefs_module模块源码包(MogileFS客户端nginx的 Web服务器,nginx模块编译进nginx)(已上传到百度云 nginx_mogilefs_module官方网址:http://www.grid.net.ru/nginx/mogilefs.en.html)
[root@node1 ~]# wget http://www.grid.net.ru/nginx/download/nginx_mogilefs_module-1.0.4.tar.gz

# 解决依赖包
[root@node1 tengine-2.1.1]# yum -y install pcre-devel openssl-devel

# 展开源码包
[root@node1 ~]# tar xf nginx_mogilefs_module-1.0.4.tar.gz 
[root@node1 ~]# tar xf tengine-2.1.1.tar.gz 

# 添加用户nginx,实现以之运行nginx服务进程:
[root@node1 tengine-2.1.1]# groupadd -r nginx
[root@node1 tengine-2.1.1]# useradd -r -g nginx nginx


# 编译和安装tengine(--add-module=需要指明模块文件包路径,这里在父目录,上一级目录下)
[root@localhost ~]# cd tengine-2.1.1

[root@node1 tengine-2.1.1]# ./configure \
--prefix=/usr \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_flv_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-pcre \
--with-debug \
--add-module=../nginx_mogilefs_module-1.0.4

##adding module in ../nginx_mogilefs_module-1.0.4      (在../nginx_mogilefs_module-1.0.4中添加模块)
## + ngx_http_mogilefs_module was configured      (+ ngx_http_mogilefs_module已配置)


########################### 这些临时目录用起来很头疼去掉##########
## --http-client-body-temp-path=/var/tmp/nginx/client/ \
## --http-proxy-temp-path=/var/tmp/nginx/proxy/ \
## --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \
## --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \
## --http-scgi-temp-path=/var/tmp/nginx/scgi \

## --add-module=      (此选项表示指明要使用的第三方模块,给他指明路径就成。) 

[root@node1 tengine-2.1.1]# make
## 编译会报错因为装的依赖包太少

# 修改 vi objs/Makefile
## CFLAGS =  -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g      (把-Werror去掉,就是警告不阻止就行了)

# 再次编译(因为之前编译过,编译的很快,但是不要认为是失败,是成功了)
[root@node1 tengine-2.1.1]# make

# 安装
[root@node1 tengine-2.1.1]# make install 

配置启动脚本,并启动(ExecStartPre=h和ExecStart=文件路径,可以使用which nginx查看,pid文件在/var/run/nginx/nginx.pid路径下,pid文件错误会报错)

# cengos7 nginx Unix file()   没有这个文件自己手动创建,添加字段,

[root@node1 tengine-2.1.1]# vi /usr/lib/systemd/system/nginx.service
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/var/run/nginx/nginx.pid
ExecStartPost=/bin/sleep 0.1
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=process
KillSignal=SIGQUIT
TimeoutStopSec=5
PrivateTmp=true

[Install]
WantedBy=multi-user.target


# 重新加载nginx的配置文件
[root@node1 tengine-2.1.1]# systemctl daemon-reload

# 关闭防火墙
[root@node1 tengine-2.1.1]# systemctl stop firewalld

# 启动nginx(80端口已开启监听,现在nginx已经可以正常访问了)
[root@node1 tengine-2.1.1]# systemctl start nginx.service

配置可以反向代理MogileFS

# 备份一下主配置文件(万一改错了还能还原回来。)
[root@localhost tengine-2.1.1]# cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak

# 修改配置文件(添加配置,可以方向代理MogileFS)(在server字段中location后面添加这个一个location ,这个/images因为key文件为/linux.jpg,否这也可以/images/,建议使用可以文件为linux.jpg不要加路径)(key文件为linux.jpg情况下,访问时这个/images/是我们自己定义的location,将来想要通过哪个location进行访问,就给他对应加一个location路径ok,而linux.jpg这里就是location后附加的key,他会自动把这个key通过请求以后转换成http://192.168.78.216:7500/dev1/0/000/000/0000000008.fid这个,而且自动在背后映射成为fid)
[root@localhost tengine-2.1.1]# vi /etc/nginx/nginx.conf
location /images {
      mogilefs_tracker 192.168.78.216:7001;
      mogilefs_domain images;
      mogilefs_methods GET;
      mogilefs_noverify on;

      mogilefs_pass {
            proxy_pass $mogilefs_path;
            proxy_hide_header Content-Type;
            proxy_buffering off;
      }
}


# 测试一下语法
[root@localhost tengine-2.1.1]# nginx -t
the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful

# 没有问题重启nginx
[root@localhost tengine-2.1.1]# systemctl restart nginx.service

#################################################### 参数的注释##############
##大体上就是这么一个简单的格式。在里面定义一个location 即可。
location ~ ([^\/]+)$ {      (注意key文件命名的时候是否添加特殊符合,有路径表示,会影响访问路径。可以指明location 根 。反正对任何路径的请求都做反向代理也行。这里定义的路径是只要不是/结尾的,那么都在这对他进行反代,做检测,事实上我们将来真正访问时,可以做映射的,比如我们只有那些images路径下的图片内容才映射到这。)
      mogilefs_tracker 192.168.1.xxx:7001;      (指明tracker节点主机地址加端口号)
      mogilefs_domain img;      (指明domain域(命名空间))
      mogilefs_methods GET;      (请求方案为 GET,如果要支持上传功能还有post操作,比如mogilefs——methods PUT DETEL; 支持 PUT 和 DETEL)
      mogilefs_noverify on;      (是不是不做校验,on 是的)

      mogilefs_pass {      (我们怎么代理呢)
            proxy_pass $mogilefs_path;      (吧请求的内容给他代理至$mogilefs_path,这个是这个模块自带的一个变量,他能够在反代时,让nginx构建出一个MogileFS请求报文,发给tracker 192.168.1.xxx:7001后端tracker主机。说白了这个值就是你请求的路径.fid之类的,就是把key转成fid,这个key可以使用mogfileinfo查看)
            proxy_hide_header Content-Type;      (发送时隐藏发送请求的Content-Type)
            proxy_buffering off;      (把缓冲关掉)
      }      
}

消除以上部署过程中Tracker是单点

  此时Tracker只有一个,很有可能Tracker这一个点发生故障,而他成为了单点所在。先姑且不论前端的nginx反代会不会单点,至少在MogileFS集群内Tracker此时是单点,现在我们要消除Tracker单点。

除了nginx反代,所以节点都开启Tracker服务器,每一个节点上有存储服务和Tracker服务两种服务

# 将在node2节点上配置好的mogilefsd.conf文件,发送给node3,node4节点
[root@node2 ~]# scp /etc/mogilefs/mogilefsd.conf node3:/etc/mogilefs/mogilefsd.conf 
root@node3's password: 
mogilefsd.conf                                                                                               100% 1464   406.2KB/s   00:00    
[root@node2 ~]# scp /etc/mogilefs/mogilefsd.conf node4:/etc/mogilefs/mogilefsd.conf 
root@node4's password: 
mogilefsd.conf                                                                                               100% 1464     1.4MB/s   00:00 


# node3,node4节点启动服务      (第一遍启动好像会是吧,可以systemctl daemon-restart提前加载一遍配置文件在启动,7001端口已监听)
[root@node3 ~]# systemctl start mogilefsd.service
[root@node4 ~]# ss -tnl
State       Recv-Q Send-Q                          Local Address:Port                                         Peer Address:Port              
LISTEN      0      128                                         *:7500                                                    *:*                  
LISTEN      0      128                                         *:7501                                                    *:*                  
LISTEN      0      128                                         *:22                                                      *:*                  
LISTEN      0      128                                         *:7001                                                    *:*                  
LISTEN      0      100                                 127.0.0.1:25                                                      *:*                  
LISTEN      0      128                                        :::22                                                     :::*                  
LISTEN      0      100                                       ::1:25   

# 测试Tracker(之前使用的是node2上的Tracker用于显示,现在使用别的Tracker)(这三个Tracker连接到哪个上面都没有问题,这三个Tracker也只要能连上mysql服务器。而我们买售后服务器之所以授权为远程连接,而没有使用localhost,就是这样的原因,因为对于node2而已虽然说mysql是在本地的,但是对于node3和node4就不是本地的了,所以这是我们有意配置成能够执行远程连接,而且授权账号也是远程连接的原因。这个时候我们就有三个Tracker了,那就意味着nginx做反代时就可以做负载均衡了。)
# 指定node2的Tracker
[root@node2 ~]# moglistkeys --trackers=192.168.78.216:7001 --domain=images
/linux.jpg
hlm.jpg

# 指定node3的Tracker
[root@node2 ~]# moglistkeys --trackers=192.168.78.218:7001 --domain=images
/linux.jpg
hlm.jpg

# 指定node4的Tracker
[root@node2 ~]# moglistkeys --trackers=192.168.78.215:7001 --domain=images
/linux.jpg
hlm.jpg

ningx对三个Tracker节点做负载均衡(注意location 的mogilefs_tracker地址是 upstream定义的trackers)

# 定义upstream (负载均衡)
[root@localhost ~]# vi /etc/nginx/nginx.conf
http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    upstream trackers {
        server 192.168.78.216:7001;
        server 192.168.78.218:7001;
        server 192.168.78.215:7001;

        check interval=1000 rise=2 fall=5 timeout=1000;

}

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }


location /images/ {
      mogilefs_tracker trackers;
      mogilefs_domain images;
      mogilefs_methods GET;
      mogilefs_noverify on;

      mogilefs_pass {
            proxy_pass $mogilefs_path;
            proxy_hide_header Content-Type;
            proxy_buffering off;
      }
}

# nginx配置文件语法检测
[root@localhost ~]# nginx -t
the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful

# 重载服务(此时已经可以了)
[root@localhost ~]# systemctl restart nginx


# 在nginx中把status页面打开,(查看后端每一台服务器的健康状态检测是否ok的,在加一个location,访问这些这里就不加了,真正使用时需要加上访问控制这些配置)
[root@localhost ~]# vi /etc/nginx/nginx.conf
location /images/ {
      mogilefs_tracker trackers;
      mogilefs_domain images;
      mogilefs_methods GET;
      mogilefs_noverify on;

      mogilefs_pass {
            proxy_pass $mogilefs_path;
            proxy_hide_header Content-Type;
            proxy_buffering off;
      }
}

location /status { 
        check_status;
 
    
}

# 修改完后做语法检查
[root@localhost ~]# nginx -t
the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful


# 重启服务
[root@localhost ~]# systemctl restart nginx

# 再次访问可以跟上status
http://192.168.78.213/status
Nginx http upstream check status
Check upstream server number: 3, generation: 1
Index	Upstream	Name	                Status   Rise counts	Fall counts	Check type	Check port
0	trackers	192.168.78.216:7001	up	 52	        0	        tcp	        0            (trackers当中的192.168.78.216:7001这个主机地址加端口号,显示为up没问题,Check type方式时tcp)
1	trackers	192.168.78.218:7001	up	 52	        0	        tcp	        0
2	trackers	192.168.78.215:7001	up	 52	        0	        tcp	        0


# 测试(停掉一个trackers服务,比如停掉node2的trackers服务,(这里stop服务停不掉,端口依然在监听,这里把他kill掉))
[root@node2 ~]# kill -9 13131

# 访问status(检查这里3秒中检查一次15秒以后他检测5次失败Status就显示down失败了,但是这并不影响我们图片的访问,如果这时我们将node2的trackers服务重新上线,这里检测2次就显示成功了(我们这里启动两次成功,第一次失败))
http://192.168.78.213/status
Nginx http upstream check status
Check upstream server number: 3, generation: 1
Index	Upstream	Name	                Status   Rise counts	Fall counts	Check type	Check port
0	trackers	192.168.78.216:7001	down	 0	        21	        tcp	        0            
1	trackers	192.168.78.218:7001	up	 52	        0	        tcp	        0
2	trackers	192.168.78.215:7001	up	 52	        0	        tcp	        0

#################################################### 参数的注释##############

upstream trackers {      (定义upstream 取名就叫trackers )
      server 192.168.78.216 weight=1;      (server要负载的服务,加上trackers的节点地址,weight=1 是权重分配  ,都是1表示平均分配)
      server 192.168.78.218 weight=1;
      server 192.168.78.215 weight=1;

      check interval=3000 rise=2 fall=5 timeout=1000;      (注意这里的check 只能在tengine(淘宝nginx)中才能使用,这种自定义使用check 命令的检测方式只有在tengine中才能用)(check 做健康状态检测,并指明检查方式interval=1000这是毫秒,表示每1秒钟检测一次,rise=2他要从正常转换成不正常我最多检测2次, fall=5表示如果他从正常变成不正常我要检测5次,timeout=1000每一次检测的超时时间为1000毫秒,也就是1秒钟)
      # check_keepalive_requests 100;
      # check_http_send "GET / HTTP/1.0/r/n/r/n";      (在tengine中这个扩展方式还可以指明直接发送http请求来做检测的,向对方发送一个请求方式,比如GET请求方式 ,GET / HTTP/1.0这些事起始行,然后/r/n/r/n回车回车)
      # check_http_expect_alive http_2xx http_3xx;      (期望对方的响应是http_2xx http_3xx,2开头的状态码,或者是3开头的状态码,如果我们要这样请求的话,我们这里需要定义一个模块等于http否者他默认使用的是TCP检测方式。)
}



location /status { 
        check_status;
 
        access_log off;      (既然是查看状态的就没必要开启他的访问日志了)
        allow SOME.IP.ADD.RESS;      (这个/status我们不应该随便共享给任何人谁便查看,这里使用allow指明只允许来至于哪些地址的主机访问)
        deny all;      (默认拒接所有)

     }   
}

制作MogileFS分布式文件系统镜像

# 使用centos7为基础镜像(有yum)
[root@localhost ~]# docker pull centos:centos7

# 交互式进入容器(centos默认启动的程序是/bin/bash,      podman ps -a可以查看,-v关联存储卷 -d运行在后台,默认启动的是/bin/bash不-it不会允许进程就直接杀掉容器,-dit让他启动/bin/bash允许在后台,容器暴露80端口,nginx反代需要)
[root@localhost ~]#  docker run --name MogileFS --rm -dit -p 7001:7001 -p 7500:7500 -p 7501:7501 -p 80:80 -p 8080:8080 -v /root:/data docker.io/library/centos:centos7
a5e181ce8e12802108c88a0579745843a8078b319290289ce2d7f0ab175d2c40

# 进入MogileFS 容器
[root@localhost ~]# docker exec -it MogileFS /bin/bash

# 下载epel源,这里没有wget使用curl进行下载,-o小写的o表示保存网页,直接保存到/etc/yum.repos.d/epel.repo下
[root@0a8f6969683f /]# curl -o  /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   664  100   664    0     0   1206      0 --:--:-- --:--:-- --:--:--  1209


# 验证一下(epel.repo这个文件以存在,仓库已识别)
[root@0a8f6969683f /]# ls /etc/yum.repos.d/
CentOS-Base.repo  CentOS-CR.repo  CentOS-Debuginfo.repo  CentOS-Media.repo  CentOS-Sources.repo  CentOS-Vault.repo  CentOS-aarch64-kernel.repo  CentOS-fasttrack.repo  epel.repo

[root@0a8f6969683f /]# yum repolist

# 安装perl依赖
[root@0a8f6969683f /]# yum -y install perl-Sys-Syslog perl-Net-Netmask perl-IO-AIO

# 安装MogileFS,安装包已经下载到宿主机/root目录下了,关联到了容器内的/data目录下
## 查看
[root@ecc89b964e1c ~]# cd /data    
[root@0a8f6969683f data]# ls
MogileFS-Server-2.46-2.el7.centos.noarch.rpm            MogileFS-Utils-2.19-1.el7.centos.noarch.rpm  mha4mysql-node-0.58-0.el7.centos.noarch.rpm        perl-Perlbal-1.78-1.el6.noarch.rpm
MogileFS-Server-mogilefsd-2.46-2.el7.centos.noarch.rpm  a.py                                         perl-Danga-Socket-1.61-1.el6.rf.noarch.rpm
MogileFS-Server-mogstored-2.46-2.el7.centos.noarch.rpm  anaconda-ks.cfg                              perl-MogileFS-Client-1.14-1.el7.centos.noarch.rpm


## 安装
[root@ecc89b964e1c data]# yum install ./*.rpm -y

# 新终端,设置databases,启动一个数据库容器
[root@localhost ~]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root --name mariadb mariadb:latest

# 进入mariadb容器,连入数据库
[root@localhost ~]# docker exec -it mariadb /bin/bash
root@e5e121b1cee9:/# mysql -uroot -p  

# 授权一个root用户能够远程连接。(注意ip地址)
MariaDB [mogilefs]> grant all on *.* to 'root'@'172.17.0.%' identified by 'fspass';

# 创建一个数据库
MariaDB [(none)]> create database mogilefs;
Query OK, 1 row affected (0.00 sec)

# 授权给moguser用户能访问mogilefs库中的所有数据。(注意ip地址)
MariaDB [mogilefs]> grant all on mogilefs.* to 'moguser'@'172.17.0.%' identified by 'mogpass';


# MogileFS终端,MogileFS设定数据库
[root@0a8f6969683f data]# mogdbsetup --dbhost=172.17.0.3 --dbrootpass='fspass' --dbuser='moguser' --dbpass='mogpass'

# databases终端,查看创建的表
MariaDB [mogilefs]> show tables;
+----------------------+
| Tables_in_mogilefs   |
+----------------------+
| checksum             |
| class                |
| device               |
| domain               |
| file                 |
| file_on              |
| file_on_corrupt      |
| file_to_delete       |
| file_to_delete2      |
| file_to_delete_later |
| file_to_queue        |
| file_to_replicate    |
| fsck_log             |
| host                 |
| server_settings      |
| tempfile             |
| unreachable_fids     |
+----------------------+
17 rows in set (0.00 sec)


# 启动Tracker进程

## 修改Tracker节点配置文件,修改这几项(其他选项未修改。)      (注意mogilefs:host数据库地址)
[root@localhost ~]# vi /etc/mogilefs/mogilefsd.conf 
db_dsn = DBI:mysql:mogilefs:host=172.17.0.3
db_user = moguser
db_pass = mogpass
listen = 0.0.0.0:7001


## 查看启动程序(/etc/rc.d/init.d/mogilefsd看清楚这是init使用systemctl启动脚本,这里使用的是容器无法使用systemctl,即使使用特权模式启动容器也不能启动,     /usr/bin/mogilefsd这个才是启动程序,不能使用root用户启动)
[root@localhost ~]# rpm  -ql MogileFS-Server-mogilefsd
/etc/rc.d/init.d/mogilefsd
/usr/bin/mogilefsd

## 创建,切换用户用来启动MogileFS
[root@a5e181ce8e12 data]# useradd mogilefs
[root@a5e181ce8e12 data]# su mogilefs

## 启动MogileFS进程(想办法运行在后台bash-4.2$ nohup /usr/bin/mogilefsd start > /dev/null  2>&1 & )
[moguser@ba3c00a0d9a0 data]$ /usr/bin/mogilefsd start

## 查看程序
[root@192b5358edba data]# ps -aux



# 修改storage配置文件,启动程序 (我们安装在一个docker中了)

## 创建设备目录
[root@546274f8ed93 data]# mkdir -pv /data/mogilefs/dev1


## 给目录授权,修改属主,属组
[root@localhost ~]# chown -R mogilefs:mogilefs /data/mogilefs/
[root@localhost ~]# ll /data/mogilefs/
总用量 0
drwxr-xr-x. 2 mogilefs mogilefs 6 9月  26 23:18 dev1


### 查看程序,和配置文件路径
[root@b5981e1fcb9e data]# rpm  -ql MogileFS-Server-mogstored
/etc/mogilefs/mogstored.conf      (配置文件路径)
/usr/bin/mogstored      (启动程序)


### 编辑配置文件
[root@b5981e1fcb9e data]# vi /etc/mogilefs/mogstored.conf 
docroot = /data/mogilefs/

## 启动程序(想办法运行在后台nohup /usr/bin/mogstored start > /dev/null 2>&1 &)
[root@546274f8ed93 data]# /usr/bin/mogstored start
Running.
beginning run

# 查看进程
[root@192b5358edba data]# ps -aux

# 新终端,显示容器上的映射端口
[root@localhost ~]# docker port MogileFS
7001/tcp -> 0.0.0.0:7001
7500/tcp -> 0.0.0.0:7500
7501/tcp -> 0.0.0.0:7501

# 将mogstored存储节点添加进Tracker(就是我们所说的元数据节点)
## 看一下Tracker容器的信息
[root@localhost ~]# docker inspect MogileFS
                    "IPAddress": "172.17.0.2",

## 指明Tracker节点是谁(--trackers选项指明Tracker节点是谁,如果这个选项不给的话,他就连接127.0.0.1的7001端口,我们还可以设置多个,第1个找不到就找第2个。这是我们提供多个Tracker节点时的用法,也可以使用check检查连接到Tracker节点的状态信息)
[root@177449ffff0f data]# mogadm --trackers=172.17.0.2:7001 check
Checking trackers...
  172.17.0.2:7001 ... OK

Checking hosts...
No devices found on tracker(s).

## 添加主机(添加节点,并列出当前节点)
[root@177449ffff0f data]#  mogadm --trackers=172.17.0.2:7001 host add 172.17.0.2 --ip=172.17.0.2 --status=alive
[root@177449ffff0f data]# mogadm host list
172.17.0.2 [1]: alive
  IP:       172.17.0.2:7500


# 添加存储设备并查看
[root@177449ffff0f data]# mogadm device add 172.17.0.2 1
[root@546274f8ed93 /]# mogadm device list
172.18.0.2 [1]: alive
                    used(G)    free(G)   total(G)  weight(%)
   dev1:   alive      2.437     47.539     49.976        100




[root@546274f8ed93 /]# mogadm check
Checking trackers...
  127.0.0.1:7001 ... OK

Checking hosts...
  [ 1] 172.18.0.2 ... OK

Checking devices...
  host device         size(G)    used(G)    free(G)   use%   ob state   I/O%
  ---- ------------ ---------- ---------- ---------- ------ ---------- -----
  [ 1] dev1            49.976      2.438     47.537   4.88%  writeable   N/A
  ---- ------------ ---------- ---------- ---------- ------
             total:    49.976      2.438     47.537   4.88%


# 安装nginx,在同一个容器内 进行反代
## 编译安装,反代设置之后,启动nginx
[root@4bd68eee4a27 sbin]# cd /usr/sbin
[root@4bd68eee4a27 sbin]# ./nginx -s start

## 查看程序是否启动,此时就可以访问了,比如访问格式为http://119.3.239.161/mp3/A.mp3
[root@4bd68eee4a27 sbin]# ps -aux


总结,数据未做持久化,数据保存在容器内不合适,url使用的http,如果网页是https,加密网页不能嵌套http,google浏览器无法播放,无效

posted @ 2020-09-24 19:46  给文明以岁月  阅读(568)  评论(0编辑  收藏  举报