zookeeper

1 概述:

1.开源的,分布式的,为分布式服务提供协调服务的Apache 项目。
2.基于观察者模式设计的分布式服务管理框架, 负责存储和管理数据,接受观察者的注册, 一旦数据状态变化, 将通知已经注册的观察者做出反应

2 特点

  1. Zookeeper 由一个领导者(leader, 有且只有一个), 多个跟随者(follower)组成的集群
  2. 集群中只有半数以上的节点存活,Zookeeper集群才能正常服务,所以zookeeper 适合安装奇数台服务器
  3. 全局数据一致性:每个存活的节点保存一份相同的数据副本, client无论链接到哪个节点,数据都是一致的
  4. 更新请求顺序执行, 来自同一个client的更新请求按其发送的顺序依次执行
  5. 数据更新原子性,一次数据更新要么成功,要么失败
  6. 实时性: 在一定时间范围内,client能读取到最新的数据

3 节点

zookeeper的数据模型和结构与unix文件系统类似, 整体可以看作是一棵树,每个节点称作为ZNode, 每个ZNode 默认能存储1MB的数据, 每个ZNode都可以通过其路径唯一标识

数据结构
3.1 节点类型:

节点可分为持久节点,临时节点,持久顺序节点,临时顺序节点

  • 持久节点(PERSISTENT):
    客户端和服务器断开链接后,创建的节点不会被删除
  • 持久顺序节点(PERSISTENT_SEQUENTIAL):
    客户端和服务器断开链接后,创建的节点不会被删除, 而且对该节点进行顺序编号
  • 临时节点(EPHEMERAL):
    客户端和服务器断开链接后,创建的节点会被删除
  • 临时顺序节点(EPHEMERAL_SEQUENTIAL):
    对该节点进行顺序编号,客户端和服务器断开链接后,创建的节点会被删除
3.2 节点信息

例:创建一个值为test持久节点 /abcd

[zk: localhost:2181(CONNECTED) 28] create /abcd "test"
Created /abcd

查看/abcd 节点信息

[zk: localhost:2181(CONNECTED) 29] get -s /abcd
test.     
cZxid = 0x10000000b
ctime = Thu Feb 10 20:12:28 CST 2022
mZxid = 0x10000000b
mtime = Thu Feb 10 20:12:28 CST 2022
pZxid = 0x10000000b
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
名称 含义
test 对应节点的值
cZxid 创建节点的事务id
ctime 创建节点的时间(ms), 从1970开始
mZxid 节点最后更新的事务id
mtime 节点最后更新的时间(ms),从1970开始
pZxid 节点对应子节点事务ID
cversion 节点对应子节点的版本号,子节点的变化数
dataVersion 节点数据版本号
aclVersion 节点权限版本号
ephemeralOwner 判断是否是临时节点,如果不是,则为0 ,如果是则为对应的sessionId
dataLength 节点数据长度
numChildren 节点对应的子节点数
3.3 节点监听(watch)

使用watcher机制实现了数据的发布订阅功能功能,当订阅的对象发生变化时会异步通知客户端

  1. watch 特性
特性 说明
一次性触发 watcher 是一次性触发的,一旦被触发就会被移除,再次使用时需要重新注册
客户端顺序回调 watcher 回调是顺序串行化执行的,只有回调后客户端才能看到最新的数据状态,一个watcher回调逻辑不应该太多,以免影响别的watcher执行
轻量级 watchEvent 是最小的通信单元,结构上只包含通知状态,事件类型和节点路径,并不会告诉数据节点前后变化的具体内容
实效性 watcher只有在当前的session彻底失效才会无效,若session有效期间内快速重新连接成功,则watcher依然有效,仍可收到通知
  1. watchEvent
    当数据有变化时,服务端会返回对应的watchEvent对象给客户端, 该对象中包括了通知的状态(keepState)和事件的类型(eventType)
    -. 通知状态(keepState)
    客户端和服务端连接状态发生变化时的对应通知类型
