Jmeter的性能测试

在众多类型的软件测试中,性能测试以软件响应速度为测试目标,尤其是在较短时间内大量并发用户的访问时,软件是否具有良好的抗压能力。本章以JMeter为性能测试工具,LifeRay为被测应用,介绍如何完成常用的压力测试和性能测试:Web测试和数据库测试。

Apache JMeter是个纯Java的应用程序,提供了可扩展自定义脚本功能用于对静态的和动态的资源(静态文件、Servlet、Java对象、数据库和FTP服务器等)进行性能测试。它可以针对服务器、网络或对象模拟大量并发负载来进行强度测试,并分析不同压力负载下的系统整体性能,包括性能的图形分析。

与其说JMeter是一个测试工具,不如说是一个测试框架。因为JMeter支持范围非常广:

1) 通过取样器,支持许多协议,如HTTP/ TCP/FTP/JDBC/SOAP/LDAP/JMS/MAIL;

针对不同的协议或不同的应用,配置相应的JMeter元件,所以在JMeter使用过程中,要熟悉各种元件的配置,而不仅仅局限于http请求。

2)  跨平台,即在所有支持JVM的平台上都可以运行JMeter;

3) 支持多语言,所有支持BSF(Bean Scripting Framework)的脚本语言可使用。

目前各种常见的应用(如Servlets、Perl scripts、Java Objects、Databases, FTP Servers)等都可以采用JMeter来完成性能测试。JMeter的一大好处就是它内部已经实现了线程机制(线程组),用户不用为并发负载的过程编写代码,只须做简单配置即可。同时,JMeter也提供了丰富的逻辑控制器,控制线程的运行;其次,JMeter对测试结果能产生相应的统计报表,简单、直观,对一般性能测试应该足够

JMeter现在作为Web程序的性能测试工具,已经得到广泛应用。测试完成后,JMeter会形成一个performance报表,里面详细地记载了各个URL请求的数量、平均响应时间、最小/大响应时间、错误率等。

JMeter除了做性能测试,也能做功能测试,特别在API层次(SOAP和REST)。这里把JMeter和Selenium做个比较,以便对JMeter有进一步的了解。

1. JMeter测试的优势

1) 不依赖于界面。通过API调用或参数传递,可以添加测试用例并执行测试;

2) 测试脚本不须要编程,熟悉业务流程和http请求,就可以根据页面中input对象来编写测试用例;

3) 可以跳过页面限制,向后台程序添加非法数据,这样可以测试后台程序的健壮性,利用这个功能还能进行安全性测试;

4) 利用badboy录制测试过程,可以快速地生成测试脚本;

5) JMeter断言可以验证程序中是否有需要得到的值;

6) 使用参数化及提供的函数功能,可以快速完成测试数据的添加和修改等。

2. JMeter测试的缺点

使用JMeter无法验证JS程序,也无法验证页面UI,所以须要和Selenium配合来完成Web2.0应用的测试。

安装

