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 参考链接:

  • 相关阅读:
    C#基于接口的编程
    在window组件服务中建立WCF宿主
    给WWF传递参数
    创建singleton模式的工作流引擎
    WCF消息交换模式之Duplex
    SqlServer中查询出多条记录变成一个记录(行变列)
    WCF三种消息交互模式MEP
    Asp.net实现在线截图!
    WWF之跟踪服务WorkFlowTrack
    hdu 2604 Queuing (Matrix)
  • 原文地址:https://www.cnblogs.com/hsmwlyl/p/12434125.html
Copyright © 2011-2022 走看看