当前位置: 首页 > news >正文

实战 - 利用 ThreadLocal 线程局部变量实现数据缓存

文章目录

    • 1. 利用 ThreadLocal 缓存 AssetBranchCache 数据
      • 1. 定义 AssetBranchCache 类
      • 2. 定义 BranchContext 类操作 AssetBranchCache 对象
      • 3. 配置拦截器实时更新和清除缓存数据
      • 4. 定义 SaasThreadContextDataHolderBranch 类持有 AssetBranchCache 对象
      • 5. 定义 SaasThreadContextHolder 接口
      • 6. 定义 SaasThreadContextHolderBranch 组件
      • 7. 定义 SaasThreadContextUtil 工具类
      • 8. 定义 BranchNameCache 组件获取 AssetBranchCache 数据
    • 2. 业务使用
    • 1. 请求入口 IncidentController
      • 1. 请求参数的封装
      • 2. 响应实体的封装
    • 2. 获取延迟加载数据 IEventDelayLoadService

业务逻辑:安全事件,安全告警,风险主机列表页面会需要一些延迟加载数据,因此当我们进入这个页面时就会请求该接口获取延迟加载数据。

1. 利用 ThreadLocal 缓存 AssetBranchCache 数据

ThreadLocal 是 Java 中的一个类,它提供了一种线程局部变量的机制。线程局部变量是指只能被同一个线程访问和修改的变量,不同线程之间互不干扰。ThreadLocal 可以用来解决多线程并发访问共享变量的问题。

ThreadLocal可以用来实现数据缓存,即在一个线程中缓存一些上下文相关的数据,以便在该线程的后续操作中使用。在使用ThreadLocal实现上下文缓存时,可以将需要缓存的数据存储在ThreadLocal对象中,然后在需要使用这些数据的地方,通过ThreadLocal对象获取数据。由于每个线程都有自己的ThreadLocal对象,因此不同线程之间的数据不会相互干扰,从而保证了线程安全性。

1. 定义 AssetBranchCache 类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class AssetBranchCache {private UnmodifiableMap<Integer, String> idToNameMap;
}

2. 定义 BranchContext 类操作 AssetBranchCache 对象

BranchContext 类定义了一个静态的 ThreadLocal 变量 BRANCH_CACHE_THREAD_LOCAL,用于存储当前线程的 AssetBranchCache 对象:

public class BranchContext {private static final ThreadLocal<@Nullable AssetBranchCache> BRANCH_CACHE_THREAD_LOCAL = new TransmittableThreadLocal<>();/*** 设置 assetBranchCache*/public static void load(@Nullable AssetBranchCache assetBranchCache) {BRANCH_CACHE_THREAD_LOCAL.set(assetBranchCache);}/*** 获取 assetBranchCache*/@Nullablepublic static AssetBranchCache save() {return BRANCH_CACHE_THREAD_LOCAL.get();}/*** 清除 assetBranchCache 信息*/public static void remove() {BRANCH_CACHE_THREAD_LOCAL.remove();}
}

3. 配置拦截器实时更新和清除缓存数据

在使用ThreadLocal时需要注意内存泄漏问题,因为ThreadLocal对象是存储在每个线程的ThreadLocalMap中的,如果不及时清除ThreadLocal对象,可能会导致内存泄漏。

