欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何使用ThreadLocal上下文解決查詢性能問題

 更新時間:2023年07月21日 10:13:07   作者:我一直在流浪  
這篇文章主要介紹了利用ThreadLocal上下文解決查詢性能問題,有兩種解決方案,一種是使用ThreadLocal上下文,另一種是使用Redis緩存,需要的朋友可以參考下

問題背景:

安全事件列表中數(shù)據(jù)右鍵操作需要控制工單處置記錄和SOAR處置記錄是否置灰,置灰的邏輯是判斷當前用戶是否有相應的菜單權限以及當前租戶是否有相應的授權權限。這個判斷邏輯在每次刷新安全事件列表時都需要調(diào)用grpc接口判斷租戶是否授權,為了提高性能,所以禁止每次都去請求調(diào)用grpc接口,有兩種解決方案,一種是使用ThreadLocal上下文,另一種是使用Redis緩存。簡單介紹下如何使用ThreadLocal上下文解決性能問題。

01. 定義實體 Subscription

@Generated
public final class TenantManagement {
    @Generated
    public static final class Subscription {
        private volatile Object id;
        private volatile Object name;
        private volatile Object productId;
        private long startTime;
        private long expiryTime;
        private long createTime;
        private long renewTime;
        private volatile Object info;
        private volatile Object status;
        private volatile Object subscribeType;
    }
}

02. 定義 SubscribeInfoListContext 上下文

利用ThreadLocal上下文存儲需要緩存的數(shù)據(jù) List<TenantManagement.Subscription>

public class SubscribeInfoListContext {
    private static final ThreadLocal<List<TenantManagement.Subscription>> SUBSCRIBE_INFO_LIST_THREAD_LOCAL = new ThreadLocal<>();
    public static void set(List<TenantManagement.Subscription> subscribeInfoList) {
        SUBSCRIBE_INFO_LIST_THREAD_LOCAL.set(subscribeInfoList);
    }
    public static List<TenantManagement.Subscription> get() {
        return SUBSCRIBE_INFO_LIST_THREAD_LOCAL.get();
    }
    public static void remove() {
        SUBSCRIBE_INFO_LIST_THREAD_LOCAL.remove();
    }
    private SubscribeInfoListContext() {
    }
}

03. 定義SubscribeInfoService 接口

進行grpc接口調(diào)用獲取 List<TenantManagement.Subscription> 信息

public interface SubscribeInfoService {
    /**
     * 獲取授權信息
     *
     * @return 授權信息
     */
    List<TenantManagement.Subscription> getSubscribeInfoList();
}

1. 定義 SubscribeInfoServiceImpl 實現(xiàn)類

@CustomLog
@Service
public class SubscribeInfoServiceImpl implements SubscribeInfoService {
    @Setter(onMethod_ = @Autowired)
    private TenantManageClient tenantManageClient;
    @NotNull
    private static final List<String> SUBSCRIPTION_NAME_LIST = List.of(
            SubscriptionConstant.ORDER_SUBSCRIPTION,
            SubscriptionConstant.SOAR_SUBSCRIPTION
    );
    @Override
    public List<TenantManagement.Subscription> getSubscribeInfoList() {
        // 先從上下文SubscribeInfoListContext中獲取List<TenantManagement.Subscription>
        List<TenantManagement.Subscription> subscriptions = SubscribeInfoListContext.get();
        // 如果上下文SubscribeInfoListContext中獲取不到,再去調(diào)用grpc接口獲取
        if (!CollectionUtil.isEmpty(subscriptions)) {
            return subscriptions;
        }
        String tenantId = Objects.requireNonNull(TenantInfoContext.getTenantInfo()).getTenantId();
        List<TenantManagement.Subscription> subscribeInfos = tenantManageClient.getSubscribeInfoByIds(tenantId, SUBSCRIPTION_NAME_LIST);
        // 獲取到List<TenantManagement.Subscription> 后存入上下文SubscribeInfoListContext中
        SubscribeInfoListContext.set(subscribeInfos);
        return subscribeInfos;
    }
}
public interface SubscriptionInfoConstant {
    // 工單
    String ORDER_SUBSCRIPTION = "XDR_ORDER_MANAGEMENT";
    // SOAR
    String SOAR_SUBSCRIPTION = "XDR_SOAR_SECURITY_MANAGEMENT";
}

2. 判斷是否授權SOAR和是否有SOAR菜單權限 GenerateSoarDisableFlagConverter