从官方网站(http://jakarta.apache.org/jmeter)下载并解压之后即可使用。最新版(即2.3.2),需要JDK 1.4及其以上版本,不再支持JDK 1.3。下载解压的JMeter有以下几个目录:bin、docs、extras、lib和printable_docs等。

1. bin目录

对于Windows用户来说,运行JMeter.bat就可以看见JMeter的Swing GUI客户端。可以根据自己机器的特定配置来调整JMeter.bat中JVM调优设置,如下所示:

    set HEAP=-Xms256m -Xmx256m

    set NEW=-XX:NewSize=128m -XX:MaxNewSize=128m

    set SURVIVOR=-XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=50%

    set TENURING=-XX:MaxTenuringThreshold=2

    set EVACUATION=-XX:MaxLiveObjectEvacuationRatio=20%

    set RMIGC=-Dsun.rmi.dgc.client.gcInterval=600000

    -Dsun.rmi.dgc.server.gcInterval=600000

    set PERM=-XX:PermSize=64m -XX:MaxPermSize=64m

    set DEBUG=-verbose:gc -XX:+PrintTenuringDistribution

根据使用经验,Java堆值最多是物理内存的一半。如果机器的物理内存为512MB,那么选用默认值即可;如果机器的物理内存低于512MB,最好将命令行中参数调小,否则JMeter运行会很慢,甚至在JMeter.log文件里能够看到“内存溢出”错误。顺便提一下,如果JMeter出现异常,第一步就是检查JMeter.log的日志信息。 bin目录下文件JMeter.properties中有一个参数log_level.JMeter。当须要查错时,我们可以把它设成DEBUG(默认是INFO)来获得更详细的日志信息。

2. Docs和printable_docs目录

Docs目录下面的文件是JMeter的Java Docs,而printable_docs的usermanual子目录下的内容是JMeter的用户手册文档,相关文档还算详细,一般需要的内容都能查到,但都是英文的。

3. extras目录

Extras目录下的文件是为了对构建工具Ant提供支持。可使用ant实现测试自动化的部分工作,如批量脚本的自动执行,产生HTML格式的报表。

4. lib目录

lib目录下还有两个目录:一个是ext,此目录存放JMeter的核心jar包;另一个是junit目录,此目录存放JUnit测试脚本。用户的扩展所依赖的包则要直接放在lib下(不是lib/ext下)。

Badbody进行脚本录制

JMeter提供了一个利用本地Proxy Server(代理服务器)来录制生成测试脚本的功能,这个功能不够直观(要用到代理服务器),而且JMeter的代理目前不支持HTTPS的录制。由于HTTPS协议是加密的,该代理无法解密通信内容和请求参数。

Badboy是一款不错的Web自动化测试辅助工具,它提供了将Web测试脚本直接导出生成JMeter脚本的功能。无论是用于非商业用途或商业用途,如果安装Badboy的机器数量不超过5台,是不须要为它支付任何费用的。

通过Badboy的官方网站(http://www.badboy.com.au/)下载Badboy的最新版本,其安装过程同一般的Windows应用程序没有什么区别。安装完成后,可以执行桌面或Windows开始菜单中的快捷方式。如果找不到,可以直接运行安装目录下的Badboy.exe文件。

启动Badboy,在地址栏中输入待录制的Web应用网站的URL。这里以LifeRay为例,即输入LifeRay访问地址 http://localhost:8080,回车开始录制,如图所示。

开始录制后,可以直接在Badboy内嵌的浏览器(主界面的右侧)中对被测应用进行操作,所有的操作都会被记录在主界面左侧的编辑窗口中。在这个简单实验中,我们在LifeRay中登录然后退出。不过,我们将看到,录制下来的脚本并不像Selenium是一个个的动作(鼠标点击click命令或键盘输入type命令),而是一个个URL。这个例子中,总共3个顶层(Top Level)HTTP请求和4个子请求被录下。Badboy提供了两种不同的录制模式:请求模式(预设模式)和导航模式(类似Selenium的动作录制),但是,只有请求模式下录制的脚本能导出到JMeter做负载和压力测试,因为请求模式下录制的脚本是独立于页面布局和外观的。绝大多数负载和压力测试引擎运行时不显示任何用户界面,因此像JMeter这种压力测试工具无法执行导航模式脚本。

录制完成后,点击工具栏中的“stop recording”按钮,完成脚本的录制,可以看到所有录制下来的请求。然后,选择“File→Export to JMeter”菜单,填写文件名LifeRayLogin.jmx,将录制好的脚本导出为JMeter脚本格式。

启动JMeter并打开刚刚生成的测试脚本。在执行前,需要增加一个监听器——“查看结果树”,可以选择左边窗口中的“step1”,按右键,选择“添加→监听器→查看结果树”。

然后,选择“运行→启动”,开始执行脚本。然后,在左边导航栏内选择“查看结果树”,会看到执行的结果,如图所示。

还可以再增加一个监听器“用表格查看结果”,然后重新执行测试(按CRTL+R启动),再选择“用表格查看结果”,结果如图所示。

用表格查看结果的显示界面

请求1+2是访问主页的响应时间 6+5= 11 ms;

请求3+4+5是登陆的响应时间 17+17+409=443 ms;

请求6+7是退出的响应时间 13+8=21 ms。

自带录制组件—Http代理服务器

如何配置JMeter自带的脚本录制功能?在JMeter控制台的“工作台”(WorkBench)元件上单击右键,然后选择添加“HTTP代理服务器”,如下图所示。这里要为记录操作进行配置而不是运行测试计划,所以是在工作台(WorkBench)上而不是在测试计划(Test  Plan)上单击右键。 HTTP代理服务器的实现原理就是通过配置浏览器的代理服务器而使所有的访问请求通过JMeter发送,因而JMeter能把访问过程录制下来。

HTTP代理服务器的显示界面

1. “HTTP代理服务器”的配置

代理服务器的端口,默认为8080,可自行修改,但不要与其他应用端口冲突,因为LifeRay使用8080端口,所以代理服务器的端口被改为8000端口。

目标控制器是代理用于存储生成的数据的控制器,即录制的脚本存放的位置。默认情况下, JMeter会在当前测试计划中找一个记录用的控制器用于存储,也可选择测试计划中的线程组或在下拉菜单中选择任意控制器来存储。通常,选默认值就可以。

分组(Grouping)的概念是将一批请求汇总分组,可以把url请求理解为组。在测试计划中为生成的元件分组,有多个选项:

1) “不对样本分组”:所有请求全部被罗列;

2) “在组间添加分隔”:加入一个虚拟的、以分割线命名的动作,运行时与“不对样本分组”没有区别,无实际意义;

