插件化技術(shù)調(diào)研

插件化技術(shù)調(diào)研

Time: 2018.12.04

調(diào)研背景

最近實驗室的項目有插件化的需求,所以讓我對插件化相關(guān)技術(shù)進行調(diào)研。

這篇文章對調(diào)研報告稍加修改萍启,作為自己對這次調(diào)研的記錄余寥。

背景與問題描述
如果你的軟件是傳統(tǒng)銷售模式怎爵,即將一套軟件完整賣給不同客戶永罚。

那么不同客戶對系統(tǒng)的需求會有所不同,通常會有一定的定制化需求卧秘,例如增加一些新功能呢袱、完善原有功能、修改皮膚樣式等翅敌。

因此面對不同客戶時羞福,需要在原有系統(tǒng)的基礎(chǔ)上進行不同程度的修改和二次開發(fā)。

而隨著客戶的增多蚯涮,將導致系統(tǒng)在版本治专、功能、需求等方面的管理變得愈加繁雜遭顶,其中也可能存在重復開發(fā)张峰、功能無法重用、開發(fā)工作量過大等問題棒旗。

因此需要尋求一種解決方案使得系統(tǒng)的開發(fā)更加靈活喘批,即實現(xiàn)可定制化的目標,實現(xiàn)以更少的開發(fā)工作量應對變化的需求铣揉。

目標
實現(xiàn)可定制化的目標饶深,實現(xiàn)以更少的開發(fā)工作量應對變化的需求。

實現(xiàn)路徑
主要實現(xiàn)路徑:系統(tǒng)插件化

附錄:對可定制化的理解

  • 從客戶角度來看逛拱,實現(xiàn)最終對外的系統(tǒng)服務可定制化敌厘,有眾多方案。因為對于客戶朽合,系統(tǒng)的內(nèi)部實現(xiàn)是一個黑箱俱两,只要對外的服務可供選擇和定制即可。
  • 從系統(tǒng)開發(fā)的角度來看旁舰,實現(xiàn)系統(tǒng)服務的可定制化的目的就是為了以更少的開發(fā)工作量應對變化的需求锋华,本質(zhì)上需要完成如下任務:
    • 對系統(tǒng)進行合理切分,且應當保證切分出來的各部分耦合度足夠低箭窜,例如模塊化毯焕、組件化、插件化
    • 提供各個部分之間交互、通信的機制

從上面的思考來看纳猫,實現(xiàn)可定制化也可有其它路徑:

  • 微服務
  • EJB
  • 模塊化

當然關(guān)鍵在于系統(tǒng)的 上是否合理和優(yōu)雅婆咸。其實思想就是只要系統(tǒng)的功能模塊劃分的足夠合理,耦合度足夠低芜辕,就已經(jīng)完成可定制化的一半了尚骄,之后再利用動態(tài)加載等技術(shù)可很容易完成可定制化。如果劃分的這一步不夠合理侵续,那加上插件化框架其實也就是多加了個殼而已倔丈。

插件化技術(shù)基礎(chǔ)

什么是插件?

Plug-in (computing) From Wikipedia
In computing, a plug-in (or plugin, add-in, addin, add-on, addon, or extension) is a software component that adds a specific feature to an existing computer program. When a program supports plug-ins, it enables customization.
譯:即插件是一種軟件組件状蜗,該組件主要用來在原有系統(tǒng)上添加一些擴展功能需五。插件將使得系統(tǒng)具有定制化的能力。

當一個軟件系統(tǒng)開發(fā)完成后轧坎,可能需要添加一些額外的新功能(有時這些新功能也被稱為擴展)宏邮,我們通常希望能夠在不影響原有系統(tǒng)的條件下完成新功能的添加,實現(xiàn)這一目標的并是所謂的插件化缸血,新增加的功能模塊就叫插件蜜氨。

插件化有利于降低模塊間的耦合度,有利于各模塊和項目的維護更新捎泻。

