Hystrix 分布式系統(tǒng)限流熊痴、降級(jí)他爸、熔斷框架

為什么需要Hystrix

在大中型分布式系統(tǒng)中,通常系統(tǒng)很多依賴果善,如下圖:

在高并發(fā)訪問(wèn)下诊笤,這些依賴的穩(wěn)定性與否對(duì)系統(tǒng)的影響非常大,但是依賴有很多不可控問(wèn)題:如網(wǎng)絡(luò)連接緩慢巾陕,資源繁忙讨跟,暫時(shí)不可用,服務(wù)脫機(jī)等鄙煤,如下圖:

當(dāng)依賴阻塞時(shí)晾匠,大多數(shù)服務(wù)器的線程池就出現(xiàn)阻塞,影響整個(gè)線上服務(wù)的穩(wěn)定性梯刚,如下圖:

在復(fù)雜的分布式架構(gòu)的應(yīng)用程序有很多的依賴凉馆,都會(huì)不可避免地在某些時(shí)候失敗。高并發(fā)的依賴失敗時(shí)如果沒(méi)有隔離措施亡资,當(dāng)前應(yīng)用服務(wù)就有被拖垮的風(fēng)險(xiǎn)澜共。


Hystrix如何解決依賴隔離

  • Hystrix使用命令模式HystrixCommand(Command)包裝依賴調(diào)用邏輯,每個(gè)命令在單獨(dú)線程中/信號(hào)授權(quán)下執(zhí)行锥腻。

  • 可配置依賴調(diào)用超時(shí)時(shí)間,超時(shí)時(shí)間一般設(shè)為比99.5%平均時(shí)間略高即可嗦董。當(dāng)調(diào)用超時(shí)時(shí),直接返回或執(zhí)行fallback邏輯瘦黑。

  • 為每個(gè)依賴提供一個(gè)小的線程池或信號(hào)京革,如果線程池已滿調(diào)用將被立即拒絕奇唤,默認(rèn)不采用排隊(duì)。加速失敗判定時(shí)間匹摇。

  • 依賴調(diào)用結(jié)果分:成功咬扇、失敗/拋出異常、超時(shí)来惧、線程拒絕、短路演顾。 請(qǐng)求失敗(異常供搀,拒絕,超時(shí)钠至,短路)時(shí)執(zhí)行fallback(降級(jí))邏輯葛虐。

  • 提供熔斷器組件,可以自動(dòng)運(yùn)行或手動(dòng)調(diào)用棉钧,停止當(dāng)前依賴一段時(shí)間(10秒)屿脐,熔斷器默認(rèn)錯(cuò)誤率閾值為50%,超過(guò)將自動(dòng)運(yùn)行宪卿。

  • 提供近實(shí)時(shí)依賴的統(tǒng)計(jì)和監(jiān)控的诵。

Hystrix依賴的隔離架構(gòu),如下圖:


如何使用Hystrix

使用maven引入Hystrix依賴
<hystrix.version>1.3.16</hystrix.version>  
<hystrix-metrics-event-stream.version>1.1.2</hystrix-metrics-event-stream.version>   

<dependency>  
     <groupId>com.netflix.hystrix</groupId>  
     <artifactId>hystrix-core</artifactId>  
     <version>${hystrix.version}</version>  
 </dependency>  
     <dependency>  
     <groupId>com.netflix.hystrix</groupId>  
     <artifactId>hystrix-metrics-event-stream</artifactId>  
     <version>${hystrix-metrics-event-stream.version}</version>  
 </dependency>  
