k8s發(fā)布Spring cloud+eureka架構(gòu)服務(wù)優(yōu)雅啟動停止方案

Spring cloud+eureka是目前微服務(wù)主流解決方案之一,kubernetes則是廣泛應(yīng)用的發(fā)布工具郎哭,兩者結(jié)合使用很常見缰泡。而兩者結(jié)合時如何優(yōu)雅啟停從而實現(xiàn)無感發(fā)布很關(guān)鍵跌帐。
下面將從不做特殊處理時啟停存在的問題谦纱、業(yè)務(wù)代碼設(shè)計要求、spring cloud+eureka本身停機處理機制华畏、k8s滾動發(fā)布如何關(guān)聯(lián)spring程序的啟停機制 幾點分析和提出解決方案鹏秋。

1、不做特殊處理時啟停存在的問題

1.1亡笑、啟動服務(wù)時的問題

(1)spring boot只要對Application類做EnableDiscoveryClient注解拼岳,服務(wù)啟動后會自動向eureka注冊。如果服務(wù)需要預(yù)加載較多數(shù)據(jù)况芒,預(yù)加載完成前的請求可能會失敗惜纸。要解決需要控制注冊發(fā)生在預(yù)加載后叶撒。
(2)spring程序啟動和預(yù)加載數(shù)據(jù)需要時間。但k8s會在啟動Pod后很快將外部請求導(dǎo)向服務(wù)耐版,導(dǎo)致異常祠够。解決這個問題需要k8s知道服務(wù)是否就緒。

1.2粪牲、停止服務(wù)時的問題

最原始的關(guān)閉服務(wù)方法是使用kill指令古瓤,常用的信號選項:
(1) kill -2 pid 向指定 pid 發(fā)送 SIGINT 中斷信號, 等同于 ctrl+c.
(2) kill -9 pid, 向指定 pid 發(fā)送 SIGKILL 立即終止信號.
(3) kill -15 pid, 向指定 pid 發(fā)送 SIGTERM 終止信號.
(4) kill pid 等同于 kill 15 pid

SIGINT/SIGKILL/SIGTERM 信號的區(qū)別:
(1) SIGINT (ctrl+c) 信號(信號編號為2),信號會被當前進程樹接收到,也就說, 不僅當前進程會收到該信號,而且它的子進程也會收到.
(2) SIGKILL信號(信號編號為 9),程序不能捕獲該信號,最粗暴最快速結(jié)束程序的方法.
(3) SIGTERM 信號 (信號編號為 15), 信號會被當前進程接收到, 但它的子進程不會收到, 如果當前進程被 kill 掉, 它的的子進程的父進程將變成 init 進程 (init 進程是那個 pid 為 1 的進程)

結(jié)束某個服務(wù),應(yīng)該盡量使用 kill pid,而不是 kill -9 pid。如果服務(wù)提供關(guān)閉通知接口,在完全退出之前,可以先做一些善后處理腺阳。

k8s關(guān)閉pod時落君,相當于執(zhí)行kill -15指令,且在一定時間后如果仍未關(guān)閉就執(zhí)行kill -9 強制殺死進程亭引,這個時間可以配置绎速,默認30s。

Java提供了注冊監(jiān)聽器接收SIGTERM信號的機制 —— 示例 焙蚓。spring服務(wù)在接收到SIGTERM信號后纹冤,框架會回收線程,終止定時任務(wù)购公,而spring cloud的服務(wù)發(fā)現(xiàn)模塊客戶端會主動向eureka請求下線萌京。但仍會存在以下問題。

