Bladeren bron

优化分组逻辑

Woody 2 maanden geleden
bovenliggende
commit
15a2bccc28

+ 34 - 0
framework-group/src/main/java/com/chelvc/framework/group/DefaultGroupHandlerFactory.java

@@ -0,0 +1,34 @@
+package com.chelvc.framework.group;
+
+import java.util.Map;
+
+import com.chelvc.framework.base.context.ApplicationContextHolder;
+import com.chelvc.framework.common.util.ObjectUtils;
+import com.google.common.collect.Maps;
+import lombok.NonNull;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * 分组处理器工厂默认实现
+ *
+ * @author Woody
+ * @date 2025/4/7
+ */
+public class DefaultGroupHandlerFactory implements GroupHandlerFactory {
+    private final ApplicationContext applicationContext;
+    private final Map<Class<?>, GroupHandler> handlers = Maps.newConcurrentMap();
+
+    public DefaultGroupHandlerFactory(@NonNull ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T extends GroupHandler> T getHandler(@NonNull Class<T> type) {
+        T handler = ApplicationContextHolder.getBean(this.applicationContext, type, false);
+        if (handler == null && (handler = (T) this.handlers.get(type)) == null) {
+            handler = (T) this.handlers.computeIfAbsent(type, k -> ObjectUtils.instance(type));
+        }
+        return handler;
+    }
+}

+ 18 - 0
framework-group/src/main/java/com/chelvc/framework/group/GroupHandlerFactory.java

@@ -0,0 +1,18 @@
+package com.chelvc.framework.group;
+
+/**
+ * 分组处理器工厂接口
+ *
+ * @author Woody
+ * @date 2025/4/7
+ */
+public interface GroupHandlerFactory {
+    /**
+     * 获取分组处理器
+     *
+     * @param type 分组处理器对象类型
+     * @param <T>  分组处理器类型
+     * @return 分组处理器实例
+     */
+    <T extends GroupHandler> T getHandler(Class<T> type);
+}

+ 91 - 84
framework-group/src/main/java/com/chelvc/framework/group/GroupingInterceptor.java → framework-group/src/main/java/com/chelvc/framework/group/GroupProcessInterceptor.java

@@ -1,17 +1,15 @@
 package com.chelvc.framework.group;
 
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
-import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.base.context.ThreadContextHolder;
-import com.chelvc.framework.common.model.BiPair;
 import com.chelvc.framework.common.model.Caps;
 import com.chelvc.framework.common.model.Invoking;
 import com.chelvc.framework.common.util.ObjectUtils;
@@ -27,11 +25,10 @@ import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Component;
 
 /**
- * 场景分组拦截器
+ * 场景分组处理拦截器
  *
  * @author Woody
  * @date 2024/11/24
@@ -40,10 +37,9 @@ import org.springframework.stereotype.Component;
 @Aspect
 @Component
 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
-public class GroupingInterceptor {
+public class GroupProcessInterceptor {
     private final GroupStore store;
-    private final ApplicationContext applicationContext;
-    private final Map<Class<?>, GroupHandler> handlers = Maps.newConcurrentMap();
+    private final GroupHandlerFactory factory;
 
     /**
      * 判断请求是否匹配分组
@@ -60,17 +56,23 @@ public class GroupingInterceptor {
     }
 
     /**
-     * 初始化分组处理器
+     * 获取匹配的场景分组注解
      *
-     * @param type 处理器类型
-     * @return 分组处理器实例
+     * @param groups 多场景分组注解实例
+     * @return 分组注解实例列表
      */
-    private GroupHandler initializeGroupHandler(Class<? extends GroupHandler> type) {
-        GroupHandler handler = ApplicationContextHolder.getBean(this.applicationContext, type, false);
-        if (handler == null && (handler = this.handlers.get(type)) == null) {
-            handler = this.handlers.computeIfAbsent(type, k -> ObjectUtils.instance(type));
+    private List<Group> matches(Groups groups) {
+        if (Objects.isNull(SessionContextHolder.getId())) {
+            return Collections.emptyList();
+        }
+
+        List<Group> matches = Lists.newLinkedList();
+        for (Group group : groups.value()) {
+            if (this.matches(group)) {
+                matches.add(group);
+            }
         }
-        return handler;
+        return matches.isEmpty() ? Collections.emptyList() : matches;
     }
 
     /**
@@ -81,79 +83,45 @@ public class GroupingInterceptor {
      */
     private void execute(List<Group> groups, Object... args) {
         // 执行场景分组
-        List<BiPair<Group, GroupHandler, Caps>> values = Lists.newLinkedList();
+        Map<String, Caps> values = Maps.newHashMapWithExpectedSize(groups.size());
         for (Group group : groups) {
             try {
-                GroupHandler handler = this.initializeGroupHandler(group.handler());
+                GroupHandler handler = this.factory.getHandler(group.handler());
                 Caps value = handler.execute(group.scene(), args);
-                if (!group.storing()) {
-                    GroupContextHolder.setGroup(group.scene(), ObjectUtils.ifNull(value, Caps.NONE));
-                }
                 if (value != null) {
-                    values.add(BiPair.of(group, handler, value));
+                    values.put(group.scene(), value);
                 }
             } catch (Exception e) {
                 log.error("Group execute failed: {}", group.scene(), e);
             }
         }
+        if (ObjectUtils.isEmpty(values)) {
+            return;
+        }
 
         // 保存场景分组,以最终结果为准
-        Map<String, Caps> stores = values.stream().filter(pair -> pair.getLeft().storing())
-                .collect(Collectors.toMap(pair -> pair.getLeft().scene(), BiPair::getRight));
+        Map<String, Caps> stores;
         try {
-            stores = ObjectUtils.ifEmpty(stores, this.store::set, Collections::emptyMap);
-            stores.forEach((scene, group) -> GroupContextHolder.setGroup(scene, ObjectUtils.ifNull(group, Caps.NONE)));
+            stores = this.store.set(values);
+            stores.forEach(GroupContextHolder::setGroup);
         } catch (Exception e) {
             log.error("Group storing failed", e);
             return;
         }
 
         // 执行分组完成回调方法
-        for (BiPair<Group, GroupHandler, Caps> pair : values) {
-            Group group = pair.getLeft();
-            Caps value = pair.getRight();
-            GroupHandler handler = pair.getMiddle();
-            if (!group.storing() || value == stores.get(group.scene())) {
+        for (Group group : groups) {
+            String scene = group.scene();
+            Caps value = values.get(scene);
+            if (value != null && value == stores.get(scene)) {
                 try {
-                    handler.complete(group.scene(), value, args);
+                    GroupHandler handler = this.factory.getHandler(group.handler());
+                    handler.complete(scene, value, args);
                 } catch (Exception e) {
-                    log.error("Group complete failed: {}", group.scene(), e);
-                }
-            }
-        }
-    }
-
-    /**
-     * 执行分组逻辑
-     *
-     * @param invoking 分组时间点
-     * @param groups   分组注解列表
-     * @param args     分组执行参数
-     */
-    private void execute(Invoking invoking, List<Group> groups, Object... args) {
-        List<Group> sync = null, async = null;
-        for (Group group : groups) {
-            if (group.invoking() == invoking) {
-                if (group.async()) {
-                    if (async == null) {
-                        async = Lists.newLinkedList();
-                    }
-                    async.add(group);
-                } else {
-                    if (sync == null) {
-                        sync = Lists.newLinkedList();
-                    }
-                    sync.add(group);
+                    log.error("Group complete failed: {}", scene, e);
                 }
             }
         }
-        if (ObjectUtils.notEmpty(async)) {
-            List<Group> copy = async;
-            ThreadContextHolder.run(() -> this.execute(copy, args));
-        }
-        if (ObjectUtils.notEmpty(sync)) {
-            this.execute(sync, args);
-        }
     }
 
     /**
@@ -166,39 +134,78 @@ public class GroupingInterceptor {
      */
     @Around("@annotation(groups)")
     public Object intercept(ProceedingJoinPoint point, Groups groups) throws Throwable {
-        if (Objects.isNull(SessionContextHolder.getId())) {
-            return point.proceed();
-        }
-
         // 获取匹配的分组注解列表
-        List<Group> matches = Stream.of(groups.value()).filter(this::matches)
-                .collect(Collectors.toCollection(Lists::newLinkedList));
+        List<Group> matches = this.matches(groups);
         if (ObjectUtils.isEmpty(matches)) {
             return point.proceed();
         }
 
         // 获取已存储分组结果
+        Set<String> scenes = matches.stream().map(Group::scene).collect(Collectors.toSet());
         try {
-            Set<String> scenes = matches.stream().filter(Group::storing).map(Group::scene).collect(Collectors.toSet());
-            Map<String, Caps> stores = ObjectUtils.ifEmpty(scenes, this.store::get, Collections::emptyMap);
-            stores.forEach((scene, group) -> {
-                if (group != null) {
-                    GroupContextHolder.setGroup(scene, group);
-                    matches.removeIf(annotation -> Objects.equals(annotation.scene(), scene));
-                }
-            });
+            Map<String, Caps> values = this.store.get(scenes);
+            if (ObjectUtils.notEmpty(values)) {
+                values.forEach((scene, value) -> {
+                    if (value != null) {
+                        GroupContextHolder.setGroup(scene, value);
+                        matches.removeIf(group -> Objects.equals(group.scene(), scene));
+                    }
+                });
+            }
         } catch (Exception e) {
             log.error("Group loading failed", e);
             return point.proceed();
         }
 
-        // 执行分组逻辑(方法调用前后)
-        if (ObjectUtils.notEmpty(matches)) {
-            this.execute(Invoking.REQUEST, matches, point.getArgs());
+        if (ObjectUtils.isEmpty(matches)) {
+            return point.proceed();
+        }
+
+        // 执行方法前置分组
+        List<Group> sync = Lists.newLinkedList();
+        List<Group> async = Lists.newLinkedList();
+        Iterator<Group> iterator = matches.iterator();
+        while (iterator.hasNext()) {
+            Group group = iterator.next();
+            if (group.invoking() == Invoking.REQUEST) {
+                if (group.async()) {
+                    async.add(group);
+                } else {
+                    sync.add(group);
+                }
+                iterator.remove();
+            }
+        }
+        Object[] args = point.getArgs();
+        if (ObjectUtils.notEmpty(async)) {
+            ThreadContextHolder.run(() -> this.execute(async, args));
         }
+        if (ObjectUtils.notEmpty(sync)) {
+            this.execute(sync, args);
+        }
+
         Object value = point.proceed();
+
+        // 执行方法后置分组
         if (ObjectUtils.notEmpty(matches)) {
-            this.execute(Invoking.RESPONSE, matches, value);
+            async.clear();
+            iterator = matches.iterator();
+            while (iterator.hasNext()) {
+                Group group = iterator.next();
+                if (group.async()) {
+                    async.add(group);
+                    iterator.remove();
+                }
+            }
+            Object[] vars = new Object[args.length + 1];
+            System.arraycopy(args, 0, vars, 0, args.length);
+            vars[args.length] = value;
+            if (ObjectUtils.notEmpty(async)) {
+                ThreadContextHolder.run(() -> this.execute(async, vars));
+            }
+            if (ObjectUtils.notEmpty(matches)) {
+                this.execute(matches, vars);
+            }
         }
         return value;
     }

+ 10 - 8
framework-group/src/main/java/com/chelvc/framework/group/RedisGroupStore.java

@@ -6,12 +6,12 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.TimeUnit;
 
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.common.model.Caps;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
+import com.chelvc.framework.group.config.GroupProperties;
 import com.chelvc.framework.group.context.GroupContextHolder;
 import com.chelvc.framework.redis.context.RedisContextHolder;
 import com.chelvc.framework.redis.context.RedisHashHolder;
@@ -32,10 +32,12 @@ import org.springframework.data.redis.core.SessionCallback;
  * @date 2024/11/28
  */
 public class RedisGroupStore implements GroupStore {
+    private final Duration timeout;
     private final GroupClient client;
 
-    public RedisGroupStore(@NonNull GroupClient client) {
+    public RedisGroupStore(@NonNull GroupClient client, @NonNull GroupProperties properties) {
         this.client = client;
+        this.timeout = Duration.ofSeconds(properties.getCache().getDuration());
     }
 
     /**
@@ -66,8 +68,8 @@ public class RedisGroupStore implements GroupStore {
                 // 设置分组信息,如果已存在则忽略
                 operations.opsForHash().putIfAbsent((K) key, scene, group.name());
 
-                // 设置分组过期时间(1天)
-                operations.expire((K) key, 1, TimeUnit.DAYS);
+                // 设置分组过期时间
+                operations.expire((K) key, timeout);
                 return null;
             }
         });
@@ -101,8 +103,8 @@ public class RedisGroupStore implements GroupStore {
                 // 设置分组信息,如果已存在则忽略
                 stored.forEach((scene, group) -> operations.opsForHash().putIfAbsent((K) key, scene, group.name()));
 
-                // 设置分组过期时间(1天)
-                operations.expire((K) key, 1, TimeUnit.DAYS);
+                // 设置分组过期时间
+                operations.expire((K) key, timeout);
                 return null;
             }
         });
@@ -154,7 +156,7 @@ public class RedisGroupStore implements GroupStore {
         HashOperations<String, String, String> operations = template.opsForHash();
         group = StringUtils.ifEmpty(operations.get(key, scene), Caps::valueOf);
         if (group == null && (group = this.client.get(scene)) != null) {
-            RedisHashHolder.set(template, key, scene, group, Duration.ofDays(1));
+            RedisHashHolder.set(template, key, scene, group, this.timeout);
         }
         return group;
     }
@@ -201,7 +203,7 @@ public class RedisGroupStore implements GroupStore {
             Map<String, Caps> reloads = this.client.get(nones);
             if (ObjectUtils.notEmpty(reloads)) {
                 groups.putAll(reloads);
-                RedisHashHolder.set(template, key, reloads, Duration.ofDays(1));
+                RedisHashHolder.set(template, key, reloads, this.timeout);
             }
         }
         return groups;

+ 1 - 8
framework-group/src/main/java/com/chelvc/framework/group/annotation/Group.java

@@ -34,14 +34,7 @@ public @interface Group {
      *
      * @return true/false
      */
-    boolean async() default false;
-
-    /**
-     * 是否需要存储
-     *
-     * @return true/false
-     */
-    boolean storing() default false;
+    boolean async() default true;
 
     /**
      * 获取渠道数组

+ 11 - 2
framework-group/src/main/java/com/chelvc/framework/group/config/GroupConfigurer.java

@@ -1,9 +1,12 @@
 package com.chelvc.framework.group.config;
 
+import com.chelvc.framework.group.DefaultGroupHandlerFactory;
 import com.chelvc.framework.group.GroupClient;
+import com.chelvc.framework.group.GroupHandlerFactory;
 import com.chelvc.framework.group.GroupStore;
 import com.chelvc.framework.group.RedisGroupStore;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -17,7 +20,13 @@ import org.springframework.context.annotation.Configuration;
 public class GroupConfigurer {
     @Bean
     @ConditionalOnMissingBean(GroupStore.class)
-    public GroupStore groupStore(GroupClient client) {
-        return new RedisGroupStore(client);
+    public GroupStore groupStore(GroupClient client, GroupProperties properties) {
+        return new RedisGroupStore(client, properties);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean(GroupHandlerFactory.class)
+    public GroupHandlerFactory groupHandlerFactory(ApplicationContext applicationContext) {
+        return new DefaultGroupHandlerFactory(applicationContext);
     }
 }

+ 29 - 0
framework-group/src/main/java/com/chelvc/framework/group/config/GroupProperties.java

@@ -0,0 +1,29 @@
+package com.chelvc.framework.group.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 分组配置
+ *
+ * @author Woody
+ * @date 2025/4/7
+ */
+@Data
+@Configuration
+@ConfigurationProperties("group")
+public class GroupProperties {
+    /**
+     * 分组缓存配置
+     */
+    private final Cache cache = new Cache();
+
+    @Data
+    public static class Cache {
+        /**
+         * 缓存时间(秒)
+         */
+        private int duration = 24 * 60 * 60;
+    }
+}

+ 22 - 16
framework-group/src/main/java/com/chelvc/framework/group/context/GroupContextHolder.java

@@ -1,13 +1,14 @@
 package com.chelvc.framework.group.context;
 
+import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.function.Function;
 
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.Session;
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.common.model.Caps;
+import com.chelvc.framework.common.model.Range;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.group.GroupStore;
 import com.fasterxml.jackson.core.type.TypeReference;
@@ -26,10 +27,10 @@ public final class GroupContextHolder {
     private static volatile GroupStore GROUP_STORE;
 
     /**
-     * 场景分组模值映射类型
+     * 场景分组/数字区间列表映射类型
      */
-    private static final TypeReference<Map<Caps, Set<Integer>>> GROUP_MOLD_TYPE =
-            new TypeReference<Map<Caps, Set<Integer>>>() {
+    private static final TypeReference<Map<Caps, List<Range>>> GROUP_RANGE_TYPE =
+            new TypeReference<Map<Caps, List<Range>>>() {
             };
 
     private GroupContextHolder() {
@@ -65,27 +66,32 @@ public final class GroupContextHolder {
      * 获取配置分组
      *
      * @param property 配置属性
-     * @param mold     分组模值
+     * @param number   匹配数字
      * @return 分组标识
      */
-    public static Caps getConfig(@NonNull String property, int mold) {
-        return getConfig(property, mold, group -> group);
+    public static Caps getConfig(@NonNull String property, @NonNull Number number) {
+        return getConfig(property, number, value -> value);
     }
 
     /**
      * 获取配置分组
      *
      * @param property 配置属性
-     * @param mold     分组模值
+     * @param number   匹配数字
      * @param function 分组回调函数
      * @return 分组标识
      */
-    public static Caps getConfig(@NonNull String property, int mold, @NonNull Function<Caps, Caps> function) {
-        Map<Caps, Set<Integer>> mapping = ApplicationContextHolder.getProperty(property, GROUP_MOLD_TYPE);
-        if (ObjectUtils.notEmpty(mapping)) {
-            for (Map.Entry<Caps, Set<Integer>> entry : mapping.entrySet()) {
-                if (ObjectUtils.notEmpty(entry.getValue()) && entry.getValue().contains(mold)) {
-                    return function.apply(entry.getKey());
+    public static Caps getConfig(@NonNull String property, @NonNull Number number,
+                                 @NonNull Function<Caps, Caps> function) {
+        Map<Caps, List<Range>> ranges = ApplicationContextHolder.getProperty(property, GROUP_RANGE_TYPE);
+        if (ObjectUtils.notEmpty(ranges)) {
+            for (Map.Entry<Caps, List<Range>> entry : ranges.entrySet()) {
+                if (ObjectUtils.notEmpty(entry.getValue())) {
+                    for (Range range : entry.getValue()) {
+                        if (range.contains(number)) {
+                            return function.apply(entry.getKey());
+                        }
+                    }
                 }
             }
         }
@@ -122,10 +128,10 @@ public final class GroupContextHolder {
      * @param scene 分组场景
      * @param group 分组标识
      */
-    public static void setGroup(@NonNull String scene, @NonNull Caps group) {
+    public static void setGroup(@NonNull String scene, Caps group) {
         Session session = SessionContextHolder.getSession(false);
         if (session != null) {
-            session.setGroup(scene, group);
+            session.setGroup(scene, ObjectUtils.ifNull(group, Caps.NONE));
         }
     }
 }