【转载】对象存储的元数据搜索

本文摘自:http://blog.umcloud.com/metasearch/

从一本食谱开始说起

多年前,一名职场失意的程序员带着新婚妻子去伦敦生活,迫于生活压力,妻子决定从事厨师这一份工作来维持家庭开支。厨师最缺的就是一本好用的菜谱,但由于菜目数量庞大,查阅起来不方便,因此这名程序员就决定为妻子写一个食物搜索引擎。而这个食物搜索引擎,正是日后大名鼎鼎的Lucene的前身,而这名程序员就是Shay Banon,Lucene的奠基者。由于Lucene使用复杂,他的妻子使用起来非常困难,Shay随后就对Lucene的代码进行抽象分装,使得其他程序员可以在应用中使用Lucene来添加全文搜索功能,Shay将这个项目取名为Compass,从此进入了开源领域。

凭借Compass优秀的功能,Shay随后顺利找到了一份工作,这份工作处于高性能和内存网格的分布式环境中,对搜索引擎的要求更高。Shay决定重写Compass库,使其成为一个独立的服务 – ElasticSearch。这个项目的意义重大自然不用说,无论是开源还是专有领域,Lucene都被认为是迄今为止性能较好、功能较为全面的搜索引擎库,而ElasticSearch将复杂的Lucene封装起来,开放简单的Restful API,让用户能十分方便地使用。

screensho1t

Figure 1.1 – ElasticSearch logo

目前ElasticSearch已经成为了Github上比较受欢迎的项目之一,代码贡献量也超过百人,并且随之产生的产品还有Kibana、Logstash等,而这一切,却只是源于一本食谱。

棋逢对手

在去年的11月份,HDS(Hitachi Data System)公司发布了它的新产品HCI(Hitachi Content Intelligence),这款内容智能方案可以立足于多种结构化与非结构化的数据孤岛进行搜索与内容读取,然后对其加以分析。作为HDS对象存储产品组合(简称HCP)中的一部分,HCI帮助这套整体方案实现文件同步与共享、云存储网关以及新的搜索与分析功能。HCI能够运行在物理或者虚拟服务器之上,亦可被托管于公有或者私有云当中。

Sacha Distel - Allez donc vous faire bronzer (online-audio-converter.com)

Figure 2.1 –HCI数据连接示意图****

HCI所提供的数据连接器能够支持Hitachi Content Platform、Hitachi Data Ingestor、HCP Anywhere、S3托管存储库以及文件系统(CIFS/NFS)。HCI拥有完备的说明文档,包括一套包含示例的软件开发者工具包,合作伙伴与客户可利用其创建未直接提供的、指向各类数据存储库的连接。HCI可作为一组容器进行实例化,并作为一项自助式服务实体被交付给用户,同时提供对详尽查询与自然语言查询的支持能力。HCI的开发团队表示,他们使用了Solr来作为HCI的内容搜索引擎。咦,Solr是什么?

如今我们正处于一个信息大爆炸的年代,数据的急速增长考量着信息技术产业各个维度、各个角落,几乎每个领域都需要更快更好的产品来满足用户的需求。同样,在搜索领域,如何快速地搜索数据便成为了一个重大的后端挑战,在上一章中我们已经介绍了性能优异的ElasticSearch,然而同样在Lucene开源平台上,还有另一个优秀的开源搜索引擎:Solr。

google

Figure 2.2 – Google Trends :ElasticSearch vs Solr

如果我们查询Google Trends,会发现ElasticSearch的热度是远远高于Solr,但是即便如此,作为一款成熟的产品,Solr也拥有强大而广泛的用户群体。 它提供分布式索引,复制,负载平衡查询和自动故障转移和修复等功能。 如果部署正确,然后进行良好的管理,Slor完全可以成为一个高可靠,可扩展,容错高的搜索引擎。 Netflix,eBay,Instagram和 Amazon(CloudSearch)等几家互联网巨头都使用Solr,当然也包括了我们前文介绍的HDS。

数据源对比

