SpringBoot+Redis(一) 基本操作

目錄

0. 版本說明

  • JDK 1.8.0_181
  • SpringBoot 2.2.6.RELEASE
  • JUnit 5.5
  • Maven 3.5.4
  • Redis redis:5.0.6

1. 環(huán)境準(zhǔn)備

1.1 Spring Boot項(xiàng)目建立

  • pom.xml中依賴項(xiàng)配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ys</groupId>
    <artifactId>redis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redis</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- 引入Redis支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- 引入Web項(xiàng)目支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 引入devtools支持熱部署, 讓修改立馬生效 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!-- 用來支持.properties和.xml配置文件解析 -->
        <dependency>
            <groupId> org.springframework.boot </groupId>
            <artifactId> spring-boot-configuration-processor </artifactId>
            <optional> true </optional>
        </dependency>
        <!-- 用來支持單元測(cè)試, 包含了JUnit5 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <!-- 用來為JUnit4過渡到JUnit5, 這里無需過渡 -->
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <!-- 表示使用Maven來執(zhí)行build -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.2 RedisConfig配置

  • application.properties中配置
# Redis數(shù)據(jù)庫(kù)HOST
spring.redis.host=127.0.0.1
# Redis數(shù)據(jù)庫(kù)PORT
spring.redis.port=6379
# Redis數(shù)據(jù)庫(kù)索引
spring.redis.database=0
# Redis數(shù)據(jù)庫(kù)密碼
spring.redis.password=
# 連接池最大連接數(shù)(使用負(fù)值表示沒有限制)
spring.redis.jedis.pool.max-active=8
# 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒有限制)
spring.redis.jedis.pool.max-wait=-1
# 連接池中的最大空閑連接
spring.redis.jedis.pool.max-idle=8
# 連接池中的最小空閑連接
spring.redis.jedis.pool.min-idle=0
# 連接超時(shí)時(shí)間(毫秒)
spring.redis.timeout=300
  • RedisConfig.java中的Bean創(chuàng)建
@Configuration
public class RedisConfig {
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        StringRedisSerializer keySerializer = new StringRedisSerializer();

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(keySerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(keySerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

}

這樣, StringRedisTemplate和RedisTemplate就交由BeanFactory來創(chuàng)建, 可以保證全局唯一

1.3 SpringBootTest準(zhǔn)備

@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class RedisStringTest {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    @Order(1)
    public void testSet() {
        stringRedisTemplate.opsForValue().set("test-string-value", "Hello World");
    }
}

重點(diǎn)是

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

@Order(1)

這兩個(gè)注解, 規(guī)定了@Test注解方法的執(zhí)行順序

2. 基本操作

2.1 字符串(string)

  • 設(shè)置
@Test
@Order(1)
public void testSet() {
    stringRedisTemplate.opsForValue().set("test-string-value", "Hello World");
}
  • 獲取
@Test
@Order(2)
public void testGet() {
    String result = stringRedisTemplate.opsForValue().get("test-string-value");
    System.out.println(result);
}

打印結(jié)果

Hello World
  • 設(shè)置值并帶有超時(shí)機(jī)制
@Test
@Order(3)
public void testSetTimeout() {
    stringRedisTemplate.opsForValue().set("test-string-key-timeout", "Hello Again", 15, TimeUnit.SECONDS);
}

15秒后, 再在Redis命令行中查詢?cè)搆ey, 發(fā)現(xiàn)已不存在

  • 刪除
@Test
@Order(4)
public void testDelete() {
    stringRedisTemplate.delete("test-string-value");
}

2.2 列表(list)

