一文講清楚 seata DataSourceProxy 的使用

seata client 與 seata 是怎么通訊的

下面這張圖弯予,就是一切的基礎(chǔ)喳张。


image.png
  1. seata server 向注冊(cè)中心注冊(cè)
  2. seata client 想注冊(cè)中心注冊(cè)
  3. seata client 通過(guò)注冊(cè)中心返回的seata server的地址與端口说庭,找到seata server

由此可見(jiàn)裳涛,注冊(cè)中行在這一場(chǎng)景下的作用就是讓client 找到server

下面我們來(lái)看注冊(cè)中心涮较,注冊(cè)中心分為兩類(lèi)左刽,file與非file襟雷,我們先來(lái)看看file 類(lèi)型

file 類(lèi)型的 注冊(cè)中心(file 類(lèi)型的配置方式)

file 類(lèi)型是個(gè)什么類(lèi)型呢刃滓,file類(lèi)型是用于做概念驗(yàn)證的注冊(cè)中心,它的目標(biāo)是通過(guò)配置文件耸弄,讓client 直接找到 seata server咧虎,從而免去第三方注冊(cè)中心的依賴,用來(lái)做快速驗(yàn)證计呈。

其核心的配置點(diǎn)如下

... 省略部分代碼
# service configuration, only used in client side 只在客戶端使用
service {
  #transaction service group mapping
  #配置事務(wù)組砰诵,如果注冊(cè)中心為nacos,需要在nacos中配置相應(yīng)的配置項(xiàng)捌显,這里就直接使用了文件配置茁彭。
  vgroupMapping.my_test_tx_group = "default" 
  #only support when registry.type=file, please don't set multiple addresses 
  #只在registry.conf 中 registry.type=file 時(shí)才使用此項(xiàng)配置,此項(xiàng)配置的作用就是制定seata server 在哪里扶歪。
  default.grouplist = "127.0.0.1:8091" 
  #degrade, current not support
  enableDegrade = false
  #disable seata
  disableGlobalTransaction = false
... 省略部分代碼
}

此時(shí) seata client 與 seata server的關(guān)系就變?yōu)槿缦聢D所示


image.png

seata server 端的file.conf 內(nèi)容如下理肺,只定義了數(shù)據(jù)存儲(chǔ)方式。

## transaction log store, only used in seata-server
store {
  ## store mode: file善镰、db
  mode = "file"

  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }

  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    user = "mysql"
    password = "mysql"
    minConn = 1
    maxConn = 10
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
  }
}

nacos 方式的注冊(cè)中心

seata-server 的registry.conf 文件

registry {
  # file 妹萨、nacos 、eureka炫欺、redis乎完、zk、consul品洛、etcd3树姨、sofa
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}
nacos配置中心的部分
config {
  # file摩桶、nacos 、apollo娃弓、zk典格、consul、etcd3
  type = "nacos"

  nacos {
    serverAddr = "localhost"
    namespace = ""
    group = "SEATA_GROUP" # 這個(gè)配置比較重要台丛。服務(wù)端對(duì)應(yīng)的配置項(xiàng)必須所屬這個(gè)配置項(xiàng)指定的組
    username = "nacos"
    password = "nacos"
  }
}
}

客戶端對(duì)配置中心的指定耍缴,有不同的方式,原始方式是使用registry.conf挽霉,如果使用spring-cloud-alibaba防嗡,則可以通過(guò)spring 的application.yml(或者application.properties)文件指定
client 端的 registry.conf 文件內(nèi)容如下

registry {
  # file 、nacos 侠坎、eureka蚁趁、redis、zk实胸、consul他嫡、etcd3、sofa
  type = "nacos"

  nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
}

config {
  # file庐完、nacos 钢属、apollo、zk门躯、consul淆党、etcd3
  type = "nacos"

  nacos {
    serverAddr = "localhost"
    namespace = ""
    group="SEATA_GROUP" 與 server 端的對(duì)應(yīng)
    username = "nacos"
    password = "nacos"
  }

}

