springBoot+Reactor整合,实现项目中异步执行操作
写在前面:
由于前段时间一直很忙(也有点懒...),没有时间写博客,好在最近稍微空闲点那就再白话白话。
说点什么呢,那就聊聊Reactor吧,Reactor是什么呢?简单说,Reactor 是一个轻量级 JVM 基础库,帮助你的服务或应用高效,异步地传递消息。那它可以怎么应用或用在哪些地方比较合适呢?我的想法:比如-发送手机或邮箱的验证码,再或者某些操作触发到的推送微信模版消息。反正就是不需要将最终的执行结果返回的都可以。别的大家就在实际开发中摸索吧!在这里我不会过多的阐述底层的实现及原理,因为这些很繁琐及臃肿,而且也不是我自己领悟出来的,网上有很多的技术博客就是叙述了过多而且不完整及全面的原理(我看都是相互复制的),也没有一个完整整合及实现的项目代码,让人感觉读了半天也是一头雾水(反正我是)。我的想法是自己先撸出一个完整实现的流程然后在逐渐的向源码辐射,要不然根本搞不明白更别说深入了。所以我对网上那些从这复制一段,那复制一段的博客很是鄙视。当然了如果你想开始就深入的研究我这里附上一份权威版的中文技术指南:http://doc.oschina.net/projectreactor?t=44474 如果你只想开发级的应用,那就看下边就可以了。
老规矩我们还是从下边的图开始~~~
这样比较清楚点了吧,下边我们来上代码,由于我代码上都加上注释了,so我在这个过程中就不在废话了!
先给大家看一下我的项目结构
首先创建一个配置类一般命名为config
package com.reactor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.Environment;
import reactor.core.Reactor;
import reactor.core.spec.Reactors;
import reactor.spring.context.config.EnableReactor;
@Configuration
@EnableReactor
public class ReactorConfig {
@Bean
Environment env() {
return new Environment();
}
/**
* 创建一个异步执行库,其中可以有多个异步的方法
* 这些会在Handler.class中看到
* @param env-固定格式-像我这样写就行,其余的自己在研究吧
* @return
*/
@Bean(name = "pathReactor")
public Reactor pathReactor(Environment env) {
return Reactors.reactor().env(env).dispatcher(Environment.RING_BUFFER).get();
}
}
然后是具体的操作执行,我这边是让其操作了邮件工具类,模拟发送验证码邮件。注意异步调用的方法是没有返回值的,反正我试过了接收不到。至于具体的原理还要深入研究下,有知道的大牛也可以在下方指点我下。
package com.reactor;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.util.mailUtil.PushMailUtils;
import reactor.core.Reactor;
import reactor.event.Event;
import reactor.spring.annotation.Selector;
@Component
public class UserInfoRectorHandler {
@Autowired
@Qualifier("pathReactor")//通过name指定刚定义的库,并注入进来
Reactor r;
@Autowired
PushMailUtils pushMailUtils;//这是我封装的发送邮件的类,不用管
SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 具体执行异步操作的方法,注意方法上的注解value中的名字要和一会调用这个方法的key一至
* reactor中的则是指定刚才的库名,你也可以指定其他的但是前提得先创建
* @param e
* @throws InterruptedException
*/
@Selector(value = "getUserMessage", reactor = "@pathReactor")
public void getUserMessage(Event<String> e) throws InterruptedException {
/*
* 获取传入的参数-可以是:String,Integer,Map及model层实体类,目前我只实验了这些
* 下面的就好理解了,我就不多说了
*/
String mailAddress = e.getData();
Thread.sleep(2000);
boolean result = pushMailUtils.pushMail(mailAddress, "注册验证", "欢迎您使用xxx,您本次的验证码为:123456 请前往注册页面完成注册");
if(result) {
System.out.println(">>>>>>>>>>>>>>>邮件已成功发送"+date.format(new Date()));
}
}
@Selector(value = "resultMessage", reactor = "@pathReactor")
public void resultInt(Event<Integer> e) throws InterruptedException {
for(int i = 0; i < e.getData(); i++) {
Thread.sleep(1000);
System.out.println(i+"打印时间:"+date.format(new Date()));
}
}
}
接下来就是在哪里需要直接调用就行,我这边是放在了controller里面,接收到请求直接返回相应状态,不需要等待处理结果的
package com.business.controller.test;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.util.http.ResponseUtil;
import reactor.core.Reactor;
import reactor.event.Event;
@Controller
@RequestMapping("user")
public class UserController {
@Autowired
@Qualifier("pathReactor")//同样指定并注入
Reactor r;
SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@RequestMapping("getUserInfo")
public void getUserInfo(HttpServletResponse response, String address) {
/**
* 通过notify方法发送,Event.wrap(address)可以为空也可以增加replyToKey参数但是具体的用法我还没有深入研究
* 还有就是这个调用方法的key不能为空,会抛异常一会给大家演示
*/
r.notify("getUserMessage", Event.wrap(address));
ResponseUtil.renderSuccessJson(response,"success","验证码邮件已发送,请注意查收!"+date.format(new Date()));
}
@RequestMapping("resultMessage")
public void result(HttpServletResponse response, Integer id) {
r.notify("resultMessage", Event.wrap(id));
ResponseUtil.renderSuccessJson(response,"success","返回时间:"+date.format(new Date()));
}
}
到这里代码部分算是结束了,接下来咱们启动项目来瞅瞅吧!
OK~启动完成
我这里用的是postman来进行测试~我们先测试一下那个循环输出的方法,下面是结果
OK!成功返回,注意看输出的时间,一会和控制台输出的比较一下就清楚了!~
这是postman的返回
这是控制台的输出
通过观察上面俩个图的返回时间可以看到页面数据是直接返回的,没有等待循环处理完成。这就是异步执行
下面我看测试一下发送邮件的,我这里设置了休眠2秒在发送
这是返回页面的
输出控制台的
邮件的提示也过来了
好了,先这样吧。本来还想测试下调用异步时哪个参数是不是必须的,但是没时间了,需求又来了。。。。再见吧各位,如果觉得我哪里写的不好或不对欢迎在下方留言,我一定虚心接受!~~~