Spring Cloud Commons 之 loadbalancer 源碼筆記

不會(huì)真的有人看吧? 不會(huì)真的有人點(diǎn)贊吧? 不會(huì)吧? 不會(huì)吧!!!

Spring Cloud Commons 是什么樣的? 有什么作用? 如何與 Spring Cloud 和 Cloud Alibaba 整合?
讓我們帶著這些問(wèn)題去研究源碼吧!

loadbalancer 原理分析

# 先來(lái)認(rèn)識(shí)一下 Spring Cloud Commons 吧
是定義了諸多接口(如ServiceRegistry/DiscoveryClient/LoadBalancerClient)和注解(如!EnableDiscoveryClient/@LoadBalanced)為主, 少量代碼實(shí)現(xiàn)(如 RandomLoadBalancer). 
以及對(duì) Spring 容器(Context) 的擴(kuò)展(如 NamedContextFactory, bootstrap 配置文件的加載, 容器重啟, 容器跟隨配置文件刷新等等)
當(dāng)然還有一些打包好的 starter.
我們要研究的 loadbalancer 就是其中一個(gè)子項(xiàng)目.

loadbalancer 關(guān)鍵類解析

# 0.驚! 一堆類僅為添加一個(gè)攔截器
LoadBalancerRequestFactory: 一個(gè)工廠, 包裝一個(gè)為請(qǐng)求對(duì)象 HttpRequest 加料的回調(diào) LoadBalancerRequest

LoadBalancerClient: 用于根據(jù) serviceId 選取一個(gè) ServiceInstance, 執(zhí)行從 LoadBalancerRequestFactory 獲得的那個(gè)回調(diào)

LoadBalancerInterceptor: restTemplate 的攔截器, 攔截后調(diào)用 LoadBalancerClient 修改 HttpRequest 對(duì)象(主要是 url), 且傳入調(diào)用 LoadBalancerRequestFactory 生成的回調(diào)給 LoadBalancerClient

RestTemplateCustomizer: 為 restTemplate 加上一個(gè)攔截器(也可以干點(diǎn)別的, 默認(rèn)就這一個(gè)用處)

SmartInitializingSingleton: 調(diào)用 RestTemplateCustomizer 為容器中所有加了 @LoadBalanced 的 RestTemplate 加上一個(gè)攔截器

# 1.獲取對(duì)象的工廠, 以 Spring 容器作為載體管理對(duì)象.
NamedContextFactory
    繼承 DisposableBean, 用于類銷毀是執(zhí)行點(diǎn)東西(指創(chuàng)建的好多個(gè)子容器)
    繼承 ApplicationContextAware, 用于將子容器和當(dāng)前容器關(guān)聯(lián)起來(lái)(所以 Spring 樹形擴(kuò)展這個(gè)設(shè)計(jì)真不錯(cuò))
    泛型 C extends NamedContextFactory.Specification, 無(wú)它, 就是個(gè) POJO, 存?zhèn)€ name 和對(duì)應(yīng)的配置 class, 用于初始化容器的(會(huì)被注冊(cè)進(jìn)去, 然后解析里面的注解啥的...)
    此類作用就是管理一大堆(取決于你微服務(wù)拆分的程度)子容器, 獲取其他代碼需要的類型對(duì)象


ReactiveLoadBalancer.Factory
  定義了獲取 ReactiveLoadBalancer 的接口以及與其相關(guān)的擴(kuò)展
  

LoadBalancerClientFactory
  繼承 NamedContextFactory, 構(gòu)造參數(shù)指定了幾個(gè)屬性值
  實(shí)現(xiàn)了 ReactiveLoadBalancer.Factory 的接口, 即提供獲取 ReactiveLoadBalancer 的方法.
  泛型具體為 LoadBalancerClientSpecification, 還是個(gè)POJO
  

# 2.包含算法邏輯的負(fù)載均衡策略的類
Response
    server 的封裝類, 一般持有一個(gè) ServiceInstance 對(duì)象, 如 DefaultResponse
    
Publisher
    響應(yīng)式編程的東西, 可獲取 Response<T> 對(duì)象, 一般為 Response<ServiceInstance>
    
ReactiveLoadBalancer.Factory
    定義了獲取 ReactiveLoadBalancer 的接口以及與其相關(guān)的擴(kuò)展

ReactiveLoadBalancer
    定義了 choose 方法, 即如何選取一個(gè) ServiceInstance, 如輪播, 隨機(jī)...