使用命令模式封裝依賴邏輯
public class HelloWorldCommand extends HystrixCommand<String> {  
    private final String name;  
    public HelloWorldCommand(String name) {  
        //最少配置:指定命令組名(CommandGroup)  
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));  
        this.name = name;  
    }  
    @Override  
    protected String run() {  
        // 依賴邏輯封裝在run()方法中  
        return "Hello " + name +" thread:" + Thread.currentThread().getName();  
    }  
    //調(diào)用實(shí)例  
    public static void main(String[] args) throws Exception{  
        //每個(gè)Command對(duì)象只能調(diào)用一次,不可以重復(fù)調(diào)用,  
        //重復(fù)調(diào)用對(duì)應(yīng)異常信息
        HelloWorldCommand helloWorldCommand = new HelloWorldCommand("sync-hystrix");  
        //使用execute()同步調(diào)用代碼,效果等同于:helloWorldCommand.queue().get();   
        String result = helloWorldCommand.execute();  
        System.out.println("result=" + result);  

        helloWorldCommand = new HelloWorldCommand("async-hystrix");  
        //異步調(diào)用,可自由控制獲取結(jié)果時(shí)機(jī),  
        Future<String> future = helloWorldCommand.queue();  
        //get操作不能超過(guò)command定義的超時(shí)時(shí)間,默認(rèn):1秒  
        result = future.get(100, TimeUnit.MILLISECONDS);  
        System.out.println("result=" + result);  
        System.out.println("mainThread=" + Thread.currentThread().getName());  
    }       
}  

使用Fallback() 提供降級(jí)策略

//重載HystrixCommand的getFallback方法實(shí)現(xiàn)邏輯  
public class HelloWorldCommand extends HystrixCommand<String> {  
    private final String name;  
    public HelloWorldCommand(String name) {  
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))
            .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                            .withExecutionIsolationThreadTimeoutInMilliseconds(500)));  
        this.name = name;  
    }  
    @Override  
    protected String getFallback() {  
        return "exeucute Falled";  
    }  
    @Override  
    protected String run() throws Exception {  
        //sleep 1 秒,調(diào)用會(huì)超時(shí)  
        TimeUnit.MILLISECONDS.sleep(1000);  
        return "Hello " + name +" thread:" + Thread.currentThread().getName();  
    }  
    public static void main(String[] args) throws Exception{  
        HelloWorldCommand command = new HelloWorldCommand("test-Fallback");  
        String result = command.execute();  
    }  
}  

NOTE: 除了HystrixBadRequestException異常之外佑钾,所有從run()方法拋出的異常都算作失敗西疤,并觸發(fā)降級(jí)getFallback()和斷路器邏輯。

HystrixBadRequestException用在非法參數(shù)或非系統(tǒng)故障異常等不應(yīng)觸發(fā)回退邏輯的場(chǎng)景休溶。

依賴命名:CommandKey
public HelloWorldCommand(String name) {  
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))  
                /* HystrixCommandKey工廠定義依賴名稱 */  
                .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")));  
        this.name = name;  
}

NOTE: 每個(gè)CommandKey代表一個(gè)依賴抽象代赁,相同的依賴要使用相同的CommandKey名稱。依賴隔離的根本就是對(duì)相同CommandKey的依賴做隔離兽掰。

依賴分組:CommandGroup

命令分組用于對(duì)依賴操作分組芭碍,便于統(tǒng)計(jì),匯總等孽尽。

//使用HystrixCommandGroupKey工廠定義  
public HelloWorldCommand(String name) {  
    Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))  
}

NOTE: CommandGroup是每個(gè)命令最少配置的必選參數(shù)窖壕,在不指定ThreadPoolKey的情況下,字面值用于對(duì)不同依賴的線程池/信號(hào)區(qū)分杉女。

線程池/信號(hào):ThreadPoolKey
public HelloWorldCommand(String name) {  
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))  
                .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))  
                /* 使用HystrixThreadPoolKey工廠定義線程池名稱*/  
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));  
        this.name = name;  
}

NOTE: 當(dāng)對(duì)同一業(yè)務(wù)依賴做隔離時(shí)使用CommandGroup做區(qū)分艇拍,但是對(duì)同一依賴的不同遠(yuǎn)程調(diào)用如(一個(gè)是redis 一個(gè)是http),可以使用HystrixThreadPoolKey做隔離區(qū)分宠纯。

最然在業(yè)務(wù)上都是相同的組卸夕,但是需要在資源上做隔離時(shí),可以使用HystrixThreadPoolKey區(qū)分婆瓜。

信號(hào)量隔離:SEMAPHORE

隔離本地代碼或可快速返回遠(yuǎn)程調(diào)用(如memcached快集,redis)可以直接使用信號(hào)量隔離贡羔,降低線程隔離開(kāi)銷。