  • 從左側(cè)新增
@Test
@Order(1)
public void lpush() {
    redisTemplate.opsForList().leftPush("TestList", "TestLeftPush");
}
  • 從右側(cè)新增
@Test
@Order(2)
public void rpush() {
    redisTemplate.opsForList().rightPush("TestList", "TestRightPush");
}
  • 從左側(cè)彈出
@Test
@Order(3)
public void lpop() {
    Object result = redisTemplate.opsForList().leftPop("TestList");
    System.out.println("lpop的結(jié)果: " + result);
}

輸出結(jié)果

lpop的結(jié)果: TestLeftPush
  • 從右側(cè)彈出
@Test
@Order(4)
public void rpop() {
    Object result = redisTemplate.opsForList().rightPop("TestList");
    System.out.println("rpop第1次的結(jié)果為: " + result);

    result = redisTemplate.opsForList().rightPop("TestList");
    System.out.println("rpop第2次的結(jié)果為: " + result);
}

輸出結(jié)果

rpop第1次的結(jié)果為: TestRightPush
rpop第2次的結(jié)果為: null

2.3 哈希(hash)

  • 設(shè)置
@Test
@Order(1)
public void testPut() {
    redisTemplate.opsForHash().put("TestHash", "FirstElement", "Hello, Redis hash.");
    Assert.isTrue(redisTemplate.opsForHash().hasKey("TestHash", "FirstElement"),
            "HashKey: key=TestHash, field=FirstElement不存在!");
}
  • 獲取
@Test
@Order(2)
public void testGet() {
    Object element = redisTemplate.opsForHash().get("TestHash", "FirstElement");
    Assert.isTrue(element.equals("Hello, Redis hash."), "Hash value不匹配!");
}
  • 刪除
@Test
@Order(3)
public void testDel() {
    redisTemplate.opsForHash().delete("TestHash", "FirstElement");
    Assert.isTrue(!redisTemplate.opsForHash().hasKey("TestHash", "FirstElement"),
            "HashKey: key=TestHash, field=FirstElement依然存在!");
}

2.4 集合(set)

  • 新增
@Test
@Order(1)
public void testAdd() {
    redisTemplate.opsForSet().add("TestSet", "e1", "e2", "e3");
    long size = redisTemplate.opsForSet().size("TestSet");
    System.out.println("TestSet's size is: " + size);
}

輸出結(jié)果

TestSet's size is: 3
  • 獲取
@Test
@Order(2)
public void testGet() {
    Set<String> testSet = redisTemplate.opsForSet().members("TestSet");
    System.out.println(testSet);
}

輸出結(jié)果

[e1, e2, e3]
  • 刪除
@Test
@Order(3)
public void testDel() {
    redisTemplate.opsForSet().remove("TestSet", "e1", "e2");
    Set<String> testSet = redisTemplate.opsForSet().members("TestSet");
    System.out.println("刪除操作后, 當(dāng)前集合元素為: " + testSet);
}

輸出結(jié)果

刪除操作后, 當(dāng)前集合元素為: [e3]

2.5 有序集合(zset)

  • 新增(單個(gè) VS 多個(gè))
@Test
@Order(1)
public void testAdd() {
    redisTemplate.opsForZSet().add("TestZset", "e1", 1);

    Set<ZSetOperations.TypedTuple<String>> zset = new HashSet();
    zset.add(new DefaultTypedTuple("e2", 20.0));
    zset.add(new DefaultTypedTuple("e3", 30.0));
    redisTemplate.opsForZSet().add("TestZset", zset);
}
  • 獲取(按名次/分?jǐn)?shù)/倒序)
@Test
@Order(2)
public void testRange() {
    Set<String> results = redisTemplate.opsForZSet().range("TestZset", 0, 1);
    System.out.println("分?jǐn)?shù)最低的2個(gè)成員 range(0, 1): " + results);

    results = redisTemplate.opsForZSet().rangeByScore("TestZset", 0.0, 100.0);
    System.out.println("分?jǐn)?shù)處于指定區(qū)間的成員 rangeByScore(0.0, 100.0): " + results);

    Set<ZSetOperations.TypedTuple<String>> zset = redisTemplate.opsForZSet().rangeByScoreWithScores("TestZset", 0.0, 100.0);
    System.out.print("分?jǐn)?shù)處于指定區(qū)間的成員 rangeByScoreWithScores(0.0, 100.0): [");
    zset.stream().forEach(x -> System.out.print("<value=" + x.getValue() + ",score=" + x.getScore() + ">,"));
    System.out.println("]");

    Set<ZSetOperations.TypedTuple<String>> topScorer = redisTemplate.opsForZSet().reverseRangeWithScores("TestZset", 0, 0);
    System.out.print("分?jǐn)?shù)最高的1個(gè)成員 reverseRangeWithScores(0, 0): [");
    topScorer.stream().forEach(x -> System.out.print("<value=" + x.getValue() + ",score=" + x.getScore() + ">,"));
    System.out.println("]");
}