Solr支持多种数据源、多种格式的索引,比如XML、CSV、JSON等纯文本格式,还有HTML、PSD、微软Office系列软件的格式等。 Elasticsearch在这方面表现可以说不分伯仲,同样支持多种数据类型,如ActiveMQ,AWS SQS,DynamoDB(Amazon NoSQL),FileSystem,Git,JDBC,JMS,Kafka,LDAP,MongoDB等,当然还有丰富的第三方插件。

文档对比

Solr在这里分数很高。 它的文档组织十分良好,具有API用例的明确示例和上下文说明,我们可以方便的找到一些关于例如查询和更新索引的语法内容,而不需要深挖源代码。 Elasticsearch在这方面就乏善可陈一些了,虽然文档是有组织的,但它缺少很好的例子和明确的配置说明,一些例子是用YAML编写的,有些是JSON,你经常能发现代码逻辑与文档的内容之间存在一些差异。

社区

Solr拥有一个成熟的开发和贡献者社区,任何人都可以为Solr写代码,只要你的代码和功能够好。ElasticSearch在技术上是开源的,所有贡献者都可以访问源代码,用户可以修改源代码,但是用户的源代码是否能被接纳还是取决于Elastic的员工。所以就这一点来说,我为Solr转身。

搜索性能对比

这两个搜索引擎都使用各种分析器和标记器,将文本分解成terms或tokens,然后进行索引。 Elasticsearch允许指定查询分析器列表;相比之下,Solr不支持此功能。Solr更倾向于文本搜索,而Elasticsearch通常用于分析查询,过滤和聚合。 Elasticsearch的开发团队一直在努力通过各种方法(通过减少内存占用和CPU使用等)来提高ElasticSearch的查询效率。并且在实时环境中,如果用户在建立索引同时查询,Solr的查询效率会比较差。

es-solr

Figure 2.3 –建立索引并查询时,ElasticSearch和Solr搜索性能对比

当比较两者时,很显然,如果我们的应用情景不局限于文本搜索,就搜索性能来说,Elasticsearch将会是更好的选择。

扩展性、分布式对比

shards作为Lucene索引的基本分区单元,Solr和ElasticSearch都使用它们。 我们可以通过在集群中的不同设备上运行分片来分发索引。相比较而言,Solr允许添加shards(使用隐式路由时)或拆分(使用复合ID时),并且允许用户增加副本,但是我们不能删除shards。在ElasticSearch中,默认情况下每个索引都有五个shards。 我们不能更改primary shards的数量,但它允许我们增加副本的数量。ElasticSearch的自动分片重平衡功能对于扩展集群时很有用。,如果我们添加新机器,它将自动平衡不同设备中可用的分片。

总结

综上所述,虽然ElasticSeach的文档不是很完善,但是凭借其优秀搜索表现,以及更符合RESTful的设计,我还是比较推崇ElasticSearch来作为对象存储的元数据搜索引擎。

存储界的深海巨兽

或许和Linux、Openstack这些开源界的航母比起来,这个项目显得不那么的如雷贯耳,但是如果你遇到一个存储行业的从业者,和他谈起Ceph,他一定不会陌生。

Ceph 最初是一项关于存储系统的PhD 研究项目,由 Sage Weil 在University of California, Santa Cruz(UCSC)实施,其初衷是为了开发一个分布式文件系统,但随着项目开发者越来越多,Ceph已经提供了完整的统一存储方案,涵盖了块存储、对象存储和文件系统存储。从2010 年 3 月底,我们可以在主线 Linux 内核(从 2.6.34 版开始)中找到 Ceph 的身影。

作为一款分布式存储系统,Ceph集结多个优良的特点:

  • 高扩展性:使用普通x86服务器,支持10~1000台服务器,支持TB到PB级的扩展。
  • 高可靠性:没有单点故障,多数据副本,自动管理,自动修复。
  • 高性能:数据分布均衡,并行化度高。对于objects storage和block storage,不需要元数据服务器。
screensho2t

Figure 3.1 – Ceph结构图

