Wasm在即時(shí)通訊IM場(chǎng)景下的Web端應(yīng)用性能提升初探

本文由得物技術(shù)WWQ分享祷舀,原題“基于IM場(chǎng)景下的Wasm初探:提升Web應(yīng)用性能”,下文進(jìn)行了排版和內(nèi)容優(yōu)化烹笔。

1裳扯、什么是Wasm

Wasm,全稱?WebAssembly谤职,官網(wǎng)描述是一種用于基于堆棧的虛擬機(jī)的二進(jìn)制指令格式饰豺。Wasm被設(shè)計(jì)為一個(gè)可移植的目標(biāo),用于編譯C/C++/Rust等高級(jí)語言允蜈,支持在Web上部署客戶端和服務(wù)器應(yīng)用程序冤吨。

簡(jiǎn)單的來說,Wasm就是使用C/C++/Rust等語言編寫的代碼饶套,經(jīng)過編譯后得到匯編指令漩蟆,再通過JavaScript相關(guān)API將文件加載到Web容器中(即運(yùn)行在Web容器中的匯編代碼)。Wasm是一種可移植妓蛮、體積小爆安、加載快速的二進(jìn)制格式,可以將各種編程語言的代碼編譯成Wasm模塊,這些模塊可以在現(xiàn)代瀏覽器中直接運(yùn)行扔仓。尤其在涉及到GPU或CPU計(jì)算時(shí)優(yōu)勢(shì)相對(duì)比較明顯褐奥。

2、Wasm有什么用

JavaScript是解釋型語言翘簇,相比于編譯型語言需要在運(yùn)行時(shí)轉(zhuǎn)換撬码,所以解釋型語言的執(zhí)行速度要慢于編譯型語言。

編譯型語言和解釋型語言代碼執(zhí)行的大致流程如下:

如上流程圖所示:解釋型語言每次執(zhí)行都需要把源碼轉(zhuǎn)換一次才能執(zhí)行版保,而轉(zhuǎn)換過程非常耗費(fèi)時(shí)間和性能呜笑,所以在 JavaScript背景下,Web執(zhí)行一些高性能應(yīng)用是非常困難的彻犁,如視頻剪輯叫胁、3D游戲等。

Wasm具有緊湊的二進(jìn)制格式汞幢,可以接近原生的性能運(yùn)行驼鹅,并為C/C++等語言提供一個(gè)編譯目標(biāo),以便它們可以在Web上運(yùn)行森篷。被設(shè)計(jì)為可以與JavaScript共存输钩,允許兩者一起工作。在特定的業(yè)務(wù)場(chǎng)景下可以完美的彌補(bǔ)JavaScript的缺陷仲智。

3买乃、Wasm的優(yōu)勢(shì)和限制

優(yōu)勢(shì):

1)性能優(yōu)異:相比JavaScript代碼,Wasm使用節(jié)省內(nèi)存钓辆,快速加載和解釋的二進(jìn)制代碼剪验,具備更快執(zhí)行速度,它是直接在底層虛擬機(jī)中運(yùn)行的前联。這使得Web應(yīng)用程序可以更高效地處理復(fù)雜的計(jì)算任務(wù)功戚,例如圖形渲染、物理模擬等宋税;

2)跨平臺(tái)兼容:Wasm可以在幾乎所有現(xiàn)代瀏覽器中運(yùn)行轧膘,兼容性可參考caniuse,無論是桌面還是移動(dòng)設(shè)備。這意味著開發(fā)者可以使用各種編程語言來編寫Web應(yīng)用程序绎秒,而不僅僅局限于JavaScript;

3)安全性:Wasm運(yùn)行在沙箱環(huán)境中汗盘,提供了良好的安全性矩欠。使用了一系列安全措施,如內(nèi)存隔離和沙箱限制幌羞,以防止惡意代碼對(duì)系統(tǒng)的攻擊寸谜;

4)模塊化:Wasm模塊可以作為獨(dú)立的組件進(jìn)行開發(fā)和部署,開發(fā)者可以更好地管理和維護(hù)代碼庫属桦。模塊化的設(shè)計(jì)也為將來的性能優(yōu)化和增量更新提供了便利熊痴。

局限性:

1)生態(tài)系統(tǒng)不夠完善:盡管Wasm已經(jīng)成為Web開發(fā)中的關(guān)鍵技術(shù)之一他爸,但生態(tài)系統(tǒng)仍然不夠完善。Wasm的工具果善、框架和庫的數(shù)量遠(yuǎn)不如JavaScript诊笤;

2)開發(fā)門檻較高:Wasm的開發(fā)門檻相對(duì)較高。Wasm需要使用一種新的語言來編寫巾陕,如C或C++等讨跟。這使得學(xué)習(xí)和使用Wasm的成本相對(duì)較高。尤其是在內(nèi)存管理等方面會(huì)增加開發(fā)的復(fù)雜性鄙煤;

3)與JavaScript集成問題:Wasm與JavaScript之間的集成問題是一個(gè)挑戰(zhàn)晾匠。開發(fā)人員需要解決如何在Web應(yīng)用程序中同時(shí)使用Wasm和JavaScript的問題;

4)兼容性問題:雖然現(xiàn)代瀏覽器已經(jīng)開始支持Wasm梯刚,但是在一些老舊的瀏覽器中可能存在兼容性問題凉馆,需要開發(fā)者進(jìn)行額外的處理來確保代碼的兼容性。

4乾巧、Wasm工作原理

通過上述的編譯型語言和解釋型語言代碼執(zhí)行的大致流程我們可以知道Wasm是不需要被解釋的句喜,是由開發(fā)者提前編譯為WebAssembly二進(jìn)制格式(如下圖所示)。

由于變量類型都是預(yù)知的沟于,因此瀏覽器加載WebAssembly文件時(shí)咳胃,JavaScript引擎無須監(jiān)測(cè)代碼。它可以簡(jiǎn)單地將這段代碼的二進(jìn)制格式編譯為機(jī)器碼旷太。

從這個(gè)流程中我們也可以看出:如果將每種編程語言都直接編譯為機(jī)器碼的各個(gè)版本展懈,這樣效率是不是更高呢?想法是好的供璧,但實(shí)現(xiàn)過程確實(shí)復(fù)雜不堪的存崖。由于瀏覽器是可以在若干不同的處理器(比如手機(jī)和平板等設(shè)備)上運(yùn)行,因此為每個(gè)可能的處理器發(fā)布一個(gè)WebAssembly代碼的編譯后版本會(huì)很難做到睡毒。

我們可以通過替代方法即取得IR代碼来惧。IR即為中間代碼(Intermediate Representation),它是編譯器中很重要的一種數(shù)據(jù)結(jié)構(gòu)演顾。編譯器在做完前端工作以后供搀,首先就生成IR,并在此基礎(chǔ)上執(zhí)行各種優(yōu)化算法钠至,最后再生成目標(biāo)代碼葛虐。

可以簡(jiǎn)化為如下流程:

編譯器將IR代碼轉(zhuǎn)換為一種專用字節(jié)碼并放入后綴為.wasm的文件中。此時(shí)Wasm文件中的字節(jié)碼還不是機(jī)器碼棉钧,它只是支持WebAssembly的瀏覽器能夠理解的一組虛擬指令屿脐。當(dāng)加載到支持WebAssembly的瀏覽器中時(shí),瀏覽器會(huì)驗(yàn)證這個(gè)文件的合法性,然后這些字節(jié)碼會(huì)繼續(xù)編譯為瀏覽器所運(yùn)行的設(shè)備上的機(jī)器碼的诵。

更加詳情的原理和使用方式可以前往https://developer.mozilla.org/en ... avaScript_interface查閱万栅。

5、Wasm應(yīng)用場(chǎng)景

在Web開發(fā)中奢驯,可以使用Wasm來提高應(yīng)用程序的性能申钩。

以下是一些使用Wasm的常見場(chǎng)景:

1)高性能計(jì)算:如果應(yīng)用程序需要進(jìn)行大量的數(shù)值計(jì)算、圖像處理或者復(fù)雜的算法運(yùn)算瘪阁,可以將這部分代碼編譯成Wasm模塊撒遣,以提高計(jì)算性能;

2)游戲開發(fā):Wasm可以用于創(chuàng)建高性能的HTML5游戲管跺,通過將游戲邏輯編譯成Wasm模塊义黎,可以實(shí)現(xiàn)更流暢的游戲體驗(yàn);

