Distributed Microservices Architecture Episode 109: HTTP Cache Optimization, Nginx Proxy Configuration, Blue-Green Deployment, Jenkins One-Click Traffic Switching Script

Contact the author via WeChat: xiaoda0423

Repository address: https://webvueblog.github.io/JavaPlusDoc/

https://1024bat.cn/

๐Ÿš€ 1. Frontend and Backend Cache Strategy Coordination

๐Ÿ”ง 1. Frontend: Effectively Utilize Browser Cache

  • Strong Cache (from disk cache / memory cache)

    • The browser hits the strong cache, does not send a request, and directly uses the local cache
    • Suitable for static resources that do not change often: such as images, fonts, JS/CSS, etc.
    • Server response:

      Cache-Control: public, max-age=86400
      Expires: Wed, 16 Apr 2025 12:00:00 GMT
      
    • Frontend performance:

  • Negotiated Cache (304)

    • Server response:

      Last-Modified: Wed, 10 Apr 2025 06:00:00 GMT
      ETag: "abc123xyz"
      
    • The client sends the next request with:

      If-Modified-Since: ...
      If-None-Match: ...
      
    • If the resource has not changed, return 304 to save bandwidth; otherwise, return a new 200

  • Force Refresh (Ctrl+F5)

    • The browser automatically adds the request header:

      Cache-Control: no-cache
      Pragma: no-cache
      

๐Ÿง  2. Backend: Set Cache Response Headers Based on Resource Type

It is recommended to configure static resource strategies uniformly with Nginx, Spring Boot, or other backend frameworks.

โœ… Example Strategy (Nginx):

location ~* .(js|css|png|jpg|jpeg|gif|woff|woff2|svg|ico)$ {
    expires 30d;
    add_header Cache-Control "public, max-age=2592000, immutable";
}
location /api/ {
    expires off;
    add_header Cache-Control "no-store";
}

โœ… Example Strategy (Spring Boot):

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
            .addResourceLocations("classpath:/static/")
            .setCacheControl(CacheControl.maxAge(30, TimeUnit.DAYS).cachePublic());
    }
}

๐ŸŽฏ 2. Coordination Ideas

Resource Type Recommended Cache Strategy Reason/Explanation
JS/CSS <span>Cache-Control: max-age=31536000, immutable</span> Rarely updated, version number renaming
Images/Fonts <span>Cache-Control: max-age=31536000, immutable</span> Cache as long as it does not change
HTML <span>Cache-Control: no-cache</span> Needs to check for updates every time
API Requests <span>Cache-Control: no-store</span> Avoid caching sensitive data, high real-time requirements
JSON Configuration Files Optional negotiated cache (ETag + Last-Modified) Can negotiate cache if update frequency is low
Uploaded Files Usually not cached Private/sensitive resources

๐ŸŽจ 3. Combine Build Tool Versioning Strategy (Frontend Focus)

๐Ÿ’ก Principle:

During each build, resource files automatically add a hash value (e.g., <span>app.7ad32f.js</span>), strong cache time can be set long (e.g., one year),when resources are updated, the file name changes, and users naturally request the latest resources.

  • Common methods for Webpack/Vite/Rollup:
output: {
  filename: '[name].[contenthash].js'
}
  • HTML references automatically update the reference path, such as:
&lt;script src="/static/js/app.7ad32f.js"&gt;&lt;/script&gt;

