缓存框架 Caffeine 的可视化探索与实践

作者:vivo 互联网服务器团队-  Wang Zhi

Caffeine 作为一个高性能的缓存框架而被大量使用。本文基于Caffeine已有的基础进行定制化开发实现可视化功能。

一、背景

Caffeine缓存是一个高性能、可扩展、内存优化的 Java 缓存库,基于 Google 的 Guava Cache演进而来并提供了接近最佳的命中率。

Caffeine 缓存包含以下特点

  1. 高效快速:Caffeine 缓存使用近似算法和并发哈希表等优化技术,使得缓存的访问速度非常快。

  2. 内存友好:Caffeine 缓存使用一种内存优化策略,能够根据需要动态调整缓存的大小,有效地利用内存资源。

  3. 多种缓存策略:Caffeine 缓存支持多种缓存策略,如基于容量、时间、权重、手动移除、定时刷新等,并提供了丰富的配置选项,能够适应不同的应用场景和需求。

  4. 支持异步加载和刷新:Caffeine 缓存支持异步加载和刷新缓存项,可以与 Spring 等框架无缝集成。

  5. 清理策略:Caffeine 使用 Window TinyLFU 清理策略,它提供了接近最佳的命中率。

  6. 支持自动加载和自动过期:Caffeine 缓存可以根据配置自动加载和过期缓存项,无需手动干预。

  7. 统计功能:Caffeine 缓存提供了丰富的统计功能,如缓存命中率、缓存项数量等,方便评估缓存的性能和效果。

正是因为Caffeine具备的上述特性,Caffeine作为项目中本地缓存的不二选择,越来越多的项目集成了Caffeine的功能,进而衍生了一系列的业务视角的需求。

日常使用的需求之一希望能够实时评估Caffeine实例的内存占用情况并能够提供动态调整缓存参数的能力,但是已有的内存分析工具MAT需要基于dump的文件进行分析无法做到实时,这也是整个事情的起因之一。

二、业务的技术视角

  • 能够对项目中的Caffeine的缓存实例能够做到近实时统计,实时查看缓存的实例个数。

  • 能够对Caffeine的每个实例的缓存配置参数、内存占用、缓存命中率做到实时查看,同时能够支持单个实例的缓存过期时间,缓存条目等参数进行动态配置下发。

  • 能够对Caffeine的每个实例的缓存数据做到实时查看,并且能够支持缓存数据的立即失效等功能。

基于上述的需求背景,结合caffeine的已有功能和定制的部分源码开发,整体作为caffeine可视化的技术项目进行推进和落地。

三、可视化能力

Caffeine可视化项目目前已支持功能包括:

  • 项目维度的全局缓存实例的管控。

  • 单缓存实例配置信息可视化、内存占用可视化、命中率可视化。

  • 单缓存实例的数据查询、配置动态变更、缓存数据失效等功能。

3.1 缓存实例的全局管控

图片

说明:

  • 以应用维度+机器维度展示该应用下包含的缓存实例对象,每个实例包含缓存设置中的大小、过期策略、过期时间、内存占用、缓存命中率等信息。

  • 单实例维度的内存占用和缓存命中率支持以趋势图进行展示。

  • 单实例维度支持配置变更操作和缓存查询操作。

3.2 内存占用趋势

图片

说明:

  • 内存占用趋势记录该缓存实例对象近一段时间内存占用的趋势变化。

  • 时间周期目前支持展示近两天的数据。

3.3 命中率趋势

图片

说明:

  • 命中率趋势记录该缓存实例对象近一段时间缓存命中的变化情况。

  • 时间周期目前支持展示近两天的数据。

3.4 配置变更

图片

说明:

  • 配置变更目前支持缓存大小和过期时间的动态设置。

  • 目前暂时支持单实例的设置,后续会支持全量生效功能。

3.5 缓存查询

图片

说明:

  • 单实例维度支持缓存数据的查询。

  • 目前支持常见的缓存Key类型包括String类型、Long类型、Int类型。

四、原理实现

4.1 整体设计框架

  • Caffeine框架功能整合

图片

