Redis實(shí)現(xiàn)分布式鎖楷兽,通過(guò)注解進(jìn)行加鎖

項(xiàng)目介紹:為公司項(xiàng)目進(jìn)行封裝common包,需要分布式鎖功能的實(shí)現(xiàn)华临,決定寫一個(gè)比較簡(jiǎn)易拿來(lái)就用的包芯杀。

分布式鎖實(shí)現(xiàn)方式通常分為3種:

  • 數(shù)據(jù)庫(kù)原生實(shí)現(xiàn)
  • redis中setNX
  • zookeeper通過(guò)有序臨時(shí)節(jié)點(diǎn)
    以上這3種方式網(wǎng)絡(luò)上的理論知識(shí)有很多,這里只講項(xiàng)目實(shí)現(xiàn)雅潭。
    項(xiàng)目結(jié)構(gòu)圖:


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

1.pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!--spring boot 版本-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
    </parent>
    <groupId>com.ict.common</groupId>
    <artifactId>Redis-Distribute-Lock</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!--Springboot與redis起步依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--AOP切面依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!--redisTemplate中jackson序列化依賴-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <!--jedis客戶端-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <type>jar</type>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

2.RedisConfig配置類

package com.ict.common.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;

/**
 * @author: DevWenjiang
 * Description: Redis配置類
 * @date : 2020-06-10 17:34
 */
@Configuration
@EnableAutoConfiguration
public class RedisConfig {

    @Value("${spring.redis.defaultExpiration:3600}")
    private Long defaultExpiration;
    /**
     * 創(chuàng)建JedisPoolConfig對(duì)象
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.redis")
    public JedisPoolConfig jedisPoolConfig(){
        return new JedisPoolConfig();
    }

    /**
     * 創(chuàng)建JedisConnectionFactory對(duì)象
     */
    @Primary
    @Bean
    @ConfigurationProperties(prefix = "spring.redis")
    public JedisConnectionFactory jedisConnectionFactory(){
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        JedisPoolConfig config = jedisPoolConfig();
        jedisConnectionFactory.setPoolConfig(config);
        return jedisConnectionFactory;
    }




    @Bean
    public RedisTemplate<String,Object> jdkRedisTemplate(){
        RedisTemplate<String,Object> jdkRedisTemplate = new RedisTemplate<>();
        jdkRedisTemplate.setConnectionFactory(jedisConnectionFactory());
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        jdkRedisTemplate.setHashKeySerializer(stringRedisSerializer);
        jdkRedisTemplate.setKeySerializer(stringRedisSerializer);
        jdkRedisTemplate.afterPropertiesSet();
        return jdkRedisTemplate;
    }

