AWS Lambda 借助 Serverless Framework,迅速起飛

前言

微服務(wù)架構(gòu)有別于傳統(tǒng)的單體式應(yīng)用方案贷帮,我們可將單體應(yīng)用拆分成多個(gè)核心功能戚揭。每個(gè)功能都被稱為一項(xiàng)服務(wù),可以單獨(dú)構(gòu)建和部署撵枢,這意味著各項(xiàng)服務(wù)在工作時(shí)不會(huì)互相影響

這種設(shè)計(jì)理念被進(jìn)一步應(yīng)用民晒,就變成了無(wú)服務(wù)(Serverless)〕荩「無(wú)服務(wù)」看似挺荒唐的潜必,其實(shí)服務(wù)器依舊存在,只是我們不需要關(guān)注或預(yù)置服務(wù)器沃但。這讓開(kāi)發(fā)人員的精力更集中——只關(guān)注功能實(shí)現(xiàn)

Serverless 的典型便是 AWS Lambda

AWS Lambda

如果你是 Java 開(kāi)發(fā)人員磁滚,你應(yīng)該聽(tīng)說(shuō)過(guò)或使用過(guò) JDK 1.8 里面的 Lambda,但是 AWS 中的 Lambda 和 JDK 中的 Lambda 沒(méi)有任何關(guān)系

這里的 AWS Lambda 就是一種計(jì)算服務(wù)宵晚,無(wú)需預(yù)置或管理服務(wù)器即可運(yùn)行代碼垂攘,借助 Lambda,我們幾乎可以為任何類型的應(yīng)用程序或后端服務(wù)運(yùn)行代碼淤刃,而且完全無(wú)需管理晒他,我們要做的只是上傳相應(yīng)的代碼,Lambda 會(huì)處理運(yùn)行和擴(kuò)展 HA 代碼所需的一切工作

說(shuō)的直白一點(diǎn)

Lambda 就好比實(shí)現(xiàn)某一個(gè)功能的方法 (現(xiàn)實(shí)中钝凶,通常會(huì)讓 Lambda 功能盡可能單一)仪芒,我們將這個(gè)方法做成了一個(gè)服務(wù)供調(diào)用

到這里你可能會(huì)有個(gè)困惑唁影,Lambda 既然就是一個(gè)「方法」耕陷,那誰(shuí)來(lái)調(diào)用掂名?或怎么來(lái)調(diào)用呢?

如何調(diào)用 Lambda

為了回答上面這個(gè)問(wèn)題哟沫,我們需要登陸到 AWS饺蔑,打開(kāi) Lambda 服務(wù),然后創(chuàng)建一個(gè) Lambda Function (hello-lambda)

Lambda 既然是個(gè)方法嗜诀,就要選擇相應(yīng)的 Runtime 環(huán)境猾警,如下圖所示,總有一款適合你的(最近在用 Node.js, 這里就用這個(gè)吧)

點(diǎn)擊右下角的 Create function 按鈕進(jìn)入配置頁(yè)面

在上圖紅色框線的位置就可以配置出發(fā) Lambda 的觸發(fā)器了隆敢,點(diǎn)擊 Add trigger

從上圖可以看出发皿,AWS 內(nèi)置的很多服務(wù)都可以觸發(fā) Lambda,我在工作中常用的有:

  • API Gateway (一會(huì)的 demo 會(huì)用到拂蝎,也是最常見(jiàn)的調(diào)用方式)
  • ALB - Application Loac Balancer
  • CloudFront
  • DynamoDB
  • S3
  • SNS - Simple Notification Service
  • SQS - Simple Queue Service

上面只是 AWS 內(nèi)置的一些服務(wù)穴墅,向下滑動(dòng),你會(huì)發(fā)現(xiàn)温自,你也可以配置很多非 AWS 的事件源

到這里玄货,上面的問(wèn)題你應(yīng)該已經(jīng)有了答案了。這里暫時(shí)先無(wú)需任何 trigger悼泌,先點(diǎn)擊右上角的 Test 測(cè)試一下 Lambda

一個(gè)簡(jiǎn)單的 Lambda Function 就實(shí)現(xiàn)了松捉,紅色框線的 response 只是告訴大家,每個(gè)請(qǐng)求都會(huì)有相應(yīng)的 Request ID馆里,更有 START/END 標(biāo)識(shí)快速定位 Log 內(nèi)容 (可以通過(guò) CloudWatch 查看隘世,這里暫不展開(kāi)說(shuō)明)