3) “每个组放入一个新的控制器”:执行时按控制器定义输出结果;

4) “只存储每个组的第1个样本”:对于一次url请求、实际很多次http请求的情况,这个选项很好用,因为我们常常并不关心后面的那些请求。

一般可以选择“只存储每个组的第1个样本”,否则,将会原样录制URL,包括图像和JavaScripts脚本在内的页面。

参考JMeter原版的测试计划:

1) 记录HTTP信息头:录制request的head信息;

2) 添加断言:录制时,加入空的检查点(须自行填写内容);

3) Regex matching:录制时,加入空的正则匹配(须自行填写内容);

4) 包含模式(Patterns to Include)和排除模式(Patterns to Exclude):帮助过滤一些不需要的访问请求,如过滤一些静态文件css、js和图片。

2. 定时器偏移量

在录制操作的同时,JMeter会根据设置建立一些定时器,其延迟时间是完全根据真实的操作来设置的。在代理服务器元件中可以增加一个定时器子元件(配置元件),用于告知JMeter在其生成的HTTP请求中自动增加一个定时器。JMeter会自动把实际的延迟时间存储为一个被命名为T的JMeter变量。因此,如果在代理服务器元件里使用了高斯随机定时器,就应该在其中的固定延迟偏移(Constant Delay Offset)设置项里添上${T},用于自动引用纪录的延迟时间。

3. 在浏览器中录制

启动HTTP代理服务器后,打开浏览器(IE、Firefox、Opera等)添加代理,地址填写本机ip或host name,端口填写刚刚所设置的代理端口(本例为8000),IE设置代理,如下图所示。

在IE中设置代理

在浏览器地址中键入待录制页面的URL,然后对页面进行操作,JMeter就会自动把所进行的操作转换为测试脚本,可以看到录制控制器节点下多了许多子节点,它们就是录制生成的脚本。操作完毕后,在JMeter中单击“停止”,停止录制,然后把浏览器的代理设置改为原来的设置。我们可以发现HTTP代理服务器录下的请求,和Badboy录出的请求是类似的。

4. 线程组属性设置

在执行脚本前,须要设置线程组(相当于虚拟用户组)的属性。线程组为JMeter的主要插件,常用来模拟并发用户访问,其内部实现了多线程,而每个线程均独立运行测试计划,用户只要添加、配置即可使用。如果客户机没有足够的能力来模拟高负载(如大于100个并发用户),可以使用JMeter的分布式测试功能,通过一个JMeter控制台,远程控制多个JMeter引擎完成测试。

