2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/server/http-server.h"
19 #include "hphp/runtime/base/backtrace.h"
20 #include "hphp/runtime/base/externals.h"
21 #include "hphp/runtime/base/file-util.h"
22 #include "hphp/runtime/base/http-client.h"
23 #include "hphp/runtime/base/memory-manager.h"
24 #include "hphp/runtime/base/program-functions.h"
25 #include "hphp/runtime/base/runtime-option.h"
26 #include "hphp/runtime/base/init-fini-node.h"
27 #include "hphp/runtime/debugger/debugger.h"
28 #include "hphp/runtime/ext/apc/ext_apc.h"
29 #include "hphp/runtime/server/admin-request-handler.h"
30 #include "hphp/runtime/server/http-request-handler.h"
31 #include "hphp/runtime/server/replay-transport.h"
32 #include "hphp/runtime/server/server-stats.h"
33 #include "hphp/runtime/server/static-content-cache.h"
34 #include "hphp/runtime/server/warmup-request-handler.h"
35 #include "hphp/runtime/server/xbox-server.h"
36 #include "hphp/runtime/vm/vm-regs.h"
38 #include "hphp/util/alloc.h"
39 #include "hphp/util/boot-stats.h"
40 #include "hphp/util/logger.h"
41 #include "hphp/util/process.h"
42 #include "hphp/util/ssl-init.h"
43 #include "hphp/util/stack-trace.h"
44 #include "hphp/util/struct-log.h"
46 #include <folly/Conv.h>
47 #include <folly/Format.h>
48 #include <folly/portability/Unistd.h>
50 #include <sys/types.h>
55 ///////////////////////////////////////////////////////////////////////////////
58 std::shared_ptr
<HttpServer
> HttpServer::Server
;
59 time_t HttpServer::StartTime
;
60 std::atomic
<double> HttpServer::LoadFactor
{1.0};
61 std::atomic_int
HttpServer::QueueDiscount
{0};
62 std::atomic_int_fast64_t
HttpServer::PrepareToStopTime
{0};
63 time_t HttpServer::OldServerStopTime
;
64 std::vector
<ShutdownStat
> HttpServer::ShutdownStats
;
65 folly::MicroSpinLock
HttpServer::StatsLock
;
67 const int kNumProcessors
= sysconf(_SC_NPROCESSORS_ONLN
);
69 static void on_kill(int sig
) {
71 // There is a small race condition here with HttpServer::reset in
72 // program-functions.cpp, but it can only happen if we get a signal while
73 // shutting down. The fix is to add a lock to HttpServer::Server but it seems
75 if (HttpServer::Server
) {
76 HttpServer::Server
->stopOnSignal(sig
);
82 ///////////////////////////////////////////////////////////////////////////////
84 HttpServer::HttpServer()
85 : m_stopped(false), m_killed(false), m_stopReason(nullptr),
86 m_watchDog(this, &HttpServer::watchDog
) {
87 LoadFactor
= RuntimeOption::EvalInitialLoadFactor
;
89 // enabling mutex profiling, but it's not turned on
90 LockProfiler::s_pfunc_profile
= server_stats_log_mutex
;
92 int startingThreadCount
= RuntimeOption::ServerThreadCount
;
93 uint32_t additionalThreads
= 0;
94 if (RuntimeOption::ServerWarmupThrottleRequestCount
> 0 &&
95 RuntimeOption::ServerThreadCount
> kNumProcessors
) {
96 startingThreadCount
= kNumProcessors
;
97 additionalThreads
= RuntimeOption::ServerThreadCount
- startingThreadCount
;
100 auto serverFactory
= ServerFactoryRegistry::getInstance()->getFactory
101 (RuntimeOption::ServerType
);
102 const std::string address
= RuntimeOption::ServerFileSocket
.empty()
103 ? RuntimeOption::ServerIP
: RuntimeOption::ServerFileSocket
;
104 ServerOptions
options(address
, RuntimeOption::ServerPort
,
105 RuntimeOption::ServerThreadCount
, startingThreadCount
);
106 options
.m_useFileSocket
= !RuntimeOption::ServerFileSocket
.empty();
107 options
.m_serverFD
= RuntimeOption::ServerPortFd
;
108 options
.m_sslFD
= RuntimeOption::SSLPortFd
;
109 options
.m_takeoverFilename
= RuntimeOption::TakeoverFilename
;
110 options
.m_hugeThreads
= RuntimeOption::ServerHugeThreadCount
;
111 m_pageServer
= serverFactory
->createServer(options
);
112 m_pageServer
->addTakeoverListener(this);
113 m_pageServer
->addServerEventListener(this);
115 if (additionalThreads
) {
116 auto handlerFactory
= std::make_shared
<WarmupRequestHandlerFactory
>(
117 m_pageServer
.get(), additionalThreads
,
118 RuntimeOption::ServerWarmupThrottleRequestCount
,
119 RuntimeOption::RequestTimeoutSeconds
);
120 m_pageServer
->setRequestHandlerFactory([handlerFactory
] {
121 return handlerFactory
->createHandler();
124 m_pageServer
->setRequestHandlerFactory
<HttpRequestHandler
>(
125 RuntimeOption::RequestTimeoutSeconds
);
128 if (RuntimeOption::EnableSSL
) {
129 assertx(SSLInit::IsInited());
130 m_pageServer
->enableSSL(RuntimeOption::SSLPort
);
133 if (RuntimeOption::EnableSSLWithPlainText
) {
134 assertx(SSLInit::IsInited());
135 m_pageServer
->enableSSLWithPlainText();
139 ServerOptions
admin_options(RuntimeOption::AdminServerIP
,
140 RuntimeOption::AdminServerPort
,
141 RuntimeOption::AdminThreadCount
);
142 admin_options
.m_queueToWorkerRatio
=
143 RuntimeOption::AdminServerQueueToWorkerRatio
;
144 m_adminServer
= serverFactory
->createServer(admin_options
);
145 m_adminServer
->setRequestHandlerFactory
<AdminRequestHandler
>(
146 RuntimeOption::RequestTimeoutSeconds
);
148 for (unsigned int i
= 0; i
< RuntimeOption::SatelliteServerInfos
.size();
150 auto info
= RuntimeOption::SatelliteServerInfos
[i
];
151 auto satellite(SatelliteServer::Create(info
));
153 m_satellites
.push_back(std::move(satellite
));
157 if (RuntimeOption::XboxServerPort
!= 0) {
158 auto xboxInfo
= std::make_shared
<XboxServerInfo
>();
159 auto satellite
= SatelliteServer::Create(xboxInfo
);
161 m_satellites
.push_back(std::move(satellite
));
165 StaticContentCache::TheCache
.load();
167 m_counterCallback
.init(
168 [this](std::map
<std::string
, int64_t>& counters
) {
169 counters
["ev_connections"] = m_pageServer
->getLibEventConnectionCount();
170 counters
["inflight_requests"] = m_pageServer
->getActiveWorker();
171 counters
["queued_requests"] = m_pageServer
->getQueuedJobs();
173 auto const sat_requests
= getSatelliteRequestCount();
174 counters
["satellite_inflight_requests"] = sat_requests
.first
;
175 counters
["satellite_queued_requests"] = sat_requests
.second
;
179 signal(SIGTERM
, on_kill
);
180 signal(SIGUSR1
, on_kill
);
181 signal(SIGHUP
, on_kill
);
184 // Synchronously stop satellites
185 void HttpServer::onServerShutdown() {
186 InitFiniNode::ServerFini();
188 Eval::Debugger::Stop();
189 if (RuntimeOption::EnableDebuggerServer
) {
190 Logger::Info("debugger server stopped");
193 // When a new instance of HPHP has taken over our page server socket,
194 // stop our admin server and satellites so it can acquire those
196 if (RuntimeOption::AdminServerPort
) {
197 m_adminServer
->stop();
199 for (unsigned int i
= 0; i
< m_satellites
.size(); i
++) {
200 std::string name
= m_satellites
[i
]->getName();
201 m_satellites
[i
]->stop();
202 Logger::Info("satellite server %s stopped", name
.c_str());
205 if (RuntimeOption::AdminServerPort
) {
206 m_adminServer
->waitForEnd();
207 Logger::Info("admin server stopped");
211 void HttpServer::takeoverShutdown() {
212 // We want to synchronously shut down our satellite servers to free up ports,
213 // then asynchronously shut down everything else.
218 void HttpServer::serverStopped(HPHP::Server
* server
) {
219 Logger::Info("Page server stopped");
220 assertx(server
== m_pageServer
.get());
223 auto sockFile
= RuntimeOption::ServerFileSocket
;
224 if (!sockFile
.empty()) {
225 unlink(sockFile
.c_str());
231 void HttpServer::playShutdownRequest(const std::string
& fileName
) {
232 if (fileName
.empty()) return;
233 Logger::Info("playing request upon shutdown %s", fileName
.c_str());
236 rt
.replayInput(fileName
.c_str());
237 HttpRequestHandler
handler(0);
239 if (rt
.getResponseCode() == 200) {
240 Logger::Info("successfully finished request: %s", rt
.getUrl());
242 Logger::Error("request unsuccessful: %s", rt
.getUrl());
245 Logger::Error("got exception when playing request: %s",
250 HttpServer::~HttpServer() {
251 m_counterCallback
.deinit();
253 // XXX: why should we have to call stop here? If we haven't already
254 // stopped (and joined all the threads), watchDog could still be
255 // running and leaving this destructor without a wait would be
260 static StaticString s_file
{"file"}, s_line
{"line"};
262 void HttpServer::runOrExitProcess() {
263 if (StaticContentCache::TheFileCache
&&
264 StructuredLog::enabled() &&
265 StructuredLog::coinflip(RuntimeOption::EvalStaticContentsLogRate
)) {
266 CacheManager::setLogger([](bool existsCheck
, const std::string
& name
) {
267 auto record
= StructuredLogEntry
{};
268 record
.setInt("existsCheck", existsCheck
);
269 record
.setStr("file", name
);
270 bool needsCppStack
= true;
271 if (!g_context
.isNull()) {
275 createBacktrace(BacktraceArgs().withArgValues(false));
276 std::vector
<std::string
> frameStrings
;
277 std::vector
<folly::StringPiece
> frames
;
278 for (int i
= 0; i
< bt
.size(); i
++) {
279 auto f
= tvCastToArrayLike(bt
.rvalAt(i
).tv());
280 if (f
.exists(s_file
)) {
281 auto s
= tvCastToString(f
.rvalAt(s_file
).tv()).toCppString();
282 if (f
.exists(s_line
)) {
285 tvCastToInt64(f
.rvalAt(s_line
).tv())
288 frameStrings
.emplace_back(std::move(s
));
289 frames
.push_back(frameStrings
.back());
292 record
.setVec("stack", frames
);
293 needsCppStack
= false;
297 record
.setStackTrace("stack", StackTrace
{StackTrace::Force
{}});
299 StructuredLog::log("hhvm_file_cache", record
);
302 auto startupFailure
= [] (const std::string
& msg
) {
304 Logger::Error("Shutting down due to failure(s) to bind in "
305 "HttpServer::runAndExitProcess");
306 // Logger flushes itself---we don't need to run any atexit handlers
307 // (historically we've mostly just SEGV'd while trying) ...
311 if (!RuntimeOption::InstanceId
.empty()) {
312 std::string msg
= "Starting instance " + RuntimeOption::InstanceId
;
313 if (!RuntimeOption::DeploymentId
.empty()) {
314 msg
+= " from deployment " + RuntimeOption::DeploymentId
;
321 if (RuntimeOption::ServerPort
) {
322 if (!startServer(true)) {
323 startupFailure("Unable to start page server");
326 Logger::Info("page server started");
329 StartTime
= time(nullptr);
331 if (RuntimeOption::AdminServerPort
) {
332 if (!startServer(false)) {
333 startupFailure("Unable to start admin server");
336 Logger::Info("admin server started");
339 for (unsigned int i
= 0; i
< m_satellites
.size(); i
++) {
340 std::string name
= m_satellites
[i
]->getName();
342 m_satellites
[i
]->start();
343 Logger::Info("satellite server %s started", name
.c_str());
344 } catch (Exception
&e
) {
346 folly::format("Unable to start satellite server {}: {}",
347 name
, e
.getMessage()).str()
353 if (!Eval::Debugger::StartServer()) {
354 startupFailure("Unable to start debugger server");
356 } else if (RuntimeOption::EnableDebuggerServer
) {
357 Logger::Info("debugger server started");
361 InitFiniNode::ServerInit();
362 } catch (std::exception
&e
) {
364 folly::sformat("Exception in InitFiniNode::ServerInit(): {}",
369 BootStats::mark("servers started");
370 Logger::Info("all servers started");
374 // continously running until /stop is received on admin server, or
375 // takeover is requested.
380 Logger::Warning("Server stopping with reason: %s\n", m_stopReason
);
382 // if we were killed, bail out immediately
384 Logger::Info("page server killed");
389 if (RuntimeOption::ServerPort
) {
390 Logger::Info("stopping page server");
391 m_pageServer
->stop();
398 m_watchDog
.waitForEnd();
399 playShutdownRequest(RuntimeOption::ServerCleanupRequest
);
402 void HttpServer::waitForServers() {
403 if (RuntimeOption::ServerPort
) {
404 m_pageServer
->waitForEnd();
406 if (RuntimeOption::AdminServerPort
) {
407 m_adminServer
->waitForEnd();
409 // all other servers invoke waitForEnd on stop
412 static void exit_on_timeout(int sig
) {
413 signal(sig
, SIG_DFL
);
415 TerminateProcess(GetCurrentProcess(), (UINT
)-1);
417 kill(getpid(), SIGKILL
);
419 // we really shouldn't get here, but who knows.
420 // abort so we catch it as a crash.
424 // Tell OOM killer to kill this process if it has to. This is used during
425 // server shutdown. If we are dying anyway, let's try to protect others.
426 static void oom_sacrifice() {
428 // Use open() instead of fopen() here to avoid additional buffering and
429 // allocation, which could go wrong if memory is really limited.
430 int fd
= open("/proc/self/oom_score_adj", O_WRONLY
, 0);
438 void HttpServer::stop(const char* stopReason
) {
439 if (m_stopped
) return;
440 // we're shutting down flush http logs
442 HttpRequestHandler::GetAccessLog().flushAllWriters();
443 MarkShutdownStat(ShutdownEvent::SHUTDOWN_INITIATED
);
446 if (RuntimeOption::ServerKillOnTimeout
) {
448 RuntimeOption::ServerPreShutdownWait
+
449 RuntimeOption::ServerShutdownListenWait
+
450 RuntimeOption::ServerGracefulShutdownWait
;
453 // Use a killer thread to _Exit() after totalWait seconds. If
454 // the main thread returns before that, the killer thread will
455 // exit. So don't do join() on this thread. Since we create a
456 // thread here, `HttpServer::stop()` cannot be called in signal
457 // handlers, use `stopOnSignal()` (which uses SIGALRM) in that
459 auto killer
= std::thread([totalWait
] {
462 param
.sched_priority
= 5;
463 pthread_setschedparam(pthread_self(), SCHED_RR
, ¶m
);
464 // It is OK if we fail to increase thread priority.
467 std::this_thread::sleep_for(std::chrono::seconds
{totalWait
});
476 m_stopReason
= stopReason
;
480 void HttpServer::stopOnSignal(int sig
) {
481 if (m_stopped
) return;
482 // we're shutting down flush http logs
484 HttpRequestHandler::GetAccessLog().flushAllWriters();
485 MarkShutdownStat(ShutdownEvent::SHUTDOWN_INITIATED
);
488 // Signal to the main server thread to exit immediately if
489 // we want to die on SIGTERM
490 if (RuntimeOption::ServerKillOnSIGTERM
&& sig
== SIGTERM
) {
495 m_stopReason
= "SIGTERM received";
502 if (RuntimeOption::ServerGracefulShutdownWait
) {
503 signal(SIGALRM
, exit_on_timeout
);
504 alarm(RuntimeOption::ServerGracefulShutdownWait
);
507 // NOTE: Server->stop does a graceful stop by design.
509 m_pageServer
->stop();
512 m_adminServer
->stop();
518 void HttpServer::EvictFileCache() {
519 // In theory, the kernel should do just as well even if we don't
520 // explicitly advise files out. But we can do it anyway when we
521 // need more free memory, e.g., when a new instance of the server is
523 advise_out(RuntimeOption::RepoLocalPath
);
524 advise_out(RuntimeOption::FileCache
);
528 void HttpServer::PrepareToStop() {
529 MarkShutdownStat(ShutdownEvent::SHUTDOWN_PREPARE
);
530 PrepareToStopTime
.store(time(nullptr), std::memory_order_release
);
534 void HttpServer::createPid() {
535 if (!RuntimeOption::PidFile
.empty()) {
536 FILE * f
= fopen(RuntimeOption::PidFile
.c_str(), "w");
538 auto const pid
= getpid();
540 snprintf(buf
, sizeof(buf
), "%" PRId64
, (int64_t)pid
);
541 fwrite(buf
, strlen(buf
), 1, f
);
544 Logger::Error("Unable to open pid file %s for write",
545 RuntimeOption::PidFile
.c_str());
550 void HttpServer::removePid() {
551 if (!RuntimeOption::PidFile
.empty()) {
552 unlink(RuntimeOption::PidFile
.c_str());
556 void HttpServer::killPid() {
557 if (!RuntimeOption::PidFile
.empty()) {
558 CstrBuffer
sb(RuntimeOption::PidFile
.c_str());
560 int64_t pid
= sb
.detach().toInt64();
562 kill((pid_t
)pid
, SIGKILL
);
566 Logger::Error("Unable to read pid file %s for any meaningful pid",
567 RuntimeOption::PidFile
.c_str());
571 ///////////////////////////////////////////////////////////////////////////////
574 void HttpServer::watchDog() {
577 while (!m_stopped
&& !noneed
) {
580 if (RuntimeOption::DropCacheCycle
> 0) {
582 if ((count
% RuntimeOption::DropCacheCycle
) == 0) { // every hour
590 if (RuntimeOption::MaxRSSPollingCycle
> 0) {
592 if ((count
% RuntimeOption::MaxRSSPollingCycle
) == 0) { // every minute
599 static bool sendAdminCommand(const char* cmd
) {
600 if (RuntimeOption::AdminServerPort
<= 0) return false;
601 std::string host
= RuntimeOption::AdminServerIP
;
602 if (host
.empty()) host
= "localhost";
603 auto passwords
= RuntimeOption::AdminPasswords
;
604 if (passwords
.empty() && !RuntimeOption::AdminPassword
.empty()) {
605 passwords
.insert(RuntimeOption::AdminPassword
);
607 auto passwordIter
= passwords
.begin();
610 if (passwordIter
!= passwords
.end()) {
611 url
= folly::sformat("http://{}:{}/{}?auth={}", host
,
612 RuntimeOption::AdminServerPort
,
616 url
= folly::sformat("http://{}:{}/{}", host
,
617 RuntimeOption::AdminServerPort
, cmd
);
619 if (CURL
* curl
= curl_easy_init()) {
620 curl_easy_setopt(curl
, CURLOPT_URL
, url
.c_str());
621 curl_easy_setopt(curl
, CURLOPT_NOBODY
, 1);
622 curl_easy_setopt(curl
, CURLOPT_FAILONERROR
, 1);
623 curl_easy_setopt(curl
, CURLOPT_TIMEOUT
, 3);
624 auto code
= curl_easy_perform(curl
);
625 curl_easy_cleanup(curl
);
626 if (code
== CURLE_OK
) {
627 Logger::Info("sent %s via admin port", cmd
);
631 } while (passwordIter
!= passwords
.end());
635 bool HttpServer::ReduceOldServerLoad() {
636 if (!RuntimeOption::StopOldServer
) return false;
637 if (OldServerStopTime
> 0) return true;
638 Logger::Info("trying to reduce load of the old HPHP server");
639 return sendAdminCommand("prepare-to-stop");
642 bool HttpServer::StopOldServer() {
643 if (OldServerStopTime
> 0) return true;
644 SCOPE_EXIT
{ OldServerStopTime
= time(nullptr); };
645 Logger::Info("trying to shut down old HPHP server by /stop command");
646 return sendAdminCommand("stop");
649 // Return the estimated amount of memory that can be safely taken into
650 // the current process RSS.
651 static inline int64_t availableMemory(const MemInfo
& mem
, int64_t rss
,
653 // Estimation of page cache that are readily evictable without
654 // causing big memory pressure. We consider it safe to take
655 // `pageFreeFactor` percent of cached pages (excluding those used by
656 // the current process, estimated to be equal to the current RSS)
657 auto const otherCacheMb
= std::max((int64_t)0, mem
.cachedMb
- rss
);
658 auto const availableMb
= mem
.freeMb
+ otherCacheMb
* factor
/ 100;
662 bool HttpServer::CanContinue(const MemInfo
& mem
, int64_t rssMb
,
663 int64_t rssNeeded
, int cacheFreeFactor
) {
664 if (!mem
.valid()) return false;
665 if (mem
.freeMb
< RuntimeOption::ServerCriticalFreeMb
) return false;
666 auto const availableMb
= availableMemory(mem
, rssMb
, cacheFreeFactor
);
667 auto const result
= (rssMb
+ availableMb
>= rssNeeded
);
668 if (result
) assertx(CanStep(mem
, rssMb
, rssNeeded
, cacheFreeFactor
));
672 bool HttpServer::CanStep(const MemInfo
& mem
, int64_t rssMb
,
673 int64_t rssNeeded
, int cacheFreeFactor
) {
674 if (!mem
.valid()) return false;
675 if (mem
.freeMb
< RuntimeOption::ServerCriticalFreeMb
) return false;
676 auto const availableMb
= availableMemory(mem
, rssMb
, cacheFreeFactor
);
677 // Estimation of the memory needed till the next check point. Since
678 // the current check point is not the last one, we try to be more
679 // optimistic, by assuming that memory requirement won't grow
680 // drastically between successive check points, and that it won't
681 // grow over our estimate.
682 auto const neededToStep
= std::min(rssNeeded
/ 4, rssNeeded
- rssMb
);
683 return (availableMb
>= neededToStep
);
686 void HttpServer::CheckMemAndWait(bool final
) {
687 if (!RuntimeOption::StopOldServer
) return;
688 if (RuntimeOption::OldServerWait
<= 0) return;
690 auto const rssNeeded
= RuntimeOption::ServerRSSNeededMb
;
691 auto const factor
= RuntimeOption::CacheFreeFactor
;
693 // Don't wait too long
694 if (OldServerStopTime
> 0 &&
695 time(nullptr) - OldServerStopTime
>= RuntimeOption::OldServerWait
) {
699 auto const rssMb
= Process::GetProcessRSS();
701 if (!Process::GetMemoryInfo(memInfo
)) {
702 Logger::Error("Failed to obtain memory information");
703 HttpServer::StopOldServer();
706 Logger::FInfo("Memory pressure check: free/cached/buffers {}/{}/{} "
707 "currentRss {}Mb, required {}Mb.",
708 memInfo
.freeMb
, memInfo
.cachedMb
, memInfo
.buffersMb
,
712 if (CanStep(memInfo
, rssMb
, rssNeeded
, factor
)) return;
714 if (CanContinue(memInfo
, rssMb
, rssNeeded
, factor
)) return;
717 // OK, we don't have enough memory, let's do something.
718 HttpServer::StopOldServer(); // nop if already called before
719 /* sleep override */ sleep(1);
720 } while (true); // Guaranteed to return in the loop, at least upon timeout.
724 void HttpServer::MarkShutdownStat(ShutdownEvent event
) {
725 if (!RuntimeOption::EvalLogServerRestartStats
) return;
726 std::lock_guard
<folly::MicroSpinLock
> lock(StatsLock
);
728 Process::GetMemoryInfo(mem
);
729 auto const rss
= Process::GetProcessRSS();
730 auto const requests
= requestCount();
731 if (event
== ShutdownEvent::SHUTDOWN_PREPARE
) {
732 ShutdownStats
.clear();
733 ShutdownStats
.reserve(ShutdownEvent::kNumEvents
);
736 if (!ShutdownStats
.empty()) {
737 assertx(ShutdownStats
.back().event
<= event
);
740 ShutdownStats
.push_back({event
, time(nullptr), mem
, rss
, requests
});
743 void HttpServer::LogShutdownStats() {
744 if (!RuntimeOption::EvalLogServerRestartStats
) return;
745 StructuredLogEntry entry
;
746 std::lock_guard
<folly::MicroSpinLock
> lock(StatsLock
);
747 if (ShutdownStats
.empty()) return;
748 for (size_t i
= 0; i
< ShutdownStats
.size(); ++i
) {
749 const auto& stat
= ShutdownStats
[i
];
750 auto const eventName
= stat
.eventName();
751 entry
.setInt(folly::sformat("{}.rss", eventName
), stat
.rss
);
752 entry
.setInt(folly::sformat("{}.free", eventName
),
753 stat
.memUsage
.freeMb
);
754 entry
.setInt(folly::sformat("{}.cached", eventName
),
755 stat
.memUsage
.cachedMb
);
756 entry
.setInt(folly::sformat("{}.buffers", eventName
),
757 stat
.memUsage
.buffersMb
);
758 // Log the difference since last event, if available
760 const auto& last
= ShutdownStats
[i
- 1];
761 auto const lastEvent
= last
.eventName();
762 entry
.setInt(folly::sformat("{}.duration", lastEvent
),
763 stat
.time
- last
.time
);
764 entry
.setInt(folly::sformat("{}.requests", lastEvent
),
765 stat
.requestsServed
- last
.requestsServed
);
766 entry
.setInt(folly::sformat("{}.rss.delta", lastEvent
),
767 stat
.rss
- last
.rss
);
770 StructuredLog::log("webserver_shutdown_timing", entry
);
771 ShutdownStats
.clear();
774 void HttpServer::dropCache() {
775 FILE *f
= fopen("/proc/sys/vm/drop_caches", "w");
777 // http://www.linuxinsight.com/proc_sys_vm_drop_caches.html
778 const char *FREE_ALL_CACHES
= "3\n";
779 fwrite(FREE_ALL_CACHES
, 2, 1, f
);
784 void HttpServer::checkMemory() {
785 int64_t used
= Process::GetProcessRSS() * 1024 * 1024;
786 if (RuntimeOption::MaxRSS
> 0 && used
> RuntimeOption::MaxRSS
) {
788 "ResourceLimit.MaxRSS %" PRId64
" reached %" PRId64
" used, exiting",
789 RuntimeOption::MaxRSS
, used
);
794 void HttpServer::getSatelliteStats(
795 std::vector
<std::pair
<std::string
, int>> *stats
) {
796 for (const auto& i
: m_satellites
) {
797 std::pair
<std::string
, int> active("satellite." + i
->getName() + ".load",
798 i
->getActiveWorker());
799 std::pair
<std::string
, int> queued("satellite." + i
->getName() + ".queued",
801 stats
->push_back(active
);
802 stats
->push_back(queued
);
806 std::pair
<int, int> HttpServer::getSatelliteRequestCount() const {
809 for (const auto& i
: m_satellites
) {
810 inflight
+= i
->getActiveWorker();
811 queued
+= i
->getQueuedJobs();
813 return std::make_pair(inflight
, queued
);
816 ///////////////////////////////////////////////////////////////////////////////
819 bool HttpServer::startServer(bool pageServer
) {
820 int port
= pageServer
?
821 RuntimeOption::ServerPort
: RuntimeOption::AdminServerPort
;
823 // 1. try something nice
824 for (unsigned int i
= 0; i
< 60; i
++) {
827 m_pageServer
->start();
829 m_adminServer
->start();
832 } catch (FailedToListenException
&e
) {
833 if (RuntimeOption::ServerExitOnBindFail
) return false;
837 if (errno
== EACCES
) {
838 Logger::Error("%s: Permission denied.", e
.what());
842 if (pageServer
&& !RuntimeOption::ServerFileSocket
.empty()) {
844 Logger::Info("Unlinking unused socket at %s",
845 RuntimeOption::ServerFileSocket
.c_str());
847 struct stat stat_buf
;
848 if (stat(RuntimeOption::ServerFileSocket
.c_str(), &stat_buf
) == 0
849 && S_ISSOCK(stat_buf
.st_mode
)) {
850 std::string cmd
= "bash -c '! fuser ";
851 cmd
+= RuntimeOption::ServerFileSocket
;
853 if (FileUtil::ssystem(cmd
.c_str()) == 0) {
854 unlink(RuntimeOption::ServerFileSocket
.c_str());
862 // 2. try something harsh
863 if (RuntimeOption::ServerHarshShutdown
) {
864 for (unsigned int i
= 0; i
< 5; i
++) {
867 m_pageServer
->start();
869 m_adminServer
->start();
872 } catch (FailedToListenException
&e
) {
874 Logger::Info("shutting down old HPHP server by pid file");
882 // 3. try something evil
883 if (RuntimeOption::ServerEvilShutdown
) {
884 for (unsigned int i
= 0; i
< 60; i
++) {
887 m_pageServer
->start();
889 m_adminServer
->start();
892 } catch (FailedToListenException
&e
) {
893 if (pageServer
&& !RuntimeOption::ServerFileSocket
.empty()) {
895 Logger::Info("unlinking socket at %s",
896 RuntimeOption::ServerFileSocket
.c_str());
899 struct stat stat_buf
;
900 if (stat(RuntimeOption::ServerFileSocket
.c_str(), &stat_buf
) == 0
901 && S_ISSOCK(stat_buf
.st_mode
)) {
902 unlink(RuntimeOption::ServerFileSocket
.c_str());
906 Logger::Info("killing anything listening on port %d", port
);
909 std::string cmd
= "lsof -t -i :";
910 cmd
+= folly::to
<std::string
>(port
);
911 cmd
+= " | xargs kill -9";
912 FileUtil::ssystem(cmd
.c_str());
922 ///////////////////////////////////////////////////////////////////////////////