輸出結(jié)果

分?jǐn)?shù)最低的2個(gè)成員 range(0, 1): [e1, e2]
分?jǐn)?shù)處于指定區(qū)間的成員 rangeByScore(0.0, 100.0): [e1, e2, e3]
分?jǐn)?shù)處于指定區(qū)間的成員 rangeByScoreWithScores(0.0, 100.0): [<value=e1,score=1.0>,<value=e2,score=20.0>,<value=e3,score=30.0>,]
分?jǐn)?shù)最高的1個(gè)成員 reverseRangeWithScores(0, 0): [<value=e3,score=30.0>,]
  • 獲取成員數(shù)量
@Test
@Order(3)
public void testSize() {
    long size = redisTemplate.opsForZSet().size("TestZset");
    System.out.println("key=TestZset的有序集合的成員數(shù): " + size);
}

輸出結(jié)果

key=TestZset的有序集合的成員數(shù): 3
  • 根據(jù)值獲得分?jǐn)?shù)
@Test
@Order(4)
public void testScore() {
    double score = redisTemplate.opsForZSet().score("TestZset", "e2");
    System.out.println("成員e2的分?jǐn)?shù)為: " + score);
}

輸出結(jié)果

成員e2的分?jǐn)?shù)為: 20.0
  • 根據(jù)值獲得名次
@Test
@Order(5)
public void testRank() {
    Set<ZSetOperations.TypedTuple<String>> zset = redisTemplate.opsForZSet().rangeWithScores("TestZset", 0, -1);
    zset.stream().forEach(x -> System.out.printf("成員%s的分?jǐn)?shù)為:%f, 名次為:%d\n",
            x.getValue(),
            x.getScore(),
            redisTemplate.opsForZSet().rank("TestZset", x.getValue())));
}

輸出結(jié)果

成員e1的分?jǐn)?shù)為:1.000000, 名次為:0
成員e2的分?jǐn)?shù)為:20.000000, 名次為:1
成員e3的分?jǐn)?shù)為:30.000000, 名次為:2
  • 修改分?jǐn)?shù)(覆蓋/加減)
@Test
@Order(6)
public void testChangeScore() {
    redisTemplate.opsForZSet().add("TestZset", "e1", 50.0);
    double score = redisTemplate.opsForZSet().score("TestZset", "e1");
    System.out.println("通過zadd后, e1的分?jǐn)?shù)被覆蓋成: " + score);

    score = redisTemplate.opsForZSet().incrementScore("TestZset", "e1", 10.0);
    System.out.println("通過incrementScore(10.0)后, e1的分?jǐn)?shù)變成: " + score);
}

輸出結(jié)果

通過zadd后, e1的分?jǐn)?shù)被覆蓋成: 50.0
通過incrementScore(10.0)后, e1的分?jǐn)?shù)變成: 60.0
  • 刪除
@Test
@Order(7)
public void testDel() {
    redisTemplate.opsForZSet().remove("TestZset", "e1");
    Set<String> zset = redisTemplate.opsForZSet().range("TestZset", 0, -1);
    System.out.println("剩余成員為: " + zset);
}

輸出結(jié)果

剩余成員為: [e2, e3]

3. 坑

3.1 key值帶額外雙引號(hào)

情況:

  1. 利用RedisTemplate來向Redis增加一個(gè)key值為TestList的列表, 然后通過lpushrpush向該列表添加兩個(gè)值TestLeftPushTestRightPush;
  2. Redis命令行中手動(dòng)運(yùn)行lpush mylist l1
  3. 然后在Redis命令行中運(yùn)行keys *, 發(fā)現(xiàn)key值為
"\"TestList\""
"mylist"

為何會(huì)不一樣?

原因:

RedisConfig類中通過

redisTemplate.setKeySerializer(jackson2JsonRedisSerializer)