public class HelloWorldCommand extends HystrixCommand<String> {  
    private final String name;  
    public HelloWorldCommand(String name) {  
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))  
                /* 配置信號(hào)量隔離方式,默認(rèn)采用線程池隔離 */  
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                                    .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)));  
        this.name = name;  
    }  
    @Override  
    protected String run() throws Exception {  
        return "HystrixThread:" + Thread.currentThread().getName();  
    }  
    public static void main(String[] args) throws Exception{  
        HelloWorldCommand command = new HelloWorldCommand("semaphore");  
        String result = command.execute();  
        System.out.println(result);  
        System.out.println("MainThread:" + Thread.currentThread().getName());  
    }  
}  

Hystrix關(guān)鍵組件分析

Hystrix流程結(jié)構(gòu)解析

流程說(shuō)明:

1个初,每次調(diào)用創(chuàng)建一個(gè)新的HystrixCommand乖寒,把依賴調(diào)用封裝在run()方法中

2,執(zhí)行execute()/queue做同步或異步調(diào)用

3院溺,判斷熔斷器(circuit-breaker)是否打開(kāi)楣嘁,如果打開(kāi)跳到步驟8,進(jìn)行降級(jí)策略珍逸,否則繼續(xù)后續(xù)步驟

4逐虚,判斷線程池/隊(duì)列/信號(hào)量是否跑滿,如果跑滿進(jìn)入降級(jí)步驟8谆膳,否則繼續(xù)后續(xù)步驟

5叭爱,調(diào)用HystrixCommand的run方法,運(yùn)行依賴邏輯

a 依賴邏輯調(diào)用超時(shí)漱病,進(jìn)入步驟8

6买雾,判斷邏輯是否調(diào)用成功

a 返回成功調(diào)用結(jié)果

b 調(diào)用出錯(cuò),進(jìn)入步驟8

7杨帽,計(jì)算熔斷器狀態(tài)漓穿,所有的運(yùn)行狀態(tài)上報(bào)給熔斷器,用于統(tǒng)計(jì)從而判斷熔斷器狀態(tài)

8注盈,getFallback()降級(jí)邏輯

以下四種情況將觸發(fā)getFallback調(diào)用:

  • run()方法拋出非HystrixBadRequestException異常
  • run()方法調(diào)用超時(shí)
  • 熔斷器開(kāi)啟攔截調(diào)用
  • 線程池/隊(duì)列/信號(hào)量是否跑滿

沒(méi)有實(shí)現(xiàn)getFallback的Command將直接拋出異常

fallback降級(jí)邏輯調(diào)用成功直接返回

降級(jí)邏輯調(diào)用失敗拋出異常

9器净,返回執(zhí)行成功結(jié)果

熔斷器:Circuit Breaker

Circuit Breaker 流程架構(gòu)和統(tǒng)計(jì)

每個(gè)熔斷器默認(rèn)維護(hù)10個(gè)bucket,每秒一個(gè)bucket当凡,每個(gè)blucket記錄成功山害、失敗、超時(shí)沿量、拒絕的狀態(tài)浪慌,默認(rèn)錯(cuò)誤超過(guò)50%且10秒內(nèi)超過(guò)20個(gè)請(qǐng)求進(jìn)行中斷攔截.。

隔離(Isolation)分析

Hystrix隔離方式采用線程/信號(hào)的方式朴则,通過(guò)隔離限制依賴的并發(fā)量和阻塞擴(kuò)散权纤。

(1) 線程隔離

把執(zhí)行依賴代碼的線程與請(qǐng)求線程分離,請(qǐng)求線程可以自由控制離開(kāi)的時(shí)間(異步過(guò)程)乌妒。

通過(guò)線程池大小可以控制并發(fā)量汹想,當(dāng)線程池飽和時(shí)可以提前拒絕服務(wù),防止依賴問(wèn)題擴(kuò)散撤蚊。

線上建議線程池不要設(shè)置過(guò)大古掏,否則大量堵塞線程有可能會(huì)拖慢服務(wù)器。