你也可能已經(jīng)開(kāi)始發(fā)散你的思維了,如何運(yùn)用 AWS Lambda鸠踪,其實(shí)在 AWS 官網(wǎng)有很多樣例:

經(jīng)典案例

比如為了適應(yīng)多平臺(tái)圖片展示以舒,一張?jiān)紙D片上傳到 S3 后,會(huì)通過(guò) Lambda resize 適應(yīng)不同平臺(tái)大小的圖片

比如使用 AWS Lambda 和 Amazon API Gateway 構(gòu)建后端慢哈,以驗(yàn)證和處理 API 請(qǐng)求蔓钟,當(dāng)某一個(gè)用戶發(fā)布一條動(dòng)態(tài),訂閱用戶將收到相應(yīng)的通知

接下來(lái)我們就用 Lambda 實(shí)現(xiàn)經(jīng)典的分布式訂單服務(wù)案例

訂單服務(wù) Demo

為了增強(qiáng)用戶使用體驗(yàn)卵贱,或者為了提升程序吞吐量滥沫,亦或是為了架構(gòu)設(shè)計(jì)程序解耦,考慮到以上這些情況键俱,我們通常都會(huì)借助消息中間件來(lái)完成

假設(shè)有一常見(jiàn)場(chǎng)景兰绣,用戶下訂單時(shí)如果選擇開(kāi)具發(fā)票,則需要調(diào)用發(fā)票服務(wù)编振,很顯然調(diào)用發(fā)票服務(wù)不是程序運(yùn)行的關(guān)鍵路徑缀辩,這種場(chǎng)景,我們就可以通過(guò)消息中間件來(lái)解耦。這里有兩個(gè)服務(wù):

  1. 訂單服務(wù)
  2. 發(fā)票服務(wù)

如果用 Lambda 來(lái)實(shí)現(xiàn)兩個(gè)服務(wù)臀玄,整體設(shè)計(jì)思想就是這樣滴:

現(xiàn)實(shí)中瓢阴,我們不可能在 AWS console 通過(guò)點(diǎn)擊按鈕來(lái)創(chuàng)建各個(gè)服務(wù)的,在 AWS 實(shí)際開(kāi)發(fā)中健无, 我們通過(guò)寫(xiě) CloudFormation Template (以下會(huì)簡(jiǎn)稱 CFT荣恐,其實(shí)就是一種 YAML 或者 JSON 格式的定義)來(lái)創(chuàng)建相關(guān) AWS 服務(wù),如果上述這個(gè) Demo累贤,從圖中可以看出叠穆,我們要?jiǎng)?chuàng)建的服務(wù)還是非常多的:

  • Lambda * 2
  • API Gateway
  • SQS

如果寫(xiě) AWS 原生的 CFT,要實(shí)現(xiàn)的內(nèi)容還是挺多的

但是...... 懶惰的程序員總是能帶來(lái)很多驚喜

Serverless Framework

寫(xiě) JDBC 麻煩臼膏,就有了各種持久層框架的出現(xiàn)硼被,同樣寫(xiě) AWS 原生 CFT 麻煩,就有了 Serverless Framework (以下會(huì)簡(jiǎn)稱 SF)的出現(xiàn)幫助我們定義相關(guān) Serverless 組件 (順便問(wèn)一下渗磅,GraphQL 你們有在用嗎嚷硫?)

SF 不但簡(jiǎn)化了 AWS 原生 CFT 的編寫(xiě),還簡(jiǎn)化了跨云服務(wù)的定義夺溢,就好比設(shè)計(jì)模式當(dāng)中的 Facade论巍,在上面建立了一層門面,隱藏了底部不同服務(wù)的細(xì)節(jié)风响,降低了跨云并用云的門檻嘉汰,目前支持的云服務(wù)有下面這些

這里暫時(shí)不會(huì)對(duì) SF 展開(kāi)深入的說(shuō)明,在我們的 demo 中只不過(guò)是要應(yīng)用 SF 來(lái)定義

安裝 Serverless Framework

如果你有安裝 Node状勤,那只需要一條 npm 命令全局安裝即可:

npm update -g serverless

安裝過(guò)后檢查一下安裝版本是否成功

sls -version

配置 Serverless Framework

