RocketMq源碼解析

一术荤、NameServer啟動(dòng)

源碼入口:NamesrvStartup#main

1.NamesrvController controller = createNamesrvController(args);
  • 檢測(cè)命令行參數(shù)
  • 創(chuàng)建核心配置對(duì)象它匕,NamesrvConfig疯淫、NettyServerConfig
  • 解析 -c 彩库、-p參數(shù)
  • 檢查RocketMQ_HOME環(huán)境變量
  • final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);創(chuàng)建controller
  • controller.getConfiguration().registerConfig(properties); 注冊(cè)所有配置信息
2.start(controller);
  • controller.initialize()威蕉; 執(zhí)行初始化
    ○ this.kvConfigManager.load(); 加載KV配置
    ○ this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);創(chuàng)建NettyServer網(wǎng)絡(luò)處理對(duì)象
    ○ this.remotingExecutor =Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_")); 創(chuàng)建Netty服務(wù)器工作線程池
    ○ this.registerProcessor(); 注冊(cè)NameServer的Processor 注冊(cè)到RemotingServer中
    ○ NamesrvController.this.routeInfoManager.scanNotActiveBroker() 啟動(dòng)定時(shí)任務(wù)玻粪,移除不活躍的Broker
    ○ NamesrvController.this.kvConfigManager.printAllPeriodically() 定時(shí)打印KV配置信息
  • Runtime.getRuntime().addShutdownHook 注冊(cè)關(guān)閉鉤子梗逮,在關(guān)閉服務(wù)時(shí)釋放資源
  • controller.start()项秉; 啟動(dòng)controller

NameServer的作用主要有兩個(gè):
1.維護(hù)broker的服務(wù)地址信息,并進(jìn)行更新
2.給Producer慷彤、consumer提供Broker的服務(wù)列表


image.png

二娄蔼、Broker啟動(dòng)

源碼入口:Brokerstartup#main

1.createBrokerController(args)
  • 構(gòu)建四個(gè)核心配置對(duì)象:BrokerConfig、NettyServerConfig底哗、NettyClientConfig卵洗、MessageStoreConfig
  • BrokerConfig只解析 -c參數(shù)
  • RocketMq_HOME環(huán)境變量檢查
  • RemotingUtil.string2SocketAddress(addr) 將namesrvAddr地址進(jìn)行拆分
  • messageStoreConfig.getBrokerRole() 通過BrokerId判斷主從:masterId=0犹撒,Deldger集群的所有Broker節(jié)點(diǎn)ID都是-1
  • 解析 -p碱茁、-m參數(shù)官脓,并將解析的參數(shù)添加到四個(gè)核心配置對(duì)象中
  • BrokerController controller = new BrokerController 創(chuàng)建brokerController,將四個(gè)核心配置類傳入
  • controller.getConfiguration().registerConfig(properties); 重新注冊(cè)(更新)配置
  • controller.initialize(); 初始化controller
    ○ 加載磁盤上的配置文件:topicConfigManager前标、consumerOffsetManager坠韩、subscriptionGroupManager、consumerFilterManager
    ○ this.messageStore =new DefaultMessageStore() 構(gòu)建消息存儲(chǔ)組件
    ○ this.messageStore.load() 加載磁盤文件
    ○ this.remotingServer = new NettyRemotingServer 構(gòu)建Netty網(wǎng)絡(luò)組件
    ○ this.fastRemotingServer = new NettyRemotingServer 這個(gè)fastRemotingServer與RemotingServer功能基本差不多炼列,處理VIP端口請(qǐng)求
    ○ 后面就是初始化一些線程池
    ○ this.registerProcessor(); broker注冊(cè)一些Processor處理方法
  • Runtime.getRuntime().addShutdownHook 注冊(cè)關(guān)閉鉤子
