微服务的接入层设计与动静资源隔离

以下内容已屏蔽图片优化访问速度
这个系列是微服务高并发设计,所以我们先从最外层的接入层入手,看都有什么样的策略保证高并发。


接入层的架构画一个简图来讲包括下面的部分



[IMG]


接下来我们依次解析各个部分以及可以做的优化。


一、数据中心之外:DNS,HttpDNS,GSLB


当我们要访问一个网站的服务的时候,首先访问的肯定是一个域名,然后由DNS,将域名解析为IP地址。


我们首先先通过DNS访问数据中心中的对象存储上的静态资源为例子,看一看整个过程。


我们建议将例如文件,图片,视频,音频等静态资源放在对象存储中,直接通过CDN下发,而非放在服务器上,和动态资源绑定在一起。


假设全国有多个数据中心,托管在多个运营商,每个数据中心三个可用区Available Zone,对象存储通过跨可用区部署,实现高可用性,在每个数据中心中,都至少部署两个内部负载均衡器,内部负载均衡器后面对接多个对象存储的前置服务proxy-server。


[IMG]




(1) 当一个客户端要访问object.yourcompany.com的时候,需要将域名转换为IP地址进行访问,所以他要请求本地的resolver帮忙

(2) 本地的resolver看本地的缓存是否有这个记录呢?如果有则直接使用
(3) 如果本地无缓存,则需要请求本地的Name Server
(4) 本地的Name Server一般部署在客户的数据中心或者客户所在的运营商的网络中,本地Name Server看本地是否有缓存,如果有则返回
(5) 如果本地没有,本地Name Server需要从Root Name Server开始查起,Root Name Server会将.com Name Server的地址返回给本地Name Server
(6) 本地的Name Server接着访问.com的Name Server,他会将你们公司的yourcompany.com的Name Server给本地Name Server
(7) 本地的Name Server接着访问yourcompany.com的Name Server,按说这一步就应该返回真实要访问的IP地址。


对于不需要做全局负载均衡的简单应用来讲,yourcompany.com的Name Server可以直接将object.yourcompany.com这个域名解析为一个或者多个IP地址,然后客户端可以通过多个IP地址,进行简单的轮询,实现简单的负载均衡即可。


但是对于复杂的应用,尤其是跨地域跨运营商的大型应用,则需要更加复杂的全局负载均衡机制,因而需要专门的设备或者服务器来做这件事情,这就是GSLB,全局负载均衡器。


从yourcompany.com的Name Server中,一般是通过配置CNAME的方式,给object.yourcompany.com起一个别名,例如object.vip.yourcomany.com,然后告诉本地Name Server,让他去请求GSLB去解析这个域名,则GSLB就可以在解析这个域名的过程中,通过自己的策略实现负载均衡。


图中画了两层的GSLB,是因为分运营商和分地域,我们希望将属于不同运营商的客户,访问相同运营商机房中的资源,这样不用跨运营商访问,有利于提高吞吐量,减少时延。


(8) 第一层GSLB通过查看请求他的本地Name Server所在的运营商,就知道了用户所在的运营商,假设是移动,然后通过CNAME的方式,通过另一个别名object.yd.yourcompany.com,告诉本地Name Server去请求第二层的GSLB


(9) 第二层的GSLB通过查看请求他的本地Name Server所在的地址,就知道了用户所在的地理位置,然后将距离用户位置比较近的Region的里面的内部负载均衡SLB的地址共六个返回给本地Name Server


(10) 本地Name Server将结果返回给resolver
(11) resolver将结果缓存后,返回给客户端
(12) 客户端开始访问属于相同运营商的距离较近的Region1中的对象存储,当然客户端得到了六个IP地址,他可以通过负载均衡的方式,随机或者轮询选择一个可用区进行访问,对象存储一般会有三份备份,从而可以实现对存储读写的负载均衡。


从上面的过程可以看出,基于DNS域名的GSLB实现全局的负载均衡,可是现在跨运营商和跨地域的流量调度,但是由于不同运营商的DNS缓存策略不同,会造成GSLB的工作实效。


有的用户的DNS会将域名解析的请求转发给其他的运营商的DNS进行解析,导致到GSLB的时候,错误的判断了用户所在的运营商。


有的运营商的DNS出口会做NAT,导致GSLB判断错误用户所在的运营商。


所以不同于传统的DNS,有另一种机制称为[IMG]consul, etcd, eureka等。


我们以consul为例子,既然服务之间的调用已经注册到consul上,则nginx自然也可以通过consul来获取后端服务的状态,实现动态的负载均衡。


nginx可以集成consul-template,可监听consul的事件, 当已注册service列表或key/value 发生变化时, consul-template会修改配置文件同时可执行一段shell, 如 nginx reload


consul-template \    -template "/tmp/nginx.hcl:/var/nginx/nginx.conf:service nginx reload" \


consul-template模式配置相对复杂,需要reload nginx。


另一种集成consul的方式是nginx-upsync-module,可以同步consul的服务列表或key/value存储,需要重新编译nginx,不需要reload nginx。


upstream test {
server 127.0.0.1:11111;
# 所有的后端服务列表会从consul拉取, 并删除上面的占位server
upsync 127.0.0.1:8500/v1/catelog/service/test upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;
# 备份的地址, 保证nginx不强依赖consul
upsync_dump_path /usr/local/nginx/conf/servers/servers_test.conf;
}


