实战 - 利用 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",缓存的键值为cacheKey,cacheKey是一个方法参数或者是一个表达式,用于生成缓存的键值。如果缓存中已经存在相同的键值,则直接返回缓存中的值,否则执行方法并将返回值存入缓存中。
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:包含一个名为incidentMap的Map对象,其中键为事件ID,值为EachIncidentDelayLoadData对象,表示每个事件的延迟加载数据。
@Data
@Builder
@ApiModel(description = "事件延迟加载数据")
@NoArgsConstructor
@AllArgsConstructor
public class IncidentDelayLoadData {@ApiModelProperty("事件延迟加载数据")private Map<String, EachIncidentDelayLoadData> incidentMap;// ....
}
② 静态内部类 EachIncidentDelayLoadData:包含了事件的各种属性,如一键遏制禁用标记、最新用户名、检出用户名、责任人、告警严重性等等。其中,MagnitudeInfo、TableCellDataVo、HostIpDelayVo、HostBranchDelayVo、HostGroupDelayVo、SrcDstIpDelayVo和AssetUserDelayVo都是其他自定义类的对象,用于表示不同的属性。
③ 静态内部类的使用:
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 赛题思路 (赛题出来以后第一时间在CSDN分享) 最短时间生产计划模型 该模型出现在好几个竞赛赛题上&#x…...
LeetCode404. 左叶子之和
404. 左叶子之和 文章目录 [404. 左叶子之和](https://leetcode.cn/problems/sum-of-left-leaves/)一、题目二、题解方法一:递归方法二:迭代 一、题目 给定二叉树的根节点 root ,返回所有左叶子之和。 示例 1: 输入: root [3,9…...
Nginx 高性能内存池 ----【学习笔记】
跟着这篇文章学习: c代码实现一个高性能内存池(超详细版本)_c 内存池库_linux大本营的博客-CSDN博客https://blog.csdn.net/qq_40989769/article/details/130874660以及这个视频学习: nginx的内存池_哔哩哔哩_bilibilihttps://w…...
iOS--frame和bounds
坐标系 首先,我们来看一下iOS特有的坐标系,在iOS坐标系中以左上角为坐标原点,往右为X正方向,往下是Y正方向如下图: bounds和frame都是属于CGRect类型的结构体,系统的定义如下,包含一个CGPoint…...
docker logs 使用说明
docker logs 可以查看某个容器内的日志情况。 前置参数说明 c_name容器名称 / 容器ID logs 获取容器的日志 , 命令如下: docker logs [options] c_name option参数: -n 查看最近多少条记录:docker logs -n 5 c_name--tail与-n 一样 &#…...
Ceph入门到精通-Ceph PG状态详细介绍(全)
本文主要介绍PG的各个状态,以及ceph故障过程中PG状态的转变。 Placement Group States(PG状态) creating Ceph is still creating the placement group. Ceph 仍在创建PG。activating The placement group is peered but not yet active.…...
【数据结构】二叉树、二叉搜索树、平衡二叉树、红黑树、B树、B+树
概述 二叉树(Binary Tree):每个节点最多有两个子节点(左子节点和右子节点),没有限制节点的顺序。特点是简单直观,易于实现,但查找效率较低。 二叉搜索树(Binary Search…...
【JVM】(二)深入理解Java类加载机制与双亲委派模型
文章目录 前言一、类加载过程1.1 加载(Loading)1.2 验证(Verification)1.3 准备(Preparation)1.4 解析(Resolution)1.5 初始化(Initialization) 二、双亲委派…...
npm i 报错项目启动不了解决方法
1.场景 在另一台电脑低版本node环境跑的react项目,换到另一台电脑node18环境执行npm i时候报错 2.解决方法 脚本前加上set NODE_OPTIONS--openssl-legacy-provider...
【从零开始学习JAVA | 第三十七篇】初识多线程
目录 前言: 编辑 引入: 多线程: 什么是多线程: 多线程的意义: 多线程的应用场景: 总结: 前言: 本章节我们将开始学习多线程,多线程是一个很重要的知识点ÿ…...
微信新功能,你都知道吗?
近日iOS 微信8.0.40正式版来了,一起来看看有哪些变化? 1、朋友圈置顶 几个月前微信开始内测「朋友圈置顶」功能,从网友们的反馈来看,iOS 微信 8.0.40 似乎扩大了内测范围,更多用户可以体验到该功能了。 大家可以去自己…...
Android 中 app freezer 原理详解(二):S 版本
基于版本:Android S 0. 前言 在之前的两篇博文《Android 中app内存回收优化(一)》和 《Android 中app内存回收优化(二)》中详细剖析了 Android 中 app 内存优化的流程。这个机制的管理通过 CachedAppOptimizer 类管理,为什么叫这个名字,而不…...
Vue3_04_ref 函数和 reactive 函数
ref 函数 声明变量时,赋值的值要写在 ref() 函数中修改变量时,变量名.value xxx在模板中使用时可以省略掉 .value,直接使用变量名即可 <template><h1>一个人的信息</h1><h2>姓名:{{name}}</h2><…...
05 Ubuntu下安装.deb安装包方式安装vscode,snap安装Jetbrains产品等常用软件
使用deb包安装类型 deb包指的其实就是debian系统,ubuntu系统是基于debian系统的发行版。 一般我们会到需要的软件官网下载deb安装包,然后你既可以采用使用“软件安装”打开的方法来进行安装,也可以使用命令行进行安装。我推荐后者ÿ…...
性能测试jmeter连接数据库jdbc(sql server举例)
一、下载第三方工具包驱动数据库 1. 因为JMeter本身没有提供链接数据库的功能,所以我们需要借助第三方的工具包来实现。 (有这个jar包之后,jmeter可以发起jdbc请求,没有这个jar包,也有jdbc取样器,但不能发起…...
8.3 C高级 Shell脚本
写一个脚本,包含以下内容: 显示/etc/group文件中第五行的内容创建目录/home/ubuntu/copy切换工作路径到此目录赋值/etc/shadow到此目录,并重命名为test将当前目录中test的所属用户改为root将test中其他用户的权限改为没有任何权限 #!/bin/b…...
2023年华数杯A题
A 题 隔热材料的结构优化控制研究 新型隔热材料 A 具有优良的隔热特性,在航天、军工、石化、建筑、交通等 高科技领域中有着广泛的应用。 目前,由单根隔热材料 A 纤维编织成的织物,其热导率可以直接测出;但是 单根隔热材料 A 纤维…...
【零基础学Rust | 基础系列 | 函数,语句和表达式】函数的定义,使用和特性
文章标题 简介一,函数1,函数的定义2,函数的调用3,函数的参数4,函数的返回值 二,语句和表达式1,语句2,表达式 总结: 简介 在Rust编程中,函数,语句…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