Ceph底层提供了分布式的RADOS存储,用与支撑上层的librados和RGW(对象存储)、RBD(块存储)、CephFS(文件系统存储)等服务。Ceph实现了非常底层的对象存储,是纯粹的SDS,并且支持XFS文件系统,能轻易得Scale,没有单点故障。Ceph 使用自动恢复、多副本技术,即使有多台存储节点发生故障,也不影响数 据的可靠性和可用性。 全分布式架构放入底层支持多硬盘同时并行工作,容量和性能可以线性扩展。凭借其优异的性能,目前已经有越来越多的公司使用Ceph作为分布式存储解决方案,而这其中,UMCloud已经实现部署30PB级别的Ceph集群,是目前世界上规模比较大的Ceph集群。

对象存储-为互联网而生

随着互联网、Web2.0技术的快速更替,用户可以分享海量的照片、视频、音乐。全世界的Web应用每天会创建出成千上亿的小文件;Youtube、Facebook这些热门网站每天都新增数十亿条内容,人们每天发送数千亿封电子邮件。据IDC统计未来在10年间数据将增长44倍,到2020年全球数据将增加到35ZB,其中80%是非结构化数据,且大部分是非活跃数据。

面对如此庞大的数据上传量,块存储(SAN)和文件存储(NAS)显得有些局促。通常块存储的一个LUN容量仅数TB,而一个文件系统支持的文件数量通常只在百万级别。人们需要一种全新的架构的存储系统来满足超大的数据量,这种存储系统需要具备极高的可扩展性,能够满足人们对存储容量TB到EB规模的扩展的需求。在这个大时代背景下,对象存储应运而生。

对象存储是一种基于对象的数据存储架构,具备智能、自我管理能力,采用扁平化结构管理所有数据,用户可以通过web服务协议实现对象的读写和存储资源的访问。文件系统存储中数据被看成一个文件层级树,在块存储中数据被区分成块和条带,而在对象存储中,数据则以对象的方式管理。一个对象包含数据本身和其属性(元数据),以及一个全局唯一的标识符。由于元数据被单独区分了出来,用户可以为一个对象自定义元数据,从而实现更好的数据索引。

2006年,云计算巨头Amazon发布S3,其使用的RESTful和SOAP访问接口成为了对象存储的事实标准。S3支持存储最大为5TB的单个任意对象,并且每个对象可以分配高达2KB的元数据。在S3中,对象被组织存放在存储桶(Bucket)中,用户在秘钥认证后,就可以通过使用RESTful样式的HTTP接口来访问存储桶、对象及相关的数元数据等。

s3

Figure 4.1 -Amazon S3 简化结构图

S3虽然不开源,并且收取每月0.15美金/GB的服务费用,但是凭借其提供包括Web托管,映像托管和备份系统等完整的存储方案,以及超强的稳定性(S3保证99.9%的每月正常运行时间服务水平协议,即每月不超过43分钟的停机时间),S3还是成为了全球对象存储客户青睐的方案,据报道,截至2013年4月,Amazon S3存储了超过2万亿个对象。

等等,难道我们非得使用AWS S3并且每个月交高昂的服务费来使用对象存储吗?答案是不需要,正如图三中所列出的,目前开源社区已经提供了成熟的对象存储解决方案了,并且提供了兼容S3的接口,所以我们可以搭建本地对象存储服务,让S3对接我们自己的存储集群,那么我们要用哪种存储集群呢?自然是Ceph。

rgw_architecture

Figure 4.2 – Ceph搭配S3提供对象存储

在上一章节我们已经介绍过,Ceph的组件RGW(Rados Gateway)封装了Rados接口而提供Gateway服务,并且RGW实现了S3兼容的接口,因此用户可以使用S3的命令行工具或是S3 SDK来使用RGW。

所以我们要干嘛

尽管Ceph目前已经很好地支持了对象存储,用户可以通过S3来操作存储桶、对象以及设置对象元数据,但是Ceph本身并没有元数据搜索的功能,不能索引元数据来访问对象。因此在接下来的内容中,本文会介绍如何使用ElasticSearch来实现Ceph的元数据搜索功能。