典型的插件例如 Chrome飒炎、Firefox 瀏覽器插件、Eclipse 插件族扰、Vscode 等各種 IDE 插件厌丑,各種軟件提供的主題或皮膚也是一種插件。

關(guān)于組件渔呵、插件與控件怒竿,以及組件化、插件化扩氢、模塊化這些概念可參見本文末尾的附錄耕驰。

插件化實現(xiàn)技術(shù)

不論是自己設(shè)計實現(xiàn)系統(tǒng)的插件化還是直接應用現(xiàn)成的插件化框架,都會涉及到一些基本的基礎(chǔ)技術(shù)录豺。下面對這些技術(shù)做一下基本介紹和整理朦肘。

動態(tài)加載

插件化系統(tǒng)的特點之一就是實現(xiàn)各個插件的可插拔,系統(tǒng)可靈活的動態(tài)拆分與組合双饥。要實現(xiàn)這一特點就離不開動態(tài)加載的概念媒抠。

Dynamic loading From Wikipedia
Dynamic loading is a mechanism by which a computer program can, at run time, load a library (or other binary) into memory, retrieve the addresses of functions and variables contained in the library, execute those functions or access those variables, and unload the library from memory.
譯:動態(tài)加載是一種讓系統(tǒng)在運行時加載并使用其他軟件的機制。

動態(tài)加載在不同系統(tǒng)咏花、平臺有不同的具體實現(xiàn)趴生,如果你的系統(tǒng)主要運行在 Java 平臺之上阀趴,那么就要著重關(guān)注 Java 的動態(tài)加載。

Java 的動態(tài)加載主要依靠 ClassLoader苍匆。

Java 源碼編譯成字節(jié)碼 class 文件刘急,class 文件是 JVM 平臺上的基本程序單位,而 ClassLoader 的作用就是加載 class 文件浸踩。Java 類加載的完整生命周期包括加載叔汁、驗證、準備(連接)检碗、解析据块、初始化、使用折剃、卸載瑰钮。

并且 Java 的 ClassLoader 在加載類時采用的是雙親委派機制(實現(xiàn)類加載的共享和隔離),如下圖所示:

類加載器雙親委派模型_1.png

