【分布式日志系統(tǒng)】springboot+zipkin+dubbo實(shí)現(xiàn)鏈路跟蹤(下)

上一篇寫(xiě)了《【分布式日志系統(tǒng)】springboot+zipkin+dubbo實(shí)現(xiàn)鏈路跟蹤(上)》《【分布式日志系統(tǒng)】springboot+zipkin+dubbo實(shí)現(xiàn)鏈路跟蹤(中)》,有興趣的小伙伴可以往回翻翻,這一篇解決如何跟蹤笑陈。

一、基本操作

  1. pom添加依賴(lài)(常規(guī)操作)刺洒;
  2. 定義公共跟蹤配置類(lèi)
  3. dubbo服務(wù)端和調(diào)用端集成相應(yīng)配置(主要是yaml中配置);
  4. 驗(yàn)證挪鹏。

二袁辈、環(huán)境介紹

提示:可能不同的框架版本會(huì)導(dǎo)致有些地方不生效(如sleuth不支持apache版的dubbo)汗洒,大家在集成的過(guò)程中有問(wèn)題可以私信我挖滤,共同探討崩溪。
主框架版本如下:
springboot 2.6.6、
dubbo 2.7.12斩松、
zipkin 2.16.3伶唯、
brave 5.13.3
nacos-discovery-spring-boot-starter 0.2.10、
nacos-config-spring-boot-starter 0.2.10惧盹、

三 工程結(jié)構(gòu)

  • basic-platform:父工程乳幸,管理框架版本等基礎(chǔ)依賴(lài);
  • basic-common:基礎(chǔ)工具包钧椰,“定義公共跟蹤基礎(chǔ)配置類(lèi)”等均在此工程粹断,其他工程均依賴(lài)該工程;
  • basic-portal:門(mén)戶(hù)的父工程(pom工程)演侯,其下包含三個(gè)子工程:
    • server:前后端分離對(duì)應(yīng)的后端服務(wù) 姿染;
    • portal-api:定義dubbo服務(wù)的接口背亥;
    • portal-api-impl:定義portal-api對(duì)應(yīng)的接口實(shí)現(xiàn)秒际;
  • basic-foundation:基礎(chǔ)的父工程(pom工程),其下包含兩個(gè)子工程:
    • foundation-api:定義dubbo服務(wù)的接口狡汉;
    • foundation-api-impl:定義 foundation-api對(duì)應(yīng)的接口實(shí)現(xiàn)娄徊;
image.png

四、pom依賴(lài)

主要的代碼均在basic-common的pom中盾戴,添加如下依賴(lài):

<dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-spring-beans</artifactId>
        </dependency>
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-context-slf4j</artifactId>
        </dependency>
        <!--        tracing -->
        <dependency>
            <groupId>io.zipkin.reporter2</groupId>
            <artifactId>zipkin-sender-okhttp3</artifactId>
        </dependency>
        <!--        tracing  & mvc-->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-spring-webmvc</artifactId>
        </dependency>
        <!--        tracing  & http-->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>io.zipkin.reporter2</groupId>
            <artifactId>zipkin-sender-okhttp3</artifactId>
        </dependency>

另外寄锐,需要定義一個(gè)配置類(lèi),來(lái)處理RPC的tracing尖啡,要不會(huì)不生效橄仆。

package com.wd.basic.common.support.trace;

import brave.CurrentSpanCustomizer;
import brave.SpanCustomizer;
import brave.Tracing;
import brave.context.slf4j.MDCScopeDecorator;
import brave.http.HttpTracing;
import brave.propagation.B3Propagation;
import brave.propagation.ExtraFieldPropagation;
import brave.propagation.ThreadLocalCurrentTraceContext;
import brave.rpc.RpcTracing;
import brave.sampler.Sampler;
import brave.servlet.TracingFilter;
import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import zipkin2.Span;
import zipkin2.codec.Encoding;
import zipkin2.reporter.AsyncReporter;
import zipkin2.reporter.Sender;
import zipkin2.reporter.okhttp3.OkHttpSender;

