ABP - 缓存模块(1)
ABP - 缓存模块(1)
- 1. 与 .NET Core 缓存的关系和差异
- 2. Abp 缓存的使用
- 2.1 常规使用
- 2.2 非字符串类型的 Key
- 2.3 批量操作
- 3. 额外功能
1. 与 .NET Core 缓存的关系和差异
ABP 框架中的缓存系统核心包是 Volo.Abp.Caching ,而对于分布式缓存的支持,abp 官方提供了基于 Redis 的方案,需要安装 Volo.Abp.Caching.StackExchangeRedis 集成包。默认的情况下,在我们使用 ABP CLI 创建 ABP 框架模板项目的时候已经集成了这个包,我们不需要手动进行安装。
ABP 框架中的缓存系统在 ASP.NET Core的分布式缓存系统上进行扩展,从而使分布式缓存使用起来更加方便。理所当然的,ABP 框架的缓存系统兼容 ASP.NET Core 原生的分布式缓存使用方式,关于 ASP.NET Core 分布式缓存的使用,可以参考我之前的文章 ASP.NET Core - 缓存之分布式缓存,也可以看下官方文档 ASP.NET Core 中的分布式缓存 。
那么 ABP 框架中的缓存系统扩展了什么呢?ASP.NET Core 的分布式缓存又有哪些不便之处呢 ?ASP.NET Core 通过 IDistributedCache 抽象了分布式缓存的存取操作,但是有这样两个问题:
-
它对缓存值的存取是基于 byte 数组的,而不是对象。
这使得我们在使用分布式缓存的时候需要先将实例对象进行序列化/反序列化,之后再转码为 byte 数组。如果每个使用分布式缓存的地方都这么做将会有很多的重复冗余的代码,这是需要抽取封装的。
-
为了能实现多个应用公用缓存,达成分布式缓存的作用,它将所有的缓存项存放在同一个 Key 池之中。
这就可能会有问题,我们要特别注意缓存键的设置,如果开发人员不注意,很可能将一些缓存相互覆盖了,特别是共用缓存的应用比较多,或者多租户的情况下,这造成的问题可能很严重,而且很难排查。
ABP 框架的缓存系统扩展了通用的泛型接口 IDistributedCache<TCacheItem>
,泛型类型就是缓存值的类型,用于解决上面提到的问题:
-
该接口内部实现了对缓存对象 序列化/反序列化 的逻辑。 默认使用 JSON 序列化, 我们如果有需要可以替换 依赖注入 系统中 IDistributedCacheSerializer 服务的实现来覆盖默认的方式。
-
该接口会根据缓存中对象类型自动向缓存key添加 缓存名称 前缀。 默认缓存名是缓存对象的全类名(如果类名以CacheItem 结尾, 那么CacheItem 会被忽略,不应用到缓存名称上),开发人员也可以在缓存类上使用 CacheName 特性 设置缓存的名称.
-
如果是多租户应用的话,它会自动将当前的租户id添加到缓存键中, 以区分不同租户的缓存项 。 如果租户之间需要共享缓存对象, 我们可以在缓存类打上 IgnoreMultiTenancy 特性,声明当前缓存不区分租户。
-
允许为每个应用程序定义 全局缓存键前缀 , 不同的应用程序可以在共享的分布式缓存中拥有自己的隔离池.
-
它提供了 错误容忍 机制,对分布式缓存存取过程中的异常进行了处理,例如分布式缓存服务连接失败等,避免因缓存问题导致应用出错。
因为缓存本身就只是一种用于提升应用性能,提升用户体验的策略,如果因为分布式缓存的问题导致应用出错,业务逻辑无法继续执行,那就得不偿失了。缓存系统应该是,就算将其从应用中摘除,也不影响应用业务逻辑正常执行的东西,只是性能可能差了点。 -
它额外提供了 GetManyAsync 和 SetManyAsync 等方法, 支持对缓存的批量操作,可以显著提高批处理的性能。
2. Abp 缓存的使用
这里还是以控制台应用作为演示,Web 应用中使用比控制台更简单,因为 Web 应用中已经集成了缓存模块,不需要再自行引入。首先,通过以下命令生成一个 ABP 的控制台启动模板:
abp new AbpCacheSample -t console
之后,如果是使用基于内存的分布式缓存的话,只需要安装 Volo.Abp.Caching 包即可。
Install-package Volo.Abp.Caching
再之后,在项目的模块文件中添加模块依赖:
这里不需要再进行依赖关系的配置,因为在 AbpCacheModule 模块的初始化之中已经配置好了相关的内容,通过源码可以看到:
当然,你也可以更高效地在项目所在的文件夹使用以下命令,省掉对模块依赖的配置。
abp add-package Volo.Abp.Caching
如果是使用 Redis 分布式缓存的话,需要安装 Volo.Abp.Caching.StackExchangeRedis 集成包,这个包对 Microsoft.Extensions.Caching.StackExchangeRedis 进行了扩展,它简化了 Redis 缓存的配置,也是前面提到的 GetManyAsync 和SetManyAsync 等更加性能的方法的实现所在。
install-package Volo.Abp.Caching.StackExchangeRedis
同时,模块依赖改成以下这样:
同样的,模块初始化的时候也已经配置好 Redis 缓存使用的内容:
通过源码我们也可以看到,Redis 相应的配置是从配置文件中的 “Redis” 节点读取的,我们可以在appsettings.json 中添加以下内容启用 Redis 缓存:
{"Redis": {"IsEnabed": true, // 控制 Redis 分布式缓存是否启用"Configuration": "xxx.xxx.xxx.xxx:6379,password=123456" // Redis 连接字符串}
}
这里其实就是通过配置信息中的 “Redis:Configuration” 节点配置 RedisCacheOpions 选项,这个选项是微软标准的 Redis 缓存支持包中的,如果有需要,我们可以在代码中通过以下方式对该选项进行配置:
[DependsOn(typeof(AbpAutofacModule),typeof(AbpCachingStackExchangeRedisModule)
)]
public class AbpCacheSampleModule : AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){Configure<RedisCacheOptions>(option =>{// ...});}
}
2.1 常规使用
首先我们定义缓存类:
/// <summary>
/// 缓存中,会以缓存类的全类名作为建,如果类名以 CacheItem 结尾,CacheItem 会被忽略
/// </summary>
// [CacheName("DateTime")] 也可以通过 CacheName 特性设置缓存键
public class DateTimeCacheItem
{public DateTime Now { get; set; }public string Name { get; set; }
}
之后,只需要在要用到的服务中注入 IDistributedCache<CacheItem>
泛型接口即可:
public class HelloWorldService : ITransientDependency
{public const string CacheKey = nameof(HelloWorldService);private readonly IDistributedCache<DateTimeCacheItem> _distributedCache;public HelloWorldService(IDistributedCache<DateTimeCacheItem> distributedCache){_distributedCache = distributedCache;}public async Task SayHelloAsync(){// 常规的 IDisctributedCache 的同名方法,不过 ABP 框架中进行了扩展// 使其支持泛型,可以直接存取对象//var cacheValue = await _distributedCache.GetAsync(CacheKey);//if (cacheValue == null)//{// cacheValue = new DateTimeCacheItem// {// Name = CacheKey,// Now = DateTime.Now,// };// await _distributedCache.SetAsync(// CacheKey, // 缓存键,最终的键会是 缓存名称(缓存类全类名去除CacheItem,或CacheName特性设置的名称) + 这里的键// cacheValue, // 直接存取对象,而不用自己序列化/反序列化 以及转码 // new DistributedCacheEntryOptions // 一样可以通过选项设置缓存策略// {// SlidingExpiration = TimeSpan.FromMinutes(1)// });//}// ABP 框架新增的方法var cacheValue = await _distributedCache.GetOrAddAsync(CacheKey,async () =>{return await Task.FromResult(new DateTimeCacheItem(){Name = CacheKey,Now = DateTime.Now});},() => new DistributedCacheEntryOptions{SlidingExpiration= TimeSpan.FromMinutes(1)});Console.WriteLine(JsonSerializer.Serialize(cacheValue));Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));}
}
2.2 非字符串类型的 Key
除了可以使用 string 类型作为缓存键之外,我们还可以通过 IDistributedCache<CacheKey, CacheItem> 接口使用其他类型,甚至复杂类型作为缓存键,使用复杂类型作为缓存键的时候需要重写 ToString() 方法。
public class MyCacheKey{public int Id { get; set; }public string Name { get; set; }public override string ToString(){return Id + Name;}}public class HelloWorldService : ITransientDependency
{private readonly IDistributedCache<DateTimeCacheItem, int> _distributedCacheKeyInt;private readonly IDistributedCache<DateTimeCacheItem, MyCacheKey> _distiributedCacheKey;public HelloWorldService(IDistributedCache<DateTimeCacheItem, int> distributedCacheKeyInt,IDistributedCache<DateTimeCacheItem, MyCacheKey> distiributedCacheKey){_distributedCache = distributedCache;}public async Task SayHelloAsync(){_ = await _distributedCacheKeyInt.GetOrAddAsync(1,async () =>{return await Task.FromResult(new DateTimeCacheItem{Name = "1",Now = DateTime.Now});},() => new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) });_ = await _distiributedCacheKey.GetOrAddAsync(new MyCacheKey { Id = 1, Name = "MyKey" },async () =>{return await Task.FromResult(new DateTimeCacheItem{Name = "1",Now = DateTime.Now});},() => new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) });}
}
2.3 批量操作
批量进行缓存存取操作的方式如下:
_ = await _distributedCache.GetOrAddManyAsync(new List<string> { "Key1", "Key2" }, async keys =>
{return await Task.FromResult(keys.Select(k => new KeyValuePair<string, DateTimeCacheItem>(k, new DateTimeCacheItem { Name = k, Now = DateTime.Now })).ToList());
},
() => new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromSeconds(10) });
ABP的分布式缓存接口定义了以下批量操作方法,当你需要在一个方法中调用多次缓存操作时,这些方法可以提高性能
- SetManyAsync 和 SetMany 方法可以用来向缓存中设置多个值.
- GetManyAsync 和 GetMany 方法可以用来从缓存中获取多个值.
- GetOrAddManyAsync 和 GetOrAddMany 方法可以用来从缓存中获取并添加缺少的值.
- RefreshManyAsync 和 RefreshMany 方法可以来用重置多个值的滚动过期时间.
- RemoveManyAsync 和 RemoveMany 方法可以用来从缓存中删除多个值.
这些不是标准的ASP.NET Core缓存方法, 所以其他的分布式缓存方案可能不支持。而 ABP 通过 Volo.Abp.Caching.StackExchangeRedis 实现了 Redis 分布式缓存下的批量操作。如果采用了其他方案实现分布式缓存,而提供程序不支持的情况下,会回退到循环调用 SetAsync 和 GetAsync 方法。
以上就是 ABP 框架中缓存系统的基本使用。除此之外,ABP 框架提供了 AbpDistributedCacheOptions 选项用于配置一些缓存策略,可用属性:
- HideErrors (bool, 默认: true): 启用/禁用隐藏从缓存服务器写入/读取值时的错误.
- KeyPrefix (string, 默认: null): 如果你的缓存服务器由多个应用程序共同使用, 则可以为应用程序的缓存键设置一个前缀. 在这种情况下, 不同的应用程序不能覆盖彼此的缓存内容.
- GlobalCacheEntryOptions (DistributedCacheEntryOptions): 用于设置保存缓内容却没有指定选项时, 默认的分布式缓存选项 (例如 AbsoluteExpiration 和 SlidingExpiration). SlidingExpiration的默认值设置为20分钟.
3. 额外功能
另外,还有 ABP 框架下的缓存系统还有以下一些功能:
-
错误处理
当为你的对象设计缓存时, 通常会首先尝试从缓存中获取值. 如果在缓存中找不到该值, 则从来源查询对象. 它可能在数据库中, 或者可能需要通过HTTP调用远程服务器.
在大多数情况下, 你希望容忍缓存错误; 如果缓存服务器出现错误, 也不希望取消该操作. 相反, 你可以默默地隐藏(并记录)错误并从来源查询. 这就是ABP框架默认的功能.
ABP的分布式缓存异常处理, 默认记录并隐藏错误,有一个全局修改该功能的选项.所有的·IDistributedCache· (和 ·IDistributedCache<TCacheItem, TCacheKey>·)方法都有一个可选的参数 hideErrors, 默认值为null. 如果此参数设置为null, 则全局生效, 否则你可以选择单个方法调用时隐藏或者抛出异常.
-
工作单元级别的缓存
分布式缓存服务提供了一个有趣的功能. 假设你已经更新了数据库中某本书的价格, 然后将新价格设置到缓存中, 以便以后使用缓存的值. 如果设置缓存后出现异常, 并且更新图书价格的事务被回滚了, 该怎么办?在这种情况下, 缓存值是错误的.
IDistributedCache<…>方法提供一个可选参数, considerUow, 默认为false. 如果将其设置为true, 则你对缓存所做的更改不会应用于真正的缓存存储, 而是与当前的工作单元关联. 你将获得在同一工作单元中设置的缓存值, 但仅当前工作单元成功时更改才会生效.
-
可替换的键、值处理方式
-
IDistributedCacheSerializer
IDistributedCacheSerializer 服务用于序列化和反序列化缓存内容. 默认实现是 Utf8JsonDistributedCacheSerializer 类, 它使用 IJsonSerializer 服务将对象转换为 JSON, 反之亦然. 然后, 它使用 UTC8 编码将 JSON 字符串转换为分布式缓存接受的字节数组.
如果你想实现自己的序列化逻辑, 可以自己实现并替换此服务.
-
IDistributedCacheKeyNormalizer
默认情况下, IDistributedCacheKeyNormalizer 是由 DistributedCacheKeyNormalizer 类实现的. 它将缓存名称、应用程序缓存前缀和当前租户id添加到缓存键中. 如果需要更高级的键规范化, 可以自己实现并替换此服务。
-
参考文章:
ABP 官方文档 - 缓存
ABP 系列总结:
目录:ABP 系列总结
上一篇:ABP - 依赖注入(2)
下一篇:ABP - 缓存模块(2)
相关文章:
ABP - 缓存模块(1)
ABP - 缓存模块(1) 1. 与 .NET Core 缓存的关系和差异2. Abp 缓存的使用2.1 常规使用2.2 非字符串类型的 Key2.3 批量操作 3. 额外功能 1. 与 .NET Core 缓存的关系和差异 ABP 框架中的缓存系统核心包是 Volo.Abp.Caching ,而对于分布式缓存…...
【unity进阶篇】unity如何实现跨平台及unity最优最小包体打包方式(.NET、Mono和IL2CPP知识介绍)
考虑到每个人基础可能不一样,且并不是所有人都有同时做2D、3D开发的需求,所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要讲解C#的基础语法,包括变量、数据类型、运算符、…...
5-1 创建和打包AXI Interface IP
创建和打包AXI Interface IP的前流程和后流程 step 1 : 选择类型 1: 将当前的工程打包成IP 2: 将当前的BD工程打包成IP 3: 将指定的源码打包成IP 4: 创建一个新的AXI 接口IP 其中3和4是比较常用的,本次…...
【C++】如何从源代码编译红色警戒2地图编辑器
【C】如何从源代码编译红色警戒2地图编辑器 操作视频视频中的代码不需要下载三方库,已经包含三方库。 一、运行效果:二、源代码来源及编程语言:三、环境搭建:安装红警2安装VS2022下载代码,源代码其实不太多,…...
HRNet,Deep High-Resolution Representation Learning for Visual Recognition解读
论文、代码和ppt地址:HRNet。代码地址: hrnet 本文通过paper解读和代码实例以及onnx模型的分析,来说明hrnet模型。 摘要——高分辨率表征对于诸如人体姿态估计、语义分割和目标检测等对位置敏感的视觉问题至关重要。现有的最先进框架首先通过一个子网&…...
Rust Actix Web 项目实战教程 mysql redis swagger:构建用户管理系统
Rust Actix Web 项目实战教程:构建用户管理系统 项目概述 本教程将指导你使用 Rust 和 Actix Web 构建一个完整的用户管理系统,包括数据库交互、Redis 缓存和 Swagger UI 文档。 技术栈 Rust 编程语言Actix Web 框架SQLx (MySQL 数据库)Redis 缓存Uto…...
# 爬楼梯问题:常见数列的解法总结
爬楼梯问题:常见数列的解法总结 在编程中,爬楼梯问题(Climbing Stairs Problem)是一个经典的动态规划问题,常常作为入门学习动态规划和递推的重要例题。这个问题看似简单,但背后包含了多种解决方式&#x…...
速通Docker === 常用命令
目录 Docker命令 镜像操作 容器操作 基础操作 启动参数 容器内部操作 打包成指定文件 发布镜像 总结 镜像操作 容器操作 启动容器参数 容器内部操作 打包镜像 启动指定镜像的容器 发布镜像 Docker命令 启动一个nginx,并将它的首页改为自己的页面,发布…...
AWS S3 跨账户访问 Cross Account Access
进入S3对应的存储桶,上面选项选权限,存储桶策略 -- 编辑,输入对应的policy。 完全控制,包含上传删除权限,policy如下: {"Version": "2012-10-17","Statement": [{"Si…...
C#中常见的锁以及用法--18
目录 一.C#中存在的锁 二.锁的作用 三.锁的概念和定义 关于锁的完整代码示例 代码逐层剖析: 全局变量与同步变量 Lock(锁)关键字示例 Monitor(监视器锁)示例 Mutex(互斥量)示例(支持跨进程同步) SemaphoreSlim(信号量)示例 ReadWriterLockSlim(读写锁)示例 SpinLock…...
【数据分享】1929-2024年全球站点的逐年平均气温数据(Shp\Excel\无需转发)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标,其中又以气温指标最为常用!说到气温数据,最详细的气温数据是具体到气象监测站点的气温数据!本次我们为大家带来的就是具体到气象监…...
Docker部署MySQL 5.7:持久化数据的实战技巧
在生产环境中使用Docker启动MySQL 5.7时,需要考虑数据持久化、配置文件管理、安全性等多个方面。以下是一个详细的步骤指南。 1. 准备工作 (1)创建挂载目录 在宿主机上创建用于挂载的目录,以便持久化数据和配置文件。 sudo mkdi…...
二叉树和堆
树概念及结构(了解) 树的概念(看看就行) 树是一种 非线性 的数据结构,它是由 n ( n>0 )个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的树,也就是…...
Zookeeper(15)Zookeeper的ZooKeeper API包含哪些主要操作?
Zookeeper 的 ZooKeeper API 提供了一系列操作来管理 Zookeeper 的数据节点(znodes)。这些操作主要包括创建节点、删除节点、读取节点数据、设置节点数据、列出子节点、检查节点是否存在,以及注册 Watcher 等。以下是这些操作的详细介绍和代码…...
深入浅出:Go语言os包中的API使用指南
深入浅出:Go语言os包中的API使用指南 引言 Go语言以其简洁、高效和强大的生态系统著称,是现代编程中不可或缺的一部分。其中,os包作为Go标准库的一部分,提供了丰富的API来与操作系统进行交互。本文将深入探讨os包中的核心功能,并通过实际案例帮助读者更好地理解和应用这些…...
【云岚到家】-day02-客户管理-认证授权
第二章 客户管理 1.认证模块 1.1 需求分析 1.基础概念 一般情况有用户交互的项目都有认证授权功能,首先我们要搞清楚两个概念:认证和授权 认证: 就是校验用户的身份是否合法,常见的认证方式有账号密码登录、手机验证码登录等 授权:则是该用…...
vben5 admin ant design vue如何使用时间范围组件RangePicker
本文参考:https://pusdn-dev.feishu.cn/wiki/VF4hwBAUliTE6TkUPKrcBNcZn9f?fromfrom_copylink 由PUSDN整理发行,收录时请保留PUSDN。 前端组件专题 年月日时间范围表单回显RangePicker 推荐使用多个字段存储,不推荐用英文逗号拼接时间&am…...
安全策略配置实验
安全策略配置实验 1.拓扑 2.需求 2、办公区PC在工作日时间(周一至周五,早8到晚6)可以正常访问OA srver,其他时间不允许 3、办公区PC可以在任意时刻访问web server 4、生产区PC可以在任意时刻访问OA Server,但是不能访问Web server 5、特…...
Win10安装WebODM和操作全流程
效果 以下是在 Windows 10 上安装和部署 WebODM 的详细教程: 一、安装 Docker Desktop for Windows 1、访问 Docker 官方网站:https://www.docker.com/products/docker-desktop 。 2、下载 Docker Desktop for Windows 的安装程序。 3、运行安装程序: 双击下载的安装程序,…...
wireshark抓路由器上的包 抓包路由器数据
文字目录 抓包流程概述设置抓包配置选项 设置信道设置无线数据包加密信息设置MAC地址过滤器 抓取联网过程 抓包流程概述 使用Omnipeek软件分析网络数据包的流程大概可以分为以下几个步骤: 扫描路由器信息,确定抓包信道;设置连接路由器的…...
第8章:Python TDD处理货币类代码重复问题
写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…...
C#,入门教程(01)—— Visual Studio 2022 免费安装的详细图文与动画教程
通过本课程的学习,你可以掌握C#编程的重点,享受编程的乐趣。 在本课程之前,你无需具备任何C#的基础知识,只要能操作电脑即可。 不过,希望你的数学不是体育老师教的。好的程序是数理化的实现与模拟。没有较好的数学基础…...
Agent Laboratory: Using LLM Agents as Research Assistants 论文简介
加速机器学习研究的智能实验室——Agent Laboratory 1. 引言 随着人工智能技术的飞速发展,机器学习领域正以前所未有的速度推进科学发现和技术创新。然而,传统的科学研究模式往往受到时间、资源和专业知识限制,阻碍了研究者们探索新想法的能…...
cuda + cudnn安装
1.安装CUDA Toolkit 在设备管理器(此电脑–右键–属性)的显示适配器中可以查看自己的显卡型号,去下载对应的CUDA Toolkit 。或者输入以下命令查看Driver Version ,cuda Version:12.2代表12.2版本以下兼容可以进行安装 …...
Next.js 实战 (八):使用 Lodash 打包构建产生的“坑”?
前言 最近一直在折腾 Nextjs15 ,也在断断续续地写《Next.js15 实战系列》的文章,后来总感觉文章如果没有线上效果预览差点意思,所以就想着先把目前做的项目先部署上线,后续再慢慢添加新功能。 因为之前没有部署过 Nextjs15 工程…...
owasp SQL 注入-03 (原理)
1: 先看一下注入界面: 点submit 后,可以看到有语法报错,说明已经起作用了: 报如下的错误: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near at line 1 2:…...
wireshark工具简介
目录 1 wireshark介绍 2 wireshark抓包流程 2.1 选择网卡 2.2 停止抓包 2.3 保存数据 3 wireshark过滤器设置 3.1 显示过滤器的设置 3.2 抓包过滤器 4 wireshark的封包列表与封包详情 4.1 封包列表 4.2 封包详情 参考文献 1 wireshark介绍 wireshark是非常流行的网络…...
队列的基本用法
以下是关于 C 语言中队列的详细知识,包括队列的生成、相关函数使用以及其他重要概念: 一、队列的概念 队列是一种线性数据结构,它遵循先进先出(First In First Out,FIFO)的原则,就像日常生活中…...
OpenHarmony-7.IDL工具
IDL 工具 1.openharmony IDL工具 在OpenHarmony中,当应用/系统服务的客户端和服务端进行IPC(Inter-Process Communication)跨线程通信时,需要定义双方都认可的接口,以保障双方可以成功通信,OpenHarmony ID…...
封装Redis工具类
基于StringRedisTemplate封装一个缓存工具类,满足以下需求: 方法1:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置TTL过期时间 方法2:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置TTL过期时间,用于处理缓存击穿问题 方法3:根据指定的…...
将n变为一个可以被表示为2^{a}+2^{b}的正整数m
给出一个正整数n,需要将n变为一个可以被表示为的正整数m,其中a和b都是非负整数且a!b,你可以进行两种操作: 1.令n加1 2.令n减1 请你求出最少需要多少次操作才能将n变成满足条件的m。 输入格式 输入一个整数,代表n。…...
第2章:Python TDD构建Dollar类基础
写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…...
搭建一个基于Spring Boot的校园台球厅人员与设备管理系统
搭建一个基于Spring Boot的校园台球厅人员与设备管理系统可以涵盖多个功能模块,例如用户管理、设备管理、预约管理、计费管理等。以下是一个简化的步骤指南,帮助你快速搭建一个基础的系统。 — 1. 项目初始化 使用 Spring Initializr 生成一个Spring …...
JavaScript系列(33)--微前端架构详解
JavaScript微前端架构详解 🏗️ 今天,让我们深入了解JavaScript的微前端架构,这是一种用于构建和管理大型前端应用的现代架构模式。 微前端基础概念 🌟 💡 小知识:微前端是一种将前端应用分解成更小、更易…...
第6章:Python TDD实例变量私有化探索
写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…...
Ubuntu 24.04 LTS 服务器折腾集
目录 Ubuntu 更改软件源Ubuntu 系统语言英文改中文windows 远程链接 Ubuntu 图形界面Windows 通过 openssh 连接 UbuntuUbuntu linux 文件权限Ubuntu 空闲硬盘挂载到 文件管理器的 other locationsUbuntu 开启 SMB 服务,并通过 windows 访问Ubuntu安装Tailscale&am…...
物联网在烟草行业的应用
物联网技术在烟草行业的应用 物联网技术在烟草行业的应用主要体现在以下几个方面: 智能制造 :物联网技术可以实现对生产过程中的关键参数进行实时监测,确保产品的质量稳定可靠。同时,通过对设备的远程维护和故障诊断,…...
2024年度总结:从后端Java到全栈成长的蜕变
目录 前言1. 用数据与实践书写成长篇章2. 技术与生活的双重蜕变3. 技术的进阶与生活的绽放 前言 今年是我入行的第十年,也是记录在CSDN平台上的第五年。这五年来,我始终坚持记录成长的点滴,将个人事业与博客创作紧密相连。一路走来࿰…...
详解构造函数和析构函数
⼀个类,我们不写的情况下编译器会默认⽣成以下6个默认成员函数。 下面我们详细介绍的是构造函数和析构函数,它们的主要作用分别是初始化工作和清理工作。 构造函数 1、构造函数的概念 构造函数虽名里带着“构造”但是其实际上并不是说开辟空间创建对…...
npm操作大全:从入门到精通
引言 在现代前端开发中,npm(Node Package Manager)是不可或缺的工具。无论是安装依赖、管理项目,还是发布自己的包,npm都扮演着重要的角色。本文将带你从npm的基础操作开始,逐步深入到高级用法,…...
ChatGPT 写作系列
ChatGPT 辅助写作 | 专栏 1 写作核心 先讲一下 ChatGPT 写作的核心。核心就是需要有文章大纲,而且文章大纲要足够细致。 具体怎么做呢? 提前准备多级标题大纲,刚开始有两个级别的标题就行,等用熟练了再细化。分一级标题&…...
kubernetes 集群搭建(kubeadm方式)
随着容器技术的日益普及,Kubernetes 作为最受欢迎的容器编排平台之一,已经成为现代云原生应用部署不可或缺的一部分。对于想要快速构建和管理 Kubernetes 集群的人来说,kubeadm 提供了一种简单而强大的工具。本文将详细介绍如何使用 kubeadm …...
OpenWrt 中使用 LuCI 界面部署 Docker 镜像
本篇博客将介绍如何在 OpenWrt 上使用 LuCI 部署 Docker 镜像,以 "hello-world" 镜像为例。 前提条件 已安装支持 Docker 的 OpenWrt 系统。 Docker 服务已在 OpenWrt 上成功安装并运行。 LuCI Docker 插件(luci-app-docker 或类似的管理界…...
阿里云 Serverless 助力盟主直播:高并发下的稳定性和成本优化
在直播场景中,阿里云 Serverless 应用引擎 SAE 提供的无缝弹性伸缩与极速部署能力,确保直播间高并发时的流畅体验,降低了我们的运营成本,简化了运维流程。结合阿里云云原生数据库 PolarDB 的 Serverless 能力,实现了数…...
C++ 多态 初学笔记
多态 虚函数虚函数的使用条件 虚函数详解对象多态多重继承时,类型转换的练习(1)情况1:(2)情况2:(3)情况3:(4)情况4: 对象多…...
深入剖析 Java 的 synchronized 锁升级过程
前言 在 Java 并发编程领域,synchronized关键字堪称保障线程安全的中流砥柱。随着 JDK 版本的迭代演进,synchronized锁的性能优化也在持续推进,其中锁升级机制尤为关键。本文将深度剖析synchronized锁从偏向锁、轻量级锁到重量级锁的升级历程…...
当文件补丁修改器因为文件操作权限有问题时,可以将文件拷贝到普通目录操作
文章目录 当文件补丁修改器因为文件操作权限有问题时,可以将文件拷贝到普通目录操作概述笔记直接在安装目录打补丁失败的情况将目标程序拷贝到补丁修改器的解压目录打补丁成功的情况备注END 当文件补丁修改器因为文件操作权限有问题时,可以将文件拷贝到普…...
【TCP】rfc文档
tcp协议相关rfc有哪些 TCP(传输控制协议)是一个复杂的协议,其设计和实现涉及多个RFC文档。以下是一些与TCP协议密切相关的RFC文档列表,按照时间顺序排列,涵盖了从基础定义到高级特性和优化的各个方面: 基…...
Linux探秘坊-------3.开发工具详解(1)
1 初识vim编辑器 创建第一个vim编辑的代码 1.新建文件 2.使用vim打开 3.打开默认是命令模式,写代码需要在屏幕上输出“i”字符 1.写完代码后要按Esc键退出到指令模式2.再按shift:wq即可保存并退出vim (因为不支持鼠标,通常 使用键盘上的箭…...
如何在Python中进行JSON数据的序列化和反序列化?
在Python中,JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。Python内置的json模块提供了简单易用的方法来实现数据的序列化和反序列化。下面将详细介绍如何…...