聊聊skywalking的jedis-pulgin

本文主要研究一下skywalking的jedis-pulgin

skywalking-plugin.def

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/resources/skywalking-plugin.def

jedis-2.x=org.apache.skywalking.apm.plugin.jedis.v2.define.JedisClusterInstrumentation
jedis-2.x=org.apache.skywalking.apm.plugin.jedis.v2.define.JedisInstrumentation
  • skywalking的jedis-pulgin提供了JedisClusterInstrumentation及JedisInstrumentation两个增强

JedisClusterInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v2/define/JedisClusterInstrumentation.java

public class JedisClusterInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String ARGUMENT_TYPE_NAME = "redis.clients.jedis.HostAndPort";
    private static final String ENHANCE_CLASS = "redis.clients.jedis.JedisCluster";
    private static final String CONSTRUCTOR_WITH_LIST_HOSTANDPORT_ARG_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.jedis.v2.JedisClusterConstructorWithListHostAndPortArgInterceptor";
    private static final String METHOD_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.jedis.v2.JedisMethodInterceptor";
    private static final String CONSTRUCTOR_WITH_HOSTANDPORT_ARG_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.jedis.v2.JedisClusterConstructorWithHostAndPortArgInterceptor";

    @Override
    public ClassMatch enhanceClass() {
        return byName(ENHANCE_CLASS);
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[] {
            new ConstructorInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getConstructorMatcher() {
                    return takesArgument(0, Set.class);
                }

                @Override
                public String getConstructorInterceptor() {
                    return CONSTRUCTOR_WITH_LIST_HOSTANDPORT_ARG_INTERCEPT_CLASS;
                }
            },
            new ConstructorInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getConstructorMatcher() {
                    return takesArgumentWithType(0, ARGUMENT_TYPE_NAME);
                }

                @Override
                public String getConstructorInterceptor() {
                    return CONSTRUCTOR_WITH_HOSTANDPORT_ARG_INTERCEPT_CLASS;
                }
            }
        };
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return RedisMethodMatch.INSTANCE.getJedisClusterMethodMatcher();
                }

                @Override
                public String getMethodsInterceptor() {
                    return METHOD_INTERCEPT_CLASS;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }
}
  • JedisClusterInstrumentation继承了ClassInstanceMethodsEnhancePluginDefine,它增强的是redis.clients.jedis.JedisCluster类;它使用org.apache.skywalking.apm.plugin.jedis.v2.JedisClusterConstructorWithListHostAndPortArgInterceptor增强其第一个参数类型为Set的构造方法;它使用org.apache.skywalking.apm.plugin.jedis.v2.JedisClusterConstructorWithHostAndPortArgInterceptor增强第一个参数类型为redis.clients.jedis.HostAndPort的构造方法;它使用org.apache.skywalking.apm.plugin.jedis.v2.JedisMethodInterceptor增强了RedisMethodMatch.INSTANCE.getJedisClusterMethodMatcher()匹配的方法

JedisClusterConstructorWithListHostAndPortArgInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v2/JedisClusterConstructorWithListHostAndPortArgInterceptor.java

public class JedisClusterConstructorWithListHostAndPortArgInterceptor implements InstanceConstructorInterceptor {

    @Override
    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
        StringBuilder redisConnInfo = new StringBuilder();
        Set<HostAndPort> hostAndPorts = (Set<HostAndPort>)allArguments[0];
        for (HostAndPort hostAndPort : hostAndPorts) {
            redisConnInfo.append(hostAndPort.toString()).append(";");
        }

        objInst.setSkyWalkingDynamicField(PeerFormat.shorten(redisConnInfo.toString()));
    }
}
  • JedisClusterConstructorWithListHostAndPortArgInterceptor实现了InstanceConstructorInterceptor接口,其onConstruct取出hostAndPorts信息然后设置到objInst的skyWalkingDynamicField

JedisClusterConstructorWithHostAndPortArgInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v2/JedisClusterConstructorWithHostAndPortArgInterceptor.java

public class JedisClusterConstructorWithHostAndPortArgInterceptor implements InstanceConstructorInterceptor {

    @Override
    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
        HostAndPort hostAndPort = (HostAndPort)allArguments[0];
        objInst.setSkyWalkingDynamicField(hostAndPort.getHost() + ":" + hostAndPort.getPort());
    }
}
  • JedisClusterConstructorWithHostAndPortArgInterceptor实现了InstanceConstructorInterceptor接口,其onConstruct方法获取hostAndPort信息然后设置到objInst的skyWalkingDynamicField

JedisMethodInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v2/JedisMethodInterceptor.java