还有一种方式是openresty+lua,相对nginx-upsync-module, 可以加入更多自己的逻辑, init_*_by_lua 阶段通过[IMG]api 获取服务列表载入Nginx 内存, 并设置timer轮训更新列表,balancer_by_lua 阶段 读取内存的列表, 设置后端服务器。
Lua实现 同样可以不reload nginx, 相比nginx-upsync-module 来说更加可扩展。


接入层作用三:动静资源隔离,静态页面缓存,页面静态化


为什么静态资源需要隔离呢,静态资源往往变化较少,但是却往往比较大,如果每次都加载,则影响性能,浪费带宽。其实静态资源可以预加载,并且可以进行缓存,甚至可以推送到CDN。



所以应该在接入层nginx中配置动态资源和静态资源的分离,将静态资源的url导入到nginx的本地缓存或者单独的缓存层如varnish或者squid,将动态的资源访问后端的应用或者动态资源的缓存。


在nginx中,可以通过配置expires,cache-control,if-modified-since来控制浏览器端的缓存控制。使得浏览器端在一段时间内,对于静态资源,不会重复请求服务端。这一层称为浏览器端的缓存。


当有的请求的确到达了接入层nginx的时候,也不用总是去应用层获取页面,可以在接入层nginx先拦截一部分热点的请求。在这里可以有两层缓存。一是nginx本身的缓存proxy_cache,二是缓存层的varnish或者squid。


在使用接入层缓存的时候,需要注意的是缓存key的选择,不应该包含于用户相关的信息,如用户名,地理信息,cookie,deviceid等,这样相当于每个用户单独的一份缓存,使得缓存的命中率比较低。


在分离了静态和动态资源之后,就存在组合的问题,可以通过ajax访问动态资源,在浏览器端进行组合,也可以在接入层进行组合。


如果在接入层聚合,或者varnish进行聚合,则可以让接入层缓存定时轮询后端的应用,当有数据修改的时候,进行动态页面静态化,这样用户访问的数据到接入层就会被拦截,缺点是更新的速度有些慢,对于大促场景下的并发访问高的页面,可以进行如此的处理。


接入层作用四:动态资源缓存


在动静分离之后,静态页面可以很好的缓存,而动态的数据还是会向后端请求,而动态页面静态化延时相对比较高,而且页面数目多的时候,静态化的工作量也比较大,因而在接入层还可以通过redis或者memcached,对动态资源进行缓存。



[IMG]


接入层作用五:资源隔离


接入层的nginx集群不是一个,而是不同的请求可以有独立的nginx集群。


例如抢券或者秒杀系统,会成为热点中的热点,因而应该有独立的nginx集群。


接入层作用六:统一鉴权,认证,过滤



API Gateway的另一个作用是统一的认证和鉴权。


一种是基于session的,当客户端输入用户名密码之后,API Gateway会向后端服务提交认证和鉴权,成功后生成session,session统一放在redis里面,则接下来的访问全部都带着session进行。
[IMG]
另一种方式是通过统一的认证鉴权中心,分配token的方式进行。
[IMG]
这是一个三角形的结构,当API Gateway接收到登陆请求的时候,去认证中心请求认证和授权,如果成功则返回token,token是一个加密过的字符串,里面包含很多的认证信息,接下来的访问中,API Gateway可以验证这个token是否有效来认证,而真正的服务可以根据token来鉴权。


接入层作用七:限流


在大促过程中,常常会遇到真实的流量远远大于系统测试下来的可承载流量,如果这些流量都进来,则整个系统一定垮掉,最后谁也别玩。所以长做的方式是限流。


限流是从上到下贯穿整个应用的,当然接入层作为最外面的屏障,需要做好整个系统的限流。


对于nginx来讲,限流有多种方式,可以进行连接数限制limit_conn,可以进行访问频率限制limit_req,可以启用过载保护sysgurad模块。


对请求的目标URL进行限流(例如:某个URL每分钟只允许调用多少次)

对客户端的访问IP进行限流(例如:某个IP每分钟只允许请求多少次)


对于被限流的用户,可以进行相对友好的返回,不同的页面的策略可以不同。


对于首页和活动页,是读取比较多的,可以返回缓存中的老的页面,或者APP定时刷新。


对于加入购物车,下单,支付等写入请求被限流的,可以返回等待页面,或者返回一个圈圈转啊转,如果过了一段时间还转不出来,就可以返回挤爆了。


对于支付结果返回,如果被限流,需要马上返回错误页面。


接入层作用八:灰度发布与AB测试


在接入层,由于可以配置访问路由,以及访问权重,可以实现灰度发布,或者AB测试,同时上线两套系统,通过切入部分流量的方式,测试新上系统的稳定性或者是否更受欢迎。
遭集体“封杀”,一个网站会员要价50万!600亿的汽车之家早变了 黄牛党打“飞的”炒茅台,月赚200万,赚钱速度比炒房快 华为员工发飙:遇到了几个垃圾基层主管,动不动就谩骂和威胁员工 小人物陈佩斯:春天不是说来就来的! 农村墙上刷标语打广告,为何仍有很大市场?
好看吗?
总执行时间0.07351899147033691,文章查询时间0.04715919494628906,分类查询时间0.009692192077636719,其他脚本0.0003330707550048828,模板渲染0.01633453369140625