ReactorLoadBalancer
  定義了 choose 方法的另一形式, 僅返回值不同, 為 Mono<Response<T>> 是 Publisher<Response<T>> 的子類, 返回值為抽象類.

ReactorServiceInstanceLoadBalancer
  繼承 ReactorLoadBalancer
    僅僅作為一個(gè)標(biāo)記類, 無(wú)新接口
    

類大致調(diào)用圖

graph TB

A(SmartInitializingSingleton)
A1(RestTemplateCustomizer)
A2(LoadBalancerInterceptor)
A3(restTemplate)
A4(LoadBalancerClient)
A5(LoadBalancerRequestFactory)
A6(ReactorServiceInstanceLoadBalancer)
A8(LoadBalancerClientFactory)

A--調(diào)用-->A1
A1--添加一個(gè)-->A2
A2--到-->A3
A2--調(diào)用-->A4
A2--調(diào)用-->A5
A5--生成一個(gè)回調(diào)給-->A4 
A4--調(diào)用-->A8
A8--獲取-->A6
A6--獲取-->A7(ServiceInstance)


a copy

瞅瞅有哪些負(fù)載均衡策略吧(看完發(fā)現(xiàn)這才是最簡(jiǎn)單的... 外面那些結(jié)構(gòu)反而不容易理清)

// 1.RandomLoadBalancer: 就是隨機(jī)數(shù)唄, 0-size, 簡(jiǎn)單!!
// 在 RandomLoadBalancer#getInstanceResponse() 中
// 覺(jué)得這個(gè)方法可以做出 protected, 這樣有些實(shí)現(xiàn)只需要重寫這個(gè)方法就行了 
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
  if (instances.isEmpty()) {
    if (log.isWarnEnabled()) {
      log.warn("No servers available for service: " + serviceId);
    }
    return new EmptyResponse();
  }
  int index = ThreadLocalRandom.current().nextInt(instances.size());

  ServiceInstance instance = instances.get(index);

  return new DefaultResponse(instance);
}

// 2.RoundRobinLoadBalancer
// 在 RoundRobinLoadBalancer#getInstanceResponse() 中
// 用一個(gè) position 保存位置, 這個(gè)主意高啊, 即保證了數(shù)據(jù)的正確性, 還.... 編不下去了!!
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
  if (instances.isEmpty()) {
    if (log.isWarnEnabled()) {
      log.warn("No servers available for service: " + serviceId);
    }
    return new EmptyResponse();
  }
  // TODO: enforce order?
  int pos = Math.abs(this.position.incrementAndGet());

  ServiceInstance instance = instances.get(pos % instances.size());

  return new DefaultResponse(instance);
}

這代碼簡(jiǎn)單的, 特別不想分析.... 但其實(shí)最開始就是沖著這個(gè)來(lái)的... 總得看看吧, 咳咳!!

loadbalancer 原理分析

  1. 先攔截 RestTemplate 對(duì)象的請(qǐng)求, 使其調(diào)用 LoadBalancerClient 的接口獲取真實(shí)IP
# org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration 中
1.@Bean 加入一個(gè) LoadBalancerRequestFactory, 并且?guī)в杏脩糇远x的 transformers(作用: 對(duì)選取真實(shí) url 后的請(qǐng)求對(duì)象進(jìn)行干預(yù))
2.@Bean 加入一個(gè) LoadBalancerClient, 其作用是, 根據(jù) serviceId 獲取/選取真實(shí) url, 以及執(zhí)行請(qǐng)求
3.@Bean 加入一個(gè) LoadBalancerInterceptor, 即核心攔截器. 邏輯是: 獲取 host, 調(diào)用 LoadBalancerRequestFactory 生成請(qǐng)求, 用 LoadBalancerClient 執(zhí)行.
4.@Bean 加入一個(gè) RestTemplateCustomizer, 其作用是: 為給定的 RestTemplate 添加一個(gè) LoadBalancerInterceptor.
5.@Bean 加入一個(gè) SmartInitializingSingleton, 作用是單例都加載后觸發(fā)回調(diào), 回調(diào)代碼為:
     遍歷所有的 RestTemplateCustomizer 和 restTemplates, 用 RestTemplateCustomizer 對(duì) RestTemplate 做設(shè)置. 包括(4)剛剛加入的那個(gè).