spring-cloud-alibaba client 端的配置方式

# ----------配置中心,如果無(wú)需使用配置中心讶凉,可以刪除此部分配置----------
# 設(shè)置配置中心服務(wù)端地址
#spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# nacos認(rèn)證信息
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=nacos
spring.cloud.nacos.config.contextPath=/nacos

# 設(shè)置注冊(cè)中心服務(wù)端地址
spring.cloud.nacos.discovery.server-addr= 127.0.0.1:8848
# nacos認(rèn)證信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos

seata client 端的內(nèi)部

client 內(nèi)部的回滾機(jī)制

按照官方文檔介紹染乌,seata會(huì)分析修改數(shù)據(jù)的sql,同時(shí)生成對(duì)應(yīng)的反向回滾SQL懂讯,這個(gè)回滾記錄在undo_log 表中荷憋。所以要求每一個(gè)client 都有一個(gè)對(duì)應(yīng)的undo_log表,定義如下

CREATE TABLE `undo_log`
(
  `id`            BIGINT(20)   NOT NULL AUTO_INCREMENT,
  `branch_id`     BIGINT(20)   NOT NULL,
  `xid`           VARCHAR(100) NOT NULL,
  `context`       VARCHAR(128) NOT NULL,
  `rollback_info` LONGBLOB     NOT NULL,
  `log_status`    INT(11)      NOT NULL,
  `log_created`   DATETIME     NOT NULL,
  `log_modified`  DATETIME     NOT NULL,
  `ext`           VARCHAR(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8;

那么發(fā)生分析的生成回滾SQL 的地方在哪里呢褐望?答案是在DataSource台谊。下面我們看看seata client 是如何辦到的。

一切的核心都在io.seata.rm.datasource.DataSourceProxy

這里盡可能的簡(jiǎn)單譬挚,不做源碼分析,用DataSourceProxy(java.sql.Datasource)這種包含關(guān)系酪呻,同時(shí)DataSourceProxy的父類(lèi)AbstractDataSourceProxy實(shí)現(xiàn)了Datasource 接口减宣,所以DataSourceProxy 也是Datasource 的一個(gè)實(shí)現(xiàn),這使得DataSourceProxy有機(jī)會(huì)分析要執(zhí)行的SQL 與生成對(duì)應(yīng)的回滾SQL玩荠。

那么我們只要把DataSourceProxy 注冊(cè)成默認(rèn)的java.sql.Datasource實(shí)現(xiàn)漆腌,并提供給其他使用框架(mybatis, jdbctemplate)裝配贼邓,就達(dá)到我們的目的了。所以client端一切的配置都圍繞著把DataSourceProxy 注冊(cè)成默認(rèn)的java.sql.Datasource闷尿,或者把數(shù)據(jù)庫(kù)訪問(wèn)框架的Datasource配置DataSourceProxy 為來(lái)進(jìn)行的塑径。

明白了上面的思路,我們開(kāi)發(fā)時(shí)所做的工作就是完成 DataSourceProxy 的配置了填具。

從seata0.9 開(kāi)始统舀,提供了DataSource自動(dòng)代理功能,并且默認(rèn)是開(kāi)啟的劳景。這個(gè)騷操作是什么意思呢誉简。就是告訴你,你不用在去管DataSource放到DataSourceProxy中這個(gè)步驟了盟广,seata會(huì)自動(dòng)幫你完成這一步的操作闷串。然后你只需要將DataSource放到其他使用DataSource的地方就好啦。

client 端 DataSourceProxy的自動(dòng)裝配

seata 是如何自動(dòng)裝配的筋量?

一切的核心都在 io.seata.spring.boot.autoconfigure.SeataAutoConfigurationio.seata.spring.annotation.datasource.SeataDataSourceBeanPostProcessor

SeataAutoConfiguration中烹吵,通過(guò)seata.enableAutoDataSourceProxy 的值來(lái)判斷是否注冊(cè) SeataDataSourceBeanPostProcessor bean。

這里有個(gè)巨大的坑 seata.enableAutoDataSourceProxy 這個(gè)配置寫(xiě)在桨武,yaml 文件里的時(shí)候肋拔,智能提示的配置項(xiàng)是enable-auto-data-source-proxy,無(wú)法生效玻募。必須寫(xiě)為enableAutoDataSourceProxy 的形式只损。

#智能提示給出的設(shè)置,無(wú)法生效七咧。
seata:
  enable-auto-data-source-proxy: false
#正常生效的例子 
seata:
  enableAutoDataSourceProxy: false

這個(gè)SeataDataSourceBeanPostProcessor 實(shí)現(xiàn)了org.springframework.beans.factory.config.BeanPostProcessor 接口 跃惫,BeanPostProcessor 接口有兩個(gè)鉤子方法 postProcessBeforeInitializationpostProcessAfterInitialization, 然后seata就在 postProcessAfterInitialization 中一頓操作猛如虎,完成了自動(dòng)代理功能艾栋。有興趣的自己看下源碼爆存。

BeanPostProcessor 鉤子流程如下


image.png

注意事項(xiàng)

  1. 接口中的兩個(gè)方法都要將傳入的bean返回,而不能返回null蝗砾,如果返回的是null那么我們通過(guò)getBean方法將得不到目標(biāo)先较。
  2. BeanFactory和ApplicationContext對(duì)待bean后置處理器稍有不同。ApplicationContext會(huì)自動(dòng)檢測(cè)在配置文件中實(shí)現(xiàn)了BeanPostProcessor接口的所有bean悼粮,并把它們注冊(cè)為后置處理器闲勺,然后在容器創(chuàng)建bean的適當(dāng)時(shí)候調(diào)用它,因此部署一個(gè)后置處理器同部署其他的bean并沒(méi)有什么區(qū)別扣猫。而使用BeanFactory實(shí)現(xiàn)的時(shí)候菜循,bean 后置處理器必須通過(guò)代碼顯式地去注冊(cè),在IoC容器繼承體系中的ConfigurableBeanFactory接口中定義了注冊(cè)方法
  3. 不要將BeanPostProcessor標(biāo)記為延遲初始化申尤。因?yàn)槿绻@樣做癌幕,Spring容器將不會(huì)注冊(cè)它們衙耕,自定義邏輯也就無(wú)法得到應(yīng)用。假如你在<beans />元素的定義中使用了'default-lazy-init'屬性勺远,請(qǐng)確信你的各個(gè)BeanPostProcessor標(biāo)記為'lazy-init="false"'橙喘。

關(guān)于生命周期送上一張福利


image.png

簡(jiǎn)單點(diǎn)看這個(gè)


image.png

自動(dòng)裝配的相關(guān)設(shè)置——均已1.1.0版本為例

開(kāi)啟與關(guān)閉

  • 對(duì)于使用seata-spring-boot-starter的方式,默認(rèn)已開(kāi)啟數(shù)據(jù)源自動(dòng)代理,如需關(guān)閉胶逢,請(qǐng)配置seata.enableAutoDataSourceProxy=false厅瞎,該項(xiàng)配置默認(rèn)為true。
    如需切換代理實(shí)現(xiàn)方式宪塔,請(qǐng)通過(guò)seata.useJdkProxy=false進(jìn)行配置,默認(rèn)為false磁奖,采用CGLIB作為數(shù)據(jù)源自動(dòng)代理的實(shí)現(xiàn)方式。
  • 對(duì)于使用seata-all的方式某筐,請(qǐng)使用@EnableAutoDataSourceProxy來(lái)顯式開(kāi)啟數(shù)據(jù)源自動(dòng)代理功能比搭。如有需要,可通過(guò)該注解的useJdkProxy屬性進(jìn)行代理實(shí)現(xiàn)方式
    的切換南誊。默認(rèn)為false,采用CGLIB作為數(shù)據(jù)源自動(dòng)代理的實(shí)現(xiàn)方式身诺。

其他版本設(shè)置
1.0.0: client.support.spring.datasource.autoproxy=true
0.9.0: support.spring.datasource.autoproxy=true

自動(dòng)配置開(kāi)啟時(shí)的配置示例-均已mybatis-plus 為例


@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid")
    public DataSource druidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        return druidDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
            .getResources("classpath*:/mapper/*.xml"));
        return factoryBean.getObject();
    }
}

