一、为什么需要多域名配置?

去年我接手过一个电商平台的改造项目,他们的主站、营销中心和用户中心分别部署在三台服务器上。每次新增活动专题都要重新部署服务器,运维团队苦不堪言。直到我们把所有流量都收敛到OpenResty网关,通过多域名配置实现智能路由,才真正解决了这个痛点。

多域名配置的核心价值在于:用软件定义的灵活路由替代物理隔离的部署方式。想象一下你有一套高级公寓,通过智能门锁系统让不同访客进入对应的房间,而不是为每个访客单独盖一栋房子。这不仅能节省资源,还能实现动态调整。

二、典型应用场景分析

2.1 多租户系统

某SaaS平台需要为每个企业客户分配独立子域名(如companyA.app.com),通过域名路由到对应的租户环境。这种场景下,OpenResty的域名匹配规则比传统负载均衡器更灵活。

2.2 微服务网关

当你的系统拆分成用户服务、订单服务、商品服务等多个微服务时,使用api.user.com、api.order.com等不同域名进行服务发现,可以避免在URL路径中维护复杂的路由逻辑。

2.3 AB测试环境

通过experiment.v1.com和experiment.v2.com两个域名,将用户流量导向不同版本的服务端实现,配合灰度发布策略使用效果更佳。

三、核心配置详解(基于OpenResty 1.21.4)

3.1 基础域名路由

server {
    listen 80;
    server_name main.com;
    
    location / {
        proxy_pass http://backend_main;
        # 添加统一的请求头标识
        proxy_set_header X-Service-Type "main";
    }
}

server {
    listen 80;
    server_name admin.com;
    
    location / {
        proxy_pass http://backend_admin;
        # 管理员系统需要特殊日志格式
        access_log /var/log/nginx/admin_access.log main;
    }
}

这种配置方式简单直观,但存在两个明显缺陷:

  1. 需要为每个新域名重复编写server块
  2. 无法处理带通配符的域名匹配

3.2 动态路由进阶方案

http {
    lua_shared_dict domain_routes 10m;  # 共享内存存储路由配置
    
    init_by_lua_block {
        local routes = {
            ["main.com"] = "http://backend_main",
            ["admin.com"] = "http://backend_admin",
            ["*.user.com"] = "http://user_service"
        }
        ngx.shared.domain_routes:set("config", cjson.encode(routes))
    }

    server {
        listen 80;
        server_name ~^(?<subdomain>.+)\.user\.com$;
        
        location / {
            access_by_lua_block {
                local cjson = require "cjson"
                local routes = cjson.decode(ngx.shared.domain_routes:get("config"))
                
                -- 处理通配符匹配
                if ngx.var.subdomain then
                    ngx.var.backend = routes["*.user.com"]
                else
                    ngx.var.backend = routes[ngx.var.host]
                end
                
                if not ngx.var.backend then
                    ngx.exit(404)
                end
            }
            
            proxy_pass $backend;
        }
    }
}

这个方案有三个关键改进点:

  1. 使用Lua共享内存动态管理路由配置
  2. 支持通配符域名匹配(如*.user.com)
  3. 配置热更新无需重启服务

3.3 SSL证书自动化管理

当需要处理HTTPS流量时,证书管理会成为新的挑战。这里推荐使用lua-resty-auto-ssl库:

http {
    lua_shared_dict auto_ssl 10m;
    
    init_by_lua_block {
        auto_ssl = require "resty.auto-ssl"
        auto_ssl.setup({
            storage_adapter = "resty.auto-ssl.storage_adapters.redis",
            redis = {
                host = "127.0.0.1",
                port = 6379,
            },
            allow_domain = function(domain)
                -- 验证域名是否在允许列表中
                local domains = {"main.com", "admin.com"}
                for _, d in ipairs(domains) do
                    if domain == d or string.match(domain, "^%*."..d:gsub("%.", "%%.")) then
                        return true
                    end
                end
                return false
            end
        })
    }

    server {
        listen 443 ssl;
        server_name ~.+;
        
        ssl_certificate_by_lua_block { auto_ssl:ssl_certificate() }
        ssl_certificate /etc/ssl/resty-auto-ssl.crt;
        ssl_certificate_key /etc/ssl/resty-auto-ssl.key;
        
        location / {
            # 复用之前的动态路由逻辑
        }
    }
}

