对 Pulsar 集群的压测与优化

前言

这段时间在做 MQ(Pulsar)相关的治理工作,其中一个部分内容关于消息队列的升级,比如:

  • 一键创建一个测试集群。
  • 运行一批测试用例,覆盖我们线上使用到的功能,并输出测试报告。
  • 模拟压测,输出测试结果。

本质目的就是想直到新版本升级过程中和升级后对现有业务是否存在影响。

一键创建集群和执行测试用例比较简单,利用了 helmk8s client 的 SDK 把整个流程串起来即可。

压测

其实稍微麻烦一点的是压测,Pulsar 官方本身是有提供一个压测工具;只是功能相对比较单一,只能对某批 topic 极限压测,最后输出测试报告。
最后参考了官方的压测流程,加入了一些实时监控数据,方便分析整个压测过程中性能的变化。

客户端 timeout

随着压测过程中的压力增大,比如压测时间和线程数的提高,客户端会抛出发送消息 timeout 异常。

org.apache.pulsar.client.api.PulsarClientException$TimeoutException: 
The producer pulsar-test-212-20 can not send message to the topic persistent://my-tenant/my-ns/perf-topic-0 within given timeout : createdAt 82.964 seconds ago, firstSentAt 8.348 seconds ago, lastSentAt 8.348 seconds ago, retryCount 1

而这个异常在生产业务环境的高峰期偶尔也出现过,这会导致业务数据的丢失;所以正好这次被我复现出来后想着分析下产生的原因以及解决办法。

源码分析客户端

既然是客户端抛出的异常所以就先看从异常点开始看起,其实整个过程和产生的原因并不复杂,如下图:

客户端流程:

  1. 客户端 producer 发送消息时先将消息发往本地的一个 pending 队列。
  2. 待 broker 处理完(写入 bookkeeper) 返回 ACK 时删除该 pending 队列头的消息。
  3. 后台启动一个定时任务,定期扫描队列头(头部的消息是最后写入的)的消息是否已经过期(过期时间可配置,默认30s)。
  4. 如果已经过期(头部消息过期,说明所有消息都已过期)则遍历队列内的消息依次抛出 PulsarClientException$TimeoutException 异常,最后清空该队列。

服务端 broker 流程:

  1. 收到消息后调用 bookkeeper API 写入消息。
  2. 写入消息时同时写入回调函数。
  3. 写入成功后执行回调函数,这时会记录一条消息的写入延迟,并通知客户端 ACK。
  4. 通过 broker metric 指标 pulsar_broker_publish_latency 可以获取写入延迟。

从以上流程可以看出,如果客户端不做兜底措施则在第四步会出现消息丢失,这类本质上不算是 broker 丢消息,而是客户端认为当时 broker 的处理能力达到上限,考虑到消息的实时性从而丢弃了还未发送的消息。

性能分析

通过上述分析,特别是 broker 的写入流程得知,整个写入的主要操作便是写入 bookkeeper,所以 bookkeeper 的写入性能便关系到整个集群的写入性能。

极端情况下,假设不考虑网络的损耗,如果 bookkeeper 的写入延迟是 0ms,那整个集群的写入性能几乎就是无上限;所以我们重点看看在压测过程中 bookkeeper 的各项指标。

CPU

首先是 CPU:

从图中可以看到压测过程中 CPU 是有明显增高的,所以我们需要找到压测过程中 bookkeeper 的 CPU 大部分损耗在哪里?

这里不得不吹一波阿里的 arthas 工具,可以非常方便的帮我们生成火焰图。

分析火焰图最简单的一个方法便是查看顶部最宽的函数是哪个,它大概率就是性能的瓶颈。

在这个图中的顶部并没有明显很宽的函数,大家都差不多,所以并没有明显损耗 CPU 的函数。

此时在借助云厂商的监控得知并没有得到 CPU 的上限(limit 限制为 8核)。


使用 arthas 过程中也有个小坑,在 k8s 环境中有可能应用启动后没有成功在磁盘写入 pid ,导致查询不到 Java 进程。

