Administrator
发布于 2025-10-16 / 2 阅读
0
0

双机房双活部署方案nginx+lua方式

背景:

1.双机房网络互通,数据库互通,redis不互通。
2.通过会话header里面的路由参数判断奇数调转机房一,偶数条状机房二。机房一,机房二应用服务做故障转移
nginx+lua+java+vue方式实现

nginx 配置:

机房一和机房二 nginx.conf 只需要修改 app-server 里面的主节点和备用节点互换

# nginx.conf
worker_processes  1;
events {
    worker_connections 1024;
}

http {
    upstream datacenter1 {
        server 192.168.10.100:8888;
    }
    
    upstream datacenter2 {
        server 192.168.10.120:8888;
    }
  
    upstream app-server {
       # app1-maser 机房一节点 
       server 192.168.10.101:8081 max_fails=3 fail_timeout=30s;
       server 192.168.10.102:8081 max_fails=3 fail_timeout=30s;
       
       # app2-backup 机房二节点 
       server 192.168.10.121:8081 backup;
       server 192.168.10.122:8081 backup;
    }

    server {
        listen 8080;
        location / {
            access_by_lua_block {
                local header_val = ngx.req.get_headers()["X-Route-Key"]
                if not header_val then
                    ngx.status = 400
                    ngx.say("Missing X-Route-Key header")
                    ngx.exit(400)
                end

                -- 尝试转为数字
                local num = tonumber(header_val)
                if not num then
                    ngx.status = 400
                    ngx.say("Invalid number in X-Route-Key")
                    ngx.exit(400)
                end

                -- 奇数去机房一,偶数去机房二
                if num % 2 == 1 then
                   ngx.exec('@proxy_cd1');
                else
                   ngx.exec('@proxy_cd2');
                end
            }
        }
        
        location @proxy_cd1 {
        	proxy_pass_header X-Route-Key;
         	proxy_pass http://proxy_cd1;
         	proxy_set_upstream error timout http_502;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-DataCenter $http_x_datacenter;
            add_header Access-Control-Expose-Headers "X-Route-Key";
        }
        
        location @proxy_cd2 {
            proxy_pass_header X-Route-Key;
         	proxy_pass http://proxy_cd2;
         	proxy_set_upstream error timout http_502;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-DataCenter $http_x_datacenter;
            add_header Access-Control-Expose-Headers "X-Route-Key";
        } 
    }
    server {
        listen 8088;
        location / {
            proxy_pass http://app-server;
		   proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

springboot 改造

HeaderTransferFilter.java

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

import org.springframework.stereotype.Component;

@Component
public class HeaderTransferFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 获取请求头
        String routeKey = httpRequest.getHeader("X-Route-Key");
        if (routeKey != null) {
            httpResponse.setHeader("X-Route-Key", routeKey);
        }

        // 继续处理请求
        chain.doFilter(request, response);
    }
}

web前端项目改造:vue

// src/utils/request.js 或 src/api/index.js

import axios from 'axios';

// 创建 axios 实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // 例如 '/api'
  timeout: 30000,
});

// 全局变量存储 x-route-key
let routeKey = null;

// --- 响应拦截器:获取 X-ROUTE-KEY ---
service.interceptors.response.use(
  (response) => {
    // 从响应头中获取 X-ROUTE-KEY
    const key = response.headers['x-route-key']; // 浏览器自动转为小写

    if (key) {
      routeKey = key;
      console.log('[Header] X-ROUTE-KEY captured:', routeKey);
    }
    // 可选:也可以从 response.data 中提取(如果后端放在 body 中)
    return response;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// --- 请求拦截器:带上 X-ROUTE-KEY(如果已获取)---
service.interceptors.request.use(
  (config) => {
    if (routeKey) {
      config.headers['X-ROUTE-KEY'] = routeKey;
      console.log('[Header] X-ROUTE-KEY added to request:', routeKey);
    }
    // 注意:不要设置 Content-Type 等会被覆盖的头,除非必要
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

export default service;

评论