Dubbo 源码分析 —— 核心流程一览


我准备战斗到最后,不是因为我勇敢,是我想见证一切。 ——双雪涛《猎人》

[TOC]
Thinking

  1. 一个技术,为什么要用它,解决了那些问题?
  2. 如果不用会怎么样,有没有其它的解决方法?
  3. 对比其它的解决方案,为什么最终选择了这种,都有何利弊?
  4. 你觉得项目中还有那些地方可以用到,如果用了会带来那些问题?
  5. 这些问题你又如何去解决的呢?

本文基于Dubbo 2.6.7-SNAPSHOT

​ 本文主要基于《Dubbo 开发指南 —— 框架设计》

1、整体设计

dubbo-framework.jpg

图例说明:

  • 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
  • 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
  • 图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
  • 图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。

1.1 各层说明

  • ========================== Business==============================
  • Service业务层:业务代码的接口与实现。暴露在外实际的使用Dubbo层。
  • ========================== RPC==============================
  • config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
    • dubbo-config 模块实现
  • proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory
    • dubbo-rpc 模块实现
  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
    • dubbo-registry 模块实现
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
    • dubbo-cluster 模块实现
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
    • dubbo-monitor 模块实现
  • protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
    • dubbo-rpc 模块实现
  • ========================== Remoting============================
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
    • dubbo-remoting-api 模块定义接口
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
    • dubbo-remoting-api 模块定义接口
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool
    • dubbo-common 模块实现。

1.2 关系说明

在 RPC 中,Protocol 是核心层,也就是只要有 Protocol + Invoker + Exporter 就可以完成非透明的 RPC 调用,然后在 Invoker 的主过程上 Filter 拦截点。

  • dubbo-rpc 模块可以独立完成该功能

图中的 Consumer 和 Provider 是抽象概念,只是想让看图者更直观的了解哪些类分属于客户端与服务器端,不用 Client 和 Server 的原因是 Dubbo 在很多场景下都使用 Provider, Consumer, Registry, Monitor 划分逻辑拓普节点,保持统一概念。

而 Cluster 是外围概念,所以 Cluster 的目的是将多个 Invoker 伪装成一个 Invoker,这样其它人只要关注 Protocol 层 Invoker 即可,加上 Cluster 或者去掉 Cluster 对其它层都不会造成影响,因为只有一个提供者时,是不需要 Cluster 的

  • dubbo-cluster 模块提供的是非必需的功能,移除不会影响到其它模块。RPC模块也可以正常运行。

Proxy 层封装了所有接口的透明化代理,而在其它层都以 Invoker 为中心,只有到了暴露给用户使用时,才用 Proxy 将 Invoker 转成接口,或将接口实现转成 Invoker,也就是去掉 Proxy 层 RPC 是可以 Run 的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。

  • 简单粗暴的说,Proxy 会拦截 service.doSomething(args) 的调用,“转发”给该 Service 对应的 Invoker ,从而实现透明化的代理。

而 Remoting 实现是 Dubbo 协议的实现,如果你选择 RMI 协议,整个 Remoting 都不会用上,Remoting 内部再划为 Transport 传输层和 Exchange 信息交换层,Transport 层只负责单向消息传输,是对 Mina, Netty, Grizzly 的抽象,它也可以扩展 UDP 传输,而 Exchange 层是在传输层之上封装了 Request-Response 语义。

Registry 和 Monitor 实际上不算一层,而是一个独立的节点,只是为了全局概览,用层的方式画在一起

2、核心流程

2.1 调用链

dubbo-extension.jpg
  • 垂直分层如下:
    • 下方 淡蓝背景( Consumer ):服务消费方使用的接口
    • 上方 淡绿色背景( Provider ):服务提供方使用的接口
    • 中间 粉色背景( Remoting ):通信部分的接口
  • 自 LoadBalance 向上,每一行分成了多个相同的 Interface ,指的是负载均衡后,向 Provider 发起调用。
  • 左边 括号 部分,代表了垂直部分更细化的分层,依次是:Common、Remoting、RPC、Interface 。
  • 右边 蓝色虚线( Init ) 为初始化过程,通过对应的组件进行初始化。例如,ProxyFactory 初始化出 Proxy 。

2.2 暴露服务

服务提供方暴露服务蓝色初始化链(Init)时序图:


dubbo-export.jpg

2.3.4 步 为本文重点:getInvoker()

2.3 引用服务

消费方引用服务的蓝色初始化链


dubbo_rpc_refer.jpg

第5步:refer() 第12步 都为本文重点。

3、领域模型

本章分享的位于Dubbo-rpc-api目录中,如下图红框部分:

领域模型

在 Dubbo 的核心领域模型中:

  • Protocol 是服务域,它是 Invoker 暴露和引用的主功能入口,它负责 Invoker 的生命周期管理。
  • Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
  • Invocation 是会话域,它持有调用过程中的变量,比如方法名,参数等。

