一篇基于AWS服务搭建的全球服务架构

一、背景

当我们在做全球业务时,例如网站/机器/游戏,为了支撑全球用户,我们会在不同的位置搭建节点,以满足业务功能和响应实效。

我们会搭建一个这样的架构满足初步的使用。

能用是ok的了,会有什么问题呢?

1、因为服务独立,上架1个SKU需要多次发布到不同的服务。

2、因为域名过多,发布时需要频繁的退出、登录切换不同的二级域名,非常繁琐不说,还可能会遗漏。

3、后期处理数据统计、报表、看板、设备地图,耗时费力。

4、这还是单节点,未考虑高可用的情况下,多区域、多节点部署服务器导致运维难度增加、过多的财务开支。
  5、各个节点各玩各的。
  有什么方式可以解决这些问题?
  AWS服务组件很好支持。
二、AWS架构
  AWS提供了非常丰富的组件,列几个常用的:
  1、Route 53 负责域名解析流量管理健康检查,高可用可以用到它。
  2、VPC 虚拟私有网络,可将同一区域的服务器、数据库置于同一内网段,提升访问速度。
  3、CloudFront CDN加速,可以给访问请求、对象存储加速。函数功能极其强大,可配置HTTP、HTTPS的请求跨域,可识别全球流量来源,根据来源分发到不同区域的节点。
  4、EC2 服务器,内含了负载均衡器ELB、目标组,可实现同一地区的服务器的负载均衡和高可用。结合CloudFront可将流量分发到就近的服务。
  5、Aurora and RDS 数据库,Aurora 作为全球数据库,已实现主从备份,可在1s内实现全球数据同步,结合Route 53的短域名实现读写分离。
  6、S3 对象存储,OSS存储图片、视频、文档、音频等。
  7、Certificate Manager 安全凭证管理,可申请和管理证书。
  架构完是这样的。

三、干活,简单的一笔带过

1、将外部域名解析到Route 53。

2、在Certificate Manager申请证书。

3、在拟定的区域创建VPC内网。

4、购买服务器和数据库时选择上面的创建的VPC内网。

5、在创建Aurora数据库时需要选确认某一个区域为主集群,主集群下会创建写入器实例、读取器实例。

再创建其它区域的读取实例,如下图:

6、在Route 53创建一个私有的短域名,用于数据库连接,无需在域名供应商购买。我这里用 db.com

7、将二级域名指向到Aurora的写域名指向到Aurora的写入器实例的DNS(路由策略:简单),

读域名指向到多个读取器实例的DNS(路由策略:延迟)。数据写入到主库后,1s内可同步到所有的从库

8、在EC2购买服务器后,新建目标组,将服务器添加到目标组中

9、在EC2创建负载均衡器,这里分NLB(服务器之间用)和ELB(服务器内部服务之间用),层级和颗粒度不同。

这里创建NLB,添加转发到的目标组到侦听器,注意选择VPC。创建完即可通过DNS访问。

10、创建CloudFront,将需要经过CDN加速的域名添加到备用域名,添加第2步创建的证书,源添加到访问目标的DNS。成功后CloudFront会分配域名。

11、在Route 53将经过CloudFront加速的域名指向到CloudFront分配域名上。

12、在Route 53创建NLB的健康检查,成功后生成ID。

13、在Route 53添加故障转移域名,用于服务区域之间的高可用,无需额外采购服务器

14、在CloudFront创建函数,将请求按照国家和地区分发到相应的服务器。

代码如下:

复制代码
import cf from 'cloudfront';

