зеркало из https://github.com/Azure/iisnode.git
fix #129: simplify support for multiple node processes
This commit is contained in:
Родитель
cfefd0a6ff
Коммит
982c518cf6
|
@ -29,7 +29,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
|
|||
<sectionSchema name="system.webServer/iisnode">
|
||||
<attribute name="node_env" type="string" expanded="true" defaultValue="%node_env%"/>
|
||||
<attribute name="asyncCompletionThreadCount" type="uint" defaultValue="0"/>
|
||||
<attribute name="maxProcessCountPerApplication" type="uint" defaultValue="4"/>
|
||||
<attribute name="nodeProcessCountPerApplication" type="uint" defaultValue="1"/>
|
||||
<attribute name="nodeProcessCommandLine" type="string" expanded="true" defaultValue=""%programfiles%\nodejs\node.exe""/>
|
||||
<attribute name="maxConcurrentRequestsPerProcess" type="uint" allowInfitnite="true" defaultValue="1024"/>
|
||||
<attribute name="maxNamedPipeConnectionRetry" type="uint" defaultValue="3"/>
|
||||
|
|
|
@ -29,7 +29,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
|
|||
<sectionSchema name="system.webServer/iisnode">
|
||||
<attribute name="node_env" type="string" expanded="true" defaultValue="%node_env%"/>
|
||||
<attribute name="asyncCompletionThreadCount" type="uint" defaultValue="0"/>
|
||||
<attribute name="maxProcessCountPerApplication" type="uint" defaultValue="4"/>
|
||||
<attribute name="nodeProcessCountPerApplication" type="uint" defaultValue="1"/>
|
||||
<attribute name="nodeProcessCommandLine" type="string" expanded="true" defaultValue=""%programfiles(x86)%\nodejs\node.exe""/>
|
||||
<attribute name="maxConcurrentRequestsPerProcess" type="uint" allowInfitnite="true" defaultValue="1024"/>
|
||||
<attribute name="maxNamedPipeConnectionRetry" type="uint" defaultValue="3"/>
|
||||
|
|
|
@ -434,7 +434,7 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat
|
|||
|
||||
CheckError(GetConfigSection(context, §ion));
|
||||
CheckError(GetDWORD(section, L"asyncCompletionThreadCount", &c->asyncCompletionThreadCount));
|
||||
CheckError(GetDWORD(section, L"maxProcessCountPerApplication", &c->maxProcessCountPerApplication));
|
||||
CheckError(GetDWORD(section, L"nodeProcessCountPerApplication", &c->nodeProcessCountPerApplication));
|
||||
CheckError(GetDWORD(section, L"maxConcurrentRequestsPerProcess", &c->maxConcurrentRequestsPerProcess));
|
||||
CheckError(GetDWORD(section, L"maxNamedPipeConnectionRetry", &c->maxNamedPipeConnectionRetry));
|
||||
CheckError(GetDWORD(section, L"namedPipeConnectionRetryDelay", &c->namedPipeConnectionRetryDelay));
|
||||
|
@ -474,6 +474,16 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat
|
|||
GetSystemInfo(&info);
|
||||
c->asyncCompletionThreadCount = 0 == info.dwNumberOfProcessors ? 4 : info.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
if (0 == c->nodeProcessCountPerApplication)
|
||||
{
|
||||
// default number of node.exe processes to create per node.js application
|
||||
|
||||
SYSTEM_INFO info;
|
||||
|
||||
GetSystemInfo(&info);
|
||||
c->nodeProcessCountPerApplication = 0 == info.dwNumberOfProcessors ? 1 : info.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
// CR: check for ERROR_ALREADY_ASSIGNED to detect a race in creation of this section
|
||||
// CR: refcounting may be needed if synchronous code paths proove too long (race with config changes)
|
||||
|
@ -521,9 +531,9 @@ DWORD CModuleConfiguration::GetAsyncCompletionThreadCount(IHttpContext* ctx)
|
|||
GETCONFIG(asyncCompletionThreadCount)
|
||||
}
|
||||
|
||||
DWORD CModuleConfiguration::GetMaxProcessCountPerApplication(IHttpContext* ctx)
|
||||
DWORD CModuleConfiguration::GetNodeProcessCountPerApplication(IHttpContext* ctx)
|
||||
{
|
||||
GETCONFIG(maxProcessCountPerApplication)
|
||||
GETCONFIG(nodeProcessCountPerApplication)
|
||||
}
|
||||
|
||||
LPCTSTR CModuleConfiguration::GetNodeProcessCommandLine(IHttpContext* ctx)
|
||||
|
|
|
@ -8,7 +8,7 @@ class CModuleConfiguration : public IHttpStoredContext
|
|||
private:
|
||||
|
||||
DWORD asyncCompletionThreadCount;
|
||||
DWORD maxProcessCountPerApplication;
|
||||
DWORD nodeProcessCountPerApplication;
|
||||
LPTSTR nodeProcessCommandLine;
|
||||
DWORD maxConcurrentRequestsPerProcess;
|
||||
DWORD maxNamedPipeConnectionRetry;
|
||||
|
@ -48,7 +48,7 @@ public:
|
|||
static HRESULT Initialize(IHttpServer* server, HTTP_MODULE_ID moduleId);
|
||||
|
||||
static DWORD GetAsyncCompletionThreadCount(IHttpContext* ctx);
|
||||
static DWORD GetMaxProcessCountPerApplication(IHttpContext* ctx);
|
||||
static DWORD GetNodeProcessCountPerApplication(IHttpContext* ctx);
|
||||
static LPCTSTR GetNodeProcessCommandLine(IHttpContext* ctx);
|
||||
static DWORD GetMaxConcurrentRequestsPerProcess(IHttpContext* ctx);
|
||||
static DWORD GetMaxNamedPipeConnectionRetry(IHttpContext* ctx);
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#include "precomp.h"
|
||||
|
||||
CNodeProcessManager::CNodeProcessManager(CNodeApplication* application, IHttpContext* context)
|
||||
: application(application), processes(NULL), processCount(0), currentProcess(0), isClosing(FALSE),
|
||||
: application(application), processes(NULL), currentProcess(0), isClosing(FALSE),
|
||||
refCount(1)
|
||||
{
|
||||
if (this->GetApplication()->IsDebugMode())
|
||||
{
|
||||
this->maxProcessCount = 1;
|
||||
this->processCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->maxProcessCount = CModuleConfiguration::GetMaxProcessCountPerApplication(context);
|
||||
this->processCount = CModuleConfiguration::GetNodeProcessCountPerApplication(context);
|
||||
}
|
||||
|
||||
// cache event provider since the application can be disposed prior to CNodeProcessManager
|
||||
|
@ -22,11 +22,19 @@ CNodeProcessManager::CNodeProcessManager(CNodeApplication* application, IHttpCon
|
|||
|
||||
CNodeProcessManager::~CNodeProcessManager()
|
||||
{
|
||||
if (NULL != processes)
|
||||
this->Cleanup();
|
||||
}
|
||||
|
||||
void CNodeProcessManager::Cleanup()
|
||||
{
|
||||
if (NULL != this->processes)
|
||||
{
|
||||
for (int i = 0; i < this->processCount; i++)
|
||||
{
|
||||
delete this->processes[i];
|
||||
if (this->processes[i])
|
||||
{
|
||||
delete this->processes[i];
|
||||
}
|
||||
}
|
||||
|
||||
delete[] this->processes;
|
||||
|
@ -43,74 +51,45 @@ HRESULT CNodeProcessManager::Initialize(IHttpContext* context)
|
|||
{
|
||||
HRESULT hr;
|
||||
|
||||
ErrorIf(NULL == (this->processes = new CNodeProcess* [this->maxProcessCount]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
RtlZeroMemory(this->processes, this->maxProcessCount * sizeof(CNodeProcess*));
|
||||
if (this->GetApplication()->IsDebuggee())
|
||||
ErrorIf(NULL == (this->processes = new CNodeProcess* [this->processCount]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
RtlZeroMemory(this->processes, this->processCount * sizeof(CNodeProcess*));
|
||||
for (int i = 0; i < this->processCount; i++)
|
||||
{
|
||||
// ensure the debugee process is started without activating message
|
||||
// this is to make sure it is available for the debugger to connect to
|
||||
|
||||
CheckError(this->AddOneProcess(NULL, context));
|
||||
CheckError(this->AddProcess(i, context));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
Error:
|
||||
|
||||
if (NULL != this->processes)
|
||||
{
|
||||
delete [] this->processes;
|
||||
this->processes = NULL;
|
||||
}
|
||||
this->Cleanup();
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CNodeProcessManager::AddOneProcessCore(CNodeProcess** process, IHttpContext* context)
|
||||
HRESULT CNodeProcessManager::AddProcess(int ordinal, IHttpContext* context)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
ErrorIf(this->processCount == this->maxProcessCount, ERROR_NOT_ENOUGH_QUOTA);
|
||||
ErrorIf(NULL == (this->processes[this->processCount] = new CNodeProcess(this, context, this->processCount)), ERROR_NOT_ENOUGH_MEMORY);
|
||||
CheckError(this->processes[this->processCount]->Initialize(context));
|
||||
if (NULL != process)
|
||||
{
|
||||
*process = this->processes[this->processCount];
|
||||
}
|
||||
this->processCount++;
|
||||
ErrorIf(NULL != this->processes[ordinal], ERROR_INVALID_PARAMETER);
|
||||
ErrorIf(NULL == (this->processes[ordinal] = new CNodeProcess(this, context, ordinal)), ERROR_NOT_ENOUGH_MEMORY);
|
||||
CheckError(this->processes[ordinal]->Initialize(context));
|
||||
|
||||
return S_OK;
|
||||
Error:
|
||||
|
||||
if (NULL != this->processes[this->processCount])
|
||||
if (NULL != this->processes[ordinal])
|
||||
{
|
||||
delete this->processes[this->processCount];
|
||||
this->processes[this->processCount] = NULL;
|
||||
delete this->processes[ordinal];
|
||||
this->processes[ordinal] = NULL;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CNodeProcessManager::AddOneProcess(CNodeProcess** process, IHttpContext* context)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (NULL != process)
|
||||
{
|
||||
*process = NULL;
|
||||
}
|
||||
|
||||
ErrorIf(this->processCount == this->maxProcessCount, ERROR_NOT_ENOUGH_QUOTA);
|
||||
CheckError(this->AddOneProcessCore(process, context));
|
||||
|
||||
return S_OK;
|
||||
Error:
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request)
|
||||
{
|
||||
HRESULT hr;
|
||||
unsigned int tmpProcess, processToUse;
|
||||
|
||||
CheckNull(request);
|
||||
|
||||
|
@ -120,24 +99,48 @@ HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request)
|
|||
{
|
||||
ENTER_SRW_SHARED(this->srwlock)
|
||||
|
||||
if (!this->isClosing && this->TryRouteRequestToExistingProcess(request))
|
||||
if (!this->isClosing)
|
||||
{
|
||||
request = NULL;
|
||||
// employ a round robin routing logic to get a "ticket" to use a process with a specific ordinal number
|
||||
|
||||
if (1 == this->processCount)
|
||||
{
|
||||
processToUse = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
tmpProcess = this->currentProcess;
|
||||
processToUse = (tmpProcess + 1) % this->processCount;
|
||||
} while (tmpProcess != InterlockedCompareExchange(&this->currentProcess, processToUse, tmpProcess));
|
||||
}
|
||||
|
||||
// try dispatch to that process
|
||||
|
||||
if (NULL != this->processes[processToUse])
|
||||
{
|
||||
CheckError(this->processes[processToUse]->AcceptRequest(request));
|
||||
request = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LEAVE_SRW_SHARED(this->srwlock)
|
||||
|
||||
if (request && !this->isClosing)
|
||||
if (NULL != request)
|
||||
{
|
||||
// existing processes were unable to accept this request; create a new process to handle it
|
||||
// the process to dispatch to does not exist and must be recreated
|
||||
|
||||
ENTER_SRW_EXCLUSIVE(this->srwlock)
|
||||
|
||||
if (!this->isClosing && !this->TryRouteRequestToExistingProcess(request))
|
||||
if (!this->isClosing)
|
||||
{
|
||||
CNodeProcess* newProcess = NULL;
|
||||
CheckError(this->AddOneProcess(&newProcess, request->GetHttpContext()));
|
||||
CheckError(newProcess->AcceptRequest(request));
|
||||
if (NULL == this->processes[processToUse])
|
||||
{
|
||||
CheckError(this->AddProcess(processToUse, request->GetHttpContext()));
|
||||
}
|
||||
|
||||
CheckError(this->processes[processToUse]->AcceptRequest(request));
|
||||
}
|
||||
|
||||
LEAVE_SRW_EXCLUSIVE(this->srwlock)
|
||||
|
@ -162,29 +165,6 @@ Error:
|
|||
return hr;
|
||||
}
|
||||
|
||||
BOOL CNodeProcessManager::TryRouteRequestToExistingProcess(CNodeHttpStoredContext* context)
|
||||
{
|
||||
if (this->processCount == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD i = this->currentProcess;
|
||||
|
||||
do {
|
||||
|
||||
if (S_OK == this->processes[i]->AcceptRequest(context))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
i = (i + 1) % this->processCount;
|
||||
|
||||
} while (i != this->currentProcess);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT CNodeProcessManager::RecycleProcess(CNodeProcess* process)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
@ -221,13 +201,7 @@ HRESULT CNodeProcessManager::RecycleProcess(CNodeProcess* process)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
if (i < (this->processCount - 1))
|
||||
{
|
||||
memcpy(this->processes + i, this->processes + i + 1, sizeof (CNodeProcess*) * (this->processCount - i - 1));
|
||||
}
|
||||
|
||||
this->processCount--;
|
||||
this->currentProcess = 0;
|
||||
this->processes[i] = NULL;
|
||||
|
||||
gracefulRecycle = TRUE;
|
||||
}
|
||||
|
@ -277,7 +251,17 @@ HRESULT CNodeProcessManager::Recycle()
|
|||
|
||||
this->isClosing = TRUE;
|
||||
|
||||
if (0 < this->processCount)
|
||||
BOOL hasActiveProcess = FALSE;
|
||||
for (int i = 0; i < this->processCount; i++)
|
||||
{
|
||||
if (this->processes[i])
|
||||
{
|
||||
hasActiveProcess = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasActiveProcess)
|
||||
{
|
||||
// perform actual recycling on a diffrent thread to free up the file watcher thread
|
||||
|
||||
|
@ -321,6 +305,7 @@ unsigned int CNodeProcessManager::GracefulShutdown(void* arg)
|
|||
ProcessRecycleArgs* args = (ProcessRecycleArgs*)arg;
|
||||
HRESULT hr;
|
||||
HANDLE* drainHandles = NULL;
|
||||
DWORD drainHandleCount = 0;
|
||||
|
||||
// drain active requests
|
||||
|
||||
|
@ -328,16 +313,20 @@ unsigned int CNodeProcessManager::GracefulShutdown(void* arg)
|
|||
RtlZeroMemory(drainHandles, args->count * sizeof HANDLE);
|
||||
for (int i = 0; i < args->count; i++)
|
||||
{
|
||||
drainHandles[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
args->processes[i]->SignalWhenDrained(drainHandles[i]);
|
||||
if (args->processes[i])
|
||||
{
|
||||
drainHandles[drainHandleCount] = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
args->processes[i]->SignalWhenDrained(drainHandles[drainHandleCount]);
|
||||
drainHandleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (args->processManager->gracefulShutdownTimeout > 0)
|
||||
{
|
||||
WaitForMultipleObjects(args->count, drainHandles, TRUE, args->processManager->gracefulShutdownTimeout);
|
||||
WaitForMultipleObjects(drainHandleCount, drainHandles, TRUE, args->processManager->gracefulShutdownTimeout);
|
||||
}
|
||||
|
||||
for (int i = 0; i < args->count; i++)
|
||||
for (int i = 0; i < drainHandleCount; i++)
|
||||
{
|
||||
CloseHandle(drainHandles[i]);
|
||||
}
|
||||
|
|
|
@ -22,18 +22,16 @@ private:
|
|||
CNodeApplication* application;
|
||||
CNodeProcess** processes;
|
||||
DWORD processCount;
|
||||
DWORD maxProcessCount;
|
||||
DWORD currentProcess;
|
||||
unsigned int currentProcess;
|
||||
SRWLOCK srwlock;
|
||||
DWORD gracefulShutdownTimeout;
|
||||
BOOL isClosing;
|
||||
long refCount;
|
||||
CNodeEventProvider* eventProvider;
|
||||
|
||||
HRESULT AddOneProcessCore(CNodeProcess** process, IHttpContext* context);
|
||||
HRESULT AddOneProcess(CNodeProcess** process, IHttpContext* context);
|
||||
BOOL TryRouteRequestToExistingProcess(CNodeHttpStoredContext* context);
|
||||
HRESULT AddProcess(int ordinal, IHttpContext* context);
|
||||
static unsigned int WINAPI GracefulShutdown(void* arg);
|
||||
void Cleanup();
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -54,12 +54,11 @@ console.log('Application started at location ' + process.env.PORT);</pre>
|
|||
* nodeProcessCommandLine - command line starting the node executable; in shared
|
||||
hosting environments this setting would typically be locked at the machine scope.
|
||||
|
||||
* maxProcessCountPerApplication - maximum number of node processes that IIS will start
|
||||
per application to accomodate incresing request load
|
||||
* nodeProcessCountPerApplication - number of node.exe processes that IIS will start per application;
|
||||
setting this value to 0 results in creating one node.exe process per each processor on the machine
|
||||
|
||||
* maxConcurrentRequestsPerProcess - maximum number of reqeusts one node process can
|
||||
handle at a time; if a request arrives while all existing node processes have reached their
|
||||
limit, a new node process is created to handle it (up to the limit of maxProcessCountPerApplication)
|
||||
handle at a time
|
||||
|
||||
* maxNamedPipeConnectionRetry - number of times IIS will retry to establish a named pipe connection with a
|
||||
node process in order to send a new HTTP request
|
||||
|
@ -87,7 +86,7 @@ console.log('Application started at location ' + process.env.PORT);</pre>
|
|||
|
||||
* logDirectoryNameSuffix - suffix of the directory name that will store files with stdout and stderr captures; directly name is created
|
||||
by appending this suffix to the file name of the node.js application; individual log files are stored in that directory, one per node
|
||||
process (in files of the form x.txt, where x is between 0 and maxProcessCountPerApplication - 1); given a node.js application at
|
||||
process (in files of the form x.txt, where x is between 0 and nodeProcessCountPerApplication - 1); given a node.js application at
|
||||
http://localhost/node/hello.js, log files would by default be stored at http://localhost/node/hello.js.logs/0.txt (thrugh 3.txt);
|
||||
SECURITY NOTE: if log files contain sensitive information, this setting should be modified to contain enough entropy to be considered
|
||||
cryptographically secure; in most situations, a GUID is sufficient
|
||||
|
@ -119,7 +118,7 @@ console.log('Application started at location ' + process.env.PORT);</pre>
|
|||
|
||||
<iisnode
|
||||
node_env="%node_env%"
|
||||
maxProcessCountPerApplication="4"
|
||||
nodeProcessCountPerApplication="1"
|
||||
maxConcurrentRequestsPerProcess="1024"
|
||||
maxNamedPipeConnectionRetry="3"
|
||||
namedPipeConnectionRetryDelay="2000"
|
||||
|
|
|
@ -20,12 +20,11 @@
|
|||
* nodeProcessCommandLine - command line starting the node executable; in shared
|
||||
hosting environments this setting would typically be locked at the machine scope.
|
||||
|
||||
* maxProcessCountPerApplication - maximum number of node processes that IIS will start
|
||||
per application to accomodate incresing request load
|
||||
* nodeProcessCountPerApplication - number of node.exe processes that IIS will start per application;
|
||||
setting this value to 0 results in creating one node.exe process per each processor on the machine
|
||||
|
||||
* maxConcurrentRequestsPerProcess - maximum number of reqeusts one node process can
|
||||
handle at a time; if a request arrives while all existing node processes have reached their
|
||||
limit, a new node process is created to handle it (up to the limit of maxProcessCountPerApplication)
|
||||
handle at a time
|
||||
|
||||
* maxNamedPipeConnectionRetry - number of times IIS will retry to establish a named pipe connection with a
|
||||
node process in order to send a new HTTP request
|
||||
|
@ -53,7 +52,7 @@
|
|||
|
||||
* logDirectoryNameSuffix - suffix of the directory name that will store files with stdout and stderr captures; directly name is created
|
||||
by appending this suffix to the file name of the node.js application; individual log files are stored in that directory, one per node
|
||||
process (in files of the form x.txt, where x is between 0 and maxProcessCountPerApplication - 1); given a node.js application at
|
||||
process (in files of the form x.txt, where x is between 0 and nodeProcessCountPerApplication - 1); given a node.js application at
|
||||
http://localhost/node/hello.js, log files would by default be stored at http://localhost/node/hello.js.logs/0.txt (thrugh 3.txt);
|
||||
SECURITY NOTE: if log files contain sensitive information, this setting should be modified to contain enough entropy to be considered
|
||||
cryptographically secure; in most situations, a GUID is sufficient
|
||||
|
@ -85,7 +84,7 @@
|
|||
|
||||
<iisnode
|
||||
node_env="%node_env%"
|
||||
maxProcessCountPerApplication="4"
|
||||
nodeProcessCountPerApplication="1"
|
||||
maxConcurrentRequestsPerProcess="1024"
|
||||
maxNamedPipeConnectionRetry="3"
|
||||
namedPipeConnectionRetryDelay="2000"
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
Four consecutive requests are dispatched to two node.exe processes following a round robin algorithm
|
||||
*/
|
||||
|
||||
var iisnodeassert = require("iisnodeassert");
|
||||
|
||||
iisnodeassert.sequence([
|
||||
iisnodeassert.get(10000, "/114_roundrobin/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/114_roundrobin/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/114_roundrobin/hello.js", 200, "Hello, world 2"),
|
||||
iisnodeassert.get(2000, "/114_roundrobin/hello.js", 200, "Hello, world 2")
|
||||
]);
|
|
@ -0,0 +1,8 @@
|
|||
var http = require('http');
|
||||
|
||||
var n = 1;
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
res.end('Hello, world ' + n++);
|
||||
}).listen(process.env.PORT);
|
|
@ -0,0 +1,8 @@
|
|||
<configuration>
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="iisnode" path="hello.js" verb="*" modules="iisnode" />
|
||||
</handlers>
|
||||
<iisnode nodeProcessCountPerApplication="2" />
|
||||
</system.webServer>
|
||||
</configuration>
|
|
@ -12,7 +12,7 @@ else {
|
|||
var cluster = require('cluster');
|
||||
|
||||
if (cluster.isMaster) {
|
||||
for (var i = 0; i < 3; i++) {
|
||||
for (var i = 0; i < 4; i++) {
|
||||
cluster.fork();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
loggingEnabled="false"
|
||||
debuggingEnabled="false"
|
||||
devErrorsEnabled="false"
|
||||
maxProcessCountPerApplication="3"
|
||||
nodeProcessCountPerApplication="4"
|
||||
maxConcurrentRequestsPerProcess="999999999"
|
||||
maxNamedPipeConnectionRetry="10"
|
||||
/>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
loggingEnabled="false"
|
||||
debuggingEnabled="false"
|
||||
devErrorsEnabled="false"
|
||||
maxProcessCountPerApplication="1"
|
||||
nodeProcessCountPerApplication="1"
|
||||
maxConcurrentRequestsPerProcess="999999999"
|
||||
maxNamedPipeConnectionRetry="10" />
|
||||
<urlCompression doStaticCompression="false" doDynamicCompression="false" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче