服务器性能测试
2016-07-10 23:04
507 查看
最近看了coolshell 上面一篇文章,《性能测试应该怎么做?》
原文链接如下:
http://coolshell.cn/articles/17381.html?from=groupmessage&isappinstalled=0
里面提到三个观点
平均值不靠谱,而是应该使用百分比分布来统计
响应时间(latency)要和吞吐量(Thoughput)挂钩
响应时间要和成功率挂钩
以及严谨测试服务器性能的方法,摘抄如下:
下面的这些性能测试的方式基本上来源自我的老老东家汤森路透,一家做real-time的金融数据系统的公司。
一,你得定义一个系统的响应时间latency,建议是TP99,以及成功率。比如路透的定义:99.9%的响应时间必需在1ms之内,平均响应时间在1ms以内,100%的请求成功。
二,在这个响应时间的限制下,找到最高的吞吐量。测试用的数据,需要有大中小各种尺寸的数据,并可以混合。最好使用生产线上的测试数据。
三,在这个吞吐量做Soak Test,比如:使用第二步测试得到的吞吐量连续7天的不间断的压测系统。然后收集CPU,内存,硬盘/网络IO,等指标,查看系统是否稳定,比如,CPU是平稳的,内存使用也是平稳的。那么,这个值就是系统的性能
四,找到系统的极限值。比如:在成功率100%的情况下(不考虑响应时间的长短),系统能坚持10分钟的吞吐量。
五,做Burst Test。用第二步得到的吞吐量执行5分钟,然后在第四步得到的极限值执行1分钟,再回到第二步的吞吐量执行5钟,再到第四步的权限值执行1分钟,如此往复个一段时间,比如2天。收集系统数据:CPU、内存、硬盘/网络IO等,观察他们的曲线,以及相应的响应时间,确保系统是稳定的。
六、低吞吐量和网络小包的测。有时候,在低吞吐量的时候,可能会导致latency上升,比如TCP_NODELAY的参数没有开启会导致latency上升(详见TCP的那些事),而网络小包会导致带宽用不满也会导致性能上不去,所以,性能测试还需要根据实际情况有选择的测试一下这两场景。
近期正好在做thrift 服务器相关latency性能的测试的工作。
于是按照测试方法, 写了简单的demo_client 和 demo_server 来测试相应的latency, 测出每次query 响应的latency 做成一个直方图histogram 来统计 latency 的百分比份额 :
我写的histogram如下:
//histogram.h
//histogram.cc
下面是测试文件
histogram_test.cc
1. 溢出没有再函数里判断,使得其他用户很容易犯错
2. 线程不安全,多线程使用会出现问题
3. 程序代码不规范
4. ..
下面贴出更改后的代码
//histogram.h
使用atomic 来实现线程安全。
由于atomic 对象不能复制,所以不能直接放入std::vector中,需要写一个AtomicWrapper , 才能放入vector中
//histogram.cc
下面是client 端和server 端的代码
原文链接如下:
http://coolshell.cn/articles/17381.html?from=groupmessage&isappinstalled=0
里面提到三个观点
平均值不靠谱,而是应该使用百分比分布来统计
响应时间(latency)要和吞吐量(Thoughput)挂钩
响应时间要和成功率挂钩
以及严谨测试服务器性能的方法,摘抄如下:
如何严谨地做性能测试
一般来说,性能测试要统一考虑这么几个因素:Thoughput吞吐量,Latency响应时间,资源利用(CPU/MEM/IO/Bandwidth…),成功率,系统稳定性。下面的这些性能测试的方式基本上来源自我的老老东家汤森路透,一家做real-time的金融数据系统的公司。
一,你得定义一个系统的响应时间latency,建议是TP99,以及成功率。比如路透的定义:99.9%的响应时间必需在1ms之内,平均响应时间在1ms以内,100%的请求成功。
二,在这个响应时间的限制下,找到最高的吞吐量。测试用的数据,需要有大中小各种尺寸的数据,并可以混合。最好使用生产线上的测试数据。
三,在这个吞吐量做Soak Test,比如:使用第二步测试得到的吞吐量连续7天的不间断的压测系统。然后收集CPU,内存,硬盘/网络IO,等指标,查看系统是否稳定,比如,CPU是平稳的,内存使用也是平稳的。那么,这个值就是系统的性能
四,找到系统的极限值。比如:在成功率100%的情况下(不考虑响应时间的长短),系统能坚持10分钟的吞吐量。
五,做Burst Test。用第二步得到的吞吐量执行5分钟,然后在第四步得到的极限值执行1分钟,再回到第二步的吞吐量执行5钟,再到第四步的权限值执行1分钟,如此往复个一段时间,比如2天。收集系统数据:CPU、内存、硬盘/网络IO等,观察他们的曲线,以及相应的响应时间,确保系统是稳定的。
六、低吞吐量和网络小包的测。有时候,在低吞吐量的时候,可能会导致latency上升,比如TCP_NODELAY的参数没有开启会导致latency上升(详见TCP的那些事),而网络小包会导致带宽用不满也会导致性能上不去,所以,性能测试还需要根据实际情况有选择的测试一下这两场景。
近期正好在做thrift 服务器相关latency性能的测试的工作。
于是按照测试方法, 写了简单的demo_client 和 demo_server 来测试相应的latency, 测出每次query 响应的latency 做成一个直方图histogram 来统计 latency 的百分比份额 :
我写的histogram如下:
//histogram.h
#ifndef COMMON_ALGORITHM_HISTOGRAM_H_ #define COMMON_ALGORITHM_HISTOGRAM_H_ #include <vector> #include <string> namespace common{ namespace algorithm{ class Histogram{ public: Histogram(float _bucketwildth, int32_t _bucketnums); void AddData(float latency); void ShowHistogram(); std::vector<int32_t>GetDataVector(); int32_t GetDataNums(); private: std::vector<int32_t>bucket; float bucketwidth; int32_t bucketnums; int32_t datanums; }; } } using common::algorithm::Histogram; #endif
//histogram.cc
#include "common/algorithm/histogram.h" #include "boost/shared_ptr.hpp" #include <iostream> namespace common{ namespace algorithm{ Histogram::Histogram(float _bucketwidth, int32_t _bucketnums){ bucketwidth = _bucketwidth; bucketnums = _bucketnums; bucket.resize(bucketnums+1,0); } void Histogram::AddData(float latency){ bucket[static_cast<int32_t>(latency/bucketwidth)]++; datanums++; } int32_t Histogram::GetDataNums(){ return datanums; } void Histogram::ShowHistogram(){ printf("Range DataNums Percentage \n\n"); for(int32_t i=0;i< bucket.size();++i){ printf("%.3f -- %.3f : ", bucketwidth*i , bucketwidth*(i+1 )); printf("%d ", bucket[i]); printf("%.3f%\n", (1.0*bucket[i])/(1.0*datanums)*100); } } std::vector<int32_t> Histogram::GetDataVector(){ return bucket; } } }
下面是测试文件
histogram_test.cc
#include<iostream> #include<vector> #include<string> #include"common/algorithm/histogram.h" #include"boost/shared_ptr.hpp" int main(){ auto p1 = std::make_shared<Histogram>(5.0,30); for(int32_t i = 0; i<100; i+=2){ p1->AddData(static_cast<float>(i)); } p1->ShowHistogram(); return 0; }
更新
上面的程序有很多问题1. 溢出没有再函数里判断,使得其他用户很容易犯错
2. 线程不安全,多线程使用会出现问题
3. 程序代码不规范
4. ..
下面贴出更改后的代码
//histogram.h
#ifndef COMMON_ALGORITHM_HISTOGRAM_H_ #define COMMON_ALGORITHM_HISTOGRAM_H_ #include <atomic> #include <string> #include <vector> #include "common/base/stl_util.h" namespace common { namespace algorithm { class Histogram { public: Histogram(double min, double max, double step); void AddData(double data); std::string ToString(); size_t data_num() const; private: std::vector<AtomicWrapper<size_t> > buckets_; double bucket_width_; double min_; double max_; size_t bucket_num_; size_t data_num_; }; } // namespace algorithm } // namespace common using common::algorithm::Histogra fab0 m; #endif // COMMON_ALGORITHM_HISTOGRAM_H_
使用atomic 来实现线程安全。
由于atomic 对象不能复制,所以不能直接放入std::vector中,需要写一个AtomicWrapper , 才能放入vector中
template <typename T> struct AtomicWrapper { AtomicWrapper(): a_() {} AtomicWrapper(const std::atomic<T>& a): a_(a.load()) {} AtomicWrapper(const AtomicWrapper& other): a_(other.a_.load()) {} AtomicWrapper& operator=(const AtomicWrapper& rhs) { a_.store(rhs.load()); } std::atomic<T> a_; };
//histogram.cc
#include "common/algorithm/histogram.h" #include <string> #include "common/base/log.h" #include "common/string/concat.h" namespace common { namespace algorithm { using std::atomic; using std::to_string; using std::string; Histogram::Histogram(double min, double max, double step) : min_(min), max_(max), bucket_width_(step), data_num_(0) { bucket_num_ = static_cast<size_t>(abs((max - min) / step) + 2); buckets_.resize(bucket_num_); } void Histogram::AddData(double data) { if (buckets_.empty()) return; if (data < min_) { buckets_[0].a_++; } else if (data < min_ + bucket_width_ * (bucket_num_ - 2)) { buckets_[static_cast<size_t>((data - min_) / bucket_width_) + 1].a_++; } else { buckets_[bucket_num_ - 1].a_++; } data_num_++; } size_t Histogram::data_num() const { return data_num_; } string Histogram::ToString() { string res = " -NaN -- "; res = Concat(res, to_string(min_), " : ", to_string(buckets_[0].a_), " ", to_string(static_cast<double>(buckets_[0].a_) / data_num_ * 100), "%\n"); for (size_t i = 1; i < bucket_num_ - 1; ++i) { res = Concat(res, to_string(bucket_width_ * (i - 1) + min_), " -- ", to_string(bucket_width_ * i + min_), " : ", to_string(buckets_[i].a_), " ", to_string(static_cast<double>(buckets_[i].a_) / data_num_ * 100), "%\n"); } res = Concat(res, to_string(bucket_width_ * (bucket_num_-2) + min_), " -- NaN : ", to_string(buckets_[bucket_num_-1].a_), " ", to_string(static_cast<double>(buckets_[bucket_num_-1].a_) / data_num_ * 100), "%\n"); return res; } } // namespace algorithm } // namespace common
使用gtest书写测试单元 unit.
//histogram_test.cc#include "common/algorithm/histogram.h" #include <memory> #include "thirdparty/glog/logging.h" #include "thirdparty/gtest/gtest.h" using std::string; TEST(HistogramTest, HistogramBaseTest) { Histogram histogram(-5.0, 10, 2); for (int32_t i = -10; i < 20; i += 1) { histogram.AddData(static_cast<double>(i)); } string res =" -NaN -- -5.000000 : 5 16.666667%\n\ -5.000000 -- -3.000000 : 2 6.666667%\n\ -3.000000 -- -1.000000 : 2 6.666667%\n\ -1.000000 -- 1.000000 : 2 6.666667%\n\ 1.000000 -- 3.000000 : 2 6.666667%\n\ 3.000000 -- 5.000000 : 2 6.666667%\n\ 5.000000 -- 7.000000 : 2 6.666667%\n\ 7.000000 -- 9.000000 : 2 6.666667%\n\ 9.000000 -- NaN : 11 36.666667%\n"; EXPECT_STREQ(res.c_str(), histogram.ToString().c_str()); }
下面是client 端和server 端的代码
// demo_multi_client.cc #include <memory> #include <string> #include <vector> #include <gtest/gtest.h> #include "common/thrift/echo_handler.h" #include "common/string/convert.h" #include "common/algorithm/histogram.h" #include "common/base/log.h" #include "common/system/time/stop_watch.h" #include "common/base/sys_utils.h" #include "common/thread/thread.h" using std::make_shared; using std::shared_ptr; void MakeClientEcho(shared_ptr<Histogram> histogram, size_t nPipelines,size_t port) { auto client = std::make_shared<TEchoClient>("localhost",port); client->Connect(); LOG(INFO) << "Client Connected!"; for (size_t k =0; k<nPipelines; ++k) { Request req; Response resp; req.__set_msg("lanfengrequest"); req.__set_seq(k); StopWatch sw; client->client()->Echo(resp,req); histogram->AddData(sw.GetElapsedUs()); if (req.seq != resp.seq) { LOG(INFO) <<"Echo sequence wrong!"; } } } int32_t main (int32_t argc, char * argv []) { LOG(INFO) << "pid = " << getpid(); if (argc > 0) { int32_t nClients = argc > 1 ? atoi(argv[1]) : 1; int32_t nPipelines = argc > 2 ? atoi(argv[2]) : 1; double min = argc > 3 ? atof(argv[3]) : 0; double max = argc > 4 ? atof(argv[4]) : 1000; double step = argc > 5 ? atof(argv[5]) : 50; int32_t port = argc > 6 ? atoi(argv[6]) : 9090; LOG(INFO) << "Clientnum = " << nClients << "Pipesnum = " << nPipelines ; LOG(INFO) << "Start "; std::vector<shared_ptr<common::Thread> > threadPtrs; auto histogram = make_shared<Histogram>(min, max, step); threadPtrs.resize(nClients); for (size_t i = 0; i < nClients; ++i) { threadPtrs[i] = make_shared<common::Thread>(); threadPtrs[i]->Start(std::bind(MakeClientEcho, histogram, nPipelines, port)); } for (auto thread: threadPtrs) { thread->Join(); } LOG(INFO) << "all finished"; LOG(INFO) << histogram->ToString(); }else{ printf("Usage: clientnum, pipelines, [min], [max], [step], [port]\n"); } }
// demo_monitor_server.h #include <gtest/gtest.h> #include "common/thrift/echo_monitor_handler.h" #include "common/string/convert.h" #include "common/base/log.h" #include "common/monitor/statistic_monitor.h" #include "common/thrift/monitor_names.h" #include "protoss/common/protoss_service.h" namespace thrift { class MonitorService : public ::protoss::ProtossService { public: MonitorService(const int32_t argc, const char* argv[]) : ProtossService(argc, argv) { } virtual ~MonitorService() { LOG(INFO) << "exiting monitor service ..."; } virtual void Run() { LOG(INFO) << "Start monitor Service ..."; printf("start monitor service ...\n"); ProtossService::Run(); echo_server_->Start(); } void InitGlobal () { echo_server_ = std::make_shared<TEchoServer> (FLAGS_port, FLAGS_thread_num); MonitorNames::RegisterAll(); } private: std::shared_ptr<TEchoServer> echo_server_; }; } // namespace thrift } // namespace common int32_t main(int32_t argc, const char* argv[]) { common::thrift::MonitorService mymonitor(argc, argv); mymonitor.InitGlobal(); mymonitor.Run(); return 0; }
相关文章推荐
- 小心服务器内存居高不下的元凶--WebAPI服务
- 使用C++实现JNI接口需要注意的事项
- 运维入门
- 关于指针的一些事情
- Java IO与NIO的一些文件拷贝测试
- c++ primer 第五版 笔记前言
- 利用开源软件打造自己的全功能远程工具
- Linux5.9无人值守安装
- 数据中心和云未来的十二大趋势
- 虚拟化基础架构Windows 2008篇之11-WSUS服务器的安装与配置
- 用vsftp快速搭建ftp服务器
- Linux快速构建apache web服务器
- 服务器监控策略浅谈
- share_ptr的几个注意点
- 如何降低服务器采购成本 原理分析
- 建议的服务器分区办法
- 服务器托管六大优势分析
- Erlang实现的一个Web服务器代码实例
- 服务器技术全面解析