線程池的使用示意圖如下圖所示侦啸,當(dāng)n個(gè)請(qǐng)求線程并發(fā)對(duì)某個(gè)接口請(qǐng)求調(diào)用時(shí)槽唾,會(huì)先從hystrix管理的線程池里面獲得一個(gè)線程丧枪,然后將參數(shù)傳遞給這個(gè)線程去執(zhí)行真正調(diào)用。線程池的大小有限庞萍,默認(rèn)是10個(gè)線程拧烦,可以使用maxConcurrentRequests參數(shù)配置,如果并發(fā)請(qǐng)求數(shù)多于線程池線程個(gè)數(shù)钝计,就有線程需要進(jìn)入隊(duì)列排隊(duì)恋博,但排隊(duì)隊(duì)列也有上限,默認(rèn)是 5私恬,如果排隊(duì)隊(duì)列也滿债沮,則必定有請(qǐng)求線程會(huì)走fallback流程。

線程池模式可以支持異步調(diào)用践付,支持超時(shí)調(diào)用秦士,支持直接熔斷缺厉,存在線程切換永高,開(kāi)銷大。

(2) 線程隔離的優(yōu)缺點(diǎn)

線程隔離的優(yōu)點(diǎn):

  • 使用線程可以完全隔離第三方代碼提针,請(qǐng)求線程可以快速放回命爬。

  • 當(dāng)一個(gè)失敗的依賴再次變成可用時(shí),線程池將清理辐脖,并立即恢復(fù)可用饲宛,而不是一個(gè)長(zhǎng)時(shí)間的恢復(fù)。

  • 可以完全模擬異步調(diào)用嗜价,方便異步編程艇抠。

線程隔離的缺點(diǎn):

  • 線程池的主要缺點(diǎn)是它增加了cpu,因?yàn)槊總€(gè)命令的執(zhí)行涉及到排隊(duì)(默認(rèn)使用SynchronousQueue避免排隊(duì))久锥,調(diào)度和上下文切換家淤。

  • 對(duì)使用ThreadLocal等依賴線程狀態(tài)的代碼增加復(fù)雜性,需要手動(dòng)傳遞和清理線程狀態(tài)瑟由。

NOTE: Netflix公司內(nèi)部認(rèn)為線程隔離開(kāi)銷足夠小絮重,不會(huì)造成重大的成本或性能的影響。

Netflix內(nèi)部API每天100億的HystrixCommand依賴請(qǐng)求使用線程隔歹苦,每個(gè)應(yīng)用大約40多個(gè)線程池青伤,每個(gè)線程池大約5-20個(gè)線程。

(3) 信號(hào)隔離

信號(hào)隔離也可以用于限制并發(fā)訪問(wèn)殴瘦,防止阻塞擴(kuò)散, 與線程隔離最大不同在于執(zhí)行依賴代碼的線程依然是請(qǐng)求線程(該線程需要通過(guò)信號(hào)申請(qǐng))狠角。

如果客戶端是可信的且可以快速返回,可以使用信號(hào)隔離替換線程隔離蚪腋,降低開(kāi)銷擎厢。

線程隔離與信號(hào)隔離區(qū)別如下圖:

信號(hào)量的使用示意圖如下圖所示究流,當(dāng)n個(gè)并發(fā)請(qǐng)求去調(diào)用一個(gè)目標(biāo)服務(wù)接口時(shí),都要獲取一個(gè)信號(hào)量才能真正去調(diào)用目標(biāo)服務(wù)接口动遭,但信號(hào)量有限芬探,默認(rèn)是10個(gè),可以使用maxConcurrentRequests參數(shù)配置厘惦,如果并發(fā)請(qǐng)求數(shù)多于信號(hào)量個(gè)數(shù)偷仿,就有線程需要進(jìn)入隊(duì)列排隊(duì),但排隊(duì)隊(duì)列也有上限宵蕉,默認(rèn)是 5酝静,如果排隊(duì)隊(duì)列也滿,則必定有請(qǐng)求線程會(huì)走fallback流程羡玛,從而達(dá)到限流和防止雪崩的目的别智。

信號(hào)量模式從始至終都只有請(qǐng)求線程自身,是同步調(diào)用模式稼稿,不支持超時(shí)調(diào)用薄榛,不支持直接熔斷,由于沒(méi)有線程的切換让歼,開(kāi)銷非常小敞恋。

(4) 總結(jié)