说明:

  • 沿用Caffeine的基础功能包括Caffeine的缓存功能和Caffeine统计功能。

  • 新增Caffeine内存占用预估功能,该功能主要是预估缓存实例对象占用的内存情况。

  • 新增Caffeine实例命名功能,该功能是针对每个实例对象提供命名功能,是全局管控的基础。

  • 新增Caffeine实例全局管控功能,该功能主要维护项目运行中所有的缓存实例。

Caffeine可视化框架

图片

说明:

  • 【项目工程侧】:Caffeine的可视化框架基于Caffeine框架功能整合的基础上增加通信层进行数据数据上报和配置的下发。

  • 【管控平台侧】:负责缓存数据上报的接收展示,配置变更命令的下发。

  • 【通信层支持push和pull两种模式】,push模式主要用于统计数据的实时上报,pull模式主要用于配置下发和缓存数据查询。

4.2 源码实现

业务层-缓存对象的管理

static Cache<String, List<String>> accountWhiteCache = Caffeine.newBuilder().expireAfterWrite(VivoConfigManager.getInteger("trade.account.white.list.cache.ttl", 10), TimeUnit.MINUTES).recordStats().maximumSize(VivoConfigManager.getInteger("trade.account.white.list.cache.size", 100)).build();
常规的Caffeine实例的创建方式static Cache<String, List<String>> accountWhiteCache = Caffeine.newBuilder().applyName("accountWhiteCache").expireAfterWrite(VivoConfigManager.getInteger("trade.account.white.list.cache.ttl", 10), TimeUnit.MINUTES).recordStats().maximumSize(VivoConfigManager.getInteger("trade.account.white.list.cache.size", 100)).build();
支持实例命名的Caffeine实例的创建方式

说明:

  • 在Caffeine实例创建的基础上增加了缓存实例的命名功能,通过.applyName("accountWhiteCache")来定义缓存实例的命名。

public final class Caffeine<K, V> {/*** caffeine的实例名称*/String instanceName;/*** caffeine的实例维护的Map信息*/static Map<String, Cache> cacheInstanceMap = new ConcurrentHashMap<>();@NonNullpublic <K1 extends K, V1 extends V> Cache<K1, V1> build() {requireWeightWithWeigher();requireNonLoadingCache();@SuppressWarnings("unchecked")Caffeine<K1, V1> self = (Caffeine<K1, V1>) this;Cache localCache =  isBounded() ? new BoundedLocalCache.BoundedLocalManualCache<>(self) : new UnboundedLocalCache.UnboundedLocalManualCache<>(self);if (null != localCache && StringUtils.isNotEmpty(localCache.getInstanceName())) {cacheInstanceMap.put(localCache.getInstanceName(), localCache);}return localCache;}
}

说明:

  • 每个Caffeine都有一个实例名称instanceName。

  • 全局通过cacheInstanceMap来维护Caffeine实例对象的名称和实例的映射关系。

  • 通过维护映射关系能够通过实例的名称查询到缓存实例对象并对缓存实例对象进行各类的操作。

  • Caffeine实例的命名功能是其他功能整合的基石。

业务层-内存占用的预估

