Spring Data Redis 最佳實踐看幼!

摘要

Spring Data Redis 是Spring 框架提供的用于操作Redis的方式直砂,最近整理了下它的用法,解決了使用過程中遇到的一些難點與坑點汹买,希望對大家有所幫助佩伤。本文涵蓋了Redis的安裝、Spring Cache結(jié)合Redis的使用晦毙、Redis連接池的使用和RedisTemplate的使用等內(nèi)容生巡。

Redis安裝

這里提供Linux和Windows兩種安裝方式,由于Windows下的版本最高只有3.2版本见妒,所以推薦使用Linux下的版本孤荣,目前最新穩(wěn)定版本為5.0,也是本文中使用的版本。

Linux

這里我們使用Docker環(huán)境下的安裝方式盐股。

下載Redis5.0的Docker鏡像钱豁;

docker pull redis:5.0

使用Docker命令啟動Redis容器;

docker run -p 6379:6379 --name redis \

-v /mydata/redis/data:/data \

-d redis:5.0 redis-server --appendonly yes

Windows

想使用Windows版本的朋友可以使用以下安裝方式疯汁。

下載Windows版本的Redis,下載地址:github.com/MicrosoftAr…

下載完后解壓到指定目錄牲尺;

在當前地址欄輸入cmd后,執(zhí)行redis的啟動命令:redis-server.exe redis.windows.conf

Spring Cache 操作Redis

Spring Cache 簡介

當Spring Boot 結(jié)合Redis來作為緩存使用時幌蚊,最簡單的方式就是使用Spring Cache了谤碳,使用它我們無需知道Spring中對Redis的各種操作,僅僅通過它提供的@Cacheable 溢豆、@CachePut 蜒简、@CacheEvict 、@EnableCaching等注解就可以實現(xiàn)緩存功能漩仙。

常用注解

@EnableCaching

開啟緩存功能臭蚁,一般放在啟動類上。

@Cacheable

使用該注解的方法當緩存存在時讯赏,會從緩存中獲取數(shù)據(jù)而不執(zhí)行方法垮兑,當緩存不存在時,會執(zhí)行方法并把返回結(jié)果存入緩存中漱挎。一般使用在查詢方法上系枪,可以設置如下屬性:

value:緩存名稱(必填),指定緩存的命名空間磕谅;

key:用于設置在命名空間中的緩存key值私爷,可以使用SpEL表達式定義;

unless:條件符合則不緩存膊夹;

condition:條件符合則緩存衬浑。

@CachePut

使用該注解的方法每次執(zhí)行時都會把返回結(jié)果存入緩存中。一般使用在新增方法上放刨,可以設置如下屬性:

value:緩存名稱(必填)工秩,指定緩存的命名空間;

key:用于設置在命名空間中的緩存key值进统,可以使用SpEL表達式定義助币;

unless:條件符合則不緩存;

condition:條件符合則緩存螟碎。

@CacheEvict

使用該注解的方法執(zhí)行時會清空指定的緩存眉菱。一般使用在更新或刪除方法上,可以設置如下屬性:

value:緩存名稱(必填)掉分,指定緩存的命名空間俭缓;

key:用于設置在命名空間中的緩存key值克伊,可以使用SpEL表達式定義;

condition:條件符合則緩存华坦。

使用步驟

在pom.xml中添加項目依賴:


org.springframework.boot

spring-boot-starter-data-redis

修改配置文件application.yml愿吹,添加Redis的連接配置;

spring:

redis:

host: 192.168.6.139 # Redis服務器地址

database: 0 # Redis數(shù)據(jù)庫索引(默認為0)

port: 6379 # Redis服務器連接端口

password: # Redis服務器連接密碼(默認為空)

timeout: 1000ms # 連接超時時間

在啟動類上添加@EnableCaching注解啟動緩存功能季春;

@EnableCaching

@SpringBootApplication

