一篇基于AWS服务搭建的全球服务架构
一、背景
当我们在做全球业务时,例如网站/机器/游戏,为了支撑全球用户,我们会在不同的位置搭建节点,以满足业务功能和响应实效。
我们会搭建一个这样的架构满足初步的使用。

能用是ok的了,会有什么问题呢?
1、因为服务独立,上架1个SKU需要多次发布到不同的服务。
2、因为域名过多,发布时需要频繁的退出、登录切换不同的二级域名,非常繁琐不说,还可能会遗漏。
3、后期处理数据统计、报表、看板、设备地图,耗时费力。
4、这还是单节点,未考虑高可用的情况下,多区域、多节点部署服务器导致运维难度增加、过多的财务开支。

三、干活,简单的一笔带过
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,完事儿

浙公网安备 33010602011771号