zoukankan      html  css  js  c++  java
  • go

    一、项目架构图

    二、现有问题 

    • register接口接收大量的SDK请求,但并未对请求的并发数进行控制,导致服务无法拥有足够的内存,从而频繁被系统 Kill。

    三、解决方案

    • consul中启用健康检查,让节点内存、CPU资源紧张时能“休息一下” 
    • register里面根据节点内存剩余量做过载保护,并将过载错误码返回给SDK,SDK延时重试。注:SDK目前对所有错误码都重试,理论上只有过载和服务器内部错误需要重试。
    • 微服务在系统内存紧张时手动GC,释放内存,让节点迅速恢复健康。
    • 把register和其他服务混布,增加节点数,充分利用资源。
    •  register对其他服务的访问目前是串行的,这不够高效,只要没有依赖关系,就应改为并行
    • 根据请求处理时间延时做负载判断,使用令牌桶做自适应限流
    • 排查register依赖的服务,找出真正的瓶颈所在

    四、具体实现

    新请求限流:使用 time/rate 实现令牌桶逻辑,控制每秒内所接收的新请求数量

    在途请求限流:记录当前正在处理的请求数量,当接收到SDK请求时,将 curConcurrency 加1,代表这个请求正在被处理(在途请求);在响应前将 curConcurrency 加1,代表这个请求已经完成。需配置在途请求的最大并发数,只有在当前的 curConcurrency < maxConcurrency 时,才对请求进行处理,否则将请求直接丢弃。

    // 构造一个限流器对象        NewLimiter(r, b): r 每秒可向Token桶中产生多少 token,b Token桶的容量大小
    limiter := NewLimiter(10, 1);        // 令牌桶大小为 1, 以每秒 10 个 token 的速率向桶中放置 Token
    
    
    // Every 方法用来指定向 Token 桶中放置 token 的时间间隔
    limit := Every(100 * time.Millisecond);        // 每 100ms 往桶中放一个 Token; 一秒钟产生 10 个token
    limiter := NewLimiter(limit, 1);
    
    
    // 消费方式:
    
    // Wait/WaitN: 阻塞消费
    // 调用 Wait 方法消费时, 若桶内的token数组长度 不足n(小于n), Wait方法将会阻塞, 直到token数量满足条件位置; 如果桶内token数组满足条件则直接返回
    func (lim *Limiter) Wait(ctx context.Context) (err error)
    func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)    // 可设置 ctx(context) 参数的 Deadline、Timeout 来决定此次 Wait 的最长时间
    
    
    
    // Allow/AllowN: 桶内token满足条件消费
    // AllowN: 截止到某一时刻, 当前桶内token数量是否至少为n个, 满足返回true, 同时从桶内消费n个token; 反之返回false不消费token
    func (lim *Limiter) Allow() bool
    func (lim *Limiter) AllowN(now time.Time, n int) bool
    
    
    
    // Reserve/ReserveN: 对象等待消费
    // 调用 ReserveN 方法后, 无论 token 是否充足都会返回一个 Reservation* 对象, 再调用该对象的 Delay 方法, 可返回需要等待的时间, 若等待时间为0, 则无需等待; 
    // 否则必须等待到之后时间后, 才能继续工作; 若期间不想再等待可调用 Cancel方法取消, 它将会把 token 归还
    r := lim.Reserve()
    f !r.OK() {
        // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
        return
    }
    time.Sleep(r.Delay())
    Act() // 执行相关逻辑
    
    
    
    // 动态调整速率
    //Limiter 支持可以调整速率和桶大小:
    SetLimit(Limit)     // 改变放入 Token 的速率, 接收的参数: 如果为 1、2、3...等数字可直接传递, 如果是将数字保存在变量中时(基本类型 int64、float64...) 必须使用 rate.Limit(variable) 进行转换为 Limit 类型, 即使底层的数据类型是一致
    SetBurst(int)         // 改变 Token 桶大小
    time/rate 使用

    为什么要实现在途请求的限流?

    服务当中是以上一秒的总请求数、正常处理请求数、超时请求数作为,下一秒设置令牌桶大小的参考, 控制着当前这1秒应该产生多少token,令牌桶的大小决定了 当前这一秒允许多少个的新请求进入屋子等待服务的响应。将令牌桶想象成大队长,它专用来清点请求数量,放指定数量的请求进屋;虽然大队长能够控制这一秒应该进去多少请求,但它无法控制这群请求什么时候出来,会在屋子里面呆多久的时间。假设每个请求需要5s才能返回响应,每秒假设会由令牌桶大队长放进来100个请求,那么到了第5秒的时候,屋子里面总共会有500个请求,既包括最后1秒的新请求,又包括了前4秒的在途请求。这些请求越积越多,但是屋子只有那么小,最后装不下了,内存也就爆了(总请求数=新请求+在途请求)。而并发控制就是为了解决 令牌桶 无法控制屋子里的总请求数的缺陷。

    令牌桶 博客参考链接:

     

    consul 参考链接:

  • 相关阅读:
    HDU 2236 无题Ⅱ
    Golden Tiger Claw(二分图)
    HDU 5969 最大的位或 (思维,贪心)
    HDU 3686 Traffic Real Time Query System (图论)
    SCOI 2016 萌萌哒
    Spring Boot支持控制台Banner定制
    构建第一个Spring Boot程序
    Spring Boot重要模块
    Java fastjson JSON和String互相转换
    BCompare 4 Windows激活方法【试用期30天重置】
  • 原文地址:https://www.cnblogs.com/hsmwlyl/p/12434125.html
Copyright © 2011-2022 走看看