微服务实战(二):微服务的接入层设计与动静资源隔离

原文连接:微服务的接入层设计与动静资源隔离(来源:刘超的通俗云计算)前端

 

这个系列是微服务高并发设计,因此咱们先从最外层的接入层入手,看都有什么样的策略保证高并发。
接入层的架构画一个简图来说包括下面的部分。nginx

1.png


接下来咱们依次解析各个部分以及能够作的优化。shell

1、数据中心以外:DNS、HttpDNS、GSLB

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

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

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

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

2.jpg


一、当一个客户端要访问object.yourcompany.com的时候,须要将域名转换为IP地址进行访问,因此他要请求本地的resolver帮忙

二、本地的resolver看本地的缓存是否有这个记录呢?若是有则直接使用

三、若是本地无缓存,则须要请求本地的Name Server

四、本地的Name Server通常部署在客户的数据中心或者客户所在的运营商的网络中,本地Name Server看本地是否有缓存,若是有则返回

五、若是本地没有,本地Name Server须要从Root Name Server开始查起,Root Name Server会将.com Name Server的地址返回给本地Name Server

六、本地的Name Server接着访问.com的Name Server,他会将大家公司的yourcompany.com的Name Server给本地Name Server

七、本地的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,是由于分运营商和分地域,咱们但愿将属于不一样运营商的客户,访问相同运营商机房中的资源,这样不用跨运营商访问,有利于提升吞吐量,减小时延。

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

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

十、本地Name Server将结果返回给resolver

十一、resolver将结果缓存后,返回给客户端

十二、客户端开始访问属于相同运营商的距离较近的Region1中的对象存储,固然客户端获得了六个IP地址,他能够经过负载均衡的方式,随机或者轮询选择一个可用区进行访问,对象存储通常会有三份备份,从而能够实现对存储读写的负载均衡。

从上面的过程能够看出,基于DNS域名的GSLB实现全局的负载均衡,但是如今跨运营商和跨地域的流量调度,可是因为不一样运营商的DNS缓存策略不一样,会形成GSLB的工做实效。

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

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

因此不一样于传统的DNS,有另外一种机制称为httpDNS,能够在用户的手机App里面嵌入SDK,经过http的方式访问一个httpDNS服务器,因为手机App能够精确的得到本身的IP地址,能够将IP地址传给httpDNS服务器,httpDNS服务器彻底由应用的服务商提供,能够实现彻底自主的全网流量调度。api

2、数据中心以外:CDN

对于静态资源来说,其实在真实的访问机房内的对象存储以前,在最最接近用户的地方,能够先经过CDN进行缓存,这也是高并发应用的一个整体的思路,能接近客户,尽可能接近客户。

CDN厂商的覆盖范围每每更广,在每一个运营商,每一个地区都有本身的POP点,因此总有更加靠近用户的相同运营商和相近地点的CDN节点就近获取静态数据,避免了跨运营商和跨地域的访问。

在使用了CDN以后,用户访问资源的时候,和上面的过程相似,可是不一样的是,DNS解析的时候,会将域名的解析权交给CDN厂商的DNS服务器,而CDN厂商的DNS服务器能够经过CDN厂商的GSLB,找到最最接近客户的POP点,将数据返回给用户。浏览器

3.jpg


当CDN中没有找到缓存数据的时候,则须要到真正的服务器中去拿,这个称为回源,仅仅很是少数的流量须要回源,大大减小了服务器的压力。缓存

3、数据中心边界与核心:边界路由、核心交换、等价路由

若是真的须要回源,或者访问的压根就不是静态资源,而是动态资源,则须要进入数据中心了。

刚才第一节中说到,最终GSLB返回了6个IP地址,都是内部负载均衡SLB的IP地址,说明这6个IP地址都是公网能够访问的,那么公网如何知道这些IP地址的呢?

这就要看机房的结构了。服务器

4.jpg


一个机房通常会有边界路由器、核心交换机,每一个AZ有汇聚交换机,6个SLB是在AZ里面的,因此他们的IP地址是经过iBGP协议告知边界路由器的。

当用户从六个IP里面选择了一个IP地址进行访问的时候,能够经过公网上面的路由,找到机房的边界路由器,边界路由器知道当时这个路由是从哪一个AZ里面给他的,因而就经过核心交换一层,将请求转发给某一个AZ,这个AZ的汇聚交换机会将请求转发给这个SLB。

