HDFS详解

认识HDFS

HDFS的特点:

高容错性
高吞吐量
故障的检测和自动快速恢复
流式的数据访问
大数据集
一次写入,多次读写

HDFS不适用的场景

不支持大量小文件的存储
不适合随机读写
不适合随意修改

HDFS的组成

hdfs是一个主从结构的体系,一个HDFS的集群由以下部分组成:
NameNode(名字节点):一个用来管理文件的名字空间和调节客户端访问文件的主服务器
DataNode(数据节点):一个或多个,用来管理存储

HDFS的数据复制

HDFS能可靠的在大量集群中存储非常大量的文件,它以块序列的形式存储每一个文件,文件中除了最后一个块的其他块都是相同的大小.属于文件的块为了故障容错而被复制.块的大小和复制数可以为每个文件配置.HDFS中的文件都是严格地要求任何时候只有一个写操作.

名字节点(NameNode)来做所有的块复制,它周期性地接收来自集群中数据节点(DataNode)的心跳和块报告.一个心跳的收条表示这个数据节点是健康的,是渴望服务数据的.一个块报告包括该数据节点上的所有块列表.复制块放置位置的选择严重影响HDFS的可靠性和性能,因此机架的复制布局目的就是提高数据的可靠性、可用性和网络带宽的利用率

默认复制数为3的情况下,HDFS放置方式是将第一个放在本地节点,将第二个复制放到本地机架上的另一个节点上,而将第三个复制到不同机架上的节点。这种方式减少了机架内的写流量,提高了写的性能.机架的失效机会远小于机器失效的机会

在复制数为3的情况下,是备份在不同的机架上,而不是三个机架.文件的复制不是均匀地分布在机架上.1/3在同一个节点上,第二个1/3复制在同一个机架上,另外1/3是均匀地分布在其他机架上.

可在hdfs-site.xml中修改dfs.replication的个数即修改复制个数,这种方案参数其实只在文件被写入dfs时起作用,虽然更改了配置文件,但是不会改变之前写入的文件的备份数,而且需要重启HDFS系统才能生效

hadoop fs -setrep -R 1 /:这种方式可以改变整个HDFS里面的备份数,不需要重启HDFS系统

数据块的复制是以pipeline的方式进行的

HDFS复制的选择

HDFS尝试满足一个读操作来自离它最近的复制。如果在读节点的同一个机架上有这个复制,则直接读.如果HDFS集群是跨越多个数据的中心,那么本地数据中心的复制优先于远程的复制

HDFS的安全模式

在启动的时候,NameNode会进入一个特殊的状态叫做安全模式。
安全模式是不发生文件块的复制的
NameNode接受DataNode的心跳和数据块报告,一个块报告包括数据节点向名字节点报告数据块的列表
每一个块有一个特定的最小复制数,当NameNode检查这个块已经大于最小的复制数,就被认为是安全的复制了.当达到配置的块安全复制比例时,NameNode就退出安全模式.它将检测数据块的列表,将小于特定的复制数的块复制到其他的数据节点

HDFS的元数据持久化

HDFS的名字空间是由NameNode来存储的。
NameNode用事务日志(EditsLog)来持久化每一个对文件系统的元数据的改变,即对元数据的每一步操作都会被记录到EditsLog中.NameNode在本地文件系统中用一个文件来存储这个EditsLog
完整的文件系统名字空间、文件块的映射和文件系统的配置都存在一个叫Fsimage的文件中,Fsimage也是在NameNode的本地文件系统中
CheckPoint(检查点):当NameNode启动的时候,它会从磁盘中读取Fsimage和EditsLog文件,然后将新的元数据刷新到本地磁盘中,生成一个新的Fsimage文件,至此EditsLog文件已经被处理并持久化的Fsimage中
任何对Fsimage和EditsLog的更新都会同步地更新每一个副本

HDFS架构

HDFS架构是一个典型的主从架构,包括一个NameNode(主节点)和多个DataNode(从节点)
架构图如下图所示:

Paste_Image.png

NameNode是整个文件系统的管理节点,它负责文件系统名字空间(NameSpace)的管理与维护,同时负责客户端文件操作的控制以及具体存储任务的管理与分配
DataNode提供了真实文件数据的存储服务

数据块

Hadoop1.X默认的数据块大小是64MB
Hadoop2.X默认的数据块大小是128MB
HDFS上的文件系统也被划分为块大小的多个分块(Chunk)作为独立的存储单元.但与其他文件系统不同的是,HDFS中小于一个块大小的文件不会占据整个块的空间

为什么HDFS默认的Block为128MB(64MB)?

HDFS的块之所以这么大,主要是为了把寻道(Seek)时间最小化.如果一个块足够大,从硬盘传输数据的时间将远远大于寻找块的起始位置的时间,这样就使HDFS的数据传输速度和硬盘的传输速度更加接近.

分布式文件系统中的块进行抽象带来的好处:

一个文件系统的大小可以大于网络中任意一个磁盘的容量
使用块抽象而非整个文件作为存储单元,大大简化了存储子系统的设计.

NameNode

NameNode是管理者,一个Hadoop集群有一个NameNode节点,是一个通常在HDFS实例中的单独机器上运行的软件.它负责管理文件系统名字空间和控制外部客户机的访问

NameNode决定是否将文件映射到DataNode的复制块上.
实际的I/O事务并没有经过NameNode,只有表示DataNode和块的文件映射的元数据经过NameNode。当外部客户机发送请求要求创建文件时,NameNode会以块标识和该块的第一个副本的DataNode IP地址作为响应.这个NameNode还会通知其他将要接收该块的副本的DataNode

NameNode主要功能如下:

1:NameNode提供名称查询服务,它是一个Jetty服务器
2:NameNode保存metadata信息.具体包括:文件owership和permissons;文件包含哪些块,Block保存在哪个DataNode(由DataNode启动时上报)
3:NameNode的metadata信息在启动后会加载在内存

DataNode

Hadoop集群中包含一个NameNode和大量的DataNode,DataNode通常以机架的形式组织,机架通过一个交换机将所有系统连接起来

DataNode响应来自HDFS客户机的读写请求,它们还响应来自NameNode的创建、删除和复制块的命令

DataNode的功能如下:
1:保存Block,每一个块对应一个元数据信息文件。这个文件主要描述这个块属于哪个文件,第几个块等信息
2:启动DataNode线程的时候会向NameNode汇报Block信息
3:通过向NameNode发送心跳保持其联系(3秒一次),如果NameNode10分钟没有收到DataNode的心跳,则认为其已经lost,并将其上的Block复制到其他DataNode

SecondaryNameNode

SecondaryNameNode(辅助元数据节点),会周期性地将EditsLog文件中记录对HDFS的操作合并到一个Fsimage中文件中,然后清空EditsLog文件.NameNode的重启会加载最新的一个Fsimage文件,并重新创建一个EditsLog文件来记录HDFS操作,由于EditsLog中记录的是从上一次Fsimage以后到现在的操作列表,所以会比较小

如果没有SecondaryNameNode这个周期性的合并过程,当每次重启NameNode的时候,就会花费很长的时间,而这样周期性地合并就能减少重启的时间,同时也能保证HDFS系统的完整性

Paste_Image.png

SecondaryNameNode合并Fsimage和EditsLog文件过程如下:

1:文件系统客户端进行写操作时,首先会把它记录在EditsLog中
2:NameNode在内存中保存了文件系统的元数据信息.在记录修改日志后,NameNode修改内存中的数据结构
3:每次的写操作成功之前,修改日志都会同步(Sync)到文件系统中
4:Fsimage文件即名字空间映像文件,是内存中的元数据在硬盘上的CheckPoint,它是一种序列化的格式,并不能够在硬盘上直接修改
5:当NameNode失败时,最新CheckPoint的元数据信息从Fsimage加载到内存中,然后逐一重新执行修改日志后的操作
6:SecondaryNameNode就是用来帮助NameNode将内存中的元数据信息CheckPoint到硬盘上的.