import javax.servlet.Filter;

@Configuration
@Import(SpanCustomizingAsyncHandlerInterceptor.class)
public class TracingConfig implements WebMvcConfigurer {

    @Bean
    Sender sender(@Value("${zipkin.base.url}") String url) {
        return OkHttpSender.newBuilder()
                .encoding(Encoding.PROTO3)
                .endpoint(url)
                .build();
    }

    /**
     * Configuration for how to buffer spans into messages for Zipkin
     */
    @Bean
    @ConditionalOnBean(Sender.class)
    AsyncReporter<Span> spanReporter(Sender sender) {
        AsyncReporter.Builder builder = AsyncReporter.builder(sender);
        builder.queuedMaxSpans(50000);
        builder.queuedMaxBytes(104857600);
        return builder.build();
    }


    /**
     * Controls aspects of tracing such as the service name that shows up in the UI
     */
    @Bean
    Tracing tracing(@Value("${dubbo.application.name}") String applicationName, @Value("${zipkin.enable:false}") Boolean enable, @Autowired(required = false) AsyncReporter spanReporter) {
        Tracing.Builder builder = Tracing.newBuilder()
                .localServiceName(applicationName)
                .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "user-name"))
                .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
                        // puts trace IDs into logs
                        .addScopeDecorator(MDCScopeDecorator.create())
                        .build()
                );
        if (enable) {
            builder.spanReporter(spanReporter);
            builder.sampler(Sampler.ALWAYS_SAMPLE);
        } else {
            builder.sampler(Sampler.NEVER_SAMPLE);
        }
        return builder.build();
    }

    @Bean
    SpanCustomizer spanCustomizer(Tracing tracing) {
        return CurrentSpanCustomizer.create(tracing);
    }

    /**
     * Decides how to name and tag spans. By default they are named the same as the http method
     */
    @Bean
    HttpTracing httpTracing(Tracing tracing) {
        return HttpTracing.create(tracing);
    }


    @Bean
    RpcTracing rpcTracing(Tracing tracing) {
        return RpcTracing.create(tracing);
    }

    /**
     * Creates server spans for http requests
     */
    @Bean
    Filter tracingFilter(HttpTracing httpTracing) {
        return TracingFilter.create(httpTracing);

    }

    @Autowired
    SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;

    /**
     * Decorates server spans with application-defined web tags
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(webMvcTracingCustomizer);
    }
}

五、其他工程添加配置

前提:所有工程均依賴(lài)basic-common衅斩。
dubbo工程的provider和consumer均添加如下配置(請(qǐng)注意必須添加dubbo.consumer.filter和dubbo.provider.filter

dubbo:
  application:
    name: foundationRpc #服務(wù)名稱(chēng)
  consumer:
    timeout: 3000 #消費(fèi)超時(shí)時(shí)間
    retries: 1 #重試次數(shù)
    filter: tracing # 請(qǐng)注意必須添加該配置
    check: false
  provider:
    filter: tracing # 請(qǐng)注意必須添加該配置
  protocol:
    name: dubbo
zipkin:
  enable: true
  base:
    url: http://127.0.0.1:9411/api/v2/spans

六盆顾、測(cè)試

設(shè)計(jì)如下調(diào)用關(guān)系:


image.png

server定義http入口:

    @GetMapping("tracing")
    public String exec(){
        log.info("come in tracing");
        return rpcDemoService.tracing();
    }

rpcDemoService為server中定義的service,實(shí)現(xiàn)如下:

package com.wd.basic.portal.server.modular.demo.service.impl;

import cn.hutool.core.util.StrUtil;
import com.wd.basic.foundation.rpc.test.service.DemoRpcService;
import com.wd.basic.portal.rpc.IDemoService;
import com.wd.basic.portal.rpc.ITraceDemoService;
import com.wd.basic.portal.server.modular.demo.service.RpcDemoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;

/**
 * rpc演示服務(wù)impl
 *
 * @author 小塵哥
 * @date 2022/04/14
 */