$ java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.6.7
[INFO] Can not find java process. Try to pass <pid> in command line.
Please select an available pid.

此时可以直接 ps 拿到进程 ID,然后在启动的时候直接传入 pid 即可。

$ java -jar arthas-boot.jar 1

通常情况下这个 pid 是 1。

磁盘

既然 CPU 没有问题,那就再看看磁盘是不是瓶颈;

可以看到压测时的 IO 等待时间明显是比日常请求高许多,为了最终确认是否是磁盘的问题,再将磁盘类型换为 SSD 进行测试。


果然即便是压测,SSD磁盘的 IO 也比普通硬盘的正常请求期间延迟更低。

既然磁盘 IO 延迟降低了,根据前文的分析理论上整个集群的性能应该会有明显的上升,因此对比了升级前后的消息 TPS 写入指标:

升级后每秒的写入速率由 40k 涨到 80k 左右,几乎是翻了一倍(果然用钱是最快解决问题的方式);

但即便是这样,极限压测后依然会出现客户端 timeout,这是因为无论怎么提高服务端的处理性能,依然没法做到没有延迟的写入,各个环节都会有损耗。

升级过程中的 timeout

还有一个关键的步骤必须要覆盖:模拟生产现场有着大量的生产者和消费者接入收发消息时进行集群升级,对客户端业务的影响。

根据官方推荐的升级步骤,流程如下:

  • Upgrade Zookeeper.
  • Disable autorecovery.
  • Upgrade Bookkeeper.
  • Upgrade Broker.
  • Upgrade Proxy.
  • Enable autorecovery.

其中最关键的是升级 Broker 和 Proxy,因为这两个是客户端直接交互的组件。

本质上升级的过程就是优雅停机,然后使用新版本的 docker 启动;所以客户端一定会感知到 Broker 下线后进行重连,如果能快速自动重连那对客户端几乎没有影响。


在我的测试过程中,2000左右的 producer 以 1k 的发送速率进行消息发送,在 30min 内完成所有组件升级,整个过程客户端会自动快速重连,并不会出现异常以及消息丢失。

而一旦发送频率增加时,在重启 Broker 的过程中便会出现上文提到的 timeout 异常;初步看起来是在默认的 30s 时间内没有重连成功,导致积压的消息已经超时。

经过分析源码发现关键的步骤如下:

客户端在与 Broker 的长连接状态断开后会自动重连,而重连到具体哪台 Broker 节点是由 LookUpService 处理的,它会根据使用的 topic 获取到它的元数据。

理论上这个过程如果足够快,对客户端就会越无感。

在元数据中包含有该 topic 所属的 bundle 所绑定的 Broker 的具体 IP+端口,这样才能重新连接然后发送消息。

bundle 是一批 topic 的抽象,用来将一批 topic 与 Broker 绑定。

而在一个 Broker 停机的时会自动卸载它所有的 bundle,并由负载均衡器自动划分到在线的 Broker 中,交由他们处理。

这里会有两种情况降低 LookUpSerive 获取元数据的速度:

因为所有的 Broker 都是 stateful 有状态节点,所以升级时是从新的节点开始升级,假设是broker-5,假设升级的那个节点的 bundle 切好被转移 broker-4中,客户端此时便会自动重连到 4 这个Broker 中。

此时客户端正在讲堆积的消息进行重发,而下一个升级的节点正好是 4,那客户端又得等待 bundle 成功 unload 到新的节点,如果恰好是 3 的话那又得套娃了,这样整个消息的重发流程就会被拉长,直到超过等待时间便会超时。

还有一种情况是 bundle 的数量比较多,导致上面讲到的 unload 时更新元数据到 zookeeper 的时间也会增加。

所以我在考虑 Broker 在升级过程中时,是否可以将 unload 的 bundle 优先与 Broker-0进行绑定,最后全部升级成功后再做一次负载均衡,尽量减少客户端重连的机会。

解决方案

