Chapter Four《SpringCloud微服務(wù)實(shí)戰(zhàn)》

客戶端負(fù)載均衡 Spring Cloud Ribbon

1.綜述

對(duì)于任何一個(gè)高可用高負(fù)載的系統(tǒng)來說叶摄,負(fù)載均衡是一個(gè)必不可少的名稱。在大型分布式計(jì)算體系中薄疚,某個(gè)服務(wù)在單例的情況下盹憎,很難應(yīng)對(duì)各種突發(fā)情況。因此阳懂,負(fù)載均衡是為了讓系統(tǒng)在性能出現(xiàn)瓶頸或者其中一些出現(xiàn)狀態(tài)下可以進(jìn)行分發(fā)業(yè)務(wù)量的解決方案。

Spring Cloud Ribbon 是一個(gè)基于Http和TCP的客服端負(fù)載均衡工具柜思,它是基于Netflix Ribbon實(shí)現(xiàn)的。它不像服務(wù)注冊(cè)中心巷燥、配置中心赡盘、API網(wǎng)關(guān)那樣獨(dú)立部署,但是它幾乎存在于每個(gè)微服務(wù)的基礎(chǔ)設(shè)施中缰揪。
包括前面的提供的聲明式服務(wù)調(diào)用也是基于該Ribbon實(shí)現(xiàn)的陨享。理解Ribbon對(duì)于我們使用Spring Cloud來講非常的重要,因?yàn)樨?fù)載均衡是對(duì)系統(tǒng)的高可用钝腺、網(wǎng)絡(luò)壓力的緩解和處理能力擴(kuò)容的重要手段之一抛姑。在上節(jié)的例子中,我們采用了聲明式的方式來實(shí)現(xiàn)負(fù)載均衡艳狐。
實(shí)際上定硝,內(nèi)部調(diào)用維護(hù)了一個(gè)RestTemplate對(duì)象,該對(duì)象會(huì)使用Ribbon的自動(dòng)化配置毫目,同時(shí)通過@LoadBalanced開啟客戶端負(fù)載均衡蔬啡。其實(shí)RestTemplate是Spring自己提供的對(duì)象,不是新的內(nèi)容镀虐。