3.1 Invoker

com.alibaba.dubbo.rpc

Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它。

它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。

public interface Invoker<T> extends Node {

    /**
     * get service interface.
     *
     * #getInterface() 获取Service接口
     *
     * @return service interface.
     */
    Class<T> getInterface();

    /**
     * invoke.
     * 调用方法
     *
     * @param invocation
     * @return result
     * @throws RpcException
     */
    Result invoke(Invocation invocation) throws RpcException;

}

3.1.1 详解Invoker

在dubbo中,万物皆是Invoker,即便是Exporter也是由Invoker进化而成的

​ 由于Invoker在Dubbo领域模型中非常重要的一个概念,很多设计思路都是向它靠拢。

这一思想渗透在整个实现代码里。

下面简单的说明Invoker的两种实现:服务提供的invoker和服务消费的invoker

满眼都是Invoker

结合Dubbo demo中的 消费和提供者代码来理解上图。

  • 服务消费者代码:
public class DemoClientAction {
    private DemoServer demoServer;

    public void setDemoServer(DemoServer demoServer) {
        this.demoServer = demoServer;
    }

    public void start() {
        String hello = demoServer.sayHello("world");
    }
}
  • 上面代码中的 DemoService 就是上图中服务消费端的 Proxy,用户代码通过这个 Proxy 调用其对应的 Invoker,而该 Invoker 实现了真正的远程服务调用。
  • 服务提供者代码:
public class DemoServiceImpl implements DemoService {

    public String sayHello(String name) throws RemoteException {
        return "Hello " + name;
    }
}
  • 上面这个类会被封装成为一个 AbstractProxyInvoker 实例,并新生成一个 Exporter 实例。这样当网络通讯层收到一个请求后,会找到对应的 Exporter 实例,并调用它所对应的 AbstractProxyInvoker 实例,从而真正调用了服务提供者的代码。

3.1.2Invoker 类图

​ 上文所提到的,在Dubbo中invoker是一个非常重要的概念,既可以理解为万物皆为Invoker。所以在Dubbo中的实现类非常多。

Invoker类图

3.2 Invocation

com.alibaba.dubbo.rpc.Invocation

Invocation 是会话域,它持有调用过程中的变量,比如方法名,参数等。

public interface Invocation {

    /**
     * get method name.
     *
     * 获取方法名
     * @return method name.
     * @serial
     */
    String getMethodName();

    /**
     * get parameter types.
     *
     * 获取方法参数类型数组
     * @return parameter types.
     * @serial
     */
    Class<?>[] getParameterTypes();

    /**
     * get arguments.
     *
     * 获取方法参数数组
     * @return arguments.
     * @serial
     */
    Object[] getArguments();

    /**
     * get attachments.
     *
     * 获取隐式参数相关
     * @return attachments.
     * @serial
     */
    Map<String, String> getAttachments();

    /**
     * get attachment by key.
     *
     * @return attachment value.
     * @serial
     */
    String getAttachment(String key);

    /**
     * get attachment by key with default value.
     *
     * @return attachment value.
     * @serial
     */
    String getAttachment(String key, String defaultValue);

    /**
     * get the invoker in current context.
     *
     * 在当前上下文中获取调用者 invoker
     *
     * 获取对应的invoker对象
     *
     * @return invoker.
     * @transient
     */
    Invoker<?> getInvoker();

}

3.2.1 类图

Invocation类图
  • DecodeableRpcInvocation:是Dubbo协议独有的。

3.3 Result

com.alibaba.dubbo.rpc.Result

Result 是会话域,它持有调用过程中返回值,异常等。

​ RPC调用的结果集

public interface Result {

    /**
     * Get invoke result.
     * <p>
     * 获取返回值
     *
     * @return result. if no result return null.
     */
    Object getValue();

    /**
     * Get exception.
     * <p>
     * 获取返回的异常
     *
     * @return exception. if no exception return null.
     */
    Throwable getException();

    /**
     * Has exception.
     * <p>
     * 判断是否存在异常
     *
     * @return has exception.
     */
    boolean hasException();

    /**
     * Recreate.
     * <p>
     * <code>
     * if (hasException()) {
     * throw getException();
     * } else {
     * return getValue();
     * }
     * </code>
     * <p>
     * com.alibaba.dubbo.rpc.RpcResult
     * RpcResult 中针对recreate() 的实现。
     *
     * @return result.
     * @throws if has exception throw it.
     * @see RpcResult // 具体实现
     */
    Object recreate() throws Throwable;

    /**
     * @see com.alibaba.dubbo.rpc.Result#getValue()
     * @deprecated Replace to getValue()
     */
    @Deprecated
    Object getResult();

