refactor RequestHandler behavior

This refactors the RequestHandler code, to decouple RequestHandler
behavior from the Server implementation.  The goal is to make it easier
to define additional Server implementations, in addition to just
LibEventServer.

This adds a RequestHandlerFactory function, rather than using a pure
virtual method of the Server class.  With the old model, you had to
subclass each server implementation separately for each RequestHandler
type you wanted to use, resulting in NxM classes if you have N server
types and M request handler types.

This also changes the behavior of the RequestHandler class somewhat:
the code now only creates a single RequestHandler per thread, and uses
that object for all request in that thread.  Previously the
LibEventServer code would attempt to create a new RequestHandler object
for each request if supportReset() returned true.  This was used by
RPCRequestHandler.  Now the RPCRequestHandler instead just resets itself
automatically when necessary, without requiring external help from
LibEventServer.

contbuild test runs failed due to git server issues.
Esse commit está contido em:
Adam Simpkins
2013-05-24 18:17:35 -07:00
commit de Sara Golemon
commit ee27bfa147
11 arquivos alterados com 150 adições e 160 exclusões
+27 -26
Ver Arquivo
@@ -33,10 +33,12 @@
#include "hphp/runtime/base/program_functions.h"
#include "hphp/runtime/debugger/debugger.h"
#include "hphp/util/db_conn.h"
#include "hphp/util/ssl_init.h"
#include "hphp/runtime/ext/ext_apc.h"
#include <boost/make_shared.hpp>
#include <sys/types.h>
#include <signal.h>
#include "hphp/util/ssl_init.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
@@ -75,44 +77,43 @@ HttpServer::HttpServer(void *sslCTX /* = NULL */)
: kNumProcessors;
if (RuntimeOption::ServerPortFd != -1 || RuntimeOption::SSLPortFd != -1) {
LibEventServerWithFd* server =
(new TypedServer<LibEventServerWithFd, HttpRequestHandler>
(RuntimeOption::ServerIP, RuntimeOption::ServerPort,
startingThreadCount,
RuntimeOption::RequestTimeoutSeconds));
maybeEnableThrottle(server);
server->setServerSocketFd(RuntimeOption::ServerPortFd);
server->setSSLSocketFd(RuntimeOption::SSLPortFd);
m_pageServer = ServerPtr(server);
} else if (RuntimeOption::TakeoverFilename.empty()) {
auto const server = new TypedServer<LibEventServer, HttpRequestHandler>
(RuntimeOption::ServerIP, RuntimeOption::ServerPort,
auto const server = boost::make_shared<LibEventServerWithFd>(
RuntimeOption::ServerIP, RuntimeOption::ServerPort,
startingThreadCount,
RuntimeOption::RequestTimeoutSeconds);
maybeEnableThrottle(server);
m_pageServer = ServerPtr(server);
} else {
LibEventServerWithTakeover* server =
(new TypedServer<LibEventServerWithTakeover, HttpRequestHandler>
(RuntimeOption::ServerIP, RuntimeOption::ServerPort,
maybeEnableThrottle(server.get());
server->setServerSocketFd(RuntimeOption::ServerPortFd);
server->setSSLSocketFd(RuntimeOption::SSLPortFd);
m_pageServer = server;
} else if (RuntimeOption::TakeoverFilename.empty()) {
auto const server = boost::make_shared<LibEventServer>(
RuntimeOption::ServerIP, RuntimeOption::ServerPort,
startingThreadCount,
RuntimeOption::RequestTimeoutSeconds));
maybeEnableThrottle(server);
RuntimeOption::RequestTimeoutSeconds);
maybeEnableThrottle(server.get());
m_pageServer = server;
} else {
auto const server = boost::make_shared<LibEventServerWithTakeover>(
RuntimeOption::ServerIP, RuntimeOption::ServerPort,
startingThreadCount,
RuntimeOption::RequestTimeoutSeconds);
maybeEnableThrottle(server.get());
server->setTransferFilename(RuntimeOption::TakeoverFilename);
server->addTakeoverListener(this);
m_pageServer = ServerPtr(server);
m_pageServer = server;
}
m_pageServer->setRequestHandlerFactory<HttpRequestHandler>();
if (RuntimeOption::EnableSSL && m_sslCTX) {
assert(SSLInit::IsInited());
m_pageServer->enableSSL(m_sslCTX, RuntimeOption::SSLPort);
}
m_adminServer = ServerPtr
(new TypedServer<LibEventServer, AdminRequestHandler>
(RuntimeOption::ServerIP, RuntimeOption::AdminServerPort,
m_adminServer = boost::make_shared<LibEventServer>(
RuntimeOption::ServerIP, RuntimeOption::AdminServerPort,
RuntimeOption::AdminThreadCount,
RuntimeOption::RequestTimeoutSeconds));
RuntimeOption::RequestTimeoutSeconds);
m_adminServer->setRequestHandlerFactory<AdminRequestHandler>();
for (unsigned int i = 0; i < RuntimeOption::SatelliteServerInfos.size();
i++) {
+5 -10
Ver Arquivo
@@ -16,8 +16,6 @@
#include "hphp/runtime/base/server/libevent_server.h"
#include <atomic>
#include "hphp/runtime/base/runtime_option.h"
#include "hphp/runtime/base/memory/memory_manager.h"
#include "hphp/runtime/base/server/server_stats.h"
@@ -78,7 +76,7 @@ void LibEventJob::stopTimer() {
///////////////////////////////////////////////////////////////////////////////
// LibEventWorker
LibEventWorker::LibEventWorker() : m_handler(nullptr) {
LibEventWorker::LibEventWorker() {
}
LibEventWorker::~LibEventWorker() {
@@ -92,11 +90,6 @@ void LibEventWorker::doJob(LibEventJobPtr job) {
server->bumpReqCount();
if (m_handler == nullptr || server->supportReset()) {
m_handler = server->createRequestHandler();
assert(m_handler);
}
LibEventTransport transport(server, request, m_id);
#ifdef _EVENT_USE_OPENSSL
if (evhttp_is_connection_ssl(job->request->evcon)) {
@@ -144,12 +137,14 @@ void LibEventWorker::onThreadEnter() {
if (RuntimeOption::EnableDebugger) {
Eval::Debugger::RegisterThread();
}
m_handler = server->createRequestHandler();
}
void LibEventWorker::onThreadExit() {
assert(m_opaque);
LibEventServer *server = (LibEventServer*)m_opaque;
server->onThreadExit(m_handler);
server->onThreadExit();
m_handler.reset();
}
///////////////////////////////////////////////////////////////////////////////
@@ -401,7 +396,7 @@ void LibEventServer::onThreadEnter() {
(&ThreadInfo::s_threadInfo->m_reqInjectionData);
}
void LibEventServer::onThreadExit(RequestHandler *handler) {
void LibEventServer::onThreadExit() {
m_timeoutThreadData.removeRequestThread
(&ThreadInfo::s_threadInfo->m_reqInjectionData);
}
+3 -6
Ver Arquivo
@@ -34,7 +34,7 @@ namespace HPHP {
DECLARE_BOOST_TYPES(LibEventJob);
class LibEventJob {
public:
LibEventJob(evhttp_request *req);
explicit LibEventJob(evhttp_request *req);
const timespec &getStartTimer() const { return start;}
void stopTimer();
@@ -68,7 +68,7 @@ struct LibEventWorker
virtual void onThreadExit();
private:
RequestHandler *m_handler;
std::unique_ptr<RequestHandler> m_handler;
};
/**
@@ -152,7 +152,7 @@ public:
int getLibEventConnectionCount();
void onThreadEnter();
virtual void onThreadExit(RequestHandler *handler);
void onThreadExit();
/**
* Request handler called by evhttp library.
@@ -176,9 +176,6 @@ public:
*/
virtual bool enableSSL(void *sslCTX, int port);
// Whether the server may reset the request handler, e.g., the RPC server.
virtual bool supportReset() { return false; }
protected:
virtual int getAcceptSocket();
virtual int getAcceptSocketSSL();
+38 -15
Ver Arquivo
@@ -33,8 +33,18 @@ namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
RPCRequestHandler::RPCRequestHandler(bool info /* = true */)
: m_count(0), m_reset(false),
m_returnEncodeType(Json) {
: m_requestsSinceReset(0),
m_reset(false),
m_logResets(info),
m_returnEncodeType(Json) {
initState();
}
RPCRequestHandler::~RPCRequestHandler() {
cleanupState();
}
void RPCRequestHandler::initState() {
hphp_session_init();
bool isServer = RuntimeOption::ServerExecutionMode();
if (isServer) {
@@ -45,25 +55,36 @@ RPCRequestHandler::RPCRequestHandler(bool info /* = true */)
m_context = g_context.getNoCheck();
m_context->obSetImplicitFlush(true);
}
m_created = time(0);
m_lastReset = time(0);
Logger::ResetRequestCount();
if (info) {
Logger::Info("creating new RPC request handler");
if (m_logResets) {
Logger::Info("initializing RPC request handler");
}
m_reset = false;
m_requestsSinceReset = 0;
}
RPCRequestHandler::~RPCRequestHandler() {
void RPCRequestHandler::cleanupState() {
hphp_context_exit(m_context, false);
hphp_session_exit();
}
bool RPCRequestHandler::needReset() const {
if (m_reset || m_serverInfo->alwaysReset()) return true;
return (time(0) - m_created) > m_serverInfo->getMaxDuration();
return (m_reset ||
m_serverInfo->alwaysReset() ||
((time(0) - m_lastReset) > m_serverInfo->getMaxDuration()) ||
(m_requestsSinceReset >= m_serverInfo->getMaxRequest()));
}
void RPCRequestHandler::handleRequest(Transport *transport) {
if (needReset()) {
cleanupState();
initState();
}
++m_requestsSinceReset;
ExecutionProfiler ep(ThreadInfo::RuntimeFunctions);
Logger::OnNewRequest();
@@ -110,8 +131,9 @@ void RPCRequestHandler::handleRequest(Transport *transport) {
}
// return encoding type
ReturnEncodeType returnEncodeType = m_returnEncodeType;
if (transport->getParam("return") == "serialize") {
setReturnEncodeType(Serialize);
returnEncodeType = Serialize;
}
// resolve virtual host
@@ -143,7 +165,7 @@ void RPCRequestHandler::handleRequest(Transport *transport) {
// record request for debugging purpose
std::string tmpfile = HttpProtocol::RecordRequest(transport);
bool ret = executePHPFunction(transport, sourceRootInfo);
bool ret = executePHPFunction(transport, sourceRootInfo, returnEncodeType);
HttpRequestHandler::GetAccessLog().log(transport, vhost);
/*
* HPHP logs may need to access data in ServerStats, so we have to
@@ -159,7 +181,8 @@ static const StaticString
s_HPHP_RPC("HPHP_RPC");
bool RPCRequestHandler::executePHPFunction(Transport *transport,
SourceRootInfo &sourceRootInfo) {
SourceRootInfo &sourceRootInfo,
ReturnEncodeType returnEncodeType) {
// reset timeout counter
ThreadInfo::s_threadInfo->m_reqInjectionData.started = time(0);
@@ -271,11 +294,11 @@ bool RPCRequestHandler::executePHPFunction(Transport *transport,
String response;
switch (output) {
case 0: {
assert(m_returnEncodeType == Json ||
m_returnEncodeType == Serialize);
assert(returnEncodeType == Json ||
returnEncodeType == Serialize);
try {
response = (m_returnEncodeType == Json) ? f_json_encode(funcRet)
: f_serialize(funcRet);
response = (returnEncodeType == Json) ? f_json_encode(funcRet)
: f_serialize(funcRet);
} catch (...) {
serializeFailed = true;
}
+12 -13
Ver Arquivo
@@ -35,7 +35,7 @@ public:
Serialize = 2,
};
RPCRequestHandler(bool info = true);
explicit RPCRequestHandler(bool info = true);
virtual ~RPCRequestHandler();
void setServerInfo(SatelliteServerInfoPtr info) { m_serverInfo = info;}
@@ -44,30 +44,29 @@ public:
virtual void handleRequest(Transport *transport);
/**
* Count how many requests have been processed on this handler.
* Force a reset before the next request.
*/
int incRequest() { return ++m_count;}
/**
* Whether state has been dirtied.
*/
bool needReset() const;
void setReset() { m_reset = true; }
time_t getCreationTime() const { return m_created; }
time_t getLastResetTime() const { return m_lastReset; }
void setReturnEncodeType(ReturnEncodeType et) { m_returnEncodeType = et; }
ReturnEncodeType getReturnEncodeType() { return m_returnEncodeType; }
ReturnEncodeType getReturnEncodeType() const { return m_returnEncodeType; }
private:
ExecutionContext *m_context;
SatelliteServerInfoPtr m_serverInfo;
int m_count;
int m_requestsSinceReset;
bool m_reset;
bool m_logResets;
ReturnEncodeType m_returnEncodeType;
time_t m_created;
time_t m_lastReset;
void initState();
void cleanupState();
bool needReset() const;
bool executePHPFunction(Transport *transport,
SourceRootInfo &sourceRootInfo);
SourceRootInfo &sourceRootInfo,
ReturnEncodeType returnEncodeType);
std::string getSourceFilename(const std::string &path,
SourceRootInfo &sourceRootInfo);
+20 -47
Ver Arquivo
@@ -22,7 +22,11 @@
#include "hphp/runtime/base/runtime_option.h"
#include "hphp/runtime/base/preg.h"
#include "hphp/util/util.h"
#include "folly/Memory.h"
#include <boost/make_shared.hpp>
using folly::make_unique;
using std::set;
namespace HPHP {
@@ -100,10 +104,10 @@ private:
class InternalPageServer : public SatelliteServer {
public:
explicit InternalPageServer(SatelliteServerInfoPtr info) {
InternalPageServerImplPtr server
(new TypedServer<InternalPageServerImpl, HttpRequestHandler>
(RuntimeOption::ServerIP, info->getPort(), info->getThreadCount(),
info->getTimeoutSeconds()));
auto const server = boost::make_shared<InternalPageServerImpl>(
RuntimeOption::ServerIP, info->getPort(), info->getThreadCount(),
info->getTimeoutSeconds());
server->setRequestHandlerFactory<HttpRequestHandler>();
server->create(info->getURLs());
m_server = server;
}
@@ -125,10 +129,10 @@ private:
class DanglingPageServer : public SatelliteServer {
public:
explicit DanglingPageServer(SatelliteServerInfoPtr info) {
m_server = ServerPtr
(new TypedServer<LibEventServer, HttpRequestHandler>
(RuntimeOption::ServerIP, info->getPort(), info->getThreadCount(),
info->getTimeoutSeconds()));
m_server = boost::make_shared<LibEventServer>(
RuntimeOption::ServerIP, info->getPort(), info->getThreadCount(),
info->getTimeoutSeconds());
m_server->setRequestHandlerFactory<HttpRequestHandler>();
}
virtual void start() {
@@ -145,48 +149,17 @@ private:
///////////////////////////////////////////////////////////////////////////////
// RPCServer: LibEventServer + RPCRequestHandler
static IMPLEMENT_THREAD_LOCAL(RPCRequestHandler, s_rpc_request_handler);
class RPCServerImpl : public LibEventServer {
public:
RPCServerImpl(const std::string &address, SatelliteServerInfoPtr info)
: LibEventServer(address, info->getPort(), info->getThreadCount(),
info->getTimeoutSeconds()),
m_serverInfo(info) {
}
virtual RequestHandler *createRequestHandler() {
if (s_rpc_request_handler.isNull()) {
s_rpc_request_handler->setServerInfo(m_serverInfo);
return s_rpc_request_handler.get();
}
if (s_rpc_request_handler->needReset() ||
s_rpc_request_handler->incRequest() > m_serverInfo->getMaxRequest()) {
s_rpc_request_handler.destroy();
s_rpc_request_handler->setServerInfo(m_serverInfo);
s_rpc_request_handler->incRequest();
}
return s_rpc_request_handler.get();
}
virtual void releaseRequestHandler(RequestHandler *handler) {
// do nothing
}
virtual void onThreadExit(RequestHandler *handler) {
s_rpc_request_handler.destroy();
}
virtual bool supportReset() { return true; }
private:
SatelliteServerInfoPtr m_serverInfo;
};
class RPCServer : public SatelliteServer {
public:
explicit RPCServer(SatelliteServerInfoPtr info) {
m_server = ServerPtr(new RPCServerImpl(RuntimeOption::ServerIP, info));
m_server = boost::make_shared<LibEventServer>(
RuntimeOption::ServerIP, info->getPort(), info->getThreadCount(),
info->getTimeoutSeconds());
m_server->setRequestHandlerFactory([info] {
auto handler = make_unique<RPCRequestHandler>();
handler->setServerInfo(info);
return handler;
});
}
virtual void start() {
+31 -35
Ver Arquivo
@@ -21,6 +21,8 @@
#include "hphp/util/exception.h"
#include "hphp/util/lock.h"
#include <memory>
/**
* (1) For people who want to quickly come up with an HTTP server handling
* their specific requests, we really want to minimize writing an HTTP
@@ -35,8 +37,8 @@
*
* Then, run a server like this,
*
* ServerPtr server(new TypedServer<LibEventServer, MyRequestHandler>
* ("127.0.0.1", 80, 20));
* ServerPtr server = make_shared<LibEventServer>("127.0.0.1", 80, 20);
* server->setRequestHandlerFactory<MyRequestHandler>();
* Server::InstallStopSignalHandlers(server);
* server->start();
*
@@ -83,6 +85,8 @@ public:
virtual void handleRequest(Transport *transport) = 0;
};
typedef std::function<std::unique_ptr<RequestHandler>()> RequestHandlerFactory;
/**
* Base class of an HTTP server. Defining minimal interface an HTTP server
* needs to implement.
@@ -113,6 +117,24 @@ public:
*/
Server(const std::string &address, int port, int threadCount);
/**
* Set the RequestHandlerFactory that this server will use.
* This must be called before start().
*/
void setRequestHandlerFactory(RequestHandlerFactory f) {
m_handlerFactory = f;
}
/**
* Helper function to set the RequestHandlerFactory to a
* GenericRequestHandlerFactory for the specified handler type.
*/
template<class TRequestHandler>
void setRequestHandlerFactory() {
setRequestHandlerFactory([] {
return std::unique_ptr<RequestHandler>(new TRequestHandler());
});
}
/**
* Informational.
*/
@@ -163,11 +185,11 @@ public:
virtual int getLibEventConnectionCount() = 0;
/**
* This is for TypedServer to specialize a worker class to use.
* Create a new RequestHandler.
*/
virtual RequestHandler *createRequestHandler() = 0;
virtual void releaseRequestHandler(RequestHandler *handler) = 0;
virtual void onThreadExit(RequestHandler *handler) {}
std::unique_ptr<RequestHandler> createRequestHandler() {
return m_handlerFactory();
}
/**
* Overwrite for URL blocking.
@@ -185,38 +207,12 @@ protected:
int m_port;
int m_threadCount;
mutable Mutex m_mutex;
RequestHandlerFactory m_handlerFactory;
private:
RunStatus m_status;
};
/**
* Binding different types together to form a concrete HTTP server that we
* can run. By conforming to their owns interfaces, RequestHandler and
* Server classes should be able to get mixed up and continue to work.
*/
template<class TServer, class TRequestHandler>
class TypedServer : public TServer {
public:
TypedServer(const std::string &address, int port, int threadCount,
int timeoutSeconds)
: TServer(address, port, threadCount, timeoutSeconds) {
}
virtual RequestHandler *createRequestHandler() {
return new TRequestHandler();
}
virtual void releaseRequestHandler(RequestHandler *handler) {
delete handler;
}
virtual void onThreadExit(RequestHandler *handler) {
TServer::onThreadExit(handler);
delete handler;
}
};
/**
* All exceptions Server throws should derive from this base class.
*/
@@ -236,14 +232,14 @@ public:
class InvalidUrlException : public ServerException {
public:
InvalidUrlException(const char *part)
explicit InvalidUrlException(const char *part)
: ServerException("Invalid URL: %s", part) {
}
};
class InvalidMethodException : public ServerException {
public:
InvalidMethodException(const char *msg)
explicit InvalidMethodException(const char *msg)
: ServerException("Invalid method: %s", msg) {
}
};
-1
Ver Arquivo
@@ -183,7 +183,6 @@ private:
if (RuntimeOption::XboxServerLogInfo) XboxRequestHandler::Info = true;
s_xbox_request_handler->setServerInfo(*s_xbox_server_info);
s_xbox_request_handler->setReturnEncodeType(RPCRequestHandler::Serialize);
s_xbox_request_handler->incRequest();
return s_xbox_request_handler.get();
}
+1 -1
Ver Arquivo
@@ -241,7 +241,7 @@ void f_xbox_schedule_thread_reset() {
int64_t f_xbox_get_thread_time() {
RPCRequestHandler *handler = XboxServer::GetRequestHandler();
if (handler) {
return time(NULL) - handler->getCreationTime();
return time(nullptr) - handler->getLastResetTime();
}
throw Exception("Not an xbox worker!");
}
+5 -2
Ver Arquivo
@@ -20,6 +20,8 @@
#include "hphp/runtime/ext/ext_zlib.h"
#include "hphp/runtime/base/server/libevent_server.h"
#include <boost/make_shared.hpp>
#define PORT_MIN 7100
#define PORT_MAX 7120
@@ -53,8 +55,9 @@ static std::string get_request_uri() {
static ServerPtr runServer() {
for (s_server_port = PORT_MIN; s_server_port <= PORT_MAX; s_server_port++) {
try {
ServerPtr server(new TypedServer<LibEventServer, TestCurlRequestHandler>
("127.0.0.1", s_server_port, 4, -1));
ServerPtr server = boost::make_shared<LibEventServer>(
"127.0.0.1", s_server_port, 4, -1);
server->setRequestHandlerFactory<TestCurlRequestHandler>();
server->start();
return server;
+8 -4
Ver Arquivo
@@ -29,6 +29,8 @@
#include "hphp/runtime/base/util/http_client.h"
#include "hphp/runtime/base/runtime_option.h"
#include <boost/make_shared.hpp>
using namespace HPHP;
#define PORT_MIN 7300
@@ -186,8 +188,9 @@ public:
static int find_server_port(int port_min, int port_max) {
for (int port = port_min; ; port++) {
try {
ServerPtr server(new TypedServer<LibEventServer, TestServerRequestHandler>
("127.0.0.1", port, 50, -1));
ServerPtr server = boost::make_shared<LibEventServer>(
"127.0.0.1", port, 50, -1);
server->setRequestHandlerFactory<TestServerRequestHandler>();
server->start();
server->stop();
server->waitForEnd();
@@ -563,8 +566,9 @@ bool TestServer::TestHttpClient() {
ServerPtr server;
for (s_server_port = PORT_MIN; s_server_port <= PORT_MAX; s_server_port++) {
try {
server = ServerPtr(new TypedServer<LibEventServer, EchoHandler>
("127.0.0.1", s_server_port, 50, -1));
server = boost::make_shared<LibEventServer>(
"127.0.0.1", s_server_port, 50, -1);
server->setRequestHandlerFactory<EchoHandler>();
server->start();
break;
} catch (const FailedToListenException& e) {