Quellcode durchsuchen

添加限流注解、通知节点细分

caixiaofeng vor 5 Monaten
Ursprung
Commit
c46eb2ee02

+ 5 - 0
flow-common/flow-common-core/pom.xml

@@ -34,6 +34,11 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
+        <!--aop-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
         <!--oauth2-->
         <dependency>
             <groupId>com.flow</groupId>

+ 29 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/annotation/RateLimiter.java

@@ -0,0 +1,29 @@
+package com.flow.common.core.annotation;
+
+
+import com.flow.common.core.enums.LimitType;
+import org.redisson.api.RateIntervalUnit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RateLimiter {
+    // 限流key
+    String key() default "rate_limit:";
+
+    // 限流时间,单位秒
+    int time() default 60;
+
+    // 限流时间单位
+    RateIntervalUnit unit() default RateIntervalUnit.SECONDS;
+
+    // 限流次数
+    int count() default 100;
+
+    // 限流类型
+    LimitType limitType() default LimitType.DEFAULT;
+}

+ 1 - 1
flow-common/flow-common-redis-starter/src/main/java/com/flow/common/redis/annotation/RedissonLock.java → flow-common/flow-common-core/src/main/java/com/flow/common/core/annotation/RedissonLock.java

@@ -1,4 +1,4 @@
-package com.flow.common.redis.annotation;
+package com.flow.common.core.annotation;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;

+ 69 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/aspect/RateLimiterAspect.java

@@ -0,0 +1,69 @@
+package com.flow.common.core.aspect;
+
+import com.flow.common.core.annotation.RateLimiter;
+import com.flow.common.core.enums.LimitType;
+import com.flow.common.core.exception.BaseException;
+import com.flow.common.core.util.RequestUtils;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.redisson.api.RRateLimiter;
+import org.redisson.api.RateIntervalUnit;
+import org.redisson.api.RateType;
+import org.redisson.api.RedissonClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+/**
+ * 限流处理
+ */
+@Aspect
+@Component
+public class RateLimiterAspect {
+    private final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
+
+    @Autowired
+    private RedissonClient redissonClient;
+
+    @Before(value = "@annotation(rateLimiter)")
+    public void before(JoinPoint joinPoint, RateLimiter rateLimiter) {
+        int rateInterval = rateLimiter.time();
+        RateIntervalUnit unit = rateLimiter.unit();
+        int rate = rateLimiter.count();
+        String combineKey = getCombineKey(rateLimiter, joinPoint);
+        try {
+            RateType rateType = RateType.OVERALL;
+            if (rateLimiter.limitType() == LimitType.CLUSTER) {
+                rateType = RateType.PER_CLIENT;
+            }
+            RRateLimiter rRateLimiter = redissonClient.getRateLimiter(combineKey);
+            rRateLimiter.setRate(rateType, rate, rateInterval, unit);
+            if (rRateLimiter.tryAcquire()) {
+                long number = rRateLimiter.availablePermits();
+                log.info("限流令牌 => {},剩余令牌 => {},缓存Key => {}", rate, number, combineKey);
+            } else {
+                throw new BaseException("请求太频繁了,请稍后再试");
+            }
+        } catch (Exception e) {
+            throw new BaseException("服务器限流异常,请稍后重试");
+        }
+    }
+
+    public String getCombineKey(RateLimiter rateLimiter, JoinPoint joinPoint) {
+        StringBuilder stringBuffer = new StringBuilder(rateLimiter.key());
+        if (rateLimiter.limitType() == LimitType.IP) {
+            String ip = RequestUtils.getIp();
+            stringBuffer.append("ip:").append(ip);
+        }
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+        Class<?> targetClass = method.getDeclaringClass();
+        stringBuffer.append("#").append(targetClass.getName()).append(".").append(method.getName()).append("()");
+        return stringBuffer.toString();
+    }
+}

+ 4 - 5
flow-app/src/main/java/com/flow/RedissonLockAspect.java → flow-common/flow-common-core/src/main/java/com/flow/common/core/aspect/RedissonLockAspect.java

@@ -1,7 +1,7 @@
-package com.flow;
+package com.flow.common.core.aspect;
 
+import com.flow.common.core.annotation.RedissonLock;
 import com.flow.common.core.exception.BaseException;
