how to improve the performance of Apache with mod_wsgi? - apache

Use Apache/2.4.12(Unix) and mod_wsgi-4.4.11 and blow configuration of apache/conf/extra:
//httpd-mpm.conf
<IfModule mpm_worker_module>
StartServers 3
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 0
</IfModule>
//httpd-vhosts.conf
WSGIRestrictEmbedded On
<VirtualHost *:443>
ServerName form.xxx.com
WSGIScriptAlias / /usr/local/apache/services/form/form.wsgi
WSGIDaemonProcess paymentform user=test processes=10 threads=5 display-name=%{GROUP} maximum-requests=100
WSGIApplicationGroup %{RESOURCE}
WSGIProcessGroup form
DocumentRoot /usr/local/apache/services/form
SSLEngine On
//any certification files
<Directory /usr/local/apache/services/form>
Require all granted
</Directory>
</VirtualHost>
In this configuration, I use Apache jmeter for testing.
GET : form.xxx.com //only return "index" string
Number of Threads(users):100
Ramp-up Period : 0
Loop count : 10
But result is..
samples: 1000
Average: 3069
Min : 13
Max : 22426
Std.Dev: 6671.693614549157
Error %: 10.0%
Throughput : 24.1/sec
KB/sec : 10.06/sec
AvgBytes : 428.5
During testing, raise connection refused or connection timeout and stop receving requests in 400~500 requests. Server cpu or memory is not full.
How to improve performance?
fix mpm worker configuration? or fix WSGI configuration in httpd-vhosts?
I modify httpd-mpm.conf below, but no difference.
<IfModule mpm_worker_module>
StartServers 10
ServerLimit 32
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 800
MaxConnectionsPerChild 0
</IfModule>

You have a number of things which are wrong in your configuration. One may be a cut and paste error. Another is a potential security issue. And one will badly affect performance.
The first is that you have:
WSGIProcessGroup form
If that is really want you have, then the web request wouldn't even be getting to the WSGI application and should return a 500 error response. If it isn't giving an error, then your request is being delegated to a mod_wsgi daemon process group not even mentioned in the above configuration. This would all come about as the value to WSGIProcessGroup doesn't match the name of the defined daemon process group specified by the WSGIDaemonProcess directive.
What you would have to have is:
WSGIProcessGroup paymentform
I suspect you have simply mucked up the configuration when you pasted it in to the question.
A related issue with delegation is that you have:
WSGIApplicationGroup %{RESOURCE}
This is what the default is anyway. There would usually never be a need to set it explicitly. What one would normally use if only delegating one WSGI application to a daemon process group is:
WSGIApplicationGroup %{GLOBAL}
This particular value forces the use of the main Python interpreter context of each process which avoids problems with some third party extension modules that will not work properly in sub interpreter contexts.
The second issue is a potential security issue. You have:
DocumentRoot /usr/local/apache/services/form
When using WSGIScriptAlias directive, there is no need to set DocumentRoot to be a parent directory of where your WSGI script file or source code for your application is.
The danger in doing this is that if WSGIScriptAlias was accidentally disabled, or changed to a sub URL, all your source code then becomes downloadable.
In short, let DocumentRoot default to the empty default directory for the whole server, or create an empty directory just for the VirtualHost and set it to that.
The final thing and which would drastically affect your performance is the use of maximum-requests option to WSGIDaemonProcess. You should never use maximum-requests in a production system unless you understand the implications and have a specific temporary need.
Setting this value and to a low value, means that the daemon processes will be killed off and restarted every 100 requests. Under a high volume of requests as with a benchmark, you would be constantly restarting your application processes.
The result of this would be increased CPU load and much slower response times, with potential for backlogging to the extent of very long response times due to overloading the server due to everything restarting all the time.
So, absolute first thing you should do is remove maximum-requests and you should see some immediate improvement.
You also have issues with process restarts in your Apache MPM settings. It is not as major as this only affects the Apache worker processes which are proxying requests, but it will also cause extra CPU usage, plus a potential need for a higher number of worker processes being required.
I have talked about the issue of Apache process churn due to MPM settings before in:
http://lanyrd.com/2013/pycon/scdyzk/
One final problem with your benchmarking is that your test, if all it is returning is the 'index' string from some simple hello world type program, is that it bears no relationship to your real world application.
Real applications are not usually so simple and time within the WSGI application is going to be much more due to template rendering, database access etc etc. This means the performance profile of a real application is going to be completely different and changes how you should configure the server.
In other words, testing with a hello world program is going to give you the completely wrong idea of what you need to do to configure the server appropriately. You really need to understand what the real performance profile of your application is under normal traffic loads and work from there. That is, hammering the server to the point of breaking is also wrong and not realistic.
I have been blogging on my blog site recently about how typical hello world tests people use are wrong, and give some examples of specific tests which show out how the performance of different WSGI servers and configurations can be markedly different. The point of that is to show that you can't base things off one simple test and you do need to understand what your WSGI application is doing.
In all of this, to really truly understand what is going on and how to tune the server properly, you need to use a performance monitoring solution which is built into the WSGI server and so can give insights into the different aspects of how it works and therefore what knobs to adjust. The blog posts are covering this also.

