首先来看一张图
在图中可以看到,用户注册通常需要完成两件事,向用户发送验证码码,及对用户验证码、信息的校对、初始化,在没有使用的MQ的项目中,验证码的生成、存储、发送都是由调用方法完成的,虽然可以将发送短信功能抽取出来作为一个函数或者工具类然后进行调用,但是仍然会出现这样一个问题,调用方法或者调用方法所属的模块宕机了,那么短信发送功能也就不能使用了,也就是短信发送功能和短信验证码的生成耦合在一起了。你可能会说影响不大,但是如果项目以模块划分,比如订单模块和库存模块,订单模块依赖于库存模块,库存模块不可用那么订单模块也不可用,不仅影响的是客户的体验,而且可能会对公司效益产生影响。那么有没有什么办法,即使在库存模块不可用的情况下,订单模块仍然可用呐,使用MQ进行解耦合。如下图。
短信验证码接口,生成验证码后将消息存入到MQ中,就不管短信发送了,短信发送交由短信发送模块进行管理。即使短信发送模块不可用了,也不会影响短信验证码接口的使用,短信验证码接口只要有短信发送任务就存到MQ中,当短信发送模块恢复了,短信发送模块就会从MQ中取出消息往下进行操作。订单模块和库存模块也是这样的道理,订单模块不管库存模块是否能用,有消息就推到MQ中,库存模块能用时就会去MQ中取消息往下处理。所以MQ在能够有解耦合的功能。
我们所知道的是,项目开发之前首先要做的是项目环境的搭建,环境搭建起来测通后,才能进行模块的jar导入,application.yml文件编写的工作。
下面是rabbitmq测通结果,初始用户名和密码都是guest
需要在rabbitmq中创建一个队列,发送短信模块会监听这个队列,如果队列中有消息,那么会取出这个消息,通过相应的方法对消息进行处理。
一般来说,在编码之前需要做的工作是找齐相关的pom文件,配置application.yml文件,创建启动类,用代码生成器生成代码,再编写代码。pom文件中的内容同常可以在官网中找到,而application.yml文件通常需要配置东西,server.port–服务端口,spring-datasource–数据源,数据源一般包括username,password,url,驱动类名字,还有如果需要用到redis,rabbatmq你也需要配置。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--简化代码的工具包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--mybatis-plus的springboot支持-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<!-- 配置rabbitmq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- 配置redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
做个解释,commons-lang3包是一个apache的对java lang3中类进行封装的一个jar包,提供了很好的工具类,建议使用,可以提高开发效率
server:
port: 9008
spring:
application:
name: tensquare-user
redis:
host: 192.168.126.129
rabbitmq:
host: 192.168.126.129
datasource:
username: root
password: root
url: jdbc:mysql://192.168.126.129:3306/tensquare_base
driver-class-name: com.mysql.jdbc.Driver
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
在编写service、controller层的时候,通常需要知道你用的类有哪些,并且把它们注进来,才能进行后续的开发。
这里需要说明的是这句代码,rabbitTemplate.convertAndSend(“ms”,map); 参数"ms"是你在rabbitmq中创建的队列名称,也就是要与你在rabbitmq中创建的队列名称对应起来。取的时候也是如此
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;
public void codeBirth(String mobile){
//用数字生成随机字符串
String code = RandomStringUtils.randomNumeric(6);
//存一份到redis中
redisTemplate.opsForValue().set("checkcode_"+mobile,code);
Map<String,String> map = new HashMap<>();
map.put("mobile",mobile);
map.put("code",code);
//存一份到rabbitTemplate
rabbitTemplate.convertAndSend("ms",map);
System.out.println("mobile:"+mobile+",code:"+code);
}
@Autowired
private ITbUserService iTbUserService;
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping(value = "/sendsms/{mobile}",method = RequestMethod.POST)
public Result sendsms(@PathVariable String mobile){
iTbUserService.codeBirth(mobile);
return new Result(StatusCode.OK,"验证码发送成功");
}
@RequestMapping(value = "/register/{code}",method = RequestMethod.POST)
public Result regist(@PathVariable String code,@RequestBody TbUser user){
String checkcodeRedis = (String) redisTemplate.opsForValue().get("checkcode_" + user.getMobile());
if (checkcodeRedis.isEmpty()){
return new Result(StatusCode.ERROR,"请先获取手机验证码");
}
if (!checkcodeRedis.equals(code)){
return new Result(StatusCode.ERROR,"验证码错误");
}
iTbUserService.save(user);
return new Result(StatusCode.ERROR,"注册成功");
}
首先对rabbatimq的@RabbitListener(queues = “ms”)和@RabbitHandler注解进行说明
@RabbitListener监听队列中是否有消息,如果队列中有消息,调用@RabbitHandler上的方法进行处理
pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
@RabbitHandler
public void executeSms(Map<String,String> map) {
String mobile = map.get("mobile");
String checkcode = map.get("code");
System.out.println("手机号:" + mobile);
System.out.println("验证码:" + checkcode);
}
我们看一下运行结果,队列中的短信验证码被消耗了