Spring Boot 2.0 集成 Dubbo 示例

简单介绍下 Dubbo

http://dubbo.apache.org/

Apache Dubbo™ (incubating) is a high-performance, java based, open source RPC framework.

通常情况 , Dubbo 应用有两种使用场景 , 其一为 Dubbo 服务提供方 , 另外一个是 Dubbo 服务消费方。

Apache Dubbo (incubating) offers three key functionalities:

  • interface based remote call 基于接口的远程调用
  • fault tolerance & load balancing 容错及负载均衡
  • automatic service registration & discovery 自动化服务注册与发现

Dubbo 的主要的服务注册发现功能便是由 Zookeeper 来提供的。ZooKeeper 是一个高可用的分布式数据管理与系统协调框架。基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题。

Dubbo 架构

Spring Boot 2.0 集成 Dubbo

通过 http://start.dubbo.io/ 构造基于 maven 的项目框架,包括服务提供端和服务消费端。

服务提供端 serviceDemo

服务消费端 clientDemo

在 IntelliJ IDEA 中分别打开这两个项目。
由于在 http://start.dubbo.io/ 只能选择 1.X 版本的 Spring Boot,因此我们需要手动修改两个项目pom.xml文件。参考 https://github.com/apache/incubator-dubbo-spring-boot-project/blob/master/README_CN.md

  • 修改 spring-boot-starter-parent 的版本号为 2.0.3.RELEASE
  • 修改 dubbo-spring-boot-starter 的版本号为 0.2.0
  • 添加如下内容:
<repositories>
    <repository>
        <id>sonatype-nexus-snapshots</id>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

分析服务提供端 serviceDemo

serviceDemo 项目结构

application.properties:Dubbo 的基本配置

dubbo.application.name = dubbo-demo-server

# Base packages to scan Dubbo Component: @com.alibaba.dubbo.config.annotation.Service
dubbo.scan.basePackages  = com.example

## RegistryConfig Bean
dubbo.registry.id = my-registry
dubbo.registry.address = zookeeper://localhost:2181?client=curator


dubbo.application.qosEnable=false

HelloService.java:接口,以后通过接口来进行服务调用

public interface HelloService {

    String sayHello(String name);

}

HelloServiceImpl.java:服务的具体实现

package com.example;

import java.util.Date;

import com.alibaba.dubbo.config.annotation.Service;

@Service(version = "1.0.0")
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String name) {
        return "Hello, " + name + ", " + new Date();
    }

}

ServiceDemoApplication.java:程序入口,修改为如下内容

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;

@SpringBootApplication
public class ServiceDemoApplication {


    public static void main(String[] args) {
        
        // start embedded zookeeper server
        new EmbeddedZooKeeper(2181, false).start();

        new SpringApplicationBuilder(ServiceDemoApplication.class)
                .web(false) // 非 Web 应用
                .run(args);
    }
    
}

EmbeddedZooKeeper.java:内嵌的 ZooKeeper 实现。

分析服务消费端 clientDemo

clientDemo 项目结构

application.properties:Dubbo 的基本配置

# Dubbo Config properties
## ApplicationConfig Bean
dubbo.application.name= dubbo-demo-client

## RegistryConfig Bean
dubbo.registry.id = my-registry
dubbo.registry.address = zookeeper://localhost:2181?client=curator




dubbo.application.qosEnable=false

HelloService.java:接口,以后通过接口来进行服务调用

public interface HelloService {

    String sayHello(String name);

}

ClientDemoApplication.java:程序入口,修改为如下内容

import com.alibaba.dubbo.config.annotation.Reference;
import com.example.HelloService;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;

import javax.annotation.PostConstruct;

@SpringBootApplication
public class ClientDemoApplication {

    @Reference(version = "1.0.0")
    private HelloService demoService;

    public static void main(String[] args) {
        
        new SpringApplicationBuilder(ClientDemoApplication.class)
                .web(false) // 非 Web 应用
                .run(args);
    }
    
    @PostConstruct
    public void init() {
        String sayHello = demoService.sayHello("world");
        System.err.println(sayHello);
    }
}

测试

分别通过 mvn spring-boot:run 启动两个项目,可以在服务消费端看到如下的日志内容,表明调用成功:

