玩轉(zhuǎn)Eureka

Register - 注冊(cè)

Eureka Instance注冊(cè)的REST入口在com.netflix.eureka.resources.ApplicationResource#addInstance

/**? ? * Registers information about a particular instance for an? ? * {@linkcom.netflix.discovery.shared.Application}.? ? *? ? *@paraminfo? ? *? ? ? ? ? ? {@linkInstanceInfo} information of the instance.? ? *@paramisReplication? ? *? ? ? ? ? ? a header parameter containing information whether this is? ? *? ? ? ? ? ? replicated from other nodes.? ? */@POST@Consumes({"application/json","application/xml"})publicResponseaddInstance(InstanceInfo info,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @HeaderParam(PeerEurekaNode.HEADER_REPLICATION)String isReplication){? ? ? ? logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);// *********** 字段校驗(yàn) ************// validate that the instanceinfo contains all the necessary required fieldsif(isBlank(info.getId())) {returnResponse.status(400).entity("Missing instanceId").build();? ? ? ? }elseif(isBlank(info.getHostName())) {returnResponse.status(400).entity("Missing hostname").build();? ? ? ? }elseif(isBlank(info.getAppName())) {returnResponse.status(400).entity("Missing appName").build();? ? ? ? }elseif(!appName.equals(info.getAppName())) {returnResponse.status(400).entity("Mismatched appName, expecting "+ appName +" but was "+ info.getAppName()).build();? ? ? ? }elseif(info.getDataCenterInfo() ==null) {returnResponse.status(400).entity("Missing dataCenterInfo").build();? ? ? ? }elseif(info.getDataCenterInfo().getName() ==null) {returnResponse.status(400).entity("Missing dataCenterInfo Name").build();? ? ? ? }// handle cases where clients may be registering with bad DataCenterInfo with missing dataDataCenterInfo dataCenterInfo = info.getDataCenterInfo();// 僅當(dāng)DataCenterInfo為AmazonInfo實(shí)例的時(shí)候精算,其父類有可能是UniqueIdentifierif(dataCenterInfoinstanceofUniqueIdentifier) {// ......}// *********** 字段校驗(yàn) END ************registry.register(info,"true".equals(isReplication));// (1)returnResponse.status(204).build();// 204 to be backwards compatible}

真正的注冊(cè)操作在(1)處津坑,需要注意的是isReplication變量取決于HTTP頭x-netflix-discovery-replication的值煤辨。繼續(xù)追蹤(1)的調(diào)用棧捏鱼,發(fā)現(xiàn)執(zhí)行注冊(cè)操作的方法是是com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#register

注意該方法的javadoc,他告訴了我們一個(gè)比較重要的訊息:將InstanceInfo實(shí)例信息注冊(cè)到Eureka并且復(fù)制該信息到其他peer盔然。如果當(dāng)前收到的注冊(cè)信息是來(lái)自其他peer的復(fù)制事件,那么將不會(huì)將這個(gè)注冊(cè)信息繼續(xù)復(fù)制到其他peer,這個(gè)標(biāo)志位就是上面所述的isReplication晚缩。

