编者注:本文为CSDN博主smooth-z的原创文章。
原文链接:
https://blog.csdn.net/smooth00/article/details/123863730
目前JMeter在接口测试和性能测试的市场占用率很高,最大的原因是其开源性、易扩展和轻量级(这是LoadRunner所不具备的),同时JMeter还可以满足多种协议的接口和性能测试(这是其他开源工具不具备的)。
而MeterSphere是一款基于JMeter引擎技术的软件测试平台,不考虑轻量级因素,基本上是可以替代JMeter的,并且更易于线上测试工作的开展(包括在线管理用例、在线编辑脚本和调试脚本,在线压测和在线分析报告等)。
MeterSphere的分布式架构优势
JMeter虽然灵活轻巧,但是基于其线下工具的特点,无法进行系统化和平台化的管理和运行。同时,JMeter的单机模式在一般的压力机配置下,会受限于JMeter自身的机制和硬件配置,最多可以支持几百至一千左右的模拟请求线程。而开展大量的分布式Slave部署,会带来运维管理方面的困难。同时,JMeter的主从(Master-Slave)模式,还会给主节点带来很大的交互压力,没人能做到部署大规模的分布式集群压测。
■ JMeter的分布式结构分析
我们分析这个结构图会得出以下的结论:
1. 从主节点调度和传输数据,依赖于稳定的端口,其中server.rmi.localport和client.rmi.localport端口默认是随机的(也可以设置成如上图固定的),server_port端口一般固定为1099,只要防火墙没有放行(如果是随机端口就要求关闭防火墙),Master-Slave主从分布式模式就会调度失败;
2. 主节点发送给从节点的数据相对较少,因为除了主从调度和数据同步外,每个从节点作为独立的压力机,自行负责自己那部分请求发送和接收。但是,从节点发送给主节点的测试结果数据一般会比较大,遇到大量的从节点时,对主节点(主控机)的压力就会很大,容易出现瓶颈;
3. 每启动一个从节点(Agent),基本上Server_Port端口就被占用了,而且只能进行单任务压测,就算一个任务结束了,进程还在端口就不会释放,属于资源独占型;
4. 主从方案部署繁琐,除了考虑端口因素,还要考虑版本一致性,插件依赖包一致性。其中一个节点崩溃,就可能导致整个压测任务失败(因为要保证调度和主从数据的一致性);
5. 如果JMeter压测的CSV需要每个节点(Agent压测机)读取的数据不一样,则需要人为提前进行切分、裁剪和放置,因为主从控制没有文件远程传输和同步机制;
6. 如果从节点硬件性能差别过大,也没法控制不同节点的压力分配来减轻某些节点的压力。因为节点机Agent只有发压功能,没有差异控制功能,线程设置为多大,每个节点就都复用一样的配置;
7. 另外,JMeter在GUI模式下通过第三方插件可以查看实时压测报告(性能较差,一般不采用),No-GUI模式下只能生成结果报告。目前一种常见的方式是,通过后端监听器向InfluxDB发送结果数据,采用Grafana进行实时展示,但这种方式在大规模压测下InfluxDB也会崩溃,除非是InfluxDB集群(集群版的未开源);
8. JMeter默认是不支持性能监控的,只能是在GUI模式下,通过扩展监听器插件(例如PerfMon Metrics Collector)来实现,但也只能监听到CPU、内存、网络这样的基础指标,可扩展性有限。另外从性能上考虑,也非常不合理,本来Master节点压力就大,再加上监控数据的汇集,性能就更差了,所以除了采用独立部署的性能监控平台,没有别的办法。
■ MeterSphere的分布式架构分析
我们分析以上架构图会得出结论:
1. MeterSphere基于Docker技术,易于分布式集群的部署,如果配合Kubernetes就更易于扩展部署节点(进阶版),也易于云部署;
2. MeterSphere没有采用传统的JMeter Slave(Agent分压器)方式去扩展节点,而是每个节点都是独立的JMeter Master。这种模式的好处是,不需要通过Server_Port端口来构建主从连接,每个节点就是个Docker版的JMeter进程,由NodeController控制,用完即停(通过Docker启动和注销);
3. 测试结果和报告再也不用通过Slave向Master汇集,而是各压测节点通过JMeter-Kafka监听类组件向Kafka回传测试结果数据,避免了主从模式下的Master汇集压力。而且,Kafka还可以考虑集群化部署,这在性能上就可以满足大数据量的吞吐压力;
4. MeterSphere将传给Kafka的结果数据采用DataStreaming进行收集和计算,并写入MySQL数据库,这确保了在生成实时报告及汇总报告上的性能。我们都知道JMeter生成报告的性能一直比较差,特别是长时间压测写入了大量结果数据到文件中,想顺利转换成HTML报告几乎不可能;
5. MeterSphere基于界面化的设置性能压测参数(并发、时间、梯度、压测资源池、文件切分等),并通过资源池管理节点资源,根据资源池分发压测脚本和文件到指定的节点,自动启动Docker JMeter完成压测;
6. 测试用例(接口)、测试场景(脚本)全都入库保存和管理,测试数据、测试结果和报告也是入库保存和管理,方便后续分析、分享和比对;
7. 性能监控上支持扩展Prometheus,如配置了相关的Prometheus监控,MeterSphere在压测的同时会自动收集被压测端系统的性能监控数据。
MeterSphere的Web平台化优势
JMeter虽然受到很多人推崇,但作为一款测试小工具,无法进行测试脚本管理、无法在线规划测试场景、无法多人实时查看测试过程和测试结果。总之,不方便基于团队的方式开展接口测试和性能测试工作,更别提是基于项目的迭代实时进行持续测试了。
另外,长期做过性能测试的都知道,接口测试和性能测试是有一定相关性的,比如性能测试的混合测试场景就是一些接口的组合,完全可以把测试通过的接口直接引用过来,就不需要重新定义和编辑了。同样,性能测试的场景规划也完全可以前移到接口测试阶段进行,而不至于到性能测试执行阶段了才开始准备测试脚本。这些事对于Web平台系统来说再平常不过了,但对于JMeter来说就不具备这样的管理方便性。
MeterSphere可以说解决了我们上面所提到问题,即完全在线运行和管理,相当于将JMeter由线下搬到了线上。
当然,这样一定会有人问,能在线编辑和调试测试脚本吗?毕竟很多基于JMeter的开源测试平台,在线压测一般都没问题,但是要在线编辑脚本就有点困难。因为要在Web端做一套和JMeter一样灵活的脚本编辑工具可没那么容易。这一点MeterSphere做得挺不错,并且相信未来还会从JMeter身上吸收更多的优点。
我们先来看看MeterSphere基于Web界面的接口定义和编辑开发能力:
1. 完善的接口定义功能
① 创建接口
目前支持创建接口包括HTTP、TCP、SQL、DUBBO,类型上还是偏少,比如还没支持WebSocket(接口配置没有该功能,但性能测试可以通过上传JMX脚本和WebSocket相关依赖Jar包来实现)。
对于接触过JMeter或Postman的人来说,在上面创建接口操作起来相对容易,因为很多要素是一样的。更让人惊喜的是可以同步创建Mock服务,只要在Mock设置中配置上期望请求条件和期望响应内容,Mock服务就生效了,易用性上没的说了。
另外我们创建的接口除了能Mock化,还能文档化(相当于Swagger化),这点也挺棒。
② 导入外部接口
支持导入多种格式的接口文件,目前HTTP协议支持Postman、Swagger、JMeter,所支持的接口工具相较其他接口测试平台涵盖更为广泛。
以JMeter的脚本为例,导进来后,基本要素和JMeter一样,就是前置处理器、后置处理器、断言等都没了。不过没关系,可以进入Case或是在自动化接口场景模式上追加。例如,在后置处理操作中添加正则表达式提取Cookie参数。
然后可以执行调试。来看我们提取的效果:
可以看到,这个正则表达式(如果响应体是JSON格式,除了正则,推荐用JSONPath提取,那样更方便精准,我这里提取的是响应头的Cookie,所以用正则)提取的是三个值的数组。我们只要取第二个值,作为我们的Cookie参数,那么直接引用变量${cookie_2}就可以了。是不是和JMeter上的操作类似?所以我就说会使用JMeter的,接受MeterSphere是很容易的(本来就是一套玩法)。把这个Cookie引用到后面接口的请求头即可。
注明:其实导入外部接口的功能还有另外一种现实意义。就是我们可以用其他工具,比如Postman、JMeter代理录制、Chrome录制插件等工具去录制接口脚本,然后导入MeterSphere。这为性能测试场景的构建提供了辅助手段,能给喜欢录制的小伙伴带来很大帮助。
2. 灵活配置的接口自动化场景
为什么很多人愿意使用JMeter来做接口测试?除了它支持的协议多以外,最主要是容易进行场景化的编排。比如我登录后,获取Token再传给查询接口,然后在查询接口进行查询,查询到结果,传给操作接口进行修改或删除操作。这样的场景化就是业务特性,要求接口按先后顺序编排,彼此还要参数关联。而这样的场景化接口编排后,是可以很方便地直接引用到后期的性能测试场景的。所以接口场景化很重要,这一点MeterSphere也做得不错(毕竟是基于JMeter的技术)。
对于JMeter技术理解比较深的话,了解和使用MeterSphere是有帮助的。特别是接口自动化场景和性能测试场景,基本上属于技术同源。
X1~X5:是负载模拟的一个过程,使用这些组件来完成负载的模拟;
X1:选择协议,模拟用户请求,检查服务响应是否正确,并收集结果信息,属于API定义部分;
X2:完善测试脚本部分,包括参数化,关联等,属于Case定义部分;
X3:控制测试脚本业务逻辑,属于业务场景定义部分;
X4:集合点,模拟用户并发,这属于性能测试涉及的内容;
X5:用户数,一个线程代表一个用户,调试接口一般单线程,性能测试就会用多线程;
Y1:可以理解为选择协议,包含负载模拟部分,负责模拟用户请求;
Y2:可以理解为检查点,结果验证部分,负责验证结果正确性,属于接口Case定义部分;
Z:可以理解为监控器,负责结果的收集,对于JMeter来说监听器不仅可以放在线程组之内,也可以放在线程组之外;
对于MeterSphere平台来说,无论接口测试,还是性能测试,都离不开以上要素结构。只是像监听器已经做到了高度集成,在平台里已融合到报告或监控中了,所以没有直观体现。
① 创建场景
我们可以创建个场景,然后从上面定义好的接口,直接复制添加(从接口列表导入)过来。
除了导入过来的接口,我们也可以在场景下直接添加新的接口。同样也可像JMeter一样,添加事务控制器、循环控制器、条件控制器、自定义脚本(BeanShell、Python等),如以下添加随机生成身份证号码的脚本:
可以说这个接口自动化场景编辑的功能还是很强大的,对于我一个擅长JMeter的测试人员来说,可以无缝接受。
② 导入脚本场景
我尝试把JMeter脚本导入后,发现连线程组、事务和各JMeter元件都一起导入了,具体如下:
很多元件目前是界面上不支持展现,但会以XML形式展现,可以编辑XML,比如Cookie管理器。
说到Cookie管理,可以用JMeter自带的HTTP Cookie管理器(MeterSphere界面目前没有提供这个元件,导入的显示为XML),也可以用我上面提到的正则表达式提取的方式,然后添加到其他接口请求头中(类似于Token处理方式)。
当然,MeterSphere也有自己的方式,就是Cookie共享。勾选了这个选项,其实就相当于是用了JMeter中的Cookie管理器的Cookie保存。
无论是新建场景,还是导入场景,其实MeterSphere做得已经很出色了,基本上吸收了JMeter的很多特性。同时,从扩展性上看,MeterSphere还可以继续扩展JMeter的一些元件。从开源的二次开发角度来说,我觉得难度应该不大。
③ 变量和参数化
关于参数化,主要有上面提到的关联提取方式。目前支持正则、JSONPath、XPath这些表达式,基本上也够用了。
至于全局变量可以在项目环境配置中去配置。配置方式很灵活,可以直接添加变量,也可以在全局前置脚本中添加脚本产生变量。
从文件中导入变量,跟JMeter中的CSV数据文件设置类似,在场景编辑页面上方有场景变量链接,设置界面如下:
④ 定时任务
接口自动化场景,肯定要有自动执行的功能,所以MeterSphere也提供了定时任务和任务通知的功能,可以满足定时执行接口场景的需要。
这个定时任务用的是Cron表达式。说实在的很多人不知道怎么配置,建议加个Cron配置器功能,比如下图这样的(当然这只是辅助功能,还是需要自己了解清晰什么是Cron):
对于任务通知,功能也是非常简单,简单到可能很多人也不知道怎么用了,具体如下:
这里的任务通知,一般就是邮件或钉钉通知用得多。可能很多人不知道WebHook,其实就是触发事件后向对方(第三方)的URL接口发送一条消息,比如钉钉我们可以这样获取WebHook:
在钉钉群聊中,通过“群设置”→“智能群助手”→“添加机器人 ”→选择“自定义”机器人。为机器人设置一个头像和名称,点击“添加”按钮后可以获得一个 WebHook地址,点击“完成”按钮即可完成钉钉机器人的添加。
说到定时任务,我们不得不提一下持续构建。一般接口测试在执行持续构建场景中会用到定时测试任务,或是触发测试任务,比如提交和发布一版新包后,就要立即开始接口回归测试。
其实在这一块MeterSphere是有Jenkins插件提供的(https://github.com/metersphere/jenkins-plugin ),我们可以把插件安装到Jenkins,就可以调度MeterSphere的接口任务。这都算是隐蔽的功能,很多人可能不知道,可以自行上网搜索了解一下。
3. 高效配置的性能测试场景
MeterSphere相较于JMeter的最大优势就是性能测试过程完全通过Web界面操作,包括脚本的上传和管理、参数化及资源文件的上传管理、压测在线配置、压测过程实时查看、压力机的性能监控、性能测试结果及报告管理,这些都是单机化的JMeter所不具备的。
① 测试资源
MeterSphere比较好的地方,就是通过资源池来管理测试资源。而且一个资源池可以创建多个压测节点,这里的每个节点不是JMeter传统理解的Slave(比如端口绑定1099的代理节点),而是关联一个新的JMeter Master进程。这个进程就是通过Docker镜像在压测时动态创建容器,压测完后就自动消亡释放压测资源。
以下是一个资源池创建两个节点的示例:
每个节点控制器NodeController负责创建和消亡JMeter Master容器。NodeController也是个容器,所对应的默认端口为8082,docker ps | grep node-controller结果如下:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3d8cbeab1861 registry.cn-qingdao.aliyuncs.com/metersphere/ms-node-controller:v1.19.0 "/deployments/run-ja…" 4 days ago Up 4 days (healthy) 0.0.0.0:8082->8082/tcp, 0.0.0.0:9100->9100/tcp ms-node-controller
② 性能监控
MeterSphere的节点部署模式(安装时install.conf配置MS_INSTALL_MODE=node-controller),启动后会看到三个相关容器。其中,JMeter Master容器是动态的(压测完就消亡)。另外两个固定的,一个是上面提到NodeController,一个是Node Exporter。
很多人不知道Node Exporter的作用,其实它是个性能监控采集器,相当于Prometheus监控平台中的Node Exporter。有了它我们压测完后,在报告中是能看到采集到的压力节点性能监控数据。
上面的监控采集数据,基本上和Prometheus是一样的。可以肯定的是,MeterSphere对其做了一些集成,既然通过Node Exporter能支持Linux性能指标的采集,那么我们除了监控压力节点机,我们还可以监控被测服务器。通过“性能测试”→“测试”→“高级配置”→“监控”,添加我们需要监控的服务器:
上面的方式可以按Prometheus的PromQL语法自定义监控指标。那么毫无疑问,我们还可以按一般Prometheus的方式去扩展其他服务的监控,比如MySQL、PostgreSQL的监控等。只需要在被监控机器上部署相应的采集器,比如MySQL Exporter、Postgres_Exporter、JMX Exporter等。真正懂性能测试和性能监控的人应该都不难理解这方面的扩展需求,监控关系示意图如下:
③ 加载测试脚本
主要包括两种方式,一种是JMX文件(包括新上传的脚本或引用平台上已有的脚本),一种是引用上面提到的接口自动化场景(这也是平台化管理的优势)。
从本质上来说,上面提到的接口自动化过程,就是一个JMeter脚本化的过程。最终压测调用的是JMX文件(会分发到各个节点),所以性能测试加载脚本和接口场景配置就是一个相通相关的过程。
④ 压力配置
基于界面的压力配置,比JMeter要简单直观。目前支持两种线程组发压模式,一种是ThreadGroup线性递增发压,一种是ConcurrencyThreadGroup阶梯递增发压。
从配置上来说,支持选择资源池(每个资源池可能对应多个压测节点),支持多场景(同时或顺序执行多脚本),除了常规的并发配置项,还包括RPS(容量限制)配置。
压测可以自定义分配不同节点的压力配比(不同机器性能不一样,有时候要区别对待)。
对于参数化文件,还支持CSVDataSet拆分(在高级配置中),可以按照分节点分隔压测数据。这是JMeter默认不具备的功能,但是在业务场景中经常需要的功能。配置页面如下:
对于我来说,目前MeterSphere不支持动态调整TPS/RPS,这点挺不方便的。因为有时候长时间压测,需要继续加压时,我们是不希望中断测试的,希望能够动态加压。
⑤ 测试报告
MeterSphere的测试报告算中规中举,但是相对于JMeter来说,优势明显。首先是可以实时直观地查看压测情况,最关注的TPS、RT指标、请求统计、错误记录都清晰明了。
JMeter如果没有引入第三方插件的话,是不能直观地查看压力变化情况的。当然JMeter测试结束后,导出的HTML报告也很详细。这点来说,MeterSphere有点不足,不过不影响我们的测试活动,毕竟最关注的指标也就这些,如果需要更多的报告视图,完全可以基于开源扩展开发。
MeterSphere的测试报告示例如下:
另外MeterSphere还有一个亮点,就是报告对比功能。我们可以把多轮测试的报告进行对比,比如比较TPS的提升情况,能清楚地说明性能的提升。
MeterSphere的扩展性优势
JMeter的最大优势是开源性,MeterSphere的最大优势也是其开源性。MeterSphere的另一个优势就是它是基于JMeter技术的。
1. 基于JMeter技术的扩展优势
JMeter最大的优势是支持多种协议的测试。这方面除了LoadRunner应该找不到第三款测试工具了吧?互联网大厂自己的工具不算,毕竟外人也不能免费享用。
所以,MeterSphere的最大优势是基于JMeter去扩展各种协议接口的测试,比如WebSocket的测试。默认JMeter是不支持WebSocket的,但是可以扩展第三方的插件获得支持。对于MeterSphere来说,我们也是同样的思路,我们可以把WebSocket第三方插件依赖Jar包全部上传,这样就能支持WebSocket协议脚本的压测了。
上面的依赖包一次上传后,就可以直接在同一个项目中引用了,调测的效果如下:
所以MeterSphere基本上可以做到支持JMeter所支持的所有协议和接口,就看个人怎么去配置和使用了。另外,MeterSphere也是基于Java的开源平台,我们可以通过二次开发去获得更多的扩展。
2. 基于平台的扩展与集成
① 监控方面可以扩展Prometheus,而Prometheus是开源的系统监控平台,相关的说明上面也有提到,具体的应用大家可以自己去Prometheus官网获得支持。MeterSphere的系统设置中有相关的外接配置:
在UI测试方面,估计不久也会有相应的扩展,比如扩展Selenium的支持和应用。
② MeterSphere与目前流行的缺陷管理平台也做了相应的连接和集成,比如我们使用禅道比较多,就可以考虑这方面的集成应用,这就是开源扩展的优势。
MeterSphere目前的不足
对于我个人来说,目前MeterSphere的最大不足,是不支持动态TPS/RPS/线程数的控制。在实际工作中,我们希望在每次压测执行时能够随时调节吞吐量或者动态改变压力的上限值。
场景一:执行线上的一个持续容量测试,如果在某个压力下服务容量没有问题,我们就希望在不停止压测的情况下,再加大一些容量;或者发现压力在某个时间段过大,需要临时降低吞吐量。这时候如果不能动态改变TPS/RPS,那我们只能停止任务重新开始了;
场景二:在我们无法估算系统的最大TPS情况下,会通过不断加大压力(用户数/线程数),来关注TPS曲线的变化情况(要求线程数连续不中断的梯度递增,保持同比例关系:TPS*RT=线程数)。如果压力数(用户数)增长,TPS却不再增长了,可能就是出现性能瓶颈了。但如果这个趋势一直没出现,而压力工具已经压到最大用户数/线程数,那么我们就需要动态改大用户数/线程数,否则测试可能要中断。
以上的两种情况,在一般的测试情况下影响不大,大不了重新开始执行测试。但是如果是在生产环境下测试,或者需要大量前置工作的情况下(准备大量测试数据或复杂的环境初始化工作),任何测试的中断,都意味着工作量的增大,所以压测过程中动态改变压力是很有必要的。
在JMeter中动态改变压力,是通过BeanShellClient和BeanShellServer来实现动态改变参数变量,例如在节点jmeter.properties设置beanshell.server.port为9000,通过命令动态改变参数:
java -jar <jmeter_path>/lib/bshclient.jar localhost 9000 update.bsh <参数>
目前基于MeterSphere进行这方面的改造有困难,一方面是开启beanshell.server.port就打破了原有JMeter-Master无需端口调用的优势(前面说到的优点);另外JMeter只支持DynamicThread动态线程技术的ConcurrencyThreadGroup、ArrivalsThreadGroup线程组和Constant Throughput Timer吞吐量定时器等进行动态改变参数变量,适用性上有限。但是,还是希望MeterSphere的工程师们能找到最佳的方案解决这个问题,哪怕是折中的方式通过外部手动配置也行。
关于JMeter动态压力的相关技术可以关注我的另一篇文章《JMeter动态吞吐量实现》:https://blog.csdn.net/smooth00/article/details/121655220?spm=1001.2014.3001.5501
当然以上的想法不适合所有人,有很多人可能用不到这个功能,盲目做到产品化的MeterSphere当中,可能会导致易用性和部署维护上变得糟糕。不过,作为云压测系统重要的一个功能还是尽早考虑考虑。
MeterSphere组件及资源
■ Frontend:MeterSphere的前端工程,基于Vue.js进行开发;
■ Backend:MeterSphere的后端工程, 基于Sprint Boot进行开发,为MeterSphere的功能主体。目前Frontend和Backend合为一套源码:https://github.com/metersphere/metersphere;
■ Node Controller:为性能测试提供独立节点类型的测试资源池,接收来自系统的性能测试任务,动态地启动JMeter容器完成性能测试。源码地址:https://github.com/metersphere/node-controller;
■ MySQL:MeterSphere项目的主要数据均存储在MySQL;
■ Kafka:接收JMeter产生的性能测试结果数据。JMeter中用到了Kafka监听器插件,源码地址:https://github.com/metersphere/jmeter-backend-listener-kafka;
■ Data Streaming:从Kafka中获取性能测试结果数据进行处理后存入 MySQL数据库。源码地址:https://github.com/metersphere/data-streaming;
■ Docker Engine:为Node Controller提供JMeter Master容器运行环境。镜像构建文件:https://github.com/metersphere/jmeter-image;
■ Chrome Plugin:浏览器插件,录制Web访问请求生成JMeter脚本,并导入到MeterSphere中用于接口测试及性能测试。源码地址:https://github.com/metersphere/chrome-extensions;
■ Jenkins Plugin:配套的Jenkins插件,在Jenkins任务中触发指定的MeterSphere平台上的测试任务执行。源码地址:https://github.com/metersphere/jenkins-plugin;
■ helm-chart:用于部署MeterSphere Kubernetes环境的Helm Chart,源文件:https://github.com/metersphere/helm-chart;
■ 其他组件:可以到MeterSphere仓库中找,地址为:https://github.com/orgs/metersphere/repositories;
■ MeterSphere的Docker镜像地址:https://hub.docker.com/u/metersphere。
写到这,发现自己写的很多了,就不再多说了。其实对于测试或技术工作来说,最重要的要素是人,其次才是工具。毕竟工具是死的,人是活的,再好的工具也得有懂它的人才能发挥价值,也只有人才能促进工具往好的方向演进。
MeterSphere开源代码地址:https://github.com/metersphere/
————————————————
版权声明:本文为CSDN博主「smooth-z」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/smooth00/article/details/123863730
2、本站永久网址:https://www.yuanmacun.com
3、本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
4、本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
6、本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
源码村资源网 » 免费个人博客源码(简易个人博客网站源码)
1 评论