function handler(event) {
    const request = event.request;
    const headers = request.headers;
    const country = headers['cloudfront-viewer-country'] && headers['cloudfront-viewer-country'].value;
    // List of countries to ALB endpoints
    const countryToContinent = {
        // 亚太地区 (Asia-Pacific) -> Asia
        'AF': 'Asia',
        'AM': 'Asia',
        'AZ': 'Asia',
        'BD': 'Asia',
        'BN': 'Asia',
        'BT': 'Asia',
        'CC': 'Asia',
        'CK': 'Asia',
        'CX': 'Asia',
        'GE': 'Asia',
        'HK': 'Asia',
        'ID': 'Asia',
        'IN': 'Asia',
        'IO': 'Asia',
        'JP': 'Asia',
        'KG': 'Asia',
        'KH': 'Asia',
        'KR': 'Asia',
        'KZ': 'Asia',
        'LA': 'Asia',
        'LK': 'Asia',
        'MM': 'Asia',
        'MN': 'Asia',
        'MO': 'Asia',
        'MP': 'Asia',
        'MV': 'Asia',
        'MY': 'Asia',
        'NC': 'Asia',
        'NF': 'Asia',
        'NP': 'Asia',
        'NR': 'Asia',
        'NU': 'Asia',
        'NZ': 'Asia',
        'PH': 'Asia',
        'PK': 'Asia',
        'PN': 'Asia',
        'SB': 'Asia',
        'SG': 'Asia',
        'TH': 'Asia',
        'TJ': 'Asia',
        'TL': 'Asia',
        'TM': 'Asia',
        'TO': 'Asia',
        'TV': 'Asia',
        'TW': 'Asia',
        'UZ': 'Asia',
        'VU': 'Asia',
        'WF': 'Asia',
        'WS': 'Asia',
        'YE': 'Asia',
        // 大洋洲 (Oceania) - 通常归类为亚太地区
        'AS': 'Asia',
        'AU': 'Asia',
        'FJ': 'Asia',
        'PF': 'Asia',
        'GU': 'Asia',
        'KI': 'Asia',
        'MH': 'Asia',
        'FM': 'Asia',
        'PW': 'Asia',
        'PG': 'Asia',
        'TK': 'Asia',
        // 南极洲 (Antarctica) - 通常单独处理,这里暂时归入亚太
        'AQ': 'Asia',
        'BV': 'Asia',
        'GS': 'Asia',
        'HM': 'Asia',
        'TF': 'Asia',
        // 欧洲、非洲、中东 -> Europe
        'AL': 'Europe',
        'AD': 'Europe',
        'AT': 'Europe',
        'AX': 'Europe',
        'BA': 'Europe',
        'BE': 'Europe',
        'BG': 'Europe',
        'BY': 'Europe',
        'CH': 'Europe',
        'CY': 'Europe',
        'CZ': 'Europe',
        'DE': 'Europe',
        'DK': 'Europe',
        'EE': 'Europe',
        'ES': 'Europe',
        'FI': 'Europe',
        'FO': 'Europe',
        'FR': 'Europe',
        'GG': 'Europe',
        'GI': 'Europe',
        'GR': 'Europe',
        'HR': 'Europe',
        'HU': 'Europe',
        'IE': 'Europe',
        'IM': 'Europe',
        'IS': 'Europe',
        'IT': 'Europe',
        'JE': 'Europe',
        'LI': 'Europe',
        'LT': 'Europe',
        'LU': 'Europe',
        'LV': 'Europe',
        'MC': 'Europe',
        'MD': 'Europe',
        'ME': 'Europe',
        'MK': 'Europe',
        'MT': 'Europe',
        'NL': 'Europe',
        'NO': 'Europe',
        'PL': 'Europe',
        'PT': 'Europe',
        'RO': 'Europe',
        'RS': 'Europe',
        'RU': 'Europe',
        'SE': 'Europe',
        'SI': 'Europe',
        'SJ': 'Europe',
        'SK': 'Europe',
        'SM': 'Europe',
        'UA': 'Europe',
        'VA': 'Europe',
        'GB': 'Europe',
        'DZ': 'Europe',
        'AO': 'Europe',
        'BJ': 'Europe',
        'BW': 'Europe',
        'BF': 'Europe',
        'BI': 'Europe',
        'CV': 'Europe',
        'CM': 'Europe',
        'CF': 'Europe',
        'TD': 'Europe',
        'KM': 'Europe',
        'CG': 'Europe',
        'CD': 'Europe',
        'CI': 'Europe',
        'DJ': 'Europe',
        'EG': 'Europe',
        'GQ': 'Europe',
        'ER': 'Europe',
        'ET': 'Europe',
        'GA': 'Europe',
        'GM': 'Europe',
        'GH': 'Europe',
        'GN': 'Europe',
        'GW': 'Europe',
        'KE': 'Europe',
        'LS': 'Europe',
        'LR': 'Europe',
        'LY': 'Europe',
        'MG': 'Europe',
        'MW': 'Europe',
        'ML': 'Europe',
        'MR': 'Europe',
        'MU': 'Europe',
        'YT': 'Europe',
        'MA': 'Europe',
        'MZ': 'Europe',
        'NA': 'Europe',
        'NE': 'Europe',
        'NG': 'Europe',
        'RW': 'Europe',
        'RE': 'Europe',
        'SH': 'Europe',
        'SN': 'Europe',
        'SC': 'Europe',
        'SL': 'Europe',
        'SO': 'Europe',
        'ZA': 'Europe',
        'SS': 'Europe',
        'SD': 'Europe',
        'ST': 'Europe',
        'SZ': 'Europe',
        'TG': 'Europe',
        'TN': 'Europe',
        'TZ': 'Europe',
        'UG': 'Europe',
        'EH': 'Europe',
        'ZM': 'Europe',
        'ZW': 'Europe',
        'AE': 'Europe',
        'BH': 'Europe',
        'IR': 'Europe',
        'IQ': 'Europe',
        'IL': 'Europe',
        'JO': 'Europe',
        'KW': 'Europe',
        'LB': 'Europe',
        'OM': 'Europe',
        'PS': 'Europe',
        'QA': 'Europe',
        'SA': 'Europe',
        'SY': 'Europe',
        'TR': 'Europe',
        // 美洲 -> America
        'AI': 'America',
        'AG': 'America',
        'AW': 'America',
        'BS': 'America',
        'BB': 'America',
        'BZ': 'America',
        'BM': 'America',
        'BQ': 'America',
        'CA': 'America',
        'KY': 'America',
        'CR': 'America',
        'CU': 'America',
        'CW': 'America',
        'DM': 'America',
        'DO': 'America',
        'SV': 'America',
        'GD': 'America',
        'GL': 'America',
        'GP': 'America',
        'GT': 'America',
        'HT': 'America',
        'HN': 'America',
        'JM': 'America',
        'MQ': 'America',
        'MX': 'America',
        'MS': 'America',
        'NI': 'America',
        'PA': 'America',
        'PR': 'America',
        'BL': 'America',
        'KN': 'America',
        'LC': 'America',
        'MF': 'America',
        'PM': 'America',
        'VC': 'America',
        'SX': 'America',
        'TT': 'America',
        'TC': 'America',
        'US': 'America',
        'UM': 'America',
        'UY': 'America',
        'VE': 'America',
        'VG': 'America',
        'VI': 'America'
    };

    const continentToRegion = {
        'Asia': 'asia.autovxxxxxx.com',         // 亚洲故障转移域名
        'Europe': 'europu.autovxxxxxx.com',     // 欧洲故障转移域名
        'America': 'america.autovxxxxxxx.com'    // 美洲故障转移域名
    };

    const DEFAULT_REGION = 'NLB-Asia-Singapore-5e42xxxxxxxx4853.elb.ap-southeast-1.amazonaws.com'; //改为自己的默认alb 域名
    const targetContinent = (country && countryToContinent[country]) || 'Asia';
    // const targetContinent = 'Asia';
    const targetOrigin = (targetContinent && continentToRegion[targetContinent]) || DEFAULT_REGION;

    // 打印所有请求头
    console.log("=== 所有请求头信息 ===");
    for (var headerName in headers) {
        if (headers.hasOwnProperty(headerName)) {
            // headers 对象的值是一个包含 'value' 属性的对象
            var headerValue = headers[headerName].value;
            console.log(headerName + ": " + headerValue);
        }
    }
    console.log('targetOrigin: ' + targetOrigin);

    // 使用CloudFront 2.0 API修改origin
    cf.updateRequestOrigin({
        "domainName": targetOrigin,
        "port": 80,
        "protocol": 'http',
        "timeouts": {
            "readTimeout": 30,
            "connectionTimeout": 5
        }
    });
    
    // 添加调试信息 - 确保值是字符串
    request.headers['x-debug-country'] = { value: String(country || 'unknown') };
    request.headers['x-debug-targetorigin'] = { value: String(targetOrigin) };

    return request;
}

解决跨域问题,函数代码如下:

复制代码
function handler(event)  {
    var response  = event.response;
    var headers  = response.headers;

    // If Access-Control-Allow-Origin CORS header is missing, add it.
    // Since JavaScript doesn't allow for hyphens in variable names, we use the dict["key"] notation.
    if (!headers['access-control-allow-origin']) {
        headers['access-control-allow-origin'] = {value: "*"};
        console.log("Access-Control-Allow-Origin was missing, adding it now.");
        headers['access-control-allow-headers'] = {value: "Origin, X-Requested-With, Content-Type, Accept"};
        headers['access-control-allow-methods'] = {value: "GET,POST,PUT, OPTIONS"};
    }

    return response;
}

ok,完事儿