CheckPoint过程如下:

1:SecondaryNameNode通知NameNode生成新的EditsLog,以后的日志都写到新的日志文件中
2:SecondaryNameNode用Http Get从NameNode获得Fsimage文件以及旧的日志文件
3:SecondaryNameNode将Fsimage文件加载到内存中,并执行日志文件中的操作,然后生成新的Fsimage文件
4:SecondaryNameNode将新的Fsimage文件用Http Post传回NameNode
5:NameNode可以将旧的Fsimage以及旧的EditLog,换成新的Fsimage文件和新的EditLog(第一步生成的),然后更新fstime文件,写入此次CheckPoint的时间
6:这样NameNode中的Fsimage文件保存了最新的CheckPoint的元数据信息,日志文件也重新开始,不会变得很大了.

SecondaryNameNode会周期性地将EditsLog文件进行合并,合并前提条件如下:

EditsLog文件到达某一阀值时对其进行合并(默认为64MB,当文件大小超过64MB,就会触发EditsLog与Fsimage文件的合并,修改core-site.xml配置文件中的fs.checkpoint.period选项)

每隔一段时间对其合并(默认为1小时合并一次,修改core-site.xml配置文件中的fs.checkpoint.size)

机架感知

默认情况下,Hadoop的机架感知是没有启用的.所以,通常情况下,Hadoop集群的HDFS在选择机器的时候是随机选择的.

启用Hadoop机架感知功能,配置非常简单,在NameNode所在机器 的hadoop-site.xml文件中配置一个选项
<property>
<name>topology.script.file.name</name>
<value>/path/to/script</value>
</property>

HDFS的RPC机制

一般我们所了解的RPC(Remote Procedure Call,远程过程调用)机制都要面对两个问题:
1对象调用方式;
2序列/反序列化机制
但Hadoop实现了自己简单的RPC组件,依赖Hadoop Writable类型的支持
Hadoop Writable接口要求每个实现类都要确保将本类的对象正确序列化(writeObject)与反序列化(readObject)。因此,Hadoop RPC使用java动态代理与反射实现对象调用方式

Paste_Image.png

RPC的实现流程

简单的说,Hadoop RPC = 动态代理+定制的二进制流

实现流程如下:
远程的对象拥有固定的接口,这个接口用户也是可见的,只是真正的实现(Object)只在服务端.用户如果想使用哪个实现,调用过程是:
先根据那个接口动态代理生成一个代理对象,调用这个代理对象的时候,用户的调用请求被RPC捕捉到,然后包装成调用请求,序列化成数据流发送到服务端;服务端从数据流中解析出调用请求,然后根据用户所希望调用的接口,调用真正的实现对象,再把调用结果返回给客户端

RPC的实体模型

注意:用户在调用代理对象时RPC是怎样拦截这次调用请求的?

创建代理对象时需要为它关联一个InvocationHandler,对代理对象的每次调用都会进入绑定的InvocationHandler中,RPC就从这里获取用户的请求

Listener:监听RPC Server的端口,如果客户端有连接请求到达,它就接收连接,然后把连接转发到某个Reader,让Reader读取那个连接的数据。如果有多个Reader,当有新连接过来时,就在这些Reader间顺序分发。注意Hadoop0.21版本在支持多Reader时有个bug(JIRA),如果有Reader在Server运行期间没有被使用,Server进程不能正常关闭

Reader:从某个客户端连接中读取数据流,把它转化成调用对象(Call),然后放到调用队列(call queue)里

Handler:真正做事的实体,它从调用队列中获取调用信息,然后反射调用真正的对象,得到结果,再把此次调用放到响应队列(response queue)里

>Responder:不断地检查响应队列中是否有调用信息,如果有,就把调用的结果返回给客户端
Paste_Image.png

HDFS的文件读取

Paste_Image.png

