服務(wù)網(wǎng)關(guān)Spring Cloud Zuul(Finchley版本)

一、Zuul簡(jiǎn)介

Zuul作為微服務(wù)系統(tǒng)的網(wǎng)關(guān)組件蟆融,用于構(gòu)建邊界服務(wù)(Edge Service),致力于動(dòng)態(tài)路由守呜、過(guò)濾型酥、監(jiān)控山憨、彈性伸縮和安全。其在微服務(wù)架構(gòu)中有著重要的作用弥喉,主要體現(xiàn)在以下六個(gè)方面:

  • Zull郁竟、Ribbon以及Eureka相結(jié)合可以實(shí)現(xiàn)智能路由和負(fù)載均衡的功能,Zull可以按照某種策略將請(qǐng)求分發(fā)到不同的實(shí)例上由境;
  • 網(wǎng)關(guān)作為邊界服務(wù)棚亩,將內(nèi)部服務(wù)的API接口進(jìn)行聚合并統(tǒng)一對(duì)外暴露接口。保護(hù)內(nèi)部服務(wù)的API接口虏杰,防止內(nèi)部服務(wù)被外界調(diào)用泄露敏感信息讥蟆;
  • 網(wǎng)關(guān)可以對(duì)用戶的身份權(quán)限進(jìn)行認(rèn)證,防止非法請(qǐng)求API接口纺阔;
  • 網(wǎng)關(guān)可以實(shí)現(xiàn)監(jiān)控功能瘸彤,實(shí)時(shí)日志輸出,對(duì)請(qǐng)求進(jìn)行記錄笛钝;
  • 網(wǎng)關(guān)可以用來(lái)實(shí)現(xiàn)流量監(jiān)控质况,在高流量的情況下,對(duì)服務(wù)進(jìn)行降級(jí)玻靡;
  • API接口從內(nèi)部服務(wù)分離出來(lái)结榄,便于測(cè)試

二、請(qǐng)求路由

使用Spring Cloud Zuul實(shí)現(xiàn)路由的規(guī)則是十分簡(jiǎn)單的囤捻。路由方式包括兩種:傳統(tǒng)路由方式臼朗,面向服務(wù)的路由方式。

2.1 傳統(tǒng)路由

下面我們看以下配置:

