前言
我们今天要实现的功能是用户在注册的时候会填写一个邮箱,后台拿到这个邮箱并向邮箱发一个激活账号的邮件
流程
生产者
第一步
配置ActiveMQconfig
项目框架是SpringMVC,所以接着上一篇那个工程在SpringMVC和Root两个容器之外,再建立一个ActiveMQConfig.java以完成消息队列的配置
消息队列的配置信息如下
@Configuration
@PropertySource(value = "classpath:mq.properties")
public class ActiveMQConfig {
@Value("${mq.username}")
private String username;
@Value("${mq.password}")
private String password;
@Value("${mq.brokerURL}")
private String url;
/*ActiveMQConnectionFactory*/
@Bean
public ActiveMQConnectionFactory activeMQConnectionFactory(){
return new ActiveMQConnectionFactory(username,password,url);
}
@Bean
/*CachingConnectionFactory给ActiveMQConnectionFactory裹上了一层外衣*/
public ConnectionFactory connectionFactory(){
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
cachingConnectionFactory.setTargetConnectionFactory(this.activeMQConnectionFactory());
cachingConnectionFactory.setSessionCacheSize(30);
return cachingConnectionFactory;
}
@Bean
/*JmsTemplate*/
public JmsTemplate jmsTemplate(){
JmsTemplate jmsTemplate = new JmsTemplate();
//传入工厂
jmsTemplate.setConnectionFactory(this.connectionFactory());
//开启服务质量控制
jmsTemplate.setExplicitQosEnabled(true);
//持久化------生产者
jmsTemplate.setDeliveryMode(DeliveryMode.PERSISTENT);
//客户端签收消息
jmsTemplate.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
return jmsTemplate;
}
}
username,password,url都是默认的null,null,tcp://localhost:61616,我的mq.properties如下
这里要说一下具体是什么意思,
- ActiveMQConnectionFactoryBean是真正可以产生Connection的ConnectionFactory,由对应的JMS服务厂商提供
- CachingConnectionFactoryBean
扩展自SingleConnectionFactory,主要用于提供缓存JMS资源功能。具体包括MessageProducer、MessageConsumer和Session的缓存功能。中发送消息的核心是JmsTemplate,然而Jmstemplate的问题是在每次调用时都要打开/关闭session和producter,效率很低,所以引申出了PooledConnectionFactory连接池,用于缓存session和producter。然而这还不是最好的。从spring2.5.3版本后,Spring又提供了CachingConnectionFactory,这才是首选的方案。然而CachingConnectionFactory有一个问题必须指出,默认情况下, CachingConnectionFactory只缓存一个session,在它的JavaDoc中,它 声明对于低并发情况下这是足够的。与之相反,PooledConnectionFactory的 默认值是500。这些设置,在很多情况下,需要亲自去测试并验证。我将其设置为100,对我来说还是很不错 - JmsTemplate是Spring提供的发送消息的工具
核心业务
添加数据而已,由用户提供用户名,邮箱和年龄,Service如下
@Service
public class UserService {
@Autowired
UserMapper dao;
@Autowired
JmsTemplate jmsTemplate;
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,rollbackFor = Throwable.class)
public int addUser(User user){
//User(name,password,age,email),这里创建密码并加密
String s = DigestUtils.md5Hex("123456");
user.setPwd(s);
//Email(receiver,topic,content)
Email email =new Email(user.getEmail(),"激活账号","请点击连接激活 :http://localhost:8080/user/active/"+user.getUname());
//发送email该对象的JSON字符串给消息队列,并且queue名字也叫email
jmsTemplate.send("email",session -> session.createTextMessage(JSONObject.toJSONString(email)));
//添到数据库,再往下的步骤我就不写了
return dao.addUser(user);
}
}
消费者
配置
Consumer
消费者的这边的ActiveMQ的配置信息我用xml写,融会贯通嘛,刚才生产者的配置由三个部分,而这里有四部分
- ActiveMQConnectionFactoryBean同上
- CacheConnectionFactoryBean同上
- Queue指明是从哪个队列里取消息
- Listener监听器,监听队列中的信息是否有未消费的条款
activeMQ.xml代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
">
<context:component-scan base-package="com.etoak" />
<bean id="mqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
c:userName=""
c:password=""
c:brokerURL="tcp://localhost:61616"/>
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"
c:targetConnectionFactory-ref="mqConnectionFactory"
p:sessionCacheSize="30"/>
<bean id="emailQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg name="name" value="email" />
</bean>
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<!--CachingConnectionFactory-->
<property name="connectionFactory" ref="connectionFactory"/>
<!--队列-->
<property name="destination" ref="emailQueue"/>
<!--自己写的监听器-->
<property name="messageListener" ref="emailMsgListener"/>
<!--签收方式 2表示手动签收 1表示自动签收-->
<property name="sessionAcknowledgeMode" value="2"/>
</bean>
<import resource="classpath:spring-email.xml" />
</beans>
由于需要发邮件所以需要额外的一个xml文件,这个我就不解释了,粘过来再在上面配置里<import>
一下就ok了
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
<!-- 简单邮件消息SimpleMailMessage -->
<!-- 获取到队列的消息,封装SimpleMailMessage -->
<!--
<bean id="simpleMailMessage"
class="org.springframework.mail.SimpleMailMessage">
<property name="from" value="luxilejn@163.com" />
</bean>
-->
<!-- 邮件发送对象:JavaMailSenderImpl -->
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<!-- 主机、用户名、授权码、默认编码、属性配置 -->
<property name="host" value="smtp.163.com" />
<property name="username" value="luxilejn@163.com" />
<!-- 不是登录密码,而是授权码 -->
<property name="password" value="et1812" />
<property name="defaultEncoding" value="UTF-8" />
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.timeout">10000</prop>
</props>
</property>
</bean>
<!-- 线程池ThreadPoolTaskExecutor:用于异步下发邮件 -->
<bean id="threadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程数:最小线程数 -->
<property name="corePoolSize" value="10" />
<!-- 空闲时间 -->
<property name="keepAliveSeconds" value="60000" />
<!-- 最大线程数 -->
<property name="maxPoolSize" value="60" />
<!-- 线程池使用的缓存队容量 -->
<property name="queueCapacity" value="200" />
</bean>
</beans>
业务流程
手动实现的监听器(上文注释中有提 ref=emailMsgListener)
@Service
public class EmailMsgListener implements MessageListener {
@Autowired
EmailService service;
@Override
public void onMessage(Message message) {
//拿到队列中的消息转TextMessage但在开发中还是建议用ObjectMessage
TextMessage textMessage =(TextMessage) message;
try {
//从TextMessage中拿消息
String json =textMessage.getText();
//解包-->JSON转对象 需要Email实体类 (String receiver,String topic,String content)
Email email = JSONObject.parseObject(json,Email.class);
//命令EmailService的实现类发邮件
service.send(email);
//签收消息
textMessage.acknowledge();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
最后是发邮件的Service
@Service
public class EmailService {
@Autowired
JavaMailSenderImpl mailSender;
@Autowired
ThreadPoolTaskExecutor executor;
public void send(Email email){
/*simpleMailMessage*/
SimpleMailMessage simplEmailMessage =new SimpleMailMessage();
//邮件发送者
simplEmailMessage.setFrom("公司邮箱@163.com");
//邮件接收者------用户注册时的邮箱
simplEmailMessage.setTo(email.getReceiver());
//邮件主题--------"激活邮件"
simplEmailMessage.setSubject(email.getTopic());
//邮件内容--------url+userName
simplEmailMessage.setText(email.getContent());
//设置抄送
simplEmailMessage.setCc("想要顺道一块发送消息的邮箱@qq.com");
executor.execute(()->{
mailSender.send(simplEmailMessage);
});
}
}