由于要使用 AWS 的 Lambda鞋怀,所以要對(duì) SF 做基本的配置,至少要讓 SF 有權(quán)限創(chuàng)建 AWS 服務(wù)持搜,當(dāng)你創(chuàng)建一個(gè) AWS 用戶時(shí)密似,你可以獲取 AK 「access_key_id」和 SK 「secret_access_key」(不是 SKII 哦),其實(shí)就是一種用戶名和密碼形式

然后通過(guò)下面一條命令添加配置就可以了:

serverless config credentials --provider aws --key 1234 --secret 5678 --profile custom-profile
  • --provider 云服務(wù)商
  • --key 你的AK
  • --secret 你的SK
  • --profile 如果你有多個(gè)賬戶時(shí)葫盼,你可以添加這個(gè) profile 做快速區(qū)分

運(yùn)行上述命令后残腌,就會(huì)在 ~/.aws/目錄創(chuàng)建一個(gè)名為 credentials 的文件存儲(chǔ)上述配置,就像這樣:

到這里準(zhǔn)備工作就都完成了贫导,開(kāi)始寫(xiě)我們的定義就好了

創(chuàng)建 Serverless 應(yīng)用

通過(guò)下面一條命令創(chuàng)建 serverless 應(yīng)用

sls create --template aws-nodejs --path ./demo --name lambda-sqs-lambda
  • --template 指定創(chuàng)建的模版
  • --path 指定創(chuàng)建的目錄
  • --name 指定創(chuàng)建的服務(wù)名稱

運(yùn)行上述命令后抛猫,進(jìn)入 demo 目錄就是下面這個(gè)結(jié)構(gòu)和內(nèi)容了

?  demo tree
.
├── handler.js
└── serverless.yml

0 directories, 2 files

因?yàn)槲覀兪怯?Node.js 來(lái)編寫(xiě) Serverless 應(yīng)用,同樣在 demo 目錄下執(zhí)行下面命令來(lái)初始化該目錄孩灯,因?yàn)槲覀兒竺嬉玫絻蓚€(gè) npm package

npm init -y

現(xiàn)在的結(jié)構(gòu)是這樣的(其實(shí)就多了一個(gè) package.json):

?  demo tree
.
├── handler.js
├── package.json
└── serverless.yml

0 directories, 3 files

至此闺金,準(zhǔn)備工作都已就緒,接下來(lái)就在 serverless.yml 中寫(xiě)相應(yīng)的定義就可以了 (門檻很低:按照相應(yīng)的 key 寫(xiě) YAML 即可峰档,是不是很簡(jiǎn)單败匹?)寨昙,打開(kāi) serverless.yml 文件來(lái)看一下,瞬間懵逼掀亩?

# Welcome to Serverless!
#
# This file is the main config file for your service.
# It's very minimal at this point and uses default values.
# You can always add more config options for more control.
# We've included some commented out config examples here.
# Just uncomment any of them to get that config option.
#
# For full config options, check the docs:
#    docs.serverless.com
#
# Happy Coding!

service: lambda-sqs-lambda
# app and org for use with dashboard.serverless.com
#app: your-app-name
#org: your-org-name

# You can pin your service to only deploy with a specific Serverless version
# Check out our docs for more details
# frameworkVersion: "=X.X.X"

provider:
  name: aws
  runtime: nodejs12.x

# you can overwrite defaults here
#  stage: dev
#  region: us-east-1

# you can add statements to the Lambda function's IAM Role here
#  iamRoleStatements:
#    - Effect: "Allow"
#      Action:
#        - "s3:ListBucket"
#      Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
#    - Effect: "Allow"
#      Action:
#        - "s3:PutObject"
#      Resource:
#        Fn::Join:
#          - ""
#          - - "arn:aws:s3:::"
#            - "Ref" : "ServerlessDeploymentBucket"
#            - "/*"

# you can define service wide environment variables here
#  environment:
#    variable1: value1

# you can add packaging information here
#package:
#  include:
#    - include-me.js
#    - include-me-dir/**
#  exclude:
#    - exclude-me.js
#    - exclude-me-dir/**

functions:
  hello:
    handler: handler.hello