@Slf4j
@Service
public class RpcDemoServiceImpl implements RpcDemoService {

    @DubboReference(interfaceClass = IDemoService.class, version = "2.0")
    private IDemoService demoService2;


    @DubboReference(interfaceClass = IDemoService.class, version = "1.0")
    private IDemoService demoService1;

    @DubboReference(interfaceClass = ITraceDemoService.class, version = "1.0")
    private ITraceDemoService traceDemoService;

    @DubboReference(interfaceClass = DemoRpcService.class, version = "1.0")
    private DemoRpcService demoRpcService;

    @Override
    public String tracing() {
        log.info("come in exec service");
        String rpc2 = demoService2.testRpc();
        return StrUtil.join(",", rpc2);
    }
}

IDemoService 定義在portal-api中畏梆,實(shí)現(xiàn)如下

package com.wd.basic.portal.rpc.impl;


import com.wd.basic.foundation.rpc.test.service.DemoRpcService;
import com.wd.basic.portal.rpc.IDemoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;

/**
 * 演示服務(wù)impl
 *
 * @author 小塵哥
 * @date 2022/03/16
 */
@Slf4j
@DubboService(interfaceClass = IDemoService.class,version = "2.0")
public class DemoServiceV2Impl implements IDemoService {

    @DubboReference(interfaceClass = DemoRpcService.class, version = "1.0")
    private DemoRpcService demoRpcService;

    @Override
    public String testRpc() {
        log.info("來(lái)自統(tǒng)一門(mén)戶(hù) RPC 測(cè)試");
        demoRpcService.testRpc();
        return "調(diào)用到了rpc服務(wù) version 2.0.0";
    }
}

DemoRpcService 定義在foundation-api中您宪,實(shí)現(xiàn)如下:

package com.wd.basic.foundation.rpc.test.service.impl;

import com.wd.basic.foundation.rpc.test.service.DemoRpcService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;

/**
 * test Rpc服務(wù)
 * @author 上官婉兒
 */
@Slf4j
@DubboService(interfaceClass = DemoRpcService.class,version = "1.0")
public class DemoRpcServiceImpl implements DemoRpcService {

    @Override
    public String testRpc() {
        log.info("來(lái)自基礎(chǔ)服務(wù) RPC 測(cè)試");
        return "RPC服務(wù)! from foundation";
    }
}

七奠涌、日志配置