public class JedisMethodInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
        String peer = String.valueOf(objInst.getSkyWalkingDynamicField());
        AbstractSpan span = ContextManager.createExitSpan("Jedis/" + method.getName(), peer);
        span.setComponent(ComponentsDefine.JEDIS);
        Tags.DB_TYPE.set(span, "Redis");
        SpanLayer.asCache(span);

        if (allArguments.length > 0 && allArguments[0] instanceof String) {
            Tags.DB_STATEMENT.set(span, method.getName() + " " + allArguments[0]);
        } else if (allArguments.length > 0 && allArguments[0] instanceof byte[]) {
            Tags.DB_STATEMENT.set(span, method.getName());
        }
    }

    @Override
    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes, Object ret) throws Throwable {
        ContextManager.stopSpan();
        return ret;
    }

    @Override
    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes, Throwable t) {
        AbstractSpan span = ContextManager.activeSpan();
        span.errorOccurred();
        span.log(t);
    }
}
  • JedisMethodInterceptor实现了InstanceMethodsAroundInterceptor接口,其beforeMethod方法获取peer信息设置DB_TYPE、DB_STATEMENT信息;其afterMethod方法执行ContextManager.stopSpan();其handleMethodException方法执行span.errorOccurred()以及span.log(t)

JedisInstrumentation

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v2/define/JedisInstrumentation.java

public class JedisInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String HOST_AND_PORT_ARG_TYPE_NAME = "redis.clients.jedis.HostAndPort";
    private static final String JEDIS_SHARD_INFO_ARG_TYPE_NAME = "redis.clients.jedis.JedisShardInfo";
    private static final String ENHANCE_CLASS = "redis.clients.jedis.Jedis";
    private static final String CONSTRUCTOR_WITH_STRING_ARG_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.jedis.v2.JedisConstructorWithStringArgInterceptor";
    private static final String CONSTRUCTOR_WITH_SHARD_INFO_ARG_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.jedis.v2.JedisConstructorWithShardInfoArgInterceptor";
    private static final String CONSTRUCTOR_WITH_HOST_AND_PORT_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.jedis.v2.JedisClusterConstructorWithHostAndPortArgInterceptor";
    private static final String CONSTRUCTOR_WITH_URI_ARG_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.jedis.v2.JedisConstructorWithUriArgInterceptor";
    private static final String JEDIS_METHOD_INTERCET_CLASS = "org.apache.skywalking.apm.plugin.jedis.v2.JedisMethodInterceptor";

    @Override
    public ClassMatch enhanceClass() {
        return byName(ENHANCE_CLASS);
    }

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[] {
            new ConstructorInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getConstructorMatcher() {
                    return takesArgument(0, String.class);
                }

                @Override
                public String getConstructorInterceptor() {
                    return CONSTRUCTOR_WITH_STRING_ARG_INTERCEPT_CLASS;
                }
            },
            new ConstructorInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getConstructorMatcher() {
                    return takesArgumentWithType(0, HOST_AND_PORT_ARG_TYPE_NAME);
                }

                @Override
                public String getConstructorInterceptor() {
                    return CONSTRUCTOR_WITH_HOST_AND_PORT_INTERCEPT_CLASS;
                }
            },
            new ConstructorInterceptPoint() {
                @Override
                 public ElementMatcher<MethodDescription> getConstructorMatcher() {
                    return takesArgumentWithType(0, JEDIS_SHARD_INFO_ARG_TYPE_NAME);
                }

                @Override
                public String getConstructorInterceptor() {
                    return CONSTRUCTOR_WITH_SHARD_INFO_ARG_INTERCEPT_CLASS;
                }
            },
            new ConstructorInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getConstructorMatcher() {
                    return takesArgument(0, URI.class);
                }

                @Override
                public String getConstructorInterceptor() {
                    return CONSTRUCTOR_WITH_URI_ARG_INTERCEPT_CLASS;
                }
            }
        };
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return RedisMethodMatch.INSTANCE.getJedisMethodMatcher();
                }

                @Override
                public String getMethodsInterceptor() {
                    return JEDIS_METHOD_INTERCET_CLASS;
                }

                @Override public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }
}
  • JedisInstrumentation继承了ClassInstanceMethodsEnhancePluginDefine,它增强的是redis.clients.jedis.Jedis类;它使用org.apache.skywalking.apm.plugin.jedis.v2.JedisConstructorWithStringArgInterceptor增强其第一个参数类型为String的构造器;它使用org.apache.skywalking.apm.plugin.jedis.v2.JedisClusterConstructorWithHostAndPortArgInterceptor增强第一个参数类型为redis.clients.jedis.HostAndPort的构造器;它使用org.apache.skywalking.apm.plugin.jedis.v2.JedisConstructorWithShardInfoArgInterceptor增强第一个参数类型为redis.clients.jedis.JedisShardInfo的构造器;它使用org.apache.skywalking.apm.plugin.jedis.v2.JedisConstructorWithUriArgInterceptor增强第一个参数类型为URI的构造器;它使用org.apache.skywalking.apm.plugin.jedis.v2.JedisMethodInterceptor增强RedisMethodMatch.INSTANCE.getJedisMethodMatcher()匹配的方法

JedisConstructorWithStringArgInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v2/JedisConstructorWithStringArgInterceptor.java

public class JedisConstructorWithStringArgInterceptor implements InstanceConstructorInterceptor {

    @Override
    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
        String host = (String)allArguments[0];
        String port = "6379";
        if (allArguments.length > 1) {
            port = String.valueOf(allArguments[1]);
        }

        objInst.setSkyWalkingDynamicField(host + ":" + port);
    }
}
  • JedisConstructorWithStringArgInterceptor实现了InstanceConstructorInterceptor接口,其onConstruct获取port信息并设置到objInst的skyWalkingDynamicField

JedisConstructorWithShardInfoArgInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v2/JedisConstructorWithShardInfoArgInterceptor.java

public class JedisConstructorWithShardInfoArgInterceptor implements InstanceConstructorInterceptor {

    @Override
    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
        String redisConnInfo;
        JedisShardInfo shardInfo = (JedisShardInfo)allArguments[0];
        redisConnInfo = shardInfo.getHost() + ":" + shardInfo.getPort();
        objInst.setSkyWalkingDynamicField(redisConnInfo);
    }
}
  • JedisConstructorWithShardInfoArgInterceptor实现了InstanceConstructorInterceptor接口,其onConstruct方法获取shardInfo信息并设置到objInst的skyWalkingDynamicField

JedisConstructorWithUriArgInterceptor

public class JedisConstructorWithUriArgInterceptor implements InstanceConstructorInterceptor {

    @Override
    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
        URI uri = (URI)allArguments[0];
        objInst.setSkyWalkingDynamicField(uri.getHost() + ":" + uri.getPort());
    }
}
  • JedisConstructorWithUriArgInterceptor实现了InstanceConstructorInterceptor接口,其onConstruct方法获取uri信息并设置到objInst的skyWalkingDynamicField

JedisMethodInterceptor

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v2/JedisMethodInterceptor.java

public class JedisMethodInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
        String peer = String.valueOf(objInst.getSkyWalkingDynamicField());
        AbstractSpan span = ContextManager.createExitSpan("Jedis/" + method.getName(), peer);
        span.setComponent(ComponentsDefine.JEDIS);
        Tags.DB_TYPE.set(span, "Redis");
        SpanLayer.asCache(span);

        if (allArguments.length > 0 && allArguments[0] instanceof String) {
            Tags.DB_STATEMENT.set(span, method.getName() + " " + allArguments[0]);
        } else if (allArguments.length > 0 && allArguments[0] instanceof byte[]) {
            Tags.DB_STATEMENT.set(span, method.getName());
        }
    }

    @Override
    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes, Object ret) throws Throwable {
        ContextManager.stopSpan();
        return ret;
    }

    @Override
    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
        Class<?>[] argumentsTypes, Throwable t) {
        AbstractSpan span = ContextManager.activeSpan();
        span.errorOccurred();
        span.log(t);
    }
}
  • JedisMethodInterceptor实现了InstanceMethodsAroundInterceptor接口,其beforeMethod方法获取peer信息设置DB_TYPE、DB_STATEMENT;其afterMethod方法执行ContextManager.stopSpan()方法;其handleMethodException方法执行span.errorOccurred()及span.log(t)方法

RedisMethodMatch

skywalking-6.6.0/apm-sniffer/apm-sdk-plugin/jedis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/jedis/v2/RedisMethodMatch.java

public enum RedisMethodMatch {
    INSTANCE;