(1) 處理中的任務(wù)會被直接打斷宏浩,無法做妥善處理知残。比如sleep阻塞的線程會直接報異常 InterruptedException,執(zhí)行中的定時任務(wù)會被直接終止退出比庄。
(2) kill指令發(fā)出SIGTERM信號后求妹,微服務(wù)會自動向eureka發(fā)送下線通知,但這個過程畢竟是網(wǎng)絡(luò)請求印蔗,有延時扒最。其他微服務(wù)仍然會有持續(xù)發(fā)送一些請求到要關(guān)停的服務(wù)丑勤,而這時服務(wù)內(nèi)部spring框架底層已經(jīng)在做停止服務(wù)的處理华嘹,容易產(chǎn)生異常。

這兩個問題前者需要提前通知服務(wù)要被關(guān)停法竞,并留足夠時間做善后處理耙厚;后者需要微服務(wù)得到服務(wù)關(guān)停預(yù)告后主動向eureka發(fā)送下線請求。

2岔霸、代碼設(shè)計要求

為了主動停止服務(wù)薛躬,業(yè)務(wù)代碼要做到以下要求

  • 定時任務(wù)的業(yè)務(wù)處理如果時間太長,要拆分成多個短時子任務(wù)呆细,執(zhí)行每個子任務(wù)前檢查服務(wù)是否要終止型宝,如果是則主動保存數(shù)據(jù)并不再執(zhí)行后續(xù)任務(wù)。
  • 未執(zhí)行的任務(wù)在服務(wù)重新啟動后或者由其他實例自動檢測并執(zhí)行,避免因停機產(chǎn)生的異常數(shù)據(jù)無法消除。

3趴酣、Spring cloud+eureka的優(yōu)雅啟停方案

只基于spring cloud+eureka體系做開發(fā)也很常見梨树,所以先了解這個體系本身解決優(yōu)雅啟停的方案。

3.1微服務(wù)下線快速感知

要做到優(yōu)雅啟停岖寞,微服務(wù)下線后快速被其他微服務(wù)節(jié)點感知至關(guān)重要抡四。快速感知才能避免微服務(wù)下線后其他服務(wù)還長時間繼續(xù)請求仗谆。

spring cloud+eureka要做到下線快速感知有以下參數(shù)要注意配置指巡。

eureka server端需要配置如下:

eureka:
  server:
    evictionIntervalTimerInMs: 5000  #啟用主動失效,并且每次主動失效檢測間隔為5000ms
    responseCacheUpdateIntervalMs: 5000 #從ReadWriteMap刷新節(jié)點信息到ReadOnlyMap的時間隶垮,client讀取的是后者藻雪,默認30s

eureka server端對微服務(wù)節(jié)點信息的記錄有兩層,ReadWriteMap和ReadOnlyMap岁疼。最新的變化在ReadWriteMap阔涉,經(jīng)過一段時間后才會更新到 ReadOnlyMap。而提供給client讀取的是 ReadOnlyMap捷绒,所以 responseCacheUpdateIntervalMs一般設(shè)置為3-5s較好瑰排。

另外提一下,很多文章解釋快速感知時暖侨,會提到一個配置——enableSelfPreservation椭住,即是自我保護開關(guān),這個配置大意是當eureka發(fā)現(xiàn)節(jié)點的心跳大面積異常時就持續(xù)一段時間(默認5分鐘字逗,可配置)不更新節(jié)點信息京郑。因為這時很可能是eureka server的網(wǎng)絡(luò)出問題了,保留錯誤的數(shù)據(jù)也要避免徹底癱瘓葫掉。此機制觸發(fā)時管理頁面會有紅字警告些举。但實測自我保護機制不會干擾服務(wù)主動下線時啟停的感知,所以自我保護開關(guān)可以自行按需設(shè)置俭厚。

eureka client端需要配置如下

eureka:
  instance:
    #服務(wù)刷新時間户魏,每隔這個時間會主動心跳一次
    leaseRenewalIntervalInSeconds: 3   
    #服務(wù)過期時間,超過這個時間沒有接收到心跳EurekaServer就會將這個實例剔除
    leaseExpirationDurationInSeconds: 10   
  client:
    fetchRegistry: true   #定期更新eureka server拉去服務(wù)節(jié)點清單,快速感知服務(wù)下線
    registryFetchIntervalSeconds: 5  #eureka client刷新本地緩存時間挪挤,默認30s