ribbon最核心的概念是:一個(gè)被命名的client箱蟆,也即一個(gè)具有唯一名字的客戶端每一個(gè)負(fù)載均衡都是整體組件的一部分,它們相互協(xié)作調(diào)用遠(yuǎn)程服務(wù)刮便。每一個(gè)client都會(huì)通過類RibbonClientConfiguration創(chuàng)建一個(gè)新的子spring ApplicationContext空猜,這個(gè)子ApplicationContext上下文的名子就是client的名字(如:@FeignClient("user-server"),每個(gè)ribbon客戶端包含:ILoadBalancer, RestClient, ServerListFilter, ServerList,IRule。
ILoadBalancer:是負(fù)載均衡的入口類辈毯。
ServerList:存儲(chǔ)遠(yuǎn)程服務(wù)所有可用節(jié)點(diǎn)
ServerListFilter:用于過濾非法的遠(yuǎn)程節(jié)點(diǎn)(如:不可用等)
IRule:負(fù)載算法(策略)坝疼,用于從可用節(jié)點(diǎn)選擇一個(gè)合適的節(jié)點(diǎn)。
RestClient:遠(yuǎn)程調(diào)用

攔截&請(qǐng)求:


image.png

2.GET請(qǐng)求

第一種getForEntity函數(shù)漓摩,該方法返回的是ResponseEntity裙士,該對(duì)象是字符串對(duì)HTTP請(qǐng)求響應(yīng)的封裝。

姓名管毙,年齡兩個(gè)參數(shù)對(duì)應(yīng)的{1}腿椎,{2}代表兩個(gè)占位符,如要傳遞多個(gè)參數(shù)以此類推夭咬。

String.class返回值啃炸,如果需要返回對(duì)象對(duì)象的.class

getBody()是返回的ResponseEntity對(duì)象中的體內(nèi)容。

    /**
 * Ribbon Get測試
 * @return
 */
@RequestMapping(value="RibbonGet",method=RequestMethod.GET)
public String RibbonGet(@RequestParam("name") String name,@RequestParam("age") String age) {
    return restTemplate.getForEntity("http://OrderService/GetTest?name={1}&age={2}", String.class,name,age).getBody();
     
}

注意占位符名稱

/**
 * Ribbon Get測試
 * @return
 */
@RequestMapping(value="RibbonGet",method=RequestMethod.GET)
public String RibbonGet(@RequestParam("name") String name,@RequestParam("age") String age) {
        
    //傳遞Map集合
    Map<String, Object> params=new HashMap<>();
    params.put("name", name);
    params.put("age", age);
    return restTemplate.getForEntity("http://OrderService/GetTest?name={name}&age={age}", String.class,params).getBody();
     
}

第二種getForObject函數(shù)卓舵,該方法可以理解為對(duì)getForEntity的進(jìn)一步封裝南用,返回的直接就是身體,如果不需要關(guān)注請(qǐng)求響應(yīng)除身體外掏湾,該函數(shù)就非常好用裹虫。

/**
 * Ribbon Get測試
 * @return
 */
@RequestMapping(value="RibbonGet",method=RequestMethod.GET)
public String RibbonGet(@RequestParam("name") String name,@RequestParam("age") String age) {

    return restTemplate.getForObject("http://OrderService/GetTest?name="+name+"&age="+age, String.class);
    
}

3.POST請(qǐng)求

第一種postForEntity函數(shù),該方法返回的是ResponseEntity <T>融击,其中?為請(qǐng)求響應(yīng)的體類型筑公。

/**
 * Ribbon Post請(qǐng)求
 * @param id
 * @return
 */
@RequestMapping(value="ribbonPostOrder",method=RequestMethod.POST)
public User PostBeanTest(@RequestParam("id") Integer id) {
    User user=new User();
    user.setId(id);
    ResponseEntity<User> entity = restTemplate.postForEntity("http://OrderService/PostBeanTest", user, User.class);
    return entity.getBody();
}

第二種postForObject函數(shù),和postForEntity類似尊浪,簡化了機(jī)身的處理匣屡。

/**
 * Ribbon Post請(qǐng)求
 * @param id
 * @return
 */
@RequestMapping(value="ribbonPostOrder",method=RequestMethod.POST)
public User PostBeanTest(@RequestParam("id") Integer id){
    User user=new User();
    user.setId(id);
    return restTemplate.postForObject("http://OrderService/PostBeanTest", user, User.class);
    
}

4.PUT請(qǐng)求

把函數(shù)為無效類型,所以沒有返回內(nèi)容拇涤。

/**
 * Ribbon Put請(qǐng)求
 * @param id
 * @return
 */
@RequestMapping(value="ribbonPutOrder",method=RequestMethod.POST)
public String ribbonPutOrder(@RequestParam("id") Integer id,@RequestParam("name") String name){
    User user=new User();
    user.setId(id);
    user.setName(name);
    restTemplate.put("http://OrderService/PutTest",user);
    return "PUT成功捣作!";
    
}

5.DELETE請(qǐng)求

deleet函數(shù)為無效類型,所以沒有返回內(nèi)容鹅士。

/**
 * Ribbon Delete請(qǐng)求
 * @param id
 * @return
 */
@RequestMapping(value="ribbonDeleteOrder",method=RequestMethod.POST)
public String ribbonDeleteOrder(@RequestParam("id") Integer id) {
    restTemplate.delete("http://OrderService/DeleteTest/?id={1}",id);
    return "刪除成功";
    
}

6.配置

自動(dòng)化配置

由于Ribbon中定義的每一個(gè)接口都有多種不同的策略實(shí)現(xiàn)券躁,同時(shí)這些之間又有一定的依賴關(guān)系。

  • com.netflix.client.config.IClientConfigRibbon的客戶端配置如绸,默認(rèn)采用com.netflix.client.config.DefaultClientConfigImpl實(shí)現(xiàn)嘱朽。

  • com.netflix.loadbalancer.IRuleRibbon的負(fù)載均衡策略,默認(rèn)采用com.netflix.loadbalancer.ZoneAvoidanceRule實(shí)現(xiàn)怔接,該策略能夠在多區(qū)域環(huán)境下選出最佳區(qū)域的實(shí)例進(jìn)行訪問搪泳。

  • com.netflix.loadbalancer.IPingRibbon的實(shí)例檢查策略,默認(rèn)采用com.netflix.loadbalancer.NoOpPing實(shí)現(xiàn)扼脐,該檢查策略是一個(gè)特殊的實(shí)現(xiàn)岸军,實(shí)際上它并不會(huì)檢查實(shí)例是否可用奋刽,而是始終返回true,默認(rèn)認(rèn)為所有服務(wù)實(shí)例都是可用的艰赞。

  • com.netflix.loadbalancer.ServerList:服務(wù)實(shí)例清單的維護(hù)機(jī)制佣谐,默認(rèn)采用com.netflix.loadbalancer.ConfigurationBasedServerList實(shí)現(xiàn)。

  • com.netflix.loadbalancer.ServerListFilter:服務(wù)實(shí)例清單過濾機(jī)制方妖,默認(rèn)采org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter狭魂,該策略能夠優(yōu)先過濾出與請(qǐng)求方處于同區(qū)域的服務(wù)實(shí)例。

  • com.netflix.loadbalancer.ILoadBalancer:負(fù)載均衡器党觅,默認(rèn)采用com.netflix.loadbalancer.ZoneAwareLoadBalancer實(shí)現(xiàn)雌澄,它具備了區(qū)域感知的能力。

上面的配置是在項(xiàng)目中沒有引入spring Cloud Eureka杯瞻,如果引入了EurekaRibbon依賴時(shí)镐牺,自動(dòng)化配置會(huì)有一些不同。

通過自動(dòng)化配置的實(shí)現(xiàn)魁莉,可以輕松的實(shí)現(xiàn)客戶端的負(fù)載均衡睬涧。同時(shí),針對(duì)一些個(gè)性化需求旗唁,我們可以方便的替換上面的這些默認(rèn)實(shí)現(xiàn)畦浓,只需要在springboot應(yīng)用中創(chuàng)建對(duì)應(yīng)的實(shí)現(xiàn)實(shí)例就能覆蓋這些默認(rèn)的配置實(shí)現(xiàn)。

@Configuration
public class MyRibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }
}