修改key值解析方式為JSON格式了, 于是key值就多了雙引號(hào)

解決辦法:

修改RedisConfig類中代碼

StringRedisSerializer keySerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setHashKeySerializer(keySerializer);

參考鏈接

3.2 Redis命令超時(shí)

報(bào)錯(cuò):

Command timed out after no timeout

原因:

未設(shè)置redis超時(shí)時(shí)間

解決辦法:

application.properties修改配置項(xiàng)

# 連接超時(shí)時(shí)間(毫秒)
spring.redis.timeout=300

參考鏈接

3.3 SpringBootTest中Test方法的執(zhí)行順序

情況:

在@SpringBootTest注解的類里, 四個(gè)@Test注解的方法, 執(zhí)行順序并不是按照定義順序, 且每次執(zhí)行都一樣; 那么如何控制這些@Test注解方法的執(zhí)行順序器躏?

原因:

使用的是JUnit5, 但未明確定義@Test執(zhí)行順序

解決辦法:

  • 增加類注解@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
  • 增加@Test方法注解 @Order(1)

參考鏈接

4. 拓展思考

4.1 適用場(chǎng)景

分布式緩存:在分布式的系統(tǒng)架構(gòu)中,將緩存存儲(chǔ)在內(nèi)存中顯然不當(dāng)捐顷,因?yàn)榫彺嫘枰c其他機(jī)器共享炕檩,這時(shí) Redis 便挺身而出了换棚,緩存也是 Redis 使用最多的場(chǎng)景。

分布式鎖:在高并發(fā)的情況下米奸,我們需要一個(gè)鎖來防止并發(fā)帶來的臟數(shù)據(jù)悔叽,Java 自帶的鎖機(jī)制顯然對(duì)進(jìn)程間的并發(fā)并不好使构韵,此時(shí)可以利用 Redis 單線程的特性來實(shí)現(xiàn)我們的分布式鎖周蹭。

Session 存儲(chǔ)/共享:Redis 可以將 Session 持久化到存儲(chǔ)中,這樣可以避免由于機(jī)器宕機(jī)而丟失用戶會(huì)話信息疲恢。

發(fā)布/訂閱:Redis 還有一個(gè)發(fā)布/訂閱的功能凶朗,您可以設(shè)定對(duì)某一個(gè) key 值進(jìn)行消息發(fā)布及消息訂閱,當(dāng)一個(gè) key 值上進(jìn)行了消息發(fā)布后显拳,所有訂閱它的客戶端都會(huì)收到相應(yīng)的消息棚愤。這一功能最明顯的用法就是用作實(shí)時(shí)消息系統(tǒng)。

任務(wù)隊(duì)列:Redis 的 lpush+brpop 命令組合即可實(shí)現(xiàn)阻塞隊(duì)列杂数,生產(chǎn)者客戶端使用 lrpush 從列表左側(cè)插入元素宛畦,多個(gè)消費(fèi)者客戶端使用 brpop 命令阻塞式的"搶"列表尾部的元素,多個(gè)客戶端保證了消費(fèi)的負(fù)載均衡和高可用性揍移。

限速次和,接口訪問頻率限制:比如發(fā)送短信驗(yàn)證碼的接口,通常為了防止別人惡意頻刷那伐,會(huì)限制用戶每分鐘獲取驗(yàn)證碼的頻率踏施,例如一分鐘不能超過 5 次石蔗。

4.2 緩存與數(shù)據(jù)庫(kù)一致性

  1. 先寫數(shù)據(jù)庫(kù), 再寫緩存
  2. 先寫緩存, 再寫數(shù)據(jù)庫(kù)

大部分情況下,我們的緩存理論上都是需要可以從數(shù)據(jù)庫(kù)恢復(fù)出來的畅形,所以基本上采取第一種順序都是不會(huì)有問題的养距。針對(duì)那些必須保證數(shù)據(jù)庫(kù)和緩存一致的情況,通常是不建議使用緩存的日熬,如果必須使用的話

4.3 緩存擊穿

用戶故意查詢數(shù)據(jù)庫(kù)中不存在(意味著緩存肯定也沒有)的內(nèi)容棍厌,導(dǎo)致每次查詢都會(huì)去庫(kù)里查一次