@NoArgsConstructor
@AllArgsConstructor
public class GenerateSoarDisableFlagConverter implements ResultConverter {
    @Getter
    @Setter
    private Supplier<LicenseInfoService> licenseInfoServiceSupplier;
    @Getter
    @Setter
    private Supplier<SessionNoRelatedService> sessionNoRelatedService;
    @Getter
    @Setter
    private Supplier<SubscribeInfoService> subscribeInfoServiceSupplier;
    private static final String SUPER_ADMIN_POLICY = "superAdmin";
    private static final String SOAR_QUERY_POLICY = "soarQuery";
    private static final String SOAR_SUBSCRIPTION = "XDR_SOAR_SECURITY_MANAGEMENT";
    @Nullable
    @Override
    public Map<?, ?> convert(@NotNull SplTranslateContext<?> splTranslateContext, @Nullable Map<?, ?> map, boolean ifUsingSplReturnEntity) {
        LinkedHashMap<Object, Object> result;
        if (map == null) {
            result = new LinkedHashMap<>();
        } else {
            result = new LinkedHashMap<>(map);
        }
        Object value = !isSoarAvailable();
        result.put(
                "soarDisableFlag",
                ifUsingSplReturnEntity ? SplArrayReturnEntity.of(value)
                        : value
        );
        return result;
    }
    private Boolean isSoarAvailable() {
        return hasSoarPolicy() && hasSubscription(SOAR_SUBSCRIPTION);
    }
    public Boolean hasSubscription(String subscriptionName) {
        LicenseInfoService licenseInfoService = licenseInfoServiceSupplier.get();
        if (licenseInfoService.ifLicenseInfoIsSaas()) {
            List<TenantManagement.Subscription> subscribeInfos = subscribeInfoServiceSupplier.get().getSubscribeInfoList();
            if (CollectionUtils.isEmpty(subscribeInfos)) {
                return false;
            }
            List<TenantManagement.Subscription> subscriptionList = subscribeInfos.stream()
                    .filter(subscriptionInfo -> subscriptionInfo.getProductID().equals(subscriptionName) && checkSubscribeStatus(subscriptionInfo.getStatus()))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(subscriptionList)) {
                return false;
            }
            return true;
        }
        Collection<String> moduleCodes = licenseInfoServiceSupplier.get().getActiveModuleCodes();
        return moduleCodes.contains(subscriptionName);
    }
    private boolean checkSubscribeStatus(String status) {
        return SubscriptionStatusEnum.getActivateSubscriptionStatus().contains(status);
    }
    public Boolean hasSoarPolicy() {
        SessionContextResponseVo sessionContextNoRelated = sessionNoRelatedService.get().getSessionContextNoRelated();
        if (sessionContextNoRelated == null) {
            return false;
        }
        Set<String> policies = sessionContextNoRelated.getPolicies();
        if (policies.isEmpty()) {
            return false;
        }
        return policies.contains(SOAR_QUERY_POLICY) || policies.contains(SUPER_ADMIN_POLICY);
    }
}

3. 判斷是否授權工單和是否有工單菜單權限 GenerateSoarDisableFlagConverter

@NoArgsConstructor
@AllArgsConstructor
public class GenerateOrderDisableFlagConverter implements ResultConverter {
    @Getter
    @Setter
    private Supplier<LicenseInfoService> licenseInfoServiceSupplier;
    @Getter
    @Setter
    private Supplier<SessionNoRelatedService> sessionNoRelatedServiceSupplier;
    @Getter
    @Setter
    private Supplier<SubscribeInfoService> subscribeInfoServiceSupplier;
    private static final String SUPER_ADMIN_POLICY = "superAdmin";
    private static final String ORDER_QUERY_POLICY = "safeOperationQuery";
    private static final String ORDER_SUBSCRIPTION = "XDR_ORDER_MANAGEMENT";
    @Nullable
    @Override
    public Map<?, ?> convert(@NotNull SplTranslateContext<?> splTranslateContext, @Nullable Map<?, ?> map, boolean ifUsingSplReturnEntity) {
        LinkedHashMap<Object, Object> result;
        if (map == null) {
            result = new LinkedHashMap<>();
        } else {
            result = new LinkedHashMap<>(map);
        }
        Object value = !isOrderAvailable();
        result.put(
                "orderDisableFlag",
                ifUsingSplReturnEntity ? SplArrayReturnEntity.of(value)
                        : value
        );
        return result;
    }
    private Boolean isOrderAvailable() {
        return hasOrderPolicy() && hasSubscription(ORDER_SUBSCRIPTION);
    }
    public Boolean hasSubscription(String subscriptionName) {
        LicenseInfoService licenseInfoService = licenseInfoServiceSupplier.get();
        if (licenseInfoService.ifLicenseInfoIsSaas()) {
            List<TenantManagement.Subscription> subscribeInfos = subscribeInfoServiceSupplier.get().getSubscribeInfoList();
            if (CollectionUtils.isEmpty(subscribeInfos)) {
                return false;
            }
            List<TenantManagement.Subscription> subscriptionList = subscribeInfos.stream()
                    .filter(subscriptionInfo -> subscriptionInfo.getProductID().equals(subscriptionName) && checkSubscribeStatus(subscriptionInfo.getStatus()))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(subscriptionList)) {
                return false;
            }
            return true;
        }
        Collection<String> moduleCodes = licenseInfoService.getActiveModuleCodes();
        return moduleCodes.contains(subscriptionName);
    }
    private boolean checkSubscribeStatus(String status) {
        return SubscriptionStatusEnum.getActivateSubscriptionStatus().contains(status);
    }
    public Boolean hasOrderPolicy() {
        SessionContextResponseVo sessionContextNoRelated = sessionNoRelatedServiceSupplier.get().getSessionContextNoRelated();
        if (sessionContextNoRelated == null) {
            return false;
        }
        Set<String> policies = sessionContextNoRelated.getPolicies();
        if (policies.isEmpty()) {
            return false;
        }
        return policies.contains(ORDER_QUERY_POLICY) || policies.contains(SUPER_ADMIN_POLICY);
    }
}