總結(jié): 為用戶自定義(如配置類中寫了個(gè)@Bean + return new RestTemplate() 這種形式)的 RestTemplate 添加一個(gè)攔截器, 在請(qǐng)求執(zhí)行前進(jìn)行攔截, 然后將請(qǐng)求數(shù)據(jù)的 host 作為 serviceId, 接著使用某個(gè)具體的 LoadBalancerClient 實(shí)現(xiàn)類調(diào)用其方法獲取真實(shí)的 url. 若對(duì)應(yīng)存在多個(gè) url, 由其算法策略決定如何選擇.

  1. 再看 LoadBalancerClient 的默認(rèn)實(shí)現(xiàn)類(在 BlockingLoadBalancerClientAutoConfiguration 中配置的), 其邏輯是, 通過(guò)工廠獲取 ReactorServiceInstanceLoadBalancer 對(duì)象并調(diào)用其接口執(zhí)行負(fù)載均衡算法.
// org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient
// 先進(jìn)入這個(gè)方法, 然后會(huì)調(diào)用第二個(gè)方法.
// 這兩個(gè)方法其實(shí)就是從工廠獲取對(duì)象執(zhí)行 choose 后再讓其完成請(qǐng)求的執(zhí)行, 大部分代碼都是 LoadBalancerLifecycle 的觸發(fā).
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
  // 遍歷 LoadBalancerLifecycle 觸發(fā) onStart 鉤子
  // 調(diào)用 choose 方法選取一個(gè) IP:PORT 得到包裝類 ServiceInstance
  // 遍歷 LoadBalancerLifecycle 觸發(fā) onComplete 鉤子
  // 執(zhí)行請(qǐng)求
  String hint = getHint(serviceId);
  LoadBalancerRequestAdapter<T, DefaultRequestContext> lbRequest = new LoadBalancerRequestAdapter<>(request,
                                                                                                    new DefaultRequestContext(request, hint));
  Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
    .getSupportedLifecycleProcessors(
    loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
    DefaultRequestContext.class, Object.class, ServiceInstance.class);
  supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));

  // 從 serviceId 對(duì)應(yīng)的容器中獲取一個(gè)負(fù)載均衡算法實(shí)現(xiàn)類對(duì)象, 即 ReactorServiceInstanceLoadBalancer.
  // 調(diào)用其 choose 方法. 從響應(yīng)中獲取 ServiceInstance 并返回.
  ServiceInstance serviceInstance = choose(serviceId, lbRequest);
  if (serviceInstance == null) {
    supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
      new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, new EmptyResponse())));
    throw new IllegalStateException("No instances available for " + serviceId);
  }
  // 可以執(zhí)行了
  return execute(serviceId, serviceInstance, lbRequest);
}

@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request)
  throws IOException {
  // 遍歷 LoadBalancerLifecycle 觸發(fā) onStartRequest 鉤子
  // 調(diào)用 request.apply 方法執(zhí)行請(qǐng)求(即進(jìn)入之前 LoadBalancerRequestFactory 中的代碼)
  // 遍歷 LoadBalancerLifecycle 觸發(fā) onComplete 鉤子

  DefaultResponse defaultResponse = new DefaultResponse(serviceInstance);
  Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
    .getSupportedLifecycleProcessors(
    loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
    DefaultRequestContext.class, Object.class, ServiceInstance.class);
  Request lbRequest = request instanceof Request ? (Request) request : new DefaultRequest<>();
  supportedLifecycleProcessors
    .forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, new DefaultResponse(serviceInstance)));
  try {
    // 請(qǐng)求調(diào)用前先使用 transformers 對(duì)原始請(qǐng)求對(duì)象進(jìn)行一些改變處理后再執(zhí)行請(qǐng)求
    T response = request.apply(serviceInstance);
    Object clientResponse = getClientResponse(response);
    supportedLifecycleProcessors
      .forEach(lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
                                                                         lbRequest, defaultResponse, clientResponse)));
    return response;
  }
  catch (IOException iOException) {
    supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
      new CompletionContext<>(CompletionContext.Status.FAILED, iOException, lbRequest, defaultResponse)));
    throw iOException;
  }
  catch (Exception exception) {
    supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
      new CompletionContext<>(CompletionContext.Status.FAILED, exception, lbRequest, defaultResponse)));
    ReflectionUtils.rethrowRuntimeException(exception);
  }
  return null;
}
  1. 所以再看看工廠是怎么獲取和存放對(duì)象的, 關(guān)鍵類: LoadBalancerClientFactory, 其繼承自 NamedContextFactory