2.start(BrokerController controller)
  • this.messageStore.start(); 這里啟動(dòng)服務(wù)主要是為了將CommitLog的寫入事件分發(fā)給ComsumeQueue和IndexFile
  • 啟動(dòng)兩個(gè)Netty服務(wù):remotingServer只搁、fastRemotingServer
  • this.fileWatchService.start(); 文件監(jiān)聽服務(wù)
  • this.brokerOuterAPI.start(); brokerOuterAPI可以理解為一個(gè)Netty客戶端,往外發(fā)請(qǐng)求的組件俭尖,例如發(fā)送心跳
  • this.pullRequestHoldService.start(); 長(zhǎng)輪詢請(qǐng)求暫停服務(wù)
  • this.filterServerManager.start(); 使用filter進(jìn)行過濾
  • BrokerController.this.registerBrokerAll() Broker核心的心跳注冊(cè)任務(wù),主要作用就是將broker注冊(cè)到Namesrv中

broker的核心作用:
1.作為client時(shí)须蜗,向nameServer發(fā)送心跳信息、發(fā)起事務(wù)的狀態(tài)檢查
2.作為服務(wù)端時(shí)目溉,用于存儲(chǔ)消息明肮、響應(yīng)consumer端的請(qǐng)求

三、Netty服務(wù)注冊(cè)框架

四缭付、Broker心跳注冊(cè)過程

源碼入口:BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister())

public synchronized void registerBrokerAll(final boolean checkOrderConfig, boolean oneway, boolean forceRegister) {
    TopicConfigSerializeWrapper topicConfigWrapper = this.getTopicConfigManager().buildTopicConfigSerializeWrapper();

    if (!PermName.isWriteable(this.getBrokerConfig().getBrokerPermission())
        || !PermName.isReadable(this.getBrokerConfig().getBrokerPermission())) {
        ConcurrentHashMap<String, TopicConfig> topicConfigTable = new ConcurrentHashMap<String, TopicConfig>();
        for (TopicConfig topicConfig : topicConfigWrapper.getTopicConfigTable().values()) {
            TopicConfig tmp =
                new TopicConfig(topicConfig.getTopicName(), topicConfig.getReadQueueNums(), topicConfig.getWriteQueueNums(),
                                this.brokerConfig.getBrokerPermission());
            topicConfigTable.put(topicConfig.getTopicName(), tmp);
        }
        topicConfigWrapper.setTopicConfigTable(topicConfigTable);
    }
    //這里才是比較關(guān)鍵的地方柿估。先判斷是否需要注冊(cè),然后調(diào)用doRegisterBrokerAll方法真正去注冊(cè)陷猫。
    if (forceRegister || needRegister(this.brokerConfig.getBrokerClusterName(),
                                      this.getBrokerAddr(),
                                      this.brokerConfig.getBrokerName(),
                                      this.brokerConfig.getBrokerId(),
                                      this.brokerConfig.getRegisterBrokerTimeoutMills())) {
        doRegisterBrokerAll(checkOrderConfig, oneway, topicConfigWrapper);
    }
}
// Broker注冊(cè)最核心的部分
private void doRegisterBrokerAll(boolean checkOrderConfig, boolean oneway,
                                 TopicConfigSerializeWrapper topicConfigWrapper) {
    // 注冊(cè)broker方法
    List<RegisterBrokerResult> registerBrokerResultList = this.brokerOuterAPI.registerBrokerAll(
        this.brokerConfig.getBrokerClusterName(),
        this.getBrokerAddr(),
        this.brokerConfig.getBrokerName(),
        this.brokerConfig.getBrokerId(),
        this.getHAServerAddr(),
        topicConfigWrapper,
        this.filterServerManager.buildNewFilterServerList(),
        oneway,
        this.brokerConfig.getRegisterBrokerTimeoutMills(),
        this.brokerConfig.isCompressedRegister());

    if (registerBrokerResultList.size() > 0) {
        RegisterBrokerResult registerBrokerResult = registerBrokerResultList.get(0);
        if (registerBrokerResult != null) {
            //注冊(cè)完保存主從節(jié)點(diǎn)的地址
            if (this.updateMasterHAServerAddrPeriodically && registerBrokerResult.getHaServerAddr() != null) {
                this.messageStore.updateHaMasterAddress(registerBrokerResult.getHaServerAddr());
            }

            this.slaveSynchronize.setMasterAddr(registerBrokerResult.getMasterAddr());

            if (checkOrderConfig) {
                this.getTopicConfigManager().updateOrderTopicConfig(registerBrokerResult.getKvTable());
            }
        }
    }
}
public List<RegisterBrokerResult> registerBrokerAll(
    final String clusterName,
    final String brokerAddr,
    final String brokerName,
    final long brokerId,
    final String haServerAddr,
    final TopicConfigSerializeWrapper topicConfigWrapper,
    final List<String> filterServerList,
    final boolean oneway,
    final int timeoutMills,
    final boolean compressed) {
    //使用CopyOnWriteArrayList提升并發(fā)安全性
    final List<RegisterBrokerResult> registerBrokerResultList = new CopyOnWriteArrayList<>();
    // 獲取所有nameServer的地址信息
    List<String> nameServerAddressList = this.remotingClient.getNameServerAddressList();
    if (nameServerAddressList != null && nameServerAddressList.size() > 0) {

        final RegisterBrokerRequestHeader requestHeader = new RegisterBrokerRequestHeader();
        requestHeader.setBrokerAddr(brokerAddr);
        requestHeader.setBrokerId(brokerId);
        requestHeader.setBrokerName(brokerName);
        requestHeader.setClusterName(clusterName);
        requestHeader.setHaServerAddr(haServerAddr);
        requestHeader.setCompressed(compressed);

        RegisterBrokerBody requestBody = new RegisterBrokerBody();
        requestBody.setTopicConfigSerializeWrapper(topicConfigWrapper);
        requestBody.setFilterServerList(filterServerList);
        final byte[] body = requestBody.encode(compressed);
        final int bodyCrc32 = UtilAll.crc32(body);
        requestHeader.setBodyCrc32(bodyCrc32);
        //通過CountDownLatch秫舌,保證在所有NameServer上完成注冊(cè)后再一起結(jié)束的妖。
        final CountDownLatch countDownLatch = new CountDownLatch(nameServerAddressList.size());
        for (final String namesrvAddr : nameServerAddressList) {
            brokerOuterExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        RegisterBrokerResult result = registerBroker(namesrvAddr, oneway, timeoutMills, requestHeader, body);
                        if (result != null) {
                            registerBrokerResultList.add(result);
                        }

                        log.info("register broker[{}]to name server {} OK", brokerId, namesrvAddr);
                    } catch (Exception e) {
                        log.warn("registerBroker Exception, {}", namesrvAddr, e);
                    } finally {
                        countDownLatch.countDown();
                    }
                }
            });
        }

        try {
            countDownLatch.await(timeoutMills, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
        }
    }

    return registerBrokerResultList;
}