/**? ? * Registers the information about the {@linkInstanceInfo} and replicates? ? * this information to all peer eureka nodes. If this is replication event? ? * from other replica nodes then it is not replicated.? ? *? ? *@paraminfo? ? *? ? ? ? ? ? the {@linkInstanceInfo} to be registered and replicated.? ? *@paramisReplication? ? *? ? ? ? ? ? true if this is a replication event from other replica nodes,? ? *? ? ? ? ? ? false otherwise.? ? */@Overridepublicvoidregister(finalInstanceInfo info,finalbooleanisReplication){// 默認(rèn)租約有效時(shí)長(zhǎng)為90sintleaseDuration = Lease.DEFAULT_DURATION_IN_SECS;// 注冊(cè)信息里包含則依照注冊(cè)信息的租約時(shí)長(zhǎng)if(info.getLeaseInfo() !=null&& info.getLeaseInfo().getDurationInSecs() >0) {? ? ? ? ? ? leaseDuration = info.getLeaseInfo().getDurationInSecs();? ? ? ? }// super為AbstractInstanceRegistrysuper.register(info, leaseDuration, isReplication);// 復(fù)制到其他peerreplicateToPeers(Action.Register, info.getAppName(), info.getId(), info,null, isReplication);? ? }

我們看到是先獲取到租約的有效時(shí)長(zhǎng),然后才是真真正正地委托給super執(zhí)行注冊(cè)操作super.register(...)并將注冊(cè)信息復(fù)制到其他peer媳危。register方法非常長(zhǎng)荞彼,我們重點(diǎn)觀察一下他的注冊(cè)表的結(jié)構(gòu):

privatefinalConcurrentHashMap>> registry

該注冊(cè)表是一個(gè)以app name為key(在Spring Cloud里就是spring.application.name),嵌套Map為value的ConcurrentHashMap結(jié)構(gòu)待笑。其嵌套Map是以Instance ID為key鸣皂,Lease對(duì)象為value的鍵值結(jié)構(gòu)。這個(gè)registry注冊(cè)表在Eureka Server或SpringBoot Admin的監(jiān)控面板上以Eureka Service這個(gè)角色出現(xiàn)暮蹂。

/**? ? * Registers a new instance with a given duration.? ? *? ? *@seecom.netflix.eureka.lease.LeaseManager#register(java.lang.Object, int, boolean)? ? */publicvoidregister(InstanceInfo registrant,intleaseDuration,booleanisReplication){try{? ? ? ? ? ? read.lock();// 可以看出registry是一個(gè)以info的app name為key的Map結(jié)構(gòu), 也就是以spring.application.name的大寫串為keyMap> gMap = registry.get(registrant.getAppName());? ? ? ? ? ? REGISTER.increment(isReplication);if(gMap ==null) {finalConcurrentHashMap> gNewMap =newConcurrentHashMap>();? ? ? ? ? ? ? ? gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);if(gMap ==null) {? ? ? ? ? ? ? ? ? ? gMap = gNewMap;? ? ? ? ? ? ? ? }? ? ? ? ? ? }// registry的value的Map結(jié)構(gòu)是以info的id為key寞缝,這里的id就是Eureka文檔上的Instance ID,給你個(gè)例子你就想起是什么東西了:10.8.88.233:config-server:10888Lease existingLease = gMap.get(registrant.getId());// .......Lease lease =newLease(registrant, leaseDuration);if(existingLease !=null) {? ? ? ? ? ? ? ? lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());? ? ? ? ? ? }? ? ? ? ? ? gMap.put(registrant.getId(), lease);// .......}finally{? ? ? ? ? ? read.unlock();? ? ? ? }? ? }

上面是register(...)中關(guān)于registry的大致操作仰泻,其中有相當(dāng)一部分的操作被略去了荆陆,如果感興趣的話可以細(xì)致地研究一下。

Renew and Cancel Lease - 續(xù)約與取消租約

續(xù)約的REST入口在com.netflix.eureka.resources.InstanceResource#renewLease

而取消租約的REST入口在com.netflix.eureka.resources.InstanceResource#cancelLease

兩者的基本思想相似集侯,經(jīng)由InstanceRegistry->AbstractInstanceRegistry->PeerAwareInstanceRegistryImpl被啼,其中PeerAwareInstanceRegistryImpl裝飾了添加復(fù)制信息到其他節(jié)點(diǎn)的功能。其中register棠枉、renew浓体、cancel、statusUpdate和deleteStatusOverride都會(huì)將其信息復(fù)制到其他節(jié)點(diǎn)辈讶。

Fetch Registry - 獲取注冊(cè)信息

獲取所有Eureka Instance的注冊(cè)信息命浴,com.netflix.eureka.resources.ApplicationsResource#getContainers,其注冊(cè)信息由ResponseCacheImpl緩存,緩存的過(guò)期時(shí)間在其構(gòu)造函數(shù)中由EurekaServerConfig.getResponseCacheUpdateIntervalMs()所控制生闲,默認(rèn)緩存時(shí)間為30s媳溺。而差量注冊(cè)信息在Server端會(huì)保存得更為長(zhǎng)一些(大約3分鐘),因此獲取的差量可能會(huì)重復(fù)返回相同的實(shí)例跪腹。Eureka Client會(huì)自動(dòng)處理這些重復(fù)信息褂删。

Evcition

Eureke Server定期進(jìn)行失效節(jié)點(diǎn)的清理,執(zhí)行該任務(wù)的定時(shí)器被定義在com.netflix.eureka.registry.AbstractInstanceRegistry#evictionTimer冲茸,真正的任務(wù)是由他的內(nèi)部類AbstractInstanceRegistry#EvictionTask所執(zhí)行屯阀,默認(rèn)為每60s執(zhí)行一次清理任務(wù),其執(zhí)行間隔由EurekaServerConfig#getEvictionIntervalTimerInMs[eureka.server.eviction-interval-timer-in-ms]所決定轴术。

回顧一下上面剛說(shuō)完的注冊(cè)流程难衰,在PeerAwareInstanceRegistryImpl#register里面特別指出了默認(rèn)的租約時(shí)長(zhǎng)為90s[eureka.Instance.lease-expiration-duration-in-seconds],即如果90s后都沒(méi)有收到特定的Eureka Instance的Heartbeats逗栽,則會(huì)認(rèn)為這個(gè)Instance已經(jīng)失效(Instance在正常情況下默認(rèn)每隔30s發(fā)送一個(gè)Heartbeats[eureka.Instance.lease-renewal-interval-in-seconds]盖袭,對(duì)以上兩個(gè)默認(rèn)值有疑問(wèn)的可以翻閱LeaseInfo),EvictionTask則會(huì)把這個(gè)Instance納入清理的范圍彼宠。我們看看EvictionTask的清理代碼是怎么寫的鳄虱。

publicvoidevict(longadditionalLeaseMs){? ? ? ? logger.debug("Running the evict task");if(!isLeaseExpirationEnabled()) {? ? ? ? ? ? logger.debug("DS: lease expiration is currently disabled.");return;? ? ? ? }// We collect first all expired items, to evict them in random order. For large eviction sets,// if we do not that, we might wipe out whole apps before self preservation kicks in. By randomizing it,// the impact should be evenly distributed across all applications.// (2) 下面的for循環(huán)就是把registry中所有的Lease提取到局部變量expiredLeasesList> expiredLeases =newArrayList<>();for(Entry>> groupEntry : registry.entrySet()) {? ? ? ? ? ? Map> leaseMap = groupEntry.getValue();if(leaseMap !=null) {for(Entry> leaseEntry : leaseMap.entrySet()) {? ? ? ? ? ? ? ? ? ? Lease lease = leaseEntry.getValue();if(lease.isExpired(additionalLeaseMs) && lease.getHolder() !=null) {? ? ? ? ? ? ? ? ? ? ? ? expiredLeases.add(lease);? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }// To compensate for GC pauses or drifting local time, we need to use current registry size as a base for// triggering self-preservation. Without that we would wipe out full registry.intregistrySize = (int) getLocalRegistrySize();intregistrySizeThreshold = (int) (registrySize * serverConfig.getRenewalPercentThreshold());// (3)intevictionLimit = registrySize - registrySizeThreshold;inttoEvict = Math.min(expiredLeases.size(), evictionLimit);if(toEvict >0) {? ? ? ? ? ? logger.info("Evicting {} items (expired={}, evictionLimit={})", toEvict, expiredLeases.size(), evictionLimit);? ? ? ? ? ? Random random =newRandom(System.currentTimeMillis());for(inti =0; i < toEvict; i++) {// Pick a random item (Knuth shuffle algorithm)intnext = i + random.nextInt(expiredLeases.size() - i);? ? ? ? ? ? ? ? Collections.swap(expiredLeases, i, next);? ? ? ? ? ? ? ? Lease lease = expiredLeases.get(i);? ? ? ? ? ? ? ? String appName = lease.getHolder().getAppName();? ? ? ? ? ? ? ? String id = lease.getHolder().getId();? ? ? ? ? ? ? ? EXPIRED.increment();? ? ? ? ? ? ? ? logger.warn("DS: Registry: expired lease for {}/{}", appName, id);? ? ? ? ? ? ? ? internalCancel(appName, id,false);? ? ? ? ? ? }? ? ? ? }? ? }

在(2)中把本地的registry中的租約信息全部提取出來(lái),并在(3)通過(guò)serverConfig.getRenewalPercentThreshold()[eureka.server.renewal-percent-threshold凭峡,默認(rèn)85%]計(jì)算出一個(gè)最大可剔除的閾值evictionLimit拙已。

新增Peer Node時(shí)的初始化

在有多個(gè)Eureka Server的情況下,每個(gè)Eureka Server之間是如何發(fā)現(xiàn)對(duì)方的呢摧冀?

通過(guò)調(diào)試之后倍踪,我們根據(jù)調(diào)用鏈從下往上追溯,其初始入口為org.springframework.cloud.netflix.eureka.server.EurekaServerBootstrap#contextInitialized

publicvoidcontextInitialized(ServletContext context){try{? ? ? ? ? ? initEurekaEnvironment();? ? ? ? ? ? initEurekaServerContext();// (4)context.setAttribute(EurekaServerContext.class.getName(),this.serverContext);? ? ? ? }catch(Throwable e) {? ? ? ? ? ? log.error("Cannot bootstrap eureka server :", e);thrownewRuntimeException("Cannot bootstrap eureka server :", e);? ? ? ? }? ? }

由下個(gè)入口(4)最終可以定位到方法com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#syncUp索昂,從對(duì)應(yīng)的javadoc上我們可以知道該方法從peer eureka節(jié)點(diǎn)往自己填充注冊(cè)表信息建车。 如果操作失敗則此同步操作將failover到其他節(jié)點(diǎn),直到遍歷完列表(service urls)為止椒惨。該方法與普通的Eureka Client注冊(cè)到Eureka Server不同的一點(diǎn)是缤至,其標(biāo)志位isReplication為true,如果不記得這是什么作用的話可以翻閱到上面的Register - 注冊(cè)小節(jié)康谆。

Peer Node信息的定時(shí)更新

首先我們看Eureka Server的上下文實(shí)體中的方法com.netflix.eureka.DefaultEurekaServerContext#initialize

@PostConstruct@Overridepublicvoidinitialize()throwsException{? ? ? ? logger.info("Initializing ...");? ? ? ? peerEurekaNodes.start();// (5)registry.init(peerEurekaNodes);? ? ? ? logger.info("Initialized");? ? }

該方法明確指出這是一個(gè)Spring Bean凄杯,在構(gòu)建Bean完成后執(zhí)行此方法,繼續(xù)追蹤(5)秉宿。

publicvoidstart(){? ? ? ? taskExecutor = Executors.newSingleThreadScheduledExecutor(newThreadFactory() {@OverridepublicThreadnewThread(Runnable r){? ? ? ? ? ? ? ? ? ? ? ? Thread thread =newThread(r,"Eureka-PeerNodesUpdater");? ? ? ? ? ? ? ? ? ? ? ? thread.setDaemon(true);returnthread;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? );try{? ? ? ? ? ? updatePeerEurekaNodes(resolvePeerUrls());// (6)Runnable peersUpdateTask =newRunnable() {// (7)@Overridepublicvoidrun(){try{? ? ? ? ? ? ? ? ? ? ? ? updatePeerEurekaNodes(resolvePeerUrls());// (6)}catch(Throwable e) {? ? ? ? ? ? ? ? ? ? ? ? logger.error("Cannot update the replica Nodes", e);? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? };? ? ? ? ? ? taskExecutor.scheduleWithFixedDelay(? ? ? ? ? ? ? ? ? ? peersUpdateTask,// (7)serverConfig.getPeerEurekaNodesUpdateIntervalMs(),// (8)serverConfig.getPeerEurekaNodesUpdateIntervalMs(),? ? ? ? ? ? ? ? ? ? TimeUnit.MILLISECONDS? ? ? ? ? ? );? ? ? ? }catch(Exception e) {thrownewIllegalStateException(e);? ? ? ? }for(PeerEurekaNode node : peerEurekaNodes) {? ? ? ? ? ? logger.info("Replica node URL:? "+ node.getServiceUrl());? ? ? ? }? ? }

上面這段代碼很清晰地告訴我們?cè)趩?dòng)Eureka Server的時(shí)候就會(huì)調(diào)用updatePeerEurekaNodes(...)更新peer的狀態(tài),并封裝為一個(gè)Runnable進(jìn)行周期性更新屯碴。這個(gè)定時(shí)時(shí)間由serverConfig.getPeerEurekaNodesUpdateIntervalMs()[eureka.server.peer-eureka-nodes-update-interval-ms]所控制描睦,默認(rèn)值為600s导而,即10min忱叭。一直經(jīng)由EndpointUtils#getDiscoveryServiceUrls、EndpointUtils#getServiceUrlsFromConfig至EurekaClientConfigBean#getEurekaServerServiceUrls獲得對(duì)應(yīng)zone的service urls爵卒,如有需要可以覆蓋上述getEurekaServerServiceUrls方法以動(dòng)態(tài)獲取service urls,而不是選擇Spring Cloud默認(rèn)從properties文件讀取撵彻。

Self Preservation - 自我保護(hù)

當(dāng)新增Eureka Server時(shí)钓株,他會(huì)先嘗試從其他Peer上獲取所有Eureka Instance的注冊(cè)信息陌僵。如果在獲取時(shí)出現(xiàn)問(wèn)題,該Eureka Server會(huì)在放棄之前嘗試在其他Peer上獲取注冊(cè)信息碗短。如果這個(gè)Eureka Server成功獲取到所有Instance的注冊(cè)信息受葛,那么他就會(huì)根據(jù)所獲取到的注冊(cè)信息設(shè)置應(yīng)該接收到的續(xù)約閾值。如果在任何時(shí)候續(xù)約的閾值低于所設(shè)定的值(在15分鐘[eureka.server.renewal-threshold-update-interval-ms]內(nèi)低于85%[eureka.server.renewal-percent-threshold])偎谁,則該Eureka Server會(huì)出于保護(hù)當(dāng)前注冊(cè)列表的目的而停止將任何Instance進(jìn)行過(guò)期處理。

在Netflix中上述保護(hù)措施被成為自我保護(hù)模式巡雨,主要是用于Eureka Server與Eureka Client存在網(wǎng)絡(luò)分區(qū)情況下的場(chǎng)景。在這種情況下澜建,Eureka Server嘗試保護(hù)其已有的實(shí)例信息蝌以,但如果出現(xiàn)大規(guī)模的網(wǎng)絡(luò)分區(qū)時(shí),相應(yīng)的Eureka Client會(huì)獲取到大量無(wú)法響應(yīng)的服務(wù)跟畅。所以,Eureka Client必須確保對(duì)于一些不存在或者無(wú)法響應(yīng)的Eureka Instance具備更加彈性的應(yīng)對(duì)策略奸攻,例如快速超時(shí)并嘗試其他實(shí)例虱痕。

在網(wǎng)絡(luò)分區(qū)出現(xiàn)時(shí)可能會(huì)發(fā)生以下幾種情況:

Peer之間的心跳可能會(huì)失敗,某Eureka Server檢測(cè)到這種情況并為了保護(hù)當(dāng)前的注冊(cè)列表而進(jìn)入了自我保護(hù)模式硝训。新的注冊(cè)可能發(fā)生在某些孤立的Eureka Server上,某些Eureka Client可能會(huì)擁有新的注冊(cè)列表窖梁,而另外一些則可能沒(méi)有(不同的實(shí)例視圖)。

當(dāng)網(wǎng)絡(luò)恢復(fù)到穩(wěn)定狀態(tài)后邀窃,Eureka Server會(huì)進(jìn)行自我修復(fù)假哎。當(dāng)Peer能正常通信之后注冊(cè)信息會(huì)被重新同步。

最重要的一點(diǎn)是位谋,在網(wǎng)絡(luò)中斷期間Eureka Server應(yīng)該更距彈性,但在這段期間Eureka Client可能會(huì)有不同的實(shí)例視圖笋轨。

作者:Chrisdon

鏈接:http://www.reibang.com/p/4e43acbad7ae

來(lái)源:簡(jiǎn)書

簡(jiǎn)書著作權(quán)歸作者所有赊淑,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钾挟,一起剝皮案震驚了整個(gè)濱河市饱岸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌汤锨,老刑警劉巖百框,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柬泽,居然都是意外死亡嫁蛇,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門琳疏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事新荤。” “怎么了篱瞎?”我有些...
    開(kāi)封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵痒芝,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我澄者,道長(zhǎng)请琳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任询筏,我火速辦了婚禮竖慧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘踱讨。我一直安慰自己碳胳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布味混。 她就那樣靜靜地躺著诫惭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪馆衔。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天拷获,我揣著相機(jī)與錄音减细,去河邊找鬼。 笑死未蝌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的左冬。 我是一名探鬼主播纸型,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼绊袋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了癌别?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤躁垛,失蹤者是張志新(化名)和其女友劉穎圾笨,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體土铺,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡板鬓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年俭令,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抄腔。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绵患,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拷况,我是刑警寧澤掘殴,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布粟誓,位于F島的核電站,受9級(jí)特大地震影響病瞳,放射性物質(zhì)發(fā)生泄漏悲酷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一逗柴、第九天 我趴在偏房一處隱蔽的房頂上張望顿肺。 院中可真熱鬧,春花似錦旷祸、人聲如沸讼昆。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至辫诅,卻和暖如春涧狮,著一層夾襖步出監(jiān)牢的瞬間么夫,已是汗流浹背肤视。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腐螟,地道東北人困后。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓摇予,卻偏偏與公主長(zhǎng)得像汽绢,于是被迫代替她去往敵國(guó)和親侧戴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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