2024-03-08 14:03:37 +08:00

240 lines
5.6 KiB
C++

#include "muduo/base/CountDownLatch.h"
#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/EventLoopThreadPool.h"
#include "muduo/net/TcpClient.h"
#include <boost/program_options.hpp>
#include <iostream>
#include <stdio.h>
namespace po = boost::program_options;
using namespace muduo;
using namespace muduo::net;
class Client : noncopyable
{
public:
enum Operation
{
kGet,
kSet,
};
Client(const string& name,
EventLoop* loop,
const InetAddress& serverAddr,
Operation op,
int requests,
int keys,
int valuelen,
CountDownLatch* connected,
CountDownLatch* finished)
: name_(name),
client_(loop, serverAddr, name),
op_(op),
sent_(0),
acked_(0),
requests_(requests),
keys_(keys),
valuelen_(valuelen),
value_(valuelen_, 'a'),
connected_(connected),
finished_(finished)
{
value_ += "\r\n";
client_.setConnectionCallback(std::bind(&Client::onConnection, this, _1));
client_.setMessageCallback(std::bind(&Client::onMessage, this, _1, _2, _3));
client_.connect();
}
void send()
{
Buffer buf;
fill(&buf);
conn_->send(&buf);
}
private:
void onConnection(const TcpConnectionPtr& conn)
{
if (conn->connected())
{
conn_ = conn;
connected_->countDown();
}
else
{
conn_.reset();
client_.getLoop()->queueInLoop(std::bind(&CountDownLatch::countDown, finished_));
}
}
void onMessage(const TcpConnectionPtr& conn,
Buffer* buffer,
Timestamp receiveTime)
{
if (op_ == kSet)
{
while (buffer->readableBytes() > 0)
{
const char* crlf = buffer->findCRLF();
if (crlf)
{
buffer->retrieveUntil(crlf+2);
++acked_;
if (sent_ < requests_)
{
send();
}
}
else
{
break;
}
}
}
else
{
while (buffer->readableBytes() > 0)
{
const char* end = static_cast<const char*>(memmem(buffer->peek(),
buffer->readableBytes(),
"END\r\n", 5));
if (end)
{
buffer->retrieveUntil(end+5);
++acked_;
if (sent_ < requests_)
{
send();
}
}
else
{
break;
}
}
}
if (acked_ == requests_)
{
conn_->shutdown();
}
}
void fill(Buffer* buf)
{
char req[256];
if (op_ == kSet)
{
snprintf(req, sizeof req, "set %s%d 42 0 %d\r\n", name_.c_str(), sent_ % keys_, valuelen_);
++sent_;
buf->append(req);
buf->append(value_);
}
else
{
snprintf(req, sizeof req, "get %s%d\r\n", name_.c_str(), sent_ % keys_);
++sent_;
buf->append(req);
}
}
string name_;
TcpClient client_;
TcpConnectionPtr conn_;
const Operation op_;
int sent_;
int acked_;
const int requests_;
const int keys_;
const int valuelen_;
string value_;
CountDownLatch* const connected_;
CountDownLatch* const finished_;
};
int main(int argc, char* argv[])
{
Logger::setLogLevel(Logger::WARN);
uint16_t tcpport = 11211;
string hostIp = "127.0.0.1";
int threads = 4;
int clients = 100;
int requests = 100000;
int keys = 10000;
bool set = false;
po::options_description desc("Allowed options");
desc.add_options()
("help,h", "Help")
("port,p", po::value<uint16_t>(&tcpport), "TCP port")
("ip,i", po::value<string>(&hostIp), "Host IP")
("threads,t", po::value<int>(&threads), "Number of worker threads")
("clients,c", po::value<int>(&clients), "Number of concurrent clients")
("requests,r", po::value<int>(&requests), "Number of requests per clients")
("keys,k", po::value<int>(&keys), "Number of keys per clients")
("set,s", "Get or Set")
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help"))
{
std::cout << desc << "\n";
return 0;
}
set = vm.count("set");
InetAddress serverAddr(hostIp, tcpport);
LOG_WARN << "Connecting " << serverAddr.toIpPort();
EventLoop loop;
EventLoopThreadPool pool(&loop, "bench-memcache");
int valuelen = 100;
Client::Operation op = set ? Client::kSet : Client::kGet;
double memoryMiB = 1.0 * clients * keys * (32+80+valuelen+8) / 1024 / 1024;
LOG_WARN << "estimated memcached-debug memory usage " << int(memoryMiB) << " MiB";
pool.setThreadNum(threads);
pool.start();
char buf[32];
CountDownLatch connected(clients);
CountDownLatch finished(clients);
std::vector<std::unique_ptr<Client>> holder;
for (int i = 0; i < clients; ++i)
{
snprintf(buf, sizeof buf, "%d-", i+1);
holder.emplace_back(new Client(buf,
pool.getNextLoop(),
serverAddr,
op,
requests,
keys,
valuelen,
&connected,
&finished));
}
connected.wait();
LOG_WARN << clients << " clients all connected";
Timestamp start = Timestamp::now();
for (int i = 0; i < clients; ++i)
{
holder[i]->send();
}
finished.wait();
Timestamp end = Timestamp::now();
LOG_WARN << "All finished";
double seconds = timeDifference(end, start);
LOG_WARN << seconds << " sec";
LOG_WARN << 1.0 * clients * requests / seconds << " QPS";
}