Arquivos
hhvm/hphp/runtime/base/server/http_server.cpp
T
Adam Simpkins ee27bfa147 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.
2013-06-03 23:54:39 -07:00

528 linhas
16 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/base/server/http_server.h"
#include "hphp/runtime/base/server/libevent_server.h"
#include "hphp/runtime/base/server/libevent_server_with_fd.h"
#include "hphp/runtime/base/server/libevent_server_with_takeover.h"
#include "hphp/runtime/base/server/http_request_handler.h"
#include "hphp/runtime/base/server/admin_request_handler.h"
#include "hphp/runtime/base/server/server_stats.h"
#include "hphp/runtime/base/server/xbox_server.h"
#include "hphp/runtime/base/runtime_option.h"
#include "hphp/runtime/base/server/static_content_cache.h"
#include "hphp/runtime/base/class_info.h"
#include "hphp/runtime/base/memory/memory_manager.h"
#include "hphp/util/logger.h"
#include "hphp/runtime/base/externals.h"
#include "hphp/runtime/base/util/http_client.h"
#include "hphp/runtime/base/server/replay_transport.h"
#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>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// statics
HttpServerPtr HttpServer::Server;
time_t HttpServer::StartTime;
const int kNumProcessors = sysconf(_SC_NPROCESSORS_ONLN);
///////////////////////////////////////////////////////////////////////////////
HttpServer::HttpServer(void *sslCTX /* = NULL */)
: m_stopped(false), m_stopReason(nullptr), m_sslCTX(sslCTX),
m_watchDog(this, &HttpServer::watchDog) {
// enabling mutex profiling, but it's not turned on
LockProfiler::s_pfunc_profile = server_stats_log_mutex;
bool const useWarmupThrottle =
RuntimeOption::ServerWarmupThrottleRequestCount > 0 &&
RuntimeOption::ServerThreadCount > kNumProcessors;
auto maybeEnableThrottle = [&] (LibEventServer* s) {
if (!useWarmupThrottle) return;
s->enableWarmupThrottle(RuntimeOption::ServerThreadCount - kNumProcessors,
RuntimeOption::ServerWarmupThrottleRequestCount);
Logger::Info("Starting with %d threads for the first %d requests\n",
kNumProcessors,
RuntimeOption::ServerWarmupThrottleRequestCount);
};
int const startingThreadCount = !useWarmupThrottle
? RuntimeOption::ServerThreadCount
: kNumProcessors;
if (RuntimeOption::ServerPortFd != -1 || RuntimeOption::SSLPortFd != -1) {
auto const server = boost::make_shared<LibEventServerWithFd>(
RuntimeOption::ServerIP, RuntimeOption::ServerPort,
startingThreadCount,
RuntimeOption::RequestTimeoutSeconds);
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.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 = server;
}
m_pageServer->setRequestHandlerFactory<HttpRequestHandler>();
if (RuntimeOption::EnableSSL && m_sslCTX) {
assert(SSLInit::IsInited());
m_pageServer->enableSSL(m_sslCTX, RuntimeOption::SSLPort);
}
m_adminServer = boost::make_shared<LibEventServer>(
RuntimeOption::ServerIP, RuntimeOption::AdminServerPort,
RuntimeOption::AdminThreadCount,
RuntimeOption::RequestTimeoutSeconds);
m_adminServer->setRequestHandlerFactory<AdminRequestHandler>();
for (unsigned int i = 0; i < RuntimeOption::SatelliteServerInfos.size();
i++) {
SatelliteServerInfoPtr info = RuntimeOption::SatelliteServerInfos[i];
SatelliteServerPtr satellite = SatelliteServer::Create(info);
if (satellite) {
if (info->getType() == SatelliteServer::KindOfDanglingPageServer) {
m_danglings.push_back(satellite);
} else {
m_satellites.push_back(satellite);
}
}
}
if (RuntimeOption::XboxServerPort != 0) {
SatelliteServerInfoPtr xboxInfo(new XboxServerInfo());
SatelliteServerPtr satellite = SatelliteServer::Create(xboxInfo);
if (satellite) {
m_satellites.push_back(satellite);
}
}
if (RuntimeOption::EnableStaticContentCache) {
StaticContentCache::TheCache.load();
}
hphp_process_init();
Server::InstallStopSignalHandlers(m_pageServer);
Server::InstallStopSignalHandlers(m_adminServer);
if (!RuntimeOption::StartupDocument.empty()) {
Hdf hdf;
hdf["cmd"] = Transport::GET;
hdf["url"] = RuntimeOption::StartupDocument;
hdf["remote_host"] = RuntimeOption::ServerIP;
ReplayTransport rt;
rt.replayInput(hdf);
HttpRequestHandler handler;
handler.handleRequest(&rt);
int code = rt.getResponseCode();
if (code == 200) {
Logger::Info("StartupDocument %s returned 200 OK: %s",
RuntimeOption::StartupDocument.c_str(),
rt.getResponse().c_str());
} else {
Logger::Error("StartupDocument %s failed %d: %s",
RuntimeOption::StartupDocument.c_str(),
code, rt.getResponse().data());
return;
}
}
for (unsigned int i = 0; i < RuntimeOption::ThreadDocuments.size(); i++) {
ServiceThreadPtr thread
(new ServiceThread(RuntimeOption::ThreadDocuments[i]));
m_serviceThreads.push_back(thread);
}
for (unsigned int i = 0; i < RuntimeOption::ThreadLoopDocuments.size(); i++) {
ServiceThreadPtr thread
(new ServiceThread(RuntimeOption::ThreadLoopDocuments[i], true));
m_serviceThreads.push_back(thread);
}
}
void HttpServer::onServerShutdown() {
Eval::Debugger::Stop();
if (RuntimeOption::EnableDebuggerServer) {
Logger::Info("debugger server stopped");
}
XboxServer::Stop();
// When a new instance of HPHP has taken over our page server socket,
// stop our admin server and satellites so it can acquire those ports.
for (unsigned int i = 0; i < m_satellites.size(); i++) {
string name = m_satellites[i]->getName();
m_satellites[i]->stop();
Logger::Info("satellite server %s stopped", name.c_str());
}
if (RuntimeOption::AdminServerPort) {
m_adminServer->stop();
Logger::Info("admin server stopped");
}
// start dangling servers, so they can serve old version of pages
for (unsigned int i = 0; i < m_danglings.size(); i++) {
string name = m_danglings[i]->getName();
try {
m_danglings[i]->start();
Logger::Info("dangling server %s started", name.c_str());
} catch (Exception &e) {
Logger::Error("Unable to start danglings server %s: %s",
name.c_str(), e.getMessage().c_str());
// it's okay not able to start them
}
}
}
void HttpServer::takeoverShutdown(LibEventServerWithTakeover* server) {
assert(server == m_pageServer.get());
// We want to synchronously shut down our satellite servers to free up ports,
// then asynchronously shut down everything else.
onServerShutdown();
stop();
}
HttpServer::~HttpServer() {
stop();
}
void HttpServer::run() {
StartTime = time(0);
m_watchDog.start();
for (unsigned int i = 0; i < m_serviceThreads.size(); i++) {
m_serviceThreads[i]->start();
}
for (unsigned int i = 0; i < m_serviceThreads.size(); i++) {
m_serviceThreads[i]->waitForStarted();
}
if (RuntimeOption::ServerPort) {
if (!startServer(true)) {
Logger::Error("Unable to start page server");
return;
}
Logger::Info("page server started");
}
if (RuntimeOption::AdminServerPort) {
if (!startServer(false)) {
Logger::Error("Unable to start admin server");
abortServers();
return;
}
Logger::Info("admin server started");
}
for (unsigned int i = 0; i < m_satellites.size(); i++) {
string name = m_satellites[i]->getName();
try {
m_satellites[i]->start();
Logger::Info("satellite server %s started", name.c_str());
} catch (Exception &e) {
Logger::Error("Unable to start satellite server %s: %s",
name.c_str(), e.getMessage().c_str());
abortServers();
return;
}
}
if (!Eval::Debugger::StartServer()) {
Logger::Error("Unable to start debugger server");
abortServers();
return;
} else if (RuntimeOption::EnableDebuggerServer) {
Logger::Info("debugger server started");
}
{
Logger::Info("all servers started");
createPid();
Lock lock(this);
// continously running until /stop is received on admin server
while (!m_stopped) {
wait();
}
if (m_stopReason) {
Logger::Warning("Server stopping with reason: %s\n", m_stopReason);
}
removePid();
Logger::Info("page server stopped");
}
onServerShutdown(); // dangling server already started here
time_t t0 = time(0);
if (RuntimeOption::ServerPort) {
m_pageServer->stop();
}
time_t t1 = time(0);
if (!m_danglings.empty() && RuntimeOption::ServerDanglingWait > 0) {
int elapsed = t1 - t0;
if (RuntimeOption::ServerDanglingWait > elapsed) {
sleep(RuntimeOption::ServerDanglingWait - elapsed);
}
}
for (unsigned int i = 0; i < m_danglings.size(); i++) {
m_danglings[i]->stop();
Logger::Info("dangling server %s stopped",
m_danglings[i]->getName().c_str());
}
for (unsigned int i = 0; i < m_serviceThreads.size(); i++) {
m_serviceThreads[i]->notifyStopped();
}
for (unsigned int i = 0; i < m_serviceThreads.size(); i++) {
m_serviceThreads[i]->waitForEnd();
}
hphp_process_exit();
m_watchDog.waitForEnd();
Logger::Info("all servers stopped");
}
static void exit_on_timeout(int sig) {
signal(sig, SIG_DFL);
kill(getpid(), SIGKILL);
exit(0);
}
void HttpServer::stop(const char* stopReason) {
if (RuntimeOption::ServerGracefulShutdownWait) {
signal(SIGALRM, exit_on_timeout);
alarm(RuntimeOption::ServerGracefulShutdownWait);
}
Lock lock(this);
m_stopped = true;
m_stopReason = stopReason;
notify();
}
void HttpServer::abortServers() {
for (unsigned int i = 0; i < m_satellites.size(); i++) {
m_satellites[i]->stop();
}
if (RuntimeOption::AdminServerPort) {
m_adminServer->stop();
}
if (RuntimeOption::ServerPort) {
m_pageServer->stop();
}
}
void HttpServer::createPid() {
if (!RuntimeOption::PidFile.empty()) {
FILE * f = fopen(RuntimeOption::PidFile.c_str(), "w");
if (f) {
pid_t pid = Process::GetProcessId();
char buf[64];
snprintf(buf, sizeof(buf), "%" PRId64, (int64_t)pid);
fwrite(buf, strlen(buf), 1, f);
fclose(f);
} else {
Logger::Error("Unable to open pid file %s for write",
RuntimeOption::PidFile.c_str());
}
}
}
void HttpServer::removePid() {
if (!RuntimeOption::PidFile.empty()) {
unlink(RuntimeOption::PidFile.c_str());
}
}
void HttpServer::killPid() {
if (!RuntimeOption::PidFile.empty()) {
CstrBuffer sb(RuntimeOption::PidFile.c_str());
if (sb.size()) {
int64_t pid = sb.detach().toInt64();
if (pid) {
kill((pid_t)pid, SIGKILL);
return;
}
}
Logger::Error("Unable to read pid file %s for any meaningful pid",
RuntimeOption::PidFile.c_str());
}
}
///////////////////////////////////////////////////////////////////////////////
// watch dog thread
void HttpServer::watchDog() {
int count = 0;
bool noneed = false;
while (!m_stopped && !noneed) {
noneed = true;
if (RuntimeOption::DropCacheCycle > 0) {
noneed = false;
if ((count % RuntimeOption::DropCacheCycle) == 0) { // every hour
dropCache();
}
}
sleep(1);
++count;
if (RuntimeOption::MaxRSSPollingCycle > 0) {
noneed = false;
if ((count % RuntimeOption::MaxRSSPollingCycle) == 0) { // every minute
checkMemory();
}
}
}
}
void HttpServer::dropCache() {
FILE *f = fopen("/proc/sys/vm/drop_caches", "w");
if (f) {
// http://www.linuxinsight.com/proc_sys_vm_drop_caches.html
const char *FREE_ALL_CACHES = "3\n";
fwrite(FREE_ALL_CACHES, 2, 1, f);
fclose(f);
}
}
void HttpServer::checkMemory() {
if (RuntimeOption::MaxRSS > 0 &&
Process::GetProcessRSS(Process::GetProcessId()) * 1024 * 1024 >
RuntimeOption::MaxRSS) {
stop();
}
}
///////////////////////////////////////////////////////////////////////////////
// page server
bool HttpServer::startServer(bool pageServer) {
int port = pageServer ?
RuntimeOption::ServerPort : RuntimeOption::AdminServerPort;
// 1. try something nice
for (unsigned int i = 0; i < 60; i++) {
try {
if (pageServer) {
m_pageServer->start();
} else {
m_adminServer->start();
}
return true;
} catch (FailedToListenException &e) {
if (i == 0) {
Logger::Info("shutting down old HPHP server by /stop command");
}
if (errno == EACCES) {
Logger::Error("Permission denied listening on port %d", port);
return false;
}
HttpClient http;
string url = "http://";
url += RuntimeOption::ServerIP;
url += ":";
url += lexical_cast<string>(RuntimeOption::AdminServerPort);
url += "/stop";
StringBuffer response;
http.get(url.c_str(), response);
sleep(1);
}
}
// 2. try something harsh
if (RuntimeOption::ServerHarshShutdown) {
for (unsigned int i = 0; i < 5; i++) {
try {
if (pageServer) {
m_pageServer->start();
} else {
m_adminServer->start();
}
return true;
} catch (FailedToListenException &e) {
if (i == 0) {
Logger::Info("shutting down old HPHP server by pid file");
}
killPid();
sleep(1);
}
}
}
// 3. try something evil
if (RuntimeOption::ServerEvilShutdown) {
for (unsigned int i = 0; i < 60; i++) {
try {
if (pageServer) {
m_pageServer->start();
} else {
m_adminServer->start();
}
return true;
} catch (FailedToListenException &e) {
if (i == 0) {
Logger::Info("killing anything listening on port %d", port);
}
string cmd = "lsof -t -i :";
cmd += lexical_cast<string>(port);
cmd += " | xargs kill -9";
Util::ssystem(cmd.c_str());
sleep(1);
}
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
}