2018-06-27 14:24:42.828  INFO 36771 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Register: consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=consumers&check=false&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 14:24:42.865  INFO 36771 --- [localhost:2181)] org.apache.zookeeper.ClientCnxn          : Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x1643fe4a4410001, negotiated timeout = 60000
2018-06-27 14:24:42.878  INFO 36771 --- [ain-EventThread] o.a.c.f.state.ConnectionStateManager     : State change: CONNECTED
2018-06-27 14:24:42.916  INFO 36771 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Subscribe: consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 14:24:42.948  INFO 36771 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Notify urls for subscribe url consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, urls: [dubbo://10.93.67.135:20880/com.example.HelloService?anyhost=true&application=dubbo-demo-server&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=36354&revision=1.0.0&side=provider&timestamp=1530080308262&version=1.0.0, empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=configurators&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0], dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 14:24:43.152  INFO 36771 --- [           main] c.a.d.remoting.transport.AbstractClient  :  [DUBBO] Successed connect to server /10.93.67.135:20880 from NettyClient 10.93.67.135 using dubbo version 2.6.2, channel is NettyChannel [channel=[id: 0x580c5d4e, /10.93.67.135:52308 => /10.93.67.135:20880]], dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 14:24:43.153  INFO 36771 --- [           main] c.a.d.remoting.transport.AbstractClient  :  [DUBBO] Start NettyClient acbc32941d79.ant.amazon.com/10.93.67.135 connect to the server /10.93.67.135:20880, dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 14:24:43.227  INFO 36771 --- [           main] com.alibaba.dubbo.config.AbstractConfig  :  [DUBBO] Refer dubbo service com.example.HelloService from url zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=dubbo-demo-client&check=false&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=36771&qos.enable=false&register.ip=10.93.67.135&remote.timestamp=1530080308262&revision=1.0.0&side=consumer&timestamp=1530080682446&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 14:24:43.239  INFO 36771 --- [           main] c.a.d.c.s.b.f.a.ReferenceBeanBuilder     : <dubbo:reference object="com.alibaba.dubbo.common.bytecode.proxy0@19da400d" singleton="true" interface="com.example.HelloService" uniqueServiceName="com.example.HelloService:1.0.0" generic="false" version="1.0.0" id="com.example.HelloService" /> has been built.
Hello, world, Wed Jun 27 14:24:43 CST 2018

将上述的服务注册到 Zookeeper

在上面的例子是,我们是使用了一个内嵌的 Zookeeper 来注册与发现服务,现在我们启动一个外部的 Zookeeper 服务来替代。

简单介绍下 Zookeeper,https://zookeeper.apache.org/
通过如下命令在 mac 中安装 Zookeeper:

brew info zookeeper
brew install zookeeper

安装完成后,默认配置文件在 /usr/local/etc/zookeeper/ 中。
通过命令 zkServer 启动 Zookeeper,默认端口是2181。

xianch@acbc32941d79 ⮀ /usr/local/etc/zookeeper ⮀ zkServer
ZooKeeper JMX enabled by default
Using config: /usr/local/etc/zookeeper/zoo.cfg
Usage: ./zkServer.sh {start|start-foreground|stop|restart|status|upgrade|print-cmd}

客户端(服务提供者与消费者)与 Zookeeper 是 TCP 长连接,默认对外端口是 2181,通过这个连接,客户端保持和 Zookeeper 服务器的心跳以维护连接,也能向 Zookeeper 发送请求并响应,同时还可以接收到注册通知。

可以通过命令 zkServer statuszkServer stop 来分别查看和关闭 Zookeeper。

服务提供端 serviceDemo

注释掉 ServiceDemoApplication.java 中的 new EmbeddedZooKeeper(2181, false).start();

启动程序,日志中可以看出,注册了一个服务 dubbo://10.93.67.135:20880/com.example.HelloService

2018-06-27 14:52:28.053  INFO 41887 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Register: dubbo://10.93.67.135:20880/com.example.HelloService?anyhost=true&application=dubbo-demo-server&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=41887&revision=1.0.0&side=provider&timestamp=1530082347373&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135

注意,每一个服务都需要一个未被使用的 dubbo 端口 ,默认是 20880,也可以通过 spring.dubbo.protocol.port=xxxxx 来配置。

做一个实验,在配置中修改端口为 spring.dubbo.protocol.port=20881,将实现类 HelloServiceImpl 做一个小的修改:变成 New Hello。然后重新打开一个 Terminal,启动程序。

public String sayHello(String name) {
        return "New Hello, " + name + ", " + new Date();
    }

此时,通过 zkCli 可以查看 Zookeeper 的信息:可以看出,已经注册了一个服务 com.example.HelloService,它有两个提供者 providers,端口分别是 20880 与 20881。

xianch@acbc32941d79 ⮀ /usr/local/etc/zookeeper ⮀ zkCli
Connecting to localhost:2181
Welcome to ZooKeeper!
JLine support is enabled

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0] ls
[zk: localhost:2181(CONNECTED) 1] ls /
[dubbo, zookeeper]
[zk: localhost:2181(CONNECTED) 2] ls /dubbo
[com.example.HelloService]
[zk: localhost:2181(CONNECTED) 3] ls /dubbo/com.example.HelloService
[configurators, providers]
[zk: localhost:2181(CONNECTED) 4] ls /dubbo/com.example.HelloService/configurators
[]
[zk: localhost:2181(CONNECTED) 5] ls /dubbo/com.example.HelloService/providers
[
dubbo%3A%2F%2F10.93.67.135%3A20880%2Fcom.example.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-server%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Dcom.example.HelloService%26methods%3DsayHello%26pid%3D50337%26revision%3D1.0.0%26side%3Dprovider%26timestamp%3D1530089150751%26version%3D1.0.0, 
dubbo%3A%2F%2F10.93.67.135%3A20881%2Fcom.example.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-server%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Dcom.example.HelloService%26methods%3DsayHello%26pid%3D50999%26revision%3D1.0.0%26side%3Dprovider%26timestamp%3D1530089806995%26version%3D1.0.0
]
  • 服务提供者在启动的时候,向 Zookeeper 上的指定节点 /dubbo/${serviceName}/providers 目录下写入自己的 URL 地址,这个操作就完成了服务的发布。
  • 服务消费者启动的时候,订阅 /dubbo/${serviceName}/providers 目录下的提供者 URL 地址, 并向 /dubbo/${serviceName}/consumers 目录下写入自己的 URL 地址。
  • 注意,所有向 Zookeeper 上注册的地址都是临时节点,这样就能够保证服务提供者和消费者能够自动感应资源的变化。临时节点的生命周期与会话一致,会话关闭则临时节点删除。这个特性经常用来做心跳,动态监控,负载等动作。

