自适应限流以及四种经典限流算法
前言
在分布式系统中,如果某个服务节点发生故障或者网络发生异常,都有可能导致调用方被阻塞等待,如果超时时间设置很长,调用方资源很可能被耗尽。这又导致了调用方的上游系统发生资源耗尽的情况,最终导致系统雪崩。
要防止系统发生雪崩,就必须要有容错设计。如果遇到突增流量,一般的做法是对非核心业务功能采用熔断和服务降级的措施来保护核心业务功能正常服务,而对于核心功能服务,则需要采用限流的措施。
相信你看完本篇文章,一定能够对系统容错的常见策略—— 限流、熔断、降级 有更深的理解和体会。
如果对同学有帮助的话,麻烦三连哦,不胜感激!!! 概述
2.1 熔断(客户端)
在服务的依赖调用中,被调用方出现故障时,出于自我保护的目的,调用方会主动停止调用,并根据业务需要进行相应处理。调用方这种主动停止调用的行为我们称之为熔断。为什么要熔断?
假定服务A依赖服务B,当服务B处于正常状态,整个调用是健康的,服务A可以得到服务B的正常响应。当服务B出现故障时,比如响应缓慢或者响应超时,如果服务A继续请求服务B,那么服务A的响应时间也会增加,进而导致服务A响应缓慢。如果服务A不进行熔断处理,服务B的故障会传导至服务A,最终导致服务A也不可用。
2.2 限流(服务端)
限流是针对服务请求数量的一种自我保护机制,当请求数量超出服务的处理能力时,会自动丢弃新来的请求。
为什么要限流?
任何一个系统的处理能力都是有极限的,假定服务A的处理能力为QPS=100,当QPS<100时服务A可以提供正常的服务。当QPS>100时,由于请求量增大,会出现争抢服务资源的情况(数据库连接、CPU、内存等),导致服务A处理缓慢;当QPS继续增大时,可能会造成服务A响应更加缓慢甚至奔溃。如果不进行限流控制,服务A始终会面临着被大流量冲击的风险。做好系统请求流量的评估,制定合理的限流策略,是我们进行系统高可用保护的第一步。
2.3 降级
降级是通过开关配置将某些不重要的业务功能屏蔽掉,以提高服务处理能力。在大促场景中经常会对某些服务进行降级处理,大促结束之后再进行复原。
为什么要降级?
在不影响业务核心链路的情况下,屏蔽某些不重要的业务功能,可以节省系统的处理时间,提供系统的响应能力,在服务器资源固定的前提下处理更多的请求。
源码拆解和分析
3.1 熔断
无论是令牌桶、漏桶还是自适应限流的方法,总的来说都是服务端的单机限流方式。虽然服务端限流虽然可以帮助我们抗住一定的压力,但是拒绝请求毕竟还是有成本的。如果我们的本来流量可以支撑 1w qps,加了限流可以支撑在 10w qps 的情况下,仍然可以提供 1w qps 的有效请求,但是流量突然再翻了 10 倍,来到 100w qps 那么服务该挂还是得挂。
所以我们的可用性建设不仅仅是服务端做建设就可以万事大吉了,得在整个链路上的每个组件都做好自己的事情才行,今天我们就来一起看一下客户端上的限流措施:熔断。
熔断器存在三种状态: 关闭(closed) : 关闭状态下没有触发断路保护,所有的请求都正常通行 打开(open) : 当错误阈值触发之后,就进入开启状态,这个时候所有的流量都会被节流,不允许通行 半打开(half-open) : 处于打开状态一段时间之后,会尝试尝试放行一个流量来探测当前 server 端是否可以接收新流量,如果这个没有问题就会进入关闭状态,如果有问题又会回到打开状态
3.1.1 方案对比hystrix-goGoogle SRE保护算法
hystrix-go
Hystrix 是由 Netflex 开发的一款开源组件,提供了基础的熔断功能。 Hystrix 将降级的策略封装在 Command 中,提供了 run 和 fallback 两个方法,前者表示正常的逻辑,比如微服务之间的调用……,如果发生了故障,再执行 fallback 方法返回结果,我们可以把它理解成保底操作。如果正常逻辑在短时间内频繁发生故障,那么可能会触发短路,也就是之后的请求不再执行 run, 而是直接执行 fallback。
hystrix-go 则是用 go 实现的 hystrix 版,更确切的说,是简化版。只是上一次更新还是 2018 年 的一次 pr, 也就毕业了?
使用方法
hystric实现熔断一般包括两步:
第一步:配置熔断规则
第二部:设置熔断逻辑
一个简单的:// 第一步:配置熔断规则 hystrix.ConfigureCommand("wuqq", hystrix.CommandConfig{ Timeout: int(3 * time.Second), MaxConcurrentRequests: 10, SleepWindow: 5000, RequestVolumeThreshold: 10, ErrorPercentThreshold: 30, }) // 第二步:设置熔断逻辑 // Do是异步,Go是同步 _ = hystrix.Do("wuqq", func() error { // talk to other services _, err := http.Get("https://www.baidu.com/") if err != nil { fmt.Println("get error:%v",err) return err } return nil }, func(err error) error { fmt.Printf("handle error:%v ", err) return nil })
Do 函数需要三个参数,第一个参数 commmand 名称,你可以把每个名称当成一个独立当服务,第二个参数是处理正常的逻辑,比如 http 调用服务,返回参数是 err。如果处理调用失败,那么就执行第三个参数逻辑, 我们称为保底操作。由于服务错误率过高导致熔断器开启,那么之后的请求也直接回调此函数。
配置参数含义:
Timeout : 执行 command 的超时时间。
MaxConcurrentRequests :command 的最大并发量 。
SleepWindow :当熔断器被打开后,SleepWindow 的时间就是控制过多久后去尝试服务是否可用了。
RequestVolumeThreshold : 一个统计窗口 10 秒内请求数量。达到这个请求数量后才去判断是否要开启熔断
ErrorPercentThreshold :错误百分比,请求数量大于等于 RequestVolumeThreshold 并且错误率到达这个百分比后就会启动熔断
核心实现
核心实现的方法是 AllowRequest,IsOpen判断当前是否处于熔断状态,allowSingleTest就是去看是否过了一段时间需要重新进行尝试func (circuit *CircuitBreaker) AllowRequest() bool { return !circuit.IsOpen() || circuit.allowSingleTest() }
IsOpen先看当前是否已经打开了,如果已经打开了就直接返回就行了,如果还没打开就去判断请求数量是否满足要求请求的错误率是否过高,如果两个都满足就会打开熔断器func (circuit *CircuitBreaker) IsOpen() bool { circuit.mutex.RLock() o := circuit.forceOpen || circuit.open circuit.mutex.RUnlock() if o { return true } if uint64(circuit.metrics.Requests().Sum(time.Now())) < getSettings(circuit.Name).RequestVolumeThreshold { return false } if !circuit.metrics.IsHealthy(time.Now()) { // too many failures, open the circuit circuit.setOpen() return true } return false }
hystrix-go已经可以比较好的满足我们的需求,但是存在一个问题就是一旦触发了熔断,在一段时间之内就会被一刀切 的拦截请求,所以我们来看看 google sre 的一个实现
Google SRE保护算法
这个算法的好处是不会直接一刀切的丢弃所有请求,而是计算出一个概率来进行判断,当成功的请求数量越少,K越小的时候计算出的概率就越大,表示这个请求被丢弃的概率越大
Kratos源码分析func (b *sreBreaker) Allow() error { // 统计成功的请求,和总的请求 success, total := b.summary() // 计算当前的成功率 k := b.k * float64(success) if log.V(5) { log.Info("breaker: request: %d, succee: %d, fail: %d", total, success, total-success) } // 统计请求量和成功率 // 如果 qps 比较小,不触发熔断 // 如果成功率比较高,不触发熔断,如果 k = 2,那么就是成功率 >= 50% 的时候就不熔断 if total < b.request || float64(total) < k { if atomic.LoadInt32(&b.state) == StateOpen { atomic.CompareAndSwapInt32(&b.state, StateOpen, StateClosed) } return nil } if atomic.LoadInt32(&b.state) == StateClosed { atomic.CompareAndSwapInt32(&b.state, StateClosed, StateOpen) } // 计算一个概率,当 dr 值越大,那么被丢弃的概率也就越大 // dr 值是,如果失败率越高或者是 k 值越小,那么它越大 dr := math.Max(0, (float64(total)-k)/float64(total+1)) drop := b.trueOnProba(dr) if log.V(5) { log.Info("breaker: drop ratio: %f, drop: %t", dr, drop) } if drop { return ecode.ServiceUnavailable } return nil } // 通过随机来判断是否需要进行熔断 func (b *sreBreaker) trueOnProba(proba float64) (truth bool) { b.randLock.Lock() truth = b.r.Float64() < proba b.randLock.Unlock() return }
熔断与failover结合的思想
一句话总结:请求先进入CircuitBreaker根据当前熔断器策略决定请求主集群或备集群,若请求主集群且主集群请求失败,则进入Failover逻辑Failover到备集群中获取数据。
3.2 限流
限流,也称流量控制。是指系统在面临高并发,或者大流量请求的情况下,限制新的请求对系统的访问,从而保证系统的稳定性。限流会导致部分用户请求处理不及时或者被拒,这就影响了用户体验。所以一般需要在系统稳定和用户体验之间平衡一下。
3.2.1 固定窗口
固定时间内对请求书进行限制,例如说每秒请求不超过50次,那就在0-1秒,1-2秒……n-n+1秒,每秒不超过50次请求。
可是会出现一个问题,在0.99秒和1.01秒分别有50次请求,对于固定窗口方法,不会限流,但是实际上在0.99秒-1.01秒,这一段不到1s的时间内已经达到了阙值的两倍,以下的滑动窗口方法可以解决这个问题。
3.2.2 滑动窗口
算法思想 滑动时间窗口算法,是从对普通时间窗口计数的优化。 使用普通时间窗口时,我们会为每个user_id/ip维护一个KV: uidOrIp: timestamp_requestCount。假设限制1秒1000个请求,那么第100ms有一个请求,这个KV变成 uidOrIp: timestamp_1,递200ms有1个请求,我们先比较距离记录的timestamp有没有超过1s,如果没有只更新count,此时KV变成 uidOrIp: timestamp_2。当第1100ms来一个请求时,更新记录中的timestamp并重置计数,KV变成 uidOrIp: newtimestamp_1 普通时间窗口有一个问题,假设有500个请求集中在前1s的后100ms,500个请求集中在后1s的前100ms,其实在这200ms没就已经请求超限了,但是由于时间窗每经过1s就会重置计数,就无法识别到此时的请求超限。 对于滑动时间窗口,我们可以把1ms的时间窗口划分成10个time slot, 每个time slot统计某个100ms的请求数量。每经过100ms,有一个新的time slot加入窗口,早于当前时间100ms的time slot出窗口。窗口内最多维护10个time slot,储存空间的消耗同样是比较低的。适用场景 与令牌桶一样,有应对突发流量的能力go语言实现 主要就是实现sliding window算法。可以参考Bilibili开源的kratos框架里circuit breaker用循环列表保存time slot对象的实现,他们这个实现的好处是不用频繁的创建和销毁time slot对象。下面给出一个简单的基本实现:package main import ( "fmt" "sync" "time" ) var winMu map[string]*sync.RWMutex func init() { winMu = make(map[string]*sync.RWMutex) } type timeSlot struct { timestamp time.Time // 这个timeSlot的时间起点 count int // 落在这个timeSlot内的请求数 } func countReq(win []*timeSlot) int { var count int for _, ts := range win { count += ts.count } return count } type SlidingWindowLimiter struct { SlotDuration time.Duration // time slot的长度 WinDuration time.Duration // sliding window的长度 numSlots int // window内最多有多少个slot windows map[string][]*timeSlot maxReq int // win duration内允许的最大请求数 } func NewSliding(slotDuration time.Duration, winDuration time.Duration, maxReq int) *SlidingWindowLimiter { return &SlidingWindowLimiter{ SlotDuration: slotDuration, WinDuration: winDuration, numSlots: int(winDuration / slotDuration), windows: make(map[string][]*timeSlot), maxReq: maxReq, } } // 获取user_id/ip的时间窗口 func (l *SlidingWindowLimiter) getWindow(uidOrIp string) []*timeSlot { win, ok := l.windows[uidOrIp] if !ok { win = make([]*timeSlot, 0, l.numSlots) } return win } func (l *SlidingWindowLimiter) storeWindow(uidOrIp string, win []*timeSlot) { l.windows[uidOrIp] = win } func (l *SlidingWindowLimiter) validate(uidOrIp string) bool { // 同一user_id/ip并发安全 mu, ok := winMu[uidOrIp] if !ok { var m sync.RWMutex mu = &m winMu[uidOrIp] = mu } mu.Lock() defer mu.Unlock() win := l.getWindow(uidOrIp) now := time.Now() // 已经过期的time slot移出时间窗 timeoutOffset := -1 for i, ts := range win { if ts.timestamp.Add(l.WinDuration).After(now) { break } timeoutOffset = i } if timeoutOffset > -1 { win = win[timeoutOffset+1:] } // 判断请求是否超限 var result bool if countReq(win) < l.maxReq { result = true } // 记录这次的请求数 var lastSlot *timeSlot if len(win) > 0 { lastSlot = win[len(win)-1] if lastSlot.timestamp.Add(l.SlotDuration).Before(now) { lastSlot = &timeSlot{timestamp: now, count: 1} win = append(win, lastSlot) } else { lastSlot.count++ } } else { lastSlot = &timeSlot{timestamp: now, count: 1} win = append(win, lastSlot) } l.storeWindow(uidOrIp, win) return result } func (l *SlidingWindowLimiter) getUidOrIp() string { return "127.0.0.1" } func (l *SlidingWindowLimiter) IsLimited() bool { return !l.validate(l.getUidOrIp()) } func main() { limiter := NewSliding(100*time.Millisecond, time.Second, 10) for i := 0; i < 5; i++ { fmt.Println(limiter.IsLimited()) } time.Sleep(100 * time.Millisecond) for i := 0; i < 5; i++ { fmt.Println(limiter.IsLimited()) } fmt.Println(limiter.IsLimited()) for _, v := range limiter.windows[limiter.getUidOrIp()] { fmt.Println(v.timestamp, v.count) } fmt.Println("a thousand years later...") time.Sleep(time.Second) for i := 0; i < 7; i++ { fmt.Println(limiter.IsLimited()) } for _, v := range limiter.windows[limiter.getUidOrIp()] { fmt.Println(v.timestamp, v.count) } }
3.2.3 漏桶
算法思想 与令牌桶是"反向"的算法,当有请求到来时先放到木桶中,worker以固定的速度从木桶中取出请求进行响应。如果木桶已经满了,直接返回请求频率超限的错误码或者页面适用场景 流量最均匀的限流方式,一般用于流量"整形",例如保护数据库的限流。先把对数据库的访问加入到木桶中,worker再以db能够承受的qps从木桶中取出请求,去访问数据库。不太适合电商抢购和微博出现热点事件等场景的限流,一是应对突发流量不是很灵活,二是为每个user_id/ip维护一个队列(木桶),workder从这些队列中拉取任务,资源的消耗会比较大。go语言实现 通常使用队列来实现,在go语言中可以通过buffered channel来快速实现,任务加入channel,开启一定数量的worker从channel中获取任务执行。package main import ( "fmt" "sync" "time" ) // 每个请求来了,把需要执行的业务逻辑封装成Task,放入木桶,等待worker取出执行 type Task struct { handler func() Result // worker从木桶中取出请求对象后要执行的业务逻辑函数 resChan chan Result // 等待worker执行并返回结果的channel taskID int } // 封装业务逻辑的执行结果 type Result struct { } // 模拟业务逻辑的函数 func handler() Result { time.Sleep(300 * time.Millisecond) return Result{} } func NewTask(id int) Task { return Task{ handler: handler, resChan: make(chan Result), taskID: id, } } // 漏桶 type LeakyBucket struct { BucketSize int // 木桶的大小 NumWorker int // 同时从木桶中获取任务执行的worker数量 bucket chan Task // 存方任务的木桶 } func NewLeakyBucket(bucketSize int, numWorker int) *LeakyBucket { return &LeakyBucket{ BucketSize: bucketSize, NumWorker: numWorker, bucket: make(chan Task, bucketSize), } } func (b *LeakyBucket) validate(task Task) bool { // 如果木桶已经满了,返回false select { case b.bucket <- task: default: fmt.Printf("request[id=%d] is refused ", task.taskID) return false } // 等待worker执行 <-task.resChan fmt.Printf("request[id=%d] is run ", task.taskID) return true } func (b *LeakyBucket) Start() { // 开启worker从木桶拉取任务执行 go func() { for i := 0; i < b.NumWorker; i++ { go func() { for { task := <-b.bucket result := task.handler() task.resChan <- result } }() } }() } func main() { bucket := NewLeakyBucket(10, 4) bucket.Start() var wg sync.WaitGroup for i := 0; i < 20; i++ { wg.Add(1) go func(id int) { defer wg.Done() task := NewTask(id) bucket.validate(task) }(i) } wg.Wait() }
打破线上社交不可能三角,语音社交可以做到既要又要还要摘要经济学里,有一个概念叫不可能三角,即高收益低风险和高流动性三者不可能同时实现但这个既要又要还要的难题,在擎声的实时语音互动场景里,只需要一个极简易用的SDK,就能够让实时语音通
她是足坛绯红女巫!18岁傍上球王,伊卡尔迪被玩弄在股掌上我被认为是意大利最知名的婊子,每一个人都等着看我的笑话。旺达一直都知道,在别人眼中自己是什么一个样子。不过她给自己的定位却是满满的正能量努力又谦虚忠诚且直率幽默而风趣的母亲女人挚友
击败特奥米林!官方韩国国脚金玟哉当选意甲9月最佳球员直播吧9月30日讯官方消息,那不勒斯韩国中卫金玟哉当选意甲9月最佳球员,他在评选中击败特奥米林科维奇等人。意甲8月最佳球员也是由那不勒斯中场克瓦拉茨赫利亚获得。金玟哉对阵拉齐奥破门
他是世界足坛最具争议性的球员!史上最厉害的射手!是球队的灵魂教练,求求你了!我儿子很有天赋的!求求你了,教练,再给这混小子一次机会吧。张扬站在一边,他的拳头攥得紧紧的,他看着眼前的爸爸低着头,点头哈腰地向对方求情,希望能给儿子一个机会,而对
揭秘火箭雷霆4换5大交易涉及8名球员换队更多交易细节曝光北京时间9月30日,据ESPN名记沃纳罗斯基报道,消息源透露,雷霆将与火箭达成一笔多人交换的大型交易,涉及8名球员和1个选秀权。点击输入图片描述(最多30字)点击输入图片描述(最多
03!女排世锦赛3队遭遇4连败,提前无缘16强,头号弱旅出炉北京时间2022年9月30日,世界女排锦标赛结束第7比赛日争夺,克罗地亚喀麦隆韩国均以03输球,这三支球队遭遇4连败,提前无缘16强。到目前为止,克罗地亚只赢了一局,喀麦隆和韩国连
究竟何为纯电四小龙,本篇文章一一为您介绍爱卡汽车导购原创毫不夸张的说,在过去的1520年间,SUV始终是中国品牌发展的主要方向。但当时间来到2022年,各家中国品牌却不约而同地调转船头,重新将目光聚焦在了轿车领域,一时间
传奇人物力挽狂澜,中国芯片的两大功臣芯芯之火,可以燎原。张汝京以一己之力拯救中国芯片产业把海外所学的带到大陆,开工厂建社区办学校,为国家民族作贡献也做慈善事业。张汝京,中芯国际创始人,中国半导体之父。是个地地道道的台
三年巨亏44亿,零跑汽车上市首日破发,暴跌33造车新势力,市场选择用脚投票。零跑汽车首日破发今天(9月29日)零跑汽车正式在港交所主板挂牌上市,成为继蔚来,理想,小鹏汽车之后,第四家成功上市的造车新势力。同时,这也是首家第二梯
A股疲软,人民币暴跌,谁在做空中国?9月15日,离岸人民币兑美元跌破7。0,仅仅13天之后,人民再度暴跌,一度跌破7。26。半个月不到,跌3。7,在汇率市场,绝对是惊天动地的大事了。和人民币走势相近的还有A股,近期也
日本8家车企8月全球产量增长31日经新闻9月30日报道,丰田等日本8家乘用车企业9月29日发布的数据显示,8月全球产量比上年同月增长31,增至200。9万辆。2021年8月受新冠疫情影响,产量减少,目前连续3个月
我觉得很理性?头条创作挑战赛不带情感,直观面对,分析厉害,这些都是理性带有的一些色彩。很多人都是兼容这感性和理性的双重状态。故而,在遇到事情的时候可能会存在这理性的分析角度去面对,但实际上很多时
说百病喜怒无常是一病,忘义取利是一病好色坏德是一病,专心系爱是一病憎欲令死是一病,纵贪蔽过是一病毁人自誉是一病,善变自可是一病轻口喜言是一病,快意逐非是一病以智轻人是一病,乘权纵横是一病
初恋情怀初恋情怀自那一日,结你于哗哗的小河边,目见了柳儿拂水后,我的颤颤抖动的心呀,便随着隐隐闪闪的涟漪儿荡漾。说过的,谁先有了梦,便来这里相聚,悄悄的,说给柳听,说给水听,说给水中的鱼儿
揭秘地下2400米的实验室长什么样国投集团雅砻江水电与清华大学共建的中国锦屏地下实验室地下2400米的实验室有哪些黑科技?主导宇宙演化的洪荒之力到底是什么?占宇宙95的暗物质与暗能量究竟是怎样的存在?探测暗物质对揭
穷人是因为懒惰,无知,没有自控力,所以穷么?穷人之所以穷,最大的原因当然是他没有有钱的父母。勤奋,意志力,本质上是一种可消耗的资源。假设你的意志力总是花在错误的事情上,那么它就不会得到正确的反馈。举个例子,如果你总是靠饥饿来
五十岁之后聪明的女人,越活越抠门01hr老话说女人心,海底针。从字面上去理解,女人的心,就好像一根针一样小,又像海底捞针一样难以理解。对生活细心,对家人关心,这是女人的天性,也是针尖的本性带来的好处。匆匆忙忙走过
致女人那些人到中年才明白的事四十以后才明白朋友就像是水中的鱼,深水层和浅水层的鱼永远也不会走在一起穷和富,官和民,草根和显贵都不可能成为真正的朋友,因为各自对人生的感悟不同,最关键是他们对待世界和自然的心不同
沐浴养生1利用水泥沙日光空气中药汤液等有形或无形的天然物理介质作用于体表已达到强生健体延年益寿为目的的养生方法。根据沐浴的不同方式从而达到发汗解表祛风除湿行气活血舒筋活络宁心安神调和阴阳。水
茶叶超过保质期就会变质吗?过期茶还能喝吗?爱喝茶的可以看看中国是茶叶的故乡,是最早发现种植茶的国家,在此后的发展中,中国的茶叶种类不断丰富,形成了完整的六大茶系。在中国,喝茶是很多人都有的习惯。但总有一个问题会被忽略掉茶叶的保质期。很多人
百趣代谢组学解读,深度解码蜜蜂肠菌调节宿主行为和神经功能文章标题DistinctRolesofHoneybeeGutBacteriaonHostMetabolismandNeurologicalProcesses发表期刊Microbio
金星才是旗袍爱好者,每一套都穿出独有的韵味,大气端庄又优雅如果有一种服饰能把中国女人的柔与刚相结合,那我想就是线条简洁且干练的旗袍了。旗袍作为东方的一张名片,它可以说是专门为东方女性所打造的。比起汉服的累赘浮夸,现代装的单一,旗袍更加多元
孙申田小小银针治各类疾病来源人民日报健康客户端专家简介孙申田,黑龙江中医药大学附属第二医院主任医师教授擅长应用针灸科及中医中药疗法治疗各种神经内科疾病神经症内外妇儿五官科疾病出诊时间周一至周五上午孙申田为
疾病也有专属睡姿,你,睡对了吗?人一天中,睡觉时间占据了三分之一,所以睡眠时间显得极为重要。睡觉姿势决定了你的睡眠质量,也会对你的身体造成不同程度的影响,尤其是患有某些慢性疾病的人群。如果你患有以下病症,你还在左
新鲜莲子全身宝,食用禁忌要牢记夏季正值荷花盛开,成片的荷叶加上亭亭玉立于其中的鲜艳的荷花,总是能让人心情愉悦。荷花盛开,新鲜的莲子也应季上市了。新鲜莲子的功效1防癌抗癌莲子善于补五脏不足,通利十二经脉气血,使气
推荐大家一个润燥养津的食谱,搞定秋天常见的咳嗽干燥和便秘我们常说秋燥,一到秋天很多人就容易上火,感觉嘴巴干鼻子干,有些人甚至会干咳,便秘今天就给大家推荐几款润燥养津的食谱,来帮助缓解秋燥,搞定秋天常见的咳嗽干燥和便秘的情况。1莲藕粥莲藕
秋天,你好这个夏天结束了,好像还有好多事没有做,好多人没有见,好多话没有说。在这个夏日,热浪席卷全国,高温少雨旱情,牵动了多少人的心,燃烧的山火中燃起多少人的爱心。在这个夏天,开始害怕夜里去
感悟秋天一场绵绵的秋雨,将夏天远远的抛开,自北国吹来的阵阵凉风,驱走了高温闷热的酷暑,将一片片泛黄的树叶吹落撒满一地。陡然凉爽的空气,催人一早一晚加衣盖被,防止秋寒的侵袭。人说自古秋日悲寂
45岁男子,酒后心梗去世,提醒酒后不要做四件事,加大心梗风险很多人都喜欢喝酒,但是酒后,你会怎么做呢?不同的人有不同的见解,有的人认为,喝了酒,醉醺醺的,看世界都是转的,当然只能好好休息,过一夜,酒醒了就好了。也有人说,喝多了酒,一定不能睡
2022年上海市社保基数标准(缴费基数一览表)随着2021年度工资性收入申报工作的结束,上海公布最新社保基数调整结果(自助经办系统里已更新),2022年7月开始上海社保最低基数由原来的5975调整为6520。上海社保系统数据已
41!日本队大获全胜,早田希娜勇夺女单冠军,国乒14人无缘四强2022年9月18日,乒乓球WTT哈萨克斯坦赛迎来收官日,国乒成为看客,没有队员打进四个项目的决赛,率先结束的女单决赛,日本名将早田希娜41战胜葡萄牙选手付玉,夺得女单冠军,拿到4
阿根廷国家队历史射手榜梅西稳居首位,现役三人进前十直播吧9月19日讯阿根廷国家队最新一期的大名单当中,梅西迪马利亚劳塔罗马丁内斯全部入选,在阿根廷队历史射手榜前十名当中,只有这三人是现役国脚(伊瓜因已退出国家队)。其中梅西以86球
国家发改委答澎湃跨区通道按最大能力支援四川能源保供9月19日,在国家发展改革委召开的9月份新闻发布会上,澎湃新闻(www。thepaper。cn)记者提问今年夏季,四川持续高温干旱天气对电力保供形成挑战,请问国家发改委如何应对这一