From 8e999d1440c88ae0625d63e3db2b2e211a45efef Mon Sep 17 00:00:00 2001 From: helge Date: Mon, 7 Mar 2005 16:18:33 +0000 Subject: [PATCH] added a first version of forking parallel child worker processes git-svn-id: http://svn.opengroupware.org/SOPE/trunk@625 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- sope-appserver/NGObjWeb/ChangeLog | 9 + sope-appserver/NGObjWeb/Defaults.plist | 1 + sope-appserver/NGObjWeb/Version | 2 +- .../NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.m | 230 +++++++++++++----- 4 files changed, 181 insertions(+), 61 deletions(-) diff --git a/sope-appserver/NGObjWeb/ChangeLog b/sope-appserver/NGObjWeb/ChangeLog index 57fac19a..53856551 100644 --- a/sope-appserver/NGObjWeb/ChangeLog +++ b/sope-appserver/NGObjWeb/ChangeLog @@ -1,3 +1,12 @@ +2005-03-07 Helge Hess + + * WOHttpAdaptor/WOHttpAdaptor.m: added the ability to fork multiple + child servers listing on the same passive socket. The OS will + distribute the load between such processes. Note that this only + works for session less processes (like ZideStore) and that automatic + restarts are not yet implemented. The number of processes can be + controlled using the 'WOHttpAdaptorForkCount' default (v4.5.129) + 2005-03-06 Helge Hess * WOWatchDogApplicationMainOSX.m: fixed some minor issues, still need diff --git a/sope-appserver/NGObjWeb/Defaults.plist b/sope-appserver/NGObjWeb/Defaults.plist index c303f68b..afb362e2 100644 --- a/sope-appserver/NGObjWeb/Defaults.plist +++ b/sope-appserver/NGObjWeb/Defaults.plist @@ -56,6 +56,7 @@ WOFormAlwaysPassDown = YES; WOFrameworksBaseURL = "/WebObjects/Frameworks"; WOGenerateMissingResourceLinks = NO; + WOHttpAdaptorForkCount = 0; WOHttpAdaptorReceiveTimeout = 120; WOHttpAdaptorSendTimeout = 120; WOHttpAdaptor_LogStream = NO; diff --git a/sope-appserver/NGObjWeb/Version b/sope-appserver/NGObjWeb/Version index 25a3a660..23c56cf3 100644 --- a/sope-appserver/NGObjWeb/Version +++ b/sope-appserver/NGObjWeb/Version @@ -1,6 +1,6 @@ # version file -SUBMINOR_VERSION:=128 +SUBMINOR_VERSION:=129 # v4.5.122 requires libNGExtensions v4.5.153 # v4.5.91 requires libNGExtensions v4.5.134 diff --git a/sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.m b/sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.m index bcb5e20f..fcea5bad 100644 --- a/sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.m +++ b/sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.m @@ -45,6 +45,9 @@ #include "WORecordRequestStream.h" #include "WOHttpTransaction.h" +#include +#include + @interface WOHttpAdaptor(Server) /* accessors */ @@ -73,6 +76,7 @@ static BOOL WOCoreOnHTTPAdaptorException = NO; static NSString *WOPort = nil; static int WOHttpAdaptorSendTimeout = 10; static int WOHttpAdaptorReceiveTimeout = 10; +static int WOHttpAdaptorForkCount = 0; static id allow = nil; static BOOL debugOn = NO; @@ -119,18 +123,21 @@ static BOOL debugOn = NO; [ud integerForKey:@"WOHttpAdaptorReceiveTimeout"]; if (allow == nil) { - allow = [ud objectForKey:@"WOHttpAllowHost"]; - if (allow == nil) { + if ((allow = [ud objectForKey:@"WOHttpAllowHost"]) == nil) { allow = [NSArray arrayWithObjects: - @"localhost", @"localhost.localdomain", nil]; + @"localhost", @"localhost.localdomain", + @"127.0.0.1", nil]; } - + if (![allow isKindOfClass:[NSArray class]]) allow = [NSArray arrayWithObject:allow]; allow = [allow copy]; } + WOHttpAdaptorForkCount = + [[ud objectForKey:@"WOHttpAdaptorForkCount"] intValue]; + if (WOCoreOnHTTPAdaptorException) [logger warnWithFormat:@"will dump core on HTTP adaptor exception!"]; } @@ -141,6 +148,67 @@ static BOOL debugOn = NO; return [addr autorelease]; } +- (void)_registerForSignals { +#if !defined(__MINGW32__) + UnixSignalHandler *us = [UnixSignalHandler sharedHandler]; + + [us addObserver:self selector:@selector(handleSIGPIPE:) + forSignal:SIGPIPE immediatelyNotifyOnSignal:NO]; +#endif +} + +- (id)addressFromDefaultsOfApplication:(WOCoreApplication*)_a{ + id lAddress = nil; + const char *cstr; + + if ([WOPort isEqualToString:@"auto"]) { + if ((lAddress = [self autoBindAddress]) != nil) + return lAddress; + } + + if ((cstr = [WOPort cString]) != NULL) { + if (isdigit(*cstr) && index(cstr, ':') == NULL) { + NSNumber *p; + + p = [(WOCoreApplication *)[_a class] port]; + if (p == nil) p = (id)WOPort; + + lAddress = + [NGInternetSocketAddress wildcardAddressWithPort:[p intValue]]; + if (lAddress != nil) + return lAddress; + } + } + + return NGSocketAddressFromString(WOPort); +} + +- (id)addressFromArguments:(NSDictionary *)_args { + id lAddress = nil; + NSString *arg = nil; + const char *cstr; + + if ((arg = [_args objectForKey:@"-p"]) != nil) + return [NGInternetSocketAddress wildcardAddressWithPort:[arg intValue]]; + + if ((arg = [_args objectForKey:@"-WOPort"]) == nil) + return nil; + + lAddress = nil; + if ([arg isEqualToString:@"auto"]) + lAddress = [self autoBindAddress]; + + if ((lAddress == nil) && (cstr = [arg cString])) { + if (isdigit(*cstr)) { + lAddress = + [NGInternetSocketAddress wildcardAddressWithPort:[arg intValue]]; + } + } + if (lAddress == nil) + lAddress = NGSocketAddressFromString(arg); + return lAddress; +} + - (id)initWithName:(NSString *)_name arguments:(NSDictionary *)_args application:(WOCoreApplication *)_application @@ -153,63 +221,13 @@ static BOOL debugOn = NO; if ([[_application recordingPath] length] > 0) WOHttpAdaptor_LogStream = YES; -#if !defined(__MINGW32__) - { - UnixSignalHandler *us = [UnixSignalHandler sharedHandler]; - - [us addObserver:self selector:@selector(handleSIGPIPE:) - forSignal:SIGPIPE - immediatelyNotifyOnSignal:NO]; - } -#endif + [self _registerForSignals]; + + if ([_args count] < 1) + self->address = [self addressFromDefaultsOfApplication:_application]; + else + self->address = [self addressFromArguments:_args]; - if ([_args count] < 1) { - const char *cstr; - - self->address = nil; - - if ([WOPort isEqualToString:@"auto"]) - self->address = [self autoBindAddress]; - - if ((self->address == nil) && ((cstr = [WOPort cString]) != NULL)) { - if (isdigit(*cstr) && index(cstr, ':') == NULL) { - NSNumber *p; - - p = [(WOCoreApplication *)[_application class] port]; - if (p == nil) p = (id)WOPort; - - self->address = - [NGInternetSocketAddress wildcardAddressWithPort:[p intValue]]; - } - } - if (self->address == nil) - self->address = NGSocketAddressFromString(WOPort); - } - else { - NSString *arg = nil; - - if ((arg = [_args objectForKey:@"-p"])) { - self->address = - [NGInternetSocketAddress wildcardAddressWithPort:[arg intValue]]; - } - else if ((arg = [_args objectForKey:@"-WOPort"])) { - const char *cstr; - - self->address = nil; - - if ([arg isEqualToString:@"auto"]) - self->address = [self autoBindAddress]; - - if ((self->address == nil) && (cstr = [arg cString])) { - if (isdigit(*cstr)) { - self->address = - [NGInternetSocketAddress wildcardAddressWithPort:[arg intValue]]; - } - } - if (self->address == nil) - self->address = NGSocketAddressFromString(arg); - } - } self->address = [self->address retain]; if (self->address == nil) { @@ -251,11 +269,99 @@ static BOOL debugOn = NO; return self->address; } +/* forking */ + +static pid_t *childPIDs = NULL; +static BOOL isForkMaster = YES; + +- (void)forkChildren { + unsigned i; + + if (WOHttpAdaptorForkCount == 0) + return; + + [self logWithFormat:@"Note: forking %d children for socket processing.", + WOHttpAdaptorForkCount]; + +#if !defined(__MINGW32__) + [[UnixSignalHandler sharedHandler] + addObserver:self selector:@selector(handleSIGCHLD:) + forSignal:SIGCHLD immediatelyNotifyOnSignal:NO]; +#endif + + childPIDs = calloc(WOHttpAdaptorForkCount + 1, sizeof(pid_t)); + for (i = 0; i < WOHttpAdaptorForkCount; i++) { + childPIDs[i] = fork(); + + if (childPIDs[i] == 0) { + /* child */ + isForkMaster = NO; + return; + } + else if (childPIDs[i] > 0) + printf("Note: successfully forked child: %i\n", childPIDs[i]); + else + [self errorWithFormat:@"failed to fork child %i.", i]; + } +} +- (void)killChildren { + int i; + + if (!isForkMaster) + return; + + for (i = 0; i < WOHttpAdaptorForkCount; i++) { + if (childPIDs[i] != 0) + kill(childPIDs[i], SIGKILL); + } +} + +- (void)checkStatusOfChildren { + /* + Note: currently this does not refork crashed processes. Reforking is harder + than it may sound because the crash can happen at arbitary execution + states. + That is, the "master process" is not virgin anymore, eg it might have + open database connections. + + So the solution might be to refork the whole cluster once a minimum + backend threshold is reached. + */ + unsigned int i; + + if (!isForkMaster) + return; + + for (i = 0; i < WOHttpAdaptorForkCount; i++) { + pid_t result; + int status; + + if (childPIDs[i] == 0) + continue; + + result = waitpid(childPIDs[i], &status, WNOHANG); + if (result == 0) /* did not exit yet */ + continue; + + if (result == -1) { /* error */ + [self errorWithFormat:@"failed to get status of child %i: %s", + childPIDs[i], strerror(errno)]; + continue; + } + + [self logWithFormat:@"Note: child %i terminated.", childPIDs[i]]; + childPIDs[i] = 0; + } +} + /* events */ - (void)handleSIGPIPE:(int)_signal { [self warnWithFormat:@"caught SIGPIPE !"]; } +- (void)handleSIGCHLD:(int)_signal { + [self checkStatusOfChildren]; +} - (void)registerForEvents { int backlog; @@ -296,8 +402,12 @@ static BOOL debugOn = NO; addFileObject:self->socket activities:NSPosixReadableActivity forMode:NSDefaultRunLoopMode]; + + [self forkChildren]; } - (void)unregisterForEvents { + [self killChildren]; + [(WORunLoop *)[WORunLoop currentRunLoop] removeFileObject:self->socket forMode:NSDefaultRunLoopMode]; [[NSNotificationCenter defaultCenter] removeObserver:self]; -- 2.39.5