    /**
     * 創(chuàng)建以jackson序列化方式redisTemplate
     * @return
     */
    @Bean
    public RedisTemplate<String,Object> redisTemplate(){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        Jackson2JsonRedisSerializer<?> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        //設(shè)置對(duì)象mapper
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        objectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        //設(shè)置key與value序列化方式
        redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 創(chuàng)建stringRedisTemplate
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate(){
        return new StringRedisTemplate(jedisConnectionFactory());
    }

    /**
     * 緩存管理器
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        //初始化一個(gè)RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        //設(shè)置CacheManager的值序列化方式為json序列化
        RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
                .fromSerializer(jsonSerializer);
        RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(pair);
        //設(shè)置默認(rèn)超過(guò)期時(shí)間是30秒
        defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
        //初始化RedisCacheManager
        return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
    }

}

3.LockStrategy所類型枚舉

package com.ict.common.enums;

/**
 * @author: DevWenjiang
 * Description:
 * @date : 2020-06-10 17:29
 */
public enum LockStrategy {
    //失敗重試
    WAIT_RETRY,
    //忽略
    IGNORE,
    //拋出異常
    THROWABLE
}

4.RedisDistributeLockAnnotion分布式鎖注解

package com.ict.common.annotion;

import com.ict.common.enums.LockStrategy;
import org.springframework.core.annotation.AliasFor;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author: DevWenjiang
 * Description: redis分布式鎖注解(需要加鎖的方法加入注解)
 * @date : 2020-06-10 17:23
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisDistributeLockAnnotion {
    @AliasFor("value")
    String key() default "";
    @AliasFor("key")
    String value() default "";
    //鎖策略:失敗忽略|重試|拋出異常
    LockStrategy LOCK_STRATEGY() default LockStrategy.IGNORE;
    /**
     * 獲鎖最長(zhǎng)時(shí)間
     */
    long maxLockTime() default 30000l;
    /**
     * 等待重試時(shí)間
     */
    long retryTime() default 500l;
    /**
     * 最大重試次數(shù)
     */
    int maxRetryTimes() default 10;
}

5.RedisClient封裝的Redis客戶端操作

package com.ict.common.lock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author : DevWenjiang
 * Description: Redis客戶端類揭厚,與redis服務(wù)器進(jìn)行數(shù)據(jù)交互類
 * @date : 2020/6/10-20:13
 */
@Component
public class RedisClient {

    @Autowired()
    @Qualifier("redisTemplate")
    private RedisTemplate<String, Object> redisTemplate;


    @Autowired()
    @Qualifier("stringRedisTemplate")
    private StringRedisTemplate stringRedisTemplate;

    /**
     * string字符串set方法
     *
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    /**
     * string字符串set方法,帶有過(guò)期時(shí)間
     *
     * @param key
     * @param value
     * @param expireTime
     * @param timeUnit
     */
    public void set(String key, String value, Long expireTime, TimeUnit timeUnit) {
        stringRedisTemplate.opsForValue().set(key, value, expireTime, timeUnit);
    }

    /**
     * Object對(duì)象set方法
     *
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * Object對(duì)象set方法,帶有過(guò)期時(shí)間
     *
     * @param key
     * @param value
     * @param expireTime
     * @param timeUnit
     */
    public void set(String key, Object value, Long expireTime, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, expireTime, timeUnit);
    }

    /**
     * setNX方法
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean setNX(String key, Object value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    /**
     * setNX,帶有過(guò)期時(shí)間
     * @param key
     * @param value
     * @param expireTime
     * @param timeUnit
     * @return
     */
    public Boolean setNX(String key, Object value, Long expireTime, TimeUnit timeUnit) {
        return redisTemplate.execute(new SessionCallback<Boolean>() {
            @Override
            public Boolean execute(RedisOperations redisOperations) throws DataAccessException {
                //開啟事務(wù)
                redisOperations.multi();
                redisOperations.opsForValue().setIfAbsent(key, value);
                redisOperations.expire(key, expireTime, timeUnit);
                List exec = redisOperations.exec();
                if (exec == null || exec.isEmpty()) {
                    return false;
                }
                return (Boolean) exec.get(0);
            }
        });
    }

    /**
     *  自增
     * @param key
     * @param i
     * @return
     */
    public Long increment(String key, int i) {

        return stringRedisTemplate.boundValueOps(key).increment(i);
    }

    /**
     * 獲取key對(duì)應(yīng)值
     * @param key
     * @return
     */
    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * Object的
     * @param key
     * @return
     */
    public Object getObj(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 模糊查詢滿足要求的key
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern){
        return redisTemplate.keys(pattern);
    }

    /**
     * 獲得過(guò)期時(shí)間(以秒為單位)
     * @param key
     * @return
     */
    public Long getExpire(String key) {
        return stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 設(shè)置過(guò)期時(shí)間
     * @param key
     * @param timeout
     * @param unit
     * @return
     */
    public Boolean expire(String key,Long timeout,TimeUnit unit){
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 刪除key
     * @param key
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 刪除多個(gè)key
     * @param keys
     */
    public void delete(Set<String> keys){
        redisTemplate.delete(keys);
    }

    /**
     * 對(duì)key進(jìn)行hash
     * @param key
     * @return
     */
    public boolean haskey(String key) {
        return stringRedisTemplate.hasKey(key);
    }

    /**
     * 右進(jìn)
     * @param key
     * @param value
     * @return
     */
    public Long rightPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 左出
     * @param key
     * @return
     */
    public Object leftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 獲取list集合
     * @param key
     * @return
     */
    public List<Object> getList(String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }
}

6.RedisDistributeLock分布式具體實(shí)現(xiàn)類

package com.ict.common.lock;


import org.springframework.util.Assert;

import java.util.concurrent.TimeUnit;

/**
 * @author : DevWenjiang
 * Description: redis分布式鎖實(shí)現(xiàn)類
 * @date : 2020/6/10-20:38
 */
public class RedisDistributeLock {
    //默認(rèn)最大線程獲取鎖時(shí)間
    private static final Long DEFAULT_MAX_LOCK_TIME = 300000l;//30s

    //redis客戶端
    private RedisClient redisClient;

    //key
    private String key;

    //最大鎖時(shí)間
    private Long maxLockTime;

    //持有鎖狀態(tài)
    private Boolean isLock;


    private RedisDistributeLock(RedisClient redisClient, String key, Long maxLockTime) {
        Assert.notNull(redisClient, "redisClient不能為空");
        Assert.hasText(key, "key不能為空");
        this.redisClient = redisClient;
        this.key = key;
        this.maxLockTime = maxLockTime;
    }

    /**
     * 建造者模式
     * @param redisClient
     * @param key
     * @param maxLockTime
     * @return
     */
    public static RedisDistributeLock build(RedisClient redisClient,String key,Long maxLockTime){
        return new RedisDistributeLock(redisClient,key,maxLockTime);
    }

    public static RedisDistributeLock build(RedisClient redisClient,String key){
        return new RedisDistributeLock(redisClient,key,DEFAULT_MAX_LOCK_TIME);
    }

    /**
     * 獲取鎖
     * @return
     */
    public Boolean lock(){
        Boolean aBoolean = redisClient.setNX(key, "1", maxLockTime, TimeUnit.MILLISECONDS);
        if (aBoolean){
            isLock = true;
            return true;
        }
        isLock = false;
        return false;

    }

    /**
     * 釋放鎖
     */
    public void unlock(){
        if (isLock){
            redisClient.delete(key);
            isLock = false;
        }
    }
}

7.RedisDistributeLockAspect分布式鎖切面增強(qiáng)類

package com.ict.common.aop;

import com.ict.common.annotion.RedisDistributeLockAnnotion;
import com.ict.common.exception.RedisDistributeLockException;
import com.ict.common.lock.RedisClient;
import com.ict.common.lock.RedisDistributeLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;


/**
 * @author: DevWenjiang
 * Description: Redis分布式鎖切面類,加入注解開啟分布式鎖
 * @date : 2020-06-11 09:36
 */
@Component
@Aspect
public class RedisDistributeLockAspect {
    //日志對(duì)象
    private static final Logger log = LoggerFactory.getLogger(RedisDistributeLockAspect.class);

    //注入redisClient
    @Autowired
    private RedisClient redisClient;


    //存放重試次數(shù)的ThreadLocal
    private static ThreadLocal<Integer> retryTimes = new ThreadLocal<>();

    //注解切面寻馏,只要方法上加該注解就會(huì)進(jìn)行切面方法增強(qiáng)
    @Pointcut("@annotation(com.ict.common.annotion.RedisDistributeLockAnnotion)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object aroundMethod(ProceedingJoinPoint pj) throws RedisDistributeLockException {
        //獲取方法參數(shù)
        Object[] args = pj.getArgs();
        //獲取方法簽名,方法名稱
        MethodSignature signature = (MethodSignature) pj.getSignature();
        //獲取參數(shù)名
        String[] parameterNames = signature.getParameterNames();
        //存放參數(shù)名:參數(shù)值
        HashMap<String, Object> params = new HashMap<>();
        if (null != parameterNames && parameterNames.length > 0 && null != args && args.length > 0) {
            for (int i = 0; i < parameterNames.length; i++) {
                params.put(parameterNames[i], args[i]);
            }
        }
        //MethodSignature signature = (MethodSignature) pj.getSignature();
        //獲取方法注解
        RedisDistributeLockAnnotion redisDistributeLockAnnotion = signature.getMethod().getAnnotation(RedisDistributeLockAnnotion.class);
        //獲取key
        String key = redisDistributeLockAnnotion.key();
        if (null == key || key.trim().equals("")) {
            key = redisDistributeLockAnnotion.value();
        }
        if (null == key || key.trim().equals("")) {

            log.error("key值不能為空");
            throw new RedisDistributeLockException("key值不能為空");
        }
        //創(chuàng)建redis分布式鎖對(duì)象
        RedisDistributeLock redisDistributeLock = RedisDistributeLock.build(redisClient, key, redisDistributeLockAnnotion.maxLockTime());
        try {
            Boolean lock = redisDistributeLock.lock();
            if (lock){
                try {
                    //獲取鎖成功,執(zhí)行方法
                    System.out.println(Thread.currentThread().getName()+"獲取到鎖"+key);
                    Object proceed = pj.proceed(args);
                    //移除ThreadLocal中的重試次數(shù)
                    retryTimes.remove();
                    //返回執(zhí)行結(jié)果
                    return proceed;
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
            }else {
                System.out.println(Thread.currentThread().getName()+"沒有獲取到鎖"+key);
            }

            switch (redisDistributeLockAnnotion.LOCK_STRATEGY()){
                case IGNORE:
                    break;
                case THROWABLE:
                    throw new RedisDistributeLockException("該"+key+"已經(jīng)被獲取");
                case WAIT_RETRY:
                    //獲取
                    Integer retryTime = retryTimes.get();
                    if (retryTime != null &&retryTime >= redisDistributeLockAnnotion.maxRetryTimes()){
                        retryTimes.remove();
                        throw new RedisDistributeLockException(Thread.currentThread().getId()+"嘗試獲取鎖失敗超過(guò)了最大重試次數(shù),key="+key);
                    }
                    if (retryTime == null){
                        retryTime = 1;
                    }else {
                        retryTime++;
                    }
                    retryTimes.set(retryTime);
                    try{
                        Thread.sleep(redisDistributeLockAnnotion.retryTime());
                    }catch (Exception e){
                        log.error(e.getMessage(),e);
                    }
                    aroundMethod(pj);
                    break;
                default:break;
            }
            return null;
        }finally {
            redisDistributeLock.unlock();
        }
    }
}

8.RedisDistributeLockException自定義異常類

package com.ict.common.exception;

/**
 * @author : DevWenjiang
 * Description: redis分布式鎖異常類
 * @date : 2020/6/10-20:10
 */
public class RedisDistributeLockException extends Exception {
    public RedisDistributeLockException() {
    }
    public RedisDistributeLockException(String message) {
        super(message);
    }
    public RedisDistributeLockException(String message, Throwable cause) {
        super(message, cause);
    }
    public RedisDistributeLockException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

9.application.yml配置文件(此項(xiàng)目無(wú)需定義棋弥,只需要調(diào)用此服務(wù)的非服務(wù)上有即可)

server:
  port: 8800
  tomcat:
    uri-encoding: UTF-8
  servlet:
    context-path: /
spring:
  redis:
    database: 2
    hostName: 119.3.182.194
    port: 6386
    password: ict2020.
    pool:
      maxActive: 50
      maxWait: 3000
      maxIdle: 30
      minIdle: 10
      timeout: 1000
    defaultExpiration: 3600

10.多線程運(yùn)行結(jié)果圖,測(cè)試時(shí)依據(jù)商品庫(kù)存所做诚欠,庫(kù)存不會(huì)出現(xiàn)超買現(xiàn)象


運(yùn)行效果圖

項(xiàng)目中用到的類就這些顽染,可以進(jìn)行一些擴(kuò)展等等漾岳;也在嘗試中。
項(xiàng)目github地址:https://github.com/yanwenjiang01/Redis_Distribute_Lock.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粉寞,一起剝皮案震驚了整個(gè)濱河市尼荆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唧垦,老刑警劉巖捅儒,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異振亮,居然都是意外死亡巧还,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門坊秸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)麸祷,“玉大人,你說(shuō)我怎么就攤上這事褒搔〗纂梗” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵星瘾,是天一觀的道長(zhǎng)走孽。 經(jīng)常有香客問我,道長(zhǎng)琳状,這世上最難降的妖魔是什么磕瓷? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮算撮,結(jié)果婚禮上生宛,老公的妹妹穿的比我還像新娘。我一直安慰自己肮柜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布倒彰。 她就那樣靜靜地躺著审洞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪待讳。 梳的紋絲不亂的頭發(fā)上芒澜,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音创淡,去河邊找鬼痴晦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛琳彩,可吹牛的內(nèi)容都是我干的誊酌。 我是一名探鬼主播部凑,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碧浊!你這毒婦竟也來(lái)了涂邀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤箱锐,失蹤者是張志新(化名)和其女友劉穎比勉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驹止,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浩聋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了臊恋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衣洁。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖捞镰,靈堂內(nèi)的尸體忽然破棺而出闸与,到底是詐尸還是另有隱情,我是刑警寧澤岸售,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布践樱,位于F島的核電站,受9級(jí)特大地震影響凸丸,放射性物質(zhì)發(fā)生泄漏拷邢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一屎慢、第九天 我趴在偏房一處隱蔽的房頂上張望瞭稼。 院中可真熱鬧,春花似錦腻惠、人聲如沸环肘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)悔雹。三九已至,卻和暖如春欣喧,著一層夾襖步出監(jiān)牢的瞬間腌零,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工唆阿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留益涧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓驯鳖,卻偏偏與公主長(zhǎng)得像闲询,于是被迫代替她去往敵國(guó)和親久免。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344