文件读取的流程如下:

    1:使用HDFS提供的客户度开发库Client,向远程的NameNode发起RPC请求
    2:NameNode会根据情况返回文件的部分或者全部Block列表,对于每个Block,NameNode都会返回有该Block副本的DataNode地址
    3:客户端开发库Client会选取离客户端最接近的DataNode来读取Block,如果客户端本身就是DataNode,将从本地直接获取数据
    4:读取完当前Block的数据后,关闭与当前的DataNode连接,并为读取下一个Block寻找最佳的DataNode
    5:读取完列表的Block后,且文件读取还没有结束,客户端开发库会继续向NameNode获取下一批Block列表
    6:读取完一个Block都会进行CheckSum验证,如果读取DataNode时会出现错误,客户端会通知NameNode,然后从下一个拥有该Block复制的DataNode继续读

HDFS的文件写入

Paste_Image.png

写入文件的过程比读取复杂,步骤如下:

  1:使用HDFS提供的客户端开发库Client,向远程的NameNode发起RPC请求
  2:NameNode会检查要创建的文件是否已经存在,创建者是否有权限进行操作,成功后会为文件创建一个记录,否则会让客户端抛出异常
  3:当客户端开始写入文件的时候,开发库会将文件切分多个packet,并在内部以数据队列(data queue)的形式管理这些packet,并向NameNode申请新的Block,获取用来存储replicas的合适的DataNode列表,列表的大小根据在NameNode中对replication的设置而定
  4:开始以管道(pipeline)的形式将Packet写入所有的replicas中。开发库把packet以流的方式写入第一个DataNode,该DataNode把packet存储之后,再把器传递给在此管道中的下一个DataNode,直到最后一个DataNode,这种写数据的方式呈流水线的形式
  5:最后一个DataNode成功存储之后会返回一个ack packet,在管道里传递给客户端,在客户端的开发库内部维护着ack queue,成功收到DataNode返回的ack packet后会从ack queue移除相应的packet
  6: 如果传输过程中,有某个DataNode出现了故障,当前的管道会被关闭,出现故障的DataNode会从当前的管道中移除,剩余的Block会继续剩下的DataNode中继续以管道的形式传输,同时NameNode会分配一个新的DataNode,保持replicas设定的数量 

HDFS的HA(High Availability,高可用性)机制

  Hadoop2.X版本之前,NameNode是HDFS集群的单点故障点(SPOF)
  
  影响HDFS集群不可用主要包括以下两种情况:
        1:类似机器宕机这样的意外情况将导致集群不可用,只有重启NameNode之后才可使用
        2:计划内的软件或硬件升级(NameNode节点),将导致集群在短时间范围内不可用
            
  一个典型的HA集群,两个单独的机器配置为NameNode,在任何时候,一个NameNode处于活动状态,另一个处于待机状态,活动NameNode负责处理集群中所有客户端的操作,待机时仅仅作为一个Slave,保持足够的状态,如果有必要提供一个快速的故障转移

   为了提供快速的故障转移,必须保证备用节点有最新的集群中块的位置信息,为了达到这一点,DataNode节点需要配置两个NameNode的位置,同时发送块的位置信息和心跳信息到两个NameNode

  为了防止"脑裂场景"的出现,必须为共享存储配置至少一个fencing方法。在宕机期间,如果不能确定之间的活动节点已经放弃活动状态,fencing进程负责中断以前的活动节点编辑存储的共享访问,这可以防止任何进一步的修改名字空间.允许新的活动节点安全地进行故障转移
Paste_Image.png

HA架构解释如下:

只有一个NameNode是Active,并且只有这个ActiveNameNode能提供服务,改变NameSpace。以后可以考虑让StandbyNameNode提供读服务

提供手动Failover,在升级过程中,Failover在NameNode-DataNode之间写不变的情况下才能生效

在之前的NameNode重新恢复之后,不能提供failback
  
数据一致性比Failover更重要

HA的设置和Failover都应该保证在两者操作错误或者配置错误的时候,不得导致数据损坏