NameServer處理請(qǐng)求:

//NameServer處理請(qǐng)求的核心代碼
@Override
    public RemotingCommand processRequest(ChannelHandlerContext ctx,
                                          RemotingCommand request) throws RemotingCommandException {

    if (ctx != null) {
        log.debug("receive request, {} {} {}",
                  request.getCode(),
                  RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
                  request);
    }


    switch (request.getCode()) {
        case RequestCode.PUT_KV_CONFIG:
            return this.putKVConfig(ctx, request);
        case RequestCode.GET_KV_CONFIG:
            return this.getKVConfig(ctx, request);
        case RequestCode.DELETE_KV_CONFIG:
            return this.deleteKVConfig(ctx, request);
        case RequestCode.QUERY_DATA_VERSION:
            return queryBrokerTopicConfig(ctx, request);
        case RequestCode.REGISTER_BROKER: //Broker注冊(cè)請(qǐng)求處理。版本默認(rèn)是當(dāng)前框架版本
            Version brokerVersion = MQVersion.value2Version(request.getVersion());
            if (brokerVersion.ordinal() >= MQVersion.Version.V3_0_11.ordinal()) {
                return this.registerBrokerWithFilterServer(ctx, request); //當(dāng)前版本
            } else {
                return this.registerBroker(ctx, request);
            }
        case RequestCode.UNREGISTER_BROKER:
            return this.unregisterBroker(ctx, request);
        case RequestCode.GET_ROUTEINFO_BY_TOPIC:
            return this.getRouteInfoByTopic(ctx, request);
        case RequestCode.GET_BROKER_CLUSTER_INFO:
            return this.getBrokerClusterInfo(ctx, request);
        case RequestCode.WIPE_WRITE_PERM_OF_BROKER:
            return this.wipeWritePermOfBroker(ctx, request);
        case RequestCode.GET_ALL_TOPIC_LIST_FROM_NAMESERVER:
            return getAllTopicListFromNameserver(ctx, request);
        case RequestCode.DELETE_TOPIC_IN_NAMESRV:
            return deleteTopicInNamesrv(ctx, request);
        case RequestCode.GET_KVLIST_BY_NAMESPACE:
            return this.getKVListByNamespace(ctx, request);
        case RequestCode.GET_TOPICS_BY_CLUSTER:
            return this.getTopicsByCluster(ctx, request);
        case RequestCode.GET_SYSTEM_TOPIC_LIST_FROM_NS:
            return this.getSystemTopicListFromNs(ctx, request);
        case RequestCode.GET_UNIT_TOPIC_LIST:
            return this.getUnitTopicList(ctx, request);
        case RequestCode.GET_HAS_UNIT_SUB_TOPIC_LIST:
            return this.getHasUnitSubTopicList(ctx, request);
        case RequestCode.GET_HAS_UNIT_SUB_UNUNIT_TOPIC_LIST:
            return this.getHasUnitSubUnUnitTopicList(ctx, request);
        case RequestCode.UPDATE_NAMESRV_CONFIG:
            return this.updateConfig(ctx, request);
        case RequestCode.GET_NAMESRV_CONFIG:
            return this.getConfig(ctx, request);
        default:
            break;
    }
    return null;
}

