戏说系列-从术和道来看基于流量的架构设计(上)

    上次写到晚上太晚了,没有写完,本来和同事约好今天写完,但是今天公司事情太多,所以只能回家继续写,微笑 ,刚同事还问我博客链接呢,继续弄。。。

    首先我们看下图:

    

    上图的目的是:

1. 上层是为了保护下层服务,即使上层服务因为流量太大被打挂了,也不应该影响下层服务的可用性。

2. 保护相同级别的服务,即使自己挂了也不应该影响到别人。

3. 各个层要尽量的挡住部分流量。

    我们先看一个正反面的例子,如下图:


    先看下上图中昨边的图,昨边在应用网关和API都进行了限流,网关的限流时为了保护API,API的限流是为了数据库。我们再看看右边的图,只在API进行了限流,这个时候其实从应用层网关来的峰值流量还是会打到API,虽然API做了限流和限流后的降级处理,但是API还是有可能被峰值冲垮的,因为毕竟峰值的时候API是需要分配资源来应对峰值挑战的。

    我们先看下流量发展的几个阶段:


    1. 野蛮生长   2. 应付流量   3. 规划流量  4. 设计流量,从野蛮生长开始研究:



    上图是流量发展的第一个阶段:野蛮生长,这个阶段公司一般处于创业的初期,特点是流量不大,但是忽大忽小。方案是使用缓存来搞定读,MQ来搞定写。有些时候可能需要考虑下多级缓存,基本方案是前端缓存,本地堆内缓存,堆外缓存(需要序列化和反序列化),本地REDIS缓存,分布式REDIS集群。根据不同的业务场景选择不同的方案。


    上图是流量发展的第二个阶段:应付流量,这个阶段公司一般处于创业期中期,特点是由于业务快速的推进,流量剧增,有瞬时事故。因为忙于应付业务快速增长,所有这个阶段会出现疲于奔命的状态,需要不断的补窟窿。解决方案一般是要进行流控(分流,限流,合流,截流,减流,拆流,隔离流量,降级流量,熔断,),通道优化,数据前置。那么大概说一下:

分流:分流是根据流量的性质和特点不同,分发给不同的服务实例进行处理。比如常见的:动静分离,冷热分离,大小分离,读写分离    …

限流:限流的作用是为了保护下游服务是可用的,从而做到有损服务,部分请求的降级处理保证大多数请求是可以被处理的。常见的限流方案:接入层限流(LUA),应用层限流   具体实现:信号量,RateLimiter 。。。

合流:合流的意思是将流量合并进行批处理,批处理完毕后将结果交给原来的请求。如下图:


    比如上图两个请求访问A服务,一般情况下A服务会处理两次,然后将结果返回。如果是合流的话,那么比如我们设置100MS内访问A服务的请求都会被写入队列,然后都交有A1服务来进行批处理(消费掉),A1服务进行批处理完毕后将结果交给原来的两个请求。具体实现请参考 SPRING CLOUD中的熔断器 。

拆流:

    拆流如上图,拆流就是把一个请求拆成多个请求,然后进行并发或者并行的调用,这样就比之前的同步调用要节省时间。比如平时同步调用A,B,C 三个服务的话需要 100 + 100 + 100 = 300MS ,现在只需要 100MS就可以了。具体实现请参加JDK并发包中的CyclicBarrierCountDownLatch.

截流: 截流的意思是把流量截断(砍断),截断后流量可以自己返回,后台可以继续处理。一般的解决方案如下:MQ异步话,Servlet3异步化  具体实现:MQ,Serlet3, @Sync  。

减流:减流就是尽量的减少请求,比如前端防重(重复提交),防抖,前端缓存 ,前端业务的动画(几秒动画)减流等。

隔离流量:数据隔离,线程隔离,进程隔离,服务隔离,机房隔离

    数据隔离主要是把不同的数据放到不同的表或者数据库中,线程隔离主要是使用线程池来实现,比如第二篇中的蝴蝶效应,如果两个服务(券服务和活动服务)是互相隔离,都有自己的线程池,那么当达到线程最大限制就不会新开线程,也就不会占用更多的系统资源,也不会相互影响。使用线程池隔离的话要注意线程池队列的大小的设置,如果太大会继续占用服务器的资源。 技术方案:请参考 SpringCloud 熔断器中的线程池隔离,或者自己实现线程池。TOMCAT中的线程池,MYSQL中的连接池,REDIS中的连接池都是池化技术的提现。

    举个例子吧: 比如活动页有3个服务,A,B,C A服务需要调用账务系统的API,B服务可能需要调用红包系统的API,A服务是主要的业务服务,B,C 服务因为是远程调用,那么超时的可能性比较大,那么就可以对B,C服务进行隔离,分别建立线程池。那么B,C服务当线程池慢了就会执行拒绝降低策略,就不会影响A服务的执行。


降级流量:降级是当服务出现问题的时候,可以使用一些方案来暂时进行处理,从而不影响正常的业务的进行。根据层级不同可分为:前端降级,接入层降级,服务降级。

    举个例子吧: 比如活动页面调用后端API获取活动的总金额,如果获取到总金额那么就正常的显示,如果获取不到那么就可以显示一张喜庆点的图片把原来的地方替换掉,那么也不影响活动的正常进行。

熔断:熔断就好比我们生活中的电力保险丝,当电力持续增高的时候就会被熔断。熔断以后一般需要进行降级的处理。

    举个例子吧: 比如调用查询活动总金额的API,如果连续5次调用都超时了,那么就可以进行熔断,熔断后其他请求直接走降级部分的逻辑,好处是不会一直都是调用超时,可

以防止蝴蝶效应,避免雪崩。需要说明的是熔断并不是一直熔断下次,而是隔一段时间会进行重试,如果调用成功会关闭熔断,继续走正常逻辑。

通道优化:一个请求走过的一个完整的路径,就是一个通道。


    一个请求可能经过的路径如下:通过终端,访问网络,网络就涉及带宽,防火墙,路由器,交换机,然后到达后端接入层的OS, 然后到接入层NG集群,然后到应用层的TOMCAT,JVM,接入层本地CACHE,分布式缓存集群,MYSQL。终端的核心NG和业务NG也可能会使用到LUA与REDIS和MQ进行交互。当然一个请求也不一定就都要经过所有的路径,有些可能直接在终端就停止了,比如访问某个图片,终端有缓存的话直接就显示了。这个全路径中有很多的点可能会是系统的瓶颈,这些点就是通道优化需要进行关注的点。


数据前置:数据前置的意思是将数据放到离用户最近的位置。比如,如果可以将数据直接放到终端的话,那么用户直接就获取了,根本不用和后端交互,那么对后端也就没有什

么压力,数据越前置越会缩短处理时间,减少系统资源占用。

    今天估计还是写不完了,家里孩子又开始闹腾了,还得去抱,答应同事的今天还是写不完了。。。真是不好意思。。。。。。可怜再见 先去忙忙孩子了,俩孩子一个比一个

闹腾。。。。明天晚上继续奋战吧。。。看来坚持真是不容易。。。