3)跨平臺(tái)應(yīng)用:使用Wasm可以實(shí)現(xiàn)跨平臺(tái)的應(yīng)用程序豁跑,無論是桌面還是移動(dòng)設(shè)備廉涕,用戶都可以通過瀏覽器來訪問和使用;

4)移植現(xiàn)有代碼:如果已經(jīng)有用其他編程語言編寫的代碼艇拍,可以通過將其編譯成Wasm模塊狐蜕,將其集成到現(xiàn)有的Web應(yīng)用程序中,而無需重寫整個(gè)應(yīng)用程序卸夕。

6层释、Wasm的應(yīng)用案例

1)設(shè)計(jì)工具Figma-Wasm文件大小為27.7M:

2)Google Earth-Wasm文件總計(jì)大小為192.M(支持各大瀏覽器的3D地圖,而且運(yùn)行流暢):

3)B站-視頻處理和播放也有使用Wasm快集,Wasm文件大小為344kb:

4)跨平臺(tái)的OpenGL圖形引擎Magnum-Wasm文件大小為844kb:

7贡羔、得物的Wasm實(shí)踐

7.1準(zhǔn)備

這里我們通過使用Rust + Wasm實(shí)現(xiàn)Wasm與JavaScript之間的數(shù)據(jù)調(diào)用,理解Rust和Wasm的交互過程个初。

使用Rust就需要做一些前置的環(huán)境配置乖寒,詳情的步驟可參考Rust官網(wǎng):https://www.rust-lang.org/zh-CN/tools/install

安裝wasm-pack院溺,wasm-pack是一個(gè)構(gòu)建楣嘁、測(cè)試和發(fā)布Wasm的Rust CLI工具,我們將使用wasm-pack相關(guān)的命令來構(gòu)建Wasm二進(jìn)制內(nèi)容珍逸。這有助于將代碼編譯為WebAssembly逐虚,并生成在瀏覽器中使用的正確包。

7.2Rust項(xiàng)目初始化

執(zhí)行cargo new rust_wasm初始化Rust項(xiàng)目弄息,自動(dòng)生成配置文件Cargo.toml痊班。

項(xiàng)目結(jié)構(gòu)如下:

/Users/admin/RustroverProjects/rust_wasm

├── Cargo.lock

├── Cargo.toml

├── src

|? └── lib.rs

└── target

???├── CACHEDIR.TAG

???└── debug

??????├── build

??????├── deps

??????├── examples

??????└── incremental

7.3配置包文件

我們可以在Cargo.toml文件中加上下列代碼并保存勤婚,保存之后Cargo會(huì)自動(dòng)下載依賴摹量。

具體是:

1)crate-type = ["cdylib"],表示編譯時(shí)候使用C標(biāo)準(zhǔn)的動(dòng)態(tài)庫;

2)#[wasm_bindgen]是一個(gè)屬性宏缨称,來自于wasm_bindgen這個(gè)crate凝果,是一個(gè)簡(jiǎn)化Rust WASM與JS之間交互的庫。

[lib]

crate-type?= ["cdylib"]


[dependencies]

wasm-bindgen = { version =?"0.2.89", features = [] }

7.4編寫代碼

編寫代碼之前我們先明確Rust中crate包的概念睦尽,Rust中包管理系統(tǒng)將crate包分為二進(jìn)制包(Binary)和庫包(Library)兩種器净,二者可以在同一個(gè)項(xiàng)目中同時(shí)存在。

二進(jìn)制包:

1)main.rs是二進(jìn)制項(xiàng)目的入口当凡;

2)二進(jìn)制項(xiàng)目可直接執(zhí)行山害;

3)一個(gè)項(xiàng)目中二進(jìn)制包可以有多個(gè),所以在Cargo.toml中通過雙方括號(hào)標(biāo)識(shí) [[bin]]沿量。

庫包:

1)lib.rs是庫包的入口浪慌;

2)庫項(xiàng)目不可直接執(zhí)行,通常用來作為一個(gè)模塊被其他項(xiàng)目引用朴则;

3)一個(gè)項(xiàng)目中庫包僅有1個(gè)权纤,在Cargo.toml中通過單方括號(hào)標(biāo)識(shí) [lib]。