public class MallTinyApplication {

public static void main(String[] args) {

SpringApplication.run(MallTinyApplication.class, args);

}

}

接下來在PmsBrandServiceImpl類中使用相關注解來實現(xiàn)緩存功能,可以發(fā)現(xiàn)我們獲取品牌詳情的方法中使用了@Cacheable注解消返,在修改和刪除品牌的方法上使用了@CacheEvict注解载弄;

/**

* PmsBrandService實現(xiàn)類

* Created by macro on 2019/4/19.

*/

@Service

public class PmsBrandServiceImpl implements PmsBrandService {

@Autowired

private PmsBrandMapper brandMapper;

@CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")

@Override

public int update(Long id, PmsBrand brand) {

brand.setId(id);

return brandMapper.updateByPrimaryKeySelective(brand);

}

@CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")

@Override

public int delete(Long id) {

return brandMapper.deleteByPrimaryKey(id);

}

@Cacheable(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id", unless = "#result==null")

@Override

public PmsBrand getItem(Long id) {

return brandMapper.selectByPrimaryKey(id);

}

}

我們可以調(diào)用獲取品牌詳情的接口測試下效果,此時發(fā)現(xiàn)Redis中存儲的數(shù)據(jù)有點像亂碼撵颊,并且沒有設置過期時間宇攻;

存儲JSON格式數(shù)據(jù)

此時我們就會想到有沒有什么辦法讓Redis中存儲的數(shù)據(jù)變成標準的JSON格式,然后可以設置一定的過期時間倡勇,不設置過期時間容易產(chǎn)生很多不必要的緩存數(shù)據(jù)逞刷。

我們可以通過給RedisTemplate設置JSON格式的序列化器,并通過配置RedisCacheConfiguration設置超時時間來實現(xiàn)以上需求妻熊,此時別忘了去除啟動類上的@EnableCaching注解夸浅,具體配置類RedisConfig代碼如下;

/**

* Redis配置類

* Created by macro on 2020/3/2.

*/

@EnableCaching

@Configuration

public class RedisConfig extends CachingConfigurerSupport {

/**

* redis數(shù)據(jù)庫自定義key

*/

public? static final String REDIS_KEY_DATABASE="mall";

@Bean

public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {

RedisSerializer serializer = redisSerializer();

RedisTemplate redisTemplate = new RedisTemplate<>();

redisTemplate.setConnectionFactory(redisConnectionFactory);

redisTemplate.setKeySerializer(new StringRedisSerializer());

redisTemplate.setValueSerializer(serializer);

redisTemplate.setHashKeySerializer(new StringRedisSerializer());

redisTemplate.setHashValueSerializer(serializer);

redisTemplate.afterPropertiesSet();

return redisTemplate;

}

@Bean

public RedisSerializer redisSerializer() {

//創(chuàng)建JSON序列化器

Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class);

ObjectMapper objectMapper = new ObjectMapper();

objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

serializer.setObjectMapper(objectMapper);

return serializer;

}

@Bean

public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {

RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);

//設置Redis緩存有效期為1天

RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()

.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1));

return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);

}

}

此時我們再次調(diào)用獲取商品詳情的接口進行測試扔役,會發(fā)現(xiàn)Redis中已經(jīng)緩存了標準的JSON格式數(shù)據(jù)帆喇,并且超時時間被設置為了1天。

使用Redis連接池

SpringBoot 1.5.x版本Redis客戶端默認是Jedis實現(xiàn)的亿胸,SpringBoot 2.x版本中默認客戶端是用Lettuce實現(xiàn)的坯钦,我們先來了解下Jedis和Lettuce客戶端。

Jedis vs Lettuce

Jedis在實現(xiàn)上是直連Redis服務侈玄,多線程環(huán)境下非線程安全婉刀,除非使用連接池,為每個 RedisConnection 實例增加物理連接序仙。