zuul.routes.holiday.path=/holiday/**
zuul.routes.holiday.url=http://localhosst:8080/

該規(guī)則配置表示所有符合/holiday/** 規(guī)則的訪問(wèn)都會(huì)被路由轉(zhuǎn)發(fā)到http://localhosst:8080/地址上最蕾,例如:當(dāng)我們?cè)L問(wèn)http://localhost:5555/holiday/getAllDays,API網(wǎng)關(guān)就會(huì)將請(qǐng)求轉(zhuǎn)發(fā)到http://localhost:8080/holiday/getAllDays提供的微服務(wù)接口上依溯。其中holiday為微服務(wù)的名稱,可以任意定義瘟则,但是一組path和url映射關(guān)系的路由名稱必須相同黎炉,下面面向服務(wù)的路由方式也是如此。

2.2 面向服務(wù)的路由

Spring Cloud Zuul 與 Spring Cloud Eureka 可以實(shí)現(xiàn)無(wú)縫對(duì)接實(shí)現(xiàn)面向服務(wù)的路由醋拧。我們讓路由的path映射到具體的服務(wù)上慷嗜,而具體的url交由Eureka的服務(wù)發(fā)現(xiàn)機(jī)制去自動(dòng)維護(hù)。具體配置如下(其他配置參考下面的實(shí)戰(zhàn)):

zuul.routes.holiday.path=/holiday/**
zuul.routes.holiday.service-id=holiday

通過(guò)上面的配置丹壕,我們不需要維護(hù)具體實(shí)例的位置,是得維護(hù)工作十分簡(jiǎn)單庆械。另外,面向服務(wù)打的路由默認(rèn)實(shí)現(xiàn)了負(fù)載均衡菌赖,而傳統(tǒng)路由還需要手動(dòng)添加所有實(shí)例的位置缭乘。

三、路由規(guī)則

Spring Cloud Zuul提供了默認(rèn)的路由規(guī)則琉用,當(dāng)然我們也可以修改這個(gè)路由規(guī)則堕绩。

3.1 默認(rèn)路由規(guī)則

Zull與Eureka的配合使用后策幼,Zull會(huì)默認(rèn)配置一個(gè)路由規(guī)則,這些默認(rèn)規(guī)則的path會(huì)使用service-id配置的服務(wù)名作為請(qǐng)求的前綴奴紧。例如:有holiday服務(wù)特姐,他的默認(rèn)規(guī)則如下

zuul.routes.holiday.path=/holiday/**
zuul.routes.holiday.service-id=holiday

由于默認(rèn)情況下所有Eureka上的服務(wù)都會(huì)被Zuul自動(dòng)創(chuàng)建映射關(guān)系進(jìn)行路由,這會(huì)使得一些我們不希望對(duì)外開(kāi)放的服務(wù)也被外部訪問(wèn)到黍氮。這個(gè)時(shí)候我們可以配置zuul.ignored-services參數(shù)來(lái)設(shè)置一個(gè)服務(wù)名匹配表達(dá)式進(jìn)行判斷唐含,如果服務(wù)名匹配表達(dá)式,那么Zull將跳過(guò)這個(gè)服務(wù)沫浆,不為其創(chuàng)建路由規(guī)則捷枯。例如:zuul.ignored-services=*表示對(duì)所有的服務(wù)不自動(dòng)創(chuàng)建路由規(guī)則,這樣我們就需要為每個(gè)服配置路由規(guī)則件缸。

3.2 自定義路由規(guī)則

有這樣一個(gè)場(chǎng)景铜靶,由于業(yè)務(wù)的擴(kuò)展,版本的升級(jí)他炊,服務(wù)存在不同的版本争剿。比如我們有這樣的命名:holiday-v1、holiday-v2痊末,默認(rèn)情況下蚕苇,Zuul自動(dòng)為服務(wù)創(chuàng)建的路由表達(dá)式會(huì)采用服務(wù)名做前綴,針對(duì)holiday-v1就會(huì)產(chǎn)生/holiday-v1,/holiday-v2兩個(gè)路徑來(lái)做映射凿叠,但這樣生成的表達(dá)式規(guī)則較為單一涩笤,不利于路徑規(guī)則的管理。通常,對(duì)于上面這種情況盒件,我們希望是生成的路徑為/v1/holiday蹬碧,/v2/holiday。我們可以通過(guò)自定義路由規(guī)則來(lái)實(shí)現(xiàn)炒刁,具體代碼如下:

@Bean
public PatternServiceRouteMapper serviceRouteMapper(){
  return new PatternServiceRouteMapper(
    "(?<name>^.+)-(?<version>v.+$)",
    "${version}/${name}");
}

PatternServiceRouteMapper對(duì)象可以讓開(kāi)發(fā)者通過(guò)正則表達(dá)式來(lái)自定義服務(wù)于路由映射的生成關(guān)系恩沽。

四、Zuul的過(guò)濾器

Zull有請(qǐng)求過(guò)濾的功能,其過(guò)濾器可以在Http請(qǐng)求的發(fā)起和響應(yīng)返回期間執(zhí)行一系列的過(guò)濾器翔始。Zuul包擴(kuò)以下四種過(guò)濾器:

  • PRE: 該類(lèi)型的filters在Request routing到源web-service之前執(zhí)行罗心。可以進(jìn)行一些權(quán)限認(rèn)證城瞎,日志記錄渤闷,或者額外給Request增加一些屬性供后續(xù)的filter使用;
  • ROUTING:該類(lèi)型的filters用于把Request routing到源web-service,源web-service是實(shí)現(xiàn)業(yè)務(wù)邏輯的服務(wù)脖镀。這里使用HttpClient請(qǐng)求web-service;
  • POST:該類(lèi)型的filters在ROUTING返回Response后執(zhí)行飒箭。用來(lái)實(shí)現(xiàn)對(duì)Response結(jié)果進(jìn)行修改,收集統(tǒng)計(jì)數(shù)據(jù)以及把Response傳輸會(huì)客戶端;
  • ERROR:上面三個(gè)過(guò)程中任何一個(gè)出現(xiàn)錯(cuò)誤都交由ERROR類(lèi)型的filters進(jìn)行處理。

Zuul過(guò)濾器具有以下關(guān)鍵特性:

  • Type(類(lèi)型):Zuul過(guò)濾器的類(lèi)型补憾,這個(gè)類(lèi)型決定過(guò)濾器在哪個(gè)階段執(zhí)行漫萄,例如:pre,post等階段盈匾;
  • Execution Order(執(zhí)行順序):規(guī)定了過(guò)濾器的執(zhí)行順序,Order的值越小毕骡,越先執(zhí)行削饵;
  • Criteria(標(biāo)準(zhǔn)):Filters執(zhí)行所需條件
  • Action(行動(dòng)):如果符合執(zhí)行條件,則執(zhí)行Action(具體邏輯代碼)

示例如下:

public class MyFilter extends ZuulFilter {
    @Override
    public Object run() {
            RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        System.out.println(String.format("%s AccessUserNameFilter request to %s", request.getMethod(), request.getRequestURL().toString()));
        // 獲取請(qǐng)求的參數(shù)
        String username = request.getParameter("username");
        // 如果請(qǐng)求的參數(shù)不為空未巫,且值為chhliu時(shí)窿撬,則通過(guò)
        if(null != username && username.equals("chhliu")) {
          // 對(duì)該請(qǐng)求進(jìn)行路由
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
            // 設(shè)值,讓下一個(gè)Filter看到上一個(gè)Filter的狀態(tài)
            ctx.set("isSuccess", true);
            return null;
        }else{
          // 過(guò)濾該請(qǐng)求叙凡,不對(duì)其進(jìn)行路由
            ctx.setSendZuulResponse(false);
            // 返回錯(cuò)誤碼
            ctx.setResponseStatusCode(401);
            // 返回錯(cuò)誤內(nèi)容
            ctx.setResponseBody("{\"result\":\"username is not correct!\"}");
            ctx.set("isSuccess", false);
            return null;
        }
    }

    @Override
    public boolean shouldFilter() {
    // 是否執(zhí)行該過(guò)濾器劈伴,此處為true,說(shuō)明需要過(guò)濾
        return true;
    }

    @Override
    public int filterOrder() {
    // 優(yōu)先級(jí)為0握爷,數(shù)字越大跛璧,優(yōu)先級(jí)越低
        return 0;
    }

    @Override
    public String filterType() {
    // 前置過(guò)濾器
        return "pre";
    }
}

Zuul請(qǐng)求的生命周期如圖所示:


GitHub

五、設(shè)置熔斷

通常在服務(wù)無(wú)法提供服務(wù)的時(shí)候新啼,需要執(zhí)行熔斷追城。zuul上實(shí)現(xiàn)熔斷需要實(shí)現(xiàn)FallbackProvider的接口。實(shí)現(xiàn)接口中的兩個(gè)方法:getRoute()用于指定應(yīng)用在哪個(gè)服務(wù)上燥撞;fallbackResponse()進(jìn)入熔斷功能的執(zhí)行邏輯座柱。示例如下:

@Component
public class CustomZuulFallbackHandler implements FallbackProvider {
  private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);
  /**
   * 指定處理的service
   *
   * @return
   */
  @Override
  public String getRoute() {
    return "*";
  }

  public ClientHttpResponse fallbackResponse(String route) {
    return new ClientHttpResponse() {
      @Override
      public HttpStatus getStatusCode() throws IOException {
        return HttpStatus.OK;
      }

      @Override
      public int getRawStatusCode() throws IOException {
        return 200;
      }

      @Override
      public String getStatusText() throws IOException {
        return "OK";
      }

      @Override
      public void close() {

      }

      @Override
      public InputStream getBody() throws IOException {
        return new ByteArrayInputStream((route+" is unavailable.").getBytes());
      }

      @Override
      public HttpHeaders getHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return headers;
      }
    };
  }

  @Override
  public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
    if (cause != null) {
      String reason = cause.getMessage();
      logger.info("Excption {}",reason);
    }
    return fallbackResponse(route);
  }
}