-import com.flow.common.redis.annotation.RedissonLock;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
@@ -34,10 +34,9 @@ public class RedissonLockAspect {
     @Autowired
     private RedissonClient redissonClient;
 
-    @Around("@annotation(com.flow.common.redis.annotation.RedissonLock)")
-    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
+    @Around("@annotation(redissonLock)")
+    public Object around(ProceedingJoinPoint joinPoint, RedissonLock redissonLock) throws Throwable {
         Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
-        RedissonLock redissonLock = method.getAnnotation(RedissonLock.class);
         // 默认方法限定名+注解排名(可能多个)
         String prefix = StringUtils.isEmpty(redissonLock.prefixKey()) ? String.format("%s#%s", method.getDeclaringClass(), method.getName()) : redissonLock.prefixKey();
         // 创建SpringEL表达式上下文

+ 10 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/enums/LimitType.java

@@ -0,0 +1,10 @@
+package com.flow.common.core.enums;
+
+public enum LimitType {
+    // 默认策略全局限流
+    DEFAULT,
+    // 根据请求IP限流
+    IP,
+    // 实例限流(集群多后端实例)
+    CLUSTER
+}

+ 34 - 33
flow-common/flow-common-core/src/main/java/com/flow/common/core/util/RequestUtils.java

@@ -1,6 +1,8 @@
 package com.flow.common.core.util;
 
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.util.StringUtils;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
@@ -10,6 +12,10 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 
 public class RequestUtils {
+    private static final Logger log = LoggerFactory.getLogger(RequestUtils.class);
+    private static final String UNKNOWN = "unknown";
+    private static final String LOCALHOST_IP = "0:0:0:0:0:0:0:1";
+    private static final String LOCALHOST_IP1 = "127.0.0.1";
 
     public static HttpServletRequest getRequest() {
         return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
@@ -18,45 +24,40 @@ public class RequestUtils {
     public static String getIp() {
         HttpServletRequest request = RequestUtils.getRequest();
         // 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
-        String ip = request.getHeader("x-forwarded-for");
-        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
-            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
-                ip = request.getHeader("Proxy-Client-IP");
-            }
-            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
-                ip = request.getHeader("WL-Proxy-Client-IP");
-            }
-            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
-                ip = request.getHeader("HTTP_CLIENT_IP");
-            }
-            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
-                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
-            }
-            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
-                ip = request.getRemoteAddr();
-            }
-            if (ip.contains(",")) {
-                ip = ip.split(",")[0];
-            }
-            if ("127.0.0.1".equals(ip)) {
+        String ip = request.getHeader("X-Original-Forwarded-For");
+        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("X-Forwarded-For");
+        }
+        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("x-forwarded-for");
+        }
+        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("HTTP_CLIENT_IP");
+        }
+        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+        }
+        if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+            if (LOCALHOST_IP1.equals(ip) || LOCALHOST_IP.equals(ip)) {
                 // 获取本机真正的ip地址
                 try {
                     ip = InetAddress.getLocalHost().getHostAddress();
-                } catch (UnknownHostException ignored) {
-                }
-            }
-
-        } else {
-            String[] ips = ip.split(",");
-            for (int index = 0; index < ips.length; index++) {
-                String strIp = ips[index];
-                if (!("unknown".equalsIgnoreCase(strIp))) {
-                    ip = strIp;
-                    break;
+                } catch (UnknownHostException e) {
+                    log.error("getClientIp error: {}", e.getMessage());
                 }
             }
         }