如果我们想要解决这个 timeout 的异常,也有以下几个方案:

  1. 将 bookkeeper 的磁盘换为写入时延更低的 SSD,提高单节点性能。
  2. 增加 bookkeeper 节点,不过由于 bookkeeper 是有状态的,水平扩容起来比较麻烦,而且一旦扩容再想缩容也比较困难。
  3. 增加客户端写入的超时时间,这个可以配置。
  4. 客户端做好兜底措施,捕获异常、记录日志、或者入库都可以,后续进行消息重发。
  5. 为 bookkeeper 的写入延迟增加报警。
  6. Spring 官方刚出炉的 Pulsar-starter 已经内置了 producer 相关的 metrics,客户端也可以对这个进行监控报警。

以上最好实现的就是第四步了,效果好成本低,推荐还没有实现的都尽快 try catch 起来。

整个测试流程耗费了我一两周的时间,也是第一次全方位的对一款中间件进行测试,其中也学到了不少东西;不管是源码还是架构都对 Pulsar 有了更深入的理解。

作者: crossoverJie

出处: http://crossoverjie.top

欢迎关注博主公众号与我交流。

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 如有问题, 可邮件(crossoverJie#gmail.com)咨询。

本文转载于网络 如有侵权请联系删除

相关文章

  • 闲话 Spark 的一个重要改变

    最近看到了ApacheSpark发布了3.2版本的预告PandasAPIonUpcomingApacheSpark™3.2,文章写得很简单,但是体现了Spark的一个很重要的发展趋势,就是拥抱Python的数据科学社区。毋庸置疑,在大数据+AI的时代,最耀眼的编程语言是Python,比如scikit-learn、XGBoost和Tensorflow/PyTorch都是Python的一部分,这些与机器学习相关的包的背后则是Numpy和Pandas。肉眼可见,暂时没有一种新的编程语言可以替代Python背后蓬勃发展的数据科学社区从而替代Python在大数据+AI领域里的地位。正因为如此,即使Spark是使用Scala语言编写的,但是依然选择“重兵投入”到Python社区,比如Spark3.2所支持的PandasAPI和背后的ProjectZen。什么是ProjectZen呢?按照Databricks的blog,Zen取自著名的Python之禅(Python之禅阐述了Python语言的精髓)。Withthismomentum,theSparkcommunitystartedtofocusmo

  • codeforces 322 A Ciel and Dancing

    题目链接题意:有n个男孩和m个女孩,他们要结对跳舞,每对要有一个女孩和一个男孩,而且其中一个要求之前没有和其他人结对,求出最大可以结多少对。如图,一条线代表一对,只有这样三种情况。#include<iostream> #include<algorithm> #include<stdio.h> usingnamespacestd; intmain() { intn,m; while(cin>>n>>m) { intmn=min(n,m); intk=(mn<<1)-1; k+=(max(n,m)-mn); inta=1,b=1; cout<<k<<endl; inti=1; intt=0; for(;i<mn;i++) { printf("%d%d\n",i,i); printf("%d%d\n",i,i+1); t=i; } t++; for(;i<=max(n,m);i++) { if(n<=m) printf("%d%

  • 偿还技术债(3)-ARouter源码详解

    国庆假期想着闲着也是闲着,就想着来深入了解下几个常用的开源库??,看下其实现原理和源码,进行总结并输出成文章。初定的目标是EventBus、ARouter、LeakCanary、Glide、Coil、Retrofit、OkHttp等几个。目前已经完成了部分,在之后的几天里会将文章陆续发布出来?? 原文地址:https://juejin.im/user/923245496518439/posts 一、ARouter路由框架在大型项目中比较常见,特别是在项目中拥有多个moudle的时候。为了实现组件化,多个module间的通信就不能直接以模块间的引用来实现,此时就需要依赖路由框架来实现模块间的通信和解耦:sunglasses:而ARouter就是一个用于帮助AndroidApp进行组件化改造的框架,支持模块间的路由、通信、解耦1、支持的功能支持直接解析标准URL进行跳转,并自动注入参数到目标页面中支持多模块工程使用支持添加多个拦截器,自定义拦截顺序支持依赖注入,可单独作为依赖注入框架使用支持InstantRun支持MultiDex(Google方案)映射关系按组分类、多级管理,按需初始化支

  • 在Egret项目中使用protobuf

    protobuf简介ProtocolBuffer是用于结构化数据串行化的灵活、高效、自动的方法,有如XML,不过它更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。使用protobuf下载protobuf的js库下载地址:http://download.csdn.net/download/yue19870813/9957415解压后包括如下几个文件:ByteBufferAB.min.jsLong.min.jsprotobuf.d.jsonprotobuf.d.tsProtoBuf.min.js编译成Egret项目可以使用的库文件白鹭官方第三方库使用文档:http://developer.egret.com/cn/github/egret-docs/extension/threes/instructions/index.html创建第三方模块当我们准备好了要用的第三方库的源文件后,还需要把它编译成egret需要用的第三方库。以我们上面下载的protobuf文件为例。创建一个egret第三方库的

  • javascript入门到进阶 - 事件冒泡和事件委托详解

    事件冒泡❝当一个子元素的事件被触发的时候(例如onclick事件),该事件会从事件(被电击的元素)开始逐个向上传播,触发父级元素的点击事件 ❞上图吧HTML代码<html> <head></head> <body> <ul> <li>111</li> <li>222</li> <li>333</li> </ul> </body> </html> 复制JAVASCRIPT代码varoLis=document.querySelectorAll('li'); varlength=oLis,length; for(leti=0;i<length;i++){ oLis[i].onclick=function(){ alert(this.innerHTML); } } 复制如果你点击了页面中的li标签,那么这个click事件会按照如下(1)li(2)ul(3)body(4)html(5)document也就

  • NodeJS模块研究 - zlib

    nodejs的zlib模块提供了资源压缩功能。例如在http传输过程中常用的gzip,能大幅度减少网络传输流量,提高速度。本文将从下面几个方面介绍zlib模块和相关知识点:文件压缩/解压HTTP中的压缩/解压压缩算法:RLE压缩算法:哈夫曼树 文件的压缩/解压以gzip压缩为例,压缩代码如下:constzlib=require("zlib"); constfs=require("fs"); constgzip=zlib.createGzip(); constrs=fs.createReadStream("./db.json"); constws=fs.createWriteStream("./db.json.gz"); rs.pipe(gzip).pipe(ws);复制如下图所示,4.7Mb大小的文件被压缩到了575Kb。解压刚才压缩后的文件,代码如下:constzlib=require("zlib"); constfs=require("fs"); const

  • 等保测评2.0:SQLServer安全审计

    一、说明本篇文章主要说一说SQLServer数据库安全审计控制点的相关内容和理解。二、测评项a)应启用安全审计功能,审计覆盖到每个用户,对重要的用户行为和重要安全事件进行审计; b)审计记录应包括事件的日期和时间、用户、事件类型、事件是否成功及其他与审计相关的信息; c)应对审计记录进行保护,定期备份,避免受到未预期的删除、修改或覆盖等; d)应对审计进程进行保护,防止未经授权的中断。三、测评项aa)应启用安全审计功能,审计覆盖到每个用户,对重要的用户行为和重要安全事件进行审计;SQLServer默认开启着错误日志,在服务器-管理-SQLServer日志中: 错误日志大概记录的内容:2.1日志自动记录的信息大概有如下: (1)SQLSERVER的启动参数,以及认证模式,内存分配模式。 (2)每个数据库是否能够被正常打开。如果不能,原因是什么? (3)数据库损坏相关的错误 (4)数据库备份与恢复动作记录 (5)DBCCCHECKDB记录 (6)内存相关的错误和警告 (7)SQL调度出现异常时的警告。一般SERVERHang服务器死机会伴随着有这些警告 (8)SQLI/O操作遇到长时间延迟

  • 编写C程序的7个步骤

    很多人觉得编写一个C语言程序是个很复杂的问题,但其实是很简单的,至少对于二级C考试题目来说都比较简单。面对一个相对复杂的问题,我们要学会理清楚思路,把它分解成若干小问题,然后条理清晰地解决这个“复杂”的问题。 写C程序有以下7个步骤:定义程序的目标 定义目标就是确定这个程序是干什么的,实现什么功能。简单说,就是输入了什么,又输出什么?设计程序结构 程序结构是尤为重要的,它体现了你的编程思路。实现第一步的目标,整体思路是怎样的,每一小步都有哪些小的问题,如何解决。编写代码 根据第二步的程序结构,一步步用代码实现。即把你的思路逻辑翻译成C语言。这是C语言的基本功,要多加练习,从参考模仿到理解吃透。 编译程序 代码基本完成后,编译程序。当然一开始难免会报很多错误,不要害怕,根据提示认真检查修改,一般都是常见问题,熟练之后就能很快解决。遇到错误应该高兴,每一次都是在积累经验。 运行程序 编译成功后,运行程序。 测试和调试程序 检查程序运行结果和自己设计的思路是否一致。如有问题从第三步重复。测试要多试一些用例,尽可能不同的情况,以发现自己考虑不周的地方。 维护和修改优化代码 维护、优化代码是很重

  • 现象级的「复联 4」,被预测票房三十亿美金创影史纪录

    By超神经场景描述:用数据挖掘、机器学习、自然语言处理等方法,对电影票房进行预测,为制片方、发行商、投资者以及影院提供参考与指导作用。关键词:数据挖掘机器学习自然语言处理票房预测复仇者联盟迎来了最终之战。此次最先在中国上映的举动也让不少欧美粉丝掩面哭泣,而周三凌晨的首映更是经历了一票难求,甚至是重金难求。还有铁粉花数百元顶着要早起的压力购买了周三早晨7点的票……预售火爆,人肉预测最终票房30亿美元据猫眼电影统计,《复仇者联盟4:终局之战》预售到今日已经突破6亿,首日观影人数近300万,刷新了影史预售纪录。万众瞩目的《复联4》被预测最终票房会达到30亿美元。漫威迷们已经迫不及待地加入火热探讨:知乎网友普遍人工预测《复联4》最终全球票房会达到30亿之前《复联3》最终的全球票房惊人,超过了20亿美元,排行影史第四。如今复联的结局看起来势头很猛,甚至会超过上一部。目前根据国外专业票房预测网站的数据显示,《复联4》的首周末票房将会高达2.5亿至2.9亿美元,如果达到平均值2.65亿美元的话,就会超越前作(2.577亿美元)成为北美影史首周末票房冠军。国外网友也纷纷人工预测上映前三天会达10亿美元

  • 如何系统的学习 R 语言数据挖掘

    “虽然是本科毕业,但是在看数据挖掘方面的算法理论时经常感觉一些公式的推导过程如天书一般,例如看svm的数学证明,EM算法,凸优化…感觉知识跳跃比较大,是我微积分学的不好还是中间有什么好的教材补充一下,数据挖掘系统的学习过程是怎么样的,应该看那些书(中文最好)?“——以上是一位咨询的学员像我们提出的疑问。和这位同学相似,很多同学在入门数据挖掘领域遭到了极大的阻力,也丧失了继续学习的兴趣。那么,正确入门数据挖掘领域的姿势是什么呢?这是一个不太好回答的问题,管中窥豹,建议大家看一下以下的一些见解。一、在学习数据挖掘之前你需要明了的几点:1.数据挖掘目前在中国的尚未流行开,犹如屠龙之技; 2.据挖掘本身融合了统计学、数据库、机器学习、模式识别、知识发现等学科,并不是新的技术。3.数据挖掘之所以能够应用不是因为算法,算法是以前就有的。数据挖掘应用的原因是大数据和云计算。比如阿法狗的后台有上千台计算机同时运行神经网络算法;4.数据初期的准备工作,也称DataWarehousing。通常占整个数据挖掘项目工作量的70%左右。在前期你需要做大量的数据清洗和字段扩充的工作。数据挖掘和报告展现只占30%左

  • 腾讯云ElasticsearchService欠费说明

    说明: ElasticsearchService(ES)集群和Logstash实例的欠费机制一致,下文以ES集群为例介绍。 包年包月集群到期预警包年包月的云资源会在到期前第7天内,向您推送到期预警消息,预警消息将通过邮件及短信的方式通知到腾讯云账户的创建者以及全局资源协作者、财务协作者。 欠费预警包年包月的云资源到期当天及以后,将向您推送欠费隔离预警消息,预警消息将通过邮件及短信的方式通知到腾讯云账户的创建者以及全局资源协作者、财务协作者。 回收机制 ES集群到期前7天内,系统会给您发送续费提醒通知。 账户余额充足的情况下,若您已设置自动续费,设备在到期当日会执行自动续费。 若您的ES集群在到期前(包括到期当天)未进行续费,系统将在到期后48小时内对其作停服处理(集群不可访问,仅保留数据)。 停服日起到停服后7天,您仍可以在对ES集群进行续费恢复访问,被续费找回的实例续费周期的起始时间为上一个周期的到期日。 若您的ES集群在停服7天后(包括第7天)未进行续费,系统将在停服后第8天的0点开始对资源进行释放,到期ES集群中的数据将被清除且不可恢复。 按量计费集群欠费预警系统在每

  • 紫金大数据平台架构之路(一)----大数据任务开发和调度平台架构设计

    一、总体设计 初来公司时,公司还没有大数据,我是作为大数据架构师招入的,结合公司的线上和线下业务,制定了如下的大数据架构路线图。 二、大数据任务开发和调度平台架构设计 在设计完总体架构后,并且搭建完hadoop/yarn的大数据底层计算平台后,按照总体架构设计思路,首先需要构建的就是大数据开发平台。这也是一个非常核心的平台,也是最基础最重要的一个环节。 一开始设计的架构图如下所示。   架构设计解释说明如下: MasterServer: MasterServer采用分布式无中心设计理念,MasterServer主要负责DAG任务切分、任务提交监控,并同时监听其它MasterServer和WorkerServer的健康状态。MasterServer服务启动时向Zookeeper注册临时节点,通过监听Zookeeper临时节点变化来进行容错处理。 该服务内主要包含:Distributed分布式调度组件,主要负责定时任务的启停操作,当Distributed调起任务后,Master内部会有线程池具体负责处理任务的后续操作 MasterScheduler是一个扫描线程,定时扫描数据库

  • Spring MVC异常处理

    1、@ExceptionHandler进行局部的异常处理 packagecontroller; importorg.apache.logging.log4j.LogManager; importorg.apache.logging.log4j.Logger; importorg.springframework.stereotype.Controller; importorg.springframework.web.bind.annotation.ExceptionHandler; importorg.springframework.web.bind.annotation.PathVariable; importorg.springframework.web.bind.annotation.RequestMapping; importorg.springframework.web.bind.annotation.ResponseBody; importexception.ExceptionUtil; @Controller publicclassExceptionControl

  • 如何实现Vue已经弃用的$dispatch和$broadcast方法?

    对于父子(含跨级)传递数据的通信方式,Vue.js并没有提供原生的API来支持,而是推荐使用大型数据状态管理工具Vuex,但Vuex对于小型项目来说用起来真的很麻烦。 在Vue.js1.x中,提供了两个方法:$dispatch和$broadcast,前者用于向上级派发事件,只要是它的父级(一级或多级以上),都可以在组件内通过$on(或events,2.x已废弃)监听到,后者相反,是由上级向下级广播事件的。 这两种方法一旦发出事件后,任何组件都是可以接收到的,就近原则,而且会在第一次接收到后停止冒泡,除非返回true。 下面我们来自行实现dispatch和broadcast方法,目标是解决父子组件(含跨级)间的通信问题。 我们要实现的dispatch和broadcast方法,将具有以下功能: 在子组件调用dispatch方法,向上级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该上级组件已预先通过$on监听了这个事件; 相反,在父组件调用broadcast方法,向下级指定的组件实例(最近的)上触发自定义事件,并传递数据,且该下级组件已预先通过$on监听了这个事件。 实现这对

  • 抽象类和接口区别

     

  • ubuntu下mysql远程连接和访问慢的解决方法

    原本连接很快的mysql服务器,连接速度奇慢。以前几十毫秒的连接现在完成一次要近5秒钟,在排除了网络问题后,只有从mysql下手。原来每次访问db,mysql就会试图去解析来访问的机器的domainname,如果这时解析不料,等一段时间会失败,数据才能被取过来。解决方法如下: 修改my.cnf [mysqld] skip-name-resolve#Don’tresolvehostnames 重启,问题解决。

  • windows 截图 win+shift+s

    最好用的windows快捷键:win+shift+s 还可以看这个博客https://sspai.com/post/44227

  • 使用ztree展示树形菜单结构

    官网:http://www.treejs.cn/v3/main.php#_zTreeInfo 一、功能简介 在权限系统中,实现给角色指定菜单权限的功能。主要包括以下几点: 读取全部菜单项,并以树形结构展现; 勾选角色拥有的菜单权限,保存入库; 重新编辑角色权限时,默认选中角色已有的菜单权限。 二、界面 三、实现过程 1.在服务端获取全部菜单资源,并转换为json字符串。 @RequestMapping("/edit")    publicStringedit(IntegerroleId,Modelmodel){        ……        //树形菜单资源        List<Map<String,Object>>allResources=resourceService.getTreeMap();  &

  • A Tour of ParallelExtensionsExtras

    ThroughoutthedevelopmentofParallelExtensionsforthe.NETFramework4,we’vecomeacrossamyriadofsituationswherecertainfunctionalitywouldbeusefulindevelopingaparticularapplicationorlibrary,butwherethatfunctionalityisn’tquiteencapsulatedinthebitswe’reshipping.Sometimesthisfunctionalityistooapplication-specifictobeincludedinthecoreoftheFramework,andothertimeswehaven’tbeensurehowbroadlyapplicablethefunctionalityis,nordidwehavethetimetofullydesignit,testit,solicitfeedbackonit,andsoforth.Luckily,inthevastmaj

  • 安装window系统

    安装服务器系统,进入windowpe后将iso中sources,bootmgr,和boot拷贝到C盘,执行bootsect.exe /nt60 c:,调试froad13的consle   win8改win7需要更改下bios的设置:   在开机时按F1进入BIOS,进入Security—SecureBoot—Disabled,再进入Startup—UEFI/LegacyBoot选项,UEFI/LegacyBoot选项选择成Both,UEFI/LegacyBootPriority选择成UEFIFirst,然后到startup-boot下调节引导启动顺序就可以的,最后再按F10(Fn+F10)选择Y保存退出。   开机F12选择U盘或者光驱,回车--连续按空格键,进入安装介质,按照提示安装系统。   win8与win7的分区格式不同,建议您删除原系统的所有分区重新创建下,原win8系统的重要数据您先按备份到U盘等设备上。

  • PyTorch学习(1)

      PyTorch学习(1) PyTorch学习(1)一、预先善其事,必先利其器-pytorch与cuda对应关系二、pytorch相关1.创建张量2.维度变换3.索引切片及数学运算4.autograd:自动求导 一、预先善其事,必先利其器-pytorch与cuda对应关系 pytorchtorchvisionpythoncuda <1.0.1 0.2.2 ==2.7,>=3.5,<=3.7 9.0,10.0 1.1.0 0.3.0 ==2.7,>=3.5,<=3.7 9.0,10.0 1.2.0 0.4.0 ==2.7,>=3.5,<=3.7 9.2,10.0 1.3.0 0.4.1 ==2.7,>=3.5,<=3.7 9.2,10.0 1.3.1 0.4.2 ==2.7,>=3.5,<=3.7 9.2,10.0 1.4.0 0.5.0 ==2.7,>=3.5,<=3.8 9.2,10.0 1.5.0 0.6.0 >=3.6 9.2,10.1,10.2

相关推荐

推荐阅读