Spring Cloud組件那么多超時(shí)設(shè)置,如何理解和運(yùn)用习贫?

前言

Spring Cloud 作為微服務(wù)解決方案 全家桶逛球,集合了豐富的微服務(wù)組件,如Gateway苫昌、Feign颤绕、Hystrix,RibbonOkHttp祟身、Eureka等等奥务。而作為服務(wù)調(diào)用環(huán)節(jié)涉及到的幾個(gè)組件:FeignHystrix,Ribbon袜硫、OkHttp 都有超時(shí)時(shí)間的設(shè)置氯葬,Spring Cloud 是如何優(yōu)雅地把它們協(xié)調(diào)好呢?本文將為你揭曉答案婉陷。

1. Spring Cloud 中發(fā)起一個(gè)接口調(diào)用帚称,經(jīng)過(guò)了哪些環(huán)節(jié)?

Spring Cloud 在接口調(diào)用上秽澳,大致會(huì)經(jīng)過(guò)如下幾個(gè)組件配合:
Feign -----> Hystrix --->Ribbon ---> Http Client(apache http components 或者 Okhttp)
具體交互流程上闯睹,如下圖所示:

Spring Cloud服務(wù)調(diào)用軌跡

  • 接口化請(qǐng)求調(diào)用
    當(dāng)調(diào)用被@FeignClient注解修飾的接口時(shí),在框架內(nèi)部肝集,會(huì)將請(qǐng)求轉(zhuǎn)換成Feign的請(qǐng)求實(shí)例feign.Request瞻坝,然后交由Feign框架處理蛛壳。
  • Feign :轉(zhuǎn)化請(qǐng)求
    至于Feign的詳細(xì)設(shè)計(jì)和實(shí)現(xiàn)原理杏瞻,在此不做詳細(xì)說(shuō)明。
    請(qǐng)參考我的另外一篇文章:Spring Cloud Feign 設(shè)計(jì)原理
  • Hystrix :熔斷處理機(jī)制
    Feign的調(diào)用關(guān)系衙荐,會(huì)被Hystrix代理攔截捞挥,對(duì)每一個(gè)Feign調(diào)用請(qǐng)求,Hystrix都會(huì)將其包裝成HystrixCommand,參與Hystrix的流控和熔斷規(guī)則忧吟。如果請(qǐng)求判斷需要熔斷砌函,則Hystrix直接熔斷,拋出異沉镒澹或者使用FallbackFactory返回熔斷Fallback結(jié)果讹俊;如果通過(guò),則將調(diào)用請(qǐng)求傳遞給Ribbon組件煌抒。
    關(guān)于Hystrix的工作原理仍劈,參考Spring Cloud Hystrix設(shè)計(jì)原理
  • Ribbon :服務(wù)地址選擇
    當(dāng)請(qǐng)求傳遞到Ribbon之后,Ribbon會(huì)根據(jù)自身維護(hù)的服務(wù)列表,根據(jù)服務(wù)的服務(wù)質(zhì)量寡壮,如平均響應(yīng)時(shí)間贩疙,Load等讹弯,結(jié)合特定的規(guī)則,從列表中挑選合適的服務(wù)實(shí)例这溅,選擇好機(jī)器之后组民,然后將機(jī)器實(shí)例的信息請(qǐng)求傳遞給Http Client客戶端,HttpClient客戶端來(lái)執(zhí)行真正的Http接口調(diào)用悲靴;
    關(guān)于Ribobn的工作原理臭胜,參考Spring Cloud Ribbon設(shè)計(jì)原理
  • HttpClient :Http客戶端,真正執(zhí)行Http調(diào)用
    根據(jù)上層Ribbon傳遞過(guò)來(lái)的請(qǐng)求癞尚,已經(jīng)指定了服務(wù)地址庇楞,則HttpClient開(kāi)始執(zhí)行真正的Http請(qǐng)求。
    關(guān)于HttpClient的其中一個(gè)實(shí)現(xiàn)OkHttp的工作原理否纬,請(qǐng)參考Spring Cloud OkHttp設(shè)計(jì)原理