4. 查詢安全事件列表接口中添加 ResultConverter

@CustomLog
@Service
public class IncidentSplTableHandlingServiceImpl extends SplTableHandlingServiceImpl implements InitializingBean {
    @Getter
    @Setter(onMethod_ = @Autowired)
    private LicenseInfoService licenseInfoService;
    @Getter
    @Setter(onMethod_ = @Autowired)
    private SessionNoRelatedService sessionNoRelatedService;
    @Getter
    @Setter(onMethod_ = @Autowired)
    private SubscribeInfoService subscribeInfoService;
    @Override
    public void afterPropertiesSet() throws Exception {
        List<ResultConverter> originalResultConverters = this.getResultConverters();
        List<ResultConverter> resultConverters = new ArrayList<>(originalResultConverters);
        resultConverters.add(
                new GenerateOrderDisableFlagConverter(
                        this::getLicenseInfoService,
                        this::getSessionNoRelatedService,
                        this::getSubscribeInfoService
                )
        );
        resultConverters.add(
                new GenerateSoarDisableFlagConverter(
                        this::getLicenseInfoService,
                        this::getSessionNoRelatedService,
                        this::getSubscribeInfoService
                )
        );
        this.setResultConverters(resultConverters);
    }
    // 省略.....
}

04. 添加攔截器 IncidentInterceptorConfig

在請求開始和請求結束時清除上下文SubscribeInfoListContext中的數(shù)據(jù),消除不同租戶間數(shù)據(jù)的影響。

@Data
@Configuration
@CustomLog
public class IncidentInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors( @NotNull InterceptorRegistry registry) {
        registry.addInterceptor(
                new HandlerInterceptorAdapter() {
                    // 在請求開始清除上下文SubscribeInfoListContext中的數(shù)據(jù)
                    @Override
                    public boolean preHandle(
                            @NotNull HttpServletRequest request,
                            @NotNull HttpServletResponse response,
                            @NotNull Object handler
                    ) throws Exception {
                        SubscribeInfoListContext.remove();
                        return true;
                    }
					// 在請求結束時清除上下文SubscribeInfoListContext中的數(shù)據(jù)
                    @Override
                    public void afterCompletion(
                            @NotNull HttpServletRequest request,
                            @NotNull HttpServletResponse response,
                            @NotNull Object handler,
                            @Nullable Exception ex
                    ) throws Exception {
                        SubscribeInfoListContext.remove();
                    }
                }
        );
    }
}

05. 添加 SaasThreadContextDataHolderSubscription 存儲緩存信息

public interface SaasThreadContextDataHolder {
}
@Data
@AllArgsConstructor
public class SaasThreadContextDataHolderSubscription implements SaasThreadContextDataHolder {
    @Nullable
    private final List<TenantManagement.Subscription> subscriptionList;
}

06. 添加 SaasThreadContextHolderSubscriptionInfo 以便后續(xù)擴展和使用