Lettuce是一種可伸縮突颊,線程安全,完全非阻塞的Redis客戶端潘悼,多個線程可以共享一個RedisConnection洋丐,它利用Netty NIO框架來高效地管理多個連接,從而提供了異步和同步數(shù)據(jù)訪問方式挥等,用于構(gòu)建非阻塞的反應性應用程序友绝。

使用步驟

修改application.yml添加Lettuce連接池配置,用于配置線程數(shù)量和阻塞等待時間肝劲;

spring:

redis:

lettuce:

pool:

max-active: 8 # 連接池最大連接數(shù)

max-idle: 8 # 連接池最大空閑連接數(shù)

min-idle: 0 # 連接池最小空閑連接數(shù)

max-wait: -1ms # 連接池最大阻塞等待時間迁客,負值表示沒有限制

由于SpringBoot 2.x中默認并沒有使用Redis連接池郭宝,所以需要在pom.xml中添加commons-pool2的依賴;

org.apache.commons

commons-pool2

如果你沒添加以上依賴的話掷漱,啟動應用的時候就會產(chǎn)生如下錯誤粘室;

Caused by: java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/GenericObjectPoolConfig

at org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration$LettucePoolingClientConfigurationBuilder.(LettucePoolingClientConfiguration.java:84) ~[spring-data-redis-2.1.5.RELEASE.jar:2.1.5.RELEASE]

at org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration.builder(LettucePoolingClientConfiguration.java:48) ~[spring-data-redis-2.1.5.RELEASE.jar:2.1.5.RELEASE]

at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$PoolBuilderFactory.createBuilder(LettuceConnectionConfiguration.java:149) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]

at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration.createBuilder(LettuceConnectionConfiguration.java:107) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]

at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration.getLettuceClientConfiguration(LettuceConnectionConfiguration.java:93) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]

at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration.redisConnectionFactory(LettuceConnectionConfiguration.java:74) ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]

at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$$EnhancerBySpringCGLIB$$5caa7e47.CGLIB$redisConnectionFactory$0() ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]

at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$$EnhancerBySpringCGLIB$$5caa7e47$$FastClassBySpringCGLIB$$b8ae2813.invoke() ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]

at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]

at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.1.5.RELEASE.jar:5.1.5.RELEASE]

at org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$$EnhancerBySpringCGLIB$$5caa7e47.redisConnectionFactory() ~[spring-boot-autoconfigure-2.1.3.RELEASE.jar:2.1.3.RELEASE]

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91]

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91]

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91]

at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91]

at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]

... 111 common frames omitted

自由操作Redis

Spring Cache 給我們提供了操作Redis緩存的便捷方法,但是也有很多局限性卜范。比如說我們想單獨設置一個緩存值的有效期怎么辦衔统?我們并不想緩存方法的返回值,我們想緩存方法中產(chǎn)生的中間值怎么辦海雪?此時我們就需要用到RedisTemplate這個類了锦爵,接下來我們來講下如何通過RedisTemplate來自由操作Redis中的緩存。

RedisService

定義Redis操作業(yè)務類奥裸,在Redis中有幾種數(shù)據(jù)結(jié)構(gòu)险掀,比如普通結(jié)構(gòu)(對象),Hash結(jié)構(gòu)湾宙、Set結(jié)構(gòu)樟氢、List結(jié)構(gòu),該接口中定義了大多數(shù)常用操作方法侠鳄。

/**

* redis操作Service

* Created by macro on 2020/3/3.

*/