NameNode的短期垃圾回收不应该触发Failover

DataNode会同时向NameNode Active和NameNode Standary汇报块的信息。NameNode Active和NameNode Standby 通过NFS备份MetaData信息到一个磁盘上面

HDFS的federation机制

 简单的说,HDFS Federation就是使得HDFS支持多个名字空间,并且允许在HDFS中同时存在多个NameNode

 引入Federation的最主要原因是对HDFS系统中文件的隔离,Federation能够快速解决大部分单NameNode HDFS的问题

HDFS Federation使用了多个独立的NameNode/NameSpace使得HDFS的命名服务能够水平扩展


HDFS Federation中的NameNode之间是联盟关系,它们之间相互独立且不需要相互协调。HDFS Federation中的NameNode提供了名字空间和块关联功能.HDFS Federation中的DataNode被所有的NameNode用作公共存储块的地方.每一个DataNode都会向所在集群中所有的NameNode注册,并周期性的发送心跳和块信息报告,同时处理来自NameNode的指令

块池(Block Pool)就是属于单个名字空间的一组Block

Federation HDFS中有多组独立的块,同一个DataNode中可以存储属于多个块池的多个块。

块池允许一个名字空间在不通知其他名字空间的情况下,为一个新的Block创建Block ID.同时,一个NameNode失效不会影响其下的DataNode为其他NameNode的服务

在HDFS中,所有的更新、回滚都是以NameNode和BlockPool为单元发生的.即同HDFS Federation中不同的NameNode/BlockPool之间没有什么关系

多个名字空间的管理问题

  在一个集群中需要唯一的名字空间还是多个名字空间,核心问题是名字空间中数据的共享和访问问题。

  使用全局唯一的名字空间是解决数据共享和访问的一种方法

  在多个名字空间下,还可以使用Client Side Mount Table方式做到数据共享和访问
Paste_Image.png

HDFS Federation中名字空间管理的基本原理:

      将各个名字空间挂载到全局mount-table中,就可以将数据到全局共享;
      同样,名字空间挂载到个人的mount-table中,就成为应用程序可见的名字空间视图

维护HDFS
追加数据

    1.Client调用fs的append操作
    2.向流对象写数据
    3.关闭流对象

并行复制

  distcp(分布式复制)是用于大规模集群内部和集群之间复制的工具。它使用MapReduce实现文件分发、错误处理和恢复,以及报告生成。它把文件和目录的列表作为Map任务的输入,每个任务会完成源列表中部分文件的复制
    
  操作命令:
    hadoop distcp -overwrite -delete -i dir1 dir2
 
  参数如下:
        -delete:删除已经存在的目标文件,不会删除源文件。这个删除是通过FS Shell实现的。所以如果垃圾回收机制启动,删除的目标文件会进入trash

        -i:忽略失败.这个选项会比默认情况提供关于复制的更精确的统计,同时它还将保留失败复制操作的日志。这些日志信息可以用于调试
  
        -overwrite:覆盖目标。如果一个Map失败并且没有使用-i选项,那些复制失败的文件,以及这个分块任务中的所有文件都会被重新复制

升级与回滚

    Hadoop升级分为两种:
            一种是集群布局不发生任何变化的,这种升级非常简单,类似安装一次新的Hadoop程序。
            另外一种是集群布局发送变化的

