Home » Developing U++ » U++ Developers corner » SCGI Class
| SCGI Class [message #28120] |
Tue, 17 August 2010 16:35  |
jeremy_c
Messages: 175 Registered: August 2007 Location: Ohio, USA
|
Experienced Member |
|
|
I have created a SCGI class. SCGI is a more simple FastCGI interface. It allows the application to persist between connections, thus respond very quick unlike a CGI program that has to be loaded, initialized, executed and closed for each request.
This really comes in handy when there are expensive start up costs such as a database connection.
An example application:
#include <SCGI/SCGI.h>
class HelloWebApp : public ScgiServer {
public:
HelloWebApp() : ScgiServer(8787)
{
WhenRequest = HandleRequest;
}
void HandleRequest()
{
clientSocket.Write("Content-Type: text/plain\r\n\r\n");
clientSocket.Write(Format("Hello, %s!", query["NAME"]));
}
};
CONSOLE_APP_MAIN
{
HelloWebApp app;
app.Run();
}
There are other callbacks such as WhenAccepted, WhenClosed. "query" is public and is an instance of HttpQuery that is automatically populated. "map" is a VectorMap<String,String> that is also automatically populated with the server variables that are passed such as REQUEST_URI, SERVER_NAME, etc...
On my NetBook (1.6ghz Atom) the above SCGI app runs ~1200 requests a second. A static hello.txt file is ~1900 requests a second.
I still have a few loose ends to wrap up (post form data, some more general testing) but wondering if anyone else would find this useful and how to share it?
Oh... most web servers (Apache included) have a "mod_scgi" to interface with this type of application. The applications need not reside on the same computer as the web server, thus they can be distributed and offer load balancing. Advanced servers like Apache can do load balancing themselves internally knowing which SCGI app is in use and which one is not.
For anyone who wants to know more about the SCGI protocol: http://python.ca/scgi/protocol.txt
Jeremy
[Updated on: Tue, 17 August 2010 16:37] Report message to a moderator
|
|
|
|
|
|
| Re: SCGI Class [message #28159 is a reply to message #28120] |
Thu, 19 August 2010 15:32   |
jeremy_c
Messages: 175 Registered: August 2007 Location: Ohio, USA
|
Experienced Member |
|
|
Ok, I am attaching the Scgi package as well as a ScgiHello example. It now handles parsing query string, post data and server variables internally. There are no docs yet but it's use is pretty simple to figure out. The ScgiHello example helps w/this as well.
Once it gets a review or two, I'll make any corrections and start documenting. The ScgiHello example includes a sample LigHTTPD configuration file that should run out of the box.
It does use some signal handling which I have not tested on Windows.
I would like to upload this and begin to maintain this in Bazaar. How does one go about doing that? (Oh, I have the MultipartForm class that I added to Web/util.h/cpp that I think would be helpful for others).
Jeremy
[Updated on: Thu, 19 August 2010 15:34] Report message to a moderator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Re: SCGI Class [message #29095 is a reply to message #29088] |
Mon, 04 October 2010 23:54   |
jeremy_c
Messages: 175 Registered: August 2007 Location: Ohio, USA
|
Experienced Member |
|
|
| luzr wrote on Mon, 04 October 2010 14:28 |
Well, perhaps you could test the svn access rights and provide this change (and to reference example too)? 
|
No problem, it's working now (didn't commit yet). Is there a naming convention I should follow in regards to the virtual events? For example, WhenAccepted... OnAccept ?
Jeremy
[Updated on: Mon, 04 October 2010 23:54] Report message to a moderator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Re: SCGI Class [message #31759 is a reply to message #31603] |
Thu, 24 March 2011 22:44   |
Mindtraveller
Messages: 917 Registered: August 2007 Location: Russia, Moscow rgn.
|
Experienced Contributor |

|
|
I plan using SCGI server class for potentially high-load service and so I make some tests first.
Simple example is created:#include <Core/Core.h>
#include <Web/Web.h>
using namespace Upp;
class MyScgiServer : public ScgiServer
{
public:
MyScgiServer() :ScgiServer(7000) {}
virtual void OnRequest()
{
Write("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, world!");
}
private:
};
CONSOLE_APP_MAIN
{
MyScgiServer server;
server.Run();
Cout() << "cleanup exit";
}
From the first glance, it works well, at least in junction with nginx/mod_scgi. You may even try no-so-high-load tests which will give you good results:
| Quote: | $ ab -t 10 -c 8 localhost:7000/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Completed 50000 requests
Finished 50000 requests
Server Software:
Server Hostname: localhost
Server Port: 7000
Document Path: /
Document Length: 13 bytes
Concurrency Level: 8
Time taken for tests: 6.806 seconds
Complete requests: 50000
Failed requests: 0
Write errors: 0
Total transferred: 2900000 bytes
HTML transferred: 650000 bytes
Requests per second: 7346.41 [#/sec] (mean)
Time per request: 1.089 [ms] (mean)
Time per request: 0.136 [ms] (mean, across all concurrent requests)
Transfer rate: 416.11 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 18
Processing: 0 1 1.1 1 48
Waiting: 0 1 0.8 1 46
Total: 0 1 1.2 1 56
Percentage of the requests served within a certain time (ms)
50% 1
66% 1
75% 1
80% 1
90% 1
95% 1
98% 2
99% 2
100% 56 (longest request)
|
The bad thing is that if you try to test with concurrency > 10, your test will fail:
| Quote: | $ ab -t 10 -c 9 localhost:7000/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
apr_socket_recv: Connection reset by peer (54)
Total of 13 requests completed
|
I started investigation on the source of the problem. If you have any clues, you are welcome.
|
|
|
|
| Re: SCGI Class [message #31766 is a reply to message #31759] |
Fri, 25 March 2011 16:32   |
Mindtraveller
Messages: 917 Registered: August 2007 Location: Russia, Moscow rgn.
|
Experienced Contributor |

|
|
OK, the problem is solved for now.
To support multiple connections on high-load projects we just need to increase listen_count parameter for server socket. So I propose a little update to ScgiServer class:
void ScgiServer::Run(int listenCount /*= 10*/)
{
ServerSocket(serverSock, port, false, listenCount);
...
That's it. With this little patch scgi server withListenCount = 10000 handles more than 1500 connection requests per second and processing more than 8000 sessions simultaniously.
[Updated on: Fri, 25 March 2011 19:07] Report message to a moderator
|
|
|
|
|
|
|
|
|
|
| Re: SCGI Class [message #31778 is a reply to message #31769] |
Sat, 26 March 2011 23:00   |
Mindtraveller
Messages: 917 Registered: August 2007 Location: Russia, Moscow rgn.
|
Experienced Contributor |

|
|
| zsolt wrote on Sat, 26 March 2011 00:55 | BTW it will not solve the problem of long running SQL queries or doing some slow communication in the scgi process.
In such situations you will have to start a lot of scgi processes to be able to handle the traffic.
Increasing that number, just allows the scgi process to have a large backlog.
|
What if in the main cycle the the newly created client socket is processed in another thread while server socket is free to accept more connections?
while (run)
{ if (serverSocket.Accept(&clientSocket)
{
GetThreadFromPoolAndProcess(clientSocket);
}
}
[Updated on: Sat, 26 March 2011 23:47] Report message to a moderator
|
|
|
|
| Re: SCGI Class [message #31779 is a reply to message #31778] |
Sun, 27 March 2011 06:54   |
nlneilson
Messages: 644 Registered: January 2010 Location: U.S. California. Mojave &...
|
Contributor |
|
|
| Mindtraveller wrote on Sat, 26 March 2011 23:00 | What if in the main cycle the the newly created client socket is processed in another thread while server socket is free to accept more connections?
while (run)
{ if (serverSocket.Accept(&clientSocket)
{
GetThreadFromPoolAndProcess(clientSocket);
}
}
|
That seems like the logical way.
My Upp apps as clients interact with Java apps/server.
New thread/socket for each client.
// The body of the server thread. Loop forever, listening for and
// accepting connections from clients. For each connection,
// create a Connection object to handle communication through the
// new Socket.
public void run() {
try {
while (true) {
Socket client_socket = listen_socket.accept();
Connection c = new Connection(client_socket);
}
} catch (IOException e) {
fail(e, "Exception while listening for connections");
}
}
}
I did not write the server code but just followed an example.
Neil
[Updated on: Sun, 27 March 2011 07:15] Report message to a moderator
|
|
|
|
|
|
|
|
| Re: SCGI Class [message #31787 is a reply to message #31781] |
Sun, 27 March 2011 22:51   |
zsolt
Messages: 702 Registered: December 2005 Location: Budapest, Hungary
|
Contributor |
|
|
| mirek wrote on Sun, 27 March 2011 10:02 |
| Mindtraveller wrote on Sat, 26 March 2011 18:00 |
| zsolt wrote on Sat, 26 March 2011 00:55 | BTW it will not solve the problem of long running SQL queries or doing some slow communication in the scgi process.
In such situations you will have to start a lot of scgi processes to be able to handle the traffic.
Increasing that number, just allows the scgi process to have a large backlog.
|
What if in the main cycle the the newly created client socket is processed in another thread while server socket is free to accept more connections?
while (run)
{ if (serverSocket.Accept(&clientSocket)
{
GetThreadFromPoolAndProcess(clientSocket);
}
}
|
Actually, this can be quite nicely with something like
while(run)
{ if (serverSocket.Accept(&clientSocket) {
DoWork();
}
}
and then simply starting several threads to run this loop (with single clientSocket). As accept is reentrant and MT safe, it would return only for single thread running, thus managing the thread pool.
|
This is a very elegant way to solve the problem using threads.
My only problem with MT in situations like this is, that a few things in Upp are global, such as lenguage-dependant setups (e.g. date or number formatting).
It is a common requirement now from a web based app to show content in the user's language, so I think multi process arrangement would be better.
Or is it somehow possible to make these global Upp settings thread local?
|
|
|
|
| Re: SCGI Class [message #31791 is a reply to message #31787] |
Mon, 28 March 2011 08:54   |
 |
mirek
Messages: 14290 Registered: November 2005
|
Ultimate Member |
|
|
| zsolt wrote on Sun, 27 March 2011 16:51 |
| mirek wrote on Sun, 27 March 2011 10:02 |
| Mindtraveller wrote on Sat, 26 March 2011 18:00 |
| zsolt wrote on Sat, 26 March 2011 00:55 | BTW it will not solve the problem of long running SQL queries or doing some slow communication in the scgi process.
In such situations you will have to start a lot of scgi processes to be able to handle the traffic.
Increasing that number, just allows the scgi process to have a large backlog.
|
What if in the main cycle the the newly created client socket is processed in another thread while server socket is free to accept more connections?
while (run)
{ if (serverSocket.Accept(&clientSocket)
{
GetThreadFromPoolAndProcess(clientSocket);
}
}
|
Actually, this can be quite nicely with something like
while(run)
{ if (serverSocket.Accept(&clientSocket) {
DoWork();
}
}
and then simply starting several threads to run this loop (with single clientSocket). As accept is reentrant and MT safe, it would return only for single thread running, thus managing the thread pool.
|
This is a very elegant way to solve the problem using threads.
My only problem with MT in situations like this is, that a few things in Upp are global, such as lenguage-dependant setups (e.g. date or number formatting).
It is a common requirement now from a web based app to show content in the user's language, so I think multi process arrangement would be better.
Or is it somehow possible to make these global Upp settings thread local?
|
I guess that while there is no direct support, it is in fact quite easy to do in app, as there is:
String GetLngString(int lang, const char *id);
-> #undef t_, replace it with your own version which is thread local... Or perhaps just use 'lang' parameter.
|
|
|
|
| Re: SCGI Class [message #31820 is a reply to message #31791] |
Tue, 29 March 2011 21:27   |
zsolt
Messages: 702 Registered: December 2005 Location: Budapest, Hungary
|
Contributor |
|
|
Thanks, but I think, there are problems whith some code in Core like this:
static char s_date_format[64] = "%2:02d/%3:02d/%1:4d";
void SetDateFormat(const char *fmt)
{
strncpy(s_date_format, fmt, 63);
}
String Format(Date date) {
String s;
if(IsNull(date))
return String();
return Format(s_date_format, date.year, date.month, date.day, DayOfWeek(date));
}
|
|
|
|
|
|
Goto Forum:
Current Time: Tue Apr 28 05:27:42 GMT+2 2026
Total time taken to generate the page: 0.01317 seconds
|