方案一

这个方案的架构比较直观,前端应用将对象上传给Ceph RGW中,并把这个对象的自定义元数据发送到ElasticSearch集群中。当用户需要获取某个对象时,先向ElasticSearch发送搜索请求来获取对象地址,该请求可以是对象名或是对象id号,亦或是用户自定义的元数据。等到ElasticSearch返回这个对象的地址后,前端应用通过这个地址来直接向Ceph RGW获取对象本身。

flowChart1
Figure 5.1.1 – 方案一架构图 对象上传

flowChart2

Figure 5.1.2 – 方案一架构图 对象获取

方案实现

部署Ceph集群

Ceph集群部署方法在这边不再详细赘述,大家可以自行查找部署方法,个人推荐使用Ceph-Ansible来部署,比较简单并且节省时间。

开启RGW服务

我们可以使用Ceph-Ansible来部署RGW,但是在这里还是想介绍一下手动配置开启RGW的方法。

1.每个rgw实例都需要一个授权用户及key,下面的例子中创建了一个名为client.radosgw.gateway的用户,并将密钥文件存储在/etc/ceph目录下:

`ceph auth get-or-create client.radosgw.gateway osd` `'allow rwx'` `mon` `'allow rwx'` `-o` `/etc/ceph/ceph``.client.radosgw.keyring`

2.将秘钥文件赋予RGW实例:

`ceph auth add client.radosgw.gateway --``in``-``file``=``/etc/ceph/ceph``.client.radosgw.keyring`

3.在/etc/ceph/ceph.conf这个Ceph集群配置文件中加入RGW实例的配置内容


`[client.radosgw.gateway]`

`fsid = xxxxxxxxxxxxxxxxx`

`mon_host = xxxxxxxx:xxxx`

`log` `file` `=` `/var/log/ceph/client``.radosgw.gateway.log`

`keyring =` `/etc/ceph/ceph``.client.radosgw.keyring`

`rgw_frontends =civetweb port=8001`

Ceph可以支持多种种RGW前端配置方法(Apache, Civetweb, Nginx等),这里我们使用默认的前端Civetweb,并让其监听8001端口,keyring参数对应上文中生成的秘钥文件地址。在实际操作中,我还遇到了许多RGW的跨域访问问题,由于本文侧重介绍元数据搜索方法,因此RGW跨域问题的解决方法会在以后的博文中详细解答。

4.启动RGW实例


`radosgw -c` `/etc/ceph/ceph``.conf -n client.radosgw.gateway`

-c参数指向Ceph集群配置文件, -n参数指向RGW实例名称

5.启动RGW实例后,我们可以通过向RGW发送GET请求来判断RGW服务是否正常开启

image

配置S3用户访问RGW

待RGW实例启动成功后,我们就需要给S3的访问做好准备。我们需要在本地Ceph集群中给S3访问创建账号,我们创建一个accessKey和secretKey均为admin的账号:

`radosgw-admin user create --uid=admin --secret-key=admin --access-key=admin --display-name=``"admin"`

部署ElasticSearch集群

