Spring Cloud Alibaba 实现操作日志

实现技术:

1. Spring AOP

2. Spring Cloud Stream

3. RocketMQ

前提准备

Centos 7 使用docker安装命令:

1. 启动Rocketmq的依赖服务: NameServer

docker run -d -p 9876:9876 --name rmqserver  foxiswho/rocketmq:server-4.7.0

2. 启动rockermq

docker run -d -p 10911:10911 -p 10909:10909\

 --name rmqbroker --link rmqserver:namesrv\

 -e "NAMESRV_ADDR=namesrv:9876" -e "JAVA_OPTS=-Duser.home=/opt"\

 -e "JAVA_OPT_EXT=-server -Xms128m -Xmx128m"\

 -v /root/ailp/rocketmq/conf/broker.conf:/etc/rocketmq/broker.conf \

 foxiswho/rocketmq:broker-4.7.0

添加依赖

        <!-- 我的其他依赖: Sring Boot依赖, Sring Cloud依赖, Spring Cloud Alibaba依赖 -->
        ...        

        <!-- rocketmq 消息驱动 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
        </dependency>

启动类添加注解

/*
1.添加自定义的Sink[消费] 或 Source类[生产者], 默认的是Sink.class和Source.class
2.如果微服务作为消费者就只添加相关的自定义Sink.class接口, 生产者端类似
3.下面代码块创建此两个接口类
*/ 
@EnableBinding({OperationLogSink.class, OperationLogSource.class})

 application.yml 添加配置

spring:
  cloud:
    # 消息驱动
    stream:
      rocketmq:
        binder:
          # rocketmq的访问URL  ip:port  
          name-server: xxx.xxx.xxx.xxx:9876
          # rocketmq 必须要定义组,否则启动不起来  
          group: user-center-group
      bindings:
        # 自定义的操作日志发送source,生产者
        operation-log-output:
          # 相当于主题topic  
          destination: operation-log
          content-type: application/json
        # 自定义的操作日志接收sink,消费者
        operation-log-input:
          destination: operation-log
          group: operation-log-group

写代码

        说明:操作日志不用实现rocketmq的分布式事务

        1. 生产者

import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;

/**
 * 自定义source
 *      1. 定义source名字
 *      2. 启动类@EnableBinger中添加此类
 *      3. 配置文件中添加 spring.cloud.stream.rocketmq.bindings.[自定义source].xxx
 *      4. 配置文件中添加 spring.cloud.stream.bindings.[自定义source].xxx
 */
public interface OperationLogSource {

    String OPERATION_LOG_OUTPUT = "operation-log-output";

    @Output(OPERATION_LOG_OUTPUT)
    MessageChannel operationLogOutput();

    // 可定义多个output结合使用
    // 调用时: this.operationLogSource .output1().send(message) ;     
    //         this.operationLogSource .output2().send(message)
    //    @Output("output1")
    //    MessageChannel output1();
    //    @Output("output2")
    //    MessageChannel output2();

}

         2.消费者

import org.springframework.cloud.stream.annotation.Input;
import org.springframework.messaging.SubscribableChannel;

public interface OperationLogSink {

    String OPERATION_LOG_INPUT = "operation-log-input";

    @Input(OPERATION_LOG_INPUT)
    SubscribableChannel operationLogInput();

}

        3.消费端消息监听类

import com.kym.api.usercenter.streammessages.OperationLogMessage;
import com.kym.usercenter.rokectmq.streams.sinks.OperationLogSink;
import com.kym.usercenter.service.IOperationLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.stereotype.Service;

/**
 * topic operation-log 的消息监听类
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OperationLogReceive {

    private final IOperationLogService operationLogService;

    @StreamListener(OperationLogSink.OPERATION_LOG_INPUT)
    public void getMessage(OperationLogMessage message) {
        # 业务代码-添加操作日志,基本的save操作
        this.operationLogService.addOperationLogMessage(message);
    }


}

         4. 操作日志切面注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationAnnotation {

    /**
     * 操作内容
     * @return String
     */
    String content() default "";
}

         5. 注解AOP实现

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import org.springframework.util.MimeTypeUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 操作日志AOP
 *      -- 保存过程由 RocketMQ 异步执行
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OperationLogAspect {

    private final OperationLogSource operationLogSource;

    /**
     * 操作日志AOP
     * @param joinPoint 切入点
     */
    @AfterReturning("@annotation(com.kym.common.base.aops.logs.OperationAnnotation)")
    public void OperationHandler(JoinPoint joinPoint){
        log.debug("开始记录操作日志》》》》》》》》》》》》》");

        // 获取主键所在方法
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // 获取当前会话request
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = attributes.getRequest();
        // 获取注解
        OperationAnnotation operationAnnotation = method.getAnnotation(OperationAnnotation.class);

        // 添加参数信息
        OperationLogMessage logMessage = OperationLogMessage.builder()
                .methodName(method.getName())
                .className(method.getDeclaringClass().getName())
                .methodParams(Arrays.toString(joinPoint.getArgs()))
                .content(operationAnnotation.content())
                .userId(request.getHeader("userId")) # 通过request中token获取
                .ip(WebRequestUtils.getIp(request))  # 获取真实IP,百度一堆
                .build();
     

        // 转换为消息体
        Message<OperationLogMessage> message =
                MessageBuilder
                    .withPayload(logMessage)
                    .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
                    .build();
        // 发送消息体
        operationLogSource.operationLogOutput().send(message);
        log.debug("结束记录操作日志》》》》》》》》》》》》》");
    }

}

         6. 具体使用, 使用http工具请求即可

    // 插入操作日志,添加注解
    @OperationAnnotation(content = "保存xxx")
    @PutMapping(value = "/save")
    public R save(@RequestBody XxxDTO xxxDTO){
        try{
            // 保存业务
            ...
            return R.success();
        } catch (VerifyDataException e) {
            throw new ApiException(500, e.getMessage());
        }
    }

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值