属性 说明
SyncConnected 客户端与服务器正常连接
Disconnected 客户端与服务器断开连接时
Expired 回话session失效时
AuthFailed 身份认证失效时

-. 事件类型(eventType)
节点(znode)发生变化时对应的通知类型,EventType变化时keepState都会处于SyncConnected的通知状态下;当keepState发生变化时,EventType永远为None

类型属性 说明
None
NodeCreated Watcher监听的数据节点被创建
NodeDeleted Watcher监听的数据节点被删除
NodeDataChanged Watcher监听的数据节点内容发生变更时(不论内容数据是否变化)
NodeChildrenChanged Watcher监听节点的子节点列表发生变化

注:客户端接收到的相关事件通知中只包含状态及类型等信息,不包括数据节点变化前后的具体内容,变化后的数据需要调用get等方法重新获取

  1. 捕获相应的事件
    在zookeeper 中采用zk.getChildren(path,watch), zk.exists(path,watch), zk.getData(path,watch)方式为某个节点注册监听
注册方式 Created ChildrenChanged Changed Deleted
zk.getChildren(path,watch) 可监控 可监控
zk.exists(path,watch) 可监控 可监控 可监控
zk.getData(path,watch) 可监控 可监控
3.4 节点权限(ACL)

ACL:Access Control List 访问控制列表
使用 scheme:id:perm 来标识,主要涵盖 3 个方面:
  权限模式(Scheme):授权的策略
  授权对象(ID):授权的对象
  权限(Permission):授予的权限

其特性如下:
  -.ZooKeeper的权限控制是基于每个znode节点的,需要对每个节点设置权限
  -.每个znode支持设置多种权限控制方案和多个权限
  -.子节点不会继承父节点的权限,客户端无权访问某节点,但可能可以访问它的子节点

3.4.1 权限模式

scheme 采用何种方式授权

  1. world:默认方式,相当于全部都能访问
  2. auth:代表已经认证通过的用户(cli中可以通过addauth digest
    user:pwd 来添加当前上下文中的授权用户)
  3. digest:即用户名:密码这种方式认证,这也是业务系统中最常用的。 用 username:password 字符串来产生一个MD5串,然后该串被用来作为ACL ID。认证是通过明文发送username:password 来进行的,当用在ACL时,表达式为username:base64 ,base64是password的SHA1摘要的编码。
  4. ip:使用客户端的主机IP作为ACL ID 。这个ACL表达式的格式为addr/bits ,此时addr中的有效位与客户端addr中的有效位进行比对。
  5. super: 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)
3.4.2 授权对象
授权模式 授权对象
world 只有一个用户:anyone, 代表登陆zookeeper的所有人(默认)
ip 通常是一个ip地址或者段,例如:192.169.19.22 或者 192.169.19.1/24
auth 使用已添加的认证的用户认证 username:password
digest 与auth类似,只不过password需要加密处理,username:BASE64(SHA-1(username:password))
super 与digest一致
3.4.3 授予的权限

CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,这5种权限简写为crwda

注意:这5种权限中,delete是指对子节点的删除权限,其它4种权限指对自身节点的操作权限

更详细的如下:

权限 缩写 含义
CREATE c 可以创建子节点
DELETE d 可以删除子节点(仅下一级节点)
READ r 可以读取节点数据及显示子节点列表
WRITE w 可以设置节点数据
ADMIN a 可以设置节点访问控制列表权限

4 选举机制

首先了解如下几个概念:

  1. SID: 服务器id,用来表示集群中该机器,每台机器不能重复,与myid一致
  2. ZXID:事物id ,用来标识一次服务器状态的变更,在某一时刻,集群中的每一台服务器的ZXID不一定相同
  3. Epoch:每个leader任期的代号,没有leader时,同一轮投票选举逻辑时钟是一致的,每投完一轮,这个数据就会增加