单击线程组,在右边的窗口中设置线程数为20,Ramp-Up Period(in seconds)为1,循环次数选项取消选中永远,在右边的框中输入10,如下图所示。Ramp-Up Period可理解为负载的启动周期。例如,上述设置的20个虚拟用户连接,用了1秒来实现,即每个连接建立用时为50ms。

5. 运行及记录结果设置

要查看运行的结果还须要加入监听器。选中线程组,单击鼠标右键选择“监听器 ”,再从其列表中选择所需要的监听器。

JMeter的脚本开发

介绍JMeter进行性能测试脚本开发,包括使用多线程、断言、变量和函数等。

1) Access Log Sampler

每天有多少访问量?如何找到Top10 URL、各个URL并发数分配?这些可以通过产品线上服务器的访问日志分析得来, JMeter的Access Log Sampler用来实现这些功能的。

要获得用户访问日志就要把webserver配置成扩展日志模式(extended log format)。

因为有些数据是加密的,可以用的录制脚本登录,再用Access Log Sampler完成余下的测试。

如何建立Access Log Sampler?先选择“线程组”,然后按右键,选择菜单“添加→Sampler→Access Log Sampler”,然后按照下图所示进行设置。

2) 监视器结果

性能测试中服务器的资源监控非常重要,这里先介绍JMeter的“监视器结果(Monitor Result)”,来检查Tomcat资源的使用情况(内存,线程等)。JMeter的“监视器结果”利用Tomcat本身的特性,说白了就是直接访问Tomcat服务器的/manager/status,获得相应的服务器状态数据并进行呈现。

“监视器结果”是Tomcat 5+设计的,如果应用服务器(AppServer)不是Tomcat 5+,使用“监视器结果”将得不到结果。但是,任何servlet container都可以移植Tomcat 5的status servelet并使用此monitor。LifeRay用到的是Tomcat 6.x,但自带的Tomcat没有Manager App,可从Apache网站(http://tomcat.apache.org/)下载一个Tomcat,把其中的Manager App复制到 ${liferayHome}\webapps\manager目录下,在${liferayHome}\conf\tomcat-users.xml文件中添加以下内容: <role rolename="manager"/>

<role rolename="admin"/>

<user username="admin" password="admin" roles="admin,manager"/>

重启Tomcat。接下来,可以在JMeter中添加“监视器结果”来监视服务器状态,步骤如下:

1) 增加一个HTTP Request的Sampler;

2) 选中该新增的HTTP Request,修改其属性。修改路径为manager/status,必要时给出服务器的IP地址和Port的值;增加一个参数,该参数的名称为大写的XML,值为小写的true,并选中下方的“用作监视器”,如下图所示;

设置“HTTP Request”的操作界面

3) 增加一个“监视器结果”的节点;

4) 增加“HTTP授权管理器”和认证信息,因为访问Tomcat的应用服务器的 /manager/status要给出用户名和口令,因此,在JMeter中增加一个HTTP授权管理器。选择“线程组”,然后按右键,选择菜单“添加→配置元件→HTTP授权管理器”,增加一条记录,包括BaseURL、用户名和密码等信息。

在上面这个测试计划中包含了两个线程组,一个用来给服务器加压,另一个用来监控服务器的资源。最后,给服务器加压的脚本增加“响应断言”去验证登录是否成功。

增加“响应断言”的操作界面
复杂的分布式大规模压力测试

当要模拟数以千计的并发用户时,使用单台机器模拟所有的并发用户就有些力不从心,甚至还会引起Java内存溢出的错误。JMeter允许用户从多台机器启动JMeter测试,通过使用多台机器来分担负载所产生的压力,并借此来获取更大的并发用户数。