這樣就會(huì)使用P使用了RandomRule實(shí)例替代了默認(rèn)的com.netflix.loadbalancer.ZoneAvoidanceRule检疫。

也可以使用@RibbonClient注解實(shí)現(xiàn)更細(xì)粒度的客戶端配置

Camden版本對(duì)RabbitClient配置的優(yōu)化

上面的方式主要是通過獨(dú)立創(chuàng)建一個(gè)Configuration類來定義IPing宅粥,IRule等接口的具體實(shí)現(xiàn)Bean,然后通過RabbonClient時(shí)指定要使用的具體Configuration類來覆蓋自動(dòng)化配置的默認(rèn)實(shí)現(xiàn)电谣。在spring Cloud Ribbon Camden版本中對(duì)RibbonClient定義個(gè)性化配置的方法做出進(jìn)一步優(yōu)化∧ㄊ矗可以直接使用<clientName>.ribbon.<key>=<value>的形式進(jìn)行配置剿牺。

user-service.ribbon.NFLoadBalancerPingClassName=com.netfix.loadbalancer.PingUrl

user-service是服務(wù)名,NFLoadBalancerPingClassName參數(shù)是用來指定IPing接口實(shí)現(xiàn)類环壤。在Camden版本中晒来,Spring Cloud Ribbon新增了一個(gè)org.springframework.cloud.netflix.ribbon.PropertiesFactory類動(dòng)態(tài)的為RibbonClient創(chuàng)建這些接口實(shí)現(xiàn)。

image

Camden版本中我們可以通過配置的方式郑现,更加方便的為RibbonClient指定ILoadBalancer湃崩,IPingIRule接箫,ServerList攒读,ServerListFilter的定制化實(shí)現(xiàn)。

參數(shù)配置

對(duì)于Ribbon的參數(shù)通常有二種方式:全局配置以及指定客戶端配置

  • 全局配置的方式很簡單
    只需要使用ribbon.<key>=<value>格式進(jìn)行配置即可辛友。其中薄扁,<key>代表了Ribbon客戶端配置的參數(shù)名剪返,<value>則代表了對(duì)應(yīng)參數(shù)的值。比如邓梅,我們可以想下面這樣配置Ribbon的超時(shí)時(shí)間
ribbon.ConnectTimeout=250

全局配置可以作為默認(rèn)值進(jìn)行設(shè)置脱盲,當(dāng)指定客戶端配置了相應(yīng)的key的值時(shí),將覆蓋全局配置的內(nèi)容

  • 指定客戶端的配置方式
    <client>.ribbon.<key>=<value>的格式進(jìn)行配置.<client>表示服務(wù)名日缨,比如沒有服務(wù)治理框架的時(shí)候(如Eureka)钱反,我們需要指定實(shí)例清單,可以指定服務(wù)名來做詳細(xì)的配置匣距,
user-service.ribbon.listOfServers=localhost:8080,localhost:8081,localhost:8082

對(duì)于Ribbon參數(shù)的key以及value類型的定義面哥,可以通過查看com.netflix.client.config.CommonClientConfigKey類。