這四個配置前兩者保證服務(wù)上下線都快速被eureka server感知叼丑,后兩者保證快速感知其他微服務(wù)的上下線。

但leaseRenewalIntervalInSeconds 和 leaseExpirationDurationInSeconds兩個配置的時間對于主動下線的感知沒有影響扛门。后面提到的優(yōu)雅啟停方案都是屬于主動下線鸠信,所以這兩個配置的值可以自行調(diào)整。

3.2啟動問題處理方案

啟動問題中的第一個問題:微服務(wù)自動向eureka注注冊時數(shù)據(jù)預(yù)加載還未完成论寨,是spring cloud本身就要處理的問題星立,第二個問題結(jié)合k8s時才會出現(xiàn)爽茴。

這里先說明第一個問題的解決方案。我們可以直接將預(yù)加載代碼放在static代碼塊或者PostConstruct標注的代碼塊绰垂,這樣可以使得預(yù)加載成為spring程序啟動的一部分闹啦,從而使得自動注冊必然發(fā)生在預(yù)加載后。同時為了方便對外提供狀態(tài)辕坝,我們在Application類main方法最后一行記錄加載完成狀態(tài)為true窍奋,并記錄時間。下面為示例代碼:

    /**
     * 服務(wù)是否已經(jīng)啟動
     */
    public volatile static boolean isStart = false;
    /**
     * 服務(wù)啟動的時間
     */
    public volatile static long startTime = 0;
    /**
     * 服務(wù)是否處于準備關(guān)閉的狀態(tài)酱畅,這個時候一些定時任務(wù)就應(yīng)該及時退出了
     */
    public volatile static boolean isPreStop = false;
    
    public static void main(String[] args) {
        SpringApplication.run(xxxApplication.class, args);
        logger.info("服務(wù)啟動完畢");
        startTime = System.currentTimeMillis();
        isStart = true;
    }

    @PostConstruct
    public void preload(){
        logger.info("預(yù)加載完畢");
    }

之后再提供一個接口給查詢是否就緒琳袄,如果沒就緒就返回500的http code。由于服務(wù)啟動完畢后還要一定時間才能穩(wěn)定注冊到eureka并被其他微服務(wù)感知到纺酸,所以需要睡眠一定時間窖逗。這個時間應(yīng)該至少是 responseCacheUpdateIntervalMs + registryFetchIntervalSeconds,如果要更保險餐蔬,可以再增加幾秒碎紊。如果要更嚴謹?shù)脑掃€可以增加判斷注冊是否成功。

示例代碼如下樊诺,其中isPreStop變量在后面停止方案部分會賦值仗考,主要是為了避免服務(wù)已經(jīng)處于停機準備階段了ifReady仍然返回true。

    public boolean ifReady() {
        long timeCur = System.currentTimeMillis();
        if (isStart && !isPreStop && timeCur - startTime > 16000) {
            return true;//success
        } else {
            //TODO:throw Exception在外層做異常處理并返回500的http code
        }
    }

3.3停止問題解決方案

停止服務(wù)兩個問題的關(guān)鍵在于

  • 主動通知服務(wù)要停止词爬,并預(yù)留時間做處理工作秃嗜;
  • 主動向eureka發(fā)送下線通知