臨門(mén)一腳了宪巨,以上都配置好了,但是哪里能看到效果呢溜畅?肯定是需要從日志來(lái)跟蹤捏卓,我們采用springboot推薦的logback來(lái)記錄日志,請(qǐng)注意日志記錄格式慈格,添加了部分內(nèi)容“【%X{traceId},%X{spanId},%X{parentId}】”怠晴,完整配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志級(jí)別從低到高分為T(mén)RACE < DEBUG < INFO < WARN < ERROR < FATAL贿肩,如果設(shè)置為WARN,則低于WARN的信息都不會(huì)輸出 -->
<!-- scan:當(dāng)此屬性設(shè)置為true時(shí)龄寞,配置文件如果發(fā)生改變汰规,將會(huì)被重新加載,默認(rèn)值為true -->
<!-- scanPeriod:設(shè)置監(jiān)測(cè)配置文件是否有修改的時(shí)間間隔物邑,如果沒(méi)有給出時(shí)間單位溜哮,默認(rèn)單位是毫秒。當(dāng)scan為true時(shí)色解,此屬性生效茂嗓。默認(rèn)的時(shí)間間隔為1分鐘。 -->
<!-- debug:當(dāng)此屬性設(shè)置為true時(shí)科阎,將打印出logback內(nèi)部日志信息述吸,實(shí)時(shí)查看logback運(yùn)行狀態(tài)。默認(rèn)值為false锣笨。 -->
<configuration scan="true" scanPeriod="10 seconds">
<!--    <include resource="org/springframework/boot/logging/logback/base.xml"
        /> -->
    <contextName>Logback For Basic Platform</contextName>
    <!-- name的值是變量的名稱(chēng)蝌矛,value的值時(shí)變量定義的值。通過(guò)定義的值會(huì)被插入到logger上下文中错英。定義變量后入撒,可以使“${}”來(lái)使用變量。 -->

    <!-- 定義日志文件 輸入位置 -->
    <springProperty scope="context" name="logDir" source="custom.log.dir" defaultValue="D:/logback"/>

    <!-- 定義日志文件 輸出級(jí)別 -->
    <springProperty scope="context" name="logLevel" source="custom.log.level" defaultValue="info"/>

    <!-- 日志最大的歷史 30天 -->
    <springProperty scope="context" name="logMaxHistory" source="custom.log.max-history" defaultValue="180"/>

    <!-- 控制臺(tái)輸出日志 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} 【%X{traceId},%X{spanId},%X{parentId}】 [%thread] %-5level %logger-%msg%n %ex{5}</pattern>
            <charset class="java.nio.charset.Charset">UTF-8</charset>
        </encoder>
    </appender>


    <!--文件日志椭岩, 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--日志文件輸出的文件名-->
            <fileNamePattern>${logDir}/%d{yyyy-MM-dd}/pf.%i.log.zip</fileNamePattern>

            <maxFileSize>50MB</maxFileSize>  <!-- 日志文件過(guò)大會(huì)使的編輯器打開(kāi)非常慢茅逮,因此設(shè)置日志最大50MB -->
            <!--日志文件保留天數(shù)-->
            <maxHistory>${logMaxHistory}</maxHistory>
            <totalSizeCap>10GB</totalSizeCap>  <!-- 總?cè)罩敬笮?-->
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化輸出:%d表示日期,%thread表示線(xiàn)程名判哥,%-5level:級(jí)別從左顯示5個(gè)字符寬度%msg:日志消息献雅,%n是換行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} 【%X{traceId},%X{spanId},%X{parentId}】 [%thread] %-5level %logger-%msg%n %ex{5}</pattern>
        </encoder>
    </appender>

    <!-- 異步輸出 -->
    <appender name="dayLogAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
        <includeCallerData>true</includeCallerData>
        <!-- 不丟失日志.默認(rèn)的,如果隊(duì)列的80%已滿(mǎn),則會(huì)丟棄TRACT、DEBUG塌计、INFO級(jí)別的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默認(rèn)的隊(duì)列的深度,該值會(huì)影響性能.默認(rèn)值為256 -->
        <queueSize>512</queueSize>
        <appender-ref ref="FILE"/>
    </appender>


    <!--專(zhuān)為 spring 定制
     -->
    <logger name="org.springframework" level="${logLevel}"/>

    <!-- root級(jí)別 DEBUG -->
    <root level="${logLevel}">
        <!-- 控制臺(tái)輸出 -->
        <appender-ref ref="STDOUT" />
        <!-- 文件輸出 -->
        <appender-ref ref="dayLogAsyncAppender" />
    </root>
</configuration>

八挺身、驗(yàn)證

來(lái)自server的日志

2022-04-26 16:44:25.817 【2926aa52265ad112,2926aa52265ad112,】 [http-nio-8080-exec-1] INFO  com.wd.basic.portal.server.modular.demo.controller.RpcDemoController-come in tracing
 2022-04-26 16:44:25.826 【2926aa52265ad112,2926aa52265ad112,】 [http-nio-8080-exec-1] INFO  com.wd.basic.portal.server.modular.demo.service.impl.RpcDemoServiceImpl-come in exec service