與Eureka結(jié)合

當(dāng)在spring Cloud的應(yīng)用同時(shí)引入Spring cloud RibbonSpring Cloud Eureka依賴時(shí)墨礁,會(huì)觸發(fā)Eureka中實(shí)現(xiàn)的對(duì)Ribbon的自動(dòng)化配置幢竹。這時(shí)的serverList的維護(hù)機(jī)制實(shí)現(xiàn)將被com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList的實(shí)例所覆蓋,該實(shí)現(xiàn)會(huì)講服務(wù)清單列表交給Eureka的服務(wù)治理機(jī)制來進(jìn)行維護(hù)恩静。IPing的實(shí)現(xiàn)將被
com.netflix.niws.loadbalancer.NIWSDiscoveryPing的實(shí)例所覆蓋焕毫,該實(shí)例也將實(shí)例接口的任務(wù)交給了服務(wù)治理框架來進(jìn)行維護(hù)。默認(rèn)情況下驶乾,用于獲取實(shí)例請(qǐng)求的ServerList接口實(shí)現(xiàn)將采用Spring Cloud Eureka中封裝的
org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList邑飒,其目的是為了讓實(shí)例維護(hù)策略更加通用,所以將使用物理元數(shù)據(jù)來進(jìn)行負(fù)載均衡级乐,而不是使用原生的AWS AMI元數(shù)據(jù)疙咸。

在與Spring cloud Eureka結(jié)合使用的時(shí)候,不需要再去指定類似的user-service.ribbon.listOfServers的參數(shù)來指定具體的服務(wù)實(shí)例清單风科,因?yàn)?code>Eureka將會(huì)為我們維護(hù)所有服務(wù)的實(shí)例清單撒轮,而對(duì)于Ribbon的參數(shù)配置,我們依然可以采用之前的兩種配置方式來實(shí)現(xiàn)贼穆。

此外题山,由于spring Cloud Ribbon默認(rèn)實(shí)現(xiàn)了區(qū)域親和策略,所以故痊,可以通過Eureka實(shí)例的元數(shù)據(jù)配置來實(shí)現(xiàn)區(qū)域化的實(shí)例配置方案顶瞳。比如可以將不同機(jī)房的實(shí)例配置成不同的區(qū)域值,作為跨區(qū)域的容器機(jī)制實(shí)現(xiàn)愕秫。而實(shí)現(xiàn)也非常簡單慨菱,只需要服務(wù)實(shí)例的元數(shù)據(jù)中增加zone參數(shù)來指定自己所在的區(qū)域,比如:
eureka.instance.metadataMap.zone=shanghai
Spring Cloud Ribbon與Spring Cloud Eureka結(jié)合的工程中戴甩,我們可以通過參數(shù)禁用Eureka對(duì)Ribbon服務(wù)實(shí)例的維護(hù)實(shí)現(xiàn)符喝。這時(shí)又需要自己去維護(hù)服務(wù)實(shí)例列表了。

ribbon.eureka.enabled=false.

7. 重試機(jī)制

由于Spring Cloud Eureka實(shí)現(xiàn)的服務(wù)治理機(jī)制強(qiáng)調(diào)了cap原理的ap機(jī)制(即可用性和可靠性)甜孤,與zookeeper這類強(qiáng)調(diào)cp(一致性洲劣,可靠性)服務(wù)質(zhì)量框架最大的區(qū)別就是备蚓,Eureka為了實(shí)現(xiàn)更高的服務(wù)可用性,犧牲了一定的一致性囱稽,在極端情況下寧愿接受故障實(shí)例也不要丟棄"健康"實(shí)例郊尝。

比如說,當(dāng)服務(wù)注冊(cè)中心的網(wǎng)絡(luò)發(fā)生故障斷開時(shí)候战惊,由于所有的服務(wù)實(shí)例無法維護(hù)續(xù)約心跳流昏,在強(qiáng)調(diào)ap的服務(wù)治理中將會(huì)把所有服務(wù)實(shí)例剔除掉,而Eureka則會(huì)因?yàn)槌^85%的實(shí)例丟失心跳而觸發(fā)保護(hù)機(jī)制吞获,注冊(cè)中心將會(huì)保留此時(shí)的所有節(jié)點(diǎn)况凉,以實(shí)現(xiàn)服務(wù)間依然可以進(jìn)行互相調(diào)用的場景,即使其中有部分故障節(jié)點(diǎn)各拷,但這樣做可以繼續(xù)保障大多數(shù)服務(wù)的正常消費(fèi)刁绒。

