mysql多条件排序查询
概述数据库中的数据直接呈现出来一般不是我们想要的,所以我们上两节演示了如何对数据进行过滤的方法。除了对数据进行过滤,我们可能还需要对数据进行排序,比如想从列表中了解消费最高的项,就可能需要对金额字段做...
2024.11.15前言
最近华为云云耀云服务器L实例上新,也搞了一台来玩,期间遇到各种问题,在解决问题的过程中学到不少和运维相关的知识。
本篇博客介绍RabbitMQ的Docker版本安装和配置,延迟插件的安装;结合QQ邮箱和阿里云短信验证码服务,采用主题模式进行验证码的发送。
关于邮箱验证码和手机短信验证码可以参考以下博客
SpringBoot项目(验证码整合)——springboot整合email & springboot整合阿里云短信服务
其他相关的华为云云耀云服务器L实例评测文章列表如下:
·初始化配置SSH连接 & 安装MySQL的docker镜像 & 安装redis以及主从搭建 & 7.2版本redis.conf配置文件
·安装Java8环境 & 配置环境变量 & spring项目部署 &【!】存在问题未解决
·部署spring项目端口开放问题的解决 & 服务器项目环境搭建MySQL,Redis,Minio...指南
·由于自己原因导致MySQL数据库被攻击 & MySQL的binlog日志文件的理解
·认识redis未授权访问漏洞 & 漏洞的部分复现 & 设置连接密码 & redis其他命令学习
·canal | 拉取创建canal镜像配置相关参数 & 搭建canal连接MySQL数据库 & spring项目应用canal初步
·canal | 基于canal缓存自动更新流程 & SpringBoot项目应用案例和源码
·Docker版的Minio安装 & Springboot项目中的使用 & 结合vue进行图片的存取
·在Redis的Docker容器中安装BloomFilter & 在Spring中使用Redis插件版的布隆过滤器
·Elasticsearch的Docker版本的安装和参数设置 & 端口开放和浏览器访问
·Elasticsearch的可视化Kibana工具安装 & IK分词器的安装和使用
·Elasticsearch的springboot整合 & Kibana进行全查询和模糊查询
引出
1.RabbitMQ的Docker版本安装和配置,延迟插件的安装; 2.结合QQ邮箱和阿里云短信验证码服务,采用主题模式进行验证码的发送;
RabbitMQ的Docker版本安装
1.拉取镜像创建容器
dockerpullrabbitmq
dockerrun-itd--name=rabbitmq_pet\-eRABBITMQ_DEFAULT_USER=admin-eRABBITMQ_DEFAULT_PASS=123\-p15672:15672-p5672:5672rabbitmq
查看rabbitmq的版本,3.9.11
docker ps查看当前运行的容器
2.开放端口和访问
华为云控制台开放端口
前端访问报错
无法显示页面
打开管理页面
前端访问,需要打开管理页面
dockerexec-itrabbitmq_petbash
rabbitmq-pluginsenablerabbitmq_management
输入用户名密码,进入rabbitmq管理页面
允许查看channels
进入rabbitmq容器进行修改
cd/etc/rabbitmq/conf.d/
echomanagement_agent.disable_metrics_collector=false>management_agent.disable_metrics_collector.conf
重启后可以进入channels页面
3.安装延迟插件
下载支持3.9.x的插件
https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases?after=rabbitmq_v3_6_12
一开始没有延迟插件,需要安装一下延迟插件
上传延迟插件到文件夹
dockercp ./rabbitmq_delayed_message_exchange-3.9.0.ezrabbitmq_pet:/plugins
进入容器,允许延迟插件
rabbitmq-pluginsenablerabbitmq_delayed_message_exchange
刷新前端页面,延迟插件安装成功
安装成功
使用rabbitmq进行验证码的发送
1.依赖导入
org.springframework.bootspring-boot-starter-amqp
spring: rabbitmq: host: 124.70.138.34 port: 5672 username: admin password: 123 # 确认收到 publisher-confirm-type: correlated publisher-returns: true
2.配置文件
package com.tianju.fresh.config;import com.tianju.fresh.util.RabbitMQConstance;import org.springframework.amqp.core.*;import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;import org.springframework.amqp.support.converter.MessageConverter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.amqp.rabbit.connection.ConnectionFactory;import org.springframework.amqp.rabbit.core.RabbitTemplate;/** * rabbitmq的配置类 */@Configurationpublic class RabbitMQConfig { /** * 创建邮箱的队列 * @return 邮箱的rabbitmq队列 */ @Bean public Queue emailQueue(){ return new Queue( RabbitMQConstance.MQ_EMAIL_QUEUE, RabbitMQConstance.durable, RabbitMQConstance.exclusive, RabbitMQConstance.autoDelete ); } /** * 电话队列 * @return 电话的队列 */ @Bean public Queue phoneQueue(){ return new Queue( RabbitMQConstance.MQ_PHONE_QUEUE, RabbitMQConstance.durable, RabbitMQConstance.exclusive, RabbitMQConstance.autoDelete ); } /** * 队列的交换机fanout * @return 队列的交换机fanout */ @Bean public FanoutExchange fanoutExchange(){ return new FanoutExchange( RabbitMQConstance.MQ_FANOUT_EXCHANGE, RabbitMQConstance.durable, RabbitMQConstance.autoDelete ); } /** * 主题模式topic的交换机 * @return 主题模式的topic交换机 */ @Bean public TopicExchange topicExchange(){ return new TopicExchange( RabbitMQConstance.MQ_TOPIC_EXCHANGE, RabbitMQConstance.durable, RabbitMQConstance.autoDelete ); } /** * ######################建立队列和交换机的绑定关系 ################### */ /** * 建立邮箱队列和交换机的绑定关系 * @return 绑定的关系 */ @Bean public Binding emailBlinding(){ return BindingBuilder.bind(emailQueue()) .to(topicExchange()) .with("topic.email"); } /** * 建立电话队列和交换机的绑定关系 * @return */ @Bean public Binding phoneBlinding(){ return BindingBuilder.bind(phoneQueue()) .to(topicExchange()) .with("topic.phone"); } /** * ###################### 对象转换成json字符串进行发送 ############## */ @Bean public MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } @Bean public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){ RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); rabbitTemplate.setMessageConverter(messageConverter());// 修改转换器 return rabbitTemplate; }}
package com.tianju.fresh.util;/** * 项目中用的常量 */public interface RabbitMQConstance { /** * rabbitmq相关的常量 */ String MQ_EMAIL_QUEUE="mq_email_queue"; String MQ_PHONE_QUEUE="mq_phone_queue"; String MQ_FANOUT_EXCHANGE="mq_fanout_exchange"; String MQ_TOPIC_EXCHANGE="mq_topic_exchange"; // 参数 String name, boolean durable, boolean exclusive, boolean autoDelete boolean durable = true; // 表示队列是否持久化 boolean exclusive = false; // 是否排它式 boolean autoDelete = false;}
3.发送的业务service代码
/** * ########################## 用户注册需要的东西 ################### */ HttpResp getRegisterSMSCode(String input); /** * 发送消息给主题模式的交换机, * 交换机将消息转发给邮箱队列 * @param smsDto */ void sendToEmailQueue(SMSDto smsDto); /** * 发送消息给主题模式的交换机, * 交换机把消息发送给电话队列 * @param smsDto */ void sendToPhoneQueue(SMSDto smsDto);
service接口的实现
@Override public HttpResp getRegisterSMSCode(String input) { // 1.输入邮箱或者手机号码是否合法 // 2.redis里面是否有 // 3.根据邮箱或手机发送不同的消息给交换机 // 4.返回给前端验证码 if(!SMSUtil.isEmailOrPhone(input)){ return HttpResp.failed("输入的邮箱或手机号码不合法"); } if (redisUtil.isKeyInRedis(input)){ return HttpResp.failed("验证码已发送,请检查,稍后重试"); } String code = SMSUtil.getCode(); if (SMSUtil.isEmail(input)){ // 发送邮箱验证码 sendToEmailQueue( new SMSDto(input,"邮箱验证码",code) ); Map map = new HashMap(); map.put(input, code); return HttpResp.success(map); } sendToPhoneQueue( new SMSDto(input, null, code) ); return HttpResp.success(code); } @Override public void sendToEmailQueue(SMSDto smsDto) { // 发送消息给邮箱,邮箱验证码 rabbitTemplate.convertAndSend( RabbitMQConstance.MQ_TOPIC_EXCHANGE, "topic.email", smsDto ); log.debug("{} [ 邮箱验证码生产者向主题模式交换机: ] 发送一条消息 {}",new Date(), smsDto); } @Override public void sendToPhoneQueue(SMSDto smsDto) { // 生产者:发送消息给主题交换机,主题交换机把消息给电话队列 // 消费者:监听电话队列,如果电话队列有消息,就进行消费,调用发送电话验证码的方法,发送手机验证码 rabbitTemplate.convertAndSend( RabbitMQConstance.MQ_TOPIC_EXCHANGE, "topic.phone", smsDto ); log.debug("{} [ 手机验证码生产者向主题模式交换机: ] 发送一条消息 {}",new Date(), smsDto); }
smsDto实体类,进行验证码对象传输
package com.tianju.fresh.entity.dto;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@NoArgsConstructor@AllArgsConstructorpublic class SMSDto { private String target;// 目标邮箱或者手机号码 private String msg; // 补充的消息 private String code; // 验证码}
4.监听队列消息发送验证码
package com.tianju.fresh.rabbitMQ;import com.tianju.fresh.entity.dto.SMSDto;/** * 监听手机验证码邮箱的队列 * 如果有消息,就进行消费发送验证码 */public interface ConsumerService { /** * 调用给邮箱发送验证码 * @param smsDto 数据传输层对象,包含目标邮箱,消息,验证码 */ void callSendToEmail(SMSDto smsDto); /** * 调用给手机发送短信验证码 * @param smsDto 数据传输层对象,包含目标手机号码,验证码 */ void callSendToPhone(SMSDto smsDto);}
接口代码的实现类
package com.tianju.fresh.rabbitMQ.impl;import com.tianju.fresh.entity.dto.SMSDto;import com.tianju.fresh.rabbitMQ.ConsumerService;import com.tianju.fresh.util.RabbitMQConstance;import com.tianju.fresh.util.RedisUtil;import com.tianju.fresh.util.SMSUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.amqp.rabbit.annotation.RabbitListener;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.Date;@Service@Slf4jpublic class ConsumerServiceImpl implements ConsumerService { @Autowired private SMSUtil smsUtil; @Autowired private RedisUtil redisUtil; @RabbitListener(queues = RabbitMQConstance.MQ_EMAIL_QUEUE) @Override public void callSendToEmail(SMSDto smsDto) { log.debug("[ 邮箱队列消费者模块:] 在{} 获得一条消息{},即将发送邮箱验证码",new Date(),smsDto); smsUtil.sendEmailCode(smsDto.getTarget(),smsDto.getMsg(),smsDto.getCode()); // 存到redis里面,有效时间是5分钟 redisUtil.saveStringValue(smsDto.getTarget(), smsDto.getCode(), 60*5); log.debug("邮箱验证码存到redis中,有效期为 5分钟"); } @RabbitListener(queues = RabbitMQConstance.MQ_PHONE_QUEUE) @Override public void callSendToPhone(SMSDto smsDto) { log.debug("[ 电话队列消费者模块:] 在{} 获得一条消息{},即将发送手机验证码",new Date(),smsDto); smsUtil.sendPhoneCode(smsDto.getTarget(), smsDto.getCode()); // 验证码存到redis中 redisUtil.saveStringValue(smsDto.getTarget(), smsDto.getCode(), 60*5); log.debug("手机验证码存到redis中,有效期为 5分钟"); }}
5.用到的工具类
package com.tianju.fresh.util;import com.aliyuncs.CommonRequest;import com.aliyuncs.CommonResponse;import com.aliyuncs.DefaultAcsClient;import com.aliyuncs.IAcsClient;import com.aliyuncs.exceptions.ClientException;import com.aliyuncs.exceptions.ServerException;import com.aliyuncs.http.MethodType;import com.aliyuncs.profile.DefaultProfile;import lombok.extern.slf4j.Slf4j;import net.minidev.json.JSONObject;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.PropertySource;import org.springframework.mail.SimpleMailMessage;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.stereotype.Component;import javax.annotation.Resource;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Map;import java.util.Random;import java.util.regex.Matcher;import java.util.regex.Pattern;@Component@PropertySource("classpath:config/ali.properties")@Slf4jpublic class SMSUtil { // 阿里云短信服务的配置 @Value("${ali.msg.AccessIdKey}") private String AccessIdKey; @Value("${ali.msg.AccessKeySecret}") private String AccessKeySecret; // QQ邮箱的配置 @Value("${spring.mail.username}") private String from; @Resource private JavaMailSender javaMailSender; /** * 发送手机验证码 * @param tel 接收验证码的手机号码 * @param code 验证码 */ public void sendPhoneCode(String tel,String code) { DefaultProfile profile = DefaultProfile.getProfile( "cn-hangzhou", AccessIdKey, //AccessIdKey AccessKeySecret); //AccessKey Secret IAcsClient client = new DefaultAcsClient(profile); CommonRequest request = new CommonRequest(); request.setSysMethod(MethodType.POST); //下面这3个不要改动 request.setSysDomain("dysmsapi.aliyuncs.com"); request.setSysVersion("2017-05-25"); request.setSysAction("SendSms"); //接收短信的手机号码 request.putQueryParameter("PhoneNumbers",tel);//此处写电话号码 //短信签名名称 request.putQueryParameter("SignName","阿里云短信测试"); //短信模板ID request.putQueryParameter("TemplateCode","SMS_154950909"); //短信模板变量对应的实际值 ${code} 中的值 Map param = new HashMap(2); param.put("code", String.valueOf(code)); //写入的短信内容,验证码 request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param)); try { CommonResponse response = client.getCommonResponse(request); System.out.println(response.getData()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); Date date = new Date(); String formattedDate = sdf.format(date); log.debug("在 {} 时,发送一条短信验证码[ {} ]给手机 {}",formattedDate,code,tel); } catch (ServerException e) { e.printStackTrace(); } catch (ClientException e) { e.printStackTrace(); } } /** * 发送qq邮箱的代码 * @param email 接收邮件信息的邮箱地址 * @param subject 邮件的主题:标题 * @param content 邮件的内容:你的验证码是3927 */ public void sendEmailCode(String email, String subject, String content) { SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setSubject(subject); mailMessage.setTo(email); mailMessage.setText(content); mailMessage.setSentDate(new Date()); mailMessage.setFrom(from); javaMailSender.send(mailMessage); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); String formattedDate = sdf.format(mailMessage.getSentDate()); log.debug("在 {} 发送一条邮件[ {} ]给 {}",formattedDate,mailMessage.getText(),mailMessage.getTo()); } /** * 判断是不是合法的手机号码 * @param input 待判断的手机号码 * @return */ public static boolean isPhoneNumber(String input) { String pattern = "^1[3456789]\\d{9}#34;; Pattern regex = Pattern.compile(pattern); Matcher matcher = regex.matcher(input); return matcher.matches(); } /** * 判断是不是合法的邮箱 * @param input 待判断的邮箱 * @return */ public static boolean isEmail(String input) { String pattern = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+#34;; Pattern regex = Pattern.compile(pattern); Matcher matcher = regex.matcher(input); return matcher.matches(); } /** * 判断是不是合法的手机号码,或者邮箱 * @param input * @return */ public static boolean isEmailOrPhone(String input){ if (isPhoneNumber(input)){ return true; }else return isEmail(input); } /** * 获取随机生成的4位密码 * @return 随机生成的4位密码 */ public static String getCode(){ Random random = new Random(); return random.nextInt(900000) + 100000 +""; } public static void main(String[] args) { System.out.println(getCode()); }}
package com.tianju.fresh.util;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component;import java.util.Set;import java.util.concurrent.TimeUnit;@Componentpublic class RedisUtil { @Autowired private RedisTemplate redisTemplate; @Autowired private StringRedisTemplate stringRedisTemplate; public void saveObjectToRedis(String key,Object json){ redisTemplate.opsForValue().set(key,json); } /** * 从redis里面获取json对象,如果没有,返回null * @param key * @return */ public Object getJsonFromRedis(String key){ return redisTemplate.opsForValue().get(key); } /** * 删除redis里面的值,键 * @param key 删除的键 * @return */ public Boolean deleteKey(String key){ return redisTemplate.delete(key); } /** * 存到Redis里面的 set 中 * @param key 存的键 * @param value 存到set的值 */ public void saveSetToRedis(String key,String... value){ for (String s:value){ if (s!=null && !s.equals("")){ stringRedisTemplate.opsForSet().add(key, s); } } } /** * 判断是否在redis的set中 * @param key * @param val * @return */ public Boolean isInSet(String key,String val){ return stringRedisTemplate.opsForSet().isMember(key, val); } /** * 获得set中的值 * @param key 键 * @return */ public Set getSet(String key){ return stringRedisTemplate.opsForSet().members(key); } /** * 从redis里面的set删除数据 * @param key * @param val */ public void removeFromSet(String key,String val){ stringRedisTemplate.opsForSet().remove(key, val); } /** * 获得存到redis里面的键对应的string类型的值 * @param key 键 * @return */ public String getStringValue(String key){ return stringRedisTemplate.opsForValue().get(key); } /** * 保存到redis里面string * @param key * @param timeout 过期时间,传的是多少s之后过期 * @param val */ public void saveStringValue(String key,String val,Integer... timeout){ if (timeout==null){ stringRedisTemplate.opsForValue().set(key,val); }else { stringRedisTemplate.opsForValue().set(key,val,timeout[0], TimeUnit.SECONDS); } } /** * 看某个键是否存在于Redis中 * @param key 待检验的键 * @return */ public Boolean isKeyInRedis(String key){ return stringRedisTemplate.hasKey(key); }}
6.controller层代码
package com.tianju.fresh.controller;import com.tianju.fresh.entity.vo.LoginVo;import com.tianju.fresh.resp.HttpResp;import com.tianju.fresh.service.CustomerService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;@RestController@RequestMapping("/api/user")@Slf4jpublic class CustomerController { @Autowired private CustomerService customerService; @PostMapping("/login") public HttpResp login(@RequestBody LoginVo loginVo){ log.debug("登陆前端传的参数>>>"+loginVo); return customerService.login(loginVo); } @GetMapping("/login/getSMSCode/{str}") public HttpResp getSMSCode(@PathVariable("str") String str){ log.debug("即将给邮箱/手机 "+str+"发送验证码"); return customerService.getLoginSMSCode(str); } @GetMapping("/register/getSMSCode/{str}") public HttpResp getRegisterSMSCode(@PathVariable("str") String str){ log.debug("即将给邮箱/手机 "+str+"发送验证码"); return customerService.getRegisterSMSCode(str); }}
效果展示
项目启动后,主题模式的交换机和两个队列初始化成功
邮箱队列和电话队列
交换机和队列之间的绑定关系
手机验证码
调用controller层代码后,后台打印日志
手机验证码存储到Redis里面,有效期5分钟
获得阿里云发送的短信
邮箱验证码
调用controller层接口发送邮箱验证码
邮箱验证码存到Redis里面
收到邮箱验证码
总结
1.RabbitMQ的Docker版本安装和配置,延迟插件的安装; 2.结合QQ邮箱和阿里云短信验证码服务,采用主题模式进行验证码的发送;
-----已授权转载
概述数据库中的数据直接呈现出来一般不是我们想要的,所以我们上两节演示了如何对数据进行过滤的方法。除了对数据进行过滤,我们可能还需要对数据进行排序,比如想从列表中了解消费最高的项,就可能需要对金额字段做...
2024.11.15数据库设计是一个令人挠头的问题,尤其是当我们涉及到各种设置和优化时。一个常见的问题是,如果在使用 MySQL 的 InnoDB 存储引擎时没有设置自增主键,会发生什么呢?本文将深入探讨这个问题,帮助你...
2024.11.12下载MySQL5.6访问MySQL官网点击Downloads,然后选择Archives选择MySQL Community Server选择合适版本和平台选择下载预先编译好的二进制安装包将下载好的包转移...
2024.11.10简介Mysql平台:Windows版本:8.0.28下载地址: https://dev.mysql.com/downloads/mysql/我们选择压缩版 下载即可 选择第一个版本【211.7M】...
2024.11.15今天登录系统,发现系统报错。判断是数据库出问题了。于是检查MySQL服务是否已启动,发现在“服务”里MySQL是启动的,但奇怪的是进程却没有MySQL。不管是重启mysql服务还是通过DOS启动MyS...
2024.11.07