]> err.no Git - sope/commitdiff
added a first version of forking parallel child worker processes
authorhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Mon, 7 Mar 2005 16:18:33 +0000 (16:18 +0000)
committerhelge <helge@e4a50df8-12e2-0310-a44c-efbce7f8a7e3>
Mon, 7 Mar 2005 16:18:33 +0000 (16:18 +0000)
git-svn-id: http://svn.opengroupware.org/SOPE/trunk@625 e4a50df8-12e2-0310-a44c-efbce7f8a7e3

sope-appserver/NGObjWeb/ChangeLog
sope-appserver/NGObjWeb/Defaults.plist
sope-appserver/NGObjWeb/Version
sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpAdaptor.m

index 57fac19a249bc446e600fa16133072134e3d2d89..53856551e16a5a5deb29fa41f840857714b67ab8 100644 (file)
@@ -1,3 +1,12 @@
+2005-03-07  Helge Hess  <helge.hess@opengroupware.org>
+
+       * 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  <helge.hess@opengroupware.org>
        
        * WOWatchDogApplicationMainOSX.m: fixed some minor issues, still need
index c303f68b3d4cfb8504733a060cd0623f37a67af3..afb362e2351ff2f093fcbbb2a822ffb026838bc2 100644 (file)
@@ -56,6 +56,7 @@
   WOFormAlwaysPassDown                  = YES;
   WOFrameworksBaseURL                   = "/WebObjects/Frameworks";
   WOGenerateMissingResourceLinks        = NO;
+  WOHttpAdaptorForkCount                = 0;
   WOHttpAdaptorReceiveTimeout           = 120;
   WOHttpAdaptorSendTimeout              = 120;
   WOHttpAdaptor_LogStream               = NO;
index 25a3a660cb6daab215b41213901b2c2e861c1900..23c56cf3cfed9a19c466dc513a2ba6a79be7d6d1 100644 (file)
@@ -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
index bcb5e20f83f3dfa044cfea980c6be8bacf391c70..fcea5bad29ff838695710ad225bffca42a2d3a80 100644 (file)
@@ -45,6 +45,9 @@
 #include "WORecordRequestStream.h"
 #include "WOHttpTransaction.h"
 
+#include <unistd.h>
+#include <sys/wait.h>
+
 @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<NGSocketAddress>)addressFromDefaultsOfApplication:(WOCoreApplication*)_a{
+  id<NGSocketAddress> 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<NGSocketAddress>)addressFromArguments:(NSDictionary *)_args {
+  id<NGSocketAddress> 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];