基于redis和websocket的聊天室

先附上兩個(gè)項(xiàng)目的github地址,非常簡單的實(shí)現(xiàn)
單機(jī)存儲(chǔ)基于redis和websocket的聊天室: https://github.com/g992987642/redis-chat
線上地址: 單機(jī)存儲(chǔ)基于redis和websocket的聊天室
redis中的發(fā)布/訂閱功能在聊天室中的應(yīng)用: https://github.com/g992987642/redis-chat-pubsub

本文主要介紹了在寫聊天室的時(shí)候,項(xiàng)目的頁面展示屿脐、需求的分析炊汤、遇到的坑或沒接觸過的知識(shí)點(diǎn)攒盈,都附上了從0開始學(xué)習(xí)的鏈接,非常簡單易懂從上到下順序?yàn)椋?strong>跑一遍項(xiàng)目了解大概功能后帶著問題來看效果更佳
1.頁面展示
2.項(xiàng)目中使用的技術(shù)
3.聊天窗口的需求分析
4.項(xiàng)目中的自定義異常類和Springboot全局異臣樱控制
5.項(xiàng)目中的統(tǒng)一請(qǐng)求響應(yīng)格式封裝類
6.項(xiàng)目中的WebSocket的應(yīng)用(包括redis的中pub/sub的應(yīng)用)
7.項(xiàng)目中的Fastjson的使用
8.項(xiàng)目中的Springboot操作redis與redis中key的命名規(guī)范
9.項(xiàng)目中的spring的定時(shí)任務(wù)的應(yīng)用
10.項(xiàng)目中的Slf4j打印日志和lombok插件
11.項(xiàng)目中遇到的問題

1.頁面展示

注冊(cè)頁面:

(O[P0X667NO`][EA]GBHLND.png

聊天頁面:
HM8CY_I1GM41ET0QRF5_(Q3.png

2.用到的技術(shù):

1.Springboot的IOC和SpringMVC的注解
2.spring整合redis的工具類Redistemplate粹污,通過高度封裝的Redistemplate來操作redis
3.redis(其中String數(shù)據(jù)結(jié)構(gòu)的使用,pub/sub的應(yīng)用)
4.websocket代替前端的輪詢來接收新消息
5.Springboot中的全局異呈琢浚控制和自定義異常類
6.Springboot的定時(shí)任務(wù)
7.lombok插件(針對(duì)實(shí)體類的get set toString 等方法的注解壮吩,可以減少冗余)
8.Slf4j日志打印插件(配合lombok效果更佳)
9.Fastjson插件的使用
10.自定義請(qǐng)求響應(yīng)格式封裝

學(xué)習(xí)中主要接觸到的新知識(shí)點(diǎn):

數(shù)據(jù)存儲(chǔ)在redis中,上線通知加缘、聊天用websocket鸭叙。
記得使用統(tǒng)一的請(qǐng)求響應(yīng)格式封裝
既然用到了redis,主要是學(xué)習(xí)redis的應(yīng)用拣宏,那么就寫點(diǎn)關(guān)于redis功能相關(guān)的沈贝。
比如redis的增刪改查,模糊查詢勋乾,
Springboot的定時(shí)任務(wù)宋下,全局異常控制

3.需求分析:

聊天的controller(主要是redis的操作)
1.左上角有自己的User對(duì)象信息 (獲得自己的信息辑莫,從redis中查)
2.左下側(cè)有已經(jīng)上線的人的User對(duì)象(只需要頭像和id学歧,有websocket的連接就是上線的人,獲得id去redis中查人的信息) (獲得上線的人的信息)
3.存在的群組的信息 (獲得群組的信息)
4.聊天框應(yīng)分為與群組的聊天記錄 (獲得群組的聊天記錄)
5.與單人的聊天記錄 (獲得單人的聊天記錄)
6.發(fā)送和接收消息的功能 (發(fā)送對(duì)應(yīng)人(包括群組)的方法)最重要8鞫帧VΡ俊!

發(fā)送和接收消息的功能分析:

發(fā)送消息時(shí)先判斷對(duì)方在不在線,如果在線(全局存儲(chǔ)session的map中是否有這個(gè)id存在)横浑,發(fā)送消息(發(fā)送消息需要調(diào)用websocket中由OnMessage修飾的方法剔桨,才能實(shí)時(shí)通知到收消息的人),把消息存到redis中(發(fā)送和存儲(chǔ)應(yīng)該是個(gè)原子操作徙融?)
補(bǔ)充:發(fā)送的消息應(yīng)該有時(shí)間戳洒缀,利用date()方法把時(shí)間加到每個(gè)消息加進(jìn)消息的實(shí)體類中
如果不在線,可以拋出自定義異常张咳,由@RestControllerAdvice修飾的統(tǒng)一異常處理器捕獲之后return帝洪。

4.自定義異常類

與普通實(shí)體類無區(qū)別,只需要繼承RuntimeException就行脚猾,里面有String對(duì)象用來描述異常葱峡。
關(guān)于@ControllerAdvice:

1.控制器增強(qiáng) spring初始化的時(shí)候可以掃描到該注解,@ControllerAdvice注解內(nèi)部使用@ExceptionHandler龙助、@InitBinder砰奕、@ModelAttribute注解的方法應(yīng)用到所有的 @RequestMapping注解的方法。非常簡單提鸟,不過只有當(dāng)使用@ExceptionHandler最有用军援,另外兩個(gè)用處不大
2.可以利用該注解實(shí)現(xiàn)異常全局統(tǒng)一處理,不必在單個(gè)controller中去(try-catch)處理
3.RestControllerAdvice 與ControllerAdvice類似 參考RestController和Controller的區(qū)別

@ControllerAdvice學(xué)習(xí)的鏈接: https://blog.csdn.net/Colton_Null/article/details/84592748

踩過的坑

1.try -catch優(yōu)先級(jí)高于@ControllerAdvice称勋,有try -catch不會(huì)被全局異常處理器捕獲胸哥,異常處理器只能捕獲最終在controller層拋出的異常,(dao,service層的異常都會(huì)向上拋到controller層被捕獲赡鲜,但對(duì)例如 Interceptor(攔截器)層的異常空厌、定時(shí)任務(wù)中的異常、異步方法中的異常银酬,不會(huì)進(jìn)行處理)
2.@RestControllerAdvice修飾的類和其他非bean文件放在一起可能不會(huì)生效嘲更,單獨(dú)建一個(gè)包或者放到 已有bean的文件夾。

5.統(tǒng)一請(qǐng)求響應(yīng)格式封裝類

1.首先需要有這樣的實(shí)體類揩瞪,里面的data放了需要返回的具體數(shù)據(jù)赋朦,code是返回的狀態(tài)碼,msg是success/error這種返回成功李破,出現(xiàn)異常等描述宠哄。


S0RAYXDQ_9}RDU1IK`G37PY.png

2.在每次調(diào)用controller方法后,返回的就是這個(gè)實(shí)體類喷屋。

$6WPOGHIG@[A]45GH~Y1`35.png

3.在前端的js中琳拨,會(huì)有方法接收到這個(gè)實(shí)體類,然后判斷發(fā)送成功與否屯曹。

)TG~0@{462EYCSBT@7`LRJG.png

6.WebSocket的使用

如果沒有接觸過WebSocket狱庇,我想下面幾個(gè)鏈接應(yīng)該會(huì)幫助到你惊畏。

websocket的各個(gè)方法詳解:
https://blog.csdn.net/zilaike/article/details/78227810
WebSocket實(shí)現(xiàn)服務(wù)器端消息推送的結(jié)構(gòu):(這兩個(gè)鏈接都是Springboot整合WebSocket的用法,有小的差異密任,可以互相印證)
https://blog.csdn.net/cwr452829537/article/details/91580331
https://www.cnblogs.com/bianzy/p/5822426.html
首先要注入ServerEndpointExporter(也就是再兩篇文章一開始都創(chuàng)建的一個(gè)WebSocketConfig颜启,然后在里面創(chuàng)建一個(gè)ServerEndpointExporter交給Spring管理),這個(gè)bean會(huì)自動(dòng)注冊(cè)使用了@ServerEndpoint注解聲明的Websocket endpoint浪讳。
要注意缰盏,如果使用獨(dú)立的servlet容器,而不是直接使用springboot的內(nèi)置容器淹遵,就不要注入ServerEndpointExporter因?yàn)樗鼘⒂扇萜髯约禾峁┖凸芾恚?否則就會(huì)報(bào)重復(fù)的endpoint錯(cuò)誤口猜。

1.為什么需要 WebSocket?

初次接觸 WebSocket 的人透揣,都會(huì)問同樣的問題:我們已經(jīng)有了 HTTP 協(xié)議济炎,為什么還需要另一個(gè)協(xié)議?它能帶來什么好處辐真?
答案很簡單须尚,因?yàn)?HTTP 協(xié)議有一個(gè)缺陷:通信只能由客戶端發(fā)起,HTTP 協(xié)議做不到服務(wù)器主動(dòng)向客戶端推送信息侍咱。
websocket連接通知的實(shí)現(xiàn) (websocket類中配置的url映射應(yīng)該在類上面而不是方法上耐床,用@ServerEndpoint(value = "/chat/{id}")類似的來映射)