2.每個(gè)組件階段的超時(shí)設(shè)置

如上一章節(jié)展示的調(diào)用關(guān)系吕晌,每個(gè)組件自己有獨(dú)立的接口調(diào)用超時(shí)設(shè)置參數(shù),下面將按照從上到下的順序梳理:

2.1 feign的默認(rèn)配置

feign 的配置可以采用feign.client.config.<feginName>....的格式為每個(gè)feign客戶端配置临燃,對(duì)于默認(rèn)值睛驳,可以使用feign.client.config.default..的方式進(jìn)行配置,該配置項(xiàng)在Spring Cloud中,使用FeignClientProperties類表示膜廊。

feign:
  client:
    config:
      <feignName>:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        retryer: com.example.SimpleRetryer
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        decode404: false
        encoder: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract

其中乏沸,關(guān)于feign的管理連接超時(shí)的配置項(xiàng):

## 網(wǎng)絡(luò)連接時(shí)間
feign.client.config.<clientname>.connectTimeout=
## 讀超時(shí)時(shí)間
feign.client.config.<clientname>.readTimeout=

2.2 Spring Cloud 加載feign配置項(xiàng)的原理:

  1. 檢查是否Feign是否制定了上述的配置項(xiàng),即是否有FeignClientProperties實(shí)例爪瓜;
  2. 如果有上述的配置項(xiàng)蹬跃,則表明Feign是通過(guò)properties初始化的,即configureUsingProperties;
  3. 根據(jù)配置項(xiàng)feign.client.defaultToProperties的結(jié)果,使用不同的配置覆蓋策略铆铆。

feign初始化的過(guò)程蝶缀,其實(shí)就是構(gòu)造Feign.Builder的過(guò)程,如下圖所示:

2019-05-29_134522.png

相關(guān)代碼實(shí)現(xiàn)如下:

protected void configureFeign(FeignContext context, Feign.Builder builder) {
        FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
        if (properties != null) {
            if (properties.isDefaultToProperties()) {
                configureUsingConfiguration(context, builder);
                configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                configureUsingProperties(properties.getConfig().get(this.name), builder);
            } else {
                configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                configureUsingProperties(properties.getConfig().get(this.name), builder);
                configureUsingConfiguration(context, builder);
            }
        } else {
            configureUsingConfiguration(context, builder);
        }
    }

2.3.場(chǎng)景分析

結(jié)合上述的加載原理薄货,初始化過(guò)程可以分為如下幾種場(chǎng)景:

  • 場(chǎng)景1:沒(méi)有通過(guò)配置文件配置
    在這種模式下翁都,將使用configureUsingConfiguration,此時(shí)將會(huì)使用Spring 運(yùn)行時(shí)自動(dòng)注入的Bean完成配置:
    protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
        Logger.Level level = getOptional(context, Logger.Level.class);
        if (level != null) {
            builder.logLevel(level);
        }
        Retryer retryer = getOptional(context, Retryer.class);
        if (retryer != null) {
            builder.retryer(retryer);
        }
        ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
        if (errorDecoder != null) {
            builder.errorDecoder(errorDecoder);
        }
        Request.Options options = getOptional(context, Request.Options.class);
        if (options != null) {
            builder.options(options);
        }
        Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
                this.name, RequestInterceptor.class);
        if (requestInterceptors != null) {
            builder.requestInterceptors(requestInterceptors.values());
        }

        if (decode404) {
            builder.decode404();
        }
    }

默認(rèn)情況下,Spring Cloud對(duì)此超時(shí)時(shí)間的設(shè)置為:

connectTimeoutMillis = 10 * 1000
readTimeoutMillis = 60 * 1000
  • 場(chǎng)景2:配置了FeignClientProperties,并且配置了feign.client.defaultToProperties = true,此時(shí)的這種場(chǎng)景谅猾,其配置覆蓋順序如下所示:
    configureUsingConfiguration---> configurationUsingPropeties("default")----> configurationUsingProperties("<client-name>")
    如下圖配置所示柄慰,最終超時(shí)時(shí)間為:connectionTimeout=4000,readTimeout=4000
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
      <client-name>:
        connectTimeout: 4000
        readTimeout: 4000
  • 場(chǎng)景3:配置了FeignClientProperties,并且配置了feign.client.defaultToProperties = false,此時(shí)的這種場(chǎng)景,配置覆蓋順序是:
    configurationUsingPropeties("default")----> configurationUsingProperties("<client-name>")---> configureUsingConfiguration
    如果按照這種策略税娜,則最終的超時(shí)時(shí)間設(shè)置就為connectionTimeout=10000,readTimeout=6000

Feign的超時(shí)時(shí)間的意義:
feign 作為最前端暴露給用戶使用的坐搔,一般其超時(shí)設(shè)置相當(dāng)于對(duì)用戶的一個(gè)承諾,所以Spring在處理這一塊的時(shí)候敬矩,會(huì)有意識(shí)地使用feign的超時(shí)時(shí)間來(lái)設(shè)置后面的ribbonhttp client組件概行。
需要注意的是:hystrix的超時(shí)處理和feign之間在當(dāng)前的Spring Cloud框架規(guī)劃中,并沒(méi)有相關(guān)關(guān)系谤绳。


2.2 Hystrix的超時(shí)設(shè)置

Hystrix的超時(shí)設(shè)置占锯,在于命令執(zhí)行的時(shí)間袒哥,一般而言,這個(gè)時(shí)間要稍微比Feign的超時(shí)時(shí)間稍微長(zhǎng)些,因?yàn)镃ommand除了請(qǐng)求調(diào)用之外消略,還有一些業(yè)務(wù)代碼消耗堡称。hystrix的配置規(guī)則和feign的風(fēng)格比較類似:hystrix.command.<service-name>

hystrix.command.default.execution.isolation.strategy = THREAD
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 10000
hystrix.command.default.execution.timeout.enabled = true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout = true
hystrix.command.default.execution.isolation.thread.interruptOnFutureCancel = false

Hystrix超時(shí)時(shí)間存在的意義
Hystrix的超時(shí)時(shí)間是站在命令執(zhí)行時(shí)間來(lái)看的,和Feign設(shè)置的超時(shí)時(shí)間在設(shè)置上并沒(méi)有關(guān)聯(lián)關(guān)系艺演。Hystrix不僅僅可以封裝Http調(diào)用却紧,還可以封裝任意的代碼執(zhí)行片段。Hystrix是從命令對(duì)象的角度去定義胎撤,某個(gè)命令執(zhí)行的超時(shí)時(shí)間晓殊,超過(guò)此此時(shí)間,命令將會(huì)直接熔斷伤提。
假設(shè)hystrix 的默認(rèn)超時(shí)時(shí)間設(shè)置了10000,即10秒巫俺,而feign 設(shè)置的是20秒,那么Hystrix會(huì)在10秒到來(lái)是直接熔斷返回肿男,不會(huì)等到feign的20秒執(zhí)行結(jié)束介汹,也不會(huì)中斷尚未執(zhí)行完的feign調(diào)用。


2.3 Ribbon 的超時(shí)時(shí)間

Ribbon的超時(shí)時(shí)間可以通過(guò)如下配置項(xiàng)指定舶沛,默認(rèn)情況下嘹承,這兩項(xiàng)的值和feign的配置保持一致:

<service-name>.ribbon.ConnectTimeout= <feign-default: 10000>
<service-name>.ribbon.ReadTimeout= <feign-default:6000>

其核心代碼邏輯如下:

    IClientConfig getClientConfig(Request.Options options /*feign配置項(xiàng)*/, String clientName) {
        IClientConfig requestConfig;
        if (options == DEFAULT_OPTIONS) {
            requestConfig = this.clientFactory.getClientConfig(clientName);
        } else {
            requestConfig = new FeignOptionsClientConfig(options);
        }
        return requestConfig;
    }
       static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
                //將Feign的配置設(shè)置為Ribbon的`IClientConfig`中
        public FeignOptionsClientConfig(Request.Options options) {
            setProperty(CommonClientConfigKey.ConnectTimeout,
                    options.connectTimeoutMillis());
            setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
        }

        @Override
        public void loadProperties(String clientName) {

        }

        @Override
        public void loadDefaultValues() {

        }

    }