當(dāng)請(qǐng)求的服務(wù)網(wǎng)絡(luò)開(kāi)銷比較大的時(shí)候,或者是請(qǐng)求比較耗時(shí)的時(shí)候谋右,我們最好是使用線程隔離策略硬猫,這樣的話,可以保證大量的容器(tomcat)線程可用改执,不會(huì)由于服務(wù)原因啸蜜,一直處于阻塞或等待狀態(tài),快速失敗返回辈挂。而當(dāng)我們請(qǐng)求緩存這些服務(wù)的時(shí)候衬横,我們可以使用信號(hào)量隔離策略,因?yàn)檫@類服務(wù)的返回通常會(huì)非常的快呢岗,不會(huì)占用容器線程太長(zhǎng)時(shí)間冕香,而且也減少了線程切換的一些開(kāi)銷,提高了緩存服務(wù)的效率后豫。

  • 線程池:適合絕大多數(shù)的場(chǎng)景悉尾,99%的。對(duì)依賴服務(wù)的網(wǎng)絡(luò)請(qǐng)求的調(diào)用和訪問(wèn)挫酿,timeout這種問(wèn)題

  • 信號(hào)量:適合你的訪問(wèn)不是對(duì)外部依賴的訪問(wèn)构眯,而是對(duì)內(nèi)部的一些比較復(fù)雜的業(yè)務(wù)邏輯的訪問(wèn),但是像這種訪問(wèn)早龟,系統(tǒng)內(nèi)部的代碼惫霸,其實(shí)不涉及任何的網(wǎng)絡(luò)請(qǐng)求猫缭,那么只要做信號(hào)量的普通限流就可以了,因?yàn)椴恍枰ゲ东@timeout類似的問(wèn)題壹店,算法+數(shù)據(jù)結(jié)構(gòu)的效率不是太高猜丹,并發(fā)量突然太高,因?yàn)檫@里稍微耗時(shí)一些硅卢,導(dǎo)致很多線程卡在這里的話射窒,不太好,所以進(jìn)行一個(gè)基本的資源隔離和訪問(wèn)将塑,避免內(nèi)部復(fù)雜的低效率的代碼脉顿,導(dǎo)致大量的線程被hang住

Reference:

http://blog.51cto.com/developerycj/1950881


對(duì) JAVA 開(kāi)發(fā)有興趣的朋友歡迎加入QQ群:833145934 里面資深架構(gòu)師會(huì)分享一些整理好的錄制視頻錄像和BATJ面試題:有Spring,MyBatis点寥,Netty源碼分析艾疟,高并發(fā)、高性能敢辩、分布式蔽莱、微服務(wù)架構(gòu)的原理,JVM性能優(yōu)化责鳍、分布式架構(gòu)等這些成為架構(gòu)師必備的知識(shí)體系碾褂。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源兽间,目前受益良多历葛。

共同探討!

原文出處:http://www.linkedkeeper.com/1157.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嘀略,一起剝皮案震驚了整個(gè)濱河市恤溶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帜羊,老刑警劉巖咒程,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異讼育,居然都是意外死亡帐姻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門奶段,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饥瓷,“玉大人,你說(shuō)我怎么就攤上這事痹籍∧孛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蹲缠,是天一觀的道長(zhǎng)棺克。 經(jīng)常有香客問(wèn)我悠垛,道長(zhǎng),這世上最難降的妖魔是什么娜谊? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任确买,我火速辦了婚禮,結(jié)果婚禮上纱皆,老公的妹妹穿的比我還像新娘拇惋。我一直安慰自己,他們只是感情好抹剩,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布撑帖。 她就那樣靜靜地躺著,像睡著了一般澳眷。 火紅的嫁衣襯著肌膚如雪胡嘿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,985評(píng)論 1 291
  • 那天钳踊,我揣著相機(jī)與錄音衷敌,去河邊找鬼。 笑死拓瞪,一個(gè)胖子當(dāng)著我的面吹牛缴罗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祭埂,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼面氓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蛆橡?” 一聲冷哼從身側(cè)響起舌界,我...
    開(kāi)封第一講書(shū)人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泰演,沒(méi)想到半個(gè)月后呻拌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡睦焕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年藐握,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垃喊。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猾普,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出缔御,到底是詐尸還是另有隱情抬闷,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站笤成,受9級(jí)特大地震影響评架,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炕泳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一纵诞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧培遵,春花似錦浙芙、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至皇耗,卻和暖如春南窗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背郎楼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工万伤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呜袁。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓敌买,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親阶界。 傳聞我的和親對(duì)象是個(gè)殘疾皇子虹钮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

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