该配置实现了:

  1. 自动签发Let's Encrypt证书
  2. 通配符证书支持
  3. 基于Redis的证书存储
  4. 域名白名单验证

四、技术方案对比分析

4.1 方案优势

  1. 性能卓越:测试数据显示,单节点OpenResty可处理超过5万QPS的HTTPS请求
  2. 动态生效:路由配置更新实时生效,无需服务重启
  3. 成本低廉:相比商业API网关节省90%以上成本
  4. 扩展性强:可通过Lua脚本实现鉴权、限流等扩展功能

4.2 潜在缺陷

  1. 学习曲线陡峭:需要同时掌握Nginx配置和Lua编程
  2. 调试困难:动态配置的错误排查比静态配置复杂
  3. 证书管理风险:自动续期失败可能导致服务中断

五、生产环境注意事项

5.1 域名解析陷阱

某次线上故障的教训:运维同学配置了新的泛解析记录(*.new.com),但忘记设置DNS TTL,导致部分用户持续解析到旧IP长达72小时。建议:

  • 提前设置较短的DNS TTL(如300秒)
  • 使用DNS监控服务
  • 在OpenResty层设置备用路由

5.2 证书管理最佳实践

  1. 维护独立的证书存储集群
  2. 设置证书到期前30天的预警机制
  3. 定期备份证书私钥
  4. 禁用不安全的SSL协议版本

5.3 性能调优参数

http {
    # 优化SSL会话缓存
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 4h;
    
    # 调大LVM缓存
    lua_shared_dict domain_cache 100m;
    
    # 启用HTTP/2
    listen 443 ssl http2;
}

六、完整示例:电商平台路由方案

http {
    upstream user_service {
        server 192.168.1.10:8000;
        server 192.168.1.11:8000;
    }

    upstream product_service {
        server 192.168.2.10:8000;
        server 192.168.2.11:8000;
    }

    lua_shared_dict domain_routes 10m;

    init_by_lua_block {
        local routes = {
            ["user.example.com"] = "http://user_service",
            ["product.example.com"] = "http://product_service",
            ["legacy.example.com"] = "http://legacy_backend"
        }
        ngx.shared.domain_routes:set("config", cjson.encode(routes))
    }

    server {
        listen 80;
        server_name ~^(?<subdomain>.+)\.example\.com$;
        
        location / {
            access_by_lua_block {
                local cjson = require "cjson"
                local routes = cjson.decode(ngx.shared.domain_routes:get("config"))
                
                -- 优先级匹配精确域名
                local target = routes[ngx.var.host] or routes["*."..ngx.var.subdomain]
                
                if not target then
                    ngx.log(ngx.ERR, "未找到路由配置: ", ngx.var.host)
                    ngx.exit(503)
                end
                
                ngx.var.target = target
            }

            proxy_pass $target;
            proxy_set_header Host $host;
            
            # 重要:设置合理的超时时间
            proxy_connect_timeout 3s;
            proxy_read_timeout 10s;
        }
        
        # 健康检查端点
        location /health {
            access_log off;
            return 200 "OK";
        }
    }
}

这个配置实现了:

  1. 多子域名自动路由
  2. 动态配置热加载
  3. 双后端集群负载均衡
  4. 健康检查机制
  5. 合理的超时控制

七、总结与展望

经过多个项目的实践验证,OpenResty的多域名配置方案在灵活性和性能之间找到了最佳平衡点。最近在Kubernetes环境中,我们将其与Ingress Controller结合使用,进一步简化了云原生环境下的流量管理。

未来值得关注的三个方向:

  1. WebAssembly集成带来的安全沙箱能力
  2. QUIC协议的原生支持
  3. 机器学习驱动的智能路由决策