因?yàn)槲覀冞@里希望將 Wasm 轉(zhuǎn)為一個(gè)可以在JS項(xiàng)目中使用的模塊乌妒,所以需要使用庫包 lib.rs 的命名汹想,代碼如下。

use wasm_bindgen::prelude::*;

#[wasm_bindgen]

pub extern?"C"?fn rust_add(left: i32, right: i32) -> i32 {

????println!("Hello from Rust!");

????left + right

}

7.5執(zhí)行編譯

這里我們要使用到wasm-pack撤蚊,將上述的Rust代碼編譯為能夠被JS導(dǎo)入的模塊古掏,根據(jù)wasm-pack提供的target方式可以指定構(gòu)建的產(chǎn)物。

如截圖所示:

編譯過程效果:

編譯完成后拴魄,我們會(huì)發(fā)現(xiàn)根目錄下多了一個(gè)pkg/ 文件夾冗茸,里面就是我們的Wasm產(chǎn)物所在的npm包了。

目錄結(jié)構(gòu)如下:

/Users/admin/RustroverProjects/rust_wasm/pkg

├── package.json

├── rust_wasm.d.ts

├── rust_wasm.js

├── rust_wasm_bg.wasm

└── rust_wasm_bg.wasm.d.ts

rust_wasm.d.ts文件內(nèi)容:

/* tslint:disable */

/* eslint-disable */

/**

* @param {number} num

* @returns {string}

*/

export?function?msg_insert(num: number): string;

/**

* @param {number} left

* @param {number} right

* @returns {number}

*/

export?function?rust_add(left: number, right: number): number;

/**

*/

export?function?rust_thread(): void;


export?type?InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;


export?interface InitOutput {

??readonly?memory: WebAssembly.Memory;

??readonly?msg_insert: (a: number, b: number) => void;

??readonly?rust_add: (a: number, b: number) => number;

??readonly?rust_thread: () => void;

??readonly?__wbindgen_add_to_stack_pointer: (a: number) => number;

??readonly?__wbindgen_free: (a: number, b: number, c: number) => void;

}


export?type?SyncInitInput = BufferSource | WebAssembly.Module;

/**

* Instantiates the given `module`,?which?can either be bytes or

* a precompiled `WebAssembly.Module`.

*

* @param {SyncInitInput} module

*

* @returns {InitOutput}

*/

export?function?initSync(module: SyncInitInput): InitOutput;


/**

* If `module_or_path` is {RequestInfo} or {URL}, makes a request and

*?for?everything?else, calls `WebAssembly.instantiate` directly.

*

* @param {InitInput | Promise<InitInput>} module_or_path

*

* @returns {Promise<InitOutput>}

*/

export?default?function?__wbg_init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;

wasm-pack打包不僅輸出一個(gè)ESM規(guī)范的模塊匹中,而且還支持自動(dòng)生成d.ts文件夏漱,對(duì)模塊的使用者非常友好。

如下:

7.6在前端項(xiàng)目中引入使用

'use client'

/*

?* @Author: wangweiqiang

?* @Date: 2024-06-18 17:03:34

?* @LastEditors: wangweiqiang

?* @LastEditTime: 2024-06-18 23:09:55

?* @Description: app.tsx

?*/

import?Image from?"next/image";

import?{ useCallback, useEffect, useState } from?"react";

import?init, * as rustLibrary from?'rust_wasm'

export?default?function?Home() {

??const [addResult, setAddResult] = useState<number | null>(null)

??const [calculateTime, setCalculateTime] = useState<string>('')


??const initRustLibrary = useCallback(() => {

????init().then(() => {

??????const result = rustLibrary.rust_add(5, 6)

??????const timeStamp = rustLibrary.msg_insert(50000)

??????setCalculateTime(timeStamp)

??????setAddResult(result)

????})

??}, [])


??useEffect(() => {

????initRustLibrary()

??}, [initRustLibrary]);


??return?(

????<main className="flex min-h-screen flex-col items-center p-24">

??????{/* .... */}

??????<div className="mt-32 grid text-center lg:mb-0 lg:w-full lg:max-w-5xl lg:grid-cols-4 lg:text-left">

????????<div>

??????????rust代碼計(jì)算結(jié)果:{addResult}

????????</div>


??????????二分法方式{calculateTime}

????????</div>

??????</div>

????</main>

??);

}