六、實(shí)戰(zhàn)

6.1 pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.southgis.ibase.parent</groupId>
        <artifactId>parentWebService</artifactId>
        <version>2.0.1-SNAPSHOT</version>
        <relativePath>../../parent/parentWebService/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>api-gateway</artifactId>
    <groupId>com.southgis.ibase.systemassistance</groupId>
    <version>2.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <description>網(wǎng)關(guān)服務(wù)</description>

    <dependencies>
      <!--服務(wù)注冊(cè)與發(fā)現(xiàn)-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--配置中心-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-client</artifactId>
        </dependency>
        <!--路由網(wǎng)關(guān)-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <!--cas 客戶端-->
        <dependency>
            <groupId>org.jasig.cas.client</groupId>
            <artifactId>cas-client-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>apiGateway</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.southgis.ibase.systemassistance.ApiGatewayCustomApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

6.2 配置文件

  • bootstrap.properties

    #服務(wù)名 對(duì)應(yīng)配置文件中的{application}部分
    spring.application.name=apiGateway
    #對(duì)應(yīng)前配置文件中的{profile}部分
    spring.cloud.config.profile=dev2
    #配置訪問(wèn)路徑
    server.servlet.context-path=/eureka-server
    
    #注冊(cè)中心
    eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka-server/eureka
    
    #為監(jiān)控端點(diǎn) /info和/health端點(diǎn)也加上類(lèi)似的前綴
    management.server.servlet.context-path=/apiGateway
    eureka.instance.statusPageUrlPath=${management.server.servlet.context-path}/actuator/info
    eureka.instance.healthCheckUrlPath=${management.server.servlet.context-path}/actuator/health
    
    #通過(guò)服務(wù)連接配置中心
    #spring.cloud.config.discovery.enabled=true
    #spring.cloud.config.discovery.serviceId=config-server
    spring.cloud.config.uri = http://localhost:8080/config-server
    #配置文件獲取失敗快速返回
    spring.cloud.config.failFast=true
    
    #日志配置
    #logging.config=classpath:logback-spring.xml
    #logging.path=D:/ibase/logs/holiday
    #logging.pattern.console=[%d{yyyy-MM-dd HH:mm:ss}] -- [%-5p]: [%c] -- %m%n
    #logging.pattern.file=[%d{yyyy-MM-dd HH:mm:ss}] -- [%-5p]: [%c] -- %m%n
    
  • apiGateway-dev2.properties

    #訪問(wèn)端口
    server.port=8080
    #設(shè)置session超時(shí)時(shí)間為540分鐘
    server.servlet.session.timeout=PT540M
    
    #zuul默認(rèn)為所有服務(wù)開(kāi)啟默認(rèn)的路由物舒,為了服務(wù)安全色洞,此處關(guān)閉
    zuul.ignored-services=*
    #代碼字典服務(wù)路由
    zuul.routes.codedict.path=/codedict/**
    zuul.routes.codedict.service-id=codedict
    #是否轉(zhuǎn)發(fā)后還帶轉(zhuǎn)發(fā)特征的字符
    zuul.routes.codedict.strip-prefix=false
    #行政區(qū)劃服務(wù)路由
    zuul.routes.adminzone.path=/adminzone/**
    zuul.routes.adminzone.service-id=adminzone
    zuul.routes.adminzone.strip-prefix=false
    
    #是否開(kāi)啟路由重試
    zuul.retryable=true
    #對(duì)當(dāng)前服務(wù)的重試次數(shù)
    ribbon.MaxAutoRetries=2
    #切換實(shí)例的重試次數(shù)
    ribbon.MaxAutoRetriesNextServer=0
    #請(qǐng)求處理的超時(shí)時(shí)間
    ribbon.ReadTimeout=6000
    #請(qǐng)求連接的超時(shí)時(shí)間
    ribbon.ConnectTimeout=6000
    #對(duì)所有操作請(qǐng)求都進(jìn)行重試
    ribbon.OkToRetryOnAllOperations=true
    
    #將 hystrix 的超時(shí)時(shí)間設(shè)置成 5000 毫秒(hystrix超時(shí)時(shí)間小于ribbon連接超時(shí)時(shí)間,先走h(yuǎn)ystrix)
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
    

6.3 過(guò)濾器配置

@Configuration
public class ApiGatewayFilter extends ZuulFilter {
  @Override
  public String filterType() {
    return "pre";
  }

  @Override
  public int filterOrder() {
    return 0;
  }

  @Override
  public boolean shouldFilter() {
    return true;
  }

  @Override
  public Object run() throws ZuulException {
    RequestContext context = RequestContext.getCurrentContext();
    HttpServletRequest request = context.getRequest();
    Principal principal = request.getUserPrincipal();
    //獲取用戶的登錄id
    String userId = principal.getName();
    context.addZuulRequestHeader("X-AUTH-ID",userId);
    return null;
  }
}

在這里我們將獲取的登錄用戶id設(shè)置到了請(qǐng)求頭中傳遞給內(nèi)部服務(wù)冠胯,內(nèi)部服務(wù)可以通過(guò)下面的代碼進(jìn)行獲然鹬睢:

String user = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("X-AUTH-ID");

6.4 熔斷配置

@Component
public class CustomZuulFallbackHandler implements FallbackProvider {
  private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);
  /**
   * 指定處理的service
   *
   * @return
   */
  @Override
  public String getRoute() {
    return "*";
  }

  public ClientHttpResponse fallbackResponse(String route) {
    return new ClientHttpResponse() {
      @Override
      public HttpStatus getStatusCode() throws IOException {
        return HttpStatus.OK;
      }

      @Override
      public int getRawStatusCode() throws IOException {
        return 200;
      }

      @Override
      public String getStatusText() throws IOException {
        return "OK";
      }

      @Override
      public void close() {

      }

      @Override
      public InputStream getBody() throws IOException {
        return new ByteArrayInputStream((route+" is unavailable.").getBytes());
      }

      @Override
      public HttpHeaders getHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return headers;
      }
    };
  }

  @Override
  public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
    if (cause != null) {
      String reason = cause.getMessage();
      logger.info("Excption {}",reason);
    }
    return fallbackResponse(route);
  }
}

