一个入口通全球 | 如何使用同一个域名访问多地部署的同一个服务

0 | 前言

最近,大模型很火,国内国外都在争相推出自己的大语言模型(LLM),有些可以公开调用 API,有些只能面向 B 端用户,各家 API 调用范式还不一致,如果单个去接的话,会花费大量的时间和经历去阅读API文档和配置 token。

由于业务需要,我们需要提供一个统一的大模型 API 聚合入口服务,对使用者屏蔽底层接入细节,统一接口规范,以及配置对应大模型的 token。

然而,大模型的提供方既有国内的(minimax,spark,文心一言等等),也有国外的(openai,claude,google-palm等等),如果将我们的聚合服务放到一个地方部署,就会有一个问题:如果部署在国内,那么很多大模型API调用的连通性就有障碍;如果部署在国外,那访问国内大模型API接口就会很慢。

本文,针对这个痛点,提出了统一域名下的跨地域联合部署方案。

1 | 方案

在最初的版本,是一套代码分别在A地和B地同时部署,通过在接口文档里,显式规定用不同的域名来区分调用A地的模型还是B地的模型API。能解决问题,但是往往使用者有概率会忽略这部分内容,导致沟通成本的提升;同时,这种方式也不利于后续的扩展(如果有C地部署,那就需要新增一个新的域名访问)。

因此,在升级方案里,通过 nginx配置转发 + header自定义字段 的形式,来实现统一域名下的跨地域服务调用。 基本原理是:统一域名指向特定的 nginx 服务器,通过在请求的 header 里的自定义字段,将请求转发到不同地域。

2 | 实操

从上面的方案原理,可以看到,关键点就在 nginx 的配置:如何配置 nginx.conf 文件,来实现根据 header 里的特定 kv,来实现请求的转发。 这里直接上 nginx.conf 上的相关配置(提供了开箱即用的全量配置文件,但是重点在最后十几行,可以直接看最后的说明):

dart 复制代码
worker_processes  4;
error_log  /error.log info;
events {
    accept_mutex on;
    multi_accept on;
    use epoll;
    worker_connections  4096;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    log_format  main  '$time_local $remote_user $remote_addr $host $request_uri $request_method $http_cookie '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
                      '$request_time $upstream_response_time "$upstream_cache_status"';
    log_format  browser '$time_iso8601 $cookie_km_uid $remote_addr $host $request_uri $request_method '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
                      '$request_time $upstream_response_time "$upstream_cache_status" $http_x_requested_with $http_x_real_ip $upstream_addr $request_body';
    log_format client '{"@timestamp":"$time_iso8601",'
                      '"time_local":"$time_local",'
                      '"remote_user":"$remote_user",'
                      '"http_x_forwarded_for":"$http_x_forwarded_for",'
                      '"host":"$server_addr",'
                      '"remote_addr":"$remote_addr",'
                      '"http_x_real_ip":"$http_x_real_ip",'
                      '"body_bytes_sent":$body_bytes_sent,'
                      '"request_time":$request_time,'
                      '"status":$status,'
                      '"upstream_response_time":"$upstream_response_time",'
                      '"upstream_response_status":"$upstream_status",'
                      '"request":"$request",'
                      '"http_referer":"$http_referer",'
                      '"http_user_agent":"$http_user_agent"}';

    access_log  /access.log  main;
    sendfile        on;
    
    keepalive_timeout 120s 100s;
    keepalive_requests 500;
    send_timeout 60000s;
    client_header_buffer_size 4k;
    proxy_ignore_client_abort on;
    proxy_buffers 16 32k;
    proxy_buffer_size 64k;
    proxy_busy_buffers_size 64k;
    proxy_send_timeout 60000;
    proxy_read_timeout 60000;
    proxy_connect_timeout 60000;
    proxy_cache_valid 200 304 2h;
    proxy_cache_valid 500 404 2s;
    proxy_cache_key $host$request_uri$cookie_user;
    proxy_cache_methods GET HEAD POST;

    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Host                $http_host;
    proxy_set_header X-Real-IP           $remote_addr;
    proxy_set_header X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto   $scheme;
    proxy_set_header X-Frame-Options     SAMEORIGIN;
    proxy_set_header Upgrade             $http_upgrade;
    proxy_set_header Connection          "upgrade";  

    server_tokens off;
    client_max_body_size 50G;
    add_header X-Cache $upstream_cache_status;
    autoindex off;

    resolver      127.0.0.1:53 ipv6=off;

    map $http_region $proxy_upstream {
        cn1 server_cn_1;
        us1 server_us_1;
        default server_us_1;
    }

    upstream server_us_1 {
        server server_us_1.com;
    }

    upstream server_cn_1 {
        server server_cn_1.com;
    }

    server {
      listen 80;

      location / {
          proxy_pass http://$proxy_upstream;
      }
    }
}

说明:

  1. proxy_set_header Connection "upgrade"; 如果需要支持 websocket 链接,需要配置此项;
  2. http_region 表示 http 请求的 header 里的 key=region 的值;
  3. map $http_region $proxy_upstream 表示是一个映射(可以理解成if else),即当 region=cn1 的时候, 转向 upstream=server_cn_1 所对应的 server=server_cn_1.com;当region=us1 的时候,转向 upstream=server_us_1 所对应的 server=server_us_1.com。

因此,以上 nginx 配置表示:监听80端口,通过判断 header 里的 region 值,将请求转发给不同的后端服务。

3 | 一些感触

  1. nginx 真的很强大,越了解它,越觉得强大
  2. 所有做工程的都应该要了解/熟悉/掌握/精通 nginx(前端/devops/后端/架构)
  3. 老毛子是真厉害
  4. 晚饭不吃真的会饿
相关推荐
狂奔solar8 分钟前
从“钢筋安装质量验收标准“谈起:知识库问答“多跳检索”架构演进与实践
架构·知识图谱·知识库溯源
勤劳打代码18 分钟前
Flutter 架构日记 —— 可演进的 Flutter Dialog 组件
flutter·架构
Java编程爱好者25 分钟前
1-5 线程池:Thread+阻塞队列+循环
后端
jnrjian27 分钟前
Library Cache Load Lock library cache pins are replaced by mutexes
java·后端·spring
用户94161469336538 分钟前
Python 批量获取 A 股全市场 K 线数据并计算技术指标(附完整代码)
后端
小江的记录本1 小时前
【Kafka核心】Kafka高性能的四大核心支柱:零拷贝、批量发送、页缓存、压缩
java·数据库·分布式·后端·缓存·kafka·rabbitmq
gQ85v10Db1 小时前
Redis分布式锁进阶第十四篇:全系列终局架构复盘 + 锁体系统一规范 + 线上全年零事故收官方案
redis·分布式·架构
SamDeepThinking1 小时前
程序员过35岁之前,应该完成的三件事
java·后端·程序员
952361 小时前
SpringAOP
java·后端·学习·spring
zx2859634002 小时前
Laravel6.x新特性全解析
java·后端·spring