某些Web和应用程序服务器可连续处理同一个IP地址的多个请求,以及并行处理不同IP地址的请求,因此,对于一些关键测试场合(请求来自不同的机器或将测试负载分布到几个客户端),可以使用该选项来执行操作。在具有1.4G~3GHz的CPU、1GB内存的JMeter客户端上,可以处理线程100~300,但web services是例外。XML处理是CPU运算密集的,会迅速消耗所有的CPU。作为一般规则,以XML技术为中心的应用系统,其性能将是普通web应用的25%~10%。另外,如果所有的负载从一台机器产生,网卡和交换机端口都可能产生瓶颈,所以一个JMeter客户端线程数不要超过100。下图描述了一个多测试机的情景:一台控制机器(Controller)控制3台负载产生(机)器。对于下图的架构,各种平台上互相调用也是没有问题的,Windows的JMeter Controller控制Linux的远程机,或者Linux的JMeter Controller控制Windows的远程机,而且一般要求负载产生机器与Controller机器上JDK、JMeter的版本一致。

JMeter多测试机的情景

1. 启动负载产生器的服务

在所有产生负载的机器上启动JMETER_HOME/bin/jmeter-server.bat (windows) 或JMETER_HOME/bin/jmeter-server (linux) 脚本。

2. 配置Controller机器

把所有负载产生器的IP地址加入到Controller机器上JMeter的配置文件中。编辑Controller机器上的JMeter属性文件/bin/jmeter.properties,找到“remote_hosts”段,然后增加负载产生器的IP地址,使用逗号分隔。

3. 启动控制端

在Controller端启动JMeter,然后在“运行(Run)”菜单下会多出几个子菜单“Remote Start”、“ Remote Start All”和“Remote Stop”、“Remote Stop All”等。这些子菜单下又会列出所有在属性文件中增加的IP地址,如下图所示,可启动某台特定的负载产生器(执行脚本)。当然,也可以启动全部负载产生器,即选择“Remote Start All”。

启动远程负载产生器

4. 手动启动远程服务

有些情况下JMeter负载产生器上的脚本可能运行不起来,必须依靠手动的方法来启动JMeter远程服务。

1) 首先要运行RMI(Remote Method Invocation,远程方法调用)注册程序(名为rmiregistry),因为JMeter使用RMI作为机器间的通信方法。rmiregistry组件在JDK的bin目录下,须要读取某些JMeter的类文件,所以在运行它之前,确认在ClassPath环境变量里包含了以下jar包:

JMETER_HOME /lib/ext/ApacheJMeter_core.jar;

JMETER_HOME /lib/jorphan.jar ;

JMETER_HOME /lib/logkit-1.2.jar。

2) 启动RMI注册程序(它使用1099端口),然后键入“jmeter -s”来启动JMeter服务。

3) JMeter服务启动之后,其他步骤和前面一致。

5. 常见问题

1) 在Controller端上启动远程机器,没有成功,系统给出错误提示:“Bad call to remote host”。这时,须要检查被控制机器上的JMeter-server是否启动。

2) 在Windows中重启rmiregistry服务很简单,即首先关闭运行rmiregistry的窗口,然后重新运行rmiregistry;而在Linux下,则应使用ps aux|grep rimregistry查询该进程的pid,然后使用kill -9 pid杀死该进程,重新启动。

数据库性能测试

在大多数企业项目中,数据库服务器是不可缺少的,其压力测试是为了找出数据库对象是否可以有效地承受来自多个用户的并发访问。被测试的对象主要有索引、触发器、存储过程和锁等。

利用JMeter,可以测试所用数据库是否满足某些指标(例如能够处理一定数量的并发用户)。JMeter可以针对各种数据库产品模拟出繁重的负载,这可以通过JMeter的多线程框架来实现。

以使用LifeRay自带的默认数据库HSQL为例来建立一个数据库测试计划。为了方便对LifeRay的HSQL进行JDBC测试,要把HSQL的JDBC驱动程序复制到 <jmeter安装目录>\lib目录下。对于LifeRay,在默认安装情况下,HSQL的JDBC驱动程序的位置为<liferay安装目录>\lib\ext\hsql.jar。