-        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
+        if (!StringUtils.isEmpty(ip) && ip.indexOf(",") > 0) {
+            ip = ip.substring(0, ip.indexOf(","));
+        }
+        return LOCALHOST_IP.equals(ip) ? LOCALHOST_IP1 : ip;
     }
 
     public static String getUserAgent() {

+ 0 - 1
flow-common/flow-common-oauth2-starter/pom.xml

@@ -47,7 +47,6 @@
             <groupId>com.flow</groupId>
             <artifactId>flow-common-redis-starter</artifactId>
             <version>0.0.1-SNAPSHOT</version>
-            <optional>true</optional>
         </dependency>
     </dependencies>
 

+ 0 - 5
flow-common/flow-common-redis-starter/pom.xml

@@ -34,11 +34,6 @@
             <groupId>org.redisson</groupId>
             <artifactId>redisson-spring-boot-starter</artifactId>
         </dependency>
-        <!--aop-->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-aop</artifactId>
-        </dependency>
     </dependencies>
 
 </project>

+ 56 - 0
flow-workflow/flow-workflow-biz/src/main/java/com/flow/delegate/MailNotifyDelegate.java

@@ -0,0 +1,56 @@
+package com.flow.delegate;
+
+import com.flow.entity.User;
+import com.flow.flowable.utils.ProcessElementUtil;
+import com.flow.service.UserService;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.model.MultiInstanceLoopCharacteristics;
+import org.flowable.bpmn.model.ServiceTask;
+import org.flowable.common.engine.impl.el.ExpressionManager;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.delegate.JavaDelegate;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.http.common.impl.ExpressionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+@Slf4j
+@Component
+public class MailNotifyDelegate implements JavaDelegate {
+    @Autowired
+    private UserService userService;
+    @Autowired
+    private JavaMailSender mailSender;
+    @Value("${spring.mail.username}")
+    private String from;
+    @Autowired
+    private ProcessEngineConfigurationImpl processEngineConfiguration;
+
+    @Override
+    public void execute(DelegateExecution execution) {
+        ServiceTask serviceTask = (ServiceTask) execution.getCurrentFlowElement();
+        MultiInstanceLoopCharacteristics loopCharacteristics = serviceTask.getLoopCharacteristics();
+        String receiver = execution.getVariableLocal(loopCharacteristics.getElementVariable(), String.class);
+        ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
+        String subject = ProcessElementUtil.getFieldExtensionExpression(serviceTask, "subject");
+        String content = ProcessElementUtil.getFieldExtensionExpression(serviceTask, "content");
+        subject = ExpressionUtils.getStringFromField(expressionManager.createExpression(subject), execution);
+        content = ExpressionUtils.getStringFromField(expressionManager.createExpression(content), execution);
+        User user = userService.getByUsername(receiver);
+        if (Objects.isNull(user)) {
+            return;
+        }
+        log.info("发送邮件:{}", content);
+        SimpleMailMessage message = new SimpleMailMessage();
+        message.setFrom(this.from);
+        message.setTo(user.getEmail());
+        message.setSubject(subject);
+        message.setText(content);
+        mailSender.send(message);
+    }
+}

+ 5 - 45
flow-workflow/flow-workflow-biz/src/main/java/com/flow/delegate/NotifyDelegate.java

@@ -1,83 +1,43 @@
 package com.flow.delegate;
 
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
-import com.flow.entity.Notify;
-import com.flow.entity.User;
-import com.flow.enums.NotifyEnum;
 import com.flow.enums.NotifyTypeEnum;
 import com.flow.flowable.utils.ProcessElementUtil;
-import com.flow.service.NotifyService;
-import com.flow.service.UserService;
 import lombok.extern.slf4j.Slf4j;
-import org.flowable.bpmn.model.MultiInstanceLoopCharacteristics;
 import org.flowable.bpmn.model.ServiceTask;
-import org.flowable.common.engine.impl.el.ExpressionManager;
 import org.flowable.engine.delegate.BpmnError;
 import org.flowable.engine.delegate.DelegateExecution;
 import org.flowable.engine.delegate.JavaDelegate;
-import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
-import org.flowable.http.common.impl.ExpressionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.mail.SimpleMailMessage;
-import org.springframework.mail.javamail.JavaMailSender;
 import org.springframework.stereotype.Component;
 
-import java.time.LocalDateTime;
-
 
 @Slf4j
 @Component
 public class NotifyDelegate implements JavaDelegate {
     @Autowired
-    private JavaMailSender mailSender;
-    @Value("${spring.mail.username}")
-    private String from;
-    @Autowired
-    private NotifyService notifyService;
-    @Autowired
-    private UserService userService;
+    private SiteNotifyDelegate siteNotifyDelegate;
     @Autowired
-    private ProcessEngineConfigurationImpl processEngineConfiguration;
+    private MailNotifyDelegate mailNotifyDelegate;
 
     @Override
     public void execute(DelegateExecution execution) {
         ServiceTask notifyServiceTask = (ServiceTask) execution.getCurrentFlowElement();
         String types = ProcessElementUtil.getFieldExtensionValue(notifyServiceTask, "types");
-        String subject = ProcessElementUtil.getFieldExtensionExpression(notifyServiceTask, "subject");
-        String content = ProcessElementUtil.getFieldExtensionExpression(notifyServiceTask, "content");
-        if (StringUtils.isBlank(types) || StringUtils.isBlank(subject) || StringUtils.isBlank(content)) {
+        if (StringUtils.isBlank(types)) {
             throw new BpmnError("notify.error", "通知参数异常");
         }
-        ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
-        subject = ExpressionUtils.getStringFromField(expressionManager.createExpression(subject), execution);
-        content = ExpressionUtils.getStringFromField(expressionManager.createExpression(content), execution);
-        MultiInstanceLoopCharacteristics loopCharacteristics = notifyServiceTask.getLoopCharacteristics();
-        String assignee = execution.getVariableLocal(loopCharacteristics.getElementVariable(), String.class);
-        User user = userService.getByUsername(assignee);
         String[] typeArray = types.split(",");
         for (String type : typeArray) {
             NotifyTypeEnum typeEnum = NotifyTypeEnum.match(type);
             switch (typeEnum) {
                 case SITE:
                     // 站内通知
-                    Notify notify = new Notify();
-                    notify.setSender("admin");
-                    notify.setType(NotifyEnum.SYSTEM);
-                    notify.setReceivingTime(LocalDateTime.now());
-                    notify.setReceiver(assignee);
-                    notify.setSubject(subject);
-                    notify.setContent(content);
-                    notifyService.notify(notify);
+                    siteNotifyDelegate.execute(execution);
                     break;
                 case EMAIL:
                     // 邮件通知
-                    SimpleMailMessage message = new SimpleMailMessage();
-                    message.setFrom(this.from);
-                    message.setTo(user.getEmail());
-                    message.setSubject(subject);
-                    message.setText(content);
-                    mailSender.send(message);
+                    mailNotifyDelegate.execute(execution);
                     break;
                 default:
                     break;

+ 49 - 0
flow-workflow/flow-workflow-biz/src/main/java/com/flow/delegate/SiteNotifyDelegate.java

@@ -0,0 +1,49 @@
+package com.flow.delegate;
+
+import com.flow.entity.Notify;
+import com.flow.enums.NotifyEnum;
+import com.flow.flowable.utils.ProcessElementUtil;
+import com.flow.service.NotifyService;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.bpmn.model.MultiInstanceLoopCharacteristics;
+import org.flowable.bpmn.model.ServiceTask;
+import org.flowable.common.engine.impl.el.ExpressionManager;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.delegate.JavaDelegate;
+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.flowable.http.common.impl.ExpressionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+
+@Slf4j
+@Component
+public class SiteNotifyDelegate implements JavaDelegate {
+    @Autowired
+    private NotifyService notifyService;
+    @Autowired
+    private ProcessEngineConfigurationImpl processEngineConfiguration;
+
+    @Override
+    public void execute(DelegateExecution execution) {
+        ServiceTask serviceTask = (ServiceTask) execution.getCurrentFlowElement();
+        MultiInstanceLoopCharacteristics loopCharacteristics = serviceTask.getLoopCharacteristics();
+        String receiver = execution.getVariableLocal(loopCharacteristics.getElementVariable(), String.class);
+        ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
+        String subject = ProcessElementUtil.getFieldExtensionExpression(serviceTask, "subject");
+        String content = ProcessElementUtil.getFieldExtensionExpression(serviceTask, "content");
+        subject = ExpressionUtils.getStringFromField(expressionManager.createExpression(subject), execution);
+        content = ExpressionUtils.getStringFromField(expressionManager.createExpression(content), execution);
+        log.info("发送站内消息:{}", content);
+        Notify notify = Notify.builder()
+                .sender("admin")
+                .type(NotifyEnum.SYSTEM)
+                .receivingTime(LocalDateTime.now())
+                .receiver(receiver)
+                .subject(subject)
+                .content(content)
+                .build();
+        notifyService.notify(notify);
+    }
+}

+ 2 - 2
flow-workflow/flow-workflow-entity/src/main/java/com/flow/entity/node/NotifyNode.java

@@ -27,8 +27,8 @@ public class NotifyNode extends AssigneeNode {
         serviceTask.setName(this.getName());
         serviceTask.setExecutionListeners(this.buidEventListener());
         serviceTask.setAsynchronous(true);
-        // serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
-        // serviceTask.setImplementation("${notifyDelegate}");
+        serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
+        serviceTask.setImplementation("${notifyDelegate}");
         String variable = String.format("%sItem", this.getId());
         MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
         multiInstanceLoopCharacteristics.setSequential(false);