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/light-process.h"
41 #include "hphp/util/logger.h"
42 #include "hphp/util/process.h"
43 #include "hphp/util/ssl-init.h"
44 #include "hphp/util/stack-trace.h"
45 #include "hphp/util/struct-log.h"
47 #include <folly/Conv.h>
48 #include <folly/Format.h>
49 #include <folly/portability/Unistd.h>
54 void DisableFork() __attribute__((__weak__
));
55 void EnableForkLogging() __attribute__((__weak__
));
60 ///////////////////////////////////////////////////////////////////////////////
63 std::shared_ptr
<HttpServer
> HttpServer::Server
;
64 time_t HttpServer::StartTime
;
65 std::atomic
<double> HttpServer::LoadFactor
{1.0};
66 std::atomic_int
HttpServer::QueueDiscount
{0};
67 std::atomic_int_fast64_t
HttpServer::PrepareToStopTime
{0};
68 time_t HttpServer::OldServerStopTime
;
69 std::vector
<ShutdownStat
> HttpServer::ShutdownStats
;
70 folly::MicroSpinLock
HttpServer::StatsLock
;
72 const int kNumProcessors
= sysconf(_SC_NPROCESSORS_ONLN
);
74 static void on_kill(int sig
) {
76 // There is a small race condition here with HttpServer::reset in
77 // program-functions.cpp, but it can only happen if we get a signal while
78 // shutting down. The fix is to add a lock to HttpServer::Server but it seems
80 if (HttpServer::Server
) {
81 HttpServer::Server
->stopOnSignal(sig
);
87 ///////////////////////////////////////////////////////////////////////////////
89 HttpServer::HttpServer()
90 : m_stopped(false), m_killed(false), m_stopReason(nullptr),
91 m_watchDog(this, &HttpServer::watchDog
) {
92 LoadFactor
= RuntimeOption::EvalInitialLoadFactor
;
94 // enabling mutex profiling, but it's not turned on
95 LockProfiler::s_pfunc_profile
= server_stats_log_mutex
;
97 int startingThreadCount
= RuntimeOption::ServerThreadCount
;
98 if (RuntimeOption::ServerWarmupThrottleRequestCount
> 0 &&
99 RuntimeOption::ServerThreadCount
> kNumProcessors
) {
100 startingThreadCount
= kNumProcessors
;
103 auto serverFactory
= ServerFactoryRegistry::getInstance()->getFactory
104 (RuntimeOption::ServerType
);
105 const std::string address
= RuntimeOption::ServerFileSocket
.empty()
106 ? RuntimeOption::ServerIP
: RuntimeOption::ServerFileSocket
;
107 ServerOptions
options(address
, RuntimeOption::ServerPort
,
108 RuntimeOption::ServerThreadCount
, startingThreadCount
,
109 RuntimeOption::ServerQueueCount
);
110 options
.m_useFileSocket
= !RuntimeOption::ServerFileSocket
.empty();
111 options
.m_serverFD
= RuntimeOption::ServerPortFd
;
112 options
.m_sslFD
= RuntimeOption::SSLPortFd
;
113 options
.m_takeoverFilename
= RuntimeOption::TakeoverFilename
;
114 options
.m_hugeThreads
= RuntimeOption::ServerHugeThreadCount
;
115 m_pageServer
= serverFactory
->createServer(options
);
116 m_pageServer
->addTakeoverListener(this);
117 m_pageServer
->addServerEventListener(this);
119 if (startingThreadCount
!= RuntimeOption::ServerThreadCount
) {
120 auto handlerFactory
= std::make_shared
<WarmupRequestHandlerFactory
>(
122 RuntimeOption::ServerWarmupThrottleRequestCount
,
123 RuntimeOption::RequestTimeoutSeconds
);
124 m_pageServer
->setRequestHandlerFactory([handlerFactory
] {
125 return handlerFactory
->createHandler();
128 m_pageServer
->setRequestHandlerFactory
<HttpRequestHandler
>(
129 RuntimeOption::RequestTimeoutSeconds
);
132 if (RuntimeOption::EnableSSL
) {
133 assertx(SSLInit::IsInited());
134 m_pageServer
->enableSSL(RuntimeOption::SSLPort
);
137 if (RuntimeOption::EnableSSLWithPlainText
) {
138 assertx(SSLInit::IsInited());
139 m_pageServer
->enableSSLWithPlainText();
143 ServerOptions
admin_options(RuntimeOption::AdminServerIP
,
144 RuntimeOption::AdminServerPort
,
145 RuntimeOption::AdminThreadCount
);
146 m_adminServer
= serverFactory
->createServer(admin_options
);
147 m_adminServer
->setRequestHandlerFactory
<AdminRequestHandler
>(
148 RuntimeOption::RequestTimeoutSeconds
);
150 for (unsigned int i
= 0; i
< RuntimeOption::SatelliteServerInfos
.size();
152 auto info
= RuntimeOption::SatelliteServerInfos
[i
];
153 auto satellite(SatelliteServer::Create(info
));
155 m_satellites
.push_back(std::move(satellite
));
159 if (RuntimeOption::XboxServerPort
!= 0) {
160 auto xboxInfo
= std::make_shared
<XboxServerInfo
>();
161 auto satellite
= SatelliteServer::Create(xboxInfo
);
163 m_satellites
.push_back(std::move(satellite
));
167 StaticContentCache::TheCache
.load();
169 m_counterCallback
.init(
170 [this](std::map
<std::string
, int64_t>& counters
) {
171 counters
["ev_connections"] = m_pageServer
->getLibEventConnectionCount();
172 counters
["inflight_requests"] = m_pageServer
->getActiveWorker();
173 counters
["queued_requests"] = m_pageServer
->getQueuedJobs();
175 auto const sat_requests
= getSatelliteRequestCount();
176 counters
["satellite_inflight_requests"] = sat_requests
.first
;
177 counters
["satellite_queued_requests"] = sat_requests
.second
;
181 signal(SIGTERM
, on_kill
);
182 signal(SIGUSR1
, on_kill
);
183 signal(SIGHUP
, on_kill
);
186 // Synchronously stop satellites
187 void HttpServer::onServerShutdown() {
188 InitFiniNode::ServerFini();
190 Eval::Debugger::Stop();
191 if (RuntimeOption::EnableDebuggerServer
) {
192 Logger::Info("debugger server stopped");
195 // When a new instance of HPHP has taken over our page server socket,
196 // stop our admin server and satellites so it can acquire those
198 if (RuntimeOption::AdminServerPort
) {
199 m_adminServer
->stop();
201 for (unsigned int i
= 0; i
< m_satellites
.size(); i
++) {
202 std::string name
= m_satellites
[i
]->getName();
203 m_satellites
[i
]->stop();
204 Logger::Info("satellite server %s stopped", name
.c_str());
207 if (RuntimeOption::AdminServerPort
) {
208 m_adminServer
->waitForEnd();
209 Logger::Info("admin server stopped");
213 void HttpServer::takeoverShutdown() {
214 // We want to synchronously shut down our satellite servers to free up ports,
215 // then asynchronously shut down everything else.
220 void HttpServer::serverStopped(HPHP::Server
* server
) {
221 Logger::Info("Page server stopped");
222 assertx(server
== m_pageServer
.get());
225 auto sockFile
= RuntimeOption::ServerFileSocket
;
226 if (!sockFile
.empty()) {
227 unlink(sockFile
.c_str());
233 void HttpServer::playShutdownRequest(const std::string
& fileName
) {
234 if (fileName
.empty()) return;
235 Logger::Info("playing request upon shutdown %s", fileName
.c_str());
238 rt
.replayInput(fileName
.c_str());
239 HttpRequestHandler
handler(0);
241 if (rt
.getResponseCode() == 200) {
242 Logger::Info("successfully finished request: %s", rt
.getUrl());
244 Logger::Error("request unsuccessful: %s", rt
.getUrl());
247 Logger::Error("got exception when playing request: %s",
252 HttpServer::~HttpServer() {
253 m_counterCallback
.deinit();
255 // XXX: why should we have to call stop here? If we haven't already
256 // stopped (and joined all the threads), watchDog could still be
257 // running and leaving this destructor without a wait would be
262 static StaticString s_file
{"file"}, s_line
{"line"};
264 void HttpServer::runOrExitProcess() {
265 if (StaticContentCache::TheFileCache
&&
266 StructuredLog::enabled() &&
267 StructuredLog::coinflip(RuntimeOption::EvalStaticContentsLogRate
)) {
268 CacheManager::setLogger([](bool existsCheck
, const std::string
& name
) {
269 auto record
= StructuredLogEntry
{};
270 record
.setInt("existsCheck", existsCheck
);
271 record
.setStr("file", name
);
272 bool needsCppStack
= true;
273 if (!g_context
.isNull()) {
277 createBacktrace(BacktraceArgs().withArgValues(false));
278 std::vector
<std::string
> frameStrings
;
279 std::vector
<folly::StringPiece
> frames
;
280 for (int i
= 0; i
< bt
.size(); i
++) {
281 auto f
= tvCastToArrayLike(bt
.rvalAt(i
).tv());
282 if (f
.exists(s_file
)) {
283 auto s
= tvCastToString(f
.rvalAt(s_file
).tv()).toCppString();
284 if (f
.exists(s_line
)) {
287 tvCastToInt64(f
.rvalAt(s_line
).tv())
290 frameStrings
.emplace_back(std::move(s
));
291 frames
.push_back(frameStrings
.back());
294 record
.setVec("stack", frames
);
295 needsCppStack
= false;
299 record
.setStackTrace("stack", StackTrace
{StackTrace::Force
{}});
301 StructuredLog::log("hhvm_file_cache", record
);
304 auto startupFailure
= [] (const std::string
& msg
) {
306 Logger::Error("Shutting down due to failure(s) to bind in "
307 "HttpServer::runAndExitProcess");
308 // Logger flushes itself---we don't need to run any atexit handlers
309 // (historically we've mostly just SEGV'd while trying) ...
313 if (!RuntimeOption::InstanceId
.empty()) {
314 std::string msg
= "Starting instance " + RuntimeOption::InstanceId
;
315 if (!RuntimeOption::DeploymentId
.empty()) {
316 msg
+= " from deployment " + RuntimeOption::DeploymentId
;
323 if (RuntimeOption::ServerPort
) {
324 if (!startServer(true)) {
325 startupFailure("Unable to start page server");
328 Logger::Info("page server started");
331 StartTime
= time(nullptr);
333 if (RuntimeOption::AdminServerPort
) {
334 if (!startServer(false)) {
335 startupFailure("Unable to start admin server");
338 Logger::Info("admin server started");
341 for (unsigned int i
= 0; i
< m_satellites
.size(); i
++) {
342 std::string name
= m_satellites
[i
]->getName();
344 m_satellites
[i
]->start();
345 Logger::Info("satellite server %s started", name
.c_str());
346 } catch (Exception
& e
) {
348 folly::format("Unable to start satellite server {}: {}",
349 name
, e
.getMessage()).str()
355 if (!Eval::Debugger::StartServer()) {
356 startupFailure("Unable to start debugger server");
358 } else if (RuntimeOption::EnableDebuggerServer
) {
359 Logger::Info("debugger server started");
363 InitFiniNode::ServerInit();
364 } catch (std::exception
& e
) {
366 folly::sformat("Exception in InitFiniNode::ServerInit(): {}",
371 BootStats::mark("servers started");
372 Logger::Info("all servers started");
373 if (!RuntimeOption::ServerForkEnabled
) {
376 // We should not fork from the server process. Use light process
377 // instead. This will intercept subsequent fork() calls and make them
381 // Otherwise, the binary we are building is not HHVM. It is probably
382 // some tests, don't intercept fork().
383 Logger::Warning("ignored runtime option Server.Forking.Enabled=false");
386 Logger::Warning("ignored Server.Forking.Enabled=false "
387 "as it only works on Linux");
390 #if FOLLY_HAVE_PTHREAD_ATFORK
391 pthread_atfork(nullptr, nullptr,
393 Process::OOMScoreAdj(1000);
397 if (RuntimeOption::ServerForkLogging
) {
399 if (EnableForkLogging
) {
402 // the binary we are building is not HHVM. It is probably
403 // some tests, don't intercept fork().
404 Logger::Warning("ignored runtime option Server.Forking.Log=true");
407 Logger::Warning("ignored Server.Forking.Log=true "
408 "as it only works on Linux");
411 if (RuntimeOption::EvalServerOOMAdj
< 0) {
412 // Avoid HHVM getting killed when a forked process uses too much memory.
413 // A positive adjustment makes it more likely for the server to be killed,
414 // and that's not what we want.
415 Process::OOMScoreAdj(RuntimeOption::EvalServerOOMAdj
);
420 // continously running until /stop is received on admin server, or
421 // takeover is requested.
426 Logger::Warning("Server stopping with reason: %s", m_stopReason
);
428 // if we were killed, bail out immediately
430 Logger::Info("page server killed");
435 if (RuntimeOption::ServerPort
) {
436 Logger::Info("stopping page server");
437 m_pageServer
->stop();
444 m_watchDog
.waitForEnd();
445 playShutdownRequest(RuntimeOption::ServerCleanupRequest
);
448 void HttpServer::waitForServers() {
449 if (RuntimeOption::ServerPort
) {
450 m_pageServer
->waitForEnd();
452 if (RuntimeOption::AdminServerPort
) {
453 m_adminServer
->waitForEnd();
455 // all other servers invoke waitForEnd on stop
458 static void exit_on_timeout(int sig
) {
459 signal(sig
, SIG_DFL
);
461 TerminateProcess(GetCurrentProcess(), (UINT
)-1);
463 kill(getpid(), SIGKILL
);
465 // we really shouldn't get here, but who knows.
466 // abort so we catch it as a crash.
470 void HttpServer::stop(const char* stopReason
) {
471 if (m_stopping
.exchange(true)) return;
472 // we're shutting down flush http logs
474 HttpRequestHandler::GetAccessLog().flushAllWriters();
475 Process::OOMScoreAdj(1000);
476 MarkShutdownStat(ShutdownEvent::SHUTDOWN_INITIATED
);
478 if (RuntimeOption::ServerKillOnTimeout
) {
480 RuntimeOption::ServerPreShutdownWait
+
481 RuntimeOption::ServerShutdownListenWait
+
482 RuntimeOption::ServerGracefulShutdownWait
;
485 // Use a killer thread to _Exit() after totalWait seconds. If
486 // the main thread returns before that, the killer thread will
487 // exit. So don't do join() on this thread. Since we create a
488 // thread here, `HttpServer::stop()` cannot be called in signal
489 // handlers, use `stopOnSignal()` (which uses SIGALRM) in that
491 auto killer
= std::thread([totalWait
] {
494 param
.sched_priority
= 5;
495 pthread_setschedparam(pthread_self(), SCHED_RR
, ¶m
);
496 // It is OK if we fail to increase thread priority.
499 std::this_thread::sleep_for(std::chrono::seconds
{totalWait
});
508 m_stopReason
= stopReason
;
512 void HttpServer::stopOnSignal(int sig
) {
513 if (m_stopping
.exchange(true)) return;
514 // we're shutting down flush http logs
516 HttpRequestHandler::GetAccessLog().flushAllWriters();
517 Process::OOMScoreAdj(1000);
518 MarkShutdownStat(ShutdownEvent::SHUTDOWN_INITIATED
);
520 // Signal to the main server thread to exit immediately if
521 // we want to die on SIGTERM
522 if (RuntimeOption::ServerKillOnSIGTERM
&& sig
== SIGTERM
) {
527 m_stopReason
= "SIGTERM received";
534 if (RuntimeOption::ServerGracefulShutdownWait
) {
535 signal(SIGALRM
, exit_on_timeout
);
536 alarm(RuntimeOption::ServerGracefulShutdownWait
);
541 m_stopReason
= "signal received";
545 void HttpServer::EvictFileCache() {
546 // In theory, the kernel should do just as well even if we don't
547 // explicitly advise files out. But we can do it anyway when we
548 // need more free memory, e.g., when a new instance of the server is
550 advise_out(RuntimeOption::RepoLocalPath
);
551 advise_out(RuntimeOption::FileCache
);
555 void HttpServer::PrepareToStop() {
556 MarkShutdownStat(ShutdownEvent::SHUTDOWN_PREPARE
);
557 PrepareToStopTime
.store(time(nullptr), std::memory_order_release
);
561 void HttpServer::createPid() {
562 if (!RuntimeOption::PidFile
.empty()) {
563 FILE * f
= fopen(RuntimeOption::PidFile
.c_str(), "w");
565 auto const pid
= getpid();
567 snprintf(buf
, sizeof(buf
), "%" PRId64
, (int64_t)pid
);
568 fwrite(buf
, strlen(buf
), 1, f
);
571 Logger::Error("Unable to open pid file %s for write",
572 RuntimeOption::PidFile
.c_str());
577 void HttpServer::removePid() {
578 if (!RuntimeOption::PidFile
.empty()) {
579 unlink(RuntimeOption::PidFile
.c_str());
583 void HttpServer::killPid() {
584 if (!RuntimeOption::PidFile
.empty()) {
585 CstrBuffer
sb(RuntimeOption::PidFile
.c_str());
587 int64_t pid
= strtoll(sb
.data(), nullptr, 10);
589 kill((pid_t
)pid
, SIGKILL
);
593 Logger::Error("Unable to read pid file %s for any meaningful pid",
594 RuntimeOption::PidFile
.c_str());
598 ///////////////////////////////////////////////////////////////////////////////
601 void HttpServer::watchDog() {
604 while (!m_stopped
&& !noneed
) {
607 if (RuntimeOption::DropCacheCycle
> 0) {
609 if ((count
% RuntimeOption::DropCacheCycle
) == 0) { // every hour
617 if (RuntimeOption::MaxRSSPollingCycle
> 0) {
619 if ((count
% RuntimeOption::MaxRSSPollingCycle
) == 0) { // every minute
626 static bool sendAdminCommand(const char* cmd
) {
627 if (RuntimeOption::AdminServerPort
<= 0) return false;
628 std::string host
= RuntimeOption::AdminServerIP
;
629 if (host
.empty()) host
= "localhost";
630 auto passwords
= RuntimeOption::AdminPasswords
;
631 if (passwords
.empty() && !RuntimeOption::AdminPassword
.empty()) {
632 passwords
.insert(RuntimeOption::AdminPassword
);
634 auto passwordIter
= passwords
.begin();
637 if (passwordIter
!= passwords
.end()) {
638 url
= folly::sformat("http://{}:{}/{}?auth={}", host
,
639 RuntimeOption::AdminServerPort
,
643 url
= folly::sformat("http://{}:{}/{}", host
,
644 RuntimeOption::AdminServerPort
, cmd
);
646 if (CURL
* curl
= curl_easy_init()) {
647 curl_easy_setopt(curl
, CURLOPT_URL
, url
.c_str());
648 curl_easy_setopt(curl
, CURLOPT_NOBODY
, 1);
649 curl_easy_setopt(curl
, CURLOPT_FAILONERROR
, 1);
650 curl_easy_setopt(curl
, CURLOPT_TIMEOUT
, 3);
651 auto code
= curl_easy_perform(curl
);
652 curl_easy_cleanup(curl
);
653 if (code
== CURLE_OK
) {
654 Logger::Info("sent %s via admin port", cmd
);
658 } while (passwordIter
!= passwords
.end());
662 bool HttpServer::ReduceOldServerLoad() {
663 if (!RuntimeOption::StopOldServer
) return false;
664 if (OldServerStopTime
> 0) return true;
665 Logger::Info("trying to reduce load of the old HPHP server");
666 return sendAdminCommand("prepare-to-stop");
669 bool HttpServer::StopOldServer() {
670 if (OldServerStopTime
> 0) return true;
671 SCOPE_EXIT
{ OldServerStopTime
= time(nullptr); };
672 Logger::Info("trying to shut down old HPHP server by /stop command");
673 return sendAdminCommand("stop");
676 // Return the estimated amount of memory that can be safely taken into
677 // the current process RSS.
678 static inline int64_t availableMemory(const MemInfo
& mem
, int64_t rss
,
680 // Estimation of page cache that are readily evictable without
681 // causing big memory pressure. We consider it safe to take
682 // `pageFreeFactor` percent of cached pages (excluding those used by
683 // the current process, estimated to be equal to the current RSS)
684 auto const otherCacheMb
= std::max((int64_t)0, mem
.cachedMb
- rss
);
685 auto const availableMb
= mem
.freeMb
+ otherCacheMb
* factor
/ 100;
689 bool HttpServer::CanContinue(const MemInfo
& mem
, int64_t rssMb
,
690 int64_t rssNeeded
, int cacheFreeFactor
) {
691 if (!mem
.valid()) return false;
692 if (mem
.freeMb
< RuntimeOption::ServerCriticalFreeMb
) return false;
693 auto const availableMb
= availableMemory(mem
, rssMb
, cacheFreeFactor
);
694 auto const result
= (rssMb
+ availableMb
>= rssNeeded
);
695 if (result
) assertx(CanStep(mem
, rssMb
, rssNeeded
, cacheFreeFactor
));
699 bool HttpServer::CanStep(const MemInfo
& mem
, int64_t rssMb
,
700 int64_t rssNeeded
, int cacheFreeFactor
) {
701 if (!mem
.valid()) return false;
702 if (mem
.freeMb
< RuntimeOption::ServerCriticalFreeMb
) return false;
703 auto const availableMb
= availableMemory(mem
, rssMb
, cacheFreeFactor
);
704 // Estimation of the memory needed till the next check point. Since
705 // the current check point is not the last one, we try to be more
706 // optimistic, by assuming that memory requirement won't grow
707 // drastically between successive check points, and that it won't
708 // grow over our estimate.
709 auto const neededToStep
= std::min(rssNeeded
/ 4, rssNeeded
- rssMb
);
710 return (availableMb
>= neededToStep
);
713 void HttpServer::CheckMemAndWait(bool final
) {
714 if (!RuntimeOption::StopOldServer
) return;
715 if (RuntimeOption::OldServerWait
<= 0) return;
717 auto const rssNeeded
= RuntimeOption::ServerRSSNeededMb
;
718 auto const factor
= RuntimeOption::CacheFreeFactor
;
720 // Don't wait too long
721 if (OldServerStopTime
> 0 &&
722 time(nullptr) - OldServerStopTime
>= RuntimeOption::OldServerWait
) {
726 auto const rssMb
= Process::GetMemUsageMb();
728 if (!Process::GetMemoryInfo(memInfo
)) {
729 Logger::Error("Failed to obtain memory information");
730 HttpServer::StopOldServer();
733 Logger::FInfo("Memory pressure check: free/cached/buffers {}/{}/{} "
734 "currentRss {}Mb, required {}Mb.",
735 memInfo
.freeMb
, memInfo
.cachedMb
, memInfo
.buffersMb
,
739 if (CanStep(memInfo
, rssMb
, rssNeeded
, factor
)) return;
741 if (CanContinue(memInfo
, rssMb
, rssNeeded
, factor
)) return;
744 // OK, we don't have enough memory, let's do something.
745 HttpServer::StopOldServer(); // nop if already called before
746 /* sleep override */ sleep(1);
747 } while (true); // Guaranteed to return in the loop, at least upon timeout.
751 void HttpServer::MarkShutdownStat(ShutdownEvent event
) {
752 if (!RuntimeOption::EvalLogServerRestartStats
) return;
753 std::lock_guard
<folly::MicroSpinLock
> lock(StatsLock
);
755 Process::GetMemoryInfo(mem
);
756 auto const rss
= Process::GetMemUsageMb();
757 auto const requests
= requestCount();
758 if (event
== ShutdownEvent::SHUTDOWN_PREPARE
) {
759 ShutdownStats
.clear();
760 ShutdownStats
.reserve(ShutdownEvent::kNumEvents
);
763 if (!ShutdownStats
.empty()) {
764 assertx(ShutdownStats
.back().event
<= event
);
767 ShutdownStats
.push_back({event
, time(nullptr), mem
, rss
, requests
});
770 void HttpServer::LogShutdownStats() {
771 if (!RuntimeOption::EvalLogServerRestartStats
) return;
772 StructuredLogEntry entry
;
773 std::lock_guard
<folly::MicroSpinLock
> lock(StatsLock
);
774 if (ShutdownStats
.empty()) return;
775 for (size_t i
= 0; i
< ShutdownStats
.size(); ++i
) {
776 const auto& stat
= ShutdownStats
[i
];
777 auto const eventName
= stat
.eventName();
778 entry
.setInt(folly::sformat("{}.rss", eventName
), stat
.rss
);
779 entry
.setInt(folly::sformat("{}.free", eventName
),
780 stat
.memUsage
.freeMb
);
781 entry
.setInt(folly::sformat("{}.cached", eventName
),
782 stat
.memUsage
.cachedMb
);
783 entry
.setInt(folly::sformat("{}.buffers", eventName
),
784 stat
.memUsage
.buffersMb
);
785 // Log the difference since last event, if available
787 const auto& last
= ShutdownStats
[i
- 1];
788 auto const lastEvent
= last
.eventName();
789 entry
.setInt(folly::sformat("{}.duration", lastEvent
),
790 stat
.time
- last
.time
);
791 entry
.setInt(folly::sformat("{}.requests", lastEvent
),
792 stat
.requestsServed
- last
.requestsServed
);
793 entry
.setInt(folly::sformat("{}.rss.delta", lastEvent
),
794 stat
.rss
- last
.rss
);
797 StructuredLog::log("webserver_shutdown_timing", entry
);
798 ShutdownStats
.clear();
801 void HttpServer::dropCache() {
802 FILE *f
= fopen("/proc/sys/vm/drop_caches", "w");
804 // http://www.linuxinsight.com/proc_sys_vm_drop_caches.html
805 const char *FREE_ALL_CACHES
= "3\n";
806 fwrite(FREE_ALL_CACHES
, 2, 1, f
);
811 void HttpServer::checkMemory() {
812 int64_t used
= Process::GetMemUsageMb() * 1024 * 1024;
813 if (RuntimeOption::MaxRSS
> 0 && used
> RuntimeOption::MaxRSS
) {
815 "ResourceLimit.MaxRSS %" PRId64
" reached %" PRId64
" used, exiting",
816 RuntimeOption::MaxRSS
, used
);
821 void HttpServer::getSatelliteStats(
822 std::vector
<std::pair
<std::string
, int>> *stats
) {
823 for (const auto& i
: m_satellites
) {
824 std::pair
<std::string
, int> active("satellite." + i
->getName() + ".load",
825 i
->getActiveWorker());
826 std::pair
<std::string
, int> queued("satellite." + i
->getName() + ".queued",
828 stats
->push_back(active
);
829 stats
->push_back(queued
);
833 std::pair
<int, int> HttpServer::getSatelliteRequestCount() const {
836 for (const auto& i
: m_satellites
) {
837 inflight
+= i
->getActiveWorker();
838 queued
+= i
->getQueuedJobs();
840 return std::make_pair(inflight
, queued
);
843 ///////////////////////////////////////////////////////////////////////////////
846 bool HttpServer::startServer(bool pageServer
) {
847 int port
= pageServer
?
848 RuntimeOption::ServerPort
: RuntimeOption::AdminServerPort
;
850 // 1. try something nice
851 for (unsigned int i
= 0; i
< 60; i
++) {
854 m_pageServer
->start();
856 m_adminServer
->start();
859 } catch (FailedToListenException
& e
) {
860 if (RuntimeOption::ServerExitOnBindFail
) return false;
864 if (errno
== EACCES
) {
865 Logger::Error("%s: Permission denied.", e
.what());
869 if (pageServer
&& !RuntimeOption::ServerFileSocket
.empty()) {
871 Logger::Info("Unlinking unused socket at %s",
872 RuntimeOption::ServerFileSocket
.c_str());
874 struct stat stat_buf
;
875 if (stat(RuntimeOption::ServerFileSocket
.c_str(), &stat_buf
) == 0
876 && S_ISSOCK(stat_buf
.st_mode
)) {
877 std::string cmd
= "bash -c '! fuser ";
878 cmd
+= RuntimeOption::ServerFileSocket
;
880 if (FileUtil::ssystem(cmd
.c_str()) == 0) {
881 unlink(RuntimeOption::ServerFileSocket
.c_str());
889 // 2. try something harsh
890 if (RuntimeOption::ServerHarshShutdown
) {
891 for (unsigned int i
= 0; i
< 5; i
++) {
894 m_pageServer
->start();
896 m_adminServer
->start();
899 } catch (FailedToListenException
& e
) {
901 Logger::Info("shutting down old HPHP server by pid file");
909 // 3. try something evil
910 if (RuntimeOption::ServerEvilShutdown
) {
911 for (unsigned int i
= 0; i
< 60; i
++) {
914 m_pageServer
->start();
916 m_adminServer
->start();
919 } catch (FailedToListenException
& e
) {
920 if (pageServer
&& !RuntimeOption::ServerFileSocket
.empty()) {
922 Logger::Info("unlinking socket at %s",
923 RuntimeOption::ServerFileSocket
.c_str());
926 struct stat stat_buf
;
927 if (stat(RuntimeOption::ServerFileSocket
.c_str(), &stat_buf
) == 0
928 && S_ISSOCK(stat_buf
.st_mode
)) {
929 unlink(RuntimeOption::ServerFileSocket
.c_str());
933 Logger::Info("killing anything listening on port %d", port
);
936 std::string cmd
= "lsof -t -i :";
937 cmd
+= folly::to
<std::string
>(port
);
938 cmd
+= " | xargs kill -9";
939 FileUtil::ssystem(cmd
.c_str());
949 ///////////////////////////////////////////////////////////////////////////////