對于上面兩個要求spring cloud+eureka體系有多種解決方案實現(xiàn),下面只介紹最簡單易用的方案顿膨。

  • 服務(wù)提供一個http接口專門用于下線通知并在接口里做服務(wù)終止處理锅锨,調(diào)用接口返回成功后預(yù)留一定時間,且在查看日志確定所有請求都結(jié)束后再調(diào)用kill -9殺死服務(wù)恋沃。這個http接口最好不對外暴露或者限制ip白名單才能調(diào)用必搞。
  • 在上一步提到的http接口中向eureka發(fā)送下線通知,比如下面的代碼就可以做到向eureka發(fā)送下線通知。由于其他微服務(wù)接收到該服務(wù)下線要一定時間囊咏,所以發(fā)送下線通知服務(wù)仍然要運行一段時間恕洲。這個時間和啟動時的等待時間類似,至少要是 responseCacheUpdateIntervalMs + registryFetchIntervalSeconds 匆笤,為了保險可以多幾秒研侣。
    @Autowired
    private EurekaAutoServiceRegistration autoServiceRegistration;
    
    public boolean preStop() {
        preStopStartTime = System.currentTimeMillis();
        isPreStop = true;
        autoServiceRegistration.stop();
        try {
            Thread.sleep(16000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       //TODO:這里才能做回收http接口要用到的資源的操作谱邪,包括數(shù)據(jù)緩存等
       return true;
    }

上面的方案http接口可以在服務(wù)器通過curl指令手工調(diào)用也可以結(jié)合shell腳本炮捧、jenkins腳本等使用做到自動化處理。

另外提一下惦银,網(wǎng)上很多文章使用的是 EurekaClient對象的 shutDown方法咆课,但是我實驗到這種方式無法真正下線服務(wù)末誓,在eurekaServer端會報以下異常,其中xxx為實例的名稱书蚪,最后也沒有搞清楚原因喇澡。

Cancelled instance xxx (replication=false)
DS: Registry: cancel failed because Lease is not registered for: xxxx
Not Found (Cancel): xxx

3.4小結(jié)

3.1提到的eureka client端fetchRegistry 、registryFetchIntervalSeconds兩個配置和后面兩個章節(jié)提到的就緒檢測接口殊校、prestop接口的預(yù)留時間也就是sleep的時間注意同一個系統(tǒng)的微服務(wù)要保持一致晴玖。

prestop接口的預(yù)留時間和eureka client的配置要一致很好理解,其他服務(wù)要感知到它已經(jīng)下線不再向其發(fā)送請求为流,這個服務(wù)才能關(guān)閉呕屎。這些時間如果不統(tǒng)一可能出現(xiàn)在服務(wù)關(guān)停后其他微服務(wù)還沒拉取到新的節(jié)點列表的情況。

就緒檢測接口的時間和eureka client的配置要一致需要舉例說明敬察。假設(shè)eureka server responseCacheUpdateIntervalMs的值為5000秀睛,有兩個微服務(wù)A和B,A有兩個實例莲祸,A1和A2蹂安。A在就緒接口的預(yù)留時間是15s,而B服務(wù)更新服務(wù)列表的時間registryFetchIntervalSeconds為20s锐帜。這種情況下發(fā)布A1服務(wù):

  • A1服務(wù)啟動完15s后就緒接口返回成功
  • 發(fā)布管理腳本認為A1服務(wù)就緒完畢田盈,別的節(jié)點也已經(jīng)感知到A1的啟動
  • 發(fā)布管理腳本開始調(diào)用A2服務(wù)的prestop接口,等返回后成功后重啟A2服務(wù)
  • 但A1的發(fā)布實際上要20+5=25s才能被B感知到,剩下的時間B服務(wù)只能看到A2服務(wù)并繼續(xù)向其發(fā)送請求

為了避免上面這種不可控的情況缴阎,各個微服務(wù)的相關(guān)配置還是直接設(shè)置為一致較好缠黍。

4、k8s發(fā)布時關(guān)聯(lián)spring cloud啟停方案

使用k8s發(fā)布服務(wù)默認使用的滾動發(fā)布方案药蜻,這個方案本身已經(jīng)有一定機制減少發(fā)布的影響瓷式。滾動發(fā)布時發(fā)布完一個新版本的pod后才會下線一個舊的pod,并把指向sevice的請求經(jīng)負載均衡指向新pod语泽,直到所有舊的pod下線贸典,新的pod全部發(fā)布完畢。

所以只要k8s在pod的啟停時做到和微服務(wù)聯(lián)動踱卵,就可以做到無感發(fā)布廊驼。關(guān)鍵在于探知微服務(wù)是否準備好了、通知服務(wù)將要停止惋砂、配置啟停過程預(yù)留的時間妒挎。這幾個方面k8s都有相關(guān)的機制,所以我們先了解這些機制西饵,再整合得出解決思路酝掩。

4.1 k8s相關(guān)機制或配置

4.1.1 啟動后就緒時間

這個機制相對簡單粗暴,通過一個屬性 minReadySeconds 設(shè)置pod啟動后多長時間才被認為就緒,默認為0s眷柔。

4.1.2 探針機制

k8s提供了是兩種探針的機制期虾,分別為就緒探針 readinessProbe原朝、存活探針 livenessProbe。

探針機制可以通過http接口镶苞、shell指令喳坠、tcp確認容器的狀態(tài)。探針還可以配置延遲探測時間茂蚓、探測間隔壕鹉、探測成功或失敗條件延后時間等參數(shù)。使用http接口探測時聋涨,可以配置header參數(shù)御板,如果響應(yīng)的狀態(tài)碼大于等于200 且小于 400,則診斷被認為是成功的牛郑。

  • 存活探針怠肋,主要用于檢測pod是否異常,如果k8s通過健康探針檢測到服務(wù)異常后會替換或重啟容器
  • 就緒探針淹朋,這個探測通過時才會將其加入到service匹配的endpoint列表中笙各,并向該容器發(fā)送http請求,否則會將pod從列表移除直到就緒探針再次通過

就緒探針和存活探針比較類似础芍,都會持續(xù)執(zhí)行檢測杈抢,只是檢測會導(dǎo)致的結(jié)果不一樣,一個會導(dǎo)致容器重啟或被替換仑性,一個會導(dǎo)致http請求停止分發(fā)到容器惶楼。

探針機制詳細介紹可以查看 這里

詳細的配置可以查看 這里诊杆。但是要注意這個文檔里還提到啟動探針機制歼捐,但筆者嘗試配置無發(fā)生效,不確定是否和版本有關(guān)晨汹,所以對其不做介紹豹储。

4.1.3 terminationGracePeriodSeconds 配置延遲關(guān)閉時間

該屬性默認30s,只配置terminationGracePeriodSeconds這個屬性而沒有配置prestop時淘这,k8s會先發(fā)送SIGTERM信號給主進程剥扣,然后然后等待terminationGracePeriodSeconds 屬性的時間,會被使用SIGKILL殺死铝穷。這個機制相對簡單粗暴钠怯。

4.1.4 prestop 機制

prestop機制,為容器生命周期鉤子中的一種曙聂,也就是在準備關(guān)閉pod時會調(diào)用的晦炊。這個接口調(diào)用是至少一次,有可能調(diào)用多次,需要做好冪等處理刽锤。官方文檔有詳細介紹,可以參考查看 這里朦佩。這個機制和就緒探針類似并思,也可以通過http接口、shell指令兩種方式進行语稠。

這個機制跟terminationGracePeriodSeconds會有關(guān)聯(lián)宋彼,k8s會根據(jù)prestop設(shè)置的方式通知服務(wù)將要關(guān)閉Pod,如果在GracePeriod時間后prestop鉤子仍然還在運行仙畦,就會直接發(fā)送SIGTERM信號输涕,等待2s后使用SIGKILL殺死主進程。更詳細的步驟說明可以查看 這里

prestop對應(yīng)的接口或者指令的指令執(zhí)行時間不宜過長慨畸,且盡量少做資源消耗大的操作莱坎,30s以內(nèi)為宜。因為k8s在pod關(guān)閉處理時會先陸續(xù)新建新的pod寸士,如果prestop過長檐什,會導(dǎo)致出現(xiàn)兩倍數(shù)量的pod,如果prestop嗲用的接口或指令處理不當弱卡,可能會導(dǎo)致資源消耗達到正常值的兩倍乃正。

4.1.5 以上幾類機制的配置示例

提供上述幾個機制的deployment文件配置示例如下

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: review-demo
  namespace: scm
  labels:
    app: review-demo
spec:
  replicas: 3
#  minReadySeconds: 60     #滾動升級時60s后認為該pod就緒
  strategy:
    rollingUpdate:  ##由于replicas為3,則整個升級,pod個數(shù)在2-4個之間
      maxSurge: 1      #滾動升級時會先啟動1個pod
      maxUnavailable: 1 #滾動升級時允許的最大Unavailable的pod個數(shù)
  template:
    metadata:
      labels:
        app: review-demo
    spec:
      terminationGracePeriodSeconds: 60 ##k8s將會給應(yīng)用發(fā)送SIGTERM信號,可以用來正確婶博、優(yōu)雅地關(guān)閉應(yīng)用,默認為30秒
      containers:
      - name: review-demo
        image: library/review-demo:0.0.1-SNAPSHOT
        imagePullPolicy: IfNotPresent
        lifecycle:
          preStop:
            httpGet:
              path: /prestop
              port: 8080
              scheme: HTTP            
        livenessProbe: #kubernetes認為該pod是存活的,不存活則需要重啟
          httpGet:
            path: /health
            port: 8080
            scheme: HTTP
            httpHeaders:
              - name: Custom-Header
              value: Awesome               
          initialDelaySeconds: 60 ## equals to the max startup time of the application + couple of seconds
          timeoutSeconds: 10
          successThreshold: 1
          failureThreshold: 5
          periodSeconds: 5 # 多少秒執(zhí)行一次檢測
        readinessProbe: #kubernetes認為該pod是準備好接收http請求了的
          httpGet:
            path: /ifready
            port: 8080
            scheme: HTTP
            httpHeaders:
              - name: Custom-Header
              value: Awesome            
          initialDelaySeconds: 30 #equals to min startup time of the app
          timeoutSeconds: 10
          successThreshold: 1
          failureThreshold: 5
          periodSeconds: 5 # 多少秒執(zhí)行一次檢測
        resources:
          # keep request = limit to keep this container in guaranteed class
          requests:
            cpu: 50m
            memory: 200Mi
          limits:
            cpu: 500m
            memory: 500Mi
        env:
          - name: PROFILE
            value: "test"
        ports:
          - name: http
            containerPort: 8080

4.2瓮具、基于以上機制解決啟動問題

使用k8s的就緒探針配合3.2提到的spring cloud+eureka程序本身的啟動和注冊機制就可以完美的解決1.1提到的微服務(wù)啟動未完成或預(yù)加載未完成就被注冊到eureka以及被k8s導(dǎo)入外部流量的問題。

4.3凡人、基于機制解決停機問題

1.2提到的k8s發(fā)布spring cloud服務(wù)時停機過程的問題只需結(jié)合3.3提到的方案名党,使用k8s preStop機制調(diào)用微服務(wù)的服務(wù)終止處理接口即可解決。但是prestop存在一個超時時間挠轴,如果設(shè)置的超時時間內(nèi)prestop調(diào)用的接口沒有完成兑巾,則服務(wù)存在強制退出的可能,從而產(chǎn)生異常數(shù)據(jù)忠荞。但絕大部分系統(tǒng)對結(jié)束處理的時長和嚴謹程度也沒那么嚴格蒋歌,而且由于異常宕機難以避免,一般程序本身也必須要有異常數(shù)據(jù)處理的方案委煤,所以這個方案基本夠用堂油。

4.4、更嚴謹?shù)姆桨?/h4>

4.3提供的方案可以滿足絕大部分場景了碧绞,但對高要求的場景而然有一定的缺陷府框。比Pod在關(guān)閉過程中終止或暫停各種在途任務(wù)和向eureka下線服務(wù)時很可能遇到異常情況,導(dǎo)致超出預(yù)期時間后服務(wù)仍未終止讥邻,可能需要繼續(xù)保持pod運行迫靖,中斷pod重啟或升級方案等院峡。

這個情況可以考慮使用k8s的ApiServer(可以通過接口管理pod等的增刪查改和啟停)結(jié)合WebHook機制訪問自定義接口確認服務(wù)是否可以關(guān)閉。這種使用方法比較高級了系宜,如果微服務(wù)體系比較大時可以考慮這種方案照激。具體參考此文章 的 另辟蹊徑:解耦 Pod 刪除的控制流 部分。

5盹牧、試驗

為了試驗這個優(yōu)雅啟停方案俩垃,基于k8s環(huán)境,使用兩個spring cloud+eureka服務(wù)汰寓,其中一個服務(wù)A為認證服務(wù)口柳,提供token有效性的檢查接口,A服務(wù)使用兩個實例有滑。另外一個服務(wù)B調(diào)用A的token校驗接口跃闹。

嘗試不使用以上的優(yōu)雅啟停方案時,使用壓測工具wrk次序訪問B服務(wù)的接口毛好,使其調(diào)用A服務(wù)的checkToken接口辣卒。然后發(fā)布新版本的A服務(wù),結(jié)果在B服務(wù)的日志中發(fā)現(xiàn)大量訪問A服務(wù)接口不通的錯誤日志睛榄,時間和A服務(wù)的發(fā)布時間符合荣茫。說明光靠k8s本身的滾動發(fā)布機制,無法保證微服務(wù)體系對內(nèi)部的高可用场靴。

而是使用以上的優(yōu)雅啟停方案后啡莉,服務(wù)B請求訪問A服務(wù)的token校驗接口失敗的情況不再存在。

6旨剥、總結(jié)

前面4.2咧欣、4.3提到的方案雖然在一些極端情況,可能還會產(chǎn)生異常數(shù)據(jù)轨帜,但對于非金融場景已經(jīng)夠用魄咕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蚌父,隨后出現(xiàn)的幾起案子哮兰,更是在濱河造成了極大的恐慌,老刑警劉巖苟弛,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喝滞,死亡現(xiàn)場離奇詭異,居然都是意外死亡膏秫,警方通過查閱死者的電腦和手機右遭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人窘哈,你說我怎么就攤上這事吹榴。” “怎么了滚婉?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵图筹,是天一觀的道長。 經(jīng)常有香客問我满哪,道長婿斥,這世上最難降的妖魔是什么劝篷? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任哨鸭,我火速辦了婚禮,結(jié)果婚禮上娇妓,老公的妹妹穿的比我還像新娘像鸡。我一直安慰自己,他們只是感情好哈恰,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布只估。 她就那樣靜靜地躺著,像睡著了一般着绷。 火紅的嫁衣襯著肌膚如雪蛔钙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天荠医,我揣著相機與錄音吁脱,去河邊找鬼。 笑死彬向,一個胖子當著我的面吹牛兼贡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播娃胆,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼遍希,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了里烦?” 一聲冷哼從身側(cè)響起凿蒜,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胁黑,沒想到半個月后篙程,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡别厘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年虱饿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡氮发,死狀恐怖渴肉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情爽冕,我是刑警寧澤仇祭,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站颈畸,受9級特大地震影響乌奇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜眯娱,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一扣墩、第九天 我趴在偏房一處隱蔽的房頂上張望惰爬。 院中可真熱鬧次舌,春花似錦田绑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至穿剖,卻和暖如春蚤蔓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背糊余。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工秀又, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人啄刹。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓涮坐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親誓军。 傳聞我的和親對象是個殘疾皇子袱讹,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

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