來(lái)自portal-api的日志

 2022-04-26 16:44:25.882 【2926aa52265ad112,6a4da6311934ab65,2926aa52265ad112】 [DubboServerHandler-10.1.252.224:20881-thread-5] INFO  com.wd.basic.portal.rpc.impl.DemoServiceV2Impl-來(lái)自統(tǒng)一門(mén)戶(hù) RPC 測(cè)試

來(lái)自foundation-api的日志

 2022-04-26 16:44:25.926 【2926aa52265ad112,53dbfb344223ef50,6a4da6311934ab65】 [DubboServerHandler-10.1.252.224:20880-thread-3] INFO  com.wd.basic.foundation.rpc.test.service.impl.DemoRpcServiceImpl-來(lái)自基礎(chǔ)服務(wù) RPC 測(cè)試

九、日志分析

提取三部分日志中括號(hào)中的內(nèi)容:

統(tǒng)一門(mén)戶(hù):traceId = 2926aa52265ad112,spanId=2926aa52265ad112夺荒,parentId=’’;
統(tǒng)一門(mén)戶(hù)RPC服務(wù):traceId = 2926aa52265ad112,spanId=6a4da6311934ab65瞒渠,parentId=2926aa52265ad112;
基礎(chǔ)服務(wù)RPC服務(wù):traceId = 2926aa52265ad112,spanId=53dbfb344223ef50,parentId=6a4da6311934ab65;

說(shuō)明:

  1. 對(duì)于server技扼,作為請(qǐng)求入口伍玖,因此traceId和spanId相同,無(wú)上游服務(wù)剿吻,因此parentId為空窍箍。
  2. 對(duì)于portal-api服務(wù),parentId=統(tǒng)一門(mén)戶(hù)請(qǐng)求入口的spanId,因此它是統(tǒng)一門(mén)戶(hù)的下游服務(wù)椰棘。
  3. 對(duì)于foundation-api服務(wù)纺棺,parentId=同一門(mén)戶(hù)RPC服務(wù)的spanId,因此它是統(tǒng)一門(mén)戶(hù)RPC服務(wù)的下游服務(wù)邪狞。

綜上祷蝌,和測(cè)試開(kāi)始時(shí)的設(shè)計(jì)邏輯一致,由traceId串聯(lián)整個(gè)請(qǐng)求過(guò)程帆卓,由spanId和parentId構(gòu)建上下游調(diào)用關(guān)系巨朦,完成dubbo服務(wù)之間調(diào)用的鏈路跟蹤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剑令,一起剝皮案震驚了整個(gè)濱河市糊啡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吁津,老刑警劉巖棚蓄,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異碍脏,居然都是意外死亡梭依,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)潮酒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)睛挚,“玉大人,你說(shuō)我怎么就攤上這事急黎。” “怎么了侧到?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵勃教,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我匠抗,道長(zhǎng)故源,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任汞贸,我火速辦了婚禮绳军,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘矢腻。我一直安慰自己门驾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布多柑。 她就那樣靜靜地躺著奶是,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上聂沙,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天秆麸,我揣著相機(jī)與錄音,去河邊找鬼及汉。 笑死沮趣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的坷随。 我是一名探鬼主播兔毒,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼甸箱!你這毒婦竟也來(lái)了育叁?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤芍殖,失蹤者是張志新(化名)和其女友劉穎豪嗽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體豌骏,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡龟梦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窃躲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片计贰。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蒂窒,靈堂內(nèi)的尸體忽然破棺而出躁倒,到底是詐尸還是另有隱情,我是刑警寧澤洒琢,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布秧秉,位于F島的核電站,受9級(jí)特大地震影響衰抑,放射性物質(zhì)發(fā)生泄漏象迎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一呛踊、第九天 我趴在偏房一處隱蔽的房頂上張望砾淌。 院中可真熱鬧,春花似錦谭网、人聲如沸汪厨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)骄崩。三九已至聘鳞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間要拂,已是汗流浹背抠璃。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脱惰,地道東北人搏嗡。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拉一,于是被迫代替她去往敵國(guó)和親采盒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容