Camden版本,整合了spring retry來增強(qiáng)RestTemplate的重試能力烤黍,對(duì)于我們開發(fā)者來說知市,只需要簡單配置,即可完成重試策略速蕊。

spring.cloud.loadbalancer.retry.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000

user-service.ribbon.ConnectTimeout=250
user-service.ribbon.ReadTimeout=1000
user-service.ribbon.OkToRetryOnAllOperations=true
user-service.ribbon.MaxAutoRetriesNextServer=2
user-service.ribbon.maxAutoRetries=1

spring.cloud.loadbalancer.retry.enabled:該參數(shù)用來開啟重試機(jī)制嫂丙,它默認(rèn)是關(guān)閉的。

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:斷路器的超時(shí)時(shí)間需要大于Ribbon的超時(shí)時(shí)間规哲,不然不會(huì)觸發(fā)重試跟啤。

user-service.ribbon.ConnectTimeout:請(qǐng)求連接超時(shí)時(shí)間。
user-service.ribbon.ReadTimeout:請(qǐng)求處理的超時(shí)時(shí)間
user-service.ribbon.OkToRetryOnAllOperations:對(duì)所有操作請(qǐng)求都進(jìn)行重試唉锌。
user-service.ribbon.MaxAutoRetriesNextServer:切換實(shí)例的重試次數(shù)隅肥。
user-service.ribbon.maxAutoRetries:對(duì)當(dāng)前實(shí)例的重試次數(shù)。

根據(jù)以上配置袄简,當(dāng)訪問到故障請(qǐng)求的時(shí)候武福,它會(huì)再嘗試訪問一次當(dāng)前實(shí)例(次數(shù)由maxAutoRetries配置),如果不行痘番,就換一個(gè)實(shí)例進(jìn)行訪問,如果還是不行平痰,再換一個(gè)實(shí)例訪問(更換次數(shù)由MaxAutoRetriesNextServer配置)汞舱,如果還不行,返回失敗宗雇。

image.png

8.Ribbon 提供的主要負(fù)載均衡策略:

1:簡單輪詢負(fù)載均衡(RoundRobin)

 以輪詢的方式依次將請(qǐng)求調(diào)度不同的服務(wù)器昂芜,即每次調(diào)度執(zhí)行i = (i + 1) mod n,并選出第i臺(tái)服務(wù)器赔蒲。

2:隨機(jī)負(fù)載均衡 (Random)

 隨機(jī)選擇狀態(tài)為UP的Server

3:加權(quán)響應(yīng)時(shí)間負(fù)載均衡 (WeightedResponseTime)

 根據(jù)響應(yīng)時(shí)間分配一個(gè)weight泌神,響應(yīng)時(shí)間越長良漱,weight越小,被選中的可能性越低欢际。

4:區(qū)域感知輪詢負(fù)載均衡(ZoneAvoidanceRule)

 復(fù)合判斷server所在區(qū)域的性能和server的可用性選擇server

less is more.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末母市,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子损趋,更是在濱河造成了極大的恐慌患久,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浑槽,死亡現(xiàn)場離奇詭異蒋失,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)桐玻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門篙挽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人镊靴,你說我怎么就攤上這事铣卡。” “怎么了邑闲?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵算行,是天一觀的道長。 經(jīng)常有香客問我苫耸,道長州邢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任褪子,我火速辦了婚禮量淌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嫌褪。我一直安慰自己呀枢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布笼痛。 她就那樣靜靜地躺著裙秋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缨伊。 梳的紋絲不亂的頭發(fā)上摘刑,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音刻坊,去河邊找鬼枷恕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛谭胚,可吹牛的內(nèi)容都是我干的徐块。 我是一名探鬼主播未玻,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼胡控!你這毒婦竟也來了扳剿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤铜犬,失蹤者是張志新(化名)和其女友劉穎舞终,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體癣猾,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敛劝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纷宇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夸盟。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖像捶,靈堂內(nèi)的尸體忽然破棺而出上陕,到底是詐尸還是另有隱情,我是刑警寧澤拓春,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布释簿,位于F島的核電站,受9級(jí)特大地震影響硼莽,放射性物質(zhì)發(fā)生泄漏庶溶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一懂鸵、第九天 我趴在偏房一處隱蔽的房頂上張望偏螺。 院中可真熱鬧,春花似錦匆光、人聲如沸套像。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夺巩。三九已至,卻和暖如春周崭,著一層夾襖步出監(jiān)牢的瞬間柳譬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國打工休傍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹲姐。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓磨取,卻偏偏與公主長得像人柿,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子忙厌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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