    private ElementMatcher.Junction<MethodDescription> getIntersectionalMethodMacher() {
        return named("zcount").or(named("sunionstore")).or(named("zunionstore"))
            .or(named("del")).or(named("zinterstore")).or(named("echo"))
            .or(named("hscan")).or(named("psubscribe")).or(named("type"))
            .or(named("sinterstore")).or(named("setex")).or(named("zlexcount"))
            .or(named("brpoplpush")).or(named("bitcount")).or(named("llen"))
            .or(named("zscan")).or(named("lpushx")).or(named("bitpos"))
            .or(named("setnx")).or(named("hvals")).or(named("evalsha"))
            .or(named("substr")).or(named("geodist")).or(named("zrangeByLex"))
            .or(named("geoadd")).or(named("expire")).or(named("bitop"))
            .or(named("zrangeByScore")).or(named("smove")).or(named("lset"))
            .or(named("decrBy")).or(named("pttl")).or(named("scan"))
            .or(named("zrank")).or(named("blpop")).or(named("rpoplpush"))
            .or(named("zremrangeByLex")).or(named("get")).or(named("lpop"))
            .or(named("persist")).or(named("scriptExists")).or(named("georadius"))
            .or(named("set")).or(named("srandmember")).or(named("incr")).or(named("setbit"))
            .or(named("hexists")).or(named("expireAt")).or(named("pexpire")).or(named("zcard"))
            .or(named("bitfield")).or(named("zrevrangeByLex")).or(named("sinter")).or(named("srem"))
            .or(named("getrange")).or(named("rename")).or(named("zrevrank")).or(named("exists"))
            .or(named("setrange")).or(named("zremrangeByRank")).or(named("sadd")).or(named("sdiff"))
            .or(named("zrevrange")).or(named("getbit")).or(named("scard")).or(named("sdiffstore"))
            .or(named("zrevrangeByScore")).or(named("zincrby")).or(named("rpushx")).or(named("psetex"))
            .or(named("zrevrangeWithScores")).or(named("strlen")).or(named("hdel")).or(named("zremrangeByScore"))
            .or(named("geohash")).or(named("brpop")).or(named("lrem")).or(named("hlen")).or(named("decr"))
            .or(named("scriptLoad")).or(named("lpush")).or(named("lindex")).or(named("zrange")).or(named("incrBy"))
            .or(named("getSet")).or(named("ltrim")).or(named("incrByFloat")).or(named("rpop")).or(named("sort"))
            .or(named("zrevrangeByScoreWithScores")).or(named("pfadd")).or(named("eval")).or(named("linsert"))
            .or(named("pfcount")).or(named("hkeys")).or(named("hsetnx")).or(named("hincrBy")).or(named("hgetAll"))
            .or(named("hset")).or(named("spop")).or(named("zrangeWithScores")).or(named("hincrByFloat"))
            .or(named("hmset")).or(named("renamenx")).or(named("zrem")).or(named("msetnx")).or(named("hmget"))
            .or(named("sunion")).or(named("hget")).or(named("zadd")).or(named("move")).or(named("subscribe"))
            .or(named("geopos")).or(named("mset")).or(named("zrangeByScoreWithScores")).or(named("zscore"))
            .or(named("pexpireAt")).or(named("georadiusByMember")).or(named("ttl")).or(named("lrange"))
            .or(named("smembers")).or(named("pfmerge")).or(named("rpush")).or(named("publish"))
            .or(named("mget")).or(named("sscan")).or(named("append")).or(named("sismember"));
    }

    public ElementMatcher<MethodDescription> getJedisMethodMatcher() {
        return getIntersectionalMethodMacher().or(named("sentinelMasters")).or(named("clusterReplicate")).or(named("readonly"))
            .or(named("randomKey")).or(named("clusterInfo")).or(named("pubsubNumSub"))
            .or(named("sentinelSlaves")).or(named("clusterSetSlotImporting")).or(named("clusterSlaves"))
            .or(named("clusterFailover")).or(named("clusterSetSlotMigrating")).or(named("watch"))
            .or(named("clientKill")).or(named("clusterKeySlot")).or(named("clusterCountKeysInSlot"))
            .or(named("sentinelGetMasterAddrByName")).or(named("objectRefcount")).or(named("clusterMeet"))
            .or(named("sentinelSet")).or(named("clusterSetSlotNode")).or(named("clusterAddSlots"))
            .or(named("pubsubNumPat")).or(named("slowlogGet")).or(named("sentinelReset")).or(named("clusterNodes"))
            .or(named("sentinelMonitor")).or(named("configGet")).or(named("objectIdletime"))
            .or(named("pubsubChannels")).or(named("getParams")).or(named("sentinelRemove"))
            .or(named("migrate")).or(named("clusterForget")).or(named("asking")).or(named("keys"))
            .or(named("clientSetname")).or(named("clusterSaveConfig")).or(named("configSet"))
            .or(named("dump")).or(named("clusterFlushSlots")).or(named("clusterGetKeysInSlot"))
            .or(named("clusterReset")).or(named("restore")).or(named("clusterDelSlots"))
            .or(named("sentinelFailover")).or(named("clusterSetSlotStable")).or(named("objectEncoding"));
    }

    public ElementMatcher<MethodDescription> getJedisClusterMethodMatcher() {
        return getIntersectionalMethodMacher();
    }
}
  • RedisMethodMatch的getIntersectionalMethodMacher主要是匹配redis相关命令对应的方法;getJedisMethodMatcher主要匹配cluster、sentinel相关命令

小结

skywalking的jedis-pulgin提供了JedisClusterInstrumentation及JedisInstrumentation两个增强

doc

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容