// 先看其如何獲取對(duì)象的 LoadBalancerClientFactory#getInstance()
@Override
public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
  // 從 serviceId 對(duì)應(yīng)的容器中獲取一個(gè)負(fù)載均衡算法實(shí)現(xiàn)類對(duì)象, 即 ReactorServiceInstanceLoadBalancer.
  return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
}
// getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class):
public <T> T getInstance(String name, Class<T> type) {
  AnnotationConfigApplicationContext context = getContext(name);
  try {
    return context.getBean(type);
  }
  catch (NoSuchBeanDefinitionException e) {
    // ignore
  }
  return null;
}
// getContext(name):
protected AnnotationConfigApplicationContext getContext(String name) {
  if (!this.contexts.containsKey(name)) {
    synchronized (this.contexts) {
      if (!this.contexts.containsKey(name)) {
        // 結(jié)論: 容器里有點(diǎn)東西, 但不多...  主要是于父容器打通... 所以又啥都有了.
        this.contexts.put(name, createContext(name));
      }
    }
  }
  return this.contexts.get(name);
}

// createContext(name):
protected AnnotationConfigApplicationContext createContext(String name) {
  // 0.結(jié)合實(shí)現(xiàn)類 LoadBalancerClientFactory 做出如下注釋
  // 1.將 LoadBalancerAutoConfiguration 掃描到 configurations 注冊(cè)到 name 對(duì)應(yīng)的容器中.
  //     這里的 name 其實(shí)就是 serviceId, 也就是說(shuō), 若我們想給某個(gè)容器加入一些東西, 則實(shí)現(xiàn) LoadBalancerClientSpecification 時(shí), name 需要與 serviceId 對(duì)應(yīng)起來(lái)(相同)
  // 2.當(dāng)我上面那句沒(méi)說(shuō)啊... 原來(lái) name 為 default. 開頭是可以加入任意 serviceId 對(duì)應(yīng)的容器的.........................(qiao)
  // 3.為容器加入一個(gè)占位符解析器, 和一個(gè) defaultConfigType(=LoadBalancerClientConfiguration.class, 作用配置一些 bean)
  //     LoadBalancerClientConfiguration 會(huì)加入一個(gè) RoundRobinLoadBalancer, 看來(lái)就是默認(rèn)的負(fù)載均衡類了.
  // 4.默認(rèn)為加了一個(gè)名為 loadbalancer 的 PropertySource, 里面有一個(gè) loadbalancer.client.name=serviceId 的配置....
  // 5.設(shè)定父容器, 父容器通過(guò) ApplicationContextAware 獲得, 這樣剛才那么辛苦的注冊(cè)方式, 就僅適合于特性, 而非通用了.
  // 6.設(shè)置名稱(啥意義呢?), 然后調(diào)用容器的 refresh() 完成容器加載


  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  if (this.configurations.containsKey(name)) {
    for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
      context.register(configuration);
    }
  }
  for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
    if (entry.getKey().startsWith("default.")) {
      for (Class<?> configuration : entry.getValue().getConfiguration()) {
        context.register(configuration);
      }
    }
  }
  context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
  // 默認(rèn)為加了一個(gè)名為 loadbalancer 的 PropertySource, 里面有一個(gè) loadbalancer.client.name=serviceId 的配置....
  context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,
                                                                               Collections.<String, Object>singletonMap(this.propertyName, name)));
  if (this.parent != null) {
    // Uses Environment from parent as well as beans
    context.setParent(this.parent);
    // jdk11 issue
    // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
    context.setClassLoader(this.parent.getClassLoader());
  }
  context.setDisplayName(generateDisplayName(name));
  context.refresh();
  return context;
}