๐Ÿ›  4. Points to Note

  1. ETag is recommended to be turned off in cluster deployments

  • The ETag generated by each node may be different, leading to cache misses โ†’ <span>nginx.conf</span>: <span>etag off;</span>
  • Avoid disabling cache on the frontend (Disable Cache)

    • Except for debugging and development, enabling it in production will greatly increase traffic
  • CDN distribution for image and font resources + permanent caching

    • Can even set:

      Cache-Control: max-age=315360000, immutable
      
  • Be cautious with cache headers for dynamic data

    • For example, user information, shopping cart, payment status, caching is not recommended

    Reasonably configure build and version strategies on the frontend, and finely control cache response headers on the backend.

    Level Optimization Measures
    Frontend hash file names, immutable tags, avoid misuse of no-cache
    Backend Extend cache for static resources, verify dynamic resources, turn off ETag (if necessary)
    Network Enable CDN caching, compress Gzip/Brotli, reduce RTT
    Business Aim for data decoupling (static vs dynamic), structural separation, atomic updates
    1. Prepare 3 Nginx instances: Simulate multi-layer proxy (Client โ†’ Proxy1 โ†’ Proxy2 โ†’ Backend)

    2. Configure test page:

    • Backend Nginx returns <span>$remote_addr</span><span>, </span><code><span>$http_x_forwarded_for</span><span>, </span><code><span>$http_x_real_ip</span><span> to verify IP forwarding.</span>
    • Also returns <span>$request_method</span><span>, </span><code><span>$request_uri</span><span> to verify </span><code><span>proxy_pass</span><span> and </span><code><span>rewrite</span><span> effects.</span>
    • Add resources with <span>ETag</span><span>, </span><code><span>Last-Modified</span><span>, </span><code><span>Cache-Control</span><span> headers to test caching strategies.</span>
  • Curl test command template:

    curl -I -H "Cache-Control: no-cache" http://localhost/static.js
    curl -I -H "If-None-Match: \"etag123\"" http://localhost/static.js
    curl -I -H "a_b: 123" http://localhost:81
    
  • Key configuration test points:

    • <span>proxy_pass</span><span> + URI rule differences</span>
    • <span>rewrite + break</span><span> impact on </span><code><span>proxy_pass</span>
    • <span>proxy_set_header</span><span> header inheritance and overriding mechanism</span>
    • <span>underscores_in_headers</span><span> switch impact on header variable naming</span>
    • Cache hits (304 vs 200 vs from disk cache)

    โœ… 1. Blue-Green Deployment

    ๐Ÿ” Core Idea:

    Deploy two versions simultaneously:

    • <span>blue</span><span>: Current stable online version</span>
    • <span>green</span><span>: Upcoming new version</span>

    Achieve rapid traffic switching through Nginx to avoid downtime.

    ๐Ÿ”ง Nginx Configuration Method (Blue-Green Environment Switching):

    upstream app_backend {
        server 10.0.0.1:8080;  # blue
        # server 10.0.0.2:8080;  # green (uncomment this line to go live, comment the above)
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://app_backend;
        }
    }
    

    ๐Ÿ” When switching versions: Simply modify the target IP of <span>upstream</span><span> and reload Nginx to complete seamless blue-green switching.</span>

    โœจ Practical Suggestions:

    • Use Jenkins/scripts for one-click traffic switching
    • Combine with health checks to avoid new version service unavailability

    โœ… 2. Canary Release

    ๐Ÿ” Core Idea:

    Only allow a portion of users to use the new version, gradually releasing traffic based on rules (such as IP, cookie, header, ratio, etc.).

    ๐ŸŽฏ Scenario 1: IP-based Canary Release

    map $remote_addr $gray_user {
        default         0;
        192.168.1.100   1;  # Specify user IP for canary
    }
    
    upstream app_v1 {
        server 10.0.0.1:8080; # Old version
    }
    
    upstream app_v2 {
        server 10.0.0.2:8080; # New version
    }
    
    server {
        listen 80;
        location / {
            if ($gray_user = 1) {
                proxy_pass http://app_v2;
            }
            proxy_pass http://app_v1;
        }
    }
    

    ๐ŸŽฏ Scenario 2: Cookie-based Canary Release

    map $http_cookie $gray_cookie {
        default     0;
        "~*gray_user=true"  1;
    }
    
    location / {
        if ($gray_cookie = 1) {
            proxy_pass http://app_v2;
        }
        proxy_pass http://app_v1;
    }
    

    ๐ŸŽฏ Scenario 3: Ratio-based Canary Release (Weighted Round Robin)

    upstream canary {
        server 10.0.0.1:8080 weight=9;  # v1 - 90%
        server 10.0.0.2:8080 weight=1;  # v2 - 10%
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://canary;
        }
    }
    

    โš ๏ธ Note: This method does not guarantee that “the same user will consistently hit the old or new version”.

    ๐Ÿ”ง Practical Tips for Nginx with Blue-Green/Canary Deployment:

    Function Point Technical Solution
    Precise User Canary <span>$remote_addr</span><span> / </span><code><span>$http_cookie</span><span> / </span><code><span>$http_user_agent</span>
    Dynamic Weight Traffic Switching Use nginx + consul/etcd + lua/openresty
    Real-time Upstream Switching Combine with <span>nginx reload</span><span> or </span><code><span>dynamic upstream</span> module
    Monitor Traffic Switching Effects Combine with <span>access_log</span><span> + log_format for log analysis</span>
    Health Check <span>proxy_next_upstream</span><span> + </span><code><span>max_fails</span><span> + </span><code><span>fail_timeout</span>

    โœ… Project Structure (Blue-Green Canary Nginx Example)

    nginx-canary-demo/
    โ”œโ”€โ”€ docker-compose.yml
    โ”œโ”€โ”€ nginx/
    โ”‚   โ”œโ”€โ”€ nginx.conf
    โ”‚   โ””โ”€โ”€ conf.d/
    โ”‚       โ””โ”€โ”€ default.conf
    โ”œโ”€โ”€ app-v1/  # Simulated blue version
    โ”‚   โ””โ”€โ”€ index.html
    โ”œโ”€โ”€ app-v2/  # Simulated green version
    โ”‚   โ””โ”€โ”€ index.html
    โ””โ”€โ”€ test/
        โ””โ”€โ”€ curl_test.sh
    

    ๐Ÿ“ฆ docker-compose.yml

    version: '3.8'
    
    services:
      nginx:
        image: nginx:latest
        volumes:
          - ./nginx/nginx.conf:/etc/nginx/nginx.conf
          - ./nginx/conf.d:/etc/nginx/conf.d
          - ./app-v1:/usr/share/nginx/html/v1
          - ./app-v2:/usr/share/nginx/html/v2
        ports:
          - "8080:80"
    
      app-v1:
        image: httpd:alpine
        volumes:
          - ./app-v1:/usr/local/apache2/htdocs/
        ports:
          - "8081:80"
    
      app-v2:
        image: httpd:alpine
        volumes:
          - ./app-v2:/usr/local/apache2/htdocs/
        ports:
          - "8082:80"
    

    ๐Ÿง  nginx/conf.d/default.conf

    # Blue-Green Canary Configuration
    map $http_cookie$is_gray_user {
        default                     0;
        "~*gray_user=true"         1;
    }
    
    upstream blue {
        server app-v1:80;
    }
    
    upstream green {
        server app-v2:80;
    }
    
    server {
        listen 80;
        server_name localhost;
    
        location / {
            if ($is_gray_user = 1) {
                proxy_pass http://green;
            }
            proxy_pass http://blue;
        }
    }
    

    ๐Ÿ”ง nginx/nginx.conf

    worker_processes 1;
    events { worker_connections 1024; }
    
    http {
        include       mime.types;
        default_type  text/html;
        sendfile        on;
        keepalive_timeout  65;
    
        include /etc/nginx/conf.d/*.conf;
    }
    

    ๐Ÿ“„ app-v1/index.html

    &lt;!DOCTYPE html&gt;
    &lt;html&gt;
    &lt;head&gt;&lt;title&gt;Blue Version&lt;/title&gt;&lt;/head&gt;
    &lt;body style="background-color:lightblue;"&gt;
    &lt;h1&gt;Blue Version: v1&lt;/h1&gt;
    &lt;/body&gt;
    &lt;/html&gt;
    

    ๐Ÿ“„ app-v2/index.html

    &lt;!DOCTYPE html&gt;
    &lt;html&gt;
    &lt;head&gt;&lt;title&gt;Green Version&lt;/title&gt;&lt;/head&gt;
    &lt;body style="background-color:lightgreen;"&gt;
    &lt;h1&gt;Green Version: v2 (Canary User Exclusive)&lt;/h1&gt;
    &lt;/body&gt;
    &lt;/html&gt;
    

    ๐Ÿงช test/curl_test.sh

    #!/bin/bash
    
    echo "== Regular User Access =="
    curl -s http://localhost:8080 | grep h1
    
    echo "== Canary User Access =="
    curl -s -H "Cookie: gray_user=true" http://localhost:8080 | grep h1
    

    ๐Ÿš€ Usage Instructions

    1. Start the project:
    docker-compose up --build
    
    1. Access test:

    • Regular access: http://localhost:8080
    • Canary access: <span>curl -H "Cookie: gray_user=true" http://localhost:8080</span>
  • Run the test script:

  • chmod +x test/curl_test.sh
    ./test/curl_test.sh
    

    ๐Ÿงฉ Further Advanced Suggestions

    Function Method
    Dynamic Traffic Ratio Adjustment Nginx + Lua + Redis to control ratios
    Weighted Canary (10% to green) <span>upstream weight</span> + sticky session
    Path/UA/Referer-based Canary Use <span>$http_user_agent</span><span> or </span><code><span>$request_uri</span><span> to map</span>

    ๐ŸŒˆ Scenario Review: Blue-Green Environment + Canary Traffic Switching

    • Blue (Current Stable)
    • Green (New Version)
    • Default traffic goes to Blue
    • When Jenkins publishes, first check Green’s health, then switch traffic
    • Traffic switching can be gradually adjusted or done in one click

    ๐Ÿ› ๏ธ 1. Nginx Target Configuration (Dynamic Upstream Switching)

    We achieve this through symbolic links + reload:

    /etc/nginx/conf.d/default.conf  -> points to: conf.d/blue.conf or conf.d/green.conf
    

    For example:

    <span>conf.d/upstream-blue.conf</span>

    upstream backend {
        server app-v1:80;
    }
    

    <span>conf.d/upstream-green.conf</span>

    upstream backend {
        server app-v2:80;
    }
    

    <span>conf.d/location.conf</span>

    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
    

    ๐Ÿงช 2. Health Check Script (<span>health_check.sh</span>)

    #!/bin/bash
    
    TARGET_URL=$1
    RETRY=5
    
    for i in $(seq 1 $RETRY); do
    echo"Health check [$i/$RETRY]: $TARGET_URL"
      code=$(curl -s -o /dev/null -w "%{http_code}"
    $TARGET_URL)
    if [ "$code" == "200" ]; then
        echo"โœ… Health check success."
        exit 0
    fi
      sleep 2
    done
    
    echo"โŒ Health check failed."
    exit 1
    

    ๐Ÿš€ 3. One-Click Traffic Switching Script (<span>switch_traffic.sh</span>)

    #!/bin/bash
    
    TARGET=$1# blue or green
    NGINX_DIR="/etc/nginx/conf.d"
    LINK="$NGINX_DIR/default.conf"
    UPSTREAM="$NGINX_DIR/upstream-${TARGET}.conf"
    
    if [ "$TARGET" != "blue" ] &amp;&amp; [ "$TARGET" != "green" ]; then
    echo"Usage: $0 [blue|green]"
    exit 1
    fi
    
    # Health check
    bash ./health_check.sh "http://127.0.0.1:808${TARGET/blue/1}${TARGET/green/2}" || exit 1
    
    # Switch upstream
    rm -f $LINK
    ln -s $UPSTREAM$LINK
    
    # Reload Nginx
    nginx -s reload
    echo"โœ… Successfully switched to $TARGET"
    

    ๐Ÿงฑ 4. Jenkins Process (Freestyle Job / Pipeline)

    Method 1:Freestyle Job + Parameters

    • Add build parameters:<span>TARGET_ENV</span><span> (value is </span><code><span>blue</span><span> or </span><code><span>green</span><span>)</span>
    • Build steps:
    cd /your/nginx-deploy-dir
    git pull
    chmod +x switch_traffic.sh
    ./switch_traffic.sh $TARGET_ENV
    

    Method 2:Pipeline Example

    pipeline {
        agent any
        parameters {
            choice(name: 'TARGET', choices: ['blue', 'green'], description: 'Select the version to switch traffic to')
        }
        stages {
            stage('Switch Traffic') {
                steps {
                    sh '''
                    cd /your/nginx-deploy-dir
                    git pull
                    ./switch_traffic.sh $TARGET
                    '''
                }
            }
        }
    }
    

    ๐Ÿ”’ Stability Assurance (Recommended to Pair)

    Function Technical Points
    Health Check curl + retry
    Canary Switching (10%) Nginx map + $request_id hash canary
    Rollback Switch soft link back to blue
    Real-time Monitoring Prometheus + Grafana
    Automatic Alarm for Exceptions Jenkins combined with DingTalk/FeiShu

    The main configuration file for Nginx (nginx.conf)

    ๐Ÿ”ง <span>worker_processes 1;</span>

    • Start 1 worker process (the core process handling requests)
    • Usually set to <span>auto</span><span> (automatically allocated based on CPU core count)</span>

    โš™๏ธ <span>events { worker_connections 1024; }</span>

    This is the event module that controls the number of connections each worker can handle simultaneously:

    • <span>worker_connections 1024</span>: A worker can handle a maximum of 1024 concurrent connections
    • Theoretical maximum concurrency:<span>worker_processes ร— worker_connections</span> (but limited by system ulimit)

    ๐ŸŒ <span>http { ... }</span>

    The core configuration block for Nginx HTTP service, containing all web server configurations.

    Explanation of each line:

    ๐Ÿ“Ž <span>include mime.types;</span>

    • Import the <span>mime.types</span><code><span> file, telling Nginx the corresponding </span><code><span>Content-Type</span><span> for various file extensions (e.g., </span><code><span>.html</span><span> is </span><code><span>text/html</span><span>))</span>

    ๐Ÿ“Ž <span>default_type text/html;</span>

    • If no suitable mime type is found, default to <span>text/html</span>

    ๐Ÿš€ <span>sendfile on;</span>

    • Enable zero copy, optimizing file transfer efficiency
    • Used for static resource scenarios (images, videos, compressed files, etc.)

    ๐Ÿ” <span>keepalive_timeout 65;</span>

    • TCP keep-alive time, in seconds
    • If no request is made within 65 seconds, the connection is closed to save resources
    • The default value is <span>75</span>, setting it to <span>65</span> indicates an earlier release

    ๐Ÿ“ <span>include /etc/nginx/conf.d/*.conf;</span>

    • Import all <span>/etc/nginx/conf.d/</span><span> directory's </span><code><span>.conf</span><span> files</span>
    • All <span>server {}</span><span> or </span><code><span>location {}</span><span> blocks are usually written here</span>

    Example:

    /etc/nginx/
    โ”œโ”€โ”€ nginx.conf          # Main configuration
    โ””โ”€โ”€ conf.d/
        โ”œโ”€โ”€ app1.conf       # Site 1
        โ”œโ”€โ”€ app2.conf       # Site 2
    

    ๐Ÿ“ Suggested Project Structure

    deploy/
    โ”œโ”€โ”€ nginx/
    โ”‚   โ”œโ”€โ”€ blue.conf          # Blue environment configuration
    โ”‚   โ”œโ”€โ”€ green.conf         # Green environment configuration
    โ”‚   โ””โ”€โ”€ main.conf          # Entry nginx configuration (references upstream)
    โ”œโ”€โ”€ scripts/
    โ”‚   โ””โ”€โ”€ switch_env.sh      # Traffic switching script (blue -> green / green -> blue)
    

    ๐Ÿงฉ <span>main.conf</span> (Nginx main configuration referencing upstream)

    # main.conf (loaded into nginx.conf or conf.d/default.conf)
    
    upstream backend {
        include /etc/nginx/upstream/active.conf;
    }
    
    server {
        listen 80;
    
        location / {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    
        location = /healthz {
            return 200 'ok';
        }
    }
    

    ๐ŸŸฆ <span>blue.conf</span>

    # blue.conf
    server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
    

    ๐ŸŸฉ <span>green.conf</span>

    # green.conf
    server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
    

    ๐Ÿ”— <span>active.conf</span> (symbolic link)

    # Initial environment is blue
    ln -s /etc/nginx/upstream/blue.conf /etc/nginx/upstream/active.conf
    

    ๐Ÿ› ๏ธ <span>switch_env.sh</span> (script for one-click traffic switching)

    #!/bin/bash
    
    UPSTREAM_DIR="/etc/nginx/upstream"
    CURRENT=$(readlink "$UPSTREAM_DIR/active.conf")
    
    if [[ $CURRENT == *"blue.conf" ]]; then
        NEW_TARGET="green.conf"
    else
        NEW_TARGET="blue.conf"
    fi
    
    echo"[INFO] Switching to environment: $NEW_TARGET"
    
    # Update symbolic link
    ln -sf "$UPSTREAM_DIR/$NEW_TARGET""$UPSTREAM_DIR/active.conf"
    
    # Health check
    echo"[INFO] Performing health check..."
    
    CHECK_URL="http://127.0.0.1/healthz"
    sleep 1
    HEALTH=$(curl -s --max-time 3 "$CHECK_URL")
    
    if [[ "$HEALTH" == "ok" ]]; then
        echo"[INFO] Health check passed, reloading Nginx..."
        nginx -s reload
        echo"[OK] Traffic switch successful! Current environment: $NEW_TARGET"
    else
        echo"[ERROR] Health check failed, rolling back..."
        # Rollback
        ln -sf "$CURRENT""$UPSTREAM_DIR/active.conf"
        nginx -s reload
        echo"[ROLLBACK] Rolled back to $CURRENT"
        exit 1
    fi
    

    โœ… Usage Instructions

    # Execute in Jenkins
    bash /path/to/scripts/switch_env.sh
    

    ๐Ÿ’ก Optional Enhancements

    Function Recommended Method
    Automatic Canary Ratio Switching Nginx <span>weight</span> parameter + hash
    Multi-Version Health Checks <span>/healthz</span> with version parameters
    Load Testing <span>wrk</span> / <span>ab</span> / <span>hey</span> tools
    Visual State Switching Combine with <span>Nginx status + Lua</span>

    Leave a Comment