I encountered a similar problem as ash84 described, I used jmeter to test the performance and found the error % becomes non-zero when the jmeter thread number is set beyond some value (50 in my case).
After I watched Graham Dumpleton's talk, I realized it happens mainly because there are not enough spare MPM threads prepared for serving the upcoming burst jmeter requests. In this case, some jmeter requests are not served in the beginning, even though the number of MPM threads catched up later.
In short, setting MinSpareThreads to a larger value fixed my problem, I raised jmeter threads from 50 to 100 and get 0% error.
MinSpareThreads 120
MaxSpareThreads 150
MaxRequestWorkers 200
The number of WSGIDaemonProcess processes times the number of WSGIDaemonProcess threads doesn't have to be greater than the number of jmeter threads. But you may need to set them to higher values to make sure WSGIDaemonProcess could handle the requests quickly enough.

Related

Best Apache Configuration

Please, Can you help me for best Apache Configuration
I own the servers for files download, Download files by direct links
ex: domain.com/files.rar
Without programming or php function
The problem: Sometimes I having a high load or stop servers
For this can you help me for best Apache Configuration
Such as:
Server Limit
Max Clients
Max Requests Per Child
Keep-Alive
Keep-Alive Timeout
Max Keep-Alive Requests
Etc.
My servers with 4GB RAM and HDD drives, and 100Mb-ps and 1GBMb-ps
Thanks.
Separate Static and Dynamic Content
Use separate servers for static and dynamic content. Apache processes serving dynamic content will carry overhead and swell to the size of the content being served, never decreasing in size. Each process will incur the size of any loaded PHP or Perl libraries. A 6MB-30MB process size [or 10% of server's memory] is not unusual, and becomes a waist of resources for serving static content.
For a more efficient use of system memory, either use mod_proxy to pass specific requests onto another Apache Server, or use a lightweight server to handle static requests:
Nginx
lighttpd
Or use a front-end caching proxy such as Squid-Cache or Varnish-Cache
The Server handling the static content goes up front.
Note that configuration settings will be quite different between a dynamic content Server and a static content Server.
mod_deflate
Reduce bandwidth by 75% and improve response time by using mod_deflate.
LoadModule deflate_module modules/mod_deflate.so
<Location />
AddOutputFilterByType DEFLATE text/html text/plain text/css text/xml application/x-javascript
</Location>
Loaded Modules
Reduce memory footprint by loading only the required modules.
Some also advise to statically compile in the needed modules, over building DSOs (Dynamic Shared Objects). Very bad advice. You will need to manually rebuild Apache every time a new version or security advisory for a module is put out, creating more work, more build related headaches, and more downtime.
mod_expires
Include mod_expires for the ability to set expiration dates for specific content; utilizing the 'If-Modified-Since' header cache control sent by the user's browser/proxy. Will save bandwidth and drastically speed up your site for [repeat] visitors.
Note that this can also be implemented with mod_headers.
KeepAlive
Enable HTTP persistent connections to improve latency times and reduce server load significantly [25% of original load is not uncommon].
prefork MPM:
KeepAlive On
KeepAliveTimeout 2
MaxKeepAliveRequests 100
worker and winnt MPMs:
KeepAlive On
KeepAliveTimeout 15
MaxKeepAliveRequests 100
With the prefork MPM, it is recommended to set 'KeepAlive' to 'Off'. Otherwise, a client will tie up an entire process for that span of time. Though in my experience, it is more useful to simply set the 'KeepAliveTimeout' value to something very low [2 seconds seems to be the ideal value]. This is not a problem with the worker MPM [thread-based], or under Windows [which only has the thread-based winnt MPM].
With the worker and winnt MPMs, the default 15 second timeout is setup to keep the connection open for the next page request; to better handle a client going from link to link. Check logs to see how long a client remains on each page before moving on to another link. Set value appropriately [do not set higher than 60 seconds].
SymLinks
Make sure 'Options +FollowSymLinks -SymLinksIfOwnerMatch' is set for all directories. Otherwise, Apache will issue an extra system call per filename component to substantiate that the filename is NOT a symlink; and more system calls to match an owner.
<Directory />
Options FollowSymLinks
</Directory>
AllowOverride
Set a default 'AllowOverride None' for your filesystem. Otherwise, for a given URL to path translation, Apache will attempt to detect an .htaccess file under every directory level of the given path.
<Directory />
AllowOverride None
</Directory>
ExtendedStatus
If mod_status is included, make sure that directive 'ExtendedStatus' is set to 'Off'. Otherwise, Apache will issue several extra time-related system calls on every request made.
ExtendedStatus Off
Timeout
Lower the amount of time the server will wait before failing a request.
Timeout 45
If you are having load-problems with your apache setup, you could also consider migrating to another system. From my personal experience I would suggest you to try nginx to serve static files.

apache/mod_wsgi: How do I spawn specific number of processes?

How do I spawn a specific number of processes in Apache when using mod_wsgi with the WSGIDaemonProcess setting?
I have my VirtualHost setup with the following (as a test):
WSGIDaemonProcess webserver user=webserver group=webserver processes=24 threads=8 display-name=%{GROUP} python-path=/var/virtualenv/lib/python2.6/site-packages
While my httpd.conf is setup as follows:
<IfModule prefork.c>
StartServers 8
MinSpareServers 1
MaxSpareServers 8
ServerLimit 24
MaxClients 24
MaxRequestsPerChild 4000
</IfModule>
Note, that I'm running a very constrained 256MB server with PostgreSQL database installed as well.
However, system shows far more than 24 processes for apache (more than 30). I expected that if I set the ServerLimit to the same as processes in WSGIDaemonProcess it would run at the constant 24. However, there seems to be a bunch of spare processes running for unknown reasons?
The ServerLimit directive has got nothing to do with mod_wsgi daemon mode. The 'processes' option to WSGIDaemonProcess is what specifies how many daemon processes mod_wsgi will create. It is a static number and not a dynamic number so just set it to how many you need. For that number of threads per process, there is no point setting it to more that 'processes=3' to start with as you are limited to 24 concurrent requests in the Apache child worker processes which proxy requests to the mod_wsgi daemon processes, so not possible to handle any more requests than that.
In general, if you are running in a memory constrained environment, then you should not be using prefork MPM but worker MPM. Is there a reason you must, such as needing to run PHP code as well? If not, change the MPM used.
How else you could configure things really depends on your code, response times and request throughput, which only you know.

Why is concurrency level not as high as expected when load testing Apache2 with "ab" ?

I am running Apache2/PHP on my CentOS 5.8 VPS server (2GB RAM, 2GHz processor) and I tried to do a basic load test. Since I am using the standard installation of Apache I assume that prefork model is the one being used. Here is the config:
<IfModule prefork.c>
StartServers 20
MinSpareServers 5
MaxSpareServers 20
ServerLimit 256
MaxClients 256
MaxRequestsPerChild 4000
</IfModule>
I did a short test with ab:
ab -q -c 100 -n 10000 mysite.com
In the same time I was checking mysite.com/server-status and I've never seen the number of requests currently being processed exceeding 10. How is this possible ?
According to my calculations the number of concurrent request should have been more than 10, ideally 100. Am I missing something here or Apache 2 server-status is reporting wrong ?
Thank you all.
You are correct that you could see more than 10 requests. You could in fact get more 256 concurrent requests being processed.
However, there's not enough information here to say why you didn't see more than 10 connections. Here are some possibilities:
A slow connection to the host being tested could reduce the number of parallel connections.
A slow application behind the URL being tested could reduce the number of parallel connections
Limitations in the /client/ could limit the number of the parallel connections. "ab" should provide some reporting on what level of concurrency it was able to achieve.
You could accidentally be setting "MaxClients" to a lower value elsewhere in your Apache configuration
... or you could have some other Apache configuration problem.
To provide a more specific answer, you could consider posting complete-as-possible copies of your "ab" output and the entire Apache configuration.

Set mod_reqtimeout to unlimited time for a specific folder

I basically have two questions:
How do you set the RequestReadTimeout (in mod_reqtimeout), header and body time to: unlimited time
and
How do I apply that to a specific folder?
The default reqtimeout.conf is:
<IfModule reqtimeout_module>
RequestReadTimeout header=10-20,minrate=500
RequestReadTimeout body=10,minrate=500
</IfModule>
So that it would be something like:
<IfModule reqtimeout_module>
#Apply this to the /var/www/unlimitedtime folder
<Directory /var/www/unlimitedtime>
RequestReadTimeout header=unlimited,MinRate=0 body=unlimited,MinRate=0
</Directory>
</IfModule>
This doesn't work but it's just an example that maybe will make my question more clear.
Thx
Several tips from official documentation of top
RequestReadTimeout :
Context: server config, virtual host
That means this directive is a quite high level directive, you do not have the Location or Directory context here. In fact the timeouts are applied far before the web server can apply a directory decision on the request (the request is not received...), so it's quite normal. What it means is that you cannot apply this directive in a Directory, and there's nothing you can do for that, sorry.
type=timeout
The time in seconds allowed for reading all of the request headers or
body, respectively. A value of 0 means no limit.
So instead of using the 10-20 form simply set 0 and it becomes an unlimited timeout. Or at least that's what the documentation seems to imply. But that's a real nice way of making your webserver DOS-enabled. A few HTTP requests on the right url and you will get a nice Deny of Service, so I hope some other Timeout setting will override it (but maybe not, be careful) :-)