public interface RedisService {

/**

* 保存屬性

*/

void set(String key, Object value, long time);

/**

* 保存屬性

*/

void set(String key, Object value);

/**

* 獲取屬性

*/

Object get(String key);

/**

* 刪除屬性

*/

Boolean del(String key);

/**

* 批量刪除屬性

*/

Long del(List keys);

/**

* 設置過期時間

*/

Boolean expire(String key, long time);

/**

* 獲取過期時間

*/

Long getExpire(String key);

/**

* 判斷是否有該屬性

*/

Boolean hasKey(String key);

/**

* 按delta遞增

*/

Long incr(String key, long delta);

/**

* 按delta遞減

*/

Long decr(String key, long delta);

/**

* 獲取Hash結(jié)構(gòu)中的屬性

*/

Object hGet(String key, String hashKey);

/**

* 向Hash結(jié)構(gòu)中放入一個屬性

*/

Boolean hSet(String key, String hashKey, Object value, long time);

/**

* 向Hash結(jié)構(gòu)中放入一個屬性

*/

void hSet(String key, String hashKey, Object value);

/**

* 直接獲取整個Hash結(jié)構(gòu)

*/

Map hGetAll(String key);

/**

* 直接設置整個Hash結(jié)構(gòu)

*/

Boolean hSetAll(String key, Map map, long time);

/**

* 直接設置整個Hash結(jié)構(gòu)

*/

void hSetAll(String key, Map map);

/**

* 刪除Hash結(jié)構(gòu)中的屬性

*/

void hDel(String key, Object... hashKey);

/**

* 判斷Hash結(jié)構(gòu)中是否有該屬性

*/

Boolean hHasKey(String key, String hashKey);

/**

* Hash結(jié)構(gòu)中屬性遞增

*/

Long hIncr(String key, String hashKey, Long delta);

/**

* Hash結(jié)構(gòu)中屬性遞減

*/

Long hDecr(String key, String hashKey, Long delta);

/**

* 獲取Set結(jié)構(gòu)

*/

Set sMembers(String key);

/**

* 向Set結(jié)構(gòu)中添加屬性

*/

Long sAdd(String key, Object... values);

/**

* 向Set結(jié)構(gòu)中添加屬性

*/

Long sAdd(String key, long time, Object... values);

/**

* 是否為Set中的屬性

*/

Boolean sIsMember(String key, Object value);

/**

* 獲取Set結(jié)構(gòu)的長度

*/

Long sSize(String key);

/**

* 刪除Set結(jié)構(gòu)中的屬性

*/

Long sRemove(String key, Object... values);

/**

* 獲取List結(jié)構(gòu)中的屬性

*/

List lRange(String key, long start, long end);

/**

* 獲取List結(jié)構(gòu)的長度

*/

Long lSize(String key);

/**

* 根據(jù)索引獲取List中的屬性

*/

Object lIndex(String key, long index);

/**

* 向List結(jié)構(gòu)中添加屬性

*/

Long lPush(String key, Object value);

/**

* 向List結(jié)構(gòu)中添加屬性

*/

Long lPush(String key, Object value, long time);

/**

* 向List結(jié)構(gòu)中批量添加屬性

*/

Long lPushAll(String key, Object... values);

/**

* 向List結(jié)構(gòu)中批量添加屬性

*/

Long lPushAll(String key, Long time, Object... values);

/**

* 從List結(jié)構(gòu)中移除屬性

*/

Long lRemove(String key, long count, Object value);

}

RedisServiceImpl

RedisService的實現(xiàn)類,使用RedisTemplate來自由操作Redis中的緩存數(shù)據(jù)伟恶。

/**

* redis操作實現(xiàn)類

* Created by macro on 2020/3/3.

*/

@Service

public class RedisServiceImpl implements RedisService {

@Autowired

private RedisTemplate redisTemplate;

@Override

public void set(String key, Object value, long time) {

redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);

}

@Override

public void set(String key, Object value) {

redisTemplate.opsForValue().set(key, value);

}

@Override

public Object get(String key) {

return redisTemplate.opsForValue().get(key);

}

@Override

public Boolean del(String key) {

return redisTemplate.delete(key);

}

@Override

public Long del(List keys) {

return redisTemplate.delete(keys);

}

@Override

public Boolean expire(String key, long time) {

return redisTemplate.expire(key, time, TimeUnit.SECONDS);

}