public interface SaasThreadContextHolder<T extends SaasThreadContextDataHolder> {
    /**
     * 獲取data holder類
     *
     * @return data holder類
     */
    @NotNull
    Class<T> getSaasThreadContextDataHolderClass();
    /**
     * 嘗試加載SaasThreadContextDataHolder
     *
     * @param holder SaasThreadContextDataHolder
     * @return 可以加載則true, 否則false
     */
    default boolean tryLoad(@NotNull SaasThreadContextDataHolder holder) {
        if (!getSaasThreadContextDataHolderClass().isInstance(holder)) {
            return false;
        }
        // noinspection unchecked
        this.load((T) holder);
        return true;
    }
    /**
     * 加載SaasThreadContextDataHolder
     *
     * @param holder SaasThreadContextDataHolder
     */
    void load(@NotNull T holder);
    /**
     * 存檔SaasThreadContextDataHolder
     *
     * @return SaasThreadContextDataHolder
     */
    @NotNull
    T save();
    /**
     * 清理SaasThreadContextDataHolder
     */
    void remove();
}
@AutoService(SaasThreadContextHolder.class)
public class SaasThreadContextHolderSubscriptionInfo implements SaasThreadContextHolder<SaasThreadContextDataHolderSubscriptionInfo> {
    @Override
    public @NotNull Class<SaasThreadContextDataHolderSubscriptionInfo> getSaasThreadContextDataHolderClass() {
        return SaasThreadContextDataHolderSubscriptionInfo.class;
    }
    @Override
    public void load(@NotNull SaasThreadContextDataHolderSubscriptionInfo holder) {
        List<TenantManagement.Subscription> subscriptionInfoList = holder.getSubscriptionInfoList();
        if(!CollectionUtils.isEmpty(subscriptionInfoList)){
            SubscriptionInfoContext.set(subscriptionInfoList);
        }
    }
    @Override
    public @NotNull SaasThreadContextDataHolderSubscriptionInfo save() {
        return new SaasThreadContextDataHolderSubscriptionInfo(
                SubscriptionInfoContext.get()
        );
    }
    @Override
    public void remove() {
        SubscriptionInfoContext.remove();
    }
}

07. 添加 SaasThreadContextUtil 工具類

public class SaasThreadContextUtil {
    @NotNull
    static List<SaasThreadContextHolder<?>> getSaasThreadContextHolders() {
        // IterableUtils.toList 方法將 ServiceLoader.load 返回的 Iterable 轉換成了 List
        return (List) IterableUtils.toList(
            	// 加載所有實現(xiàn)了 SaasThreadContextHolder 接口的類,并將它們轉換成 List 返回
            	ServiceLoader.load(SaasThreadContextHolder.class)
        );
    }
    @NotNull
    public static List<SaasThreadContextDataHolder> save() {
        List<SaasThreadContextHolder<?>> saasThreadContextHolders = getSaasThreadContextHolders();
        List<SaasThreadContextDataHolder> saasThreadContextDataHolders = new ArrayList<>(
                saasThreadContextHolders.size()
        );
        for (SaasThreadContextHolder<?> saasThreadContextHolder : saasThreadContextHolders) {
            saasThreadContextDataHolders.add(saasThreadContextHolder.save());
        }
        return saasThreadContextDataHolders;
    }
    public static void load(
            @NotNull List<SaasThreadContextDataHolder> saasThreadContextDataHolders
    ) {
        for (SaasThreadContextHolder<?> saasThreadContextHolder : getSaasThreadContextHolders()) {
            for (SaasThreadContextDataHolder saasThreadContextDataHolder : saasThreadContextDataHolders) {
                if (saasThreadContextHolder.tryLoad(saasThreadContextDataHolder)) {
                    break;
                }
            }
        }
    }
    public static void remove() {
        for (SaasThreadContextHolder<?> saasThreadContextHolder : getSaasThreadContextHolders()) {
            saasThreadContextHolder.remove();
        }
    }
    private SaasThreadContextUtil() {
    }
}

08. 使用 SaasThreadContextUtil

