八.利用springAMQP实现异步消息队列的日志管理
经过前段时间的学习和铺垫,已经对spring amqp有了大概的了解。俗话说学以致用,今天就利用springAMQP来完成一个日志管理模块。大概的需求是这样的:系统中有很多地方需要记录操作日志,比如登录、退出、查询等,如果将记录日志这个操作掺杂在主要的业务逻辑当中,势必会增加响应的时间,对客户来说是一种不好的体验。所以想到用异步消息队列来进行优化。系统处理完主要业务逻辑之后,将日志的相关实体发布到特定Queue下,然后设置一个监听器,监该Queue的消息并做处理。客户不用等待日志的处理就可直接返回。
大概的业务流程如下图所示。

1.首先建立日志的数据表和实体,数据表起名为t_log。实体如下。主要包含操作者,操作的事件,操作时间等几个主要参数。
package com.xdx.entity;
import java.util.Date;
public class TLog {
private Integer logId;
private String operator;
private String event;
private Date createTime;
private Integer isDel;
public TLog(String operator, String event) {
this.operator = operator;
this.event = event;
}
public TLog() {
}
public Integer getLogId() {
return logId;
}
public void setLogId(Integer logId) {
this.logId = logId;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator == null ? null : operator.trim();
}
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event == null ? null : event.trim();
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Integer getIsDel() {
return isDel;
}
public void setIsDel(Integer isDel) {
this.isDel = isDel;
}
}
2.编写保存日志的方法,很简单,就是一个数据库的save过程。
package com.xdx.service; import javax.annotation.Resource; import org.springframework.stereotype.Service; import com.xdx.dao.BaseDao;
import com.xdx.entity.TLog; @Service
public class LogService {
@Resource(name = "baseDao")
private BaseDao<TLog, Integer> baseDao; public Integer saveLog(TLog log) {
Integer result = baseDao.addT("TLogMapper.insertSelective", log);
return result;
}
}
其中的TLogMapper.insertSelective代码如下:
<insert id="insertSelective" parameterType="com.xdx.entity.TLog" >
insert into t_log
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="logId != null" >
log_id,
</if>
<if test="operator != null" >
operator,
</if>
<if test="event != null" >
event,
</if>
<if test="createTime != null" >
create_time,
</if>
<if test="isDel != null" >
is_del,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="logId != null" >
#{logId,jdbcType=INTEGER},
</if>
<if test="operator != null" >
#{operator,jdbcType=VARCHAR},
</if>
<if test="event != null" >
#{event,jdbcType=VARCHAR},
</if>
<if test="createTime != null" >
#{createTime,jdbcType=TIMESTAMP},
</if>
<if test="isDel != null" >
#{isDel,jdbcType=INTEGER},
</if>
</trim>
</insert>
3.接下来就跟我们的spring amqp有关了,首先要在pom.xml中引入相关的jar包。
<!-- spring-rabbitMQ -->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<!-- spring -amqp -->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-amqp</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
4.编写主配置文件。在项目新建一个com.xdx.spring_rabbit包。关于rabbit的所有代码都写在这边。
编写一个抽象的rabbit的主配置文件,之所以这样做是为了以后扩展方便,让不同的异步消息队列的业务可以继承并扩展它。如下所示。
主配置的文件主要是配置了连接Rabbit服务的基本信息,并且指定了消息转换器是json转换器。
package com.xdx.spring_rabbit; import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; /**
* 抽象类,rabbitMQ的主配置类
*
* @author xdx
*
*/
public abstract class AbstractRabbitConfiguration { @Value("${amqp.port:5672}")
private int port = 5672; protected abstract void configureRabbitTemplate(RabbitTemplate template); /**
* 由于connectionFactory会与项目中的redis的connectionFactory命名冲突,
* 所以这边改名为rabbit_connectionFactory
*
* @return
*/
@Bean
public ConnectionFactory rabbit_connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(
"192.168.1.195");
connectionFactory.setUsername("xdx");
connectionFactory.setPassword("xxxx");
connectionFactory.setPort(port);
return connectionFactory;
} @Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(rabbit_connectionFactory());
template.setMessageConverter(jsonMessageConverter());
configureRabbitTemplate(template);
return template;
} @Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
} @Bean
public AmqpAdmin amqpAdmin() {
RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbit_connectionFactory());
return rabbitAdmin;
}
}
5.编写我们这个日志项目需要用到的配置文件,继承上述的抽象类,在该配置文件中,我们具体指定Exchange,RouteKey,Queue,Binding以及监听器这些要素。
package com.xdx.spring_rabbit; import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* 日志管理的Rabbit配置类的具体实现类
*
* @author xdx
*
*/
@Configuration
public class LogRabbitConfiguration extends AbstractRabbitConfiguration {
protected static String LOG_EXCHANGE_NAME = "warrior.exchange.log";// topic
// exchange的名称
protected static String LOG_QUEUE_NAME = "warrior.queue.log";// 接收消息的queue
protected static String LOG_ROUTING_KEY = LOG_QUEUE_NAME;
@Autowired
private LogRabbitRecHandler logRabbitRecHandler;// 监听器的委托类,委托其处理接收到的消息 /**
* 设置Exchange为LOG_EXCHANGE_NAME,RoutingKey为LOG_ROUTING_KEY,这样将信息发送到
* Exchange为LOG_EXCHANGE_NAME,RouteKey为LOG_ROUTING_KEY的通道中
*/
@Override
protected void configureRabbitTemplate(RabbitTemplate template) {
System.err.println("创建一个RabbitTemplate,名字是 " + template);
template.setExchange(LOG_EXCHANGE_NAME);
template.setRoutingKey(LOG_ROUTING_KEY);
} /**
* 用于接收日志消息的Queue,默认绑定自己的名称
*
* @return
*/
@Bean
public Queue logQueue() {
return new Queue(LOG_QUEUE_NAME);
} /**
* 定义一个topExchange
*
* @return
*/
@Bean
public TopicExchange logExchange() {
return new TopicExchange(LOG_EXCHANGE_NAME);
} /**
* 定义一个绑定日志接收的Queue的binding
*
* @return
*/
@Bean
public Binding logQueueBinding() {
return BindingBuilder.bind(logQueue()).to(logExchange())
.with(LOG_ROUTING_KEY);
} /**
* 这个bean为监听适配器,用于日志消息,并交由logRabbitRecHandler处理
*
* @return
*/
@Bean
public MessageListenerAdapter messageListenerAdapter() {
return new MessageListenerAdapter(logRabbitRecHandler,
jsonMessageConverter());
} /**
* 这个bean用于监听服务端发过来的消息,监听的Queue为logQueue(),
* 因为该Queue绑定了logExchange和logRouteKey, 所以它可以接收到我们发送的日志消息
* @return
*/
@Bean
public SimpleMessageListenerContainer messageListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(
rabbit_connectionFactory());
container.setConcurrentConsumers(5);
container.setQueues(logQueue());
container.setMessageListener(messageListenerAdapter());
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
return container;
}
}
6.封装发送消息的接口,如下所示。这是一个泛型的接口,目的是为了传入不同的消息类型。
package com.xdx.spring_rabbit;
/**
* 定义一个泛型接口,用于发送消息,T为要发送的消息类型
* @author xdx
*
* @param <T>
*/
public interface RabbitSend<T> {
void send(T t);
}
7.实现这个发送消息的接口。在这个实现类中,我们注入了之前生成的RabbitTemplate对象。用于发送消息。
package com.xdx.spring_rabbit; import javax.annotation.Resource; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component; import com.xdx.entity.TLog; /**
* 用于发送日志消息的通用实现类
*
* @author xdx
*
*/
@Component("logRabbitSend")
public class LogRabbitSend implements RabbitSend<TLog> {
@Resource(name = "rabbitTemplate")
private RabbitTemplate rabbitTemplate; @Override
public void send(TLog log) {
rabbitTemplate.convertAndSend(log);
System.err.println("发送消息:" + log);
}
}
8.封装监听器委托对象的接口,该接口用于处理监听器监听到的消息。同意是一个泛型的类,如下所示。
package com.xdx.spring_rabbit; /**
* 用于处理监听到的消息的消息处理器接口,T为接收到的消息的类型
*
* @author xdx
*
* @param <T>
*/
public interface RabbitRecHandler<T> {
void handleMessage(T t);
}
9.实现上述委托对象的接口,如下所示。在该接口中,我们注入了日志处理类的对象。用于储存日志信息到数据库。
package com.xdx.spring_rabbit; import javax.annotation.Resource; import org.springframework.stereotype.Component; import com.xdx.entity.TLog;
import com.xdx.service.LogService;
@Component("logRabbitRecHandler")
public class LogRabbitRecHandler implements RabbitRecHandler<TLog> {
@Resource(name="logService")
private LogService logService; @Override
public void handleMessage(TLog log) {
System.err.println("开始存储日志"+log.getOperator()+","+log.getEvent());
logService.saveLog(log);
}
}
10.最后,我们在具体的业务类中调用消息发送的接口,就可以实现日志消息的发送了。如下所示。
@Controller
public class AdminController {
@Resource(name = "logRabbitSend")
private LogRabbitSend logRabbitSend; @RequestMapping("admin")
public ModelAndView admin(HttpSession session,String adminName, String password) throws Exception {
List<Map<String,Object>>adminMap=adminService.getAllAdminMap();
ModelAndView mv = new ModelAndView();
//登录操作的主要逻辑代码……
session.setAttribute("adminName", admin.getAdminName());
session.setAttribute("realName", admin.getRealName());
TLog log=new TLog(adminName, "登录系统");
logRabbitSend.send(log);
return mv;
}
}
运行我们的系统,我们先看看RabbitMQ的后台。看到了我们定义的Exchange和Queue等元素。


