PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/perl/dklab_realplexor.pl

https://github.com/DmitryKoterov/dklab_realplexor
Perl | 173 lines | 119 code | 25 blank | 29 comment | 12 complexity | 119b3ed0168e017c187dd9502d73ee54 MD5 | raw file
  1. #!/usr/bin/perl -w
  2. #@
  3. #@ Dklab Realplexor: Comet server which handles 1000000+ parallel browser connections
  4. #@ Author: Dmitry Koterov, dkLab (C)
  5. #@ GitHub: http://github.com/DmitryKoterov/
  6. #@ Homepage: http://dklab.ru/lib/dklab_realplexor/
  7. #@
  8. use strict;
  9. BEGIN {
  10. if (!eval('use EV; 1')) {
  11. print STDERR "Error: EV library is not found in your system:\n";
  12. print STDERR "http://search.cpan.org/~mlehmann/EV-3.9/EV.pm\n";
  13. print STDERR "You must install EV, e.g.:\n";
  14. print STDERR "# yum install gcc\n";
  15. print STDERR "# perl -MCPAN -e \"install EV\"\n";
  16. print STDERR " - or -\n";
  17. print STDERR "# apt-get install gcc\n";
  18. print STDERR "# perl -MCPAN -e \"install EV\"\n";
  19. exit(1);
  20. }
  21. }
  22. use File::Basename;
  23. use lib dirname(__FILE__);
  24. use Storage::DataToSend;
  25. use Storage::ConnectedFhs;
  26. use Storage::OnlineTimers;
  27. use Storage::PairsByFhs;
  28. use Realplexor::Event::Connection;
  29. use Realplexor::Event::Server;
  30. use Connection::Wait;
  31. use Connection::In;
  32. use Realplexor::Config;
  33. use Realplexor::Common;
  34. use Getopt::Long;
  35. use Realplexor::Event::Signal;
  36. # Main processing loop.
  37. sub mainloop {
  38. my $ulimit = `/bin/sh -c "ulimit -n"`; chomp $ulimit;
  39. Realplexor::Common::logger("Starting. Opened files limit (ulimit -n): $ulimit.");
  40. # Load configs.
  41. my $additional_conf = $ARGV[0];
  42. Realplexor::Config::load($additional_conf);
  43. # Turn on STDOUT buffering in non-verbose mode.
  44. $| = 0 if $CONFIG{VERBOSITY} < 3;
  45. # Initialize servers.
  46. my $wait = Realplexor::Event::Server->new(
  47. name => "WAIT",
  48. listen => $CONFIG{WAIT_ADDR},
  49. timeout => $CONFIG{WAIT_TIMEOUT},
  50. connectionclass => "Connection::Wait",
  51. logger => \&Realplexor::Common::logger,
  52. );
  53. my $in = Realplexor::Event::Server->new(
  54. name => "IN",
  55. listen => $CONFIG{IN_ADDR},
  56. timeout => 0, #$CONFIG{IN_TIMEOUT},
  57. connectionclass => "Connection::In",
  58. logger => \&Realplexor::Common::logger,
  59. );
  60. # Catch signals.
  61. use POSIX qw(SIGHUP SIGINT SIGPIPE);
  62. Realplexor::Event::Signal::create(SIGHUP, sub {
  63. Realplexor::Common::logger("SIGHUP received, reloading the config");
  64. my $low_level_opt = Realplexor::Config::reload($additional_conf);
  65. if ($low_level_opt) {
  66. Realplexor::Common::logger("Low-level option $low_level_opt is changed, restarting the script from scratch");
  67. exit();
  68. }
  69. return;
  70. });
  71. Realplexor::Event::Signal::create(SIGINT, sub {
  72. Realplexor::Common::logger("SIGINT received, exiting");
  73. exit();
  74. });
  75. Realplexor::Event::Signal::create(SIGPIPE, sub {
  76. Realplexor::Common::logger("SIGPIPE ignored");
  77. return 0;
  78. });
  79. # If running as root, SU to safe user.
  80. my $user = $CONFIG{SU_USER};
  81. if (!$> && $user) {
  82. my ($implemented,$name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = eval { (1, getpwnam($user)) };
  83. if ($implemented) {
  84. die "User $user is not found\n" if !defined $name;
  85. Realplexor::Common::logger("Switching current user to unprivileged \"$user\"");
  86. $) = $gid; $( = $gid;
  87. $> = $uid; $< = $uid;
  88. }
  89. }
  90. Realplexor::Event::Server::mainloop();
  91. }
  92. # Re-run self with high ulimit.
  93. Realplexor::Tools::rerun_unlimited();
  94. $0 = "dklab_realplexor"; # for killall in smoke tests
  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. }