服务消费端 clientDemo

启动程序,日志中可以看出,订阅了一个服务 consumer://10.93.67.135/com.example.HelloService,并且收到了一个 Notify,告诉它服务提供者的地址。

2018-06-27 15:32:19.338  INFO 44571 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Subscribe: consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=44571&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530084738835&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 15:32:19.367  INFO 44571 --- [           main] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Notify urls for subscribe url consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=44571&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530084738835&version=1.0.0, urls: [dubbo://10.93.67.135:20880/com.example.HelloService?anyhost=true&application=dubbo-demo-server&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=41887&revision=1.0.0&side=provider&timestamp=1530082347373&version=1.0.0, empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=configurators&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=44571&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530084738835&version=1.0.0, empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=44571&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530084738835&version=1.0.0], dubbo version: 2.6.2, current host: 10.93.67.135

此时,通过 zkCli 可以查看 Zookeeper 的信息:可以看出,服务 com.example.HelloService,它有一个消费者 consumers

[zk: localhost:2181(CONNECTED) 12] ls /dubbo/com.example.HelloService
[consumers, configurators, routers, providers]
[zk: localhost:2181(CONNECTED) 13] ls /dubbo/com.example.HelloService/consumers
[consumer%3A%2F%2F10.93.67.135%2Fcom.example.HelloService%3Fapplication%3Ddubbo-demo-client%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.6.2%26interface%3Dcom.example.HelloService%26methods%3DsayHello%26pid%3D44571%26qos.enable%3Dfalse%26revision%3D1.0.0%26side%3Dconsumer%26timestamp%3D1530084738835%26version%3D1.0.0]

做一个实验,我们依次停止之前启动的两个 serviceDemo,从 clientDemo 的日志中,可以看出,它收到了通知 subscribe url,并且依次断开了与两个服务的连接 disconnected from

2018-06-27 17:07:32.789  INFO 52762 --- [ain-EventThread] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Notify urls for subscribe url consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=52762&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530090434982&version=1.0.0, urls: [dubbo://10.93.67.135:20880/com.example.HelloService?anyhost=true&application=dubbo-demo-server&dubbo=2.6.2&generic=false&interface=com.example.HelloService&methods=sayHello&pid=50337&revision=1.0.0&side=provider&timestamp=1530089150751&version=1.0.0], dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 17:07:32.794  INFO 52762 --- [ain-EventThread] c.a.d.r.transport.netty.NettyChannel     :  [DUBBO] Close netty channel [id: 0x208cebb4, /10.93.67.135:55699 => /10.93.67.135:20881], dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 17:07:32.799  INFO 52762 --- [andler-thread-1] c.a.d.rpc.protocol.dubbo.DubboProtocol   :  [DUBBO] disconnected from /10.93.67.135:20881,url:dubbo://10.93.67.135:20881/com.example.HelloService?anyhost=true&application=dubbo-demo-client&check=false&codec=dubbo&dubbo=2.6.2&generic=false&heartbeat=60000&interface=com.example.HelloService&methods=sayHello&pid=52762&qos.enable=false&register.ip=10.93.67.135&remote.timestamp=1530089806995&revision=1.0.0&side=consumer&timestamp=1530090434982&version=1.0.0, dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 17:07:38.387  INFO 52762 --- [ain-EventThread] c.a.d.r.zookeeper.ZookeeperRegistry      :  [DUBBO] Notify urls for subscribe url consumer://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers,configurators,routers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=52762&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530090434982&version=1.0.0, urls: [empty://10.93.67.135/com.example.HelloService?application=dubbo-demo-client&category=providers&dubbo=2.6.2&interface=com.example.HelloService&methods=sayHello&pid=52762&qos.enable=false&revision=1.0.0&side=consumer&timestamp=1530090434982&version=1.0.0], dubbo version: 2.6.2, current host: 10.93.67.135
2018-06-27 17:07:38.389  INFO 52762 --- [ain-EventThread] c.a.d.r.transport.netty.NettyChannel     :  [DUBBO] Close netty channel [id: 0xee9aa5e5, /10.93.67.135:55698 => /10.93.67.135:20880], dubbo version: 2.6.2, current host: 10.93.67.135

关于 Zookeeper 的一些补充

参考 http://www.importnew.com/24411.html
对于一个服务框架,注册中心是其核心中的核心,虽然暂时挂掉并不会导致整个服务出问题,但是一旦挂掉,整体风险就很高。考虑一般情况,注册中心就是单台机器的时候,其实现很容易,所有机器起来都去注册服务给它,并且所有调用方都跟它保持长连接,一旦服务有变,即通过长连接来通知到调用方。但是当服务集群规模扩大时,这事情就不简单了,单机保持连接数有限,而且容易故障。

作为一个稳定的服务化框架,Dubbo 可以选择并推荐 Zookeeper 作为注册中心。其底层将 Zookeeper 常用的客户端 zkclient 和 curator 封装成为 ZookeeperClient。

  • 当服务提供者服务启动时,向 Zookeeper 注册一个节点;
  • 服务消费者则订阅其父节点的变化,诸如启动停止都能够通过节点创建删除得知,异常情况比如被调用方掉线也可以通过临时节点session 断开自动删除得知;
  • 服务消费方同时也会将自己订阅的服务以节点创建的方式放到 Zookeeper;
  • 于是可以得到映射关系,诸如谁提供了服务,谁订阅了谁提供的服务,基于这层关系再做监控,就能轻易得知整个系统情况。

Zookeeper 所有数据都在内存中,模型类似一颗文件树,ZNode Tree,每个 ZNode 节点都会保存自己的数据内容和一系列属性。
ZNode 分为持久节点临时节点,后者和客户端会话绑定。

Zookeeper 的节点模型
  • 同一时刻多台机器创建同一个节点,只有一个会争抢成功。利用这个特性可以做分布式锁。
  • 临时节点的生命周期与会话一致,会话关闭则临时节点删除。这个特性经常用来做心跳,动态监控,负载等动作。
  • 顺序节点保证节点名全局唯一。这个特性可以用来生成分布式环境下的全局自增长 ID。

通过zookeeper提供的原语服务,可以对 Zookeeper 能做的事情有个精确和直观的认识:

  • 创建节点
  • 删除节点
  • 更新节点
  • 获取节点信息
  • 权限控制
  • 事件监听

关于 Zookeeper 的扩展

这里我们只有一台 Zookeeper 服务器,假如它挂了,那么整个系统就挂了。
为了防止这种情况,我们需要设置 Zookeeper 集群。
Zookeeper 不仅可以单机提供服务,同时也支持多机组成集群来提供服务,实际上 Zookeeper 还支持另外一种伪集群的方式,也就是可以在一台物理机上运行多个 Zookeeper 实例。

在 Zookeepe r中,有 Leader、Follower、Observer 三种角色。集群中所有机器通过一个 Leader 选举来决定一台机器作为 Leader,Leader 为客户端提供读和写服务。Follower 和 Observer 都提供读服务,区别在于 Observer 机器不参与选举。

Zookeeper 通过复制来实现高可用性,只要集合体中半数以上的机器处于可用状态,它就能够保证服务继续。

因为官网建议至少3个节点,3台机器只要有2台可用就可以选出leader并且对外提供服务(2n+1台机器,可以容n台机器挂掉)。

具体的方式,参见 https://blog.52itstyle.com/archives/363/

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

推荐阅读更多精彩内容