4.1 初次启动选举机制

假设有5台服务器组成的的zookeeper集群,server ID为1~5,同时他们是最新启动的,且没有历史数据,在存放数据量上面是一样的


WX20220210-154003@2x.png

此时依次按顺序启动服务,
1.服务器1 启动,此时没有leader节点,服务器1 发起选举投票,并投给自己,此时投票不超过半数(3票),选举失败,此时服务1处于looking状态,

  1. 服务器2 启动,此时再次发起leader选举,服务器1和2 分别投票给自己,并互相交换选举结果,此时服务器1 发现自己的myid小于服务器2的,并且都没有历史数据,服务器1就会更改投票给服务器2,此时服务器2 有2 票,服务器1 有0票,都没有超过半数投票,选举失败,服务器1 和2 都处于looking状态
  2. 服务器3 启动,如果服务器2 启动时一样,再次选举,由于服务器3的myid最大,最终服务器3 有3 票,满足超过半数投票,服务器3 就选举为leader节点,服务器1,2为follower几点,集群启动成功
  3. 服务器4 启动,leader节点已经存在,此时服务器4 成为follwer节点
  4. 服务器5 启动,和服务器4一样,也成为follower节点
4.2 非第一次启动选举
image.png

当集群中的一台服务器出现以下情况时,此时会进入选举机制

  1. 服务器初始化启动
  2. 服务器运行期间和leader节点丢失通信

当一台机器开始选举时,会出现如下几种情形之一:
1.集群中已经有leader了,此时该机器发起选举时,会被告知leader节点信息,此时该机器只要和leader节点建立连接并完成同步就行

  1. 集群中leader节点挂了,
    假设 5台服务器的sid为1,2,3,4,5, ZXID为8,8,8,7,7。
    此时服务器3(leader)和服务器5(follower)挂了,此时剩下的1,2,4 服务器重新开始选举.

选举规则: Epoch大的直接胜出, Epoch相同,ZXID大的胜出,Epoch和ZXID都相同的,服务器id大的胜出

由于1,2,4的Epoch 相同, 但是2的ZXID,serverID 相对于最大,此时服务器2 就被选举为新的leader

5 安装(Ver 3.5.7)

5.1 配置参数解读:

参数名 含义
clientPort 客户端连接server的端口,即对外服务端口,一般设置为2181
dataDir 存储快照文件snapshot的目录。默认情况下,事务日志也会存储在这里。建议同时配置参数dataLogDir, 事务日志的写性能直接影响zk性能
tickTime ZK中的一个时间单元。ZK中所有时间都是以这个时间单元为基础,进行整数倍配置的。例如,session的最小超时时间是2*tickTime
dataLogDir 事务日志输出目录。尽量给事务日志的输出配置单独的磁盘或是挂载点,这将极大的提升ZK性能
globalOutstandingLimit 最大请求堆积数。默认是1000。ZK运行的时候, 尽管server已经没有空闲来处理更多的客户端请求了,但是还是允许客户端将请求提交到服务器上来,以提高吞吐性能。当然,为了防止Server内存溢出,这个请求堆积数还是需要限制下的
preAllocSize 预先开辟磁盘空间,用于后续写入事务日志。默认是64M,每个事务日志大小就是64M。如果ZK的快照频率较大的话,建议适当减小这个参数
snapCount 每进行snapCount次事务日志输出后,触发一次快照(snapshot), 此时,ZK会生成一个snapshot.文件,同时创建一个新的事务日志文件log.。默认是100000.(真正的代码实现中,会进行一定的随机数处理,以避免所有服务器在同一时间进行快照而影响性能)
traceFile 用于记录所有请求的log,一般调试过程中可以使用,但是生产环境不建议使用,会严重影响性能
maxClientCnxns 单个客户端与单台服务器之间的连接数的限制,是ip级别的,默认是60,如果设置为0,那么表明不作任何限制。请注意这个限制的使用范围,仅仅是单台客户端机器与单台ZK服务器之间的连接数限制,不是针对指定客户端IP,也不是ZK集群的连接数限制,也不是单台ZK对所有客户端的连接数限制。指定客户端IP的限制策略,这里有一个patch,可以尝试一下:[http://rdc.taobao.com/team/jm/archives/1334](http://rdc.taobao.com/team/jm/archives/1334
clientPortAddress 对于多网卡的机器,可以为每个IP指定不同的监听端口。默认情况是所有IP都监听 clientPort 指定的端口
minSessionTimeoutmaxSessionTimeout 事务日志输出时,如果调用fsync方法超过指定的超时时间,那么会在日志中输出警告信息。默认是1000ms
fsync.warningthresholdms 用于记录所有请求的log,一般调试过程中可以使用,但是生产环境不建议使用,会严重影响性能
autopurge.purgeInterval 在上文中已经提到,3.4.0及之后版本,ZK提供了自动清理事务日志和快照文件的功能,这个参数指定了清理频率,单位是小时,需要配置一个1或更大的整数,默认是0,表示不开启自动清理功能
autopurge.snapRetainCount 这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目。默认是保留3个
electionAlg 在之前的版本中, 这个参数配置是允许我们选择leader选举算法,但是由于在以后的版本中,只会留下一种“TCP-based version of fast leader election”算法,所以这个参数目前看来没有用了,这里也不详细展开说了
initLimit Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。Leader允许F在 initLimit 时间内完成这个工作。通常情况下,我们不用太在意这个参数的设置。如果ZK集群的数据量确实很大了,F在启动的时候,从Leader上同步数据的时间也会相应变长,因此在这种情况下,有必要适当调大这个参数了
syncLimit 在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。如果L发出心跳包在syncLimit之后,还没有从F那里收到响应,那么就认为这个F已经不在线了。注意:不要把这个参数设置得过大,否则可能会掩盖一些问题
leaderServes 默认情况下,Leader是会接受客户端连接,并提供正常的读写服务。但是,如果你想让Leader专注于集群中机器的协调,那么可以将这个参数设置为no,这样一来,会大大提高写操作的性能
server.x=[hostname]:nnnnn[:nnnnn] 这里的x是一个数字,与myid文件中的id是一致的。右边可以配置两个端口,第一个端口用于F和L之间的数据同步和其它通信,第二个端口用于Leader选举过程中投票通信。
group.x=nnnnn[:nnnnn]weight.x=nnnnn 对机器分组和权重设置,可以 参见这里
cnxTimeout Leader选举过程中,打开一次连接的超时时间,默认是5s
zookeeper.DigestAuthenticationProvider.superDigest ZK权限设置相关,具体参见 使用super **身份对有权限的节点进行操作 **》 ZooKeeper **权限控制 **》
skipACL 对所有客户端请求都不作ACL检查。如果之前节点上设置有权限限制,一旦服务器上打开这个开头,那么也将失效
forceSync 这个参数确定了是否需要在事务日志提交的时候调用 [FileChannel ](http://rdc.taobao.com/team/%5C/java%5C/jdk1.6.0_22%5C/jre%5C/lib%5C/rt.jar%3Cjava.nio.channels(FileChannel.class%E2%98%83FileChannel).force来保证数据完全同步到磁盘
jute.maxbuffer 每个节点最大数据量,是默认是1M。这个限制必须在server和client端都进行设置才会生效
5.2. 单机节点
  1. 解压安装包
tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz
  1. 修改配置文件
    将 解压后文件夹下的conf 路径下的zoo_sample.cfg 改为zoo.cfg,
    设置配置文件中参数 dataDir
    配置文件:
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/root/zookeeper/apache-zookeeper-3.5.7-bin/data
# the port at which the clients will connect
clientPort=2181 
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
  1. 启动
    在解压文件的bin目录中
[root@iZuf6g3hri8hvnuqng6id7Z bin]  ./zkServer.sh --config ../conf start
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: ../conf/zoo.cfg
Starting zookeeper ... 、STARTED

  1. 停止
[root@iZuf6g3hri8hvnuqng6id7Z bin]  ./zkServer.sh stop
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /root/zookeeper/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED

  1. 查看状态
[root@iZuf6g3hri8hvnuqng6id7Z bin]# ./zkServer.sh  status
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /root/zookeeper/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: standalone

5.3. 集群

示例: 在三个服务器上部署三个zookeeper节点,组成集群

  1. 解压安装:
    和单机节点安装一样,在三台服务器上分别解压设置dataDir目录
  2. 配置服务器编号
    在参数dataDir 目录下创建myid文件, 此文件说明当前zookeeper在集群中的唯一编号,在文件中填写对应server的编号(例如1,2,3)
  3. 配置集群文件
    在每个节点的配置文件中添加集群的信息
# server.A=B:C:D
#A: serverId 与myid文件的编号对应
#B:服务器地址
#C:follower 与leader 通信的端口
#D:leader节点挂了之后,重新选举时的端口号
server.1=139.196.58.234:2888:3888
server.2=139.196.58.235:2888:3888
server.3=139.196.58.236:2888:3888
  1. 分别启动zookeeper 节点

6 客户端操作

6.1 常用节点操作
语法 含义
ls path 查看当前节点的子节点。-w 监听子节点变化, -s 附加次级信息
create path 创建节点 -e 创建临时节点, -s 创建顺序节点
get path 获得节点对应的值 -w 监听节点内容变化 -s 附加次级信息
set path value 设置节点具体值
state path 查看节点状态
delete path 删除节点
deleteall path 递归删除节点
6.2 权限操作
  1. 获取节点权限
    客户端操作指令:getAcl path
#创建一个/abcd节点, 观察节点的权限,此时默认是world:anyone:cdrwa 的权限
[zk: localhost:2181(CONNECTED) 4] create /abcd "test"
Created /abcd
[zk: localhost:2181(CONNECTED) 5] getAcl  /abcd
'world,'anyone
: cdrwa
  1. 设置节点权限
    指令 setAcl path acl
#world 权限设置 setAcl path world:anyone:<perm>
#设置/abcd 节点没有创建子节点的权限,此时就不能创建子节点了
[zk: localhost:2181(CONNECTED) 6] setAcl /abcd world:anyone:drwa
[zk: localhost:2181(CONNECTED) 8] getAcl  /abcd
'world,'anyone
: drwa
[zk: localhost:2181(CONNECTED) 7] create /abcd/a "a"
Authentication is not valid : /abcd/a

#ip 权限设置 setAcl path ip:<ip>:<perm>
[zk: localhost:2181(CONNECTED) 9] setAcl /abcd ip:127.0.0.1:cdrwa
[zk: localhost:2181(CONNECTED) 10] getAcl  /abcd
'ip,'127.0.0.1
: cdrwa
# 此时/abcd 节点的cdrwa 权限只能是ip为127.0.0.1的客户端才拥有
# 可同时支持设置多个ip地址的权限, 以逗号分隔
[zk: localhost:2181(CONNECTED) 11] setAcl /abcd ip:127.0.0.1:cdrwa,ip:192.162.12.11:cdrwa
[zk: localhost:2181(CONNECTED) 12] getAcl  /abcd
'ip,'127.0.0.1
: cdrwa
'ip,'192.162.12.11
: cdrwa



  1. 添加认证用户
    指令 addauth digest <auth>
#添加 用户名为gagaya 密码为123456的认证用户
[zk: localhost:2181(CONNECTED) 0] addauth digest gagaya:123456
# auth授权模式, 需要现通过addauth 添加认证用户(登陆)
[zk: localhost:2181(CONNECTED) 1] setAcl /abcd auth:gagaya:cdrwa 
[zk: localhost:2181(CONNECTED) 2] getAcl /abcd
'digest,'gagaya:MiCKcSLnJ5sLUpkr1wj81iMhnKQ=
: cdrwa

#digest 授权模式,
#setAcl  path digest username:password(加密处理):<perm>,
# 在Linux中可用以下指令获取加密密码 
#echo -n <username>:<password> | openssl dgst -binary -sha1 |openssl base64 
[root@iZuf6g3hri8hvnuqng6id7Z ~]# echo -n gagaya:123456 | openssl dgst -binary -sha1 |openssl base64
MiCKcSLnJ5sLUpkr1wj81iMhnKQ=

# 使用digest授权模式相比于auth 模式需要提供用户密码的加密密钥
[zk: localhost:2181(CONNECTED) 7] setAcl /abcd digest:gagaya:MiCKcSLnJ5sLUpkr1wj81iMhnKQ=:cdrwa 
[zk: localhost:2181(CONNECTED) 8] getAcl /abcd
'digest,'gagaya:MiCKcSLnJ5sLUpkr1wj81iMhnKQ=
: cdrwa
  1. 多种授权模式
    同一个节点可以设置多种授权的模式
# 用逗号分隔
[zk: localhost:2181(CONNECTED) 11] setAcl /abcd digest:gagaya:MiCKcSLnJ5sLUpkr1wj81iMhnKQ=:cdrwa,ip:127.0.0.1:cdrwa 
[zk: localhost:2181(CONNECTED) 12] getAcl /abcd
'digest,'gagaya:MiCKcSLnJ5sLUpkr1wj81iMhnKQ=
: cdrwa
'ip,'127.0.0.1
: cdrwa

  1. 超级管理员(super)
    超级管理员,可以访问任何权限的节点,只能在启动服务器的时候添加。
    假设这个超管是:super:admin
    先获取加密后的密码:
[root@iZuf6g3hri8hvnuqng6id7Z ~]# echo -n super:admin | openssl dgst -binary -sha1 |openssl base64
xQJmxLMiHGwaqBvst5y6rkB6HQs=

打开zk目录下的/bin/zkServer.sh服务器脚本文件,找到如下一行:

nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}"

这就是脚本中启动zk的命令,默认只有以上两个配置项,我们需要加一个超管的配置项

"-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBvst5y6rkB6HQs="

那么修改以后这条完整命令变成了:

nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBvst5y6rkB6HQs="\
    -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &

之后启动zk集群, 然后在客户端链接集群后,添加对应的超级用户,此时客户端就是超级管理员了

addauth digest super:admin
7. 共识协议

7 应用场景

提供的服务包括:统一命名服务, 统一配置管理,通知集群管理等,服务节点动态上下线,软负载均衡等

4.1 统一命名服务
4.2 统一配置管理

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

推荐阅读更多精彩内容

  • 如何成为一个会学习的人 粂原圭太郎 188个想法 前言 >> 一旦身体开始下滑,即使想停也停不下来,这就是学习...
    悠思情趣阅读 332评论 0 1
  • 背景 在使用AS创建新项目时,创建的项目中会生成很多预定的代码,比如gradle、AndroidManifest....
    颈椎以上瘫痪阅读 1,423评论 0 0
  • 这是我发表在内蒙古网站12348的一篇征文选登 ▏我亲爱的母亲——中国共产党,写于2021年6月底,在这里分享给大...
    此城阅读 499评论 0 6
  • 值得期待的L2扩容方案 - Rollup Rollup 最近在 Ethereum 社区风靡一时,有望在未来成为 E...
    Aaron阿酷阅读 174评论 0 0
  • 我是一个兵 耿士鹏 疫情爆发以来,在上下一心,众志成城,向疫情攻坚的...
    寿元堂阅读 773评论 0 0