@Override

public Long getExpire(String key) {

return redisTemplate.getExpire(key, TimeUnit.SECONDS);

}

@Override

public Boolean hasKey(String key) {

return redisTemplate.hasKey(key);

}

@Override

public Long incr(String key, long delta) {

return redisTemplate.opsForValue().increment(key, delta);

}

@Override

public Long decr(String key, long delta) {

return redisTemplate.opsForValue().increment(key, -delta);

}

@Override

public Object hGet(String key, String hashKey) {

return redisTemplate.opsForHash().get(key, hashKey);

}

@Override

public Boolean hSet(String key, String hashKey, Object value, long time) {

redisTemplate.opsForHash().put(key, hashKey, value);

return expire(key, time);

}

@Override

public void hSet(String key, String hashKey, Object value) {

redisTemplate.opsForHash().put(key, hashKey, value);

}

@Override

public Map hGetAll(String key) {

return redisTemplate.opsForHash().entries(key);

}

@Override

public Boolean hSetAll(String key, Map map, long time) {

redisTemplate.opsForHash().putAll(key, map);

return expire(key, time);

}

@Override

public void hSetAll(String key, Map map) {

redisTemplate.opsForHash().putAll(key, map);

}

@Override

public void hDel(String key, Object... hashKey) {

redisTemplate.opsForHash().delete(key, hashKey);

}

@Override

public Boolean hHasKey(String key, String hashKey) {

return redisTemplate.opsForHash().hasKey(key, hashKey);

}

@Override

public Long hIncr(String key, String hashKey, Long delta) {

return redisTemplate.opsForHash().increment(key, hashKey, delta);

}

@Override

public Long hDecr(String key, String hashKey, Long delta) {

return redisTemplate.opsForHash().increment(key, hashKey, -delta);

}

@Override

public Set sMembers(String key) {

return redisTemplate.opsForSet().members(key);

}

@Override

public Long sAdd(String key, Object... values) {

return redisTemplate.opsForSet().add(key, values);

}

@Override

public Long sAdd(String key, long time, Object... values) {

Long count = redisTemplate.opsForSet().add(key, values);

expire(key, time);

return count;

}

@Override

public Boolean sIsMember(String key, Object value) {

return redisTemplate.opsForSet().isMember(key, value);

}

@Override

public Long sSize(String key) {

return redisTemplate.opsForSet().size(key);

}

@Override

public Long sRemove(String key, Object... values) {

return redisTemplate.opsForSet().remove(key, values);

}

@Override

public List lRange(String key, long start, long end) {

return redisTemplate.opsForList().range(key, start, end);

}

@Override

public Long lSize(String key) {

return redisTemplate.opsForList().size(key);

}

@Override

public Object lIndex(String key, long index) {

return redisTemplate.opsForList().index(key, index);

}

@Override

public Long lPush(String key, Object value) {

return redisTemplate.opsForList().rightPush(key, value);

}

@Override

public Long lPush(String key, Object value, long time) {

Long index = redisTemplate.opsForList().rightPush(key, value);

expire(key, time);

return index;

}

@Override

public Long lPushAll(String key, Object... values) {

return redisTemplate.opsForList().rightPushAll(key, values);

}

@Override

public Long lPushAll(String key, Long time, Object... values) {

Long count = redisTemplate.opsForList().rightPushAll(key, values);

expire(key, time);

return count;

}

@Override

public Long lRemove(String key, long count, Object value) {

return redisTemplate.opsForList().remove(key, count, value);

}

}

RedisController

測試RedisService中緩存操作的Controller霸妹,大家可以調(diào)用測試下。

/**

* Redis測試Controller

* Created by macro on 2020/3/3.

*/

@Api(tags = "RedisController", description = "Redis測試")

@Controller

@RequestMapping("/redis")