實(shí)際就是將broker信息注冊(cè)到routeInfo中:

public RemotingCommand registerBrokerWithFilterServer(ChannelHandlerContext ctx, RemotingCommand request)
    throws RemotingCommandException {
    final RemotingCommand response = RemotingCommand.createResponseCommand(RegisterBrokerResponseHeader.class);
    final RegisterBrokerResponseHeader responseHeader = (RegisterBrokerResponseHeader) response.readCustomHeader();
    final RegisterBrokerRequestHeader requestHeader =
        (RegisterBrokerRequestHeader) request.decodeCommandCustomHeader(RegisterBrokerRequestHeader.class);

    if (!checksum(ctx, request, requestHeader)) {
        response.setCode(ResponseCode.SYSTEM_ERROR);
        response.setRemark("crc32 not match");
        return response;
    }

    RegisterBrokerBody registerBrokerBody = new RegisterBrokerBody();

    if (request.getBody() != null) {
        try {
            registerBrokerBody = RegisterBrokerBody.decode(request.getBody(), requestHeader.isCompressed());
        } catch (Exception e) {
            throw new RemotingCommandException("Failed to decode RegisterBrokerBody", e);
        }
    } else {
        registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setCounter(new AtomicLong(0));
        registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setTimestamp(0);
    }
    //routeInfoManager就是管理路由信息的核心組件足陨。
    RegisterBrokerResult result = this.namesrvController.getRouteInfoManager().registerBroker(
        requestHeader.getClusterName(),
        requestHeader.getBrokerAddr(),
        requestHeader.getBrokerName(),
        requestHeader.getBrokerId(),
        requestHeader.getHaServerAddr(),
        registerBrokerBody.getTopicConfigSerializeWrapper(),
        registerBrokerBody.getFilterServerList(),
        ctx.channel());

    responseHeader.setHaServerAddr(result.getHaServerAddr());
    responseHeader.setMasterAddr(result.getMasterAddr());

    byte[] jsonValue = this.namesrvController.getKvConfigManager().getKVListByNamespace(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG);
    response.setBody(jsonValue);

    response.setCode(ResponseCode.SUCCESS);
    response.setRemark(null);
    return response;
}

