当前位置: 首页 > 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;语句…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器

一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下&#xff0c;音视频内容犹如璀璨繁星&#xff0c;点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频&#xff0c;到在线课堂中知识渊博的专家授课&#xff0c;再到影视平台上扣人心弦的高清大片&#xff0c;音…...

Xcode 16 集成 cocoapods 报错

基于 Xcode 16 新建工程项目&#xff0c;集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...

精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑

精益数据分析&#xff08;98/126&#xff09;&#xff1a;电商转化率优化与网站性能的底层逻辑 在电子商务领域&#xff0c;转化率与网站性能是决定商业成败的核心指标。今天&#xff0c;我们将深入解析不同类型电商平台的转化率基准&#xff0c;探讨页面加载速度对用户行为的…...

英国云服务器上安装宝塔面板(BT Panel)

在英国云服务器上安装宝塔面板&#xff08;BT Panel&#xff09; 是完全可行的&#xff0c;尤其适合需要远程管理Linux服务器、快速部署网站、数据库、FTP、SSL证书等服务的用户。宝塔面板以其可视化操作界面和强大的功能广受国内用户欢迎&#xff0c;虽然官方主要面向中国大陆…...