    /**
     * 下面的getAttachments等方法 都是获取但会的隐式参数相关。
     */

    /**
     * get attachments.
     *
     * @return attachments.
     */
    Map<String, String> getAttachments();

    /**
     * get attachment by key.
     *
     * @return attachment value.
     */
    String getAttachment(String key);

    /**
     * get attachment by key with default value.
     *
     * @return attachment value.
     */
    String getAttachment(String key, String defaultValue);

}

#recreate()的具体实现:

    private Object result;

    private Throwable exception;  

@Override
public Object recreate() throws Throwable {
        if (exception != null) {
            throw exception;
        }
        return result;
    }

3.3.1 类图

Result类图

3.4 Filter

com.alibaba.dubbo.rpc.Filter

过滤器接口,和我们平时理解的 javax.servlet.Filter 基本一致。

/**
 * Filter. (SPI, Singleton, ThreadSafe)
 */
@SPI
public interface Filter {

    /**
     * do invoke filter.
     执行invoker的过滤逻辑。
     * <p>
     * <code>
     * // before filter 自己实现
     * Result result = invoker.invoke(invocation);
     * // after filter 自己实现
     * return result;
     * </code>
     *
     * @param invoker    service
     * @param invocation invocation.
     * @return invoke result.
     * @throws RpcException
     * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
     */
    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;

}

3.4.1 类图

Filter类图

4.5 ProxyFactory

com.alibaba.dubbo.rpc.ProxyFactory代理工厂类

/**
 * ProxyFactory. (API/SPI, Singleton, ThreadSafe)
 */
@SPI("javassist")
public interface ProxyFactory {

    /**
     * create proxy.
     *
     * 创建Proxy,在引用服务调用。
     *
     * @param invoker
     * @return proxy
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;

    /**
     * create proxy.
     *
     * @param invoker
     * @return proxy
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;

    /**
     * create invoker.
     *
     * 创建Invoker,在暴露服务时调用。
     *
     * @param <T>
     * @param proxy Service对象
     * @param type  Service接口类型
     * @param url   Service对应的Dubbo URL
     * @return invoker
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}

服务消费者消费一个服务的详细过程

dubbo_rpc_refer.jpg

首先 ReferenceConfig 类的 init 方法调用 Protocolrefer 方法生成 Invoker 实例(如上图中的红色部分),这是服务消费的关键。接下来把 Invoker 转换为客户端需要的接口(如:HelloWorld)。

  • 从图中我们可以看出,方法的 invoker 参数,通过 Protocol 将 Service接口 创建出 Invoker 。
  • 通过创建 Service 的 Proxy ,实现我们在业务代理调用 Service 的方法时,透明的内部转换成调用 Invoker 的 #invoke(Invocation) 方法
  • 服务提供者暴露服务的 主过程 如下图:

首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。接下来就是 Invoker 转换到 Exporter 的过程。

从图中我们可以看出,该方法创建的 Invoker ,下一步会提交给 Protocol ,从 Invoker 转换到 Exporter 。

Dubbo 的实现

Dubbo 协议的 Invoker 转为 Exporter 发生在 DubboProtocol 类的 export 方法,它主要是打开 socket 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现。

RMI 的实现

RMI 协议的 Invoker 转为 Exporter 发生在 RmiProtocol类的 export 方法,它通过 Spring 或 Dubbo 或 JDK 来实现 RMI 服务,通讯细节这一块由 JDK 底层来实现,这就省了不少工作量。

3.5.1 类图

image-20200205163900241.png

从类图可以看出,Dubbo支持JavassistJDK Proxy两种方式生成代理。

3.6 Protocol

com.alibaba.dubbo.rpc.Protocol Dubbo支持协议的顶层接口

Protocol 是服务域,它是 Invoker 暴露和引用的主功能入口。
它负责 Invoker 的生命周期管理。

/**
 * Protocol. (API/SPI, Singleton, ThreadSafe)
 *
 * Dubbo 支持RPC协议的 顶层接口
 */
@SPI("dubbo")
public interface Protocol {

    /**
     * Get default port when user doesn't config the port.
     *
     * 定义 默认端口
     *
     * @return default port
     */
    int getDefaultPort();