#    The following are a few example events you can configure
#    NOTE: Please make sure to change your handler code to work with those events
#    Check the event documentation for details
#    events:
#      - http:
#          path: users/create
#          method: get
#      - websocket: $connect
#      - s3: ${env:BUCKET}
#      - schedule: rate(10 minutes)
#      - sns: greeter-topic
#      - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
#      - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx
#      - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
#      - iot:
#          sql: "SELECT * FROM 'some_topic'"
#      - cloudwatchEvent:
#          event:
#            source:
#              - "aws.ec2"
#            detail-type:
#              - "EC2 Instance State-change Notification"
#            detail:
#              state:
#                - pending
#      - cloudwatchLog: '/aws/lambda/hello'
#      - cognitoUserPool:
#          pool: MyUserPool
#          trigger: PreSignUp
#      - alb:
#          listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/
#          priority: 1
#          conditions:
#            host: example.com
#            path: /hello

#    Define function environment variables here
#    environment:
#      variable2: value2

# you can add CloudFormation resource templates here
#resources:
#  Resources:
#    NewResource:
#      Type: AWS::S3::Bucket
#      Properties:
#        BucketName: my-new-bucket
#  Outputs:
#     NewOutput:
#       Description: "Description for the output"
#       Value: "Some output value"

乍一看舔哪,你可能覺(jué)得眼花繚亂,其實(shí)這是一個(gè)相對(duì)完整的 Lambda 配置全集归榕,我們不需要這么詳細(xì)的內(nèi)容尸红,不過(guò)這個(gè)文件作為我們的參考

接下來(lái)我們就定義 demo 所需要的一切 (關(guān)鍵注釋已經(jīng)寫(xiě)在代碼中)

service:
  name: lambda-sqs-lambda # 定義服務(wù)的名稱

provider:
  name: aws # 云服務(wù)商為 aws
  runtime: nodejs12.x # 運(yùn)行時(shí) node 的版本
  region: ap-northeast-1 # 發(fā)布到 northeast region吱涉,其實(shí)就是東京 region
  stage: dev # 發(fā)布環(huán)境為 dev
  iamRoleStatements: # 創(chuàng)建 IAM role刹泄,允許 lambda function 向隊(duì)列發(fā)送消息
    - Effect: Allow
      Action:
        - sqs:SendMessage
      Resource:
        - Fn::GetAtt: [ receiverQueue, Arn ]
      
functions: # 定義兩個(gè) lambda functions
  order:
    handler: app/order.checkout # 第一個(gè) lambda function 程序入口是 app 目錄下的 order.js 里面的 checkout 方法
    events: # trigger 觸發(fā)器是 API Gateway 的方式,當(dāng)接收到 /order 的 POST 請(qǐng)求時(shí)觸發(fā)該 lambda function
      - http:
          method: post
          path: order

  invoice:
    handler: app/invoice.generate # 第二個(gè) lambda function 程序入口是 app 目錄下的 invoice.js 里面的 generate 方法
    timeout: 30
    events: # trigger 觸發(fā)器是 SQS 服務(wù)怎爵,消息隊(duì)列有消息時(shí)觸發(fā)該 lambda function 消費(fèi)消息
      - sqs:
          arn:
            Fn::GetAtt:
              - receiverQueue
              - Arn
resources:
  Resources:
    receiverQueue: # 定義 SQS 服務(wù)特石,也是 Lambda 需要依賴的服務(wù)
      Type: AWS::SQS::Queue
      Properties:
        QueueName: ${self:custom.conf.queueName}

# package:
#   exclude:
#     - node_modules/**

custom: 
  conf: ${file(conf/config.json)} # 引入外部定義的配置變量

config.json 內(nèi)容僅僅定義了 queue 的名稱,只是為了說(shuō)明配置的靈活性

{
  "queueName": "receiverQueue"
}

因?yàn)槲覀円M訂單的生成鳖链,這里用 UUID 來(lái)模擬訂單號(hào)姆蘸,