// 根據(jù)這句 context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
// 而 defaultConfigType 在 LoadBalancerClientFactory 定義為 LoadBalancerClientConfiguration.class, 其配置了一個(gè) bean, 代碼如下
@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
                                                                               LoadBalancerClientFactory loadBalancerClientFactory) {
  String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
  return new RoundRobinLoadBalancer(
    loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
// 這里會(huì)與服務(wù)發(fā)現(xiàn)結(jié)合起來(lái), 即 loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class)
// 此方法從容器中獲取能提供 ServiceInstanceListSupplier.class 類型的 BeanProvider, 其實(shí)就是能獲取這種類型的 bean 唄, 然后用這個(gè)類來(lái)獲取 url 列表...  具體實(shí)現(xiàn)要看 Nacos / Consul 了.

// 所以默認(rèn)獲取的負(fù)載均衡策略就是它了:  RoundRobinLoadBalancer

總結(jié): 用了一個(gè) LoadBalancerAutoConfiguration, 為 RestTemplate 加一個(gè)攔截器使得執(zhí)行請(qǐng)求前先修改一下請(qǐng)求對(duì)象(主要修改url唄), 修改的步驟是 LoadBalancerClient.execute(), 里面則會(huì)使用 choose 獲取微服務(wù)真實(shí)url, choose 是 ReactorLoadBalancer 的接口, 代表負(fù)載均衡策略. 啊對(duì)了, 既然是負(fù)載均衡算法, 那就是負(fù)責(zé)選取, 不負(fù)責(zé)獲取才對(duì)... 于是我發(fā)現(xiàn) loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class) 才是得到的對(duì)象, 才是獲取 url 列表的代碼(肯定和consul或nacos有關(guān)了)!!

暗示下集出 Nacos!!

Cloud Alibaba 和 Spring Cloud 整合 Spring Cloud Commons 步驟(指服務(wù)注冊(cè)與發(fā)現(xiàn))

1.希望別太簡(jiǎn)單
2.就不寫這里了(因?yàn)檫€沒(méi)寫啊!)
3.下集見(jiàn)

Spring Cloud 整合 spring-cloud-loadbalancer

1.pom 添加依賴即可
2.pom 添加依賴即可
3.pom 添加依賴即可

# 總結(jié)
我也沒(méi)想到, 沒(méi)多寫一個(gè)類, 直接就能用...  原理上面分析了 ??????????

Cloud Alibaba 整合 spring-cloud-loadbalancer

1.pom 添加依賴即可
2.pom 添加依賴即可
3.pom 添加依賴即可

# 總結(jié)
我也沒(méi)想到, 沒(méi)多寫一個(gè)類, 直接就能用...  原理上面分析了 ??????????

總結(jié): 你沒(méi)卡, 你電腦沒(méi)問(wèn)題, 我就是寫(zhan)了(tie)兩遍!!!

Spring Cloud Commons 的核心類及其作用

1.LoadBalancerClient: 實(shí)現(xiàn)它就實(shí)現(xiàn)了負(fù)載均衡策略
    但其實(shí)實(shí)現(xiàn) ReactorServiceInstanceLoadBalancer 更簡(jiǎn)單
2.DiscoveryClient: 實(shí)現(xiàn)它就實(shí)現(xiàn)了服務(wù)發(fā)現(xiàn)
3.ServiceRegistry: 實(shí)現(xiàn)它就實(shí)現(xiàn)了服務(wù)注冊(cè)
4.ServiceInstance: 代表一個(gè)服務(wù), 前面加個(gè) Micro 就是微服務(wù)了 :D  

PS: 就這!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末梅肤,一起剝皮案震驚了整個(gè)濱河市喻犁,隨后出現(xiàn)的幾起案子声登,更是在濱河造成了極大的恐慌擅笔,老刑警劉巖握巢,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)昂芜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赔蒲,“玉大人泌神,你說(shuō)我怎么就攤上這事良漱。” “怎么了欢际?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵母市,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我损趋,道長(zhǎng)窒篱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任舶沿,我火速辦了婚禮,結(jié)果婚禮上配并,老公的妹妹穿的比我還像新娘括荡。我一直安慰自己,他們只是感情好溉旋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布畸冲。 她就那樣靜靜地躺著,像睡著了一般观腊。 火紅的嫁衣襯著肌膚如雪邑闲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天梧油,我揣著相機(jī)與錄音苫耸,去河邊找鬼。 笑死儡陨,一個(gè)胖子當(dāng)著我的面吹牛褪子,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骗村,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼嫌褪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了胚股?” 一聲冷哼從身側(cè)響起笼痛,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎琅拌,沒(méi)想到半個(gè)月后缨伊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡财忽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年倘核,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片即彪。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡紧唱,死狀恐怖活尊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情漏益,我是刑警寧澤蛹锰,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站绰疤,受9級(jí)特大地震影響铜犬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜轻庆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一癣猾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧余爆,春花似錦纷宇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至桩砰,卻和暖如春拓春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背亚隅。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工硼莽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人枢步。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓沉删,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親醉途。 傳聞我的和親對(duì)象是個(gè)殘疾皇子矾瑰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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