application.yaml

spring:
  application:
    name: alicloudapp
  datasource:
    name: storageDataSource
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 2
      max-active: 20
      min-idle: 2
      url: 'jdbc:mysql://localhost:3306/seata_storage?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true'
      username: root
      password: openstack
      driver-class-name: 'com.mysql.cj.jdbc.Driver'
      name: storageDataSource

自動(dòng)配置關(guān)閉時(shí)的配置示例-均已mybatis-plus 為例

application.yaml

#seata 使用有幾個(gè)點(diǎn)
#1 自定義配置數(shù)據(jù)源參見(jiàn)SeataConfiguration,同時(shí)排除DataSourceAutoConfiguration
#2 配置微服務(wù)的seata 注冊(cè)名稱(chēng)抄囚,通過(guò)spring.cloud.alibaba.seata.tx-service-group=<service-group-name> 來(lái)配置霉赡,此配置需要和nacos 中的配置對(duì)應(yīng)。
#3 在nacos中幔托,需要配置對(duì)應(yīng)的配置性穴亏,已本工程為例,需要配置Data_ID=serivce.vgroup_mapp.<service-group-name>, GROUP=SEATA_GROUP 配置格式為text重挑,配置內(nèi)容為default的配置項(xiàng)嗓化,參見(jiàn)nacos-seata-configuration.png
#4 配置registry.conf 中的type 使用nacos 作為服務(wù)發(fā)現(xiàn)與配置中心
#5 seata server 需要修改 conf 目錄下的registry.conf 配置使用nacos 作為服務(wù)發(fā)現(xiàn)與配置中心,注意config段的配置項(xiàng)中谬哀,需要把nacos的group項(xiàng)添加上刺覆,并指定為SEATA_GROUP
#6 在使用spring-cloud-starter-alibaba-seata 的情況下,client端與server端需要保持版本一致史煎。
#7 在 seata 在 0.9 開(kāi)始會(huì)自動(dòng)代理(auto datasource proxy)datasource谦屑,需要選擇自定義裝配ProxyDataSource或者自動(dòng)代理裝配
spring:
  autoconfigure:
#    自定義配置DataSource 和 ProxyDataSource時(shí),需要排除spring 原生的自動(dòng)化裝配類(lèi)
    exclude: [org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure]
  profiles: seata
  cloud:
    alibaba:
      seata:
        tx-service-group: storage-service-group
# 取消datasource自動(dòng)代理  enable-auto-data-source-proxy 這種連字符的寫(xiě)法是無(wú)法生效的篇梭,詳情看SeataAutoConfiguration源碼
seata:
  enableAutoDataSourceProxy: false

MybatisPlusConfiguration.java

@Configuration
@MapperScan(basePackages = {"com.mycompany.alicloudapp.mapper"})
@Slf4j
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class MybatisPlusConfiguration {

    @Profile("dev")
    @Bean
    public PerformanceMonitorInterceptor performanceMonitorInterceptor() {
        return new PerformanceMonitorInterceptor();
    }

    /**
     * MP 自帶分頁(yè)插件
     *
     * @return
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor page = new PaginationInterceptor();
        page.setCountSqlParser(new JsqlParserCountOptimize(true));

        return page;
    }

    /**
     * @param sqlSessionFactory SqlSessionFactory
     * @return SqlSessionTemplate
     */
    @Autowired(required = true)
    private DataSourceProperties dataSourceProperties;

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    /**
     * 從配置文件獲取屬性構(gòu)造datasource氢橙,注意前綴,這里用的是druid恬偷,根據(jù)自己情況配置,
     * 原生datasource前綴取"spring.datasource"
     *
     * @return
     */
    @Bean(name = "datasource")
    @Primary
    public DataSourceProxy druidDataSource() {

        DruidDataSource druidDataSource = new DruidDataSource();
        log.info("dataSourceProperties.getUrl():{}", dataSourceProperties);
        druidDataSource.setUrl(dataSourceProperties.getUrl());
        druidDataSource.setUsername(dataSourceProperties.getUsername());
        druidDataSource.setPassword(dataSourceProperties.getPassword());
        druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        druidDataSource.setInitialSize(1);
        druidDataSource.setMaxActive(120);
        druidDataSource.setMaxWait(60000);
        druidDataSource.setMinIdle(1);
        druidDataSource.setValidationQuery("Select 1 from DUAL");
        druidDataSource.setTestOnBorrow(false);
        druidDataSource.setTestOnReturn(false);
        druidDataSource.setTestWhileIdle(true);
        druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
        druidDataSource.setMinEvictableIdleTimeMillis(25200000);
        druidDataSource.setRemoveAbandoned(true);
        druidDataSource.setRemoveAbandonedTimeout(1800);
        druidDataSource.setLogAbandoned(true);
        log.info("裝載dataSource........");
        return new DataSourceProxy(druidDataSource);
    }


    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
//         DataSource dataSourceProxy = new DataSourceProxy(druidDataSource);

        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSourceProxy);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // bean.setConfigLocation(resolver.getResource("classpath:mybatis-config.xml"));
        bean.setMapperLocations(resolver.getResources("classpath*:/com.mycompany.alicloudapp.mapper/**/*.xml"));
        SqlSessionFactory factory = null;
        try {
            factory = bean.getObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return factory;
    }
}