因?yàn)槲覀円{(diào)用 AWS 服務(wù)API,所以要使用 aws-sdk芙委,

所以要安裝這兩個(gè) package (這兩個(gè)理由夠充分嗎逞敷?)

{
  "name": "lambda-sqs-lambda",
  "version": "1.0.0",
  "description": "demo for lambda",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "license": "MIT",
  "dependencies": {
    "uuid": "^8.1.0"
  },
  "devDependencies": {
    "aws-sdk": "^2.6.15"
  }
}

接下來(lái),我們就可以編寫(xiě)兩個(gè) Lambda function 的代碼邏輯了

Order Lambda Function

訂單服務(wù)很簡(jiǎn)單灌侣,接收一個(gè)下單請(qǐng)求推捐,下單成功后快速返回給用戶,同時(shí)將訂單下單成功的消息發(fā)送到 SQS 中侧啼,供下游發(fā)票服務(wù)開(kāi)具發(fā)票使用

'use strict';

const config = require('../conf/config.json')
const AWS = require('aws-sdk');
const sqs = new AWS.SQS();
const { v4: uuidv4 } = require('uuid');

module.exports.checkout = async (event, context, callback) => {
    console.log(event)
    let statusCode = 200
    let message

    if (!event.body) {
        return {
        statusCode: 400,
        body: JSON.stringify({
            message: 'No order body was found',
        }),
        };
    }

    const region = context.invokedFunctionArn.split(':')[3]
    const accountId = context.invokedFunctionArn.split(':')[4]
    const queueName = config['queueName']

    // 組裝 SQS 服務(wù)的 URL
    const queueUrl = `https://sqs.${region}.amazonaws.com/${accountId}/${queueName}`
    const orderId = uuidv4()

    try {
        // 調(diào)用 SQS 服務(wù)
        await sqs.sendMessage({
            QueueUrl: queueUrl,
            MessageBody: event.body,
            MessageAttributes: {
                orderId: {
                    StringValue: orderId,
                    DataType: 'String',
                },
            },
        }).promise();

        message = 'Order message is placed in the Queue!';

  } catch (error) {
    console.log(error);
    message = error;
    statusCode = 500;
  }

  // 快速返回訂單 ID
  return {
    statusCode,
    body: JSON.stringify({
      message, orderId,
    }),
  };
};

Invoice Lambda Function

發(fā)票服務(wù)邏輯同樣很簡(jiǎn)單牛柒,消費(fèi) SQS 指定隊(duì)列中的消息,并將開(kāi)具出的發(fā)票發(fā)送到客戶訂單信息的 email 中

module.exports.generate = (event, context, callback) => {
    console.log(event)
    try {
        for (const record of event.Records) {
          const messageAttributes = record.messageAttributes;
          console.log('OrderId is  -->  ', messageAttributes.orderId.stringValue);
          console.log('Message Body -->  ', record.body);
          const reqBody = JSON.parse(record.body)
          // 睡眠 20 秒痊乾,模擬生成發(fā)票的耗時(shí)過(guò)程
          setTimeout( () => {
              console.log("Receipt is generated and sent to :" + reqBody.email)
          }, 20000)
        }
    } catch (error) {
        console.log(error);
    }
}

到此 demo 的代碼就全部實(shí)現(xiàn)了皮壁,從中你可以看到:

我們沒(méi)有關(guān)注 lambda 的底層服務(wù)細(xì)節(jié),沒(méi)有關(guān)注 sqs 的服務(wù)哪审,只是簡(jiǎn)單的代碼邏輯實(shí)現(xiàn)以及服務(wù)之間的串聯(lián)定義

最后我們看一下整體的目錄結(jié)構(gòu)吧:

.
├── app
│   ├── invoice.js
│   └── order.js
├── conf
│   └── config.json
├── package.json
└── serverless.yml

2 directories, 5 files

發(fā)布 Lambda 應(yīng)用

在發(fā)布之前蛾魄,編譯一下應(yīng)用,安裝必須的 package「uuid 和 aws-sdk」

npm install

發(fā)布應(yīng)用非常簡(jiǎn)單湿滓,只需要一條命令:

sls deploy -v

運(yùn)行上述命令后大概需要等帶幾十秒鐘, 在構(gòu)建的最后滴须,會(huì)打印出我們的構(gòu)建服務(wù)信息:

上圖的 endpoints 就是我們一會(huì)要訪問(wèn)的 API gateway 觸發(fā) lambda 的入口,在調(diào)用之前茉稠,我們先到 AWS console 看一下我們定義的服務(wù)

lambda functions

SQS-receverQueue

API Gateway

S3

從上圖的構(gòu)建信息中你應(yīng)該還看到一個(gè) S3 bucket 的名稱描馅,我們并沒(méi)有創(chuàng)建 S3, 這是 SF 自動(dòng)幫我們創(chuàng)建而线,用來(lái)存儲(chǔ) lambda zip package 的

測(cè)試

調(diào)用 API gateway 的 endpoint 來(lái)測(cè)試 lambda

打開(kāi) SQS 服務(wù)铭污,你會(huì)發(fā)現(xiàn)恋日,接收到一條消息:

接下來(lái)我們看看 Invoice Lambda function 的消費(fèi)情況,打開(kāi) CloudWatch 查看 log:

從 log 中可以看出程序“耗費(fèi)” 20 秒后打印了向客戶郵件的 log(郵件也可以借助 AWS SES 郵件服務(wù)來(lái)實(shí)現(xiàn))

至此嘹狞,一個(gè)完整的 demo 就完成了岂膳,實(shí)際編寫(xiě)的代碼并沒(méi)有多少,就搞定了這么緊密的串聯(lián)

刪除服務(wù)

Lambda 是按照調(diào)用次數(shù)進(jìn)行收取費(fèi)用的磅网,為了防止造成額外的開(kāi)銷谈截,demo 結(jié)束后通常都會(huì)將服務(wù)銷毀,使用 SF 銷毀剛剛創(chuàng)建的服務(wù)也非常簡(jiǎn)單涧偷,只需要在 serverless.yml 文件目錄執(zhí)行這條命令:

sls remove

總結(jié)與感受

AWS Lambda 是 Serverless 的典型簸喂,借助 Lambda 可以實(shí)現(xiàn)更小粒度的“服務(wù)”,無(wú)需服務(wù)搭建也加快了開(kāi)發(fā)速度燎潮。Lambda 同樣可以結(jié)合 AWS 很多其服務(wù)喻鳄,接收請(qǐng)求,將計(jì)算結(jié)果傳遞給下游服務(wù)等确封。另外很多第三方合作伙伴也在加入 Lambda 的 trigger 大部隊(duì)除呵,給 Lambda 更多觸發(fā)可能,同時(shí)爪喘,借助 CI/CD颜曾,可以快速實(shí)現(xiàn)功能閉環(huán)

開(kāi)通 AWS free tier,足夠你玩轉(zhuǎn) Lambda : https://dayarch.top/p/aws-lambda-with-serverless-framework.html

個(gè)人博客:https://dayarch.top

加我微信好友, 進(jìn)群娛樂(lè)學(xué)習(xí)交流秉剑,備注「進(jìn)群」

歡迎持續(xù)關(guān)注公眾號(hào):「日拱一兵」

  • 前沿 Java 技術(shù)干貨分享
  • 高效工具匯總 | 回復(fù)「工具」
  • 面試問(wèn)題分析與解答
  • 技術(shù)資料領(lǐng)取 | 回復(fù)「資料」

以讀偵探小說(shuō)思維輕松趣味學(xué)習(xí) Java 技術(shù)棧相關(guān)知識(shí)泛豪,本著將復(fù)雜問(wèn)題簡(jiǎn)單化,抽象問(wèn)題具體化和圖形化原則逐步分解技術(shù)問(wèn)題秃症,技術(shù)持續(xù)更新候址,請(qǐng)持續(xù)關(guān)注......


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市种柑,隨后出現(xiàn)的幾起案子岗仑,更是在濱河造成了極大的恐慌,老刑警劉巖聚请,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荠雕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡驶赏,警方通過(guò)查閱死者的電腦和手機(jī)炸卑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)煤傍,“玉大人盖文,你說(shuō)我怎么就攤上這事◎悄罚” “怎么了五续?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵洒敏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我疙驾,道長(zhǎng)凶伙,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任它碎,我火速辦了婚禮函荣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扳肛。我一直安慰自己傻挂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布敞峭。 她就那樣靜靜地躺著踊谋,像睡著了一般蝉仇。 火紅的嫁衣襯著肌膚如雪旋讹。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天轿衔,我揣著相機(jī)與錄音沉迹,去河邊找鬼。 笑死害驹,一個(gè)胖子當(dāng)著我的面吹牛鞭呕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宛官,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼葫松,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了底洗?” 一聲冷哼從身側(cè)響起腋么,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎亥揖,沒(méi)想到半個(gè)月后珊擂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡费变,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年摧扇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挚歧。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扛稽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出滑负,到底是詐尸還是另有隱情在张,我是刑警寧澤锡搜,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站瞧掺,受9級(jí)特大地震影響耕餐,放射性物質(zhì)發(fā)生泄漏题禀。R本人自食惡果不足惜玻孟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一套利、第九天 我趴在偏房一處隱蔽的房頂上張望蓝谨。 院中可真熱鬧击困,春花似錦沮峡、人聲如沸钧唐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)趟妥。三九已至,卻和暖如春佣蓉,著一層夾襖步出監(jiān)牢的瞬間披摄,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工勇凭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疚膊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓虾标,卻偏偏與公主長(zhǎng)得像寓盗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子璧函,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355