7.7在IM場(chǎng)景下的性能比較

在IM場(chǎng)景下顶捷,聊天消息中核心的處理流程在于數(shù)據(jù)的排序挂绰、去重,大量的數(shù)據(jù)查找會(huì)非常耗時(shí)服赎,在這里我們通過二分法的方式對(duì)Rust和JavaScript兩種實(shí)現(xiàn)方式的耗時(shí)進(jìn)行一個(gè)簡(jiǎn)單的對(duì)比葵蒂。

Rust代碼如下:

use chrono::{DateTime, Utc};

use rand::Rng;


#[derive()]

#[allow(dead_code)]

struct Data {

????content: String,

????from: String,

????head: String,

????msg_id: String,

????seq: i32,

????sid: String,

????topic: String,

????ts: DateTime<Utc>,

}


impl Data {

????fn new(

????????content: String,

????????from: String,

????????head: String,

????????msg_id: &str,

????????seq: i32,

????????sid: String,

????????topic: String,

????????ts: DateTime<Utc>,

????) -> Self {

????????Data {

????????????content,

????????????from,

????????????head,

????????????msg_id: msg_id.to_string(),

????????????seq,

????????????sid,

????????????topic,

????????????ts,

????????}

????}

}


//?獲取原始數(shù)據(jù)

fn get_origin_data(num: i32) -> Vec<Data> {

????let?mut data: Vec = vec![];?//?存儲(chǔ)數(shù)據(jù)的向量

????....?//?創(chuàng)建 num 個(gè)數(shù)據(jù)

????data

}

//?初始化結(jié)構(gòu)體數(shù)據(jù)

fn init_struct_data(num: i32, text: &str) -> Data {

????let?mut rng = rand::thread_rng();

????let?content =?format!("{}_{}", rng.gen_range(1000..=9999), text).to_string();

????....

????let?ts = Utc::now();

????Data::new(content, from,?head, &msg_id.as_str(),?seq, sid, topic, ts)

}


//?二分法插入

fn binary_insert(data: &mut Vec<Data>, new_data: Data) {

????let?_insert_pos = match data.binary_search_by_key(&new_data.seq, |d| d.seq) {

????????Ok(pos) => {

????????????data[pos] = new_data;

????????????pos

????????}

????????Err(pos) => {

????????????data.insert(pos, new_data);

????????????pos

????????}

????};

}

#[wasm_bindgen]

pub extern?"C"?fn msg_insert(num: i32) -> String {

????let?mut data: Vec<Data> = get_origin_data(1000);

????let?test_mode = [num];

????let?start_time = Utc::now().naive_utc().timestamp_micros();

????for?test_num?in?0..test_mode.len() {

????????for?num?in?0..test_mode[test_num] {

????????????let?data_list = init_struct_data(num,?"test");

????????????binary_insert(&mut data, data_list);

????????}

????}

????let?duration = Utc::now().naive_utc().timestamp_micros() - start_time;

????let?result =?format!("插入{}條數(shù)據(jù)執(zhí)行耗時(shí):{}微秒", num, duration);

????result

}

數(shù)據(jù)對(duì)比分析:

可以看到:在數(shù)據(jù)量不大的場(chǎng)景下,Wasm的耗時(shí)是比純JavaScript長的重虑,這是因?yàn)闉g覽器需要在VM容器中對(duì) Wasm模塊進(jìn)行實(shí)例化践付,這一部分會(huì)消耗相當(dāng)?shù)臅r(shí)間,導(dǎo)致性能不如純JavaScript的執(zhí)行缺厉。但隨著運(yùn)算規(guī)模變大永高,Wasm的優(yōu)化越來越明顯隧土。這是因?yàn)閃ebAssembly是一種低級(jí)別的二進(jìn)制格式,經(jīng)過高度優(yōu)化命爬,并且能夠更好地利用系統(tǒng)資源曹傀。相比之下,JavaScript是一種解釋性語言饲宛,性能可能會(huì)受到解釋器的限制皆愉。

8、本文小結(jié)