更簡(jiǎn)潔的寫(xiě)法是

@Configuration
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class MyDataSourceConfig {
 
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid")
    public DataSource druidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        return druidDataSource;
    }

    @Primary
    @Bean("dataSource")
    public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }
}

到此為止DataSourceProxy 相關(guān)的部分就配置完了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悍手,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谓苟,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件协怒,死亡現(xiàn)場(chǎng)離奇詭異涝焙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)孕暇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)仑撞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人妖滔,你說(shuō)我怎么就攤上這事隧哮。” “怎么了座舍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵沮翔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我曲秉,道長(zhǎng)采蚀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任承二,我火速辦了婚禮榆鼠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘亥鸠。我一直安慰自己妆够,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布负蚊。 她就那樣靜靜地躺著神妹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盖桥。 梳的紋絲不亂的頭發(fā)上灾螃,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音揩徊,去河邊找鬼腰鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛塑荒,可吹牛的內(nèi)容都是我干的熄赡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼齿税,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼彼硫!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拧篮,失蹤者是張志新(化名)和其女友劉穎词渤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體串绩,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缺虐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了礁凡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片高氮。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖顷牌,靈堂內(nèi)的尸體忽然破棺而出剪芍,到底是詐尸還是另有隱情,我是刑警寧澤窟蓝,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布罪裹,位于F島的核電站,受9級(jí)特大地震影響疗锐,放射性物質(zhì)發(fā)生泄漏坊谁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一滑臊、第九天 我趴在偏房一處隱蔽的房頂上張望口芍。 院中可真熱鬧,春花似錦雇卷、人聲如沸鬓椭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)小染。三九已至,卻和暖如春贮折,著一層夾襖步出監(jiān)牢的瞬間裤翩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工调榄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留踊赠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓每庆,卻偏偏與公主長(zhǎng)得像筐带,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缤灵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355