Java 具有四種類型的 ClassLoader:

  • Bootstrap ClassLoader:加載 jre/lib/rt.jar 或 Xbootclassoath 選項指定的 jar 包微驶。
  • Extension ClassLoader:加載 jre/lib/*.jar 或 -Djava.ext.dirs 指定目錄下的 jar 包。
  • Application ClassLoader:加載 classpath 或 Djava.class.path 所指定目錄下的類和jar包开睡。
  • Custom ClassLoader:用戶自定義的 ClassLoader

但是在具體實現(xiàn)中有可能為特定需求(如頂層 ClassLoader 調(diào)用底層 ClassLoader 負責的類)破壞雙親委派模型因苹。

反射

既然已經(jīng)實現(xiàn)類的動態(tài)加載,那么為了訪問以及使用動態(tài)加載類中的屬性篇恒、方法就必須應用到反射技術(shù)扶檐,所以反射也是實現(xiàn)插件化過程中會頻繁涉及到的技術(shù)之一。

反射技術(shù)可講的內(nèi)容很多胁艰,自己在實驗室讀研期間做的幾個核心項目都與反射息息相關(guān)款筑,所以估計以后會專門寫一篇詳細介紹反射的文章。

而文本主要集中在插件化技術(shù)腾么,所以反射在這里就不做過多介紹了奈梳。

這里只稍微提及一下反射最重要的核心思想和關(guān)鍵:獲取類的元信息。

Java 中類的元信息編譯到了 class 文件中解虱,運行時加載至內(nèi)存模型的元空間區(qū)域攘须,所以 Java 本身可以很容易的支持反射技術(shù)。

而 Python 這樣的動態(tài)語言就更容易記錄和使用類的元信息殴泰,所以反射的實現(xiàn)也很自然于宙。

而 c/c++ 因為其編譯結(jié)果只有變量、函數(shù)地址偏移悍汛、函數(shù)關(guān)系捞魁,所以只能在運行時構(gòu)建函數(shù)名稱等元信息與地址之間的映射關(guān)系,以此實現(xiàn)反射离咐,而 c/c++ 在語言層面并未幫我們做這一工作谱俭,所以需要程序自己實現(xiàn),例如 ProtoBuf 的反射實現(xiàn)。

動態(tài)代理

在實現(xiàn)插件化的過程中旺上,有時需要拿到系統(tǒng)或平臺層面方法的控制權(quán)瓶蚂,從而獲得更為強大的能力。典型的就是在 android 平臺上實現(xiàn)插件化宣吱,有時候需要 hook 部分系統(tǒng)方法才能實現(xiàn)對資源的動態(tài)加載窃这。

在 Java 平臺上,常用的動態(tài)代理方式有:

  • JDK 動態(tài)代理:初始創(chuàng)建速度相對較快征候,創(chuàng)建代理后的運行速度相對較慢
  • CGLib 動態(tài)代理:初始創(chuàng)建速度相對較慢杭攻,創(chuàng)建代理后的運行速度相對較快

其他相關(guān)技術(shù)

除動態(tài)加載之外,還有其他一些調(diào)用組件的方法疤坝,同樣可應用于插件化:

  • 靜態(tài)鏈接:在編譯鏈接兆解,直接將需要的其他模塊的執(zhí)行代碼拷貝到調(diào)用處。
  • 動態(tài)鏈接:在編譯時只記錄一系列符號和參數(shù)跑揉,在程序運行或加載時將這些信息傳遞給操作系統(tǒng)锅睛,操作系統(tǒng)負責將需要的動態(tài)庫加載到內(nèi)存中,然后程序在運行到指定的代碼時历谍,去共享執(zhí)行內(nèi)存中已經(jīng)加載的動態(tài)庫可執(zhí)行代碼现拒。例如 Windows 的 dll 文件,Linux 下的 so 文件望侈,動態(tài)鏈接也提供了系統(tǒng)動態(tài)擴展功能印蔬、可插拔的插件化能力。

除上述技術(shù)之外脱衙,實現(xiàn)插件化過程中還需要考慮插件之間的通信(通過反射拿到引用侥猬、面向服務)、插件的描述和管理捐韩、插件的狀態(tài)和生命周期等

插件化框架

除了上面介紹的實現(xiàn)插件化的基礎(chǔ)技術(shù)退唠,要完整的實現(xiàn)插件化甚至搭建一個插件化框架,還需要做很多關(guān)于插件管理奥帘、插件狀態(tài)铜邮、插件生命周期定義等工作。其實也就是實現(xiàn)一個框架所要考慮和設(shè)計的種種細節(jié)寨蹋。

借助上面介紹的動態(tài)加載松蒜、反射等技術(shù),再做好業(yè)務系統(tǒng)的模塊劃分已旧,那么完全可以自己動手實現(xiàn)系統(tǒng)插件化了秸苗。

但是實際情況是業(yè)界已經(jīng)存在很多比較成熟和實用的框架甚至是規(guī)范了,下面是對一些框架的調(diào)研結(jié)果运褪。

插件化框架列表:

  • OSGI
    • Felix
    • Equinox
    • Makewave Knopflerfish
    • Spring DM
    • Gemini
  • 其他插件化框架
    • Hudson(for Jenkins)
    • JSPF
    • pf4j

OSGI

OSGI - Open Services Gateway initiative

OSGI From Wikipedia
The OSGi specification describes a modular system and a service platform for the Java programming language that implements a complete and dynamic component model, something that does not exist in standalone Java/VM environments.

即 OSGI 是 Java 的動態(tài)模塊化系統(tǒng)的規(guī)范惊楼。

OSGi 將使開發(fā)者能夠構(gòu)建動態(tài)化玖瘸、模塊化的 Java 系統(tǒng),系統(tǒng)的每個模塊將可以像插件一樣實現(xiàn)“可插拔”檀咙、“即插即用”雅倒。

OSGI 框架如下圖所示:

layering-osgi.png

從圖中可知,OSGI 框架從概念上可以劃分為以下幾個層次:

  • Bundles
    開發(fā)者開發(fā)的 OSGi 組件弧可,即 OSGI 解析單位蔑匣,其中包含 Class 文件、資源文件棕诵、元數(shù)據(jù)(MANIFEST.MF)
  • Services
    Services 層以 publish-find-bind (發(fā)布-綁定-查詢)的機制動態(tài)連接 bundles裁良。主要涉及模塊之間的交互和通信。
  • Life-Cycle
    提供安裝校套、啟動价脾、停止、更新笛匙、卸載 bundles 等關(guān)于生命周期的 API
  • Modules
    定義 bundle 如何導入和導出代碼的層侨把,即涉及到包及共享的代碼
  • Security
    安全層,提供安全機制
  • Execution Environment
    定義特定平臺下哪些方法和類是可用的

bundle
Bundle 是 OSGi 中的基本組件妹孙,其表現(xiàn)形式仍然為 Java 概念中傳統(tǒng)的 Jar 包座硕,其中包含了代碼、資源文件和元數(shù)據(jù)涕蜂。
通過 META-INF 目錄下的 MANIFEST.MF 文件記錄 bundle 的元數(shù)據(jù)描述信息。
例如:

 Bundle-Name: Hello World
 Bundle-SymbolicName: org.wikipedia.helloworld
 Bundle-Description: A Hello World bundle
 Bundle-ManifestVersion: 2
 Bundle-Version: 1.0.0
 Bundle-Activator: org.wikipedia.Activator  // 激活包后所要調(diào)用的類名
 Export-Package: org.wikipedia.helloworld;version="1.0.0" // 導出的包
 Import-Package: org.osgi.framework;version="1.3.0" // 導入的包

OSGI 為每個 bundle 創(chuàng)建獨立的 ClassLoader映琳,這樣使得各個 bundle 的類盡可能相互隔離(包名 + 類名 + ClassLoader 才能唯一確定一個類)机隙,從而降低 bundle 之間的耦合度。

Services
主要涉及模塊之間的交互和通信萨西。
bundle 之間的交互可通過以下幾種方式:

  • 面向服務式: 實現(xiàn)服務注冊中心提供 bundle 的注冊有鹿、查詢、注銷等服務
  • Event 服務: 采用發(fā)布-訂閱的方式調(diào)用所需要的 bundle

OSGI 框架

OSGI 框架即可視為實現(xiàn) OSGI 規(guī)范的容器谎脯,如同 Web 開發(fā)中實現(xiàn)了 J2EE 規(guī)范的 Web 容器葱跋。

下面介紹幾款 OSGI 框架。

felix
felix 官方網(wǎng)站
felix 是由 Apache 基金組織開發(fā)的面向社區(qū)的 OSGi 框架實現(xiàn)源梭,提供了標準的服務以及 OSGi 相關(guān)的服務實現(xiàn)娱俺。

Felix 項目比較成熟且在很多其他項目中得到應用,提供服務全面废麻,涵蓋了全部的 OSGi 4.2 標準荠卷。社區(qū)活躍,文檔豐富烛愧∮鸵耍框架設(shè)計緊湊掂碱,上手簡單。

Equinox
Equinox 官方網(wǎng)站-1
Equinox 官方網(wǎng)站-2
Equinox 是 Eclipse 所使用的 OSGi 框架慎冤,Eclipse 使用這個框架實現(xiàn)了它強大的插件系統(tǒng)疼燥,目前是 Eclipse 項目下的子項目。

Equinox 與 Eclipse 結(jié)合緊密蚁堤,Eclipse 內(nèi)置大量插件醉者、功能、操作等违寿,且配備專門的 Debug 工具湃交,可非常方便的創(chuàng)建、管理藤巢、開發(fā)搞莺、調(diào)試以及部署 Equinox 項目。

雖然 Equinox 功能也算全面掂咒,但是對比 Felix 相對較少才沧。

其他 OSGI 框架

  • Makewave Knopflerfish:OSGi的先行者,但是后續(xù)發(fā)展和維護不足绍刮,整體不如 Felix温圆、Equinox 等主要框架
  • Spring DM: Spring 旗下的 OSGi 框架,最大特點就是結(jié)合了 Spring 框架孩革∷昵福可以和 Spring 家族的各個框架結(jié)合的更好。但是 Spring DM 已經(jīng)在 2009 年底轉(zhuǎn)換成 Eclipse 下的 Gemini 項目
  • Gemini: Eclipse 下的另一個 OSGI 子項目膝蜈,相比于其它幾個框架锅移,Gemini 有專門針對 Web 的子項目(Gemini Web),但是該項目最近更新是在 2012 年饱搏,應該已經(jīng)停止更新和維護了非剃。

其他插件化框架

  • JSPF: Java Simple Plugin Framework,一款十分輕量級推沸、簡單的插件框架备绽,但是最新版本發(fā)布是在 2011 年五月,基本已經(jīng)停止更新和維護鬓催。
  • pf4j: Plugin Framework for Java肺素,開源輕量級插件化框架。源碼可訪問 GitHub 倉庫宇驾,支持 Spring 以及 web 應用压怠。

插件化方案

綜上,可總結(jié)以下幾種主要的飞苇、可選的插件化方案菌瘫。

一蜗顽、自定義插件化框架

即利用【插件化技術(shù)基礎(chǔ)】一節(jié)中提到的實現(xiàn)插件化的基礎(chǔ)技術(shù)如動態(tài)加載、反射雨让、動態(tài)代理等基礎(chǔ)自己動手編寫符合我們項目的插件化框架雇盖。在開發(fā)過程中,應該需要解決和設(shè)計好以下幾個方面:

  • 插件狀態(tài)栖忠、依賴關(guān)系等靜態(tài)描述
  • 插件生命周期管理崔挖,包括加載、運行庵寞、卸載的等
  • 插件之間的通信狸相,可通過服務注冊中心、事件發(fā)布與訂閱模式捐川、反射獲取引用等方式
  • Web 應用插件化還要著重資源文件的管理

具體設(shè)計還可參考現(xiàn)有的插件化系統(tǒng)脓鹃,詳見本文附錄部分的【現(xiàn)有插件化系統(tǒng)架構(gòu)參考】一節(jié)。

這一方案的優(yōu)點是可以根據(jù)項目系統(tǒng)的實際情況開發(fā)簡單古沥、完全可控瘸右、靈活、定制化岩齿、滿足需求的插件化框架太颤。而缺點就是具有一定的開發(fā)工作量且最終開發(fā)效果無法預計,預估至少需要 3 個月以上的開發(fā)盹沈、調(diào)試龄章、整合等。

二乞封、使用 Felix 框架

借助于 Felix 框架瓦堵,可以很方便的開發(fā)插件化系統(tǒng)(當然重點和主要的工作量在于系統(tǒng)模塊化的設(shè)計)。

更為詳細的使用教程可參見如下文章:
Felix Documentation
Java Web應用集成OSGI
OSGi簡介與基于OSGi框架(Felix)的簡單應用實現(xiàn)

三歌亲、使用 Equinox 框架

與 Felix 類似,具體使用教程可參見如下文章:
Writing a bundle-based server application
OSGi as a Web Application Server
使用 Equinox 框架進行 OSGi 環(huán)境下的 Web 開發(fā)
使用 Equinox 開發(fā) OSGi 應用程序

對上述方案總結(jié)如下表所示:

方案 規(guī)范 活躍度 工作量
自定義插件化框架 自定義 預估 6 個月以上
使用 Felix 框架 OSGi 最高 預估 3 個月
使用 Equinox 框架 OSGi 預估 3 個月

附錄

調(diào)研過程中的額外知識點整理

組件澜驮、插件與控件

  • 組件:組件(Component)是是一個含義很大的概念陷揪,一般是指軟件系統(tǒng)的一部分,承擔了特定的職責杂穷,可以獨立于整個系統(tǒng)進行開發(fā)和測試悍缠,一個良好設(shè)計的組件應該可以在不同的軟件系統(tǒng)中被使用(可復用)。例如 V8 引擎是 Chrome 瀏覽器的一部分耐量,負責運行 javascript 代碼飞蚓,這里 V8 引擎就可以視為一個組件。V8 引擎同時也是 Node.js 的 javascript 解釋器廊蜒,這體現(xiàn)了組件的可復用性趴拧。
  • 插件:插件(或擴展)是對已有應用程序或者庫的功能補充溅漾,一個軟件的插件(或擴展)是實現(xiàn)了該軟件預定義接口的組件,用來向已有的軟件添加功能著榴。插件在目標軟件發(fā)布時可以不預先包含添履,而是在運行時被使用者注冊,然后再被目標軟件調(diào)用脑又。
  • 控件:gui 編程的一個概念暮胧,一般來說一個最終用戶可以看到的、可交互的組件问麸,被稱為一個控件往衷。

來自 https://www.zhihu.com/question/49536781/answer/117606933

組件化、插件化严卖、模塊化

組件化與模塊化沒有太多必要做名詞上的區(qū)分席舍,他們的目的都是實現(xiàn)系統(tǒng)的低耦合和可復用。
插件化:比起組件化和模塊化妄田,插件化更加強調(diào)對現(xiàn)有功能的靈活擴展俺亮。主要優(yōu)點是面向需求變更時的開發(fā)靈活性。
系統(tǒng)實現(xiàn)了組件化與模塊化疟呐,并不一定支持插件化脚曾。

現(xiàn)有插件化系統(tǒng)架構(gòu)參考

Eclipse Plug-in Architecture
Confluence Plug-in Architecture
Hudson Plugin Architecture
ElasticSearch Plugin
joomla plugin

參考資料

osgi-architecture
Notes on the Eclipse Plug-in Architecture
Hudson Plugin Architecture
Converting an EJB JAR file to an OSGi EJB bundle
Felix 官方網(wǎng)站
Equinox 官方網(wǎng)站
Introduction to Maven Plugin Development
Java Web application “plugin” architecture

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市启具,隨后出現(xiàn)的幾起案子本讥,更是在濱河造成了極大的恐慌,老刑警劉巖鲁冯,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拷沸,死亡現(xiàn)場離奇詭異,居然都是意外死亡薯演,警方通過查閱死者的電腦和手機撞芍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跨扮,“玉大人序无,你說我怎么就攤上這事『獯矗” “怎么了帝嗡?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長璃氢。 經(jīng)常有香客問我哟玷,道長,這世上最難降的妖魔是什么一也? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任巢寡,我火速辦了婚禮喉脖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘讼渊。我一直安慰自己动看,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布爪幻。 她就那樣靜靜地躺著菱皆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挨稿。 梳的紋絲不亂的頭發(fā)上仇轻,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音奶甘,去河邊找鬼篷店。 笑死,一個胖子當著我的面吹牛臭家,可吹牛的內(nèi)容都是我干的疲陕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼钉赁,長吁一口氣:“原來是場噩夢啊……” “哼蹄殃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起你踩,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤诅岩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后带膜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吩谦,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年膝藕,在試婚紗的時候發(fā)現(xiàn)自己被綠了式廷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡芭挽,死狀恐怖滑废,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情览绿,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布穗慕,位于F島的核電站饿敲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏逛绵。R本人自食惡果不足惜怀各,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一倔韭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瓢对,春花似錦寿酌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至法焰,卻和暖如春秧荆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背埃仪。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工乙濒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卵蛉。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓颁股,卻偏偏與公主長得像,于是被迫代替她去往敵國和親傻丝。 傳聞我的和親對象是個殘疾皇子甘有,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

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