两种升级升级都简单分为以下几步:

      1.在执行新一轮的升级前,要确保前一次升级已经完成
                hadoop dfsadmin -upgradeProgress status
      2.进行数据的备份,以方便升级后对照,如果有问题可发现然后回滚版本
              hadoop fs -lsr / > ~/namenode.log
              hadoop fsck / >> ~/namenode.log
              cp -r ${dfs.name.dir} ~/namenode_backup
    
      3.使用stop-all.sh关闭hadoop集群
      4.把${dfs.name.dir}目录下的所有内容复制到新配置的路径下,这是集群布局有变化的操作,如果没有新配置布局就不用这个操作了,单独启动新的主节点NameNode的hdfs进行更新操作
            ${NEW_HADOOP}/bin/start-dfs.sh -upgrade
        使用如下命令进行监控,查看是否升级完成
             ${NEW_HADOOP}/bin/hadoop dfsadmin -upgradeProgress status

      5.把新版本的Hadoop程序和配置文件一起分发给集群里所有的机器,根据情况看是不是需要进行个别修改,没有特别的就不用改了。修改/etc/profile中的Hadoop环境变量,改成新版本Hadoop的指向。现在使用启动集群的start-all.sh命令来启动集群就行了
      6.使用fs -lsr 和fsck与namenode.log文件进行核对前后信息变化,如果没有问题,数据块体验也通过,就顺利完成升级,这时需要执行如下命令来最终确定升级完成和清理前版本数据
        hadoop dfsadmin -finalizeUpgrade
        rm -r ~/namenode_backup ~/namenode.log
      至此升级完成,不能再进行回滚操作,如果升级后发现数据不符,可以使用如下命令回滚版本
            stop-all.sh
            start-dfs.sh -rollback

添加节点

    1.修改host。和普通的DataNode一样,添加NameNode的IP
    2.修改NameNode的配置文件conf/slaves。添加新增节点的Ip或主机名
    3.在新节点的机器上启动服务
        bin/hadoop-daemon.sh start datanode
        bin/hadoop-daemon.sh start tasktracker
    4.均衡Block
        bin/start-balancer.sh 

    为什么要执行start-balancer.sh操作呢?
    如果不执行start-balancer.sh操作.cluster会把新的数据都存放在新的节点上,这样会降低MapReduce的工作效率

  执行start-balancer.sh操作的优化如下
    1.设置平衡阀值,默认是10%,值越低各节点越平衡,但消耗时间也更长
    bin/start-balancer.sh -threshold 5
    2.设置balancer的带宽,默认只有1M/s.编辑hdfs-site.xml文件
    <property>
                <name>dfs.balance.bandwidthPerSec</name>
                <value>10485760</value>
    </property>

删除节点

  1.集群配置
        修改conf/hdfs-site.xml文件
            <property>
                    <name>dfs.hosts.exclude</name>
                    <value>/data/soft/hadoop/conf/excludes</value>
            </property>
  2.确定要下架的机器
          dfs.hosts.exclude定义的文件内容为每个需要下线的机器,一行一个。这将阻止它们连接NameNode.
          例如/data/soft/hadoop/conf/excludes的内容如下:
                          ubuntu4
          这样就把ubuntu4这台设备从hadoop集群中删除了。还有一种方法可以实现ubuntu4设备从hadoop集群中删除,配置如下:
            <property>
                  <name>dfs.hosts.exclude</name>
                  <value>ubuntu4</value>
            </property>
            
  3.强制重新加载配置
        bin/hadoop dfsadmin -refreshNode
  它会在后台进行Block块的移动
  4.关闭节点
      bin/hadoop dfsadmin -report
    可以查看到现有集群上连接的节点信息
  5.再次编辑excludes文件
          一旦完成机器下架,下架的设备就可以从excludes文件移除了。登录要下架的机器,会发现DataNode进程没有了,但是TaskTracker依然存在,需要手动关闭

HDFS权限管理

hadoop分布式文件系统实现了一个和POSIX系统类似的文件和目录的权限模型。每个文件和目录有一个所有者(owner)和一个组(group)

 总的来说,文件或目录的权限就是它的模式。HDFS采用了UNIX表示和显示模式的习惯,包括使用八进制数来表示权限。当新建一个文件或目录,所有者即客户进程的用户,它的所属组是父目录的组

 HDFS并不提供创建用户身份、创建组或用户凭证等功能
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,653评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,321评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,833评论 0 324
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,472评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,306评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,274评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,658评论 3 385
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,335评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,638评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,697评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,454评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,311评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,699评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,986评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,254评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,647评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,847评论 2 335

推荐阅读更多精彩内容