import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;public abstract class BoundedLocalCache<K, V> extends BLCHeader.DrainStatusRef<K, V>implements LocalCache<K, V> {final ConcurrentHashMap<Object, Node<K, V>> data;@Overridepublic long getMemoryUsed() {// 预估内存占用return ObjectSizeCalculator.getObjectSize(data);}
}

说明:

  • 通过ObjectSizeCalculator.getObjectSize预估内存的缓存值。

  • data值是Caffeine实例用来保存真实数据的对象。

业务层-数据上报机制

public static StatsData getCacheStats(String instanceName) {Cache cache = Caffeine.getCacheByInstanceName(instanceName);CacheStats cacheStats = cache.stats();StatsData statsData = new StatsData();statsData.setInstanceName(instanceName);statsData.setTimeStamp(System.currentTimeMillis()/1000);statsData.setMemoryUsed(String.valueOf(cache.getMemoryUsed()));statsData.setEstimatedSize(String.valueOf(cache.estimatedSize()));statsData.setRequestCount(String.valueOf(cacheStats.requestCount()));statsData.setHitCount(String.valueOf(cacheStats.hitCount()));statsData.setHitRate(String.valueOf(cacheStats.hitRate()));statsData.setMissCount(String.valueOf(cacheStats.missCount()));statsData.setMissRate(String.valueOf(cacheStats.missRate()));statsData.setLoadCount(String.valueOf(cacheStats.loadCount()));statsData.setLoadSuccessCount(String.valueOf(cacheStats.loadSuccessCount()));statsData.setLoadFailureCount(String.valueOf(cacheStats.loadFailureCount()));statsData.setLoadFailureRate(String.valueOf(cacheStats.loadFailureRate()));Optional<Eviction> optionalEviction = cache.policy().eviction();optionalEviction.ifPresent(eviction -> statsData.setMaximumSize(String.valueOf(eviction.getMaximum())));Optional<Expiration> optionalExpiration = cache.policy().expireAfterWrite();optionalExpiration.ifPresent(expiration -> statsData.setExpireAfterWrite(String.valueOf(expiration.getExpiresAfter(TimeUnit.SECONDS))));optionalExpiration = cache.policy().expireAfterAccess();optionalExpiration.ifPresent(expiration -> statsData.setExpireAfterAccess(String.valueOf(expiration.getExpiresAfter(TimeUnit.SECONDS))));optionalExpiration = cache.policy().refreshAfterWrite();optionalExpiration.ifPresent(expiration -> statsData.setRefreshAfterWrite(String.valueOf(expiration.getExpiresAfter(TimeUnit.SECONDS))));return statsData;
}

说明:

  • 通过Caffeine自带的统计接口来统计相关数值。

  • 统计数据实例维度进行统计。

public static void sendReportData() {try {if (!VivoConfigManager.getBoolean("memory.caffeine.data.report.switch", true)) {return;}// 1、获取所有的cache实例对象Method listCacheInstanceMethod = HANDLER_MANAGER_CLASS.getMethod("listCacheInstance", null);List<String> instanceNames = (List)listCacheInstanceMethod.invoke(null, null);if (CollectionUtils.isEmpty(instanceNames)) {return;}String appName = System.getProperty("app.name");String localIp = getLocalIp();String localPort = String.valueOf(NetPortUtils.getWorkPort());ReportData reportData = new ReportData();InstanceData instanceData = new InstanceData();instanceData.setAppName(appName);instanceData.setIp(localIp);instanceData.setPort(localPort);// 2、遍历cache实例对象获取缓存监控数据Method getCacheStatsMethod = HANDLER_MANAGER_CLASS.getMethod("getCacheStats", String.class);Map<String, StatsData> statsDataMap = new HashMap<>();instanceNames.stream().forEach(instanceName -> {try {StatsData statsData = (StatsData)getCacheStatsMethod.invoke(null, instanceName);statsDataMap.put(instanceName, statsData);} catch (Exception e) {}});// 3、构建上报对象reportData.setInstanceData(instanceData);reportData.setStatsDataMap(statsDataMap);// 4、发送Http的POST请求HttpPost httpPost = new HttpPost(getReportDataUrl());httpPost.setConfig(requestConfig);StringEntity stringEntity = new StringEntity(JSON.toJSONString(reportData));stringEntity.setContentType("application/json");httpPost.setEntity(stringEntity);HttpResponse response = httpClient.execute(httpPost);String result = EntityUtils.toString(response.getEntity(),"UTF-8");EntityUtils.consume(response.getEntity());logger.info("Caffeine 数据上报成功 URL {} 参数 {} 结果 {}", getReportDataUrl(), JSON.toJSONString(reportData), result);} catch (Throwable throwable) {logger.error("Caffeine 数据上报失败 URL {} ", getReportDataUrl(), throwable);}
}

说明:

  • 通过获取项目中运行的所有Caffeine实例并依次遍历收集统计数据。

  • 通过http协议负责上报对应的统计数据,采用固定间隔周期进行上报。

业务层-配置动态下发

public static ExecutionResponse dispose(ExecutionRequest request) {ExecutionResponse executionResponse = new ExecutionResponse();executionResponse.setCmdType(CmdTypeEnum.INSTANCE_CONFIGURE.getCmd());executionResponse.setInstanceName(request.getInstanceName());String instanceName = request.getInstanceName();Cache cache = Caffeine.getCacheByInstanceName(instanceName);// 设置缓存的最大条目if (null != request.getMaximumSize() && request.getMaximumSize() > 0) {Optional<Eviction> optionalEviction = cache.policy().eviction();optionalEviction.ifPresent(eviction ->eviction.setMaximum(request.getMaximumSize()));}// 设置写后过期的过期时间if (null != request.getExpireAfterWrite() && request.getExpireAfterWrite() > 0) {Optional<Expiration> optionalExpiration = cache.policy().expireAfterWrite();optionalExpiration.ifPresent(expiration -> expiration.setExpiresAfter(request.getExpireAfterWrite(), TimeUnit.SECONDS));}// 设置访问过期的过期时间if (null != request.getExpireAfterAccess() && request.getExpireAfterAccess() > 0) {Optional<Expiration> optionalExpiration = cache.policy().expireAfterAccess();optionalExpiration.ifPresent(expiration -> expiration.setExpiresAfter(request.getExpireAfterAccess(), TimeUnit.SECONDS));}// 设置写更新的过期时间if (null != request.getRefreshAfterWrite() && request.getRefreshAfterWrite() > 0) {Optional<Expiration> optionalExpiration = cache.policy().refreshAfterWrite();optionalExpiration.ifPresent(expiration -> expiration.setExpiresAfter(request.getRefreshAfterWrite(), TimeUnit.SECONDS));}executionResponse.setCode(0);executionResponse.setMsg("success");return executionResponse;
}

说明:

  • 通过Caffeine自带接口进行缓存配置的相关设置。

业务层-缓存数据清空

/*** 失效缓存的值* @param request* @return*/public static ExecutionResponse invalidate(ExecutionRequest request) {ExecutionResponse executionResponse = new ExecutionResponse();executionResponse.setCmdType(CmdTypeEnum.INSTANCE_INVALIDATE.getCmd());executionResponse.setInstanceName(request.getInstanceName());try {// 查找对应的cache实例String instanceName = request.getInstanceName();Cache cache = Caffeine.getCacheByInstanceName(instanceName);// 处理清空指定实例的所有缓存 或 指定实例的key对应的缓存Object cacheKeyObj = request.getCacheKey();// 清除所有缓存if (Objects.isNull(cacheKeyObj)) {cache.invalidateAll();} else {// 清除指定key对应的缓存if (Objects.equals(request.getCacheKeyType(), 2)) {cache.invalidate(Long.valueOf(request.getCacheKey().toString()));} else if (Objects.equals(request.getCacheKeyType(), 3)) {cache.invalidate(Integer.valueOf(request.getCacheKey().toString()));} else {cache.invalidate(request.getCacheKey().toString());}}executionResponse.setCode(0);executionResponse.setMsg("success");} catch (Exception e) {executionResponse.setCode(-1);executionResponse.setMsg("fail");}return executionResponse;}
}

业务层-缓存数据查询

public static ExecutionResponse inspect(ExecutionRequest request) {ExecutionResponse executionResponse = new ExecutionResponse();executionResponse.setCmdType(CmdTypeEnum.INSTANCE_INSPECT.getCmd());executionResponse.setInstanceName(request.getInstanceName());String instanceName = request.getInstanceName();Cache cache = Caffeine.getCacheByInstanceName(instanceName);Object cacheValue = cache.getIfPresent(request.getCacheKey());if (Objects.equals(request.getCacheKeyType(), 2)) {cacheValue = cache.getIfPresent(Long.valueOf(request.getCacheKey().toString()));} else if (Objects.equals(request.getCacheKeyType(), 3)) {cacheValue = cache.getIfPresent(Integer.valueOf(request.getCacheKey().toString()));} else {cacheValue = cache.getIfPresent(request.getCacheKey().toString());}if (Objects.isNull(cacheValue)) {executionResponse.setData("");} else {executionResponse.setData(JSON.toJSONString(cacheValue));}return executionResponse;
}

说明:

  • 通过Caffeine自带接口进行缓存信息查询。

通信层-监听服务

public class ServerManager {private Server jetty;/*** 创建jetty对象* @throws Exception*/public ServerManager() throws Exception {int port = NetPortUtils.getAvailablePort();jetty = new Server(port);ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);context.setContextPath("/");context.addServlet(ClientServlet.class, "/caffeine");jetty.setHandler(context);}/*** 启动jetty对象* @throws Exception*/public void start() throws Exception {jetty.start();}
}public class ClientServlet extends HttpServlet {private static final Logger logger = LoggerFactory.getLogger(ClientServlet.class);@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doGet(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ExecutionResponse executionResponse = null;String requestJson = null;try {// 获取请求的相关的参数String contextPath = req.getContextPath();String servletPath = req.getServletPath();String requestUri = req.getRequestURI();requestJson = IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8);// 处理不同的命令ExecutionRequest executionRequest = JSON.parseObject(requestJson, ExecutionRequest.class);// 通过反射来来处理类依赖问题executionResponse = DisposeCenter.dispatch(executionRequest);} catch (Exception e) {logger.error("vivo-memory 处理请求异常 {} ", requestJson, e);}if (null == executionResponse) {executionResponse = new ExecutionResponse();executionResponse.setCode(-1);executionResponse.setMsg("处理异常");}// 组装相应报文resp.setContentType("application/json; charset=utf-8");PrintWriter out = resp.getWriter();out.println(JSON.toJSONString(executionResponse));out.flush();}
}

说明:

  • 通信层通过jetty启动http服务进行监听,安全考虑端口不对外开放。

  • 通过定义ClientServlet来处理相关的请求包括配置下发和缓存查询等功能。

通信层-心跳设计

/*** 发送心跳数据*/
public static void sendHeartBeatData() {try {if (!VivoConfigManager.getBoolean("memory.caffeine.heart.report.switch", true)) {return;}// 1、构建心跳数据String appName = System.getProperty("app.name");String localIp = getLocalIp();String localPort = String.valueOf(NetPortUtils.getWorkPort());HeartBeatData heartBeatData = new HeartBeatData();heartBeatData.setAppName(appName);heartBeatData.setIp(localIp);heartBeatData.setPort(localPort);heartBeatData.setTimeStamp(System.currentTimeMillis()/1000);// 2、发送Http的POST请求HttpPost httpPost = new HttpPost(getHeartBeatUrl());httpPost.setConfig(requestConfig);StringEntity stringEntity = new StringEntity(JSON.toJSONString(heartBeatData));stringEntity.setContentType("application/json");httpPost.setEntity(stringEntity);HttpResponse response = httpClient.execute(httpPost);String result = EntityUtils.toString(response.getEntity(),"UTF-8");EntityUtils.consume(response.getEntity());logger.info("Caffeine 心跳上报成功 URL {} 参数 {} 结果 {}", getHeartBeatUrl(), JSON.toJSONString(heartBeatData), result);} catch (Throwable throwable) {logger.error("Caffeine 心跳上报失败 URL {} ", getHeartBeatUrl(), throwable);}
}

说明:

  • 心跳功能上报项目实例的ip和端口用来通信,携带时间戳用来记录上报时间戳。

  • 实际项目中因为机器的回收等场景需要通过上报时间戳定时清理下线的服务。

五、总结

vivo技术团队在Caffeine的使用经验上曾有过多次分享,可参考公众号文章《如何把 Caffeine Cache 用得如丝般顺滑》,此篇文章在使用的基础上基于使用痛点进行进一步的定制。

目前Caffeine可视化的项目已经在相关核心业务场景中落地并发挥作用,整体运行平稳。使用较多的功能包括项目维度的caffeine实例的全局管控,单实例维度的内存占用评估和缓存命中趋势评估。

如通过单实例的内存占用评估功能能够合理评估缓存条目设置和内存占用之间的关系;通过分析缓存命中率的整体趋势评估缓存的参数设置合理性。

期待此篇文章能够给业界缓存使用和监控带来一些新思路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/49182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu20.04 设置静态ip

Ubuntu 从 17.10 开始&#xff0c;已放弃在 /etc/network/interfaces 里固定 IP 的配置&#xff0c;interfaces 文件不复存在&#xff0c;即使配置也不会生效&#xff0c;而是改成 netplan 方式 &#xff0c;配置写在 /etc/netplan/01-netcfg.yaml &#xff0c;50-cloud-init.y…

机器学习笔记-02-基础线性算法认识(问题-解答自查版)

前言 以下问题以Q&A形式记录&#xff0c;基本上都是笔者在初学一轮后&#xff0c;掌握不牢或者频繁忘记的点 Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系&#xff0c;也适合做查漏补缺和复盘。 本文可以让读者用作自查&#xff0c;答案在后面&#xff0…

跟《经济学人》学英文:2024年07月20日这期 At last, Wall Street has something to cheer

At last, Wall Street has something to cheer 华尔街终于有值得欢呼的事情了 at last&#xff1a;终于&#xff1b;最后&#xff1b; Consumer banks, on the other hand, are starting to suffer 原文&#xff1a; Capital markets are twitchy. When interest rates spi…

数据危机!4大硬盘数据恢复工具,教你如何正确挽回珍贵记忆!

在这个数字化的时代&#xff0c;硬盘里的数据对我们来说简直太重要了。但糟糕的是&#xff0c;数据丢失这种事时不时就会发生&#xff0c;可能是因为不小心删了&#xff0c;硬盘坏了&#xff0c;或者中了病毒。遇到这种情况&#xff0c;很多人可能就慌了&#xff0c;不知道怎么…

货架管理a

路由->vue的el标签->Api->call方法里calljs的api接口->数据声明const xxxData-> 编辑按钮:点击跳出页面并把这一行的数据给到表单formDataba2 保存按钮:formDataba2改过的数据->xxApi发送->查询Api 跳转仓库:把tableData.value数据清空->callXxxAp…

Windows环境下安装Redis并设置Redis开机自启

文章目录 0. 前言1. 下载 Windows 版本的Redis2. 为 Redis 设置连接密码&#xff08;可选&#xff09;3. 启动 Redis4. 设置 Redis 开机自启4.1 将 Redis 进程注册为服务4.2 设置 Redis 服务开机自启4.3 重启电脑测试是否配置成功4.4 关闭 Redis 开机自启&#xff08;拓展&…

Typora笔记上传到CSDN

1.Typora 安装 Typora链接&#xff1a;百度网盘 提取码&#xff1a;b6d1 旧版本是不需要破解的 后来的版本比如1.5.9把放在typora的根目录下就可以了 2.上传到CSDN 步骤 csdn 写文章-使用MD编辑器-导入本地md文件即可 问题 图片没法显示 原因 图片的链接是本地的 当然没法…

Ubuntu 24 PXE Server bios+uefi 自动化部署esxi 6 7 8

pxe server 前言 PXE(Preboot eXecution Environment,预启动执行环境)是一种网络启动协议,允许计算机通过网络启动而不是使用本地硬盘。PXE服务器是实现这一功能的服务器,它提供了启动镜像和引导加载程序,使得客户端计算机可以通过网络启动并安装操作系统或运行其他软件…

java学习记录09:Lambda表达式与引用

Lambda表达式 例子&#xff1a; &#xff08;o1&#xff0c;o2&#xff09; -> Interger.compare(o1,o2); 格式&#xff1a; -> : lambda操作符 -> 的左边: lambda形参列表&#xff0c;对应着要重写的接口中的抽象方法的形参列表。 ->的右边&#xff1a;lamb…

python编程表白爱心代码,来自程序员的浪漫!

Python爱心表白代码 感觉的紫色要更加浪漫&#xff0c;其中的文字也是可以直接更改的&#xff0c;非常方便 <文末附带精品籽料> 改变爱心的颜色: 在源代码的13-15行位置&#xff0c;可以通过更改16进制颜色色值进行改变爱心的颜色&#xff0c;这里小编改了一点绿色&…

阶层差异下的社会角色与认知逻辑

阶层差异下的社会角色与认知逻辑 在中国传统文化中&#xff0c;有句古话&#xff1a;“仗义每从屠狗辈&#xff0c;负心多是读书人。”这句话揭示了不同社会阶层的人&#xff0c;在认知和行为上存在的差异。 社会底层&#xff1a;道德与情感的坚守者 社会底层人群&#xff0c…

05 循环神经网络

目录 1. 基本概念 2. 简单循环网络 2.1 简单循环网络 2.2 长程依赖问题 3. 循环神经网络的模式与参数学习 3.1 循环神经网络的模式 3.2 参数学习 4. 基于门控的循环神经网络 4.1 长短期记忆网络 4.2 LSTM网络的变体网络 4.3 门控循环单元网络 5. 深层循环神经网络…

PCL Local Surface Patches 关键点提取

目录 一、算法原理1、算法原理2、 参考文献二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 1、算法原理 主曲率是某一点局部形状的体现,Local Surface Patches 关键点检测法…

深入理解计算机系统 CSAPP 家庭作业11.7

静态内容是指在不同请求中访问到的数据都相同的静态文件。例如&#xff1a;图片、视频、网站中的文件&#xff08;html、css、js&#xff09;、软件安装包、apk文件、压缩包文件等。 /** get_filetype - derive file type from file name*/ void get_filetype(char *filename,…

Pytorch使用教学3-特殊张量的创建与类型转化

1 特殊张量的创建 与numpy类似&#xff0c;PyTorch中的张量也有很多特殊创建的形式。 zeros:全0张量 # 形状为2行3列 torch.zeros([2, 3]) # tensor([[0., 0., 0.], # [0., 0., 0.]])ones:全1张量 # 形状为2行3列 torch.ones([2, 3]) # tensor([[1., 1., 1.], # …

「12月·长沙」第四届机器人、自动化与智能控制国际会议(ICRAIC 2024)

随着科技的飞速发展&#xff0c;智能机器人在当今社会的重要性愈发凸显。从制造业的自动化生产线&#xff0c;到医疗领域的手术机器人&#xff0c;再到家庭生活中的智能助手&#xff0c;机器人与人工智能的融合正在改变着我们的生产和生活方式。第四届机器人、自动化与智能控制…

Docker(十)-Docker运行elasticsearch7.4.2容器实例以及分词器相关的配置

1.下载镜像 1.1存储和检索数据 docker pull elasticsearch:7.4.2 1.2可视化检索数据 docker pull kibana:7.4.22.创建elasticsearch实例 创建本地挂载数据卷配置目录 mkdir -p /software/elasticsearch/config 创建本地挂载数据卷数据目录 mkdir -p /software/elasticse…

IEC104转MQTT网关支持将IEC104数据转换为华为云平台可识别的格式

随着智能电网和物联网技术的深度融合&#xff0c;传统电力系统中的IEC104协议设备正逐步向更加开放、智能的物联网体系转型。华为云作为全球领先的云计算和AI服务提供商&#xff0c;其物联网平台为IEC104设备的接入与数据处理提供了强大的支撑。本文将探讨IEC104转MQTT网关在MQ…

微信小程序-本地部署(前端)

遇到问题&#xff1a;因为是游客模式所以不能修改appID. 参考链接&#xff1a;微信开发者工具如何从游客模式切换为开发者模式&#xff1f;_微信开发者工具如何修改游客模式-CSDN博客 其余参考&#xff1a;Ego微商项目部署&#xff08;小程序项目&#xff09;&#xff08;全网…

GoFly快速开发框架基于Go语言和Vue3开发后台管理附件管理插件包

说明 为了给客户提供更好的交互体验&#xff0c;框架把附件管理独立打包成插件包&#xff0c;这样附件管理接可以做个不通需求的附件管理插件包来满足不同甲方客户需求。 目前附件插件包有2个&#xff1a;一个基础包、一个高级包 附件插件包功能 1.基础包 统一管理业务系统…