    /**
     * Export service for remote invocation: <br>
     * 1. Protocol should record request source address after receive a request:
     * RpcContext.getContext().setRemoteAddress();<br>
     * 2. export() must be idempotent, that is, there's no difference between invoking once and invoking twice when
     * export the same URL<br>
     * 3. Invoker instance is passed in by the framework, protocol needs not to care <br>
     *
     * 暴露远程调用服务:
     * 1. 协议在接受请求时,应该记录请求的来源地址:RpcContext.getContext().setRemoteAddress();<br>
     * 2. export()必须是幂等的,也就是说,在暴露服务时,一次调用和两次调用时没有区别的,
     * 3.传入的Invoker实例由框架实现并传入,协议无需关心。
     *
     * @param <T>     Service type 服务的类型
     * @param invoker Service invoker 服务的执行体
     * @return exporter reference for exported service, useful for unexport the service later
     * @throws RpcException thrown when error occurs during export the service, for example: port is occupied
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    /**
     * Refer a remote service: <br> 引用远程服务
     * 1. When user calls `invoke()` method of `Invoker` object which's returned from `refer()` call, the protocol
     * needs to correspondingly execute `invoke()` method of `Invoker` object <br>
     * 2. It's protocol's responsibility to implement `Invoker` which's returned from `refer()`. Generally speaking,
     * protocol sends remote request in the `Invoker` implementation. <br>
     * 3. When there's check=false set in URL, the implementation must not throw exception but try to recover when
     * connection fails.
     *
     * 引用远程服务:
     * 1. 当用户调用Refer()所返回的Invoker对象的invoke()方法时,协议需要相应地执行Invoker对象的Invoke()方法
     * 2. 实现由`refer()`返回的`Invoker`是协议的责任。一般来说,协议在`Invoker`实现中发送远程请求。
     * 3. 当url中设置了 check = false时,连接失败时不能抛出异常,且只能内部消化。
     *
     * @param <T>  Service type 服务的类型
     * @param type Service class 服务的 class对象
     * @param url  URL address for the remote service 远程服务的url地址
     * @return invoker service's local proxy 服务的本地代理
     * @throws RpcException when there's any error while connecting to the service provider 当连接服务提供方失败时,抛出该异常。
     */
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    /**
     * Destroy protocol: <br>
     * 1. Cancel all services this protocol exports and refers <br>
     * 2. Release all occupied resources, for example: connection, port, etc. <br>
     * 3. Protocol can continue to export and refer new service even after it's destroyed.
     *
     * 销毁/释放协议:
     * 1. 取消该协议所有已经暴露和引用的服务。<br>
     * 2. 释放协议所占用的所有资源,比如:连接,端口等等。。
     * 3. 协议即使销毁后也可以继续暴露并引用新服务。
     */
    void destroy();

}

3.6.1 类图

Protocl类图

从类图就可以很清楚的看出,Dubbo支持多协议。

3.7 Exporter

com.alibaba.dubbo.rpc.Exporter

Exporter,Invoker 暴露服务在Protocol上的对象。

/**
 * Exporter. (API/SPI, Prototype, ThreadSafe)
 *
 * 暴露服务的 顶层接口
 *
 * @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)
 * @see com.alibaba.dubbo.rpc.ExporterListener
 * @see com.alibaba.dubbo.rpc.protocol.AbstractExporter
 */
public interface Exporter<T> {

    /**
     * get invoker.
     *
     * 获取对应的Invoker
     *
     * @return invoker
     */
    Invoker<T> getInvoker();

    /**
     * unexport.
     *
     * 取消 暴露
     * <p>
     * <code>
     * getInvoker().destroy();
     * </code>
     */
    void unexport();

}

3.7.1 类图


类图

3.8 ExporterListener

com.alibaba.dubbo.rpc.ExporterListener

Exporter 监听器

/**
 * ExporterListener. (SPI, Singleton, ThreadSafe)
 */
@SPI
public interface ExporterListener {

    /**
     * The exporter exported.
     *
     * 当服务暴露完成
     *
     * @param exporter
     * @throws RpcException
     * @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)
     */
    void exported(Exporter<?> exporter) throws RpcException;

    /**
     * The exporter unexported.
     *
     * 当服务取消暴露完成
     *
     * @param exporter
     * @throws RpcException
     * @see com.alibaba.dubbo.rpc.Exporter#unexport()
     */
    void unexported(Exporter<?> exporter);

}

3.8.1 类图

3.9 InvokerListener

com.alibaba.dubbo.rpc.InvokerListener invoker监听器

/**
 * InvokerListener. (SPI, Singleton, ThreadSafe)
 * Invoker 监听器
 */
@SPI
public interface InvokerListener {

    /**
     * The invoker referred
     *
     * 当服务引用完成
     *
     * @param invoker
     * @throws RpcException
     * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, com.alibaba.dubbo.common.URL)
     */
    void referred(Invoker<?> invoker) throws RpcException;

    /**
     * The invoker destroyed.
     *
     * 当服务销毁引用完成
     *
     * @param invoker
     * @see com.alibaba.dubbo.rpc.Invoker#destroy()
     */
    void destroyed(Invoker<?> invoker);

}

3.9.1 类图

4、总结

​ 本文重点概述了,Dubbo在服务的暴露与引用的大概流程。

本文仅供笔者本人学习,一起进步!

——努力努力再努力xLg

加油!

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

推荐阅读更多精彩内容