在大多數(shù)場(chǎng)景下我們都不需要用到WebAssembly艇抠。因?yàn)閂8等JS引擎的優(yōu)化帶來了巨大的性能提升幕庐,已經(jīng)足夠讓JavaScript應(yīng)對(duì)絕大多數(shù)的普通場(chǎng)景了,如果要做進(jìn)一步優(yōu)化密集計(jì)算任務(wù)時(shí)使用Web worker也都能解決掉家淤。只有在以上的少數(shù)場(chǎng)景下翔脱,我們才需要做這種“二次提升”。

WebAssembly雖然有天然的優(yōu)勢(shì)媒鼓,但也有自己的局限性届吁,在使用時(shí)我們也需要考慮多方面因素,例如生態(tài)绿鸣、開發(fā)成本等等疚沐。不過我們依然可以持續(xù)關(guān)注WebAssembly的發(fā)展。

9潮模、相關(guān)資料

[1]?一文讀懂前端技術(shù)演進(jìn):盤點(diǎn)Web前端20年的技術(shù)變遷史

[2]?新手入門貼:史上最全Web端即時(shí)通訊技術(shù)原理詳解

[3]?Web端即時(shí)通訊技術(shù)盤點(diǎn):短輪詢亮蛔、Comet、Websocket擎厢、SSE

[4]?新手快速入門:WebSocket簡(jiǎn)明教程

[5]?WebSocket詳解(六):刨根問底WebSocket與Socket的關(guān)系

[6]?WebSocket從入門到精通究流,半小時(shí)就夠!

[7]?搞懂現(xiàn)代Web端即時(shí)通訊技術(shù)一文就夠:WebSocket动遭、socket.io芬探、SSE

[8]?詳解Web端通信方式的演進(jìn):從Ajax、JSONP 到 SSE厘惦、Websocket

[9]?從理論到實(shí)踐偷仿,詳細(xì)對(duì)比Electron和Tauri的優(yōu)劣

[10]?快速對(duì)比跨平臺(tái)框架Electron、Flutter宵蕉、Tauri酝静、React Native等

10、得物技術(shù)團(tuán)隊(duì)其它文章

得物從0到1自研客服IM系統(tǒng)的技術(shù)實(shí)踐之路

得物自研客服IM中收發(fā)聊天消息背后的技術(shù)邏輯和思考實(shí)現(xiàn)

得物從零構(gòu)建億級(jí)消息推送系統(tǒng)的送達(dá)穩(wěn)定性監(jiān)控體系技術(shù)實(shí)踐

得物基于Electron開發(fā)客服IM桌面端的技術(shù)實(shí)踐

得物自研移動(dòng)端弱網(wǎng)診斷工具的技術(shù)實(shí)踐分享

得物移動(dòng)端常見白屏問題優(yōu)化(網(wǎng)絡(luò)優(yōu)化篇)

(本文已同步發(fā)布于:http://www.52im.net/thread-4742-1-1.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末羡玛,一起剝皮案震驚了整個(gè)濱河市别智,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稼稿,老刑警劉巖薄榛,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浓若,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蛇数,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門是越,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耳舅,“玉大人,你說我怎么就攤上這事倚评∑只玻” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵天梧,是天一觀的道長盔性。 經(jīng)常有香客問我,道長呢岗,這世上最難降的妖魔是什么冕香? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮后豫,結(jié)果婚禮上悉尾,老公的妹妹穿的比我還像新娘。我一直安慰自己挫酿,他們只是感情好构眯,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著早龟,像睡著了一般惫霸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葱弟,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天壹店,我揣著相機(jī)與錄音,去河邊找鬼芝加。 笑死茫打,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妖混。 我是一名探鬼主播老赤,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼制市!你這毒婦竟也來了抬旺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤祥楣,失蹤者是張志新(化名)和其女友劉穎开财,沒想到半個(gè)月后汉柒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡责鳍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年碾褂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片历葛。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡正塌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恤溶,到底是詐尸還是另有隱情乓诽,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布咒程,位于F島的核電站鸠天,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏帐姻。R本人自食惡果不足惜稠集,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饥瓷。 院中可真熱鬧巍杈,春花似錦、人聲如沸扛伍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刺洒。三九已至鳖宾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逆航,已是汗流浹背鼎文。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留因俐,地道東北人拇惋。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像抹剩,于是被迫代替她去往敵國和親撑帖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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