@Override
public FusionAlertVo countFusionAlerts(FusionAlertQo fusionAlertQo) {
    // 調(diào)用 SaasThreadContextUtil.save()
    List<SaasThreadContextDataHolder> threadContextDataHolders = SaasThreadContextUtil.save();
    CompletableFuture<Long> fusionAlertCountFuture = CompletableFuture.supplyAsync(() -> {
        try {
            // 調(diào)用 SaasThreadContextUtil.load(threadContextDataHolders);
            SaasThreadContextUtil.load(threadContextDataHolders);
            SearchRequest searchRequest = new SearchRequest();
            searchRequest.indices(SaasEsFactory.getTenantIndex(DatabaseConstants.ALERT));
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchRequest.source(searchSourceBuilder);
            searchSourceBuilder.trackTotalHits(true);
            searchSourceBuilder.query(createBoolQueryBuilder(timeRange));
            return getSearchResponseTotalHits(fusionAlertQo, searchRequest);
        } finally {
            // 調(diào)用 SaasThreadContextUtil.remove();
            SaasThreadContextUtil.remove();
        }
    }, THREAD_POOL_EXECUTOR);
   CompletableFuture<Long> multiSourceAssociateAlertCountFuture = CompletableFuture.supplyAsync(() -> {
        try {
            // 調(diào)用 SaasThreadContextUtil.load(threadContextDataHolders);
            SaasThreadContextUtil.load(threadContextDataHolders);
            SearchRequest searchRequest = new SearchRequest();
            searchRequest.indices(SaasEsFactory.getTenantIndex(DatabaseConstants.ALERT));
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchRequest.source(searchSourceBuilder);
            searchSourceBuilder.trackTotalHits(true);
            BoolQueryBuilder boolQueryBuilder = createBoolQueryBuilder(timeRange);
            searchSourceBuilder.query(boolQueryBuilder);
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("fusionAlert", true);
            boolQueryBuilder.must(termQueryBuilder);
            return getSearchResponseTotalHits(fusionAlertQo, searchRequest);
        } finally {
            // 調(diào)用 SaasThreadContextUtil.remove();
            SaasThreadContextUtil.remove();
        }
    }, THREAD_POOL_EXECUT
    // ...                                                                                            
}

到此這篇關于利用ThreadLocal上下文解決查詢性能問題的文章就介紹到這了,更多相關ThreadLocal查詢性能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • springboot項目中出現(xiàn)同名bean異常報錯的解決方法

    springboot項目中出現(xiàn)同名bean異常報錯的解決方法

    這篇文章給大家聊聊springboot項目出現(xiàn)同名bean異常報錯如何修復,文中通過代碼示例給大家介紹解決方法非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-01-01
  • java api返回值的標準化詳解

    java api返回值的標準化詳解

    這篇文章主要介紹了java api返回值的標準化詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • Spring如何根據(jù)條件創(chuàng)建bean,@Conditional注解使用方式

    Spring如何根據(jù)條件創(chuàng)建bean,@Conditional注解使用方式

    這篇文章主要介紹了Spring如何根據(jù)條件創(chuàng)建bean,@Conditional注解使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • spring Boot打包部署到遠程服務器的tomcat中

    spring Boot打包部署到遠程服務器的tomcat中

    這篇文章主要給大家介紹了關于spring Boot打包部署到遠程服務器的tomcat中的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2017-12-12
  • Java與前端交互出現(xiàn)跨域問題的14種解決方案

    Java與前端交互出現(xiàn)跨域問題的14種解決方案

    跨域問題是前端與后端分離開發(fā)中的常見挑戰(zhàn),這篇文章為大家整理了14個常見的解決方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-04-04
  • mybatis-plus指定字段模糊查詢的實現(xiàn)方法

    mybatis-plus指定字段模糊查詢的實現(xiàn)方法

    最近項目中使用springboot+mybatis-plus來實現(xiàn),所以下面這篇文章主要給大家介紹了關于mybatis-plus實現(xiàn)指定字段模糊查詢的相關資料,需要的朋友可以參考下
    2022-04-04
  • 淺談java如何實現(xiàn)Redis的LRU緩存機制

    淺談java如何實現(xiàn)Redis的LRU緩存機制

    今天給大家?guī)淼氖顷P于Java的相關知識,文章圍繞著java如何實現(xiàn)Redis的LRU緩存機制展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Java中Stream流的常用方法代碼示例

    Java中Stream流的常用方法代碼示例

    這篇文章主要介紹了Java中Stream流的常用方法代碼示例,Stream類中每一個方法都對應集合上的一種操作,將真正的函數(shù)式編程引入到Java中,能 讓代碼更加簡潔,極大地簡化了集合的處理操作,提高了開發(fā)的效率和生產(chǎn)力,需要的朋友可以參考下
    2023-10-10
  • Java的LinkedHashSet解析

    Java的LinkedHashSet解析

    這篇文章主要介紹了Java的LinkedHashSet解析,Set接口的哈希表和鏈表實現(xiàn),具有可預測的迭代順序,此實現(xiàn)與 HashSet的不同之處在于它維護一個雙向鏈表,該列表貫穿其所有條目,這個鏈表定義了迭代順序,需要的朋友可以參考下
    2023-09-09
  • idea如何關閉頁面顯示的瀏覽器圖標

    idea如何關閉頁面顯示的瀏覽器圖標

    這篇文章主要介紹了idea如何關閉頁面顯示的瀏覽器圖標問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07

最新評論