【导读】Go语言进阶!不懂架构怎么行?今天学习一篇架构文章,详细介绍了CDN、DNS、多活架构的系统设计理念和实现。
DNS(Domain Name System,域名系统),DNS 服务用于在网络请求时,将域名转为 IP 地址。能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的 IP 数串。 传统的基于 UDP 协议的公共 DNS 服务极易发生 DNS 劫持,从而造成安全问题。
DNS 劫持问题?
1、对于互联网,域名是访问的第一跳,而这一跳很多时候会“失足”(尤其是移动端网络),例如访问错误内容、失败连接等,让用户在互联网上畅游的爽快瞬间消失; 2、经常会出现域名缓存、劫持、跨网 DNS缓存问题 dns缓存指向旧的IP 导致用户连接不上服务器
移动端DNS收敛 走7层nginx做转发 因为移动端解析成本高
通过域名找到IP 找到边缘节点
域名查询过程
递归查询
如果主机所询问的本地域名服务器不知道被查询域名的 IP 地址,那么本地域名服务器就以 DNS 客户的身份,向其他根域名服务器继续发出查询请求报文,而不是让该主机自己进行下一步的查询。
迭代查询
当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的 IP 地址,要么告诉本地域名服务器:你下一步应当向哪一个域名服务器进行查询。然后让本地域名服务器进行后续的查询,而不是替本地域名服务器进行后续的查询。
客户端到 Local DNS 服务器,Local DNS 与上级 DNS 服务器之间属于递归查询;
DNS 服务器与根 DNS 服务器之前属于迭代查询。
Local DNS 劫持:Local DNS 把域名劫持到其他域名,实现其不可告人的目的。
1、记录是随机的、每条记录引来有基本相同的数量请求;2、SRV记录权重(HTTP不支持);3、一般是维护物理位置对应的对照表,按照”最近“原来回复;
域名缓存就是 LocalDNS 缓存了业务的域名的解析结果,不向权威 DNS 发起递归。
保证用户访问流量在本网内消化:国内的各互联网接入运营商的带宽资源、网间结算费用、IDC机房分布、网内 ICP 资源分布等存在较大差异。为了保证网内用户的访问质量,同时减少跨网结算,运营商在网内搭建了内容缓存服务器,通过把域名强行指向内容缓存服务器的 IP 地址,就实现了把本地本网流量完全留在了本地的目的。 推送广告:有部分 LocalDNS 会把部分域名解析结果的所指向的内容缓存,并替换成第三方广告联盟的广告。
除了域名缓存以外,运营商的 LocalDNS 还存在解析转发的现象。 解析转发是指运营商自身不进行域名递归解析,而是把域名解析请求转发到其它运营商的递归 DNS 上的行为。
而部分小运营商为了节省资源,就直接将解析请求转发到了其它运营的递归 LocalDNS 上去了。 这样的直接后果就是权威 DNS 收到的域名解析请求的来源 IP 就成了其它运营商的 IP,最终导致用户流量被导向了错误的 IDC,用户访问变慢。
LocalDNS 递归出口 NAT 指的是运营商的 LocalDNS 按照标准的 DNS 协议进行递归,但是因为在网络上存在多出口且配置了目标路由 NAT,结果导致 LocalDNS 最终进行递归解析的时候的出口 IP 就有概率不为本网的 IP 地址。 这样的直接后果就是 DNS 收到的域名解析请求的来源 IP 还是成了其它运营商的 IP,最终导致用户流量被导向了错误的 IDC,用户访问变慢。
实时监控 + 商务推动:这种方案就是周期比较长,毕竟通过行政手段来推动运营商来解决这个问题是比较耗时的。另外我们通过大数据分析,得出的结论是 Top3 的问题用户均为移动互联网用户。对于这部分用户,我们有什么技术手段可以解决以上的问题呢?绕过自动分配 DNS,使用 114DNS 或 Google public DNS:如何在用户侧构造域名请求:对于 PC 端的客户端来说,构造一个标准的 DNS 请求包并不算什么难事。但在移动端要向一个指定的 LocalDNS 上发送标准的 DNS 请求包,而且要兼容各种 iOS 和 Android 的版本的话,技术上是可行的,只是兼容的成本会很高。推动用户修改配置极高:如果要推动用户手动修改 PC 的 DNS 配置的话,在 PC 端和手机客户端的 WiFi 下面还算勉强可行。但是要用户修改在移动互联网环境下的 DNS 配置,其难度不言而喻。
HTTPDNS
完全抛弃域名,自建 HTTPDNS 进行流量调度:如果要采用这种这种方案的话,首先你就得要拿到一份准确的 IP 地址库来判断用户的归属,然后再制定个协议搭个服务来做调度,然后再对接入层做调度改造。这种方案和2种方案一样,不是不能做,只是成本会比较高,尤其对于大体量业务规模如此庞大的公司而言。当前主流的解决方案:HTTPDNS出现了!
HTTPDNS 利用 HTTP 协议与 DNS 服务器交互,代替了传统的基于 UDP 协议的 DNS 交互,绕开了运营商的 Local DNS,有效防止了域名劫持,提高域名解析效率。另外,由于 DNS 服务器端获取的是真实客户端 IP 而非 Local DNS 的 IP,能够精确定位客户端地理位置、运营商信息,从而有效改进调度精确性。
DNS---> 边缘节点---> 中心机房 多活切换流量的时候 修改边缘节点的路由配置 7层转发
https -> http ( token ,自己定义验证) -> net/rpc, grpc
由于 HTTP DNS 是通过 ip 直接请求 http获取服务器 A 记录地址,不存在向本地运营商询问 domain 解析过程,所以从根本避免了劫持问题。平均访问延迟下降:由于是 ip 直接访问省掉了一次 domain 解析过程。用户连接失败率下降:通过算法降低以往失败率过高的服务器排序 通过时间近期访问过的数据提高服务器排序 通过历史访问成功记录提高服务器排序
根治域名解析异常:由于绕过了运营商的LocalDNS,用户解析域名的请求通过 HTTP 协议直接透传到了 HTTPDNS 服务器 IP 上,用户在客户端的域名解析请求将不会遭受到域名解析异常的困扰。调度精准:HTTPDNS 能直接获取到用户 IP,通过结合 IP 地址库以及测速系统,可以保证将用户引导的访问最快的 IDC 节点上;实现成本低廉:接入 HTTPDNS 的业务仅需要对客户端接入层做少量改造,无需用户手机进行 root 或越狱;而且由于 HTTP 协议请求构造非常简单,兼容各版本的移动操作系统更不成问题;另外 HTTPDNS 的后端配置完全复用现有权威 DNS 配置,管理成本也非常低。
如果只有一个 VIP,即可以增加 DNS 记录的TTL,减少解析的延迟。Anycast 可以使用一个 IP,将数据路由到最近的一组服务器,通过 BGP 宣告这个 IP,但是这存在两个问题:如果某个节点承载过多的用户会过载 BGP 路由计算可能会导致连接重置 因此需要一个 “稳定 Anycast” 技术来实现。
用户DNS--请求 local DNS --> DNS递归查询 ---> local DNS缓存数据---> 用户拿到最佳IP 然后连接边缘节点--> 连接中心机房
缓存代理
通过智能 DNS 的筛选,用户的请求被透明地指向离他最近的省内骨干节点,最大限度的缩短用户信息的传输距离。
路由加速
利用接入节点和中继节点或者多线节点互联互通。
安全保护
无论面对是渗透还是 DDoS攻击,攻击的目标大都会被指向到了 CDN,进而保护了用户源站。
节省成本
CDN 节点机房只需要在当地运营商的单线机房,或者带宽相对便宜的城市,采购成本低。
内容路由
DNS系统、应用层重定向,传输层重定向。
内容分发
PUSH:主动分发,内容管理系统发起,将内容从源分发到 CDN 的 Cache 节点。 PULL:被动分发技术,用户请求驱动,用户请求内容中 miss,从源中或者其他 CDN 节点中实时获取内容。
内容存储
随机读、顺序写、小文件的分布式存储。
内容管理
提高内容服务的效率,提高CDN的缓存利用率。
PUSH
不存在数据一致性问题。
PULL
缓存更新不及时,数据一致性问题,可设置缓存的失效时间,可以达到最终一致性。如果用户对一致性要求比较高也可以使用 ?version=xx 的技术,也可以每次上传图片返回的url是不同的方式来代替版本号。CDN 存储的资源复本指定过期时间,因而缓存图像文件可在一个小时,一个月有效的。任何资源缓存在 CDN 上,是潜在历史版本,因为在源数据与副本之间总是有一个更新与传输的延迟。
Expires
即在 HTTP 头中指明具体失效的时间(HTTP/1.0)
Cache Control
max-age 在 HTTP 头中按秒指定失效的时间,优先级高于Expires(HTTP/1.1)
Last-Modified / If-Modified-Since
文件最后一次修改的时间(精度是秒,HTTP/1.0),需要 Cache-Control 过期。
Etag
当前资源在服务器的唯一标识(生成规则由服务器决定)优先级高于Last-Modified
业务分级
按照一定的标准将业务进行分级,挑选出核心的业务,只为核心业务核心场景设计异地多活,降低方案整体复杂度和实现成本。例如:1、访问量;2、核心场景;3、收入;避免进入所有业务都要全部多活,分阶段分场景推进。
数据分类
挑选出核心业务后,需要对核心业务相关的数据进一步分析,目的在于识别所有的数据及数据特征,这些数据特征会影响后面的方案设计。常见的数据特征分析维度有:1、数据量;2、唯一性;3、实时性;4、可丢失性;5、可恢复性;
数据同步
确定数据的特点后,我们可以根据不同的数据设计不同的同步方案。常见的数据同步方案有:1、存储系统同步;2、消息队列同步;3、重复生成;
异常处理
无论数据同步方案如何设计,一旦出现极端异常的情况,总是会有部分数据出现异常的。例如,同步延迟、数据丢失、数据不一致等。异常处理就是假设在出现这些问题时,系统将采取什么措施来应对。常见的异常处理措施:1、多通道同步;2、同步和异步访问;3、日志记录;4、补偿;
多活不是整个体系业务的多活,而是分成不同维度,不同重要性的多活,比如我们业务观看体验为主(淘宝以交易单元,买家为维度),那么第一大前提就是浏览、观看上的多活。我们将资源分为三类:
Global 资源:多个 Zone(机房)共享访问的资源,每个 Zone 访问本 Zone 的资源,但是 Global 层面来说是单写 Core Zone(核心机房),即:单写+多读、利用数据复制(写Zone 单向)实现最终一致性方案实现; Multi Zone 资源:多个 Zone 分片部署,每个 Zone 拥有部分的 Shard 数据,比如我们按照用户维度拆分,用户 A 可能在 ZoneA,用户 B 可能在 ZoneB,即:多写+多读、利用数据复制(写 Zone 双向复制)方案实现; Single Zone 资源:单机房部署业务;
核心主要围绕:PC/APP 首页可观看、视频详情页可打开、账号可登陆、鉴权来开展,我们认为最合适我们观看类业务最合适的场景就是采用 Global 资源策略,对于社区类(评论、弹幕)可能会采用 Multi Zone 的策略。
业务过程中包含3个最重要的角色,分别是用户、商家和骑手,一个订单包含3个步骤:
用户打开我们的APP,系统会推荐出用户位置附近的各种美食,推荐顺序中结合了用户习惯,推荐排序,商户的推广等。用户找到中意的食物 ,下单并支付,订单会流转到商家。 商家接单并开始制作食物,制作完成后,系统调度骑手赶到店面,取走食物。 骑手按照配送地址,把食物送到客户手中。
业务内聚
单个订单的旅单过程,要在一个机房中完成,不允许跨机房调用。
这个原则是为了保证实时性,旅单过程中不依赖另外一个机房的服务,才能保证没有延迟。 我们称每个机房为一个 ezone,一个 ezone 包含了饿了么需要的各种服务。 一笔业务能够内聚在一个 ezone 中,那么一个定单涉及的用户,商家,骑手,都会在相同的机房,这样订单在各个角色之间流转速度最快,不会因为各种异常情况导致延时。 恰好我们的业务是地域化的,通过合理的地域划分,也能够实现业务内聚。
可用性优先
当发生故障切换机房时,优先保证系统可用,首先让用户可以下单吃饭,容忍有限时间段内的数据不一致,在事后修复。 每个 ezone 都会有全量的业务数据,当一个 ezone 失效后,其他的 ezone 可以接管用户。 用户在一个ezone的下单数据,会实时的复制到其他ezone。
保证数据正确
在确保可用的情况下,需要对数据做保护以避免错误,在切换和故障时,如果发现某些订单的状态在两个机房不一致,会锁定该笔订单,阻止对它进行更改,保证数据的正确。
业务可感
因为基础设施还没有强大到可以抹去跨机房的差异,需要让业务感知多活逻辑,业务代码要做一些改造,包括:需要业务代码能够识别出业务数据的归属,只处理本 ezone 的数据,过滤掉无关的数据。完善业务状态机,能够在数据出现不一致的时候,通过状态机发现和纠正。
为了实现业务内聚,我们首先要选择一个划分方法(Sharding Key),对服务进行分区,让用户,商户,骑手能够正确的内聚到同一个 ezone 中。分区方案是整个多活的基础,它决定了之后的所有逻辑。
根据饿了么的业务特点,我们自然的选择地理位置(地理围栏,地理围栏主体按照省界划分,再加上局部微调)作为划分业务的单元,把地理位置上接近的用户,商户,骑手划分到同一个ezone,这样一个订单的履单流程就会在一个机房完成,能够保证最小的延时,在某个机房出现问题的时候,也可以按照地理位置把用户,商户,骑手打包迁移到别的机房即可。
基于地理位置划分规则,开发了统一的流量路由层(API Router),这一层负责对客户端过来的 API 调用进行路由,把流量导向到正确的 ezone。API Router 部署在多个公有云机房中,用户就近接入到公有云的API Router,还可以提升接入质量。
最基础的分流标签是地理位置,有了地理位置,AR 就能计算出正确的 shard 归属。但业务是很复杂的,并不是所有的调用都能直接关联到某个地理位置上,我们使用了一种分层的路由方案,核心的路由逻辑是地理位置,但是也支持其他的一些 High Level Sharding Key,这些 Sharding Key 由 APIRouter 转换为核心的 Sharding Key,具体如下图。这样既减少了业务的改造工作量,也可以扩展出更多的分区方法。除了入口处的路由,我们还开发了 SOA Proxy,用于路由SOA调用的,和API Router基于相同的路由规则。