6.5 啟動(dòng)類(lèi)

**
 * 路由網(wǎng)關(guān)服務(wù)部署啟動(dòng)類(lèi)
 *
 * @author simon
 **/
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableZuulProxy
@EnableEurekaClient
@SpringCloudApplication
public class ApiGatewayMicroApplication {
  public static void main(String[] args) {
    SpringApplication.run(ApiGatewayMicroApplication.class, args);
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市涵叮,隨后出現(xiàn)的幾起案子惭蹂,更是在濱河造成了極大的恐慌,老刑警劉巖割粮,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盾碗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡舀瓢,警方通過(guò)查閱死者的電腦和手機(jī)廷雅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人航缀,你說(shuō)我怎么就攤上這事商架。” “怎么了芥玉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵蛇摸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我灿巧,道長(zhǎng)赶袄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任抠藕,我火速辦了婚禮饿肺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盾似。我一直安慰自己敬辣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布零院。 她就那樣靜靜地躺著溉跃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪门粪。 梳的紋絲不亂的頭發(fā)上喊积,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音玄妈,去河邊找鬼乾吻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛拟蜻,可吹牛的內(nèi)容都是我干的绎签。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼酝锅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诡必!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起搔扁,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤爸舒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后稿蹲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體扭勉,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年苛聘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涂炎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忠聚。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖唱捣,靈堂內(nèi)的尸體忽然破棺而出两蟀,到底是詐尸還是另有隱情,我是刑警寧澤震缭,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布赂毯,位于F島的核電站,受9級(jí)特大地震影響蛀序,放射性物質(zhì)發(fā)生泄漏欢瞪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一徐裸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧啸盏,春花似錦重贺、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至怯晕,卻和暖如春潜圃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背舟茶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工谭期, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吧凉。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓隧出,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親阀捅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胀瞪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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