五嫂粟、Producer發(fā)送消息

源碼入口:DefaultMQProducer#start
1.this.defaultMQProducerImpl.start(); 生產(chǎn)端啟動(dòng)

public void start(final boolean startFactory) throws MQClientException {
    switch (this.serviceState) {
        case CREATE_JUST:
            // 默認(rèn)就是CREATE_JUST
            this.serviceState = ServiceState.START_FAILED;

            this.checkConfig();
            //修改當(dāng)前的instanceName為當(dāng)前進(jìn)程ID
            if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
                this.defaultMQProducer.changeInstanceNameToPID();
            }
            //客戶端核心的MQ客戶端工廠 對(duì)于事務(wù)消息發(fā)送者,在這里面會(huì)完成事務(wù)消息的發(fā)送者的服務(wù)注冊(cè)
            this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook);
            //注冊(cè)MQ客戶端工廠示例
            boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this);
            if (!registerOK) {
                this.serviceState = ServiceState.CREATE_JUST;
                throw new MQClientException("The producer group[" + this.defaultMQProducer.getProducerGroup()
                                            + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
                                            null);
            }

            this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo());
            //啟動(dòng)示例 --所有客戶端組件都交由mQClientFactory啟動(dòng)
            if (startFactory) {
                mQClientFactory.start();
            }

            log.info("the producer [{}] start OK. sendMessageWithVIPChannel={}", this.defaultMQProducer.getProducerGroup(),
                     this.defaultMQProducer.isSendMessageWithVIPChannel());
            this.serviceState = ServiceState.RUNNING;
            break;
        case RUNNING:
        case START_FAILED:
        case SHUTDOWN_ALREADY:
            throw new MQClientException("The producer service state not OK, maybe started once, "
                                        + this.serviceState
                                        + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
                                        null);
        default:
            break;
    }
    // 向所有的broker發(fā)送心跳
    this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();

    this.startScheduledTask();

}

六墨缘、Consumer消費(fèi)消息

消費(fèi)端入口:DefaultMQPushConsumer#start
this.defaultMQPushConsumerImpl.start();