策略:

  • 使用互斥鎖排隊(duì): 當(dāng)從緩存中獲取數(shù)據(jù)失敗時(shí),給當(dāng)前接口加上鎖竖席,從數(shù)據(jù)庫(kù)中加載完數(shù)據(jù)并寫入后再釋放鎖定铜。若其它線程獲取鎖失敗,則等待一段時(shí)間后重試怕敬。
  • 使用布隆過濾器: 將所有可能存在的數(shù)據(jù)緩存放到布隆過濾器中揣炕,當(dāng)黑客訪問不存在的緩存時(shí)迅速返回避免緩存及 DB 掛掉。

4.4 緩存雪崩

緩存down了东跪,所有查詢都落到數(shù)據(jù)庫(kù)

策略: 讓緩存不會(huì)真正的down畸陡,具體來說

  • 像解決緩存穿透一樣加鎖排隊(duì)
  • 建立備份緩存: 緩存A和緩存B虽填,A設(shè)置超時(shí)時(shí)間丁恭,B 不設(shè)值超時(shí)時(shí)間,先從 A 讀緩存斋日,A 沒有讀 B牲览,并且更新 A 緩存和 B 緩存。
  • 計(jì)算數(shù)據(jù)緩存節(jié)點(diǎn)的時(shí)候采用一致性 hash: 這樣在節(jié)點(diǎn)數(shù)量發(fā)生改變時(shí)不會(huì)存在大量的緩存數(shù)據(jù)需要遷移的情況發(fā)生恶守。

4.5 緩存并發(fā)

這里的并發(fā)指的是多個(gè) Redis 的客戶端同時(shí) set 值引起的并發(fā)問題第献。比較有效的解決方案就是把 set 操作放在隊(duì)列中使其串行化,必須得一個(gè)一個(gè)執(zhí)行兔港。

5. 參考鏈接:

了解 Redis 并在 Spring Boot 項(xiàng)目中使用 Redis

SpringBoot高級(jí)篇Redis之ZSet數(shù)據(jù)結(jié)構(gòu)使用姿勢(shì)

SpringBoot Guide-與Redis通訊

解決redis redistemplate KEY為字符串是多雙引號(hào)的問題

報(bào)錯(cuò)Command timed out after no timeout

JUnit5.5的TestMethodOrder API說明

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庸毫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子衫樊,更是在濱河造成了極大的恐慌飒赃,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件科侈,死亡現(xiàn)場(chǎng)離奇詭異载佳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)臀栈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蔫慧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挂脑,你說我怎么就攤上這事藕漱∮辏” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵肋联,是天一觀的道長(zhǎng)威蕉。 經(jīng)常有香客問我,道長(zhǎng)橄仍,這世上最難降的妖魔是什么韧涨? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮侮繁,結(jié)果婚禮上虑粥,老公的妹妹穿的比我還像新娘。我一直安慰自己宪哩,他們只是感情好娩贷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锁孟,像睡著了一般彬祖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上品抽,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天储笑,我揣著相機(jī)與錄音,去河邊找鬼圆恤。 笑死突倍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盆昙。 我是一名探鬼主播羽历,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼弱左!你這毒婦竟也來了窄陡?” 一聲冷哼從身側(cè)響起炕淮,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤拆火,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后涂圆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體们镜,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年润歉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了模狭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡踩衩,死狀恐怖嚼鹉,靈堂內(nèi)的尸體忽然破棺而出贩汉,到底是詐尸還是另有隱情,我是刑警寧澤锚赤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布匹舞,位于F島的核電站,受9級(jí)特大地震影響线脚,放射性物質(zhì)發(fā)生泄漏赐稽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一浑侥、第九天 我趴在偏房一處隱蔽的房頂上張望姊舵。 院中可真熱鬧,春花似錦寓落、人聲如沸括丁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)躏将。三九已至,卻和暖如春考蕾,著一層夾襖步出監(jiān)牢的瞬間祸憋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工肖卧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚯窥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓塞帐,卻偏偏與公主長(zhǎng)得像拦赠,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子葵姥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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