PageRenderTime 23ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/dklab_realplexor.pl

https://github.com/isage/dklab_realplexor
Perl | 173 lines | 116 code | 25 blank | 32 comment | 12 complexity | 160bdb4f04217d713243c08621addd94 MD5 | raw file
  1. #!/usr/bin/perl -w
  2. ##
  3. ## dklab_realplexor: simple and lightweight HTTP persistent
  4. ## connection emulation for JavaScript. Tool to handle 1000000+
  5. ## parallel browser connections.
  6. ##
  7. ## version 2.x
  8. ## (C) dkLab, http://dklab.ru/lib/dklab_realplexor/
  9. ## Changelog: http://github.com/DmitryKoterov/dklab_realplexor/commits/master/
  10. ##
  11. use strict;
  12. BEGIN {
  13. if (!eval('use EV; 1')) {
  14. print STDERR "Error: EV library is not found in your system:\n";
  15. print STDERR "http://search.cpan.org/~mlehmann/EV-3.9/EV.pm\n";
  16. print STDERR "You must install EV, e.g.:\n";
  17. print STDERR "# yum install gcc\n";
  18. print STDERR "# perl -MCPAN -e \"install EV\"\n";
  19. print STDERR " - or -\n";
  20. print STDERR "# apt-get install gcc\n";
  21. print STDERR "# perl -MCPAN -e \"install EV\"\n";
  22. exit(1);
  23. }
  24. }
  25. use Storage::DataToSend;
  26. use Storage::ConnectedFhs;
  27. use Storage::OnlineTimers;
  28. use Storage::PairsByFhs;
  29. use Realplexor::Event::Connection;
  30. use Realplexor::Event::Server;
  31. use Connection::Wait;
  32. use Connection::In;
  33. use Realplexor::Config;
  34. use Realplexor::Common;
  35. use Getopt::Long;
  36. use Realplexor::Event::Signal;
  37. # Main processing loop.
  38. sub mainloop {
  39. my $ulimit = `/bin/sh -c "ulimit -n"`; chomp $ulimit;
  40. Realplexor::Common::logger("Starting. Opened files limit (ulimit -n): $ulimit.");
  41. # Load configs.
  42. my $additional_conf = $ARGV[0];
  43. Realplexor::Config::load($additional_conf);
  44. # Turn on STDOUT buffering in non-verbose mode.
  45. $| = 0 if $CONFIG{VERBOSITY} < 3;
  46. # Initialize servers.
  47. my $wait = Realplexor::Event::Server->new(
  48. name => "WAIT",
  49. listen => $CONFIG{WAIT_ADDR},
  50. timeout => $CONFIG{WAIT_TIMEOUT},
  51. connectionclass => "Connection::Wait",
  52. logger => \&Realplexor::Common::logger,
  53. );
  54. my $in = Realplexor::Event::Server->new(
  55. name => "IN",
  56. listen => $CONFIG{IN_ADDR},
  57. timeout => 0, #$CONFIG{IN_TIMEOUT},
  58. connectionclass => "Connection::In",
  59. logger => \&Realplexor::Common::logger,
  60. );
  61. # Catch signals.
  62. use POSIX qw(SIGHUP SIGINT SIGPIPE);
  63. Realplexor::Event::Signal::create(SIGHUP, sub {
  64. Realplexor::Common::logger("SIGHUP received, reloading the config");
  65. my $low_level_opt = Realplexor::Config::reload($additional_conf);
  66. if ($low_level_opt) {
  67. Realplexor::Common::logger("Low-level option $low_level_opt is changed, restarting the script from scratch");
  68. exit();
  69. }
  70. return;
  71. });
  72. Realplexor::Event::Signal::create(SIGINT, sub {
  73. Realplexor::Common::logger("SIGINT received, exiting");
  74. exit();
  75. });
  76. Realplexor::Event::Signal::create(SIGPIPE, sub {
  77. Realplexor::Common::logger("SIGPIPE ignored");
  78. return 0;
  79. });
  80. # If running as root, SU to safe user.
  81. my $user = $CONFIG{SU_USER};
  82. if (!$> && $user) {
  83. my ($implemented,$name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = eval { (1, getpwnam($user)) };
  84. if ($implemented) {
  85. die "User $user is not found\n" if !defined $name;
  86. Realplexor::Common::logger("Switching current user to unprivileged \"$user\"");
  87. $) = $gid; $( = $gid;
  88. $> = $uid; $< = $uid;
  89. }
  90. }
  91. Realplexor::Event::Server::mainloop();
  92. }
  93. # Re-run self with high ulimit.
  94. Realplexor::Tools::rerun_unlimited();
  95. # Chdir to script's directory.
  96. use FindBin qw($Bin);
  97. chdir($Bin);
  98. # Turn off buffering in debug log. (Load test says that there is
  99. # no difference in performance if bufferring is turned off or not.)
  100. $| = 1;
  101. # Turn on zombie auto-reaper.
  102. $SIG{CHLD} = 'IGNORE';
  103. # Handle all signals to call END{} block at the end.
  104. use sigtrap qw(die untrapped normal-signals);
  105. # Parse command-line args.
  106. my @save_argv = @ARGV;
  107. my $pid_file;
  108. GetOptions("p=s" => \$pid_file);
  109. # Load config: it is also needed by parent watchdog.
  110. my $additional_conf = $ARGV[0];
  111. Realplexor::Config::load($additional_conf, 1);
  112. # Save PID?
  113. if ($pid_file) {
  114. open(local *F, ">", $pid_file) or die "Cannot create $pid_file: $!\n";
  115. print F $$;
  116. close(F);
  117. }
  118. # Child PID.
  119. my $pid;
  120. # Run watchdog loop.
  121. while (1) {
  122. delete $SIG{HUP};
  123. $pid = fork();
  124. if (!defined $pid) {
  125. # Fork failed.
  126. Realplexor::Common::logger("fork() failed: $!");
  127. } elsif (!$pid) {
  128. # Child process.
  129. eval {
  130. @ARGV = @save_argv; # because GetOptions() modifies ARGV
  131. mainloop();
  132. };
  133. die $@ if $@;
  134. exit();
  135. }
  136. # Pass SIGHUP to children.
  137. $SIG{HUP} = sub {
  138. kill 1, $pid;
  139. };
  140. # Waid for child termination.
  141. Realplexor::Tools::wait_pid_with_memory_limit($pid, $CONFIG{MAX_MEM_MB});
  142. sleep(1);
  143. }
  144. # Called if process dies.
  145. END {
  146. return if !$pid; # children
  147. Realplexor::Tools::graceful_kill($pid, $pid_file);
  148. }