运行AdmintController类中的admin方法,登录系统,我们发现确实已经发送了消息,并且消息被监听到,然后存储到了数据库。
控制台打印出来的消息为:

数据库存入的记录为:

八.利用springAMQP实现异步消息队列的日志管理的更多相关文章
- C#后台异步消息队列实现
简介 基于生产者消费者模式,我们可以开发出线程安全的异步消息队列. 知识储备 什么是生产者消费者模式? 为了方便理解,我们暂时将它理解为垃圾的产生到结束的过程. 简单来说,多住户产生垃圾(生产者)将垃 ...
- php 利用activeMq+stomp实现消息队列
php 利用activeMq+stomp实现消息队列 一.activeMq概述 ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线.ActiveMQ 是一个完全支持JMS1.1和J ...
- C#实现异步消息队列
原文:C#实现异步消息队列 拿到新书<.net框架设计>,到手之后迅速读了好多,虽然这本书不像很多教程一样从头到尾系统的讲明一些知识,但是从项目实战角度告诉我们如何使用我们的知识,从这本书 ...
- 异步消息队列Celery
Celery是异步消息队列, 可以在很多场景下进行灵活的应用.消息中包含了执行任务所需的的参数,用于启动任务执行, suoy所以消息队列也可以称作 在web应用开发中, 用户触发的某些事件需要较长事件 ...
- 【Redis】redis异步消息队列+Spring自定义注解+AOP方式实现系统日志持久化
说明: SSM项目中的每一个请求都需要进行日志记录操作.一般操作做的思路是:使用springAOP思想,对指定的方法进行拦截.拼装日志信息实体,然后持久化到数据库中.可是仔细想一下会发现:每次的客户端 ...
- 三.RabbitMQ之异步消息队列(Work Queue)
上一篇文章简要介绍了RabbitMQ的基本知识点,并且写了一个简单的发送和接收消息的demo.这一篇文章继续介绍关于Work Queue(工作队列)方面的知识点,用于实现多个工作进程的分发式任务. 一 ...
- .NetCore利用BlockingCollection实现简易消息队列
前言 消息队列现今的应用场景越来越大,常用的有RabbmitMQ和KafKa. 我们用BlockingCollection来实现简单的消息队列. 实现消息队列 用Vs2017创建一个控制台应用程序.创 ...
- Spring Boot 入门(八):集成RabbitMQ消息队列
本片文章续<Spring Boot 入门(七):集成 swagger2>,关于RabbitMQ的介绍请参考<java基础(六):RabbitMQ 入门> 1.增加依赖 < ...
- ELK之使用kafka作为消息队列收集日志
参考:https://www.cnblogs.com/fengjian2016/p/5841556.html https://www.cnblogs.com/hei12138/p/7805475 ...
随机推荐
- JavaScript数据可视化编程学习(一)Flotr2,包含简单的,柱状图,折线图,饼图,散点图
一.基础柱状图 二.基础的折线图 三.基础的饼图 四.基础的散点图 一.基础柱状图 如果你还没有想好你的数据用什么类型的图表来展示你的数据,你应该首先考虑是否可以做成柱状图.柱状图可以表示数据的变化过 ...
- [转载] 谷歌技术"三宝"之谷歌文件系统
转载自http://blog.csdn.net/opennaive/article/details/7483523 题记:初学分布式文件系统,写篇博客加深点印象.GFS的特点是使用一堆廉价的商用计算机 ...
- 四、VueJs 填坑日记之搭建Axios接口请求工具
上一章,我们认识了项目的目录结构,以及对项目的目录结构做了一些调整,已经能把项目重新跑起来了.今天我们来搭建api接口调用工具Axios.Vue本身是不支持ajax调用的,如果你需要这些功能就需要安装 ...
- 维多利亚的秘密 golang入坑系统
原文在gitbook,字字原创,版权没有,转载随意. 在写本文的前一天,2017维密在上海开始了. 为了纪念屌丝界的盛世,特为本节起名维多利亚的秘密.现在的社会,要想出名只有抓眼球.所以写份技术文章, ...
- 在ThinkPHP中使用常量解决路由常规地址不安全传送数据问题
在ThinkPHP搭建项目的同时,会考虑到后期对静态页面的维护问题, 在项目的不断完善的同时,会有大量图片,css文件,以及js文件等一些容易修改.添加.或者删除的资源 如果在中后期对各个静态页面,j ...
- React Native学习——动画Animated(笔记)
很多地方都需要用到动画,先看下文档吧. 一.两个互补的动画系统 LayoutAnimation:用于全局的布局动画 Animated:用于创建更精细的交互控制的动画(主要是这个) 二.Animated ...
- 实践作业2:黑盒测试实践——选择并下载测试工具 Day 2
1.选择工具为Katalon Studio 基于 Selenium 和 Appium 框架,Katalon Studio隐藏幕后的所有技术复杂性,并提供友好的用户界面与手动模式(用户可以拖放,选择关键 ...
- [安全]服务器安全之 PHP权限目录
1.为每个主机配置增加一个 fastcgi_param PHP_VALUE "open_basedir=$document_root:/tmp/"; 或是直接把这句话放到fa ...
- NGUI_概述
序言:这是张三疯第一次开始NGUI插件的学习,刚开始学习,肯定有很多漏洞,后期会及时的补上的. 希望大家可以见谅,希望大佬多多指教. 一.什么是NGUI: NGUI是严格遵循KISS原则并用C#编写的 ...
- dock使用方法
Docker 是一个开源项目,为开发者和系统管理员提供了一个开放的平台,在任何地方通过打包和运行应用程序作为一个轻量级的容器.Docker 在软件容器内自动部署应用程序.Docker 最开始由 Sol ...