2.WebSocket中的Session

介紹: Websocket中有一個(gè)session(不同于httpsession,這個(gè)是屬于websocket的)
httpsession是用來保存用戶的信息的楔脯,而這些信息也需要在用戶登錄的時(shí)候通過代碼邏輯保存在session里面 撩轰。
session可以理解成服務(wù)端點(diǎn)與遠(yuǎn)程客戶端點(diǎn)的一次會(huì)話,他是你使用了WebSocket后,WebSocket自帶的一個(gè)容器昧廷,里面有g(shù)etAsyncRemote()和getBasicRemote()兩個(gè)方法(前者異步钧敞,后者同步),需要?jiǎng)?chuàng)建session對(duì)象之后調(diào)用這兩個(gè)方法才能實(shí)現(xiàn)對(duì)這個(gè)session對(duì)象的對(duì)應(yīng)用戶的推送麸粮。(比如服務(wù)器包擁有用戶A的session,需要拿這個(gè)session去給A發(fā)消息)

private void sendMessage(String message) throws IOException {
      this.session.getBasicRemote().sendText(message);
   }

3.如何在服務(wù)器端保存這些Session镜廉?

問題描述: Session對(duì)象建立在由@ServerEndpoint注解的類中弄诲。那么每個(gè)用戶連接,都會(huì)建立起一個(gè)Session娇唯,怎么保存這些session跟用戶一一對(duì)應(yīng)齐遵?
解決方法:可以建立一個(gè)全局的map(必須要是static),每次創(chuàng)建一個(gè)websocket的時(shí)候塔插,同時(shí)創(chuàng)建session梗摇,并且把這個(gè)session放到map中,key為session的userId想许。這樣map中就包含著所有的session連接伶授,也不會(huì)像set一樣每次取session都要遍歷(map可以通過key來快速找到對(duì)應(yīng)的value断序,在session特別多的時(shí)候可以提高效率)。

4.Session是無法序列化的糜烹,考慮一下分布式的情況违诗?

問題描述:我們了解到,session是無法序列化疮蹦,也就是沒有實(shí)現(xiàn)Seriazable接口诸迟,現(xiàn)在我們的session都是存儲(chǔ)在單機(jī)上的,沒法保存在數(shù)據(jù)庫里愕乎,如果有多個(gè)服務(wù)器呢阵苇?
session的映射關(guān)系是一對(duì)一的,就是一臺(tái)服務(wù)器對(duì)應(yīng)一臺(tái)客戶機(jī)感论,比如A號(hào)機(jī)連著用戶1绅项,B號(hào)機(jī)連著用戶2,用戶1怎么給用戶2發(fā)消息笛粘?在A號(hào)機(jī)里沒有用戶2的session對(duì)象趁怔,沒法序列化也存不到數(shù)據(jù)庫里去查這個(gè),那咋辦嘛薪前。
解決方法:
用redis的發(fā)布/訂閱功能润努,既然我們封裝不了session,我們把UserId和要發(fā)的消息存到redis中示括,每臺(tái)機(jī)子訂閱這個(gè)頻道铺浇,每次有新消息過來就存到這,(還記得我們的map嗎垛膝,對(duì)應(yīng)的key-value是UserId-Session)每個(gè)服務(wù)器都去查自己有沒有這個(gè)ID鳍侣,有的話就發(fā)給這個(gè)用戶。(優(yōu)化方案:可以每次消息過來先查詢自己有沒有這個(gè)ID吼拥,有的話就直接發(fā)送倚聚,就不用發(fā)布到頻道了,節(jié)省時(shí)間)
參考鏈接:
訂閱/發(fā)布的思路: https://blog.csdn.net/u011692924/article/details/81076263
訂閱/發(fā)布已有的實(shí)現(xiàn): https://gitee.com/xxssyyyyssxx/jfinal-websocket
如何在springboot中使用redis的訂閱/發(fā)布功能: https://www.cnblogs.com/sxdcgaq8080/p/10953693.html

5.WebSocket與Servlet無關(guān)凿可,怎么拿到Httpsession惑折?