mod_wsgi and Django app periodically hang

I've got a large Django app. It has two Apache virtual hosts pointing to different settings files, so part of the application is accessible via one URL, and part via another. The Django app uses virtualenv.
mod_wsgi is configured to run in daemon mode with the following in Apache's VirtualHost block:
# domain 1:
WSGIDaemonProcess fc processes=5 threads=5 display-name=%{GROUP} \
user=nobody group=nobody
WSGIScriptAlias / /var/www/python/mine/apache/my.wsgi \
process-group=fc application-group=%{GROUP}
# different apache.conf file for domain 2:
WSGIDaemonProcess fm processes=5 threads=5 display-name=%{GROUP} \
user=nobody group=nobody
WSGIScriptAlias / /var/www/python/mine/apache/other.wsgi \
process-group=fm application-group=%{GROUP}
Every now and again while using the sites, a request will hang. It never completes. I have to use the browser's 'refresh' button to reload the page, and then the request normally works.
Apache itself runs in prefork mode and MaxRequestsPerChild is set to 0 because I've read that could be a problem. This happens often enough for it to be a potential problem - every 100 requests perhaps, something like that.
Has anyone got any idea why this is happening?
Thanks
That should be 'application-group=%{GLOBAL}' for WSGIDaemonProcess options, not '%{GROUP}'. The '%{GLOBAL}' is special and means the main Python interpreter. Using the main interpreter often gets around problems with third party C extension modules for Python which don't work in sub interpreters, including experiencing deadlocks. The '%{GROUP}' value is only relevant to the 'display-name' option.