Ribbon超時(shí)時(shí)間存在的意義
Ribbon的超時(shí)時(shí)間通過(guò)Feign配置項(xiàng)加載,構(gòu)造其Ribbon客戶端表示:IClientConfig,實(shí)際上該超時(shí)時(shí)間并沒(méi)有實(shí)際使用的場(chǎng)景,僅僅作為配置項(xiàng)如庭。
由上面的原則可以看出叹卷,當(dāng)feign設(shè)置了超時(shí)時(shí)間,Ribbon會(huì)依據(jù)feign的設(shè)置同步坪它。Ribbon的這個(gè)超時(shí)時(shí)間骤竹,用于指導(dǎo)真正調(diào)用接口時(shí),設(shè)置真正實(shí)現(xiàn)者的超時(shí)時(shí)間哟楷。

在沒(méi)有Feign的環(huán)境下瘤载,Ribbon·和·Http Client客戶端的關(guān)系
RibbonFeign是相對(duì)獨(dú)立的組件,在一個(gè)Spring Cloud框架運(yùn)行環(huán)境中卖擅,可以沒(méi)有Feign。那么墨技,在這種場(chǎng)景下惩阶,假設(shè)Http Client客戶端使用的是OKHttp,并且通過(guò)ribbon.okhttp.enabled 指定ribbon調(diào)用時(shí)扣汪,會(huì)使用ribbon的超時(shí)配置來(lái)初始化OkHttp.代碼如下所示:

@Configuration
@ConditionalOnProperty("ribbon.okhttp.enabled")
@ConditionalOnClass(name = "okhttp3.OkHttpClient")
public class OkHttpRibbonConfiguration {
   @RibbonClientName
   private String name = "client";

   @Configuration
   protected static class OkHttpClientConfiguration {
       private OkHttpClient httpClient;

       @Bean
       @ConditionalOnMissingBean(ConnectionPool.class)
       public ConnectionPool httpClientConnectionPool(IClientConfig config,
                                                      OkHttpClientConnectionPoolFactory connectionPoolFactory) {
                       
           RibbonProperties ribbon = RibbonProperties.from(config);
           int maxTotalConnections = ribbon.maxTotalConnections();
           long timeToLive = ribbon.poolKeepAliveTime();
           TimeUnit ttlUnit = ribbon.getPoolKeepAliveTimeUnits();
           return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
       }

       @Bean
       @ConditionalOnMissingBean(OkHttpClient.class)
       public OkHttpClient client(OkHttpClientFactory httpClientFactory,
                                  ConnectionPool connectionPool, IClientConfig config) {
           RibbonProperties ribbon = RibbonProperties.from(config);
           this.httpClient = httpClientFactory.createBuilder(false)
                       //使用Ribbon的超時(shí)時(shí)間來(lái)初始化OKHttp的 
                   .connectTimeout(ribbon.connectTimeout(), TimeUnit.MILLISECONDS)
                   .readTimeout(ribbon.readTimeout(), TimeUnit.MILLISECONDS)
                   .followRedirects(ribbon.isFollowRedirects())
                   .connectionPool(connectionPool)
                   .build();
           return this.httpClient;
       }

       @PreDestroy
       public void destroy() {
           if(httpClient != null) {
               httpClient.dispatcher().executorService().shutdown();
               httpClient.connectionPool().evictAll();
           }
       }
   }

2.4 Http Client的超時(shí)時(shí)間

為了保證整個(gè)組件調(diào)用鏈的超時(shí)關(guān)系断楷,一般Spring Cloud采取的策略是:依賴方的超時(shí)配置覆蓋被依賴方的配置
當(dāng)然這個(gè)也不是絕對(duì)的,實(shí)際上對(duì)于Feign而言崭别,可以直接指定FeignHttpClient之間的配置關(guān)系冬筒,如下所示:

@ConfigurationProperties(prefix = "feign.httpclient")
public class FeignHttpClientProperties {
    public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false;
    public static final int DEFAULT_MAX_CONNECTIONS = 200;
    public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50;
    public static final long DEFAULT_TIME_TO_LIVE = 900L;
    public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS;
    public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;
    public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;
    public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000;