通过下面的命令来启动LifeRay的名叫lportal的数据库,所有的数据都在<liferay安装目录>\lib\lportal.script里。hsqldb保存的就是所有操作的sql语句,如下所示。

    D:\liferay\bin>java -cp ..\lib\ext\hsql.jar org.hsqldb.Server -database.0

    lportal -dbname.0 lportal

[Server@e0e1c6]: [Thread[main,5,main]]: checkRunning(false) entered

[Server@e0e1c6]: [Thread[main,5,main]]: checkRunning(false) exited

[Server@e0e1c6]: Startup sequence initiated from main() method

[Server@e0e1c6]: Loaded properties from [D:\liferay\bin\server.properties]

[Server@e0e1c6]: Initiating startup sequence...

[Server@e0e1c6]: Server socket opened successfully in 31 ms.

[Server@e0e1c6]: Database [index=0, id=0, db=file:lportal, alias=lportal]

opened sucessfully in 594 ms.

[Server@e0e1c6]: Startup sequence completed in 625 ms.

[Server@e0e1c6]: 2009-04-13 20:32:32.563 HSQLDB server 1.8.0 is online

[Server@e0e1c6]: To close normally, connect and execute SHUTDOWN SQL

[Server@e0e1c6]: From command line, use [Ctrl]+[C] to abort abruptly

数据库启动后,我们可以按照下面的步骤用JMeter来完成一个基于LifeRay项目的数据库测试计划。

1) 新建一个Thread Group。

2) 新增JDBC Connection Configuration(连接配置)。

3) 点击新增的JDBC Connection  Configuration,须要修改的参数如下。

JDBC连接配置界面

Variable Name:填入“HSQL”。

Database URL:jdbc: hsqldb:hsql://{host_IP_or_machine_name}:{HSQL监听器监听的端口}:{HSQL实例名} ——注意,“{ }” 大括号只用于说明,在配置JMeter时,请将大括号 “{ }”去掉。

JDBC Driver class:org.hsqldb.jdbcDriver。

Username:访问HSQL实例的用户名。

Password:对应的密码。

上面这些数据库参数从LifeRay中得到:

    <liferay安装目录>\conf\Catalina\localhost\ROOT.xml

      <Resource

          name="jdbc/LiferayPool"

          auth="Container"

          type="javax.sql.DataSource"

    driverClassName="org.hsqldb.jdbcDriver"

          url="jdbc:hsqldb:lportal"

          username="sa"

          password=""

          maxActive="20"

      />

4) 新增一个JDBC Request,需要修改参数如下。

Variable Name:和上面的JDBC Connection Configuration填写同样的内容。这里表示JDBC Connection Configuration建立一个名为Oracle的连接池,之后其他的JDBC Request都共用这个连接池;

Query:select * from table_name 。建议另行使用数据查询工具,输入一条SQL查询语句,保证可以看到执行结果,然后再将SQL语句拷贝到这里。例如,在<liferay安装目录>\lib\lportal.script 里有表名。

5) 新增一个View Results Tree。

6) 点击Run,查看结果。可以在Response data tab看到响应的返回结果。

如果所测试的数据库是Oracle,请注意以下事项。

1) 执行时提示:No Suitable Driver。在电脑上搜索classes12.jar,然后复制到JMeter的Lib目录下。如果找不到classes12.jar,就找classes12.zip文件,然后把扩展名改为jar。这里顺便说一句,其实classes12.jar并不是最快的Oracle JDBCdriver,如果将classes12.jar和Oranxo.jar(http://www.inetsoftware.de/products/jdbc-driver/oracle/oranxo)比较,Oranxo.jar要快得多。

2) 执行时提示:ORA-00911: invalid  character。确保在JDBC Request里面的Query中输入的SQL语句是正确的。

JMeter用户手册中关于配置通过JDBC对数据库性能进行测试的部分如下:

http://jakarta.apache.org/jmeter/usermanual/build-db-test-plan.html


本文来自朱少民老师的《轻轻松松自动化测试》,如有侵权,请通知后删除。

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