問題描述:寫代碼的過程中會(huì)遇到websocket獲取不到httpsession的情況,因?yàn)閣ebsocket與servlet無關(guān)枯跑,所以取不到惨驶,怎么辦?
解決方法:修改握手方法敛助,一開始握手的時(shí)候就把HttpSession放到WebSocket對(duì)象的ServerEndpointConfig的map中粗卜。
下面鏈接是具體解決方案
https://www.cnblogs.com/hellxz/p/8063867.html

6.怎么使用WebSocket實(shí)現(xiàn)聊天功能?

websocket的方法的簡單聊天室實(shí)現(xiàn),只提供了思路纳击,如果想看具體實(shí)現(xiàn)請(qǐng)轉(zhuǎn)步文章頂部的Github续扔。
@onopen(這個(gè)用戶連接登錄時(shí))
需要在map中把Session對(duì)象加入進(jìn)來
@onclose(用戶關(guān)閉網(wǎng)頁或者注銷時(shí))
需要移除map中對(duì)應(yīng)的Session
@OnMessage(用戶發(fā)送消息時(shí))
需要把消息加到redis中
需要通過session.getBasicRemote().sendText()方法去把消息推送到對(duì)應(yīng)的用戶
@onerror
調(diào)用e.printStackTrace()

7.Fastjson的簡單使用

222.png

首先在maven中引入jar包攻臀。

333.png

上圖中第n次推送消息的方法解析:

1.圖中Objects.requireNonNull()方法可以提前拋出空指針異常(如果value為空指針,會(huì)拋出的空指針異常會(huì)定位到這個(gè)方法中)测砂,防止把這個(gè)異常帶到更深的方法中難debug茵烈。

  1. 拿到message對(duì)象和redis中的value后,想把message加到value中砌些,需要先把value(狀態(tài)是String)轉(zhuǎn)成JSONArray呜投,再調(diào)用JSONArray里的toJavaList方法轉(zhuǎn)化java中的list集合,最后把message對(duì)象加到list中存璃,最后把list用toJSONString()方法轉(zhuǎn)換成字符串的形式重新放在value中仑荐。

舉例:有多個(gè)聊天記錄,比如發(fā)了一句“在干嘛”發(fā)送成功后纵东,又發(fā)了一句“吃了嗎”粘招,此時(shí)redis里存在的是兩個(gè)message的字符串對(duì)象,在后面繼續(xù)append字符串是不現(xiàn)實(shí)的偎球,只能先把這兩個(gè)message的字符串轉(zhuǎn)化成JAVA的message對(duì)象保存在list中洒扎,然后新來了個(gè)message,再把這個(gè)message保存在list中衰絮,最后再把這個(gè)list轉(zhuǎn)化成JSON字符串后重新賦值給value袍冷,才算是保存成功了。

8.使用StringRedisTemplate對(duì)象操作redis

首先附上針對(duì)RedisTemplate方法操作解釋猫牡,非常簡單易懂:
RedisTemplate方法操作解釋 : https://blog.csdn.net/qieyi28/article/details/84902209

針對(duì)redis中key的命名規(guī)范:因?yàn)閞edis中不像mysql中有字段可以知道這個(gè)數(shù)據(jù)列是干嘛的胡诗,這邊用的又是key-value值的存儲(chǔ),需要對(duì)key命名的時(shí)候有一定的格式淌友。
這里我用的是interface接口存儲(chǔ)String字符串 煌恢,因?yàn)樽址淖兞慷际莝tatic和final,直接在接口中命名

444.png

*的作用是如果要對(duì)redis進(jìn)行模糊查詢震庭,需要在后面加上*

(可以用StringRedisTemplate中的keys()方法模糊查詢)
每次存儲(chǔ)的key都需要帶上這些前綴瑰抵,每次查也可以通過這些前綴去查

555.png

這里圖中分別表示的是一個(gè)公共聊天室記錄,一個(gè)單對(duì)單的聊天記錄器联,兩個(gè)用戶的個(gè)人信息谍憔。其中第二個(gè)單對(duì)單的聊天記錄由CHAT_FROM_+id+TO+id2組成

9.通過設(shè)置key的過期時(shí)間和Springboot的定時(shí)任務(wù)來刪除key

這兩個(gè)方法都可以來控制key的有效時(shí)間,有不同的應(yīng)用場景主籍。

1.設(shè)置key的過期時(shí)間

可以用redis的key過期時(shí)間來設(shè)置每個(gè)用戶和會(huì)話的存在時(shí)間,這個(gè)比較簡單逛球,下面?zhèn)鲄⒎謩e對(duì)應(yīng)的是key千元,value,時(shí)間颤绕,單位幸海。這個(gè)意思就是baike-100 的這個(gè)鍵值對(duì)存在600秒

