基于信号量Semaphore的服务接口限流实例

前景回顾:
《基于计数器的服务接口限流实例》
《基于RateLimiter的服务接口限流实例》

一、Semaphore信号量的介绍

Semaphore是一种在多线程环境下使用的设施,该设施负责协调各个线程,以保证它们能够正确、合理的使用公共资源的设施,也是操作系统中用于控制进程同步互斥的量。

我们通常使用Semaphore来确保系统中的最大线程并发数量。

二、使用信号量的acquire和release

我们在以下实例中,设置getUserMail这个URL最多只能同时被两个请求访问,其它请求要想访问该URL,必须等待前面的请求释放信号量才行。

@Slf4j
@RestController
public class UserMailRest {
    /**
     * 同一时刻最多只允许有两个并发
     */
    private Semaphore semaphore = new Semaphore(2);

    @GetMapping("/getUserMail")
    public String getUserMail() {
        try {
            // 获取信号量的请求立即得到执行,未获取到信号量的请求需要等待
            semaphore.acquire();
            Thread.sleep(2000);
            log.info("请求得到服务!");
            return "OK";
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 请求结束后将信号量释放供其它请求使用
            semaphore.release();
        }
        return "fail";
    }
}

我们使用Jmeter模拟10个用户在1秒内的并发请求该URL得到的结果如下:

2019-12-24 20:59:04.862: 请求得到服务!
2019-12-24 20:59:04.862: 请求得到服务!
2019-12-24 20:59:06.864: 请求得到服务!
2019-12-24 20:59:06.864: 请求得到服务!
2019-12-24 20:59:08.865: 请求得到服务!
2019-12-24 20:59:08.865: 请求得到服务!
2019-12-24 20:59:10.867: 请求得到服务!
2019-12-24 20:59:10.867: 请求得到服务!
2019-12-24 20:59:12.869: 请求得到服务!
2019-12-24 20:59:12.869: 请求得到服务!

可以看出,该URL确实在同一时间最多只能供两个请求访问。

三、使用信号量的tryAcquire

和前面文章中提到的RateLimiter类似,Semaphore也有tryAcquire方法,用来拒绝单位时间内超出的并发请求。

@Slf4j
@RestController
public class UserMailRest {
    /**
     * 同一时刻最多只允许有两个并发
     */
    private Semaphore semaphore = new Semaphore(2);

    @GetMapping("/getUserMail")
    public String getUserMail() {
        if(!semaphore.tryAcquire()){
            log.warn("调用太频繁了,拒绝服务!");
            return "调用太频繁了,拒绝服务!";
        }
        try {
            log.info("接受服务!");
            Thread.sleep(2000);
            return "ok";
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
        }
        return "fail";
    }
}

运行结果如下:

2019-12-24 21:15:16.034 : 接受服务!
2019-12-24 21:15:16.034 : 接受服务!
2019-12-24 21:15:16.050 : 调用太频繁了,拒绝服务!
2019-12-24 21:15:16.149 : 调用太频繁了,拒绝服务!
2019-12-24 21:15:16.259 : 调用太频繁了,拒绝服务!
2019-12-24 21:15:16.352 : 调用太频繁了,拒绝服务!
2019-12-24 21:15:16.455 : 调用太频繁了,拒绝服务!
2019-12-24 21:15:16.552 : 调用太频繁了,拒绝服务!
2019-12-24 21:15:16.658 : 调用太频繁了,拒绝服务!
2019-12-24 21:15:16.754 : 调用太频繁了,拒绝服务!

可以看出,与acquire的区别是,超出的8个请求全部直接被拒绝了,而不是一直等待信号量的释放。

四、总结

Semaphore是比较常用的服务接口限流方案,它与计数器和RateLimiter比较最大的优点是,能限制住接口的最大并发数,哪怕每一个请求都是Long Call,也都不用害怕累计的并发线程会将服务器压垮的问题了。

全文完。

你可能感兴趣的:(基于信号量Semaphore的服务接口限流实例)