Golang 使用channel作并发访问吞吐量限制
    golang中可以使用Buffered channel作为信号量来对服务的并发访问作吞吐量限制。
如下代码中,Serve函数遍历请求队列,对每次请求,启动一个goroutine来进行handle,sem的缓冲大小限制了同时调用handle函数的数量,Serve函数虽可保障每一刻最多有MaxOutstanding个goroutine正在调用handle函数,但在请求过频与过多的情况下无法保证goroutine的过度创建以造成资源耗尽的风险。
ServeWithThroughputLimit函数对Serve作了改进,即对给sem发送消息提到了goroutine创建之前,以对goroutine的创建作限制。这样,同一时刻最多有MaxOutstanding个goroutine对请求进行handle。
golang中可以使用Buffered channel作为信号量来对服务的并发访问作吞吐量限制。
如下代码中,Serve函数遍历请求队列,对每次请求,启动一个goroutine来进行handle,sem的缓冲大小限制了同时调用handle函数的数量,Serve函数虽可保障每一刻最多有MaxOutstanding个goroutine正在调用handle函数,但在请求过频与过多的情况下无法保证goroutine的过度创建以造成资源耗尽的风险。
ServeWithThroughputLimit函数对Serve作了改进,即对给sem发送消息提到了goroutine创建之前,以对goroutine的创建作限制。这样,同一时刻最多有MaxOutstanding个goroutine对请求进行handle。
<div class="dp-highlighter"><div class="bar"></div><ol start="1" class="dp-j"><li class="alt"><span><span class="keyword">package</span><span> main  </span></span></li><li class=""><span>  </span></li><li class="alt"><span><span class="keyword">import</span><span> (  </span></span></li><li class=""><span>    <span class="string">"fmt"</span><span>  </span></span></li><li class="alt"><span>    <span class="string">"sync"</span><span>  </span></span></li><li class=""><span>    <span class="string">"time"</span><span>  </span></span></li><li class="alt"><span>)  </span></li><li class=""><span>  </span></li><li class="alt"><span><span class="keyword">const</span><span> MaxOutstanding = </span><span class="number">2</span><span>  </span></span></li><li class=""><span>  </span></li><li class="alt"><span><span class="keyword">type</span> Req <span class="keyword">struct</span> {  </span></li><li class=""><span>    id <span class="keyword">int</span><span>  </span></span></li><li class="alt"><span>}  </span></li><li class=""><span>  </span></li><li class="alt"><span><span class="keyword">func</span> handle(req *Req) {  </span></li><li class=""><span>    time.Sleep(time.Second)  </span></li><li class="alt"><span>    fmt.Println(<span class="string">"handle req"</span><span>, req.id)  </span></span></li><li class=""><span>}  </span></li><li class="alt"><span>  </span></li><li class=""><span><span class="keyword">func</span> Serve(queue <span class="keyword">chan</span> *Req) {  </span></li><li class="alt"><span>    <span class="keyword">var</span> wg sync.WaitGroup  </span></li><li class=""><span>    sem := <span class="keyword">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span><span>, MaxOutstanding)  </span></span></li><li class="alt"><span>    <span class="keyword">for</span><span> req := <span class="keyword">range</span> queue {  </span></span></li><li class=""><span>        wg.Add(<span class="number">1</span><span>)  </span></span></li><li class="alt"><span>        <span class="keyword">go</span> <span class="keyword">func</span>(req *Req) {  </span></li><li class=""><span>            fmt.Println(<span class="string">"a goroutine launched"</span><span>)  </span></span></li><li class="alt"><span>            <span class="keyword">defer</span> wg.Done()  </span></li><li class=""><span>            sem <- <span class="number">1</span><span>  </span></span></li><li class="alt"><span>            handle(req)  </span></li><li class=""><span>            <-sem  </span></li><li class="alt"><span>        }(req)  </span></li><li class=""><span>    }  </span></li><li class="alt"><span>    wg.Wait()  </span></li><li class=""><span>}  </span></li><li class="alt"><span>  </span></li><li class=""><span><span class="keyword">func</span> ServeWithThroughputLimit(queue <span class="keyword">chan</span> *Req) {  </span></li><li class="alt"><span>    <span class="keyword">var</span> wg sync.WaitGroup  </span></li><li class=""><span>    sem := <span class="keyword">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span><span>, MaxOutstanding)  </span></span></li><li class="alt"><span>    <span class="keyword">for</span><span> req := <span class="keyword">range</span> queue {  </span></span></li><li class=""><span>        wg.Add(<span class="number">1</span><span>)  </span></span></li><li class="alt"><span>        sem <- <span class="number">1</span><span>  </span></span></li><li class=""><span>        <span class="keyword">go</span> <span class="keyword">func</span>(req *Req) {  </span></li><li class="alt"><span>            fmt.Println(<span class="string">"a goroutine launched"</span><span>)  </span></span></li><li class=""><span>            <span class="keyword">defer</span> wg.Done()  </span></li><li class="alt"><span>            handle(req)  </span></li><li class=""><span>            <-sem  </span></li><li class="alt"><span>        }(req)  </span></li><li class=""><span>    }  </span></li><li class="alt"><span>    wg.Wait()  </span></li><li class=""><span>}  </span></li><li class="alt"><span>  </span></li><li class=""><span><span class="keyword">func</span> main() {  </span></li><li class="alt"><span>    queue := <span class="keyword">make</span>(<span class="keyword">chan</span> *Req, <span class="number">5</span><span>)  </span></span></li><li class=""><span>  </span></li><li class="alt"><span>    <span class="comment">// requests</span><span>  </span></span></li><li class=""><span>    <span class="keyword">go</span> <span class="keyword">func</span>() {  </span></li><li class="alt"><span>        <span class="keyword">for</span><span> i := </span><span class="number">0</span><span>; i < </span><span class="number">5</span><span>; i++ {  </span></span></li><li class=""><span>            queue <- &Req{i}  </span></li><li class="alt"><span>        }  </span></li><li class=""><span>        <span class="keyword">close</span>(queue)  </span></li><li class="alt"><span>    }()  </span></li><li class=""><span>  </span></li><li class="alt"><span>    <span class="comment">// server</span><span>  </span></span></li><li class=""><span>    <span class="comment">// Serve(queue)</span><span>  </span></span></li><li class="alt"><span>    ServeWithThroughputLimit(queue)  </span></li><li class=""><span>}  </span></li></ol></div>
调用Serve函数的输出为:
<div class="dp-highlighter nogutter"><div class="bar"></div><ol start="1" class="dp-j"><li class="alt"><span><span>a goroutine launched  </span></span></li><li class=""><span>a goroutine launched  </span></li><li class="alt"><span>a goroutine launched  </span></li><li class=""><span>a goroutine launched  </span></li><li class="alt"><span>a goroutine launched  </span></li><li class=""><span>handle req <span class="number">4</span><span>  </span></span></li><li class="alt"><span>handle req <span class="number">3</span><span>  </span></span></li><li class=""><span>handle req <span class="number">1</span><span>  </span></span></li><li class="alt"><span>handle req <span class="number">2</span><span>  </span></span></li><li class=""><span>handle req <span class="number">0</span><span>  </span></span></li></ol></div>
调用ServeWithThroughputLimit函数的输出为:
<div class="dp-highlighter nogutter"><div class="bar"></div><ol start="1" class="dp-j"><li class="alt"><span><span>a goroutine launched  </span></span></li><li class=""><span>a goroutine launched  </span></li><li class="alt"><span>handle req <span class="number">0</span><span>  </span></span></li><li class=""><span>a goroutine launched  </span></li><li class="alt"><span>handle req <span class="number">1</span><span>  </span></span></li><li class=""><span>a goroutine launched  </span></li><li class="alt"><span>handle req <span class="number">2</span><span>  </span></span></li><li class=""><span>a goroutine launched  </span></li><li class="alt"><span>handle req <span class="number">3</span><span>  </span></span></li><li class=""><span>handle req <span class="number">4</span><span>  </span></span></li></ol></div>
本文代码托管地址:<a href="https://github.com/olzhy/go-excercises/tree/master/throughput_limit" target="blank">https://github.com/olzhy/go-excercises/tree/master/throughput_limit</a>
原文地址:https://leileiluoluo.com/posts/golang-throughput-limit.html
  
  