public synchronized void start() throws MQClientException {
    switch (this.serviceState) {
        case CREATE_JUST:
            log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(),
                     this.defaultMQPushConsumer.getMessageModel(), this.defaultMQPushConsumer.isUnitMode());
            this.serviceState = ServiceState.START_FAILED;

            this.checkConfig();

            this.copySubscription();

            if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) {
                this.defaultMQPushConsumer.changeInstanceNameToPID();
            }
            //客戶端示例工廠星虹,生產(chǎn)者也是交由這個(gè)工廠啟動(dòng)的。
            this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook);
            //負(fù)載均衡策略
            this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup());
            this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel());
            this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer.getAllocateMessageQueueStrategy());
            this.rebalanceImpl.setmQClientFactory(this.mQClientFactory);

            this.pullAPIWrapper = new PullAPIWrapper(
                mQClientFactory,
                this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode());
            this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList);

            if (this.defaultMQPushConsumer.getOffsetStore() != null) {
                this.offsetStore = this.defaultMQPushConsumer.getOffsetStore();
            } else {
                //從這里可以看出镊讼,廣播模式與集群模式的最本質(zhì)區(qū)別就是offset存儲(chǔ)的地方不一樣宽涌。
                switch (this.defaultMQPushConsumer.getMessageModel()) {
                        //廣播模式是在消費(fèi)者本地存儲(chǔ)offset
                    case BROADCASTING:
                        this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
                        break;
                        //集群模式是在Broker遠(yuǎn)端存儲(chǔ)offset
                    case CLUSTERING:
                        this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
                        break;
                    default:
                        break;
                }
                this.defaultMQPushConsumer.setOffsetStore(this.offsetStore);
            }
            this.offsetStore.load();
            //順序消費(fèi)監(jiān)聽創(chuàng)建ConsumeMessageOrderlyService
            if (this.getMessageListenerInner() instanceof MessageListenerOrderly) {
                this.consumeOrderly = true;
                this.consumeMessageService =
                    new ConsumeMessageOrderlyService(this, (MessageListenerOrderly) this.getMessageListenerInner());
                //并發(fā)消費(fèi)監(jiān)聽創(chuàng)建ConsumeMessageConcurrentlyService
            } else if (this.getMessageListenerInner() instanceof MessageListenerConcurrently) {
               this.consumeOrderly = false;
               this.consumeMessageService =
               new ConsumeMessageConcurrentlyService(this, (MessageListenerConcurrently) this.getMessageListenerInner());
            }

           this.consumeMessageService.start();
           //注冊(cè)消費(fèi)者。與生產(chǎn)者類似蝶棋,客戶端只要按要求注冊(cè)即可卸亮,后續(xù)會(huì)隨mQClientFactory一起啟動(dòng)。
           boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this);
           if (!registerOK) {
               this.serviceState = ServiceState.CREATE_JUST;
               this.consumeMessageService.shutdown(defaultMQPushConsumer.getAwaitTerminationMillisWhenShutdown());
               throw new MQClientException("The consumer group[" + this.defaultMQPushConsumer.getConsumerGroup()
               + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
               null);
           }

           mQClientFactory.start();
           log.info("the consumer [{}] start OK.", this.defaultMQPushConsumer.getConsumerGroup());
           this.serviceState = ServiceState.RUNNING;
           break;
           case RUNNING:
           case START_FAILED:
           case SHUTDOWN_ALREADY:
            throw new MQClientException("The PushConsumer service state not OK, maybe started once, "
                + this.serviceState
                + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),null);
           default:
                break;
           }

               this.updateTopicSubscribeInfoWhenSubscriptionChanged();
               this.mQClientFactory.checkClientInBroker();
               this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
               this.mQClientFactory.rebalanceImmediately();
           }

1玩裙、consumer端的消費(fèi)模式:
● 集群模式:集群模式下每個(gè)consumer都會(huì)分配不同的消息
● 廣播模式:廣播模式下每個(gè)消息都推送給所有consumer
2兼贸、關(guān)于offset存儲(chǔ):
● 廣播模式:this.offsetStore = new LocalFileOffsetStore(); 存儲(chǔ)在每個(gè)consumer中
● 集群模式:this.offsetStore = new RemoteBrokerOffsetStore(); 存儲(chǔ)在broker端

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吃溅,隨后出現(xiàn)的幾起案子溶诞,更是在濱河造成了極大的恐慌,老刑警劉巖罕偎,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異京闰,居然都是意外死亡颜及,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門蹂楣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俏站,“玉大人,你說我怎么就攤上這事痊土∫拊” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵赁酝,是天一觀的道長(zhǎng)犯祠。 經(jīng)常有香客問我,道長(zhǎng)酌呆,這世上最難降的妖魔是什么衡载? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮隙袁,結(jié)果婚禮上痰娱,老公的妹妹穿的比我還像新娘弃榨。我一直安慰自己,他們只是感情好梨睁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布鲸睛。 她就那樣靜靜地躺著,像睡著了一般坡贺。 火紅的嫁衣襯著肌膚如雪官辈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天拴念,我揣著相機(jī)與錄音钧萍,去河邊找鬼。 笑死政鼠,一個(gè)胖子當(dāng)著我的面吹牛风瘦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播公般,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼万搔,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了官帘?” 一聲冷哼從身側(cè)響起瞬雹,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刽虹,沒想到半個(gè)月后酗捌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涌哲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年胖缤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阀圾。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哪廓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出初烘,到底是詐尸還是另有隱情涡真,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布肾筐,位于F島的核電站哆料,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吗铐。R本人自食惡果不足惜剧劝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抓歼。 院中可真熱鬧讥此,春花似錦拢锹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至他巨,卻和暖如春充坑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背染突。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工捻爷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人份企。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓也榄,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親司志。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甜紫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

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