    private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION;
        //連接池最大連接數(shù)恐锣,默認(rèn)200
    private int maxConnections = DEFAULT_MAX_CONNECTIONS;
        //每一個(gè)IP最大占用多少連接 默認(rèn) 50
    private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
        //連接池中存活時(shí)間,默認(rèn)為5
    private long timeToLive = DEFAULT_TIME_TO_LIVE;
        //連接池中存活時(shí)間單位舞痰,默認(rèn)為秒
    private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT;
        //http請(qǐng)求是否允許重定向
    private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS;
        //默認(rèn)連接超時(shí)時(shí)間:2000毫秒
    private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
        //連接池管理定時(shí)器執(zhí)行頻率:默認(rèn) 3000毫秒
    private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT;

}

Http Client的實(shí)現(xiàn)OkHttp為例土榴,如果指定了feign.okhttp.enabled,則會(huì)初始化Okhttp,其中,OkHttp的超時(shí)時(shí)間設(shè)置為:feign.httpclient.connectionTimeout,默認(rèn)值為2000毫秒

@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled")
class OkHttpFeignLoadBalancedConfiguration {

    @Configuration
    @ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
    protected static class OkHttpFeignConfiguration {
        private okhttp3.OkHttpClient okHttpClient;

        @Bean
        @ConditionalOnMissingBean(ConnectionPool.class)
        public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
                                                       OkHttpClientConnectionPoolFactory connectionPoolFactory) {
            Integer maxTotalConnections = httpClientProperties.getMaxConnections();
            Long timeToLive = httpClientProperties.getTimeToLive();
            TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
            return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
        }

        @Bean
        public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
                                           ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
            Boolean followRedirects = httpClientProperties.isFollowRedirects();
            Integer connectTimeout = httpClientProperties.getConnectionTimeout();
            this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()).
                    connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
                    followRedirects(followRedirects).
                    connectionPool(connectionPool).build();
            return this.okHttpClient;
        }

        @PreDestroy
        public void destroy() {
            if(okHttpClient != null) {
                okHttpClient.dispatcher().executorService().shutdown();
                okHttpClient.connectionPool().evictAll();
            }
        }
    }

    @Bean
    @ConditionalOnMissingBean(Client.class)
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                              SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
        OkHttpClient delegate = new OkHttpClient(okHttpClient);
        return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
    }
}

3. 最佳實(shí)踐