@Data
@Configuration
@CustomLog
public class IncidentInterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors( @NotNull InterceptorRegistry registry) {registry.addInterceptor(new HandlerInterceptorAdapter() {// preHandle方法:在请求处理前会执行@Overridepublic boolean preHandle( @NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {BranchContext.remove();return true;}// afterCompletion方法:在请求处理后会执行@Overridepublic void afterCompletion(@NotNull HttpServletRequest request,@NotNull HttpServletResponse response, @NotNull Object handler,@Nullable Exception ex) throws Exception {BranchContext.remove();}});}
}

该类实现WebMvcConfigurer接口,用于配置拦截器。在addInterceptors方法中,注册了一个HandlerInterceptorAdapter类型的拦截器,该拦截器在请求处理前和请求处理后都会执行。在preHandle方法中,调用了BranchContext类的remove方法,用于清除ThreadLocal 缓存的数据。在afterCompletion方法中也调用了BranchContext类的remove方法,确保在请求处理完成后也清除当前线程的ThreadLocal 缓存的数据。

使用拦截器可以保证ThreadLocal中缓存的数据是实时更新的,每次请求进来都会清除缓存中的数据,重新加载。

4. 定义 SaasThreadContextDataHolderBranch 类持有 AssetBranchCache 对象

为了在项目的各个地方获取ThreadLocal中缓存的资产组数据,可以定义一个线程上下文资产组数据持有者类 SaasThreadContextDataHolderBranch

public interface SaasThreadContextDataHolder {}
@Data
@AllArgsConstructor
public class SaasThreadContextDataHolderBranch implements SaasThreadContextDataHolder {@Nullableprivate final AssetBranchCache assetBranchCache;
}

5. 定义 SaasThreadContextHolder 接口

SaasThreadContextHolder 接口中定义了一些方法来管理 SaasThreadContextDataHolder 对象

public interface SaasThreadContextHolder<T extends SaasThreadContextDataHolder> {// 获取 SaasThreadContextDataHolder 类@NotNullClass<T> getSaasThreadContextDataHolderClass();// 尝试加载SaasThreadContextDataHolderdefault boolean tryLoad(@NotNull SaasThreadContextDataHolder holder) {if (!getSaasThreadContextDataHolderClass().isInstance(holder)) {return false;}this.load((T) holder);return true;}// 加载 SaasThreadContextDataHoldervoid load(@NotNull T holder);// 存储 SaasThreadContextDataHolder@NotNullT save();// 清理 SaasThreadContextDataHoldervoid remove();
}

6. 定义 SaasThreadContextHolderBranch 组件

// 应用程序中自动发现和注册该组件
@AutoService(SaasThreadContextHolder.class)
public class SaasThreadContextHolderBranch implements SaasThreadContextHolder<SaasThreadContextDataHolderBranch> {@NotNull@Overridepublic Class<SaasThreadContextDataHolderBranch> getSaasThreadContextDataHolderClass() {return SaasThreadContextDataHolderBranch.class;}// 加载 SaasThreadContextDataHolderBranch 设置 ThreadLocal 缓存数据@Overridepublic void load(@NotNull SaasThreadContextDataHolderBranch holder) {AssetBranchCache assetBranchCache = holder.getAssetBranchCache();if (assetBranchCache != null) {BranchContext.load(assetBranchCache);}}// 获取 ThreadLocal 缓存数据填充 SaasThreadContextDataHolderBranch @NotNull@Overridepublic SaasThreadContextDataHolderBranch save() {return new SaasThreadContextDataHolderBranch(BranchContext.save());}// 清除 ThreadLocal 缓存数据@Overridepublic void remove() {BranchContext.remove();}
}

7. 定义 SaasThreadContextUtil 工具类

public class SaasThreadContextUtil {// 获取Spring容器中所有实现了SaasThreadContextHolder接口的组件@NotNullstatic List<SaasThreadContextHolder<?>> getSaasThreadContextHolders() {return (List) IterableUtils.toList(ServiceLoader.load(SaasThreadContextHolder.class));}@NotNullpublic 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();}}
}

8. 定义 BranchNameCache 组件获取 AssetBranchCache 数据

@Component
@CustomLog
public class BranchNameCache {@Getter@Setter(onMethod_ = @Autowired)private IBranchService branchService;@Setter(onMethod_ = @Autowired)private ApplicationContext applicationContext;// 查询缓存数据 AssetBranchCachepublic AssetBranchCache getAssetBranchCache() {// 从ThreadLocal对象中获取缓存数据,如果不为null,直接返回AssetBranchCache result = BranchContext.save();if (result != null) {return result;}// 查询缓存数据BranchNameCache bean = applicationContext.getBean(BranchNameCache.class);String cacheKey = String.format(RedisKey.ASSET_BRANCH, Objects.requireNonNull(TenantInfoContext.getTenantInfo()).getTenantId())+ CacheUtil.getCacheUserRegion();result = bean.getAssetBranchInfo(cacheKey);// 将缓存数据设置到ThreadLocal对象中BranchContext.load(result);return result;}// Spring Cache 注解@Cacheable(value = "assetBranchCache", key = "#cacheKey")public AssetBranchCache getAssetBranchInfo(String cacheKey) {List<Branch> branches = this.getBranchService().listAll();Map<Integer, String> idToNameMap = new HashMap<>(branches.size());branches.forEach(branch -> {idToNameMap.put(branch.getId().intValue(), branch.getName());});return new AssetBranchCache( (UnmodifiableMap<Integer, String>) UnmodifiableMap.unmodifiableMap(idToNameMap));}public Map<Integer, String> getBranchIdToNameMap() {return this.getAssetBranchCache().getIdToNameMap();}
}

① getAssetBranchCache方法:获取 AssetBranchCache数据,先从ThreadLocal对象中查询,如果查询结果不为null,直接返回,否则调用资产服务查询,最后将查询结果设置到ThreadLocal对象中。

② @Cacheable注解:Spring框架的注解,用于缓存方法的返回值。具体来说,@Cacheable注解表示该方法的返回值应该被缓存,value属性指定了缓存的名称,key属性指定了缓存的键值,即用于查找缓存的唯一标识符。在这个例子中,缓存的名称为"assetBranchCache",缓存的键值为cacheKeycacheKey是一个方法参数或者是一个表达式,用于生成缓存的键值。如果缓存中已经存在相同的键值,则直接返回缓存中的值,否则执行方法并将返回值存入缓存中。

2. 业务使用

1. 请求入口 IncidentController

@Api("安全事件信息")
@CustomLog
@Validated
@ResponseResult
@RestController
@RequestMapping("/api/v1/incidents")
public class IncidentController {@CheckValidateAble@PreAuthorize("hasAnyAuthority('superAdmin','incidentQuery')")@PostMapping("/delayloaddata")@ApiOperation("安全事件额外数据延迟加载")@OperateLog(handle = { LoggerEnum.operation }, target = "operate.incident.log", action = "operate.incident.delayloaddata.log")public IncidentDelayLoadData delayLoadData(@RequestBody @Validated IncidentDelayLoadDataQo qo) {Map<String, IncidentDelayLoadData.EachIncidentDelayLoadData> incidentMap = delayLoadService.richInfo(qo);return IncidentDelayLoadData.builder().incidentMap(incidentMap).build();}
}

1. 请求参数的封装

@Data
@Validated
@Builder
@ApiModel(description = "事件延迟加载数据")
@NoArgsConstructor
@AllArgsConstructor
public class IncidentDelayLoadDataQo {@ApiModelProperty(value = "数据类型", example = "INCIDENT")RiskTypeEnum riskType = RiskTypeEnum.INCIDENT;@Valid@Size(max = 1000, message = "延迟加载的数据最大1000条")@NotNullprivate List<SecurityEntry> data;
}

① 风险类型:安全事件,安全告警,风险资产

public enum RiskTypeEnum {/*** 安全事件*/INCIDENT,/*** 安全告警*/ALERT,MOCK,/*** 风险资产*/RISK_ASSET;
}

② 事件延迟加载数据:

@Data
@Builder
@ApiModel(description = "事件延迟加载数据")
@NoArgsConstructor
@AllArgsConstructor
public class SecurityEntry {// 安全事件id、安全告警id、风险主机id@NotBlank@Pattern(regexp = "[^\"'&<>()+%\\\\]+")private String uuId;@Nullableprivate Long assetId;@Nullableprivate Long lastTime;@Nullableprivate List<@NotBlank @Pattern(regexp = "[^\"'&<>()+%\\\\]+") String> alertIds;}

@Nullable 注解是一种用于 Java 代码中的注解,它用于标记一个方法的返回值、参数或字段可以为 null。

③ 请求参数示例:

{"data": [{"uuId": "alert-ad41da97-94b2-4091-9a6d-a6972a7ce919","assetId": 17582,"lastTime": 1690460941},{"uuId": "alert-a6a2d31f-34df-4176-bca5-1ee368e41006","assetId": 17581,"lastTime": 1690460896}],"riskType": "ALERT"
}

2. 响应实体的封装

@Data
@Builder
@ApiModel(description = "事件延迟加载数据")
@NoArgsConstructor
@AllArgsConstructor
public class IncidentDelayLoadData {@ApiModelProperty("事件延迟加载数据")private Map<String, EachIncidentDelayLoadData> incidentMap;@Data@Builder@ApiModel(description = "每条事件的延迟加载数据")@NoArgsConstructor@AllArgsConstructorpublic static class EachIncidentDelayLoadData {@ApiModelProperty("一键遏制禁用标记")private Boolean oneClickDisposeDisabled;private String oneClickDisposeStatus;private String newestUsername;private String checkOutUsername;private String responsible;private AlertSeverityNumber alertSeverityNumber;private Long remarkNumber;private Integer connectStatus;@ApiModelProperty("当前资产类别信息")private MagnitudeInfo magnitude;@ApiModelProperty("处置入口威胁根除判断")private TableCellDataVo entryDisposal;@ApiModelProperty("主机IP")private HostIpDelayVo hostIp;@ApiModelProperty("主机资产组")private HostBranchDelayVo hostBranchId;@ApiModelProperty("主机业务组")private HostGroupDelayVo hostGroupIds;@ApiModelProperty("目的IP")private SrcDstIpDelayVo dstIp;@ApiModelProperty("源IP")private SrcDstIpDelayVo srcIp;@ApiModelProperty("源IP")private AssetUserDelayVo assetUser;}
}

① 外部类 IncidentDelayLoadData:包含一个名为incidentMapMap对象,其中键为事件ID,值为EachIncidentDelayLoadData对象,表示每个事件的延迟加载数据。

@Data
@Builder
@ApiModel(description = "事件延迟加载数据")
@NoArgsConstructor
@AllArgsConstructor
public class IncidentDelayLoadData {@ApiModelProperty("事件延迟加载数据")private Map<String, EachIncidentDelayLoadData> incidentMap;// ....
}

② 静态内部类 EachIncidentDelayLoadData:包含了事件的各种属性,如一键遏制禁用标记、最新用户名、检出用户名、责任人、告警严重性等等。其中,MagnitudeInfoTableCellDataVoHostIpDelayVoHostBranchDelayVoHostGroupDelayVoSrcDstIpDelayVoAssetUserDelayVo都是其他自定义类的对象,用于表示不同的属性。

③ 静态内部类的使用:

Java中的静态内部类是指在一个类的内部定义的静态类。静态内部类与非静态内部类的区别在于,静态内部类不依赖于外部类的实例,可以直接通过外部类名访问,而非静态内部类必须依赖于外部类的实例才能访问。

静态内部类可以访问外部类的静态成员和方法,但不能访问外部类的非静态成员和方法。静态内部类也可以定义静态成员和方法,这些静态成员和方法与外部类的静态成员和方法类似,可以直接通过类名访问。

public class OuterClass {// 外部类的成员和方法public static class StaticInnerClass {// 静态内部类的成员和方法}
}

静态内部类的实例化方式如下:

OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();

④ 响应数据示例:

{"strCode": null,"message": "成功","data": {"incidentMap": {"lqlapp35test3-452799db-2557-424b-ba86-aa1f2d72eb3e": {"oneClickDisposeDisabled": true,"oneClickDisposeStatus": "WAIT_DEAL","newestUsername": null,"checkOutUsername": null,"responsible": null,"alertSeverityNumber": null,"remarkNumber": 0,"connectStatus": null,"magnitude": null,"entryDisposal": {"originalValue": true,"renderValue": null},"hostIp": {"originalValue": "6.6.6.7","renderValue": "6.6.6.7(美国)"},"hostBranchId": {"originalValue": 0,"renderValue": ""},"hostGroupIds": {"count": 0,"data": []},"dstIp": null,"srcIp": null,"assetUser": null},"16c28c0d-649a-dispose-entity111-5d419e8790129": {"oneClickDisposeDisabled": false,"oneClickDisposeStatus": "WAIT_DEAL","newestUsername": null,"checkOutUsername": null,"responsible": null,"alertSeverityNumber": null,"remarkNumber": 0,"connectStatus": null,"magnitude": null,"entryDisposal": {"originalValue": true,"renderValue": null},"hostIp": {"originalValue": "192.168.40.29","renderValue": "192.168.40.29(管理IP范围)"},"hostBranchId": {"originalValue": 1,"renderValue": ""},"hostGroupIds": {"count": 0,"data": []},"dstIp": null,"srcIp": null,"assetUser": null}}},"code": 0
}

2. 获取延迟加载数据 IEventDelayLoadService

利用线程池管理并发任务的执行。通过将任务提交到线程池中,让线程池自动分配线程来执行任务,从而实现并发执行。线程池还可以控制并发任务的数量,避免系统资源被过度占用,从而提高系统的稳定性和可靠性。

@CustomLog
@Service
public class EventDelayLoadServiceImpl implements IEventDelayLoadService, ApplicationListener<ContextStoppedEvent> {// 定义一个线程池private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(5,20,4,TimeUnit.SECONDS,new LinkedBlockingQueue<>(),new ThreadFactoryBuilder().setNameFormat(IncidentResponseDispositionServiceImpl.class.getSimpleName() + "-pool-%d").setDaemon(true).build(),new ThreadPoolExecutor.DiscardOldestPolicy());// 监听应用程序上下文停止事件,用于关闭线程资源@Overridepublic void onApplicationEvent(@NotNull ContextStoppedEvent ignored) {try {THREAD_POOL_EXECUTOR.shutdown();} catch (Exception e) {log.error("停止线程池失败", e);}}@Overridepublic Map<String, IncidentDelayLoadData.EachIncidentDelayLoadData> richInfo(IncidentDelayLoadDataQo delayLoadDataQo) {List<SecurityEntry> securityEntryList = delayLoadDataQo.getData();if (CollectionUtils.isEmpty(securityEntryList)) {return Collections.emptyMap();}RiskTypeEnum riskType = delayLoadDataQo.getRiskType();ConcurrentHashMap<String, IncidentDelayLoadData.EachIncidentDelayLoadData> data = new ConcurrentHashMap<>(securityEntryList.size());securityEntryList.forEach(entry -> data.put(entry.getUuId(), new IncidentDelayLoadData.EachIncidentDelayLoadData()));List<Callable<Void>> callables;// 根据riskType的不同,调用不同的方法获取Callable列表callablesswitch (riskType) {case INCIDENT:callables = getIncidentCallableList(securityEntryList, data);break;       case ALERT:callables = getAlertCallableList(securityEntryList, data);break;       case RISK_ASSET:callables = getRiskAssetCallableList(securityEntryList, data);break;default:return Collections.emptyMap();}// 通过线程池执行callables中的任务,并将结果存储在ConcurrentHashMap类型的数据data中try {List<Future<Void>> futures = THREAD_POOL_EXECUTOR.invokeAll(callables, 1, TimeUnit.MINUTES);for (Future<Void> future : futures) {try {future.get();} catch (Exception e) {log.warn("incident delay data,future get warn", e);}}} catch (Exception e) {log.warn("incident delay data warn ", e);}return data;}
}

以获取安全告警页面延迟加载数据的线程执行任务为例:getAlertCallableList

private List<Callable<Void>> getAlertCallableList(List<SecurityEntry> securityEntryList, ConcurrentHashMap<String, IncidentDelayLoadData.EachIncidentDelayLoadData> data) {HashMap<@NotNull String, @Nullable Long> uuIdToAssetId = new HashMap<>(securityEntryList.size());for (SecurityEntry securityEntry : securityEntryList) {uuIdToAssetId.put(securityEntry.getUuId(), securityEntry.getAssetId());}try {List<Alert> alerts = alertDao.getAlerts(securityEntryList.stream().map(SecurityEntry::getUuId).collect(Collectors.toList()));if (CollectionUtils.isEmpty(alerts)) {log.warn("no alert data to deal");data.clear();return List.of();}// 尝试加载ThreadLocal对象中的缓存数据List<SaasThreadContextDataHolder> contextDataHolders = SaasThreadContextUtil.save();Callable<Void> assetCallable = getAssetCallable(securityEntryList, data, uuIdToAssetId, contextDataHolders);Callable<Void> assetUserCallable = getAssetUserCallable(securityEntryList, data, uuIdToAssetId, contextDataHolders);Callable<Void> assetBranchCallable = getAlertAssetBranchCallable(securityEntryList, alerts, data, contextDataHolders);Callable<Void> remarkNumberCallable = getRemarkNumberCallable(securityEntryList, ALERT, data, contextDataHolders);return Arrays.asList(assetCallable,assetUserCallable,assetBranchCallable,remarkNumberCallable);} catch (IOException | JsonSerializeException e) {throw new IncidentRuntimeException(I18nUtils.i18n(I18nConstant.AlertOperateConstant.EXCEPTION_TO_BE_TRANSFERRED), e);}
}

在该方法中定义了一个获取安全告警资产组信息线程体,继续看下 getAlertAssetBranchCallable 方法:

@CustomLog
@Service
public class EventDelayLoadServiceImpl implements IEventDelayLoadService, ApplicationListener<ContextStoppedEvent> {@Setter(onMethod_ = { @Autowired })private BranchNameCache branchNameCache; private Callable<Void> getAssetBranchCallable(List<SecurityEntry> securityEntryList, List<Incident> incidentList, ConcurrentHashMap<String, IncidentDelayLoadData.EachIncidentDelayLoadData> data,List<SaasThreadContextDataHolder> contextDataHolders) {return () -> {try {// 设置ThreadLocal对象中的缓存数据SaasThreadContextUtil.load(contextDataHolders);// BranchNameCache 获取 AssetBranchCache 数据Map<Integer, String> branchIdToNameMap = branchNameCache.getBranchIdToNameMap();for (SecurityEntry securityEntry : securityEntryList) {//设置响应数据eachIncidentDelayLoadData.setHostBranchId(new HostBranchDelayVo(branchId, HostIpUtils.getHostBranch(assetId, branchId, branchIdToFullNameMap)));}return null;} finally {// 清除ThreadLocal对象中的缓存数据SaasThreadContextUtil.remove();}};}
}

相关文章:

实战 - 利用 ThreadLocal 线程局部变量实现数据缓存

文章目录 1. 利用 ThreadLocal 缓存 AssetBranchCache 数据1. 定义 AssetBranchCache 类2. 定义 BranchContext 类操作 AssetBranchCache 对象3. 配置拦截器实时更新和清除缓存数据4. 定义 SaasThreadContextDataHolderBranch 类持有 AssetBranchCache 对象5. 定义 SaasThreadC…...

wxwidgets Ribbon使用简单实例

// RibbonSample.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <wx/wx.h> #include "wx/wxprec.h" #include "wx/app.h" #include "wx/frame.h" #include "wx/textctrl.h" #include "…...

2023年第四届“华数杯”数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; 最短时间生产计划模型 该模型出现在好几个竞赛赛题上&#x…...

LeetCode404. 左叶子之和

404. 左叶子之和 文章目录 [404. 左叶子之和](https://leetcode.cn/problems/sum-of-left-leaves/)一、题目二、题解方法一&#xff1a;递归方法二&#xff1a;迭代 一、题目 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9…...

Nginx 高性能内存池 ----【学习笔记】

跟着这篇文章学习&#xff1a; c代码实现一个高性能内存池&#xff08;超详细版本&#xff09;_c 内存池库_linux大本营的博客-CSDN博客https://blog.csdn.net/qq_40989769/article/details/130874660以及这个视频学习&#xff1a; nginx的内存池_哔哩哔哩_bilibilihttps://w…...

iOS--frame和bounds

坐标系 首先&#xff0c;我们来看一下iOS特有的坐标系&#xff0c;在iOS坐标系中以左上角为坐标原点&#xff0c;往右为X正方向&#xff0c;往下是Y正方向如下图&#xff1a; bounds和frame都是属于CGRect类型的结构体&#xff0c;系统的定义如下&#xff0c;包含一个CGPoint…...

docker logs 使用说明

docker logs 可以查看某个容器内的日志情况。 前置参数说明 c_name容器名称 / 容器ID logs 获取容器的日志 , 命令如下&#xff1a; docker logs [options] c_name option参数&#xff1a; -n 查看最近多少条记录&#xff1a;docker logs -n 5 c_name--tail与-n 一样 &#…...

Ceph入门到精通-Ceph PG状态详细介绍(全)

本文主要介绍PG的各个状态&#xff0c;以及ceph故障过程中PG状态的转变。 Placement Group States&#xff08;PG状态&#xff09; creating Ceph is still creating the placement group. Ceph 仍在创建PG。activating The placement group is peered but not yet active.…...

【数据结构】二叉树、二叉搜索树、平衡二叉树、红黑树、B树、B+树

概述 二叉树&#xff08;Binary Tree&#xff09;&#xff1a;每个节点最多有两个子节点&#xff08;左子节点和右子节点&#xff09;&#xff0c;没有限制节点的顺序。特点是简单直观&#xff0c;易于实现&#xff0c;但查找效率较低。 二叉搜索树&#xff08;Binary Search…...

【JVM】(二)深入理解Java类加载机制与双亲委派模型

文章目录 前言一、类加载过程1.1 加载&#xff08;Loading&#xff09;1.2 验证&#xff08;Verification&#xff09;1.3 准备&#xff08;Preparation&#xff09;1.4 解析&#xff08;Resolution&#xff09;1.5 初始化&#xff08;Initialization&#xff09; 二、双亲委派…...

npm i 报错项目启动不了解决方法

1.场景 在另一台电脑低版本node环境跑的react项目&#xff0c;换到另一台电脑node18环境执行npm i时候报错 2.解决方法 脚本前加上set NODE_OPTIONS--openssl-legacy-provider...

【从零开始学习JAVA | 第三十七篇】初识多线程

目录 前言&#xff1a; ​编辑 引入&#xff1a; 多线程&#xff1a; 什么是多线程&#xff1a; 多线程的意义&#xff1a; 多线程的应用场景&#xff1a; 总结&#xff1a; 前言&#xff1a; 本章节我们将开始学习多线程&#xff0c;多线程是一个很重要的知识点&#xff…...

微信新功能,你都知道吗?

近日iOS 微信8.0.40正式版来了&#xff0c;一起来看看有哪些变化&#xff1f; 1、朋友圈置顶 几个月前微信开始内测「朋友圈置顶」功能&#xff0c;从网友们的反馈来看&#xff0c;iOS 微信 8.0.40 似乎扩大了内测范围&#xff0c;更多用户可以体验到该功能了。 大家可以去自己…...

Android 中 app freezer 原理详解(二):S 版本

基于版本&#xff1a;Android S 0. 前言 在之前的两篇博文《Android 中app内存回收优化(一)》和 《Android 中app内存回收优化(二)》中详细剖析了 Android 中 app 内存优化的流程。这个机制的管理通过 CachedAppOptimizer 类管理&#xff0c;为什么叫这个名字&#xff0c;而不…...

Vue3_04_ref 函数和 reactive 函数

ref 函数 声明变量时&#xff0c;赋值的值要写在 ref() 函数中修改变量时&#xff0c;变量名.value xxx在模板中使用时可以省略掉 .value&#xff0c;直接使用变量名即可 <template><h1>一个人的信息</h1><h2>姓名&#xff1a;{{name}}</h2><…...

05 Ubuntu下安装.deb安装包方式安装vscode,snap安装Jetbrains产品等常用软件

使用deb包安装类型 deb包指的其实就是debian系统&#xff0c;ubuntu系统是基于debian系统的发行版。 一般我们会到需要的软件官网下载deb安装包&#xff0c;然后你既可以采用使用“软件安装”打开的方法来进行安装&#xff0c;也可以使用命令行进行安装。我推荐后者&#xff…...

性能测试jmeter连接数据库jdbc(sql server举例)

一、下载第三方工具包驱动数据库 1. 因为JMeter本身没有提供链接数据库的功能&#xff0c;所以我们需要借助第三方的工具包来实现。 &#xff08;有这个jar包之后&#xff0c;jmeter可以发起jdbc请求&#xff0c;没有这个jar包&#xff0c;也有jdbc取样器&#xff0c;但不能发起…...

8.3 C高级 Shell脚本

写一个脚本&#xff0c;包含以下内容&#xff1a; 显示/etc/group文件中第五行的内容创建目录/home/ubuntu/copy切换工作路径到此目录赋值/etc/shadow到此目录&#xff0c;并重命名为test将当前目录中test的所属用户改为root将test中其他用户的权限改为没有任何权限 #!/bin/b…...

2023年华数杯A题

A 题 隔热材料的结构优化控制研究 新型隔热材料 A 具有优良的隔热特性&#xff0c;在航天、军工、石化、建筑、交通等 高科技领域中有着广泛的应用。 目前&#xff0c;由单根隔热材料 A 纤维编织成的织物&#xff0c;其热导率可以直接测出&#xff1b;但是 单根隔热材料 A 纤维…...

【零基础学Rust | 基础系列 | 函数,语句和表达式】函数的定义,使用和特性

文章标题 简介一&#xff0c;函数1&#xff0c;函数的定义2&#xff0c;函数的调用3&#xff0c;函数的参数4&#xff0c;函数的返回值 二&#xff0c;语句和表达式1&#xff0c;语句2&#xff0c;表达式 总结&#xff1a; 简介 在Rust编程中&#xff0c;函数&#xff0c;语句…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...