若是一个AZ出现了问题,是否可让对某个公网IP的访问给另外一个AZ呢?固然是能够的,在核心路由和核心交换之间,能够作ECMP等价路由。固然也能够在边界路由上将外部地址NAT称为内部的一个VIP地址,经过等价路由实现跨AZ的流量分担。cookie

4、数据中心可用区中:负载均衡SLB、LVS、HAProxy

进入一个可用区AZ以后,首先到达的是负载均衡SLB,能够购买商用的SLB,也能够本身搭建,例如经过LVS实现基本的负载均衡功能。

LVS的性能比较好,不少工做经过内核模块ipvs完成。网络

5.png


LVS可以使用keepalived实现双机热备,也能够经过OSPF使用等价路由的方式,在多个LVS之间进行流量分担,每每做为统一的负载均衡入口,承载大的流量。

6.jpg


有时候须要更加复杂的4层和7层负载均衡,则会在LVS后面加上HAProxy集群,也即将LVS导入的流量,分发到一大批HAProxy上,这些HAProxy能够根据不一样的应用或者租户进行隔离,每一个租户独享单独的HAProxy,可是全部的租户共享LVS集群。

若是有云环境,则HAProxy能够部署在虚拟机里面,能够根据流量的状况和租户的请求进行动态的建立和删除。

7.png

 

5、数据中心可用区中:接入层Nginx、接入层缓存

在负载均衡以后,是接入网关,或者API网关,每每须要实现不少灵活的转发策略,这里会选择使用Nginx+Lua或者OpenResty作这一层。

因为Nginx自己也有负载均衡机制,有的时候会将HAProxy这一层和Nginx这一层合并,LVS后面直接跟Nginx集群。

接入层做用一:API的聚合

使用微服务以后,后端的服务会拆分的很是的细,于是前端应用若是要获取整个页面的显示,每每须要从多个服务获取数据,将数据作必定的聚合后,方可以显示出来。

8.jpg


若是是网页其实还好,若是你用Chrome的debug模式下,打开一个复杂的电商主页的时候,你会发现这个页面同时会发出不少的http的请求,最终聚合称为一个页面。

若是是APP的话,其实也没有问题,可是会有大量的工做要在客户端作,这样会很是的耗电,用户体验很是很差,于是最好有一个地方能够将请求聚合,这就是API网关的职责之一。这样对于前端APP来说,后端接是彷佛是一个统一的入口,则后端的服务的拆分和聚合,灰度发布,缓存策略等所有被屏蔽了。

9.jpg

 

接入层做用二:服务发现与动态负载均衡

既然统一的入口变为了接入层,则接入层就有责任自动的发现后端拆分、聚合、扩容、缩容的服务集群,当后端服务有所变化的时候,可以实现健康检查和动态的负载均衡。

对于微服务来说,服务之间也是须要作服务发现的,常见的框架是Dubbo和Spring Cloud,服务的注册中心能够是ZooKeeper、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阶段经过http 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,对动态资源进行缓存。

10.png

 

接入层做用五:资源隔离

接入层的Nginx集群不是一个,而是不一样的请求能够有独立的Nginx集群。

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

接入层做用六:统一鉴权、认证、过滤

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

一种是基于session的,当客户端输入用户名密码以后,API Gateway会向后端服务提交认证和鉴权,成功后生成session,session统一放在Redis里面,则接下来的访问所有都带着session进行。

11.png


另外一种方式是经过统一的认证鉴权中心,分配token的方式进行。

12.jpg


这是一个三角形的结构,当API Gateway接收到登录请求的时候,去认证中心请求认证和受权,若是成功则返回token,token是一个加密过的字符串,里面包含不少的认证信息,接下来的访问中,API Gateway能够验证这个token是否有效来认证,而真正的服务能够根据token来鉴权。

接入层做用七:限流

在大促过程当中,经常会遇到真实的流量远远大于系统测试下来的可承载流量,若是这些流量都进来,则整个系统必定垮掉,最后谁也别玩。因此长作的方式是限流。

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

对于Nginx来说,限流有多种方式,能够进行链接数限制limit_conn,能够进行访问频率限制limit_req,能够启用过载保护sysgurad模块。

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

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

对于被限流的用户,能够进行相对友好的返回,不一样的页面的策略能够不一样。

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

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

对于支付结果返回,若是被限流,须要立刻返回错误页面。

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

在接入层,因为能够配置访问路由,以及访问权重,能够实现灰度发布,或者AB测试,同时上线两套系统,经过切入部分流量的方式,测试新上系统的稳定性或者是否更受欢迎。

相关文章
相关标签/搜索