范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

面试官你说说限流的原理?

  限流作为现在微服务中常见的稳定性措施,在面试中肯定也是经常会被问到的,我在面试的时候也经常喜欢问一下你对限流算法知道哪一些?有看过源码吗?实现原理是什么?
  第一部分先讲讲限流算法,最后再讲讲源码的实现原理。 限流算法
  关于限流的算法大体上可以分为四类:固定窗口计数器、滑动窗口计数器、漏桶(也有称漏斗,英文Leaky bucket)、令牌桶(英文Token bucket)。 固定窗口
  固定窗口,相比其他的限流算法,这应该是最简单的一种。
  它简单地对一个固定的时间窗口内的请求数量进行计数,如果超过请求数量的阈值,将被直接丢弃。
  这个简单的限流算法优缺点都很明显。优点的话就是简单,缺点举个例子来说。
  比如我们下图中的黄色区域就是固定时间窗口,默认时间范围是60s,限流数量是100。
  如图中括号内所示,前面一段时间都没有流量,刚好后面30秒内来了100个请求,此时因为没有超过限流阈值,所以请求全部通过,然后下一个窗口的20秒内同样通过了100个请求。
  所以变相的相当于在这个括号的40秒的时间内就通过了200个请求,超过了我们限流的阈值。
  滑动窗口
  为了优化这个问题,于是有了滑动窗口算法,顾名思义,滑动窗口就是时间窗口在随着时间推移不停地移动。
  滑动窗口把一个固定时间窗口再继续拆分成N个小窗口,然后对每个小窗口分别进行计数,所有小窗口请求之和不能超过我们设定的限流阈值。
  以下图举例子来说,假设我们的窗口拆分成了3个小窗口,小窗口都是20s,同样基于上面的例子,当在第三个20s的时候来了100个请求,可以通过。
  然后时间窗口滑动,下一个20s请求又来了100个请求,此时我们滑动窗口的60s范围内请求数量肯定就超过100了啊,所以请求被拒绝。
  漏桶Leaky bucket
  漏桶算法,人如其名,他就是一个漏的桶,不管请求的数量有多少,最终都会以固定的出口流量大小匀速流出,如果请求的流量超过漏桶大小,那么超出的流量将会被丢弃。
  也就是说流量流入的速度是不定的,但是流出的速度是恒定的。
  这个和MQ削峰填谷的思想比较类似,在面对突然激增的流量的时候,通过漏桶算法可以做到匀速排队,固定速度限流。
  漏桶算法的优势是匀速,匀速是优点也是缺点,很多人说漏桶不能处理突增流量,这个说法并不准确。
  漏桶本来就应该是为了处理间歇性的突增流量,流量一下起来了,然后系统处理不过来,可以在空闲的时候去处理,防止了突增流量导致系统崩溃,保护了系统的稳定性。
  但是,换一个思路来想,其实这些突增的流量对于系统来说完全没有压力,你还在慢慢地匀速排队,其实是对系统性能的浪费。
  所以,对于这种有场景来说,令牌桶算法比漏桶就更有优势。 令牌桶token bucket
  令牌桶算法是指系统以一定地速度往令牌桶里丢令牌,当一个请求过来的时候,会去令牌桶里申请一个令牌,如果能够获取到令牌,那么请求就可以正常进行,反之被丢弃。
  现在的令牌桶算法,像Guava和Sentinel的实现都有冷启动/预热的方式,为了避免在流量激增的同时把系统打挂,令牌桶算法会在最开始一段时间内 冷启动 ,随着流量的增加,系统会根据流量大小动态地调整生成令牌的速度,最终直到请求达到系统的阈值。 源码举例
  我们以sentinel举例,sentinel中统计用到了滑动窗口算法,然后也有用到漏桶、令牌桶算法。 滑动窗口
  sentinel  中就使用到了滑动窗口算法来进行统计,不过他的实现和我上面画的图有点不一样,实际上sentinel中的滑动窗口用一个圆形来描述更合理一点。
  前期就是创建节点,然后slot串起来就是一个责任链模式,StatisticSlot通过滑动窗口来统计数据,FlowSlot是真正限流的逻辑,还有一些降级、系统保护的措施,最终形成了整个sentinel的限流方式。
  滑动窗口的实现主要可以看 LeapArray  的代码,默认的话定义了时间窗口的相关参数。
  对于sentinel来说其实窗口分为 秒 和 分钟 两个级别,秒的话窗口数量是2,分钟则是60个窗口,每个窗口的时间长度是1s,总的时间周期就是60s,分成60个窗口,这里我们就以分钟级别的统计来说。 public abstract class LeapArray {     //窗口时间长度,毫秒数,默认1000ms     protected int windowLengthInMs;     //窗口数量,默认60     protected int sampleCount;     //毫秒时间周期,默认60*1000     protected int intervalInMs;     //秒级时间周期,默认60     private double intervalInSecond;     //时间窗口数组     protected final AtomicReferenceArray> array;
  然后我们要看的就是它是怎么计算出当前窗口的,其实源码里写的听清楚的,但是如果你按照之前想象把他当做一条直线延伸去想的话估计不太好理解。
  首先计算数组索引下标和时间窗口时间这个都比较简单,难点应该大部分在于第三点窗口大于old这个是什么鬼,详细说下这几种情况。 数组中的时间窗口是是空的,这个说明时间走到了我们初始化的时间之后了,此时new一个新的窗口通过CAS的方式去更新,然后返回这个新的窗口就好了。 第二种情况是刚好时间窗口的时间相等,那么直接返回,没啥好说的 第三种情况就是比较难以理解的,可以参看两条时间线的图,就比较好理解了,第一次时间窗口走完了达到1200,然后圆形时间窗口开始循环,新的时间起始位置还是1200,然后时间窗口的时间来到1676,B2的位置如果还是老的窗口那么就是600,所以我们要重置之前的时间窗口的时间为当前的时间。 最后一种一般情况不太可能发生,除非时钟回拨这样子
  从这个我们可以发现就是针对每个 WindowWrap  时间窗口都进行了统计,最后实际上在后面的几个地方都会用到时间窗口统计的QPS结果,这里就不再赘述了,知道即可。 private int calculateTimeIdx(/*@Valid*/ long timeMillis) {     long timeId = timeMillis / windowLengthInMs;     // Calculate current index so we can map the timestamp to the leap array.     return (int) (timeId % array.length()); }  protected long calculateWindowStart(/*@Valid*/ long timeMillis) {     return timeMillis - timeMillis % windowLengthInMs; }  public WindowWrap currentWindow(long timeMillis) {     //当前时间如果小于0,返回空     if (timeMillis < 0) {         return null;     }     //计算时间窗口的索引     int idx = calculateTimeIdx(timeMillis);     // 计算当前时间窗口的开始时间     long windowStart = calculateWindowStart(timeMillis);      while (true) {         //在窗口数组中获得窗口         WindowWrap old = array.get(idx);         if (old == null) {             /*              *     B0       B1      B2    NULL      B4              * ||_______|_______|_______|_______|_______||___              * 200     400     600     800     1000    1200  timestamp              *                             ^              *                          time=888              * 比如当前时间是888,根据计算得到的数组窗口位置是个空,所以直接创建一个新窗口就好了              */             WindowWrap window = new WindowWrap(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));             if (array.compareAndSet(idx, null, window)) {                 // Successfully updated, return the created bucket.                 return window;             } else {                 // Contention failed, the thread will yield its time slice to wait for bucket available.                 Thread.yield();             }         } else if (windowStart == old.windowStart()) {             /*              *     B0       B1      B2     B3      B4              * ||_______|_______|_______|_______|_______||___              * 200     400     600     800     1000    1200  timestamp              *                             ^              *                          time=888              * 这个更好了,刚好等于,直接返回就行              */             return old;         } else if (windowStart > old.windowStart()) {             /*              *     B0       B1      B2     B3      B4              * |_______|_______|_______|_______|_______||___              * 200     400     600     800     1000    1200  timestamp              *             B0       B1      B2    NULL      B4              * |_______||_______|_______|_______|_______|_______||___              * ...    1200     1400    1600    1800    2000    2200  timestamp              *                              ^              *                           time=1676              * 这个要当成圆形理解就好了,之前如果是1200一个完整的圆形,然后继续从1200开始,如果现在时间是1676,落在在B2的位置,              * 窗口开始时间是1600,获取到的old时间其实会是600,所以肯定是过期了,直接重置窗口就可以了              */             if (updateLock.tryLock()) {                 try {                     // Successfully get the update lock, now we reset the bucket.                     return resetWindowTo(old, windowStart);                 } finally {                     updateLock.unlock();                 }             } else {                 Thread.yield();             }         } else if (windowStart < old.windowStart()) {             // 这个不太可能出现,嗯…时钟回拨             return new WindowWrap(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));         }     } }漏桶
  sentinel主要根据 FlowSlot  中的流控进行流量控制,其中 RateLimiterController  就是漏桶算法的实现,这个实现相比其他几个还是简单多了,稍微看一下应该就明白了。 首先计算出当前请求平摊到1s内的时间花费,然后去计算这一次请求预计时间 如果小于当前时间的话,那么以当前时间为主,返回即可 反之如果超过当前时间的话,这时候就要进行排队等待了,等待的时候要判断是否超过当前最大的等待时间,超过就直接丢弃 没有超过就更新上一次的通过时间,然后再比较一次是否超时,还超时就重置时间,反之在等待时间范围之内的话就等待,如果都不是那就可以通过了 public class RateLimiterController implements TrafficShapingController {   //最大等待超时时间,默认500ms   private final int maxQueueingTimeMs;   //限流数量   private final double count;   //上一次的通过时间   private final AtomicLong latestPassedTime = new AtomicLong(-1);    @Override   public boolean canPass(Node node, int acquireCount, boolean prioritized) {       // Pass when acquire count is less or equal than 0.       if (acquireCount <= 0) {           return true;       }       // Reject when count is less or equal than 0.       // Otherwise,the costTime will be max of long and waitTime will overflow in some cases.       if (count <= 0) {           return false;       }        long currentTime = TimeUtil.currentTimeMillis();       //时间平摊到1s内的花费       long costTime = Math.round(1.0 * (acquireCount) / count * 1000); // 1 / 100 * 1000 = 10ms        //计算这一次请求预计的时间       long expectedTime = costTime + latestPassedTime.get();        //花费时间小于当前时间,pass,最后通过时间 = 当前时间       if (expectedTime <= currentTime) {           latestPassedTime.set(currentTime);           return true;       } else {           //预计通过的时间超过当前时间,要进行排队等待,重新获取一下,避免出现问题,差额就是需要等待的时间           long waitTime = costTime + latestPassedTime.get() - TimeUtil.currentTimeMillis();           //等待时间超过最大等待时间,丢弃           if (waitTime > maxQueueingTimeMs) {               return false;           } else {               //反之,可以更新最后一次通过时间了               long oldTime = latestPassedTime.addAndGet(costTime);               try {                   waitTime = oldTime - TimeUtil.currentTimeMillis();                   //更新后再判断,还是超过最大超时时间,那么就丢弃,时间重置                   if (waitTime > maxQueueingTimeMs) {                       latestPassedTime.addAndGet(-costTime);                       return false;                   }                   //在时间范围之内的话,就等待                   if (waitTime > 0) {                       Thread.sleep(waitTime);                   }                   return true;               } catch (InterruptedException e) {               }           }       }       return false;   }  }令牌桶
  最后是令牌桶,这个不在于实现的复制,而是你看源码会发现都算的些啥玩意儿…sentinel的令牌桶实现基于Guava,代码在 WarmUpController  中。
  这个算法那些各种计算逻辑其实我们可以不管(因为我也没看懂…),但是流程上我们是清晰的就可以了。
  几个核心的参数看注释,构造方法里那些计算逻辑暂时不管他是怎么算的(我也没整明白,但是不影响我们理解),关键看 canPass  是怎么做的。 拿到当前窗口和上一个窗口的QPS 填充令牌,也就是往桶里丢令牌,然后我们先看填充令牌的逻辑 public class WarmUpController implements TrafficShapingController {     //限流QPS     protected double count;     //冷启动系数,默认=3     private int coldFactor;     //警戒的令牌数     protected int warningToken = 0;     //最大令牌数     private int maxToken;     //斜率,产生令牌的速度     protected double slope;      //存储的令牌数量     protected AtomicLong storedTokens = new AtomicLong(0);     //最后一次填充令牌时间     protected AtomicLong lastFilledTime = new AtomicLong(0);      public WarmUpController(double count, int warmUpPeriodInSec, int coldFactor) {         construct(count, warmUpPeriodInSec, coldFactor);     }      public WarmUpController(double count, int warmUpPeriodInSec) {         construct(count, warmUpPeriodInSec, 3);     }      private void construct(double count, int warmUpPeriodInSec, int coldFactor) {         if (coldFactor <= 1) {             throw new IllegalArgumentException("Cold factor should be larger than 1");         }         this.count = count;         this.coldFactor = coldFactor;          //stableInterval 稳定产生令牌的时间周期,1/QPS         //warmUpPeriodInSec 预热/冷启动时间 ,默认 10s         warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1);         maxToken = warningToken + (int)(2 * warmUpPeriodInSec * count / (1.0 + coldFactor));         //斜率的计算参考Guava,当做一个固定改的公式         slope = (coldFactor - 1.0) / count / (maxToken - warningToken);     }      @Override     public boolean canPass(Node node, int acquireCount, boolean prioritized) {         //当前时间窗口通过的QPS         long passQps = (long) node.passQps();         //上一个时间窗口QPS         long previousQps = (long) node.previousPassQps();         //填充令牌         syncToken(previousQps);          // 开始计算它的斜率         // 如果进入了警戒线,开始调整他的qps         long restToken = storedTokens.get();         if (restToken >= warningToken) {             //当前的令牌超过警戒线,获得超过警戒线的令牌数             long aboveToken = restToken - warningToken;             // 消耗的速度要比warning快,但是要比慢             // current interval = restToken*slope+1/count             double warningQps = Math.nextUp(1.0 / (aboveToken * slope + 1.0 / count));             if (passQps + acquireCount <= warningQps) {                 return true;             }         } else {             if (passQps + acquireCount <= count) {                 return true;             }         }          return false;     } }
  填充令牌的逻辑如下: 拿到当前的时间,然后去掉毫秒数,得到的就是秒级时间 判断时间小于这里就是为了控制每秒丢一次令牌 然后就是 coolDownTokens  去计算我们的冷启动/预热是怎么计算填充令牌的 后面计算当前剩下的令牌数这个就不说了,减去上一次消耗的就是桶里剩下的令牌 protected void syncToken(long passQps) {   long currentTime = TimeUtil.currentTimeMillis();   //去掉当前时间的毫秒   currentTime = currentTime - currentTime % 1000;   long oldLastFillTime = lastFilledTime.get();   //控制每秒填充一次令牌   if (currentTime <= oldLastFillTime) {     return;   }   //当前的令牌数量   long oldValue = storedTokens.get();   //获取新的令牌数量,包含添加令牌的逻辑,这就是预热的逻辑   long newValue = coolDownTokens(currentTime, passQps);   if (storedTokens.compareAndSet(oldValue, newValue)) {     //存储的令牌数量当然要减去上一次消耗的令牌     long currentValue = storedTokens.addAndGet(0 - passQps);     if (currentValue < 0) {       storedTokens.set(0L);     }     lastFilledTime.set(currentTime);   }  }最开始的事实因为 lastFilledTime  和 oldValue  都是0,所以根据当前时间戳会得到一个非常大的数字,最后和 maxToken  取小的话就得到了最大的令牌数,所以第一次初始化的时候就会生成 maxToken  的令牌 之后我们假设系统的QPS一开始很低,然后突然飙高。所以开始的时候回一直走到高于警戒线的逻辑里去,然后 passQps  又很低,所以会一直处于把令牌桶填满的状态( currentTime - lastFilledTime.get()  会一直都是1000,也就是1秒),所以每次都会填充最大QPS count  数量的令牌 然后突增流量来了,QPS瞬间很高,慢慢地令牌数量就会消耗到警戒线之下,走到我们 if  的逻辑里去,然后去按照 count  数量增加令牌 private long coolDownTokens(long currentTime, long passQps) {   long oldValue = storedTokens.get();   long newValue = oldValue;    //水位低于警戒线,就生成令牌   if (oldValue < warningToken) {     //如果桶中令牌低于警戒线,根据上一次的时间差,得到新的令牌数,因为去掉了毫秒,1秒生成的令牌就是阈值count     //第一次都是0的话,会生成count数量的令牌     newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);   } else if (oldValue > warningToken) {     //反之,如果是高于警戒线,要判断QPS。因为QPS越高,生成令牌就要越慢,QPS低的话生成令牌要越快     if (passQps < (int)count / coldFactor) {       newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);     }   }   //不要超过最大令牌数   return Math.min(newValue, maxToken); }
  上面的逻辑理顺之后,我们就可以继续看限流的部分逻辑: 令牌计算的逻辑完成,然后判断是不是超过警戒线,按照上面的说法,低QPS的状态肯定是一直超过的,所以会根据斜率来计算出一个 warningQps  ,因为我们处于冷启动的状态,所以这个阶段就是要根据斜率来计算出一个QPS数量,让流量慢慢地达到系统能承受的峰值。举个例子,如果 count  是100,那么在QPS很低的情况下,令牌桶一直处于满状态,但是系统会控制QPS,实际通过的QPS就是 warningQps  ,根据算法可能只有10或者20(怎么算的不影响理解)。QPS主键提高的时候, aboveToken  再逐渐变小,整个 warningQps  就在逐渐变大,直到走到警戒线之下,到了 else  逻辑里。 流量突增的情况,就是 else  逻辑里低于警戒线的情况,我们令牌桶在不停地根据 count  去增加令牌,这时候消耗令牌的速度超过我们生成令牌的速度,可能就会导致一直处于警戒线之下,这时候判断当然就需要根据最高QPS去判断限流了。   long restToken = storedTokens.get();   if (restToken >= warningToken) {     //当前的令牌超过警戒线,获得超过警戒线的令牌数     long aboveToken = restToken - warningToken;     // 消耗的速度要比warning快,但是要比慢     // current interval = restToken*slope+1/count     double warningQps = Math.nextUp(1.0 / (aboveToken * slope + 1.0 / count));     if (passQps + acquireCount <= warningQps) {       return true;     }   } else {     if (passQps + acquireCount <= count) {       return true;     }   }
  所以,按照低QPS到突增高QPS的流程,来想象一下这个过程: 刚开始,系统的QPS非常低,初始化我们就直接把令牌桶塞满了 然后这个低QPS的状态持续了一段时间,因为我们一直会填充最大QPS数量的令牌(因为取最小值,所以其实桶里令牌基本不会有变化),所以令牌桶一直处于满的状态,整个系统的限流也处于一个比较低的水平
  这以上的部分一直处于警戒线之上,实际上就是叫做冷启动/预热的过程。 接着系统的QPS突然激增,令牌消耗速度太快,就算我们每次增加最大QPS数量的令牌任然无法维持消耗,所以桶里的令牌在不断低减少,这个时候,冷启动阶段的限制QPS也在不断地提高,最后直到桶里的令牌低于警戒线 低于警戒线之后,系统就会按照最高QPS去限流,这个过程就是系统在逐渐达到最高限流的过程
  那这样一来,实际就达到了我们处理突增流量的目的,整个系统在漫漫地适应突然飙高的QPS,然后最终达到系统的QPS阈值。 最后,如果QPS回复正常,那么又会逐渐回到警戒线之上,就回到了最开始的过程。
  总结
  因为算法如果单独说的话都比较简单,一说大家都可以听明白,不需要几个字就能说明白,所以还是得弄点源码看看别人是怎么玩的,所以尽管我很讨厌放源码,但是还是不得不干。
  光靠别人说一点其实有点看不明白,按照顺序读一遍的话心里就有数了。
  那源码的话最难以理解的就是令牌桶的实现了,说实话那几个计算的逻辑我看了好几遍不知道他算的什么鬼,但是思想我们理解就行了,其他的逻辑相对来说就比较容易理解。

十三确实香!今年的iPhone13更值得买今天凌晨1点苹果召开了一年一度的秋季发布会,主题是加州来电。本次发布会所带来的产品有iPad2021iPadmini6AppleWatchSerise7和iPhone13系列。最受张学良恢复自由后,记者问最想见谁,他脱口而出一位汉奸的名字张学良这位曾经叱咤风云的民国少帅,从1936年到1990年,被蒋介石囚禁了半个多世纪,终于在将近90岁高龄的时候重获自由,晚年他定居美国并接受了媒体采访,当被问到如果有机会回大陆,很多人都只知道他是美国国籍,却不知道他对国家以及全世界的贡献2021年9月22日,是世界著名物理学家杨振宁老先生的99岁生日,因为自古有着过九不过十的习俗,所以很多人多发来了对他百岁生日的祝福。然而还有很多不明所以的人在下面评论一些酸臭的言蒋介石临终留4字遗言,道出至死不放张学良原因,宋美龄听闻痛哭1975年蒋介石临终前留下4字遗言,这四个字直接影响了张学良的一生,蒋经国听到遗言后先是震惊然后沉默不语,随后又微微点头认同,而宋美龄则惊讶万分大失所望。那么蒋介石临终之际说出的四2021大变局时代开启,推翻老百姓身上三座大山,中国正在德国化?虽然现在生活越来越好,幸福指数也在逐渐增高,但是压在百姓身上的三座大山也越来越沉重,这到底是怎么回事呢?国家又采取了什么样的措施?为什么说中国正在不断的德国化?这样对我国的发展会产中国撤侨德国巴铁可以帮助,唯独美国不行,原因大快人心最近中国撤侨再次火遍全球,其实这已经不是中国,第一次在撤侨事件上亮相了,但最近的这次行动,又一次惊艳了众人的目光,再次让我们感受到祖国的强大,和生而为中国人的骄傲,有些细心的小伙伴阿富汗和中国这么近,难民为什么不逃到中国,背后缘由引人深思最近阿富汗的局势,引起了全球人民的广泛关注,尤其是那些难民的情况,更是被很多西方国家注意,我国距离阿富汗这么近,为什么没有难民逃到中国呢?难民又是怎么形成的?如果这些难民逃到了中国中国又传好消息!美元霸权进入倒计时?人民币荣登第3大国际货币自从在阿富汗撤军之后,美国在国际上的地位一落千丈,虽然美国依旧是超级大国,但不可否认的是他确实在走下坡路,大家都知道美国有如今的地位,除了超强的军事实力之外,还有一个重要的原因,那辽宁舰退役后何去何从,卖给巴铁?中国做法值得称赞世界上现在拥有航母的国家,可以说是少之又少,因为研制一艘航母不仅需要高新的技术,还需要源源不断的投入大量的资金,这对一些经济不发达的国家来说,是巨大的压力。一般航空母舰退役后该何去美国的报应来了!走投无路会不会发动战争,张召忠给出神预言美国最近真是厄运连连,国内不断的爆发新的危机,很多人都开始猜测,美国会不会靠引发战争解决这次危机,毕竟他们也不是没有这样做过。对于这样的猜想,张绍忠给出神预言,到底是怎么样的呢?美中国直接弃权!130联合国通过阿富汗决议,原因展现大国风范最近国际上关于阿富汗的事情闹得沸沸扬扬,随着最后一个美军离开阿富汗,好像美国已经全完从这次战争中抽身的,但其实不是这样的,阿富汗境内可能还有很多美国公民被滞留,为了保护这些公民的安
安以轩新动作清空与老公陈荣炼昔日照片,疑似正式退圈安静生活安以轩当下的处境不太理想,虽然她暂时安全,但其家人已经遭受到了一定的打击。尤其是她带着一儿一女,女儿2岁不到,老公陈荣炼被澳门警方关进监狱,如今近5个月的时间过去了,安以轩在此期间刘涛婚变风波更厉害了!老公取关清空动态,刘涛霸气逆袭变女王最近因为刘涛的婚变传言,让大家开始尤其的关注刘涛和王珂的婚姻感情,而不前刘涛霸气发声维护婚姻,否认了离婚的传言,这也让刘涛的婚变风波备受大家的质疑,但是随后王珂居然取关了刘涛,并且一口气追了20集,越看越上瘾,这部女主大戏有点意思这两个月的影视剧似乎有点令人应接不暇,而且最近播出的都是实力演员参演的影视剧,流量小生的剧也几乎是播完了。从林更新谭松韵的请叫我总监,到潘粤明王鸥的新居之约,再到刘敏涛江疏影的女士上线2天,冲到榜单第一,剧情过瘾,东北狙王凭啥这么生猛?在观众的口诛笔伐下,抗战神剧终于走向衰退,无论是电影还是电视剧,近几年的战争片谍战片拍的可是越来越好了。以长津湖系列为首的战争片,都取得了不错的成绩,还有王一博主演的谍战片无名,虽陶慧敏嫁给初恋王设忠,夫妻恩爱几十年,女儿像妈妈一样漂亮有些女人天生就很幸运,不仅在事业上,在感情上也是。她们嫁给了好丈夫,而且生活幸福,家庭美满,孩子也很懂事。这样的人无疑成了人生赢家。陶慧敏就是这样一个女人,她长得美丽而优雅,具有中张凯丽34岁嫁给作家,和丈夫相爱26年,却从不让女儿喊她妈妈相信我在很多人心中的标签是演员凯丽,但我曾经还有一个很多人都不知道的身份,就是速滑运动员。国家一级演员著名表演艺术家张凯丽,曾在接受采访的时候,非常感慨地说出了这句话。也确实如同她电视剧父母爱情中,喜欢文化人的老丁为什么不敢娶葛老师?在电视连续剧父母爱情中,主线是父母的爱情,即海军军官江德福和资本家小姐安杰的50年爱情。但为了衬托,也用较重的笔墨描述了老丁的爱情婚姻生活。老丁的第一任妻子留下四个样儿,在生四样儿2012年王刚鉴宝节目惹争议,收藏者将其告上法庭后,结局两败俱伤经过多次询问藏宝人坚持自己的意见2012年10月19日,在北京电视台的演播室内,文物鉴赏类节目收藏天下正在进行。此时节目已经进入尾声,现场的三位专家已经对藏宝人付常勇所展示的一对甜曾经火遍全国的降央卓玛,后被好友刀郎告上法庭,她近况怎样?01hr很多年前,一首2002年第一场雪,让一个名叫刀郎的男人一夜成名。他声音嘶哑,里面还夹杂着强烈的沧桑感,歌曲非常深入人心。但刀郎怎么都不会想到。有一天自己的好友兼老乡降央卓玛台偶审美让人疑惑,姿色平平的总演女主,美若天仙反而沦为工具人台湾偶像剧基本都有一个王子与灰姑娘的共同套路,这种模式下的青春爱情剧,诞生出了诸多收视爆款好剧。但台偶剧在审美上却让人疑惑,饰演女主角的基本都是身材娇小且姿色平平的女演员,而那些为亲子动画大片开心超人之英雄的心六一超前点映圆娃超人梦拯救不开心今日,国漫经典IP开心超人大电影开心超人之英雄的心再曝全新物料,以开心超人为代表的主角团与大大怪等人展开激烈战斗,全新升级的视效体验让人众多观众眼前一亮。同时,该片也宣布将于6月1