#include <ApplicationPoolClientServer.h>
ApplicationPoolServer implements a client/server architecture for ApplicationPool. This allows one to use ApplicationPool in a multi-process environment (unlike StandardApplicationPool). The cache/pool data is stored in the server. Different processes can then access the pool through the server.
ApplicationPoolServer itself does not inherit ApplicationPool. Instead, it returns an ApplicationPool object via the connect() call. For example:
// Create an ApplicationPoolServer. ApplicationPoolServer server(...); // Now fork a child process, like Apache's prefork MPM eventually will. pid_t pid = fork(); if (pid == 0) { // Child process // Connect to the server. After connection, we have an ApplicationPool // object! ApplicationPoolPtr pool(server.connect()); // The child process doesn't run a server (only the parent process does) // so we call detach to free the server resources (things like file // descriptors). server.detach(); ApplicationPool::SessionPtr session(pool->get("/home/webapps/foo")); do_something_with(session); _exit(0); } else { // Parent process waitpid(pid, NULL, 0); }
Notice that ApplicationPoolServer does do not use TCP sockets at all, or even named Unix sockets, depite being a server that can handle multiple clients! So ApplicationPoolServer will expose no open ports or temporary Unix socket files. Only child processes are able to use the ApplicationPoolServer.
This is implemented through anonymous Unix sockets (socketpair()
) and file descriptor passing. It allows one to emulate accept()
. During initialization, ApplicationPoolServer creates a pair of Unix sockets, one called serverSocket
and the other called connectSocket
. There is a thread which continuously listens on serverSocket for incoming data. The data itself is not important, because it only serves to wake up the thread. ApplicationPoolServer::connect() sends some data through connectSocket, which wakes up the server thread. The server thread will then create a pair of Unix sockets. One of them is passed through serverSocket. The other will be handled by a newly created client thread. So the socket that was passed through serverSocket is the client's connection to the server, while the other socket is the server's connection to the client.
Note that serverSocket and connectSocket are solely used for setting up new connections ala accept(). They are not used for any actual data. In fact, they cannot be used in any other way without some sort of inter-process synchronization mechanism, because all child processes are connected to the same serverSocket. In contrast, ApplicationPoolServer::connect() allows one to setup a private communicate channel between the server and the current child process.
Also note that each client is handled by a seperate thread. This is necessary because ApplicationPoolServer internally uses StandardApplicationPool, and the current algorithm for StandardApplicationPool::get() can block (in the case that the spawning limit has been exceeded). While it is possible to get around this problem without using threads, a thread-based implementation is easier to write.
Public Member Functions | |
ApplicationPoolServer (const string &spawnServerCommand, const string &logFile="", const string &environment="production", const string &rubyCommand="ruby", const string &user="") | |
Create a new ApplicationPoolServer object. | |
ApplicationPoolPtr | connect () |
Connects to the server and returns a usable ApplicationPool object. | |
void | detach () |
Detach the server by freeing up some server resources such as file descriptors. |
Passenger::ApplicationPoolServer::ApplicationPoolServer | ( | const string & | spawnServerCommand, | |
const string & | logFile = "" , |
|||
const string & | environment = "production" , |
|||
const string & | rubyCommand = "ruby" , |
|||
const string & | user = "" | |||
) | [inline] |
Create a new ApplicationPoolServer object.
spawnServerCommand | The filename of the spawn server to use. | |
logFile | Specify a log file that the spawn server should use. Messages on its standard output and standard error channels will be written to this log file. If an empty string is specified, no log file will be used, and the spawn server will use the same standard output/error channels as the current process. | |
environment | The RAILS_ENV environment that all RoR applications should use. If an empty string is specified, the current value of the RAILS_ENV environment variable will be used. | |
rubyCommand | The Ruby interpreter's command. | |
user | The user that the spawn manager should run as. This parameter only has effect if the current process is running as root. If the empty string is given, or if the user is not a valid username, then the spawn manager will be run as the current user. |
SystemException | An error occured while trying to setup the spawn server or the server socket. | |
IOException | The specified log file could not be opened. | |
boost::thread_resource_error | A threading resource could not be allocated or initialized. |
ApplicationPoolPtr Passenger::ApplicationPoolServer::connect | ( | ) | [inline] |
Connects to the server and returns a usable ApplicationPool object.
All cache/pool data of this ApplicationPool is actually stored on the server and shared with other clients, but that is totally transparent to the user of the ApplicationPool object.
ApplicationPoolPtr pool = server.connect(); Application::SessionPtr session1 = pool->get(...); Application::SessionPtr session2 = pool->get(...);
Instead, one should call connect() multiple times:
ApplicationPoolPtr pool1 = server.connect(); Application::SessionPtr session1 = pool1->get(...); ApplicationPoolPtr pool2 = server.connect(); Application::SessionPtr session2 = pool2->get(...);
SystemException | Something went wrong. | |
IOException | Something went wrong. |
void Passenger::ApplicationPoolServer::detach | ( | ) | [inline] |
Detach the server by freeing up some server resources such as file descriptors.
This should be called by child processes that wish to use a server, but do not run the server itself.
This method may only be called once. The ApplicationPoolServer object will become unusable once detach() has been called.