stringRedisTemplate.opsForValue().set("baike", "100", 60 * 10, TimeUnit.SECONDS);

2.用Spring的定時(shí)任務(wù)刪除key

@EnableScheduling 在配置類上使用祟身,開啟計(jì)劃任務(wù)的支持(類上)
@Scheduled 來申明這是一個(gè)任務(wù),包括cron,fixDelay,fixRate等類型(方法上物独,需先開啟計(jì)劃任務(wù)的支持)
@Scheduled中有個(gè)cron表達(dá)式 下面是學(xué)習(xí)鏈接
https://blog.csdn.net/Linweiqiang5/article/details/86741258
具體實(shí)現(xiàn):

666.png

這里cron表達(dá)的就是每30分鐘做個(gè)定時(shí)任務(wù)袜硫,刪除注冊(cè)時(shí)間超過20分鐘的用戶,以及會(huì)話信息挡篓,我們的公共聊天室只有一個(gè)婉陷,誰先說話,后面的id跟著就是誰的官研。
在項(xiàng)目中UserId用getTime()這個(gè)方法得來的秽澳,其中g(shù)etTime()這個(gè)方法獲得的是1970年01月1日0點(diǎn)零分以來的毫秒數(shù),圖中MINUTE_30這個(gè)值代表的就是30分鐘的毫秒數(shù)戏羽。
最長用戶可能有59分59秒的壽命担神,剛刪過一次后注冊(cè),然后第二次刪除他的注冊(cè)時(shí)間只有29分29秒始花,不符合妄讯,等再下一次定時(shí)任務(wù)時(shí)回收。

10.Slf4j打印日志和lombok插件

這兩個(gè)插件的使用和學(xué)習(xí)都非常簡單酷宵,簡單過一遍就能上手了亥贸。

slf4j日志打印的學(xué)習(xí): https://blog.csdn.net/MengDiL_yl/article/details/86648197
注意在上面那張用Spring的定時(shí)任務(wù)刪除key的圖中,沒有像log4j一樣需要
private final Logger logger = LoggerFactory.getLogger(LoggerTest.class);
之后調(diào)用logger.info()
是因?yàn)橐昧薼ombok包忧吟,他可以讓你在實(shí)體類的時(shí)候少寫get set方法砌函,也可以與slf4j配合不用創(chuàng)建這個(gè)logger對(duì)象,直接log.info()就可以用了
lombok的學(xué)習(xí): https://www.cnblogs.com/heyonggang/p/8638374.html

11.項(xiàng)目中遇到的問題:

注冊(cè)時(shí)溜族,如果瀏覽器卡一下讹俊,多點(diǎn)幾次注冊(cè),會(huì)出現(xiàn)同用戶名不同id的用戶煌抒,(第二個(gè)注冊(cè)的時(shí)候去查redis中的的keys沒發(fā)現(xiàn)有這個(gè)id)
解決方案:需要在前端在點(diǎn)擊按鈕后仍劈,把按鈕置灰?guī)酌耄乐惯B點(diǎn)寡壮。

訂閱/通知版本中的問題

在controller調(diào)用redis的通知方法后贩疙,在Controller執(zhí)行完畢return后才會(huì)執(zhí)行監(jiān)聽器方法,前端是拿到controller的return的R之后去從redis查况既,在聊天記錄里打印出來这溅,現(xiàn)在的話就會(huì)造成發(fā)送成功但是不會(huì)顯示,需要重新點(diǎn)一下頭像才能刷新消息記錄棒仍。
解決方案:待更新

感謝TyCoding的指導(dǎo)悲靴,他的GitHub地址:https://github.com/TyCoding
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市莫其,隨后出現(xiàn)的幾起案子癞尚,更是在濱河造成了極大的恐慌耸三,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浇揩,死亡現(xiàn)場離奇詭異仪壮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)胳徽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門积锅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人膜廊,你說我怎么就攤上這事乏沸。” “怎么了爪瓜?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵蹬跃,是天一觀的道長。 經(jī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
  • 文/蒼蘭香墨 我猛地睜開眼腹尖,長吁一口氣:“原來是場噩夢啊……” “哼柳恐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤胎撤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后断凶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伤提,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有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
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扣汪,卻和暖如春断楷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背崭别。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國打工冬筒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人茅主。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓舞痰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親诀姚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子响牛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355