00:00
接下来我们来去看一下sing sing呢,翻译过来就是信号量的意思。那么之前呢,我们也聊过GUUC里面的信号量,那么啊,学习过或者关注过我们商业股课程同学呀,对guc的信号量应该不陌生。那么guc的信号量和我们这个信号量呢,有什么区别呢?啊,其实呢,本质上没有太多区别。只是呢,一个是单机版本的,而一个呢,是分布式版本的啊。好,那我们,呃,这两个信号量的作用啊,都是一样的,它都可以对我们的请求线程来进行限流。他们呢,都适用于需求量大而资源又很紧张这种场景下。那比如说呢,我们在生活中有马路上有信号灯,那么信号灯呢,它就可以对车流量来进行限流。那么马路上车多路少,那怎么办呢?如果我们不进行限流的话,可能会导致每一个路口都被堵死,最终呢,我们的交通呢,就会瘫痪。
01:04
那再比如说呢,我们周末去商场里面进行消费。那么热门商场啊,我们的车位呢,是有限的。那么此时如果我们不进行限流的话,那么整个停车场可能无法通行了啊,都会被堵死。那么所以呢,我们要进行限流,等有车出来了,我们才会放新的车进去。好,那么使用信号量呢,是非诚适的啊。好,那么在讲我们这个里面这个信号量之前,我们可以先去回顾一下GUUC里面这个信号量。好,我们先去认识一下这个信号量,咱们可以写一个脉冲法来去演示一下。那么这里呢,我们嗯,来定义一个慢方法,那么这个慢方法里面,我们首先来去玩doc这个信号量好,我们可以去搜一下这个C。啊,这么一个玩意儿啊,这guc给咱提供的。
02:00
那么JUC呢,提供的是C,它是一个不同的类,那我是不是可以直接去new呢?咱们了解看一下有没有构造偶法。那么这个里面啊,它明显是有构造方法的。那么最简单的构造方法,它就需要一个啊,资源量这样的一个玩意儿。好,Int里面可以指定一个资源量。那比如说呢,我们现在有三个停车位啊,假设我们这个商场还是个小商场,有三个停车位。结果呢?来了六辆车来抢停车位。那我们这个int呀,就可以设置为三。设置为三,我们只有三个资源量。那么结果六个车,六个车就相当于六个请求线程来去争抢这三个资源。那么六个呢,肯定是有三个线程可以抢成功。那么剩下三个怎么办呢?剩下三个它只能在外面等着。等有车开走了,他才能占用这个停车位。好,那我们,呃,到时候去可以去初始化一个C,然后有三个停车位。
03:01
好,那我们呢,可以呢,边分析边去写我们的这样的一个应用程序。那么首先啊,咱是六辆车,我们可以去初始化六个线程啊,六个线程。那么然后呢,呃,这个地方我们可以来一个,嗯,那个车吧,对吧,可以给他来一个编号啊,比如说这是几号车。几号车,那么这个地方呢,咱们是I可以加上这样的一个号车啊,啊就多少号车,那么从零到五。那么然后呢,咱们得要去争抢资源,那么增强哪个资源呢?我们在这个地方初始化一个S啊,这里面可以指定我们的资源量啊,咱是三个停车位来指定一个三。那么六个县城相当于六辆车,可以抢我们这三个资源。
04:01
好,那么现在我们呃,怎么去抢呢。好,接下来我们来看这个S里面还有什么方法。C方里面它有主要是有这样两个方法啊,那么一个是写方法,那它可以呢,获取一个资源。好,那我们可以通过呃呃亏要方法来去获取一个资源B和亏要。啊,获取资源。那么然后呢?呃,我们获取到资源之后,我们可以去执行这个停车的操作了。啊,多少号车,咱可以来一个瑞的。点上kind street,点上get name,来去加上嗯,加上,然后呢,抢到了,抢到了停车。好,那么抢到停车位之后啊,我们可能会停一会儿,来一个time time unit,点上second.sleep,比如就没一个随机时间。呃,睡一定的时间,相当于停了一会儿嘛,比如我你有一个十以内的。
05:04
这里个随机值,那么然后听完之后呢,我们要去开走啊,来一个red,点上kind street点跟name,哎,加上好,那么这是哪个车停了一会儿。停了一会,然后开走了,开走,那开走之后呀,其他人呢,就可以强调停掉位了,那我们得要把这个资源给释放掉,来一个singful,好在sing里面呢,还有这样的一个方法,好我们可以去看一下。还有这个release方法,它可以去释放资源。那么其他方法呀,咱几乎呢都用不到,我们主要是啊亏方法以及release方法。好,那么再回到我们这个代码里面去,咱可以点上release啊,把车给开走,把资源给释放掉。那么后续的啊,其他车就可以获取这样的一个停车位了。
06:01
那么有异常我们可以来一个TRY开启一下啊,来个TRY开启。处理一下好,咱们这个代码呢,就已经写好了。那么写好之后呢,我们来去运行一下,看最终效果怎么样。嗯。那我们可以看到,首先呢,零一啊,012号车抢到停车位了。那么然后呢?一号车停了一会儿,然后立马开走了。开走之后呀,三三号车就可以停进来,他就抢到一个停车位,这就可以开进来了。那么然后呢,又停了一会儿之后呢,零号车也开走了。零号车开走之后呀,那么四号车又进来了。那么四号车啊,进来之后,然后二号车也停了一会儿,然后停了一会儿开走了,他开走之后呢,然后五号车就进来了。那么咱这三辆车呢,这停了一会儿也都开走了。那我们可以看到这种效果了啊。好,它呢就可以进行限流。
07:00
啊,限限制我们的,嗯,请求量啊。好,那么这个呢,是单机版本,如果哎,有同学说我如果不使用私法会怎么样呢。那么有时呢,我们把这个给它注释掉。假设我们没有四号,它会怎么样?我们正反面呢,要去演示一下。好,我们来去运行走。那么现在你一下子咱六个车全部都抢到停车位了,那么能抢到了,不应该抢到,因为呢,咱只有三个停车位,结果六辆车呢都抢到了,那这是不可能的啊,现在我们需要通过C来去限制一下啊。那比如说我们在秒杀场景下。那秒杀场景啊,秒杀的环节是非常非常复杂的,那某一个环节可能它的资源量,它需要的资源呢,可能会比较多,它是一个资源密集性的一个环节。处理起来呢比较复杂,然后它存在一个性能瓶颈,可能每秒钟呢只能处理1000个请求。啊,结果呢,我的并发量呢,可能达到1万的并发量进来了,那怎么办呢?我们就可以使用C否对于我们这个环节啊,来进行限流,哎限流呢,限流为1000,我每秒钟只放1000个请求进去。
08:13
那么等后续请求处理处理完了啊,有一个请求处理完了,那通过这个release啊来去释放资源。啊,释放资源,那么后续请求呢,就可以接着进来了。那么咱这个环节中间啊,永远只有1000个请求啊,永远只有1000个请求啊,他就可以对我们的请求来进行限求。那么只有亏要成功的这些请求啊,才会被放行啊,那么比如说这两个请求,咱可能没有亏要成功,那他就会被阻塞在这个位置。哎,直到呢,有人release释放资源了,那么剩下这些请求呢,才可以进来。好,是咱们这个s for的那个玩意儿,那么单机版的我们已经会了,他那分布式版本的又该如何去玩呢?
09:02
好,那我们来去看看这个red的这个东西啊。那么咱之前这个东西,如果你在分布式项目里面,你使用它的话。那就会存在很多问题哈,那我们呢,可以写一个案例呢,去尝试一下。那比如说呢,我们写一个啊,Get mapping,那么这个路径我们可以来一个test for。啊,这个路径,那么然的方法范围结果集呢,来一个spring。然后test。Sful啊,类似点stop service,点上啊test sful这个方法。好,最终呢,咱们就来一个hello,嗯,Singful就随便打印一句话啊,这样子好,代表呢,我们的请求呢,已经处理完了。那我们来去创建一个service维方法,在这个service维的方法里面,那我们就可以通过这个C的阴线流。
10:07
那如果我依然使用这个单机版本的的话,那它会怎么样,比如说呢,我首先肯定要在这个里面呢,去new一个S,哎,我依然呢是限留三个。啊,现在有三个。那么然后呢,咱这呢,就很难去使用这个停车位这个案例了啊。好,我们可以这样子,那么首先我们的C,你当下一个请求得要获取一个资源,来一个亏啊,获取一个资源,那么让中间来进行业务处理,你处理完了,我们再通过release来去释放资源。那比如说我每秒钟我只能放三个球球过去啊。三个星期过去,呃,我的资源量啊是三个。好,那么然后呢,在这个中间呢,我们来进行业务处理。好,这个处理啊,我们可以呢去输出一下,那比如说呢,咱这个地方可以去输出一下,这是10010。
11:01
然后呢,获取了。获取了资源,那么开始处理业务逻辑。好,那么最后呢,我们10010。那么处理完业务逻辑,那么来去释放资源啊,这个地方。嗯。这是释放资源,那为了方便观察,我们可以来一个华丽的分割线。那么然后在这个地方呢,我们也可以加上这个线程图啊,来一个read变成kind street get name。那么然后这个位置呢,我们也是一样,加上这个点上kind street,点上get name。好,那么在这个中间这个位置,我们可以这样去睡一会啊,来一个time unit,点点sleep。点sleep。那我可以呢,稍微睡的长一点,因为咱是有多个微服啊,为了方方便观察,我们可以多睡一会,那你有一个。
12:07
Random点上。点上next in,来一个十引力的随机值啊,在十的基础上再来一个十引力的随机值。它那么有异常,我们可以呢,来一个try catch处理一下啊。处理一下好,那我们这样呢,就已经处理好了,那么处理好之后呀,我们来去重启这两个服务,好来看一下咱们这个C号好能不能给我们控制住,在分布式场景下啊。嗯。启动好之后呢,我们打开浏览器,咱们在地址栏输入SIM来去访问一下。啊,那么此时我们来看一下请求有没有进入我们的后台服务啊,他已经进来了,那说明咱们这个程序呢啊可以正常运行,那只是呢,我们睡眠时间有点长,他可能呢会超时浏览器上。
13:02
好,我们来去多刷新几次去看看啊,那么有没有给我们控制住啊,我已经刷了很多次了。来看我们的控制台,你可以发现也同时进入了,好多请求都进来了。啊,那么这个服务器也是一样,同时有好多请求度可以进来。那控制住了吗?没有控制住。但有同学老师你的代码写的有问题,你放在这个里面,你每次都初始化了一个新的sing,每个线程都有一个新的,那所以呢,控制不住。如果你想控制住的话,你你应该给他声明到这个方法的外面去,给它变成一个全局变量。那么这样呢,在每一个啊,每一个服务内部啊,就只有一个使用手了。像我们这个talk swis是一个单立的,那么它的话呢,也是一个单立的。也是有代理的。那么这样呢,我们才能控制住。好来看啊,这样说呢是有道理的啊,因为现在呢,完全没有控制住,即使在某一个服务内部都没有控制住。
14:03
啊,服务与服务之间,那更不用说了。好,咱给他啊移到外面去啊,移到外面去之后呢,我们再来去重新启动我们这两个服务。嗯。啊,启动呢,已经成功,我们来去刷刷刷刷,无限制的去刷啊,啊我已经刷了很多次了。好来看现在我们这两个服务里面,那还有很多个服务吗?很多个请求进来吧,不会了,他每次只进入三个请求,因为这三个请求,那这个服里面呢,也是有三个请求。那么每一个服内部只有三个请求啊,也只有这个请求,每一个请求处理完了,才会进入下一个,下个请求才可以进来,那么这个请求处理完了,那么下个请求呢,才可以进来啊。在它这样的一个过程。那你也发现呢,Sing for,诶,它好像有一点点作用了。
15:00
它可以解决服务内部的限流问题,那服务与服务之间它能限流吗?能限制住吗?限制不住,那比如说这个服务里面有三个,那这个服务里面呢,也有三个。它不具备分布式,它只能限定单机情况下的。啊,这种请求啊。好,那我们,呃,如果想限定住的话,我们必须要使用red里面的sing for啊,咱可以呢,把这个呢。要注册掉啊,我们稍微改造一下啊。那么这个地方首先我们要给初始化一个singful,那么咱ready里面这个sing啊,啊,你就不用去放到外面去了啊,你放在内部就可以了。来一个this.reason.reason定点上get same for,好,这个K,比如咱就叫same for。啊,那么最终返回的是一个C对象。啊,那么这个thing呀,你放在任何地方它都可以控制住啊,哎,你可以给它放在外面去,你也可以给它放在这个里面啊,都没啥问题啊,都可以控制住。
16:06
好,那么然sing for queon sing for release来去获取和释放资源,只有获取成功了的才可以放行。啊,然后呢,最后呢,我们把资源给释放掉,其他请求就可以获取这个资源了。好,那么改造好之后啊,我们来去试一下啊,看看他现在能不能给我限制住了。好,我们来去重新启动咱们这两个服务。启动成功之后呢,我们还是把这个日志呢,先清空掉吧,清空掉之后呢,我们再回到浏览器里面去,好,我们来去刷刷刷刷刷刷刷,我刷了一堆。好,来看最终的效果怎么样。啊,这两个服务里面呢,呃,没有一个请求可以进来。啊,很奇怪啊,我们等他一会看看呢。
17:06
啊,没有一个请求进来。那我们来看这个release里面,它有没有给我们初始化一个啊。哎,好像也没有。那是咋回事呢?首先我们得要初始化。一个。那么这个thing for啊,我们可以通过reding客户端,那么来去获取singful,好,再给它取个名字,来一个same for,它将来呢,会以singful作为key保存到release里面去,它最终返回的是一个sing对象。哎,那这还没完呢,那我这个C信号量,我限定多少资源量呢?好,我们得去设置限定的资源量呢,来给这个C法啊,去设置它有这样的一个啊,Tree set promise这样的一个玩意,好在这个里面咱们就可以去设置限定的资源量,比如说呢,我就限定三个请求进去每一次啊。
18:22
好,那么这样呢,咱就设置好了,那它比我们guc里面那个,它多了一个方法,就多了这样的一个方法。那么限定好之后啊,我们来去重新。啊,启动咱们这两个工程。启动呢,已经启动成功了,咱把这个日志呢,全部都给它清空掉。清掉之后呢,打开浏览器,好,我们来去刷新刷新刷新刷新啊,一直刷一直刷好来看。看能不能给我先定住啊,那么此时呢,只有三个请求进来了。
19:02
你这个服务与服务之间,那么也可以先定住两个服务呢,一共只有三个钱就可以行了。那么此时呢,这个请求处理完了,另外一个请求才可以进来啊。好,那我们可以看到啊,这个请求处理完了,这个请才可以进来,那么这个请求处理完了呢,这个请才可以出来,那我们这个地方呢,哎,也释放了一些资源,那么这边呢,就额外放进了一些。额外放进一个清楚啊。但是由于咱是多个客户端。多个控制台被演示的,这个演示起来呢,看的有点乱啊,有点乱,那怎么办呢。那么我们啊,可以呢,给它输入到某一个地方上去,某一个同一个地方上面去啊,咱就很容易看清楚了啊。那比如说咱们不要再空它去打印了,我们可以给它输到这个red里面去啊,啊来看这个最终效果会怎么样。那么首先呢,我们来打开历史去看一下啊,你C音一旦触化好之后啊,它里面就会有这个C音这个信号量了。
20:02
就有这个三方。所以呢,啊这个东西啊C呢,一旦初始化好之后,你就不能去改这个数量了。你要改这个数量的话,它不会立马生效啊。因为呢,因为你里面之前已经生明好一个了,比如说呢,我之前声明为三,我再改成一个五,那你再去运行的话,那这个五有没有生效呢?它其实是没有生效的,它依然会读取这个三啊,那你可以认为呢,这是red里面的一个bug。哎,他明明呢都已经呃使用完了信号量,但这个信号量呢,还在这个地方呢。开这个地方呢,他没有删掉啊,因为这个信号量还在这个地方呢。呃,就会使你的罪行修改呢,无法生效啊,这是一个小问题哈,所以你要改这个数量的情况下,你首先得要把ready里面这个东西呢先给删掉,那么这个修改呢才能生效啊。好,那我们,呃,先不管它,咱们解决这个日志问题,我们最好输入到同一个地方上去,那咱才容易去观察哈,好,那么咱可以呢,都输入到这个list上面去,我不要在这里打印日志了。
21:06
那咱可以呢,使用这个雷点,Ready tempet,点上option for list。那我们知道在里面有五种数据模型,它其中呢,List呀,它是有序的啊,那我们就可以使用这个玩意,那就输出统一输出我们的日志。那么咱们观察起呢,就比较方便了。那么然后在点上我们可以使用这个right push,哎,我从右侧啊来去push进去,那么这样的话最终效果会怎么样呢?哎,比如说呢,我这是一个历子的集合啊,Release里面啊,那我每次呢,从右侧来去剖析啊,右侧来去剖析,那么这样话就是就是从前往后这么一个顺序啊。那如果你从左侧来去剖析的话。那么可能就是从后往前了,你越左边的啊就越靠前。啊,约是最新的。那咱肯定不符合我们的观察观察习惯啊,那我们可以使用它,使用right push的这个地方。
22:06
好,这里呢,咱来一个啊,Write write push,那么key咱们就来一个log,好就可以把这个日志直接给他复写到。咱们的,嗯,这个里面去了。啊,不一定,这是开始的日志啊,那么结束的日志也是一样的,我们之前是通过s out来打印的,那么现在我不想通过它来打印了。我也把这个日志呢说到里面去,好,Object for value啊,For list变write push,那么K呢,依然是log作为K,那么value,我们可以把这个日志内容拷贝过来,完呢放在咱们的value这个位置。好,再加一个分号。好代码咱们这样呢,就处理好了,写好之后呢,我们来去启动一下,那么此时呢,我这是一个1001这个服务啊,咱把它给重启一下。
23:06
啊,等它启动好之后呢,我们再把这个代码呀,改成100。八六啊100。八六好,我们再来去重新启动10086。那么1010啊,它打印的是10010啊,那么10086呢,我们打印的是10086,那这样的话咱也可以区分出来了。好,那么这个启动好之后啊,我们打开浏览器,好,我们在这里呢,疯狂的去刷这一下一直刷。好,那么他已经刷好了,我们先看这个日志的顺序怎么样,来查看一下这个log日志,那我们可以看到有三个请求已经进来了。那注意看啊,咱可以看出两个效果出来。一个呢,是这个C法的初始值呀,我已经改成五了,这个五生效了吗?没有生效,那么在咱的里面依然只放三个星进来啊。
24:00
啊,那么然后呢,我们来去看,呃,咱这个分布式场景下已经可以控制住了,那此时呢,100861个请求进来了,1010呢有两个请求进来了,那10010呢,有个请求处理完了,让10086哎获取这样的一个资源。那么让10086呢,有个请求,处理完了,让10010呢,获取这个资源,那么10010让他处理释放了一个资源,那么另外一个1010。啊,请求他获取了这样一个资源,这开始进行处理嘛,好,我们就可以看到这样的一个顺序了。啊,这就非常容易去观察了啊,给它输入到同一个地方上去。好,那我们嗯这里呢,咱就可以看到这两种现象了啊,也都观察到了,当所有的情都处理完的情况下,它会还原为三啊,如果这三个资源都被占用了会怎么样呢?我们一刷刷刷刷刷刷刷啊,来看这个release里面。人家这个值呢,就变成零了啊,三个都被占用了,也就是说如果它不是零的情况下,此时我就可以放一个请求进来哈。那么后续那些请求没有抢到资源怎么办呢?哎,发现这个值是零,那它存在这里一直自旋,一直自旋。
25:07
直到这个值大于零的时候,咱们这个请求呢,后续请求才可以进来啊。
我来说两句