00:00
前面我们使用find的request intercept拦截器功能,我们改造了一下我们的业务,保证我们这个业务去来远程调用其他服务的时候,其他服务呢也能感知当前登录的用户,那接下来呢,继续来改造我们这一块的业务代码,其实在我们这个订单确认页,我们需要确认的数据有很多,我在这呢只列举了两三种情况,那在这种情况下,我们一般呢都是使用异步编排,因为我们要等每一个远程调用都来做完,我们这个方法可能时间太长了,所以呢我们可以让方法们同时运行,而且使用异步编排,那这个异步编排呢,我们以前给我们容器中已经放了一个线程池,所以在任何用到异步的地方,应该将我们的异步任务都提交给线程池,所以我们把我们的线程池我们先拿过来,这是我们自己的线程池,我们就叫executor吧,然后呢,来owa,接下来我们给我们线程池里边。
01:00
提交一些义务任务,首先第一个远程查询所有的地址列表,又不依赖其他什么东西,那么就直接来compible future,点一个run and ethnic,我们来使用一个异步的方式来运行一下来我们最终把这个方法来放到这,只要异步运行完成,自动的给confirm view里边放东西,然后呢,我们把它ex能提交给我们的线程池,这是我们的第一个异步任务,同样的第二个异步任务,这这是我们的第一个get address future。好,我们第一个异步任务完了以后呢,接下来是我们第二个异步任务,我们要获取购物车里边的数据,我们还是一样complete future,点一个run anthnic来以一个异步的方式来运行,提交给我们自己的线程池,然后呢,我们这个异步任务,我们把它放到这个里边,好把这一块呢拿过来,而且我们这一块呢,现在有了拦截器,也不害怕远程找不到东西了。然后接下来这是我们的第二个异步任务,这个呢是我们称为加cut future,我们购物车的一步任务,当然我们最终所有要返回这个VO之前,要等我们以上的这些任务全部完成,Commitable future,点一个all of来等这些任务都来做完get future这一块,好,还有以及我们的cut future。
02:27
包括每一个任务如果出现了什么问题,我们也可以在这,如果任务出现了问题,我们使用问之类的,我们可以来处理一些问题等等,好那么现在呢,这个异步任务我们拿到这所有的这个任务我们要都等它完成,我们才能给他做一个完整返回来点一个get,好,那我们现在这一块该抛的异常我们就来抛出去,全部都来抛出去,包括我们来调用我们这个方法的ctrler,好,这一块呢,要抛的异常我们全部抛出去,那现在呢,我们这一块就改造我们使用异步任务的方式,我们将我们这两个方法我们都做成一个异步,让他去来远程查询,好,同样的我们先来到我们的这个控制台。
03:11
那么将我们的订单服务我们重新给bug启动起来,那么现在再来测试,那异步的模式下,我们这两个能不能正确获取到结果,好,我们现在呢,让我们的订单服务重新启动,购物车服务我们现在之前已经启动过了,我们可以把他们之间的断点全部都来删掉,好,以前的这些断点我们全部都来删掉。那现在我们来重新来打断点。我们给购物车里边先给他的session打上一个断点,然后呢来到我们这个订单,订单呢提交两个异步任务,我们也不等他了,我们直接给all of来打断点,我们来看一下异步任务,主要就是我们这个购物车。购物车我们有了拦截器以后,我们同步了cookie远程服务呢就没问题了,但如果我们引入了异步任务,是不是还能正常工作呢?我们来看一下效果好,现在购物车在这儿,以及我们的订单在这儿,现在又去来结账,来还是发送出突出的请求,先来保证我们现在是登录状态,好我们现在来确认一下我们的登录状态,那们现在呢,还是登录状态没问题,那接下来现在来继续突出走,然后来到我们的订单服务,那订单服务呢,要等这些线程全部运行完,然后我们的购物车服务在这一块,好我们断点呢,现在我们来看停到哪一块,我们现在来到购物车的拦截器,购物车从session里边取数据,肯定呢是有登录的,好,我们来看一下,那么取到了登录的这个用户,好,我们来放行,现在我们来重新来测试把这个购物车呢,我们最好重启一下,那么现在呢,是登录状态,我们给订单来提交我们的这个请求,提交过来以后加我们的第一个异步任务去来查我们的远程。
04:51
的地址,第二个异步任务来查购物车里边的所有我们当前的数据,而且呢,我们给这个购物车,我们给他打了一个断点,购物车好,只要一进来呢,在intercept来打断点。
05:05
我们现在就来确认购物车里边能不能获取到当前登录的用户,那现在来重新确认一下,好,我们看现在还是订单发送突出的请求,那发这个请求的时候,我们肯定会带这个cookie,没问题走来刷新。好,现在来到我们的这个方法,那么这个方法呢,第一个异步任务去来查数据,这个数据呢,我们是后台运行的所confirm form VO,什么时候有数据,我们这个address呢,就会有东西,好,我们现在先来确定这一块没有,接下来我们去来异步去来查我们的这一块,好,我们现在只要来放行,我们放行呢,我们先来给他断点放到这儿,我们来放行,我们义务任务去来查它,那我们这个购物车肯定呢就会进来,我们现在来还是来放行,来到我们的这个订单这一块,走,那们现在来等待我们的异步任务运行完。那要等待我们的异步任务运行完,我们的购物车现在来看断点有没有进来,我发现购物车这一块呢没有什么反应,确定给我们的购物车已经打上了断点,我们购物车的这个拦截器,好,我们给他呢打上断点,只要购物车服务有动静,那我们断点呢就在这儿。
06:15
我们可以把订单的所有断点我们都来去掉,订单要执行的时候走把这个订单的这个断点呢,我们先来去掉,那现在就来看,我们现在要去来进行结账,我来刷新服务器内部异常,我们这儿有一个控制帧,我们来看一下,排除一下这个控制帧来到控制台,控制帧呢,在这儿我们发现呢,这一块它打印为空,这一块为空的原因是什么?我们来断点还是打在这拦截器居然能变成空的。那么现在再来查购物车,我们来重新来走,我来刷新,好现在呢,我们来到拦截器这一块,我们先来看我们的断点,我们拦截器呢,现在进来,我们上一步呢是执行哪个方法进的拦截器,我们来看一下,我们上一步呢是执行这个方法进到拦截器,好,我们来放行,没问题。接下来第二个我们又来到我们的这个方法,我们看我们第二次进拦截器里边是哪个方法,我现在呢是get current这个user item,我们相当于现在是来获取购物车里边的数据,我们进来了,进来以后呢,我们来走。
07:15
我们来获取request这个请求这一块呢,就已经报了这个空指针异常,所以我们发现我们现在想要来远程查购物车,我们这一块已经不成立了,就是当我们来调用这一块的时候,我们的request的对象已经成了我们的这个空了,这一块是空的了,这是为什么呢?大家来考虑一下,就是我们现在跟以前唯一不同的编码方式就是我们使用了异补,所以接下来我们就要说一下,那在异步模式下份还会丢失我们上下文的问题,我们来给大家画一个简单的图,以前呢,我们是这样子的,一个老请求过来,然后呢,他先请求我们的订单服务,这没什么问题,然后呢,我们这个从请求发出到我们订单服务的接收,我们订单服务的controller肯定接收,接收呢,最终调用我们订单服务的service,好来调用order的service来进行处理order service我们一个请求。
08:15
来肯定是分一条线程,我们来进行处理,所以呢,订单的controller跟service是同一个线程,所以相当于在订单服务一开始它的thread local共享了的数据,因为这两个呢是同一个线程,我们说请求的这个上下文,我们在这拦截器里边想要获取这个请求上下文,当前请求信息我们是用这个holder获取的,Holder是利用thread local来做的,所以呢,我们这个份要共享这个老请求的这个数据信息,它相当于在同一个线程,它使用thread local来共享的。但问题来了,我们以前如果是一连串的调用没什么问题,我们以前一连串的调用order service里边我们会调两个,一个呢是我们的这个去来查我们地址,那调了一个address,另外一个呢是查购物车cut,我们以前呢,这两个是一个同步调用,我们没开异步,相当于从前到后我们都是。
09:15
同一条线程,所以我们的这个上下文thread local,我们来到购物车里边,我们在发请求之前,我们可以获取到老请求,老请求在thread local里边共享着,但是我们现在的模式变成了这样,老请求还是老请求,订单服务我们把这一块呢拿过来,但现在问题就来了。订单服务这个线程假设呢,如果一直执行下去,那就是这一条线程,但是我们现在开了两个线程,一个线程来去来查询地址,一个线程去来查询购物车,好,我们把这开到两个线程放到这儿。走,然后呢,我们把这个也放到这儿走,所以接下来出现的问题就是我们以前老请求在thread local里边共享这那现在应该是这样来打开我们的业务代码,我们呢,一进这个service方法,Thread local给我们共享了上下文信息,以前想要远程调用来到我们的份这一块,我们份的整个这个线程的调用跟我们的controller service这些都是一个线程,所以没问题。但是呢,我们现在发现我们的业务代码变成这样,我们的份调用放在了一个异步里边,所以相当于假设我们从头到尾的这个线程,那是一号线程的话。
10:41
那我们接下来的这两次调用,这个是一号线程的话,那我们这两次调用就不指定是哪个线程了,可能呢它是二号线程,然后呢,他可能呢是三号线程,所以这样的问题就来了,那我们二号线程他想要发一个远程调用请求,那要发请求之,这是我们发请求肯定之前我们要经过拦截器,但是呢,这个intercept它是在二号线程里边的intercept,那你说他在二号线程里边能拿到人家一号线程read local共享的数据吗?这肯定是不行的,所以这两个都不行,所以现在的问题就在这儿了,我们如果是使用异步模式,因为每一个异步请求进来都调用intercept,是在它自己的这个里边来进行调用的,我们可以来看一下,它呢是在我们自己里边,每一个请求有它自己的这个。
11:41
线程它在自己的线程里边调用两个不一样,所以一号共享的thread local数据来不到我们intercept里边,所以我们看到我们这一块的intercept得到的它老是为空的,我们的老请求老是为空的,所以我们这一行呢,老是控指针异常,导致我们这一块出现了问题,那接下来我们这个问题该如何解决?
12:04
原因就是我们controller到service,如果以前是同步调用,把这一块代码放在这儿同步调用,那这样的好处就是我们这个代码跟我们这个service controller,以及我们这个代码执行之前还要执行拦截器,这四个是一个线程的,但现在我们放到了异步调用,相当于我们要执行调用之前我们调用拦截器,这又开了另外一个线程,包括我们可以把这个线程号打印给大家。这个是this out,这个this out,我们主线程,主线程呢,来打印一下线程的ID thread,我们现在thread直接点一个current thread来让它得到当前的这个线程,然后打一下它的ID,而我们在这里边,这是我们的副线程。来看一下我们的第一个副线程,Member的这个线程,我们member的这个线程呢,它的ID是什么,包括我们再给大家打印一下我们这一块的线程,它的ID cut线程它的ID是什么,以及我们给大家打印下我们拦截器这个线程在执行方法的时候ID是什么,由于这一块呢可能为空,所以为了一切能正常执行,我们把这一块的代码先注掉。
13:23
然后呢,我来只给这儿来打印我们的线程号,把我们的线程号的打印来复制过来,那现在来看一个完整效果,那现在呢,是拦截器的线程request intercept,我们把拦截器的线程呢在这来打印一下,好,我们现在来启动我们的订单服务,订单服务我们以debug模式启动,我们来看一下我们一系列请求,他们的这个线程是不是一样的,如果不一样,那肯定共享不了我们thread local相关的数据。好,我们现在来到我们这一块,我们把以前的其他断点,我们现在呢,全部都禁用掉,我们来到我们断点这一块,把我们以前的这些断点呢,我们现在全部去掉,好,我现在来发一个请求。
14:08
我们去来订单去结算,我来刷新一下,好,现在来到我们的订单服务,来我们来看一下我们的这个,我们来到订单服务,先打印的主线程,主线程呢是72号,然后再打印member线程,Member线程101号,然后cut线程102号。所以呢,这个是101,这个是102,当然每一个远程调用开头我们都要调用我们的拦截器,而我们拦截器在这一块的打印,我们来看拦截器,拦截器呢,相当于我们的一个线程,101号调用了一次,它的拦截器102号调用了一次,所以最终就是我们图示的这样效果,你redad local是给我们这一号共享数据的,那就我们说的72号,但是呢,你开线程要执行的这两个方法,一个是101,一个是102,所以你共享的这个数据你用不了,所以为了让他在intercept能用掉,我们只有一个办法,那就是我们在放行之前,我们再来执行之前,这两个是异步任务,在执行,执行之前我们让它放一下数据,把这个request contest holder拿过来,拿过来以后呢,我们来给它调用一个set方法。
15:28
把它的这个东西再来set进去,那它的原数据是什么?我们可以拿到它的原请求点,Get a request attributete,这是它原来的request attribute,我们在每一个线程进来,你们知道这块呢,开了一个线程,所以我在它自己的线程里边,我在调用这个工具,这个工具是thread local,想在自己的线程里边再来共享一下我们的request attribute,那下边也一样,每一个线程里边都来共享一下这个东西,你一共享以后,我们这一块呢,才能真正的得到数据,当然我们为了保险起见,可以来做一个判断,If request等等呢,等等呢,我们什么都不做了,不等呢,我们才来做下边的事情。
16:16
好,我们把这一块呢,全部拿来走。所以我们现在的做法就是我们在每一个副线程进来,进来以后呢,我们把原来的数据还用request contexttest hold给它放进来,有的同学说你用的这个request contest holder这个类不是从头到尾都一样吗?虽然从头到尾都一样,但是它里边共享数据用的是带的local,就是只要线程不一样,里边的东西就不一样,所以呢,我们给主线程,从主线程里边拿到它原来的数据,在副线程里边我们都给它共享来,那只有共享来我们的拦截器里边才会有所有的数据。好,我们现在来把我们的订单服务重新启动,以前拦截器里边没数据,我们现在给它断点打到这儿,那看拦截器里边还有没有数据,所以在异步模式下,我们一定要注意很多的细节问题,那现在再来做一个测试,我们来刷新,好,现在来到我们的拦截器,以前我们拦截器拿到的request对象是空的。
17:20
好看我们现在现在呢,什么东西都有,所以他不为空,他把cookie数据,诶,我们现在又能获取到了,我们来放行一个。再放行一个,那么现在呢,所有数据都可以获取到,那现在就没问题了,那么这一块还说内部异常,我们来可以看一下内部异常,内部异常呢就在这儿,我们购物车获取数据的这一块,我们先来看购物车里边有没有什么异常,好购物车里边,购物车在这打印呢,价格获取404,我们把它清空,重新来测一下刷新,我们直接来进行一个放行,走放行好两个都已放行,那保证我们的购物车,购物车我们发现呢,它的远程获取价格总是有问题,那下一节课呢,我们把这个再优化,那现在呢,购物车里边的数据已经没问题了,我们这两个呢,也能获取到它原生的cookie数据了。
18:10
这就是我们在异步模式下需要调用份,还要保留我们之前请求头各种信息,我们来再做的处理,我们获取之前的请求,然后呢,每一个线程都要共享之前的请求,每一个线程都来共享之前的请求数据。所以这就是我们来做的额外处理好。
我来说两句