Scaling a PHP Application: Optimizing Apache2 to Handle 500 Concurrent Users
Nov 28, 2024
3 min read
10 views
Share

By admin

We were tasked with optimizing a PHP application running on an Apache2 server to handle a significantly higher number of concurrent users efficiently. The primary goal was to identify the upper limit of the current configuration and implement optimizations to maximize throughput, ensuring stable performance under load.

Problem:
Initial load testing, conducted using BlazeMeter (leveraging JMeter), revealed that the default Apache2 configuration allowed only about 100 concurrent users per application instance before reaching performance bottlenecks. This was insufficient for the application’s requirements.

Identifying the Bottleneck:
Our analysis pointed to two main areas of concern:

  1. Apache2 Configuration: Default settings for connection handling (MaxKeepAliveRequests and KeepAliveTimeout) and request processing modules were limiting performance.
  2. Memory Management: Apache2 slow resource release for connections was preventing new clients from being served promptly, leading to queue build-ups and increased response times.

Solution Implementation:
We approached the optimization in several steps:

  1. Adjusting Apache2 Connection Settings:
    Default settings:

    MaxKeepAliveRequests 100
    KeepAliveTimeout 5

    Optimized settings:

    MaxKeepAliveRequests 2000
    KeepAliveTimeout 2
    
    

    These changes allowed clients to perform more requests before connection termination while also reducing the timeout, freeing server resources more quickl

  2. Tuning Apache2 Process Management:
    Default process settings:

    StartServers 5
    MinSpareServers 5
    MaxSpareServers 10
    MaxRequestWorkers 150
    MaxConnectionsPerChild 0

    Optimized settings:

    StartServers 300
    MinSpareServers 75
    MaxSpareServers 150
    ServerLimit 1500
    MaxRequestWorkers 1500
    MaxConnectionsPerChild 15000

     

    These adjustments enabled Apache2 to handle a much larger number of concurrent processes, leveraging the server’s available memory more effectively. However, we carefully monitored memory usage to avoid out-of-memory (OOM) issues.

  3. Database Tuning:
    We examined the database configuration to ensure it supported a high number of concurrent connections. Manual adjustments were made to the connection limits where the DB engine did not automatically optimize based on the instance type.
  4. Testing and Validation:
    Using BlazeMeter Recorder, we generated test scenarios and executed them through Taurus. These tests validated the impact of the optimizations. Additionally, we ensured no intermediary (e.g., AWS Load Balancer) interfered with the load-testing results, as some intermediaries block a high volume of requests for security reasons.
  5. Addressing Other Factors:
    • File System: We optimized file system usage, ensuring directories with a large number of small files were managed appropriately to avoid performance degradation.
    • Application Code: Developers also made incremental improvements to the PHP codebase for better efficiency.

Results:
The optimized setup allowed the application to handle up to 500 concurrent users, a fivefold increase compared to the initial configuration. This result came with a trade-off of increased memory usage, but it ensured stable performance without users experiencing gateway timeouts or long wait times.

Conclusion:
This project provided valuable insights into Apache2 server tuning, load testing, and holistic performance optimization. It highlighted the importance of iterative testing, balancing resource utilization, and addressing bottlenecks across multiple layers, from server configurations to application design.