public class RedisController {

@Autowired

private RedisService redisService;

@Autowired

private PmsBrandService brandService;

@ApiOperation("測試簡單緩存")

@RequestMapping(value = "/simpleTest", method = RequestMethod.GET)

@ResponseBody

public CommonResult simpleTest() {

List brandList = brandService.list(1, 5);

PmsBrand brand = brandList.get(0);

String key = "redis:simple:" + brand.getId();

redisService.set(key, brand);

PmsBrand cacheBrand = (PmsBrand) redisService.get(key);

return CommonResult.success(cacheBrand);

}

@ApiOperation("測試Hash結(jié)構(gòu)的緩存")

@RequestMapping(value = "/hashTest", method = RequestMethod.GET)

@ResponseBody

public CommonResult hashTest() {

List brandList = brandService.list(1, 5);

PmsBrand brand = brandList.get(0);

String key = "redis:hash:" + brand.getId();

Map value = BeanUtil.beanToMap(brand);

redisService.hSetAll(key, value);

Map cacheValue = redisService.hGetAll(key);

PmsBrand cacheBrand = BeanUtil.mapToBean(cacheValue, PmsBrand.class, true);

return CommonResult.success(cacheBrand);

}

@ApiOperation("測試Set結(jié)構(gòu)的緩存")

@RequestMapping(value = "/setTest", method = RequestMethod.GET)

@ResponseBody

public CommonResult> setTest() {

List brandList = brandService.list(1, 5);

String key = "redis:set:all";

redisService.sAdd(key, (Object[]) ArrayUtil.toArray(brandList, PmsBrand.class));

redisService.sRemove(key, brandList.get(0));

Set cachedBrandList = redisService.sMembers(key);

return CommonResult.success(cachedBrandList);

}

@ApiOperation("測試List結(jié)構(gòu)的緩存")

@RequestMapping(value = "/listTest", method = RequestMethod.GET)

@ResponseBody

public CommonResult> listTest() {

List brandList = brandService.list(1, 5);

String key = "redis:list:all";

redisService.lPushAll(key, (Object[]) ArrayUtil.toArray(brandList, PmsBrand.class));

redisService.lRemove(key, 1, brandList.get(0));

List cachedBrandList = redisService.lRange(key, 0, 3);

return CommonResult.success(cachedBrandList);

}

}

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末知押,一起剝皮案震驚了整個濱河市叹螟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌台盯,老刑警劉巖罢绽,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異静盅,居然都是意外死亡良价,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門蒿叠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來明垢,“玉大人,你說我怎么就攤上這事市咽∪” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵施绎,是天一觀的道長溯革。 經(jīng)常有香客問我贞绳,道長,這世上最難降的妖魔是什么致稀? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任冈闭,我火速辦了婚禮,結(jié)果婚禮上抖单,老公的妹妹穿的比我還像新娘萎攒。我一直安慰自己,他們只是感情好矛绘,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布耍休。 她就那樣靜靜地躺著,像睡著了一般蔑歌。 火紅的嫁衣襯著肌膚如雪羹应。 梳的紋絲不亂的頭發(fā)上揽碘,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天次屠,我揣著相機與錄音,去河邊找鬼雳刺。 笑死劫灶,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的掖桦。 我是一名探鬼主播本昏,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼枪汪!你這毒婦竟也來了涌穆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤雀久,失蹤者是張志新(化名)和其女友劉穎宿稀,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赖捌,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡祝沸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了越庇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罩锐。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖卤唉,靈堂內(nèi)的尸體忽然破棺而出涩惑,到底是詐尸還是另有隱情,我是刑警寧澤桑驱,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布境氢,位于F島的核電站蟀拷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏萍聊。R本人自食惡果不足惜问芬,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寿桨。 院中可真熱鬧此衅,春花似錦、人聲如沸亭螟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽预烙。三九已至墨微,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扁掸,已是汗流浹背翘县。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谴分,地道東北人锈麸。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像牺蹄,于是被迫代替她去往敵國和親忘伞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359