我们可以在Elastic官网(https://www.elastic.co/downloads/elasticsearch)里下载到新版本的ElasticSearch,解压缩后直接运行即可(需要非root账户):


`.``/bin/elasticsearch` `-d`

``

-d参数让Elasticsearch运行在后台。由于这边只是POC,因此我们只开启了一个ElasticSearch节点,并且只开放本地端口,因此不需要修改config下的配置文件。

![screenshot4](http://upload-images.jianshu.io/upload_images/8376186-57be463de6509585.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Elasticsearch的默认端口为9200,同样的做法,我们通过检测端口号确定ElasticSearch正常运行。我们也可以发送一个HTTP请求来看看ElasticSearch运行情况:

curl '[http://localhost:9200/?pretty](http://localhost:9200/?pretty)'

``

ElasticSearch收到请求后会返回一组JSON格式的数据:

screenshot5

Demo演示

为了让整个流程可视化,我写了一个简单前端web应用,可以让用户来自定义一些元数据类型。用户上传对象时需要输入自定义元数据的值。用户随后可以搜索相应元数据来获取对象。ElasticSearch和S3都有相应的JS SDK,可以很方便地使用他们:S3只需要提供endpoint(指向RGW),accessKey和secretKey就可以来访问RGW,这三样东西在之前我们都已经准备好;ElasticSearch在程序中只需要指定ElasticSearch的端口即可。

metasearch_main

Figure 5.1.3 – Demo演示 – 对象搜索主页

我们在这边几个自定义元数据:’Bukcet’用来表明存储桶名称,’endpoint’用来标记RGW端口, ’Category’用以区分不同的对象类别。

metasearch_upload

Figure 5.1.4 – Demo演示 – 对象上传

前端在上传对象时,我们在刚才新添加的元数据‘Category’写入’car’,然后前端将对象本身发送至Ceph RGW端的bucket2中,将元数据发送到ElasticSearch。我们可以在后台检查是否元数据是否已经被上传到ElasticSearch,这里我们对整个ElasticSearch进行car字样全文搜索:

`curl -XGET` `'[http://localhost:9200/_search](http://localhost:9200/_search)'`` -d` `'{"query":{"match":{"_all":"car"}}}'`

screenshot8

在后台返回结果中我们可以看到这个对象的元数据已经被上传至ElasticSearch,并且可以看到这个对象名、owner(admin)、新增的元数据字段’category’以及对应的值为’car’。Elasticsearch根据结果相关性评分来对结果集进行排序,所谓的「结果相关性评分」就是文档与查询条件的匹配程度(_score参数)。

metasearch_search

Figure 5.1.5 – Demo演示 – 对象搜索

在搜索框中输入car,前端随即向ElasticSearch发送搜索请求,其实等同于上面我们在后台验证时发送的请求,前端会分析ElasticSearch返回的JSON数据并且提取对象地址,然后向RGW发送请求,RGW返回对象。

方案一总结

以上便是方案一的实现过程,不难看出,这个方案的思路比较直接,实现也不难,但是RGW与ElasticSearch之间的数据一致性完全依赖于前端应用。S3除了SDK以外还有s3cmd这个命令行工具,RGW自身也可以通过HTTP请求来上传对象,试想一下,用户完全有可能直接在后台上传对象,而不是通过前端应用,这样就造成了ElasticSearch无法同步对应的元数据。

方案二

Ceph在Jewel版本之后加入对ElasticSearch的支持,我们可以通过定义新的zone类型来实现RGW元数据自动同步到ElasticSearch中,这样一来便保证了RGW和ElasticSearch数据的一致性,让前后端耦合大大降低。

flowChart4

Figure 5.2.1 – 方案二架构图 对象上传

flowChart2

Figure 5.2.2 – 方案二架构图 对象获取

从架构图可以看出,方案一和方案二的唯一区别在与对象上传:前端上传对象时不再需要上传元数据到ElasticSearch,Ceph RGW中自带的sync plugin可以自动将元数据同步到ElasticSearch中去。

Ceph元数据同步原理

Ceph的元数据同步依赖于RGW Admin API,其更新的依据对应一组Zonegroup和Zone,等等,什么是Zonegroup和Zone?Zonegroup一般来代表逻辑上的地理区域(省会,国家等),一个Zonegroup中可以包含一个或多个Zone,并且需要指定一个Zone作为Master来处理来自前端的元数据写入请求,也就是说,我们只能往Master Zone里写入或是更新元数据。

zone

Figure 5.2.3 – Ceph对象存储数据同步示意图

更新Master Zone上的元数据更新日志会被记录在mdlog(metadata log)里,而Master Zone也是通过对比mdlog来把更新后的元数据推送到Slave Zone上。Slave zone上更新的元数据会被forward到Master Zone里,Master Zone确认更新后会主动把更新推送到所有Zone上。而同步的过程是通过RGW Admin API来获取mdlog,然后更新对应的marker。

方案实现

为了实现RGW的元数据同步到ElasticSearch,我们配置一个zonegroup(us),在其中添加2个zone:us-1(master), us-2(slave)。并且分别在每个zone上启一个RGW实例,RGW.us-1用来接受来自前端的读写请求,RGW.us-2用来把元数据同步至ElasticSearch。

fangan2

Figure 5.2.4 – 方案二具体架构图

部署Ceph集群

同方案一

Ceph集群配置

1.为了实现Zone之间的同步,我们首先需要为集群创建一个realm:


`radosgw-admin -c .``/ceph``.conf realm create --rgw-realm=earth --default`

realm是Ceph集群的一个独立命名空间,每个realm可以包括多个Zonegroup从而来实现Zonegroup之间的同步,每个 realm 都有关联的当前的period 以及一组按照时间顺序排列的 period 列表,还有zonegroup的当前配置。

2.在earth这个命名空间下创建一个zonegroup: us


`radosgw-admin -c .``/ceph``.conf zonegroup create --rgw-zonegroup=us --endpoints=http:``//localhost``:8001 --master --default --rgw-realm=earth`

3.在us这个zonegroup下面创建一个master zone: us-1:


`radosgw-admin -c .``/ceph``.conf zone create --rgw-zonegroup=us --rgw-zone=us-1 --endpoints=http:``//localhost``:8001 --master --default --access-key=admin --secret=admin`

注意master zone的endpoint需要对应zonegroup的endpoint

4.创建一个s3用户:


`radosgw-admin -c .``/ceph``.conf user create --uid=admin --display-name=``"admin"` `--access-key=admin --secret=admin --system`

5.提交集群配置:


`radosgw-admin -c .``/ceph``.conf period update --commit`

一个period用来标定当前realm的状态(zonegroup以及zone的配置),其拥有唯一的id和epoch。编辑一个zonegroup或是zone后的period更新处于stagging状态并不生效,如果要生效需要运行这行命令。

6.在us这个zonegroup下面创建第二个zone(非master):us-2


`radosgw-admin -c .``/ceph``.conf zone create --rgw-zonegroup=us --rgw-zone=us-2 --endpoints=http:``//localhost``:8002 --access-key=admin --secret=admin --tier-``type``=elasticsearch`

这边的tier-type字段用来告诉zone,我们需要用哪个module进行跨zone的数据同步,这里我们当然填elasticsearch。

7.让us-2获取period更新:


`radosgw-admin period pull --rgw-zone=us-2`

8.修改us-2的配置,让其指向ElasticSearch:


`radosgw-admin -c .``/ceph``.conf zone modify --rgw-zonegroup=us --rgw-zone=us-2 --access-key=admin --secret=admin --tier-config=endpoint=http:``//localhost``:9200`

我们在tier-config这个参数中添加endpoint这个key,并且让它指向ElasticSearch监听的端口。

9.再次提交集群配置:


`radosgw-admin -c .``/ceph``.conf period update --commit --rgw-zone=us-2`

启动RGW实例

RGW配置以及对应的秘钥生成方法与方案一中相同,只是需要监听不同的端口:


`[client.radosgw.us-1]`

`fsid = xxxxxxxxxxxxxxxxx`

`mon_host = xxxxxxxx:xxxx`

`keyring =` `/etc/ceph/ceph``.client.radosgw.keyring`

`log` `file` `=` `/etc/ceph/ceph``.client.radosgw.us-1.log`

`rgw frontends = civetweb port=8001`

`rgw zone = us-1`

`[client.radosgw.us-2]`

`fsid = xxxxxxxxxxxxxxxxx`

`mon_host = xxxxxxxx:xxxx`

`keyring =` `/etc/ceph/ceph``.client.radosgw.keyring`

`log` `file` `=` `/etc/ceph/ceph``.client.radosgw.us-2.log`

`rgw frontends = civetweb port=8002`

`rgw zone = us-2`

开启这两个RGW实例后,RGW会自动开始同步元数据,我们可以尝试通过前端上传对象,然后在ElasticSearch端检测元数据是否同步成功。

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

推荐阅读更多精彩内容