有的同學(xué)可能覺(jué)得Spring Cloud 使用起來(lái)很方便,只需要引入一些組件即可响牛。實(shí)際上玷禽,這正是Spring Cloud的坑所在的地方:因?yàn)樗銐蜢`活,組件組裝非常便捷呀打,但是組件太多時(shí)矢赁,必須要有一個(gè)清晰的脈絡(luò)去理清其間的關(guān)系
在整個(gè)組件配置組裝的過(guò)程贬丛,超時(shí)設(shè)置遵循的基本原則是:依賴方的超時(shí)配置覆蓋被依賴方的配置撩银,而其配置覆蓋的形式,則是使用的Spring Boot 的 AutoConfiguration 機(jī)制實(shí)現(xiàn)的豺憔。

綜上所述蜒蕾,一般在Spring Cloud設(shè)置過(guò)程中,

  • 只需要指定Feign使用什么Http Client客戶端即可焕阿,比如feign.okhttp.enabled=true
  • Feign客戶端的Http Client的配置項(xiàng)咪啡,統(tǒng)一使用如下配置即可,Spring Cloud會(huì)拿才配置項(xiàng)初始化不同的Http Client客戶端的暮屡。
### http client最大連接數(shù)撤摸,默認(rèn)200
feign.httpclient.maxConnections = 200
### 每個(gè)IP路由最大連接數(shù)量
feign.httpclient.maxConnectionsPerRoute= 50
### 連接存活時(shí)間
feign.httpclient.timeToLive = 900
### 連接存活時(shí)間單位
feign.httpclient.timeToLiveUnit = SECONDS
### 連接超時(shí)時(shí)間
feign.httpclient.connectionTimeout = 2000
### 連接超時(shí)定時(shí)器的執(zhí)行頻率
fein.httpclient.connectionTimeout=3000
  • Hystrix的作用:Feign或者Http Client 只能規(guī)定所有接口調(diào)用的超時(shí)限制,而Hystrix可以設(shè)置到每一個(gè)接口的超時(shí)時(shí)間褒纲,控制力度最細(xì)准夷,相對(duì)應(yīng)地,配置會(huì)更繁瑣莺掠。

Hystrix的超時(shí)時(shí)間和Feign或者Http Client的超時(shí)時(shí)間關(guān)系
Hystrix的超時(shí)意義是從代碼執(zhí)行時(shí)間層面控制超時(shí)衫嵌;而FeignHttp Client 則是通過(guò)Http底層TCP/IP的偏網(wǎng)絡(luò)層層面控制的超時(shí)。
我的建議是:一般情況下彻秆,Hystrix 的超時(shí)時(shí)間要大于FeignHttp Client的超時(shí)時(shí)間楔绞;而對(duì)于特殊需求的接口調(diào)用上,為了避免等待時(shí)間太長(zhǎng)唇兑,需要將對(duì)應(yīng)的Hystrix command 超時(shí)時(shí)間配置的偏小一點(diǎn)酒朵,滿足業(yè)務(wù)側(cè)的要求。


以上是個(gè)人對(duì)Spring Cloud使用過(guò)程中扎附,對(duì)超時(shí)時(shí)間的理解蔫耽,如果不同的見(jiàn)解和看法,請(qǐng)不吝指出留夜,相互學(xué)習(xí)進(jìn)步匙铡。

作者聲明图甜,如需轉(zhuǎn)載,請(qǐng)注明出處鳖眼,亦山札記 http://www.reibang.com/u/802bbe244ebb


亦山札記黑毅,聚焦微服務(wù)、中間件
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末具帮,一起剝皮案震驚了整個(gè)濱河市博肋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜂厅,老刑警劉巖匪凡,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異掘猿,居然都是意外死亡病游,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)稠通,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)衬衬,“玉大人,你說(shuō)我怎么就攤上這事改橘∽涛荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵飞主,是天一觀的道長(zhǎng)狮惜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)碌识,這世上最難降的妖魔是什么碾篡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮筏餐,結(jié)果婚禮上开泽,老公的妹妹穿的比我還像新娘。我一直安慰自己魁瞪,他們只是感情好穆律,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著佩番,像睡著了一般众旗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上趟畏,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音滩租,去河邊找鬼赋秀。 笑死利朵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的猎莲。 我是一名探鬼主播绍弟,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼著洼!你這毒婦竟也來(lái)了樟遣?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤身笤,失蹤者是張志新(化名)和其女友劉穎豹悬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體液荸,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞻佛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了娇钱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伤柄。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖文搂,靈堂內(nèi)的尸體忽然破棺而出适刀,到底是詐尸還是另有隱情,我是刑警寧澤煤蹭,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布笔喉,位于F島的核電站,受9級(jí)特大地震影響疯兼,放射性物質(zhì)發(fā)生泄漏然遏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一吧彪、第九天 我趴在偏房一處隱蔽的房頂上張望待侵。 院中可真熱鬧,春花似錦姨裸、人聲如沸秧倾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)那先。三九已至,卻和暖如春赡艰,著一層夾襖步出監(jiān)牢的瞬間售淡,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留揖闸,地道東北人揍堕。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像汤纸,于是被迫代替她去往敵國(guó)和親衩茸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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