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
有疑问加站长微信联系(非本文作者))