• [迁移工具] 【欢迎大家试用鲲鹏开发套件V1.0,这里是功能介绍和使用指导,欢迎反馈试用问题和建议哟】
    鲲鹏开发套件已经在鲲鹏社区发布,欢迎大家试用并提宝贵意见。地址:  华为云-》鲲鹏社区-》鲲鹏软件栈-》开发工具,在开发工具列表中包含这三个工具。也可以直接访问如下链接下载试用:(1)Denpendency Advisorhttps://www.huaweicloud.com/kunpeng/software/dependencyadvisor.html 华为鲲鹏分析扫描工具是一款可以简化客户应用迁移到TaiShan服务器过程的工具。该工具安装在X86服务器上,当客户有软件需要移植到TaiShan服务器上时,可先用该工具分析可移植性和移植投入。该工具解决了客户软件移植评估分析过程中人工分析投入大、准确率低、整体效率低下的痛点,通过该工具能够自动分析并输出指导报告。工具支持的功能特性如下:l   检查用户软件资源包(RPM、JAR、TAR、zip、gzip文件)中包含的SO依赖库,并评估SO依赖库的可移植性。l   检查指定的用户软件安装路径下的SO依赖库,并评估SO依赖库的可移植性。l   检查用户软件C/C++软件构建工程文件,并评估该文件的可移植性。l   检查用户软件C/C++源码,并评估软件源文件的可移植性。l   向用户提供软件移植报告,提供移植工作量评估。l   支持命令行方式和Web两种工作模式。(2)Porting Advisorhttps://www.huaweicloud.com/kunpeng/software/portingadvisor.html软件介绍华为鲲鹏代码迁移工具是一款可以简化客户应用迁移到TaiShan服务器的过程的工具。当客户有X86平台上源代码的软件要移植到TaiShan服务器上时,可用该工具自动分析出需修改的代码内容,并指导用户如何修改。该工具解决了用户代码兼容性人工排查困难、移植经验欠缺、反复依赖编译调错定位等痛点。支持的功能特性如下:l   检查用户C/C++软件构建工程文件,并指导用户如何移植该文件。l   检查用户C/C++软件构建工程文件使用的链接库,并提供可移植性信息。l   检查用户C/C++软件源码,并指导用户如何移植源文件。l   检查用户软件中X86汇编代码,并指导用户如何移植。l   支持命令行方式和Web两种工作模式。(3)Tuning Kithttps://www.huaweicloud.com/kunpeng/software/tuningkit.html软件介绍华为鲲鹏性能优化工具是针对TaiShan服务器的性能分析和优化工具,能收集服务器的处理器硬件、操作系统、进程/线程、函数等各层次的性能数据,分析出系统性能指标,定位到瓶颈点及热点函数。华为鲲鹏性能优化工具支持的功能特性如下:l   支持采集整个系统或指定进程(包括运行中的进程或直接启动的进程)的CPU Cycles性能事件,能够快速定位到热点函数,包括应用程序函数、模块函数与内核函数,甚至能够定位到热点指令。l   支持热点函数按照CPU核/线程/模块进行分组,支持查看热点函数调用栈。l   支持通过火焰图查看热点函数及其调用栈。l   支持代码映射功能,即查看函数内的热点指令及该指令对应的高级语言文件及行号。l   支持显示汇编代码的控制流图。l   支持分析Java代码的热点函数及热点指令。
  • [技术干货] 【wangkai】华为云TaurusDB性能挑战赛参赛总结
    1、前言华为云TaurusDB结束有一段时间了,这几天抽时间写一下参赛总结,我是从阿里第三届中间件比赛开始参加类似比赛的,TaurusDB这次是第三次,虽然有过两次参赛经验,但是数据库比赛还是第一次,报名也是无意中看到朋友在朋友圈的分享,正好想要学习一下数据库相关知识,于是报名参加了比赛,熬了无数个夜晚,很幸运最终以第10名的成绩擦边入围,主要还是cpp大佬和其他选手承让^_^。2、赛题回顾比赛分成了初赛和复赛两个部分,初赛要求实现一个简化、高效的本地 kv(为简化开发,K固定大小8B,V固定大小4KB)存储引擎,其中每个KVStore为一个实例,每个线程操作独立的KV实例,线程间互不影响,复赛在初赛的基础上增加了计算存储分离的架构,计算节点需要通过网络传输将数据递交给存储节点存储。程序主要提供三个接口:评测程序分为2个阶段正确性评测此阶段评测程序会并发写入随机数据(key 8B、value 4KB),写入数据过程中进行任意次进程意外退出测试,引擎需要保证异常中止不影响已经写入的数据正确性。异常中止后,重启引擎,验证已经写入数据正确性和完整性,并继续写入数据,重复此过程直至数据写入完毕。只有通过此阶段测试才会进入下一阶段测试。性能评测随机写入:16个线程并发随机写入,每个线程使用Set各写400万次随机数据(key 8B、value 4KB)。顺序读取:16个线程并发按照写入顺序逐一读取,每个线程各使用Get读取400万次随机数据。热点读取:16个线程并发读取,每个线程按照写入顺序热点分区,随机读取400万次数据,读取范围覆盖全部写入数据。热点的逻辑为:按照数据的写入顺序按10MB数据粒度分区,分区逆序推进,在每个10MB数据分区内随机读取,随机读取次数会增加约10%。评测环境计算节点:内存占用不得超过4G(CPP), 5G(JAVA),不得写入和读取磁盘空间。存储节点:内存占用不得超过2G(C++),3G(JAVA),磁盘占用不得超过320G。注:后台将CPU统一限制为16核。语言限定CPP & Java,一起排名3、赛题分析拿到赛题后首先我们对赛题已知条件做下分析:数据总量为4M*16(64M)条数据Key数据总量为64M*8(B),索引可以完全加载到内存定长Key(8B)和Value(4KB)极大简化索引构建和写入缓存设计读取分为顺序和逆序遍历(10M内随机)可以考虑使用LRU作为缓存策略kill -9保证数据不丢需使用mmap保证数据最终落盘因为我们比系统更了解程序的缓存策略读写考虑使用DirectIO+自管理缓存计算存储分离可以把存储节点当作一块磁盘对外提供read, write接口,把索引构建及索引查找放到计算节点因为DB是分布式的应考虑使用大块网络传输来减少rtt带来的性能损耗可以看出此次大赛的核心考察点是计算存储分离,因为计算量不大,存储介质性能又非常强悍,程序的瓶颈可能是在网络层面,如何高效利用网络是本次比赛重点思考内容。4、程序设计4.1 功能设计首先要考虑的是功能评测,即保证kill -9数据不丢,因为一个程序只有功能正确才能谈性能,保证kill -9数据不丢有两种方式,第一种是来一条数据写一次盘,每条数据都即时落盘,这个方式虽然可以保证数据不丢,但是性能堪忧,好在操作系统给我们提供了另一种方式mmap(memory mapping),通过mmap可以将文件直接映射到user内存区,user可以直接操作这块内存,程序可以通过主动(force)或者被动(程序结束或崩溃)方式将内存中的数据持久到磁盘,这个过程不经过kernel内存区,省去一次内存拷贝,同时也保证了数据安全。保证了存储节点数据不丢后下面要考虑在有网络传输的情况下数据不丢,如果允许数据丢失则可以使用异步io方式传输数据,但是要保证数据不丢则计算机点需要一直阻塞直到存储节点数据落盘并返回ack后计算节点set方法才能返回,这样网络模型为PING/PONG模型。4.2 文件分布设计因为程序是多实例数据库,实例之间互不干扰,这里没有对数据进行分片操作,其实换一个角度看我们可以把每个实例看作一个数据库分片,因为分片的目的是为了让分片之间互不干扰,提高程序并行能力,每个实例(分片)内的写操作都比较简单,采用追加写的方式。每个实例包含3个文件: MergeIO(mmap):merge io索引文件(mmap):Key实际存储文件数据文件:Value实际存储文件写入Value时先写入MergeIO文件,Merge满后批量写入数据文件,即使程序被kill,也可以从MergeIO文件恢复Value数据。这里感谢一下徐靖峰(岛风)同学开源的kdio(java操作DirectIO的库)4.3 索引设计数组二分查找:构建索引时需要排序,单点查询对cpu分支预测不友好,但是方便进行范围LongIntMap(开放寻址法):可以动态构建索引,单点查询可快速定位(key冲突量不大时),难以范围查询因为这里考虑只有单点get没有范围查询,所以用了LongIntMap 4.4 读取设计读取采用LRU算法对数据进行缓存读取,每个LRU Item包含4M Value数据(1K个Value,参数可调)每个实例包含14个LRU Item,包含4M(总消息数)/1K个空Item,比如说通过Key找到Index为1,对1取模得到缓存位置0,因为缓存已存在则直接使用item.get(),如果Index为1024,因为缓存不存在,则根据缓存最后访问时间找到需要失效的缓存,通过read方法从存储节点读取该缓存的内容,然后get出结果。4.5 网络传输设计存储节点:存储节点采用Epoll EventLoop模式,节点启动16条线程,计算节点与存储节点tcp连接均匀分布在16条线程上,后面数据交互均在固定线程完成,充分利用多核优势。计算节点:计算节点采用bio方式与存储节点建立连接,计算节点与存储节点交互均采用req/res阻塞模式交互。计算节点从存储节点read数据时,存储节点dio读取的数据直接使用ByteBuf.wrap,没有内存拷贝(程序层面),直接发送给计算节点。连接建立后开启tcp_nodelay以减少网络延迟。在比赛结束最后一天之前计算节点一直用的nio开发,写入耗时始终在719秒往上,因为一直觉得虽然nio在发送接收时与set/get线程有cpu切换,但是这些切换耗时与网络rtt相比应该不算什么,可事实总是在啪啪啪打脸,最后一天(其实也就写好两三天的样子,java环境最后几天才好,(/ □ \))把nio改写成bio传输,写入时间从725降到560左右(不确定是否是因为cpu切换导致),总结一下就是不能搞经验主义,一定要benchmark everything。5、可探索的改进一般情况下我们使用nio都是将其fd绑定到某个event loop上,该event loop监听该fd的事件,这里其实我们可以改变nio使用方式,在set/get当前线程监听fd事件来读写数据,有set/get线程充当event loop,可能会有一个不错的提升。6、一些感想第一次参加数据库相关的比赛,收获颇多,对数据库相关知识有了初步的了解,也学到了其他选手不错的分享,同时也非常感谢举办方举办这次比赛,给选手提供一个展示和学习的平台,预祝比赛越办越好。源码地址:https://github.com/wangkaish/hw_race2019_r2_kvstore 
  • [技术干货] 【watermelon】华为云TaurusDB性能挑战赛参赛总结
    大家好,我们是watermelon团队,今天我们的答辩展示主要分为四个部分来介绍。首先是我们的团队和成员的简介然后是我们对这次题目重点的理解接下来,我会介绍一下我们最基础的架构,包括网络、磁盘、缓存等最后,重点介绍我们每一个性能优化点团队名叫watermelon,我们三个人都是在读的研究生,分别来自浙江大学和上海交通大学,都是明年毕业。初赛和复赛的成绩都是第四名,历史成绩非常的稳定。我们总结了几个题目重点:首先,KV 都是定长的:这样简化了⽂件和内存的操作和管理。第二点,value远大于key:把 KV 分离存储,解除索引和数据之间的耦合性。第三点,线程数固定:测评程序固定使用 16 个线程访问数据库。第四点,只需要保证进程意外退出时的持久性:所以可以利用操作系统的缓存对写入方式进行一些优化。第五点,分阶段测评:随机写、随机读、顺序读三个阶段互相没有重叠。第六点,计算节点无状态:我们知道,在这种基于共享存储的计算存储分离架构下,所有持久化数据只能存于存储节点,计算节点只进行逻辑操作。第七点,数据读取的线程和数据写入线程之间没有绑定关系:就是说,每个线程不是只读取自己写入的数据。  //与初赛不一样,读取线程id不一读的是相同id写入的数据最后一点,随机读的随机性在每个时刻只局限在一个 10M 热点分区内:并且热点分区按写入的顺序逆序推进。 下面是我们方案的核心架构:    我们计算节点和存储节点的线程采用一对一的tcp连接,因为测评程序是16线程,所以连接数就是16。在存储节点,我们有数据持久化产生的文件,以及读写文件的缓存。在计算节点,我们维护了读数据的缓存,但是没有写数据的缓存,因为要保证计算节点被 kill 的数据持久性。并且我们的索引也是只在计算节点上维护的,在数据库启动阶段从存储节点把索引数据拉到计算节点。接下来,具体介绍一下我们的文件组织形式:首先,按写入线程进行数据的分区,就是说,每个写入线程只顺序写自己的分区文件,这样就避免了多线程写同一个文件冲突的问题。然后,在每个分区内,将 key 和 value 分离存储为两个文件,一个是key log,一个是value log。可以解除索引和数据之间的耦合性。并且我们为了提高写入value 的速度,对 value 进行了缓存,缓存中凑齐若干个value后,再一起进行刷盘。为了保证缓存不丢失,缓存也使用了mmap的形式,因此对应有一个缓存文件。我们的索引也是按分区进行构建的,每个分区是一个hash,里面存的是该分区所有数据的索引项,索引项就是一个 key 和 一个value offset复赛的优化历程,我们从最开始跑通的 2300 分,到最后的 780 分,中间经过了好几次架构和方法的改变。首先我们的第一个优化是,我们在启动阶段把 key 从存储节点批量的传到计算节点,这样相比每个 key 都请求一次,批量的方法相当于减少了一半的网络请求,使得时间提升了 300 秒。接下来,由于我们无法搞定网络传输大数据包的问题,因此我们选择先在存储节点实现顺序读和随机读的缓存,这样减少了存储节点的 io 次数,时间提升了 500s再后来,我们解决了网络传输的问题,所以先在计算计算节点进行了顺序读的缓存,成绩提升到 1100s这样紧接着,我们按存储节点缓存一样的思路,把随机读缓存也拿到了计算节点,这样做之后我们的成绩就已经突破了 800s在最后阶段,我们又优化了一些细节问题,最终成绩是 781s围绕读写文件,缓存策略,和网络传输这三个方面,来讲解我们是如何把这个系统的性能压榨到极致的。对于 key 这种小数据量的读写模式,采用 mmap 可以利用 page cache 将小数据读写转换为整个内存页的读写,减少了系统调用的时间消耗。value 的大小为固定 4KB,我们知道,写入数据大小要对齐 ssd 的内部 page 时,可以达到最优写入性能。我们经过线上测试,按 1M 大小顺序写入数据可以达到最大吞吐量。所以,为每一个分区分配一个容量为 1M 的写缓冲区,写满 256 个 value 再将缓存数据一起刷盘,刷盘方式采用 direct io。并且,用 mmap 做缓存,可以保证数据持久性。第二点,我们来看,如何做随机读的缓存,使得缓存命中率最高。现在背景是,随机读在每个时刻只局限在一个 10M 热点分区内,并且热点分区按写入的顺序逆序推进。看图,上面是我们实际存储的文件,按10M分为了若干个block,就是说,我们的缓存只能按 block 对齐进行加载数据。然后,下面是测评的热点分区示意图,我们发现一共 400w 数据,热点分区的边界跟实际数据 10M 边界很可能是不对齐的,并且边界值未知。所以,如果我们只用单个 10M 的缓存,会出现下面这样的问题。假设当前阶段,测评程序要随机访问 hot1 这个热点分区。第一个位置,我们缓存加载了block3,然后,访问第二个位置,发现缓存失效,加载了block2,继续访问第三个位置,缓存又失效了,重现加载回了block3所以,这样就产生了一个缓存来回震荡的问题,极端情况是,我要访问400万数据,一共要加载400万次大块的缓存,肯定超时,还不如不做缓存。我们的方案是,采用两个 10M 的buffer,一前一后,一起往前推进,这样非常完美了避免了缓存来回震荡的问题。并且这个 buffer 不一定是10M,只要大于10M都可以解决这个问题。第三点,我们来看,如何使得网络的传输效率最高。我们发现,写入阶段的网络传输时间主要瓶颈是在周转时间上,也就是说,不是网络有多么拥塞导致网络传输变慢,而是说,value发过去再回来,本身就需要这么长的时间。所以我们的优化只能是让存储节点尽可能快的返回ack信号,我们的做法是在数据写入存储节点的mmap之后,就返回ack,而不用等待page cache 刷盘。对于持久性,recover 阶段会把 cachebuffer 里面的数据都重新写进 value 文件里。对于大块数据进行拆分,然后进行多次发送,可以在发送的同时进行流量控制,使吞吐量保持在一个较高的水平。我们的流量控制方法非常简单粗暴,在发送每两个 4k 数据之间,直接加一个延时,延时的方法是让 CPU 自旋一会。并且,存储节点也做了读缓存,把存储节点的读缓存,批量拆分传到计算节点。最后,回到一个宏观的位置上来看,通过这次比赛,我们严格按照计算存储分离的思想来设计了我们的系统架构。对基于共享存储的计算存储分离架构有了一些认知和理解。首先,我们看计算节点我们的架构只支持一个 RW 节点,进行数据的写入。但是 RO 节点在理论上是可以无限扩展的。并且,由于底层的共享存储,所以主从复制的延迟可以做到非常低。当 RW 写入一个 kv 数据,对于 RO 节点,它只需要更新已经存在自己 buffer pool 中的数据,而如果发现 RW 写入的数据不在它的 buffer pool 中,那它什么也不做。只有在 RO 节点读取数据时,发现要请求的数据不在自己的buffer pool中,它才去下面的存储节点中拉取这个数据到自己的 buffer pool 中。这样看来,我们在实现高可用功能时候,可以很直接的进行主从切换,RO 节点可以迅速提升为 RW 节点,直接开始对外服务。再看存储节点我们当前只是在单个节点上保证了数据的持久性,而这种 kv 存储可以扩展为分布式架构,采用多副本存储,从而能获得更好的容错性,和更好的读写性能。所以这样一整套架构,可以解决很多实际的痛点,因此,会成为当今云数据库的一个趋势。
  • [技术干货] 【Rollbacked】华为云TaurusDB性能挑战赛参赛总结
    缘起TaurusDB是华为云自主研发的最新一代云原生分布式数据库,完全兼容mysql8.0,采用计算与存储分离、日志即数据的架构设计,支持1写15读,性能达到原生Mysql的7倍。Taurus构建在共享分布式存储上,存储空间最高达128T,能跨AZ部署,具有可靠、高性能、易伸缩等特性。华为云TaurusDB性能挑战赛是由华为云主办的数据库领域顶级赛事,大赛将综合科研教学成果及商业领域需求,探索数据库领域的技术问题的可行性,为需求方和开发者提供联接的桥梁;并联合合作伙伴,搭建一个技术交流、人才培养、机遇共创数据库开发者平台和生态。听起来很激动人心吧,关键是奖金也很多, 同时还能刷脸! 最早是听一个同事说起的,恰好本人的工作也是“新一代数据库”开发,看着这么多人工智能的比赛不能参加,只能后悔自己选错了专业,但是机会来了,终于有自己擅长的领域了,心想不能错过这次机会,一边默默加了收藏。比赛内容一句话概括,就是实现一个kvstore,初赛是单机引擎,复赛要求存储计算分离,并且kv都是定长的,如果通用性做得好,可以直接用作块存储。题目初赛和复赛稍有区别,这里只说复赛的题目。存储引擎K,V都是定长的,key 8bytes, value 4K bytes。 测试分为三轮,第一轮, 16个线程写,每个线程写400万次;第二轮,16个线程读,每个线程按顺序读取400万次;第三轮,16个线程读,每个线程逆序读取400万次。第三轮的逆序是局部随机,总体逆序,也即在10M的数据范围内随机读,然后以10M为单位逆序推进。 成绩就是看总时间,时间越短越好。前面已经说过,初赛要实现的是单机引擎,复赛要实现的是存储计算分离的引擎,最大的限制是计算节点不能持久化任何数据。预选赛不得不说,华为可真会玩,这次预选赛竟然还要做题,并且是必须要学习他们的资料才能通过的题目,题目包括数据库的基本常识,但是也有产品介绍,经过深夜两个小时的学习,我算是第一次知道了这么多种不同的产品究竟是干嘛的。第一次答题还错了一道,以防万一,又做了一遍,这次错了两道,算了不玩了,95分也够了,睡觉去。但是心里总是不舒服,究竟是哪错了,又看了一遍,原来是幻读的理解这道题错了。初赛不出意外,预选赛顺利通过,初赛就开始写代码了,初赛只要写对差不多就可以晋级复赛。 当然任何一个程序员都不会满足刚好够用的状态,因此正常能想到的优化都加上了: 比如Direct IO, 多文件做数据分区,引入写buffer,比较频繁修改元数据,读取使用自己实现的page cache,以免4K读不能打满磁盘带宽。复赛复赛要求存储计算分离, 我认真做了一些分析,同时也对他们的硬件做了测试,分析下来,这个比赛比拼的点和我平时在工作中要追求的还是很不一样的。 平时无论做什么系统,都是在延迟差不多的情况下,把吞吐量做高,这次比赛最关键的一个点是:延迟第一,延迟是最重要的,特别是写的阶段,因为并发只有16,想通过聚合换吞吐量都不好使了。首先考虑持久化方法:4K恰好能对齐IO,所以key和value要分开存储,      想使用rocksdb之类的存储引擎即使不被禁止也是没有竞争力的。value非常随机,基本不用考虑压缩,就算有一点点好处,实现起来也太复杂了。再次考虑如何优化IO:SSD的IO吞吐量高于4K * iops, 不管是读还是写,IO聚合是必须的。单文件会遇到文件系统瓶颈,需要多文件, 也即要对数据做partition。关于同步IO还是异步IO的选择, 因为延迟优先,所以应该选同步IO。最后考虑网络框架:首先,我直接放弃考虑任何已有的网络框架,因为这是benchmark, 再小的开销也是开销,都会让程序变慢。其次,实际上任何IO      multiplexing的框架都会造成额外的延迟,不仅复杂,也是得不尝失的。综上,我决定就使用最简单的阻塞式IO来做socket通信。总体设计计算节点和存储节点分工, 关键就是索引维护在哪?经过思考, 决定索引维护在计算节点。 索引的内容是: key -> <file_id, pos>, 索引在计算节点的内存中维护为hash表。因为build索引大概需要400ms,写入再读取index比重新构建一遍index时间还长, 所以索引不需要持久化, 这其实是一个反直觉的决定。 计算节点在发送第一个读请求之前,会从存储节点把所有写入的key和pos发送过来, 然后由计算节点构造索引。存储节点起16个线程,listen在16个不同的端口。计算节点也会有16个线程, 每个计算节点的线程只会连接一个存储节点的端口,从而和一个存储节点线程通信。写入请求:写入过程不同的线程完全独立,每个线程负责一部分数据。请求发到存储节点后由接受请求的线程写入。读取过程:读取过程每个存储线程会读取任意一个线程写入的数据。由计算节点指定要读取那个分区的数据。也即写请求发到哪个存储线程,就由那个存储线程写入,每个存储线程实际上就对应了一个数据分区。 读请求发到一个存储线程,它可能要跨线程读取其它分区的数据。存储文件存储节点把数据分为16个partition,每个partition由一个线程负责写入。 每个partition共三个文件: 之所以有三个文件,是因为首先key和value要分开存储,这就要用两个文件;其次为了优化写,额外引入了文件做写入buffer。文件命名规则如下, 以第一个分区为例:00.k: 保存写入的key。 fallocate 4M * 8B, mmap到内存。00.v:      保存写入的value。fallocate 4M *      4K, DIO读写。00.b:      用作写入buffer。fallocate 16K      buffer + 4K 元数据,mmap到内存。写入原子性先写key和value,再更新key count。 key count改成功,则写成功;否则写失败,下次重启进程当作这次写没发生过。换句话说,key count改成功实际上表示这次写入commit成功。key count记录在00.k的第一个8字节, 如前所诉, 00.k是mmap到内存的,所以更新key count是没有什么代价的。value先记录到写buffer里,然后批量刷到00.v文件。key file内容如下:key countk1k2k3k4k5k6……value file内容如下:value countv1v2v3v4v5v6……buffer file内容如下:b0b1b2b3…………b15flushed poswrite buffer也是mmap到内存的, 前64K记录数据。 紧跟64K的8个字节记录flushed pos。 因为mmap必须以page为单位,所以实际内存占用64K + 4K。 它实际上是一个mmap持久化的ring buffer。 Ring Buffer的元数据包括filled pos和flushed pos。 filled pos由key count可以算出来, 所以不需要单独再记。build indexbuild index实际上是构造一个key -> offset的hash表。 要在400ms内完成build index,难点是如何并行:基本思路是把key做partition,每个partition内独立构造hash。这本质上是一个MapReduce的过程。map阶段: 并行划分range, 16个线程,每个线程负责处理一个key文件。reduce阶段: 并行构造hash, 16个线程,每个线程处理一个range。线程同步: 第二阶段开始之前要等第一全部完成,也即两个Stage之间是个Barrier, 这个同步过程和map reduce的shuffle是类似的。读取cache的实现cache是value的镜像,value文件分成了16个,cache也对应分成16组。 因为顺序和热点访问模式对cache都很友好,cache不需要特别大,每一组cache大小为64M。为了应对热点读,cache的最小单元设为16M,借用CPU cache的术语,我把它叫cache line,这是一个很大的值。 cache的内存因为是定长的,所以通过mmap一次申请好,不需要动态分配。 cache的索引本质是一个hash,但是不用解决冲突, 用file offset直接计算得到索引的下标。对16组cache中的每一组来说,有4个cache line, 每个cache line有三种状态,用一个uint32_t表示:Invalid: UINT32_MAXLocked:      UINT32_MAX - 1Valid:      file_offset »12cache line的状态被叫做indexStat,它被单独记在另外一个小数组里。读取不用加锁,使用double check即可:读取之前检查IndexStat有效,并且offset和我要读取的内容匹配。拷贝cache line的内容到私有buffer。拷贝完cache line的内容后再检查IndexStat是否发生过变化, 如果indexStat没有变化过,则说明我们拷贝的cache      line内容是对的。为了让上述double check生效, 更新cache的线程需要在写入之前把indexStat改为Locked,更新完成后再把indexStat改为有效值。关于网络通信关于网络框架还有一个问题,就是是否有必要用UDP,犹豫之下,觉得UDP还是会更复杂,稳妥起见,选择了TCP。后来得知在测试环境里UDP是不同的,也就释然了。但是TCP看起来开箱即用,用起来却暗藏机关,至少要调整以下三个参数:tcp_nodelaytcp_quickacksend/recv      buf最后为了规避TCP的流控,我们要避免另外两个坑:避免发送太快,超过交换机的队列长度,从而导致丢包,因为TCP的工作方式就是,只要你给它数据,它就会不停的加大,发送窗口,直到发生丢包,然后触发限流。要避免这种情况,本质是要限制TCP的连接数, 因为只要连接数限制住了,发送窗口总长度也就限制住了。避免TCP”冷却”之后重新进入慢启动状态,如果有root权限,是可以通过sysctl关闭slow start的,但是我们没法控制系统参数,这就要求我们一个连接最好要不停的发包,不要停下来。有人定义了packet格式,而我遵循一切从简的原则,没有实现通常RPC要有的功能:没有定义pcode,因为整个通信过程就只有读和写两种request,每一个socket只用来发送一种类型的request,如果要发送不同的request,只需要切换socket即可。没有实现序列化,因为request很简单,只需要把结构体直接发送到socket即可。细节决定性能细节也就是很多关于性能的小点:首先关于文件IO: 同步IO比异步IO延迟要小; open的时候加入O_NOATIME避免读取操作也修改元数据其次关于内存分配: 分配完内后提前触发page fault至少可以让性能更稳定,如果不能提高性能的话;      分配内存可以尝试hugepage, 失败之后再使用4K page; mmap的内存可以不用清零最好关于CPU, 绑核,通用可以让延迟更稳定, 理论上对性能是有提升的。正确性测试如前所属,我基本上是一切从简,但即使如此,提交了好多次,都通不过正确性测试。为了排查问题,专门写了一个随机的验证程序,这个比官方的测试强度高多了。确实发现了一个readv之后更新iovec的bug,还有一处cache的bug。 事后看来,在这个测试上花的时间非常值得,否则我可能到最后都没有成绩。总结这次比赛感觉有点像马拉松,听完大家的分享,感觉每个人的时间都比较有限,大家都是争分夺秒,每个人都有一些优化没来得及试。本人最大的一个遗憾是读预取没有实现。另外一个是写的过程中网络和IO并行化做的不好。最后听了第一名的方案,深有体会,要想拿到最好的成绩,需要把写和读分开考虑,同时把网络传输和本地IO分开考虑。单纯从工程上说,我的代码有一些特有的风格, 简单来说就是总爱重复造轮子,不愿意引入依赖:没有用pthread mutex/cond,全部用atomic      ops + futex没有spin,等待的地方都有睡眠和唤醒机制另外,本人虽然工作中用C++, 但是我情愿用plain old C, 所以我很少用高级C++特性。如果未来还做这种系统实现的比赛,希望有可以利用RDMA的和NVM的比赛, 毕竟,新硬件总是有更多的可能。当然比赛的设置要考虑引入更多自由度,这样会更加有趣。 
  • [技术干货] 【ADSL】华为云TaurusDB性能挑战赛参赛总结
    赛题分析赛题要求一个无状态的计算节点和一个存储节点,并要求保证程序崩溃时的数据完整性,所以当计算节点写入时必须等待存储节点的 ack,而存储节点需快快确保数据写入到 page cache。赛题说明 16 个线程,每个线程写入 4 百万条记录,按写入顺序读取及热点区间读取,所以数据按写入线程分离存储。此外 Key 大小固定 8B,value 大小固定 4KB,所以需要批量写入才能打满带宽,并且索引只需要保存位置序号就可以,减小了索引的大小。整体架构针对赛题的要求,可以把问题分解成三个模块来看,分别是计算节点,存储节点和网络通信,因此我们设计了如下图所示的总体结构。在存储节点上,数据分为 16 组存储,按照写入线程分组,保证同一线程的数据写入到同一分组,这样在读取阶段不论是按写入顺序读取还是热点区间读取都可以一次从一个分组中读取一整块 cache,降低网络开销,另外这样并发管理也比较简单,因为一个线程只会对应到一个文件,所以读写的时候是不需要对分组加锁的。具体到到每个分组内部,key 和 value 是分离存储的,因为 key 和 value都是定长的,kv 分离以后恢复索引时只需要读取 key 文件就够了。最后存储节点会保证一旦写入成功数据就不会再丢失,也就是可以容忍 kill -9 退出。我们把索引放在计算节点,并且使用 TCP 进行节点间通信。由于并发数目不高,所以计算节点初始化时创建 16 条 tcp 连接,每个前台线程对应一条连接,在存储节点为每个 tcp 连接创建一个新线程。数据在写入时首先由计算节点发送写请求到存储节点,存储节点收到请求并确认数据成功写入不会丢失后,会把存储的位置回复给计算节点,然后计算节点把位置代入内存索引后返回。读取时计算节点首先根据内存索引判断数据在不在 cache pool 里,如果不在就去向存储节点读取一块新的 cache,然后从 cache 中读数据。存储节点之前我们也提到存储节点按写入线程分组,每个分组里包含四类文件,value data 文件保存 value,key data文件保存 key,两者各有一个 mmap buffer 文件,用来将写入 batch 到一起再刷盘。然后有一个全局的 mmap meta file,记录各个分组当前的数据长度,也就是各分组分别有多少个 kv 对,主要用于数据恢复和重建索引。数据写入时直接将key和value写入对应分组的mmap buffer,两者都写入成功后更新 meta file。只有当 mmap buffer 写满了以后才会 flush 到磁盘中,并且 flush 操作都是用 directIO 进行的。采用这种设计使得写入均是内存操作,将数据写入到 mmap 管理的 page cache 中即可返回,并且我们对mmap使用了mmap_lock标记使其一直锁定在内存中,只有当程序退出时才会刷盘。每次向data文件刷盘都是以一个 buffer 为单位,这样可以最大化利用 nvme 磁盘带宽,采用 directIO 的方式刷盘可以跳过 page cache,一方面减少了一次内存复制,另一方面降低了阶段切换时清空 page cache 的时间。而 mmap 的机制保障了即使进程意外退出,操作系统也会让 mmap buffer 中的数据安全落盘,不会引起数据丢失。我们实际为每个 key 和 data 文件维护了多个 mmap buffer,当一个 buffer 写满时转入后台 flush,接下来的写入会写进另一个 buffer。这是因为写入操作的开销是由网络传输开销和磁盘 IO 开销两部分组成的,单个buffer的情况下两者完全串行执行,在发生刷盘时写入操作需要消耗网络传输加上磁盘IO的时间。采用多个 buffer 以后相当于将磁盘 IO 与网络开销并行化了,产生的阻塞会减少很多。对于数据读取操作,读请求会将 offset 对齐到 cache 长度,也就是 10MB,然后存储节点直接以 directIO 方式从对齐后的 offset 处读出这一块数据返回即可。当存储节点意外关闭时,因为我们已经确保了所有数据都能安全落盘,唯一需要恢复的就是 mmap buffer 文件当前的写入位置,所以只需要从 meta file 中读取个分组的长度,对 buffer 长度取余便可以计算出各 buffer 的当前写入位置了。计算节点计算节点中主要包含了索引和 cache 两部分。索引部分,索引存储在 hash 表中,每个 entry 的格式为 (key, 分组编号+分组内偏移量)。因为 key 和 value 都是大小固定的,因此偏移量只需要保存它是分组内的第几个 kv 对,所以只需要 4 字节。因此每个 entry 的空间占用为 key 8字节加分组编号 4 字节加偏移量 4 字节,总共 16 字节,那么 6400 万个 entry 一共需要大约 1G 左右空间,因此可以完全保存在内存里。我们实现了一个原子的 hash 表。hash 表底层是一个大小为 8000 万左右的数组,以线性探测法处理 hash 冲突。说hash表的每一个 entry 由两个 64 位无符号整数组成,分别代表 key 与索引,并初始化为0。因为 key 本身可能是0,所以我们让分组编号从1开始,然后对索引部分的原子变量做 cas 操作来实现原子代入。代码如下:因为计算节点本身是无状态的,所以重启时需要恢复索引。我们把索引恢复的时机放在第一次 get 操作的时候,这样可以避免写入阶段计算节点请求恢复索引。索引恢复由 16 个线程并行执行,每一个线程向存储节点请求一整个 key 文件,因为 key 在 key 文件中的位置与 value 在 value 文件中的位置是一样的,所以遍历 key 文件就可以重建索引了。如果每次 get 都去存储节点读取,那么网络延迟开销太大,无法打满带宽。所以我们每次向存储节点请求数据时都读取一整块数据保存在内存 cache 里,大小为 10MB。由于我们不确定是不是完全按照 10MB 对齐的,因此我们为每个分组准备了两个 cache,这样当 cache miss 时,可以保证前一块 cache 的数据一定已经被读完了,可以被新的 cache 置换掉。此外我们还实现了 cache 预取机制。由于赛题要求顺序读取及热点区间读取,热点区间读取是按照数据的写入顺序以 10MB 数据粒度分区,分区逆序推进,在每个 10MB 数据分区内随机读取,所以当向存储节点请求一个新 cache 完成时,另一个线程会根据当前两个 cache 的 offset 之差向存储节点预取下一块 cache。在顺序读取阶段差为整数,因此会顺序预取,在热点区间读取阶段差为负数,因此会逆序预取。总结我们的最好成绩是 786 秒,其中写用了 505 秒,顺序读取用了 134 秒,热点读取用了 147 秒,最终排名第 5 名。此外各阶段分别跑出的最佳成绩为505/132/142秒,可惜我们对读操作的优化是最后一天完成的,但是最后一天的评测环境不太好,写性能一直上不去(我们的写入时间基本稳定在520秒以内,但是最后一天一直在560秒+)。在热点读阶段实际消耗在读取上的时间为133秒,但是恢复索引使用了9 秒,这里还有很大的改进空间。考虑到评测系统的带宽大约15Gbps,我们的读取性能已经基本跑满网络带宽了。第一次参加这种类型的比赛,我们学习和积累了很多经验,希望来年还有机会参加,取得更好的成绩。 作者:ADSL
  • [调优工具] 性能监控Nmon工具:可以生成excel图表,很方便
    一、Nmon介绍(这句不用看——Nmon得名于 Nigel 的监控器,是IBM的员工 Nigel Griffiths 为 AIX 和 Linux 系统开发的,使用 Nmon 可以很轻松的监控系统的 CPU、内存、网络、硬盘、文件系统、NFS、高耗进程、资源和 IBM Power 系统的微分区的信息)。Nmon是一款计算机性能系统监控工具,因为它免费,体积小,安装简单,耗费资源低,广泛应用于AIX和Linux系统。二,Nmon下载首先查看Linux系统内核版本(两种方式):1,cat /proc/version [qgc@localhost nmon16d]$ cat /proc/versionLinux version 2.6.32-431.el6.x86_64 (mockbuild@c6b8.bsys.dev.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) ) #1 SMP Fri Nov 22 03:15:09 UTC 20132,uname -a[qgc@localhost nmon16d]$ uname -aLinux localhost.localdomain 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 03:15:09 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux对照上图,我是VMware虚拟机安装的64位centos6.5。接着下载nmon软件包:1,wget方式下载,地址:  https://nchc.dl.sourceforge.net/project/nmon/nmon16d_x86.tar.gz[qgc@localhost nmon16d]$ wget https://nchc.dl.sourceforge.net/project/nmon/nmon16d_x86.tar.gz--2018-08-27 11:26:11--  https://nchc.dl.sourceforge.net/project/nmon/nmon16d_x86.tar.gzResolving nchc.dl.sourceforge.net... 211.79.60.17, 2001:e10:ffff:1f02::17Connecting to nchc.dl.sourceforge.net|211.79.60.17|:443... connected.HTTP request sent, awaiting response... 200 OKLength: 9252938 (8.8M) [application/x-gzip]Saving to: “nmon16d_x86.tar.gz” 100%[===========================================================>] 9,252,938   1.77M/s   in 5.0s     2018-08-27 11:26:17 (1.77 MB/s) - “nmon16d_x86.tar.gz” saved [9252938/9252938] [qgc@localhost nmon16d]$ lsnmon16d_x86.tar.gz 2,官网手动下载,地址:http://nmon.sourceforge.net/pmwiki.php?n=Site.Downlo ,翻译了下,如下图因为CentOS就是 RedHat 的免费版本(上面内核版本 2.6.32-431.el6.x86_64),所以对应系统是RHEL 6.5,选择右边包含RedHat 6.5的下载。 三,解压安装下载完成后,可以新建一个目录,作为解压后存放的目录,这里为nmon16d,如果是手工下载的,需要拷贝到虚拟机。[qgc@localhost Desktop]$ mkdir nmon16d输入解压命令:tar -zxvf nmon16d_x86.tar.gz -C nmon16d ,-C 是指定解压目录[qgc@localhost Desktop]$ tar -zxvf nmon16d_x86.tar.gz  -C nmon16dnmon_power_32_linuxnmon_power_32_rhel6nmon_power_32_sles11nmon_power_64_kvm2nmon_power_64_linux...在nmon6d目录中,可以找到nmon_x86_64_centos6这个文件,并对它添加执行权限[qgc@localhost nmon16d]$ chmod +x nmon_x86_64_centos6再把该程序文件加入环境变量运行目录,如果不是管理员,需要加上sudo命令[qgc@localhost nmon16d]$ mv nmon_x86_64_centos6 /usr/local/bin/nmonmv: cannot move `nmon_x86_64_centos6' to `/usr/local/bin/nmon': Permission denied[qgc@localhost nmon16d]$ sudo mv nmon_x86_64_centos6 /usr/local/bin/nmon[sudo] password for qgc: [qgc@localhost nmon16d]$ 四,运行Nmon输入nmon命令启动Nmon,如下图在上面的交互式窗口中,可以使用nmon 快捷键来显示不同的系统资源统计数据:·        q : 停止并退出 Nmon·        h : 查看帮助·        c : 查看 CPU 统计数据·        m : 查看内存统计数据·        d : 查看硬盘统计数据·        k : 查看内核统计数据·        n : 查看网络统计数据·        N : 查看 NFS 统计数据·        j : 查看文件系统统计数据·        t : 查看高耗进程·        V : 查看虚拟内存统计数据·        v : 详细模式按下c,m,d后如下图,CPU、内存、磁盘的消耗情况都直观的展现出来了,按q键可以退出。五,数据采集性能测试时,需要根据测试场景的执行情况,分析一段时间内系统资源的变化,这时需要nmon采集数据并保存下来,以下是常用的参数:      -f 参数:生成文件,文件名=主机名+当前时间.nmon     -T 参数:显示资源占有率较高的进程     -s 参数:-s 10表示每隔10秒采集一次数据     -c 参数:-s 10表示总共采集十次数据     -m 参数:指定文件保存目录具体的参数学习可参考博客:https://www.cnblogs.com/iclk/p/4456311.html如每隔5秒采集一次,一共采集12次,就是1分钟的数据(生成的文件已标红):[qgc@localhost nmon16d]$ nmon -f -s 5 -c 12 -m /home/qgc/Desktop/[qgc@localhost nmon16d]$ nmon -f -T -s 5 -c 12 -m /home/qgc/Desktop/[qgc@localhost Desktop]$ lslmon16g.c                   localhost_180827_2318.nmon  nmon16d_x86.tar.gzlocalhost_180827_2317.nmon  nmon16d                     Test数据采集完毕后,如需关闭nmon进程,需要获取nmon的pid(已标红)[qgc@localhost Desktop]$ ps -ef | grep nmonqgc        4455(pid)   4349(ppid)  0 23:40 pts/0    00:00:00 nmonqgc        4491   4429  0 23:40 pts/1    00:00:00 grep nmon再安全杀掉该进程:kill -9 pid[qgc@localhost Desktop]$ kill -9 4455[qgc@localhost Desktop]$ ps -ef | grep nmonqgc        4493   4429  0 23:40 pts/1    00:00:00 grep nmon六,数据分析1,下载nmon analyser借助nmon analyser可以把nmon采集的数据生成直观的Excel表,nmon analyser可以在IBM的官网下载,https://www.ibm.com/developerworks/community/wikis/home?lang=en#!/wiki/Power+Systems/page/nmon_analyser 在windows上下载后解压,有word和exce两个文档,Word是说明文档,包括更新日志,详细参数等,其中的Excel就是nmon analyser工具了。2,打开nmon analyser双击打开nmon analyser v54.xlsm,点击Analyze nmon data按钮: 注:因为我用的个人免费版WPS(10.1),没有包含宏,需要安装宏插件(VBA for WPS),Excel是自带宏插件的,如果宏不能运行,需要做以下操作:工具 -> 宏 -> 安全性 -> 中,然后再打开文件并允许运行宏。 3,下载VBA for WPS地址:https://pan.baidu.com/s/1QzW4ebQxYQtxgVfkTmxVJw,下载VBA7.0.1590_For WPS(中文).exe后,先退出WPS,再直接安装就行,再次打开nmon analyser,启用宏4,使用nmon analyser生成图表成功打开nmon analyser后,点击Analyze nmon data按钮,选择nmon数据文件,会再次提示另存为,选择地址保存即可。  下图就是nmon生成的数据报告截图,底部可以切换其他资源信息。    References:1.       https://www.cnblogs.com/qgc1995/p/7523786.html2.       http://nmon.sourceforge.net/pmwiki.php
  • 如何定制服务消费者的发送线程池?
    A服务向B服务发消息,采用CSE同步restTemplate方式,使用jmeter压测,一分钟500次,故意让B服务延时几秒后返回响应,A服务会报大量的超时错误(设置了30秒超时),看默认的发送线程貌似只有几个。如何定制发送的线程池大小呢?
  • [热门活动] 进程造成容器dump
    Jul 30 14:54:31 huawei kernel: Pid 21310(fcst_cal) over core_pipe_limit Jul 30 14:54:31 huawei kernel: Skipping core dump Jul 30 14:54:31 huawei kernel: Pid 21311(clean_db) over core_pipe_limit Jul 30 14:54:31 huawei kernel: Skipping core dump,容器在运行过程中发生如上问题,请问如何引起?如何处理?
  • [问题求助] CSE provider线程配置
    provider侧microservice.yaml中有线程配置,容器(tomcat)自身也有线程配置,最终线程数的上限是由什么决定的?
  • [已解决问题归档] 【入驻式求助】动态库coredump
    【问题简要】动态库coredump 的问题【问题类别】vxml2.0 【IPCC解决方案版本】IPCC V200R001C80【问题现象描述】您好!我们的测试流程大概如下:语音识别获取结果 id 后,使用结果 id 去调用动态库,测试现象为 ivr trace 日志卡死崩溃,我们参考文档初步排查为动态库coredump 的问题。关键日志如下:附件 mds.log 的 137 行「进程未启动或者进程意外退出」。附件 ivr301.log 的 12 行「callinginstance[100] accesscode:777 flow:/home/icd/icddir/bin/Y:/flow/vxml/temp.jsp get sig[11] in dlg:use_dll 」。ivr 服务器的「/home/icd/icddir/corebak」目录下有 core 文件生成。在 gdb 提示符环境下,输入 where 回车看到Core was generated by `/home/icd/icddir/bin/ivr -p 301'.Program terminated with signal 6, Aborted.#0  0xffffe430 in __kernel_vsyscall ()(gdb) where#0  0xffffe430 in __kernel_vsyscall ()#1  0xf6347e30 in raise () from /lib/libc.so.6#2  0xf6349765 in abort () from /lib/libc.so.6#3  0x082c3efa in ProgramExitSIGDeal (sig=11) at ../scp/ntscp.cpp:285#4 #5  0x00000000 in ?? ()#6  0xf71f1dba in CObj::Obj_UseDll (this=0xd813ed8) at ../vxml/Obj.cpp:9982#7  0xf7214137 in CObj::Visit (this=0xd813ed8) at ../vxml/Obj.cpp:1883#8  0xf70ce634 in CForm::Collect (this=0xd82c9a8, pItem=0xd813edc) at ../vxml/Form.cpp:700#9  0xf70f486f in CInterpreter::Collect (this=0xa0b1e70) at ../vxml/Interpreter.cpp:474#10 0xf70ff1a6 in CInterpreter::Go (this=0xa0b1e70) at ../vxml/Interpreter.cpp:1136#11 0xf71040d4 in CInterpreter::Run (this=0xa0b1e70, sUrl=0xd58fc11 "/home/icd/icddir/bin/Y:/flow/vxml/temp.jsp", usRetCode=@0xdc0db302: 65535) at ../vxml/Interpreter.cpp:2541#12 0xf71042c7 in CInterpreter::VXMLRun (this=0xa0b1e70, pUrl=0xd58fc11 "/home/icd/icddir/bin/Y:/flow/vxml/temp.jsp", usRetCode=@0xdc0db302: 65535,    dwGetFileTime=@0xdc0db2dc: 0, bGetFileSuccessFalg=@0xdc0db2d8: 0) at ../vxml/Interpreter.cpp:2389#13 0x0812988d in CCallingInstance::ThreadProc (lpParam=0xa0c01e8) at ../icd_files/icd_callapp.cpp:1478#14 0xf6491809 in start_thread () from /lib/libpthread.so.0#15 0xf63f63be in clone () from /lib/libc.so.6问题:如何继续排查和解决这个问题?源码和日志已发附件。
  • [中间件] 【鲲鹏翱翔】消息中间件04-ELK6.2.3环境搭建指南
    1.   简介ELK是集中日志解决方案的一种,具有收集、传输、存储、分析日志的功能。ELK分别是Elasticsearch、Logstash和Kibana三个开源软件的首字母缩写。Elasticsearch 是一个实时的分布式搜索和分析引擎,它可以用于全文搜索,结构化搜索以及分析。它是一个建立在全文搜索引擎 Apache Lucene 基础上的搜索引擎,使用 Java 语言编写。Logstash 的作用就是一个数据收集器,将各种格式各种渠道的数据通过它收集解析之后格式化输出到 Elasticsearch。Kibana 是一款基于 Apache 开源协议,使用 JavaScript 语言编写,为 Elasticsearch 提供分析和可视化的 Web 平台。它可以在 Elasticsearch 的索引中查找,交互数据,并生成各种维度的表图。类别:中间件2.   部署环境Packet NameVersion备注EulerEuler 2.8与Centos二选一CentOSCentOS 7.5 with ARM与Euler二选一Elasticsearch6.2.3Logstash6.2.3Kibana6.2.3整个环境在一台服务器部署    3.   安装部署    3.1 约束说明 & 配置华为官方镜像源Kibana 的版本需要和 Elasticsearch 的版本一致,这是官网的配置要求,可参考网址:https://www.elastic.co/guide/cn/kibana/current/setup.html 配置华为官方镜像源Centos7.5 mkdir -p /etc/yum.repos.d/repo_bak/ mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/repo_bak/ curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.myhuaweicloud.com/repo/Centos-altarch-7.repo yum makecacheEuler2.8 wget http://mirrors.myhuaweicloud.com/repo/mirrors_source.sh && sh mirrors_source.sh     3.2  JDK安装Centos7.5 yum -y install java-1.8.0-openjdk java-1.8.0-openjdk-develEuler2.8#Euler2.8 没有找到java-1.8.0-openjdk-devel, yum search openjdk yum -y install java-1.8.0-openjdk源码安装java(option)#下载tar包wget http://openjdk.linaro.org/releases/jdk8u-server-release-1803.tar.xz#解压tar -jxvf jdk8u-server-release-1803.tar.xz -C /usr/lib/jvm#创建软连接ln -sf /usr/lib/jvm/jdk8u-server-release-1803/bin/java /etc/alternative/java #配置环境变量vi /etc/profile.d/java.sh-------------------java.sh---------------#JAVA_HOMEexport JAVA_HOME=/usr/lib/jvm/jdk8u-server-release-1803export JRE_HOME=$JAVA_HOME/jreexport JAVA=$JAVA_HOME/binexport CLASSPATH=.:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jarexport PATH=$PATH:${JAVA_HOME}/bin--------------------------------------------source /etc/profile.d/java.sh#查看java版本java -version      3.3 安装部署Elasticsearch6.2.3 wget  https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.2.3.tar.gz tar xvf elasticsearch-6.2.3.tar.gz -C /opt && cd /opt# 添加用户,elasticsearch不允许使用root执行 groupadd elastic && useradd elastic -g elastic -p elastic chown -R elastic:elastic elasticsearch-6.2.3 su elastic cd elasticsearch-6.2.3# 后台启动elasticsearch ./bin/elasticsearch &# 验证 curl 127.0.0.1:9200{ "name" : "Surge", "cluster_name" : "elasticsearch", "version" : { "number" : "2.1.0", "build_hash" : "72cd1f1a3eee09505e036106146dc1949dc5dc87", "build_timestamp" : "2015-11-18T22:40:03Z", "build_snapshot" : false, "lucene_version" : "5.3.1" }, "tagline" : "You Know, for Search"}备注:如果遇到如下错误ERROR: [2] bootstrap checks failed[2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]# 切换至root用户,进行操作# 临时生效# elasticsearch需要在内存中保存索引,max_map_count文件包含限制一个进程可以拥有的VMA(虚拟内存区域)的数量. 防止elasticsearch抛出内存不足的异常。 sudo sysctl vm.max_map_count=262144 [1]: max file descriptors [65535] for elasticsearch process is too low, increase to at least [65536]# centos7.5 执行如下命令,退出ssh或者重启生效 vi /etc/security/limits.conf# 在文件末尾添加* soft nofile 65536      #限制(软)进程数量* hard nofile 131072     #限制(硬)进程数量# Euler2.8执行如下命令,退出ssh或者重启生效# 限制资源使用的配置文件是 /etc/security/limits.conf,和/etc/security/limits.d/目录,/etc/security/limits.d/里面配置会覆盖/etc/security/limits.conf的配置 vi /etc/security/limits.d/huawei-nofile.conf# 在文件末尾添加* soft nofile 65536* hard nofile 65536部署Kibana6.2.3 wget https://artifacts.elastic.co/downloads/kibana/kibana-6.2.3-linux-x86_64.tar.gz tar xvf kibana-6.2.3-linux-x86_64.tar.gz -C /opt rm -rf /opt/kibana-6.2.3-linux-x86_64/node# 直接下载的Kibana是x86的,需要将node组件换成arm版的运行 wget https://nodejs.org/dist/v8.11.3/node-v8.11.3-linux-arm64.tar.xz tar xvf /opt/node-v8.11.3-linux-arm64.tar.xz -C /opt/kibana-6.2.3-linux-x86_64 cd /opt/kibana-6.2.3-linux-x86_64 && mv node-v8.11.3-linux-arm64 node# 修改配置文件 vi config/kibana.yml# 配置成任意IP可以访问,生产环境建议为特定IPserver.host: "0.0.0.0"# 后台启动kibana ./bin/kibana &访问http://IP:5601部署Logstash6.2.3 wget https://artifacts.elastic.co/downloads/logstash/logstash-6.2.3.tar.gz tar xvf logstash-6.2.3.tar.gz && cd logstash-6.2.3# 解压完成,早config目录下新建一个logstash.conf# 然后做好input ,filter,output三大块, 其中input是吸取logs文件下的所有log后缀的日志文件,filter是一个过滤函数,这里不用配置,output配置了导入到# hosts为127.0.0.1:9200的elasticsearch中,每天一个索引。 vi  config/logstash.confinput {     file {        type => "log"        path => "/logs/*.log"        start_position => "beginning"    }} output {  stdout {   codec => rubydebug { }  }   elasticsearch {    hosts => "127.0.0.1"    index => "log-%{+YYYY.MM.dd}"  }}# 启动logstash ./bin/logstash -f ./config/logstash.conf开启另一个ssh终端,往日志文件目录中写入数据 mkdir -p /logs cd /logs echo 'hello world' > 1.log      3.4  验证在浏览器中访问http://IP:/5601,可以打开kibana页面,                                                选择Management标签,在index-name-*框中输入log*,选择Time-field name 为@timestamp 点击Discover,就可以看见内容了3.5 参考信息[1] 通过ELK快速搭建一个你可能需要的集中化日志平台:https://www.cnblogs.com/huangxincheng/p/7918722.html4.   FAQ4.1 点击Management标签时显示如下界面说明数据源不存在,在logs目录下增加相应日志 mkdir -p /logs cd /logs echo 'hello world' > 1.log
  • [分享交流] TensorFlow 队列与线程
    TensorFlow 队列与线程深度学习的模型训练过程往往需要大量的数据,而将这些数据一次性的读入和预处理需要大量的时间开销,所以通常采用队列与多线程的思想解决这个问题,而且TensorFlow为我们提供了完善的函数。本文介绍了TensorFlow的线程和队列。在使用TensorFlow进行异步计算时,队列是一种强大的机制。正如TensorFlow中的其他组件一样,队列就是TensorFlow图中的节点。这是一种有状态的节点,就像变量一样:其他节点可以修改它的内容。具体来说,其他节点可以把新元素**到队列后端(rear),也可以把队列前端(front)的元素删除。在Python中是没有提供直接实现队列的函数的,所以通常会使用列表模拟队列。而TensorFlow提供了整套实现队列的函数和方法,在TensorFlow中,队列和变量类似,都是计算图上有状态的节点。操作队列的函数主要有:l  FIFOQueue():创建一个先入先出(FIFO)的队列 l  RandomShuffleQueue():创建一个随机出队的队列 l  enqueue_many():初始化队列中的元素 l  dequeue():出队 enqueue():入队与队列Queue有关的有以下三个概念:l  Queue是TF队列和缓存机制的实现l  QueueRunner是TF中对操作Queue的线程的封装l  Coordinator是TF中用来协调线程运行的工具虽然它们经常同时出现,但这三样东西在TensorFlow里面是可以单独使用的,不妨先分开来看待。1.    Queue,队列根据实现的方式不同,分成具体的几种类型,例如:l  tf.FIFOQueue 按入列顺序出列的队列l  tf.RandomShuffleQueue 随机顺序出列的队列l  tf.PaddingFIFOQueue 以固定长度批量出列的队列l  tf.PriorityQueue 带优先级出列的队列l  ...这些类型的Queue除了自身的性质不太一样外,创建、使用的方法基本是相同的,以下介绍两个最常用的1)  tf.FIFOQueue(capacity, dtypes, name='fifo_queue') 创建一个以先进先出的顺序对元素进行排队的队列参数:capacity:整数。可能存储在此队列中的元素数量的上限dtypes:DType对象列表。长度dtypes必须等于每个队列元 素中的张量数,dtype的类型形状,决定了后面进队列元素形状方法:q.dequeue()获取队列的数据q.enqueue(值)将一个数据添加进队列q.enqueue_many(列表或者元组)将多个数据添加进队列q.size() 返回队列的大小2)、tf.RandomShuffleQueue() 随机出的队列Queue主要包含入列(enqueue)和出列(dequeue)两个操作。enqueue操作返回计算图中的一个Operation节点,dequeue操作返回一个Tensor值。Tensor在创建时同样只是一个定义(或称为“声明”),需要放在Session中运行才能获得真正的数值。下面是一个单独使用Queue的例子:#模拟同步操作   ''' 1.创建一个空的队列 2.向队列设置几个初始的值 3.从队列中取出一个数据,再将数据加1 4.数据加1之后的结果进队列 5.打印出队列中的数据 '''   def sync_test():     #创建一个空的队列     q = tf.FIFOQueue(3,tf.float32)     #向队列设置几个初始的值     enq_many = q.enqueue_many(([0.1,0.2,0.3],))       #从队列中取出一个数据,再将数据加1     out_q = q.dequeue()     data = out_q+1     #数据加1之后的结果进队列     en_q = q.enqueue(data)     with tf.Session() as sess:         sess.run(enq_many)           #执行en_q op 99次         for i in range(99):             sess.run(en_q)           #打印出队列中的数据         for i in range(q.size().eval()):             print(q.dequeue().eval())     return None  2.     QueueRunner,队列管理器tf.train.QueueRunner(queue, enqueue_ops=None)参数:l  queue:A Queuel  enqueue_ops:添加线程的队列操作列表,[]*2,指定两个线程l  create_threads(sess, coord=None,start=False) 创建线程来运行给定会话的入队操作l  start:布尔值,如果True启动线程;如果为False调用者 必须调用start()启动线程l  coord:线程协调器  用于线程的管理Tensorflow的计算主要在使用CPU/GPU和内存,而数据读取涉及磁盘操作,速度远低于前者操作。因此通常会使用多个线程读取数据,然后使用一个线程消费数据。QueueRunner就是来管理这些读写队列的线程的。QueueRunner需要与Queue一起使用(这名字已经注定了它和Queue脱不开干系),但并不一定必须使用Coordinator。看下面这个例子:import tensorflow as tf   import sys q = tf.FIFOQueue(10, "float") counter = tf.Variable(0.0)  #计数器 # 给计数器加一   increment_op = tf.assign_add(counter, 1.0)   # 将计数器加入队列   enqueue_op = q.enqueue(counter)     # 创建QueueRunner # 用多个线程向队列添加数据 # 这里实际创建了4个线程,两个增加计数,两个执行入队   qr = tf.train.QueueRunner(q, enqueue_ops=[increment_op, enqueue_op] * 2)     # 主线程   sess = tf.InteractiveSession() tf.global_variables_initializer().run()   # 启动入队线程   qr.create_threads(sess, start=True)   for i in range(20):     print (sess.run(q.dequeue()))增加计数的进程会不停的后台运行,执行入队的进程会先执行10次(因为队列长度只有10),然后主线程开始消费数据,当一部分数据消费被后,入队的进程又会开始执行。最终主线程消费完20个数据后停止,但其他线程继续运行,程序不会结束。3.    Coordinator,线程协调器tf.train.Coordinator() 线程协调员,实现一个简单的机制来协调一 组线程的终止方法: 返回的是线程协调实例            request_stop()  请求停止            join(threads=None, stop_grace_period_secs=120) 等待线程终止Coordinator是个用来保存线程组运行状态的协调器对象,它和TensorFlow的Queue没有必然关系,是可以单独和Python线程使用的。例如:#模拟异步操作   '''   通过队列管理器来实现变量加1,入队,主线程出队列的操作,观察效果? (异步操作) '''   def async_test():     # # 1、定义一个队列,1000     q = tf.FIFOQueue(300,tf.float32)       # 2、定义要做的事情 循环值,+1, 放入队列当中     var = tf.Variable(0.0)     # 实现一个自增  tf.assign_add     data = tf.assign_add(var,tf.constant(1.0))     en_q = q.enqueue(data)       # 3、定义队列管理器op, 指定多少个子线程,子线程该干什么事情     # 1,2,3,4,5,6,7,8,9,10     qr = tf.train.QueueRunner(q, enqueue_ops=[en_q] * 2)       # 初始化变量的OP     init_op = tf.global_variables_initializer()       with tf.Session() as sess:         #运行初始化op         sess.run(init_op)         # 开启线程管理器,coord线程协调器         coord = tf.train.Coordinator()         # 真正开启子线程,读数据入队列,t1,t2  0.0001  10         threads = qr.create_threads(sess, coord=coord, start=True)           # 主线程,不断读取数据训练  t0         for i in range(300):             #             print(sess.run(q.dequeue()))         print("size==>",sess.run(q.size()))         # 停止子线程,回收         coord.request_stop()         coord.join(threads)       return None 将这个程序运行起来,主线程会等待所有子线程都停止后结束,从而使整个程序结束。由此可见,只要有任何一个线程调用了Coordinator的request_stop方法,所有的线程都可以通过should_stop方法感知并停止当前线程。将QueueRunner和Coordinator一起使用,实际上就是封装了这个判断操作,从而使任何一个现成出现异常时,能够正常结束整个程序,同时主线程也可以直接调用request_stop方法来停止所有子线程的执行。 更多的技术内容,请访问腾科it教育集团网站 www.togogo.net
  • [大咖交流] idou老师教你学Istio 29:Envoy启动流程
    1. 功能概述Envoy启动时,会启动一个进程,并在这个进程中启动很多线程,这样,可以启动很多worker线程,一般worker线程数与核心数相同,每个worker线程处理所有已配置的listener上的请求,管理连接并处理filterchain,非阻塞;同时,在这个进程中会启动一个主线程,它负责启动和停止envoy,也是通过API提供配置管理的线程,同时它收集不同的指标,管理其它线程,也是非阻塞的。2. 重要数据结构定义2.1 Filter过滤器,包括listener filter、network filter和http filter。Listener filter可以用于操作连接元数据,在新接收的套接字上进行操作,例如获取原始目的地址,重定向连接等;network filter主要负责数据读写;http filter主要负责数据处理。2.2 Listener监听器,envoy中支持在每个线程中配置任意数量的监听器,每个监听器独立配置一定数量的network filter,也可以选择性的配置listener filter,listener filter在连接建立之前处理,network filter在连接建立之后处理。2.3 Worker一个worker对应一个envoy的执行线程,将listener绑定在worker上,worker负责监听、过滤和转发,每个连接的生命周期会绑定在一个单独的worker上,通常情况下,envoy实现了100%的非阻塞。3. 代码流程3.1 流程概述Envoy启动时,首先启动主线程,在主线程中对listener和filter进行初始化操作,然后将listener绑定到worker上,并由主线程拉起worker线程,由worker线程负责监听新连接。3.2 初始化3.2.1 main入口main函数是envoy启动的总入口,首先生成main_common,用于后面的初始化。3.2.2 初始化main_common在main_common里面会生成maincommonbase,它会做server instance的初始化,一个instance是一个服务的实例.3.2.3 Instance初始化在maincommonbase里调用InstanceImpl函数后,首先对启动携带的配置信息进行注册,然后执行instance的初始化。Instance的初始化包括两部分:① 将当前instance注册到ListenerManager,来管理更新;② 创建并初始化MainImpl,MainImpl用来初始化监听listener;MainImpl根据配置文件获取静态监听listener列表,将它们实例化并注册到ListenerManager。3.2.4 初始化listener对于每个静态listener,根据配置文件为它创建ListenerFilterFactoryList,并根据配置为它添加ListenerFilterFactory。listener filter有三个:original dst filter,proxy protocol filter, TLS inspector filter,一一按照配置判断是否加入ListenerFilterFactoryList。配置ListenerFilterFactoryList的同时,也会根据配置为这个listener创建NetworkerFilterFactoryList,供后续建立在这个listener上的连接使用。3.3 启动3.3.1 启动入口在main_common初始化正常完成后,执行main_common→run()启动,从而后续执行instance的run()方法,在instance的run()方法,会执行网络级别上的listener初始化。3.3.2 启动worker,将listener绑定到worker上此处,会将从配置文件读取的所有listener绑定到所有的worker上,worker是服务的并发线程,数目一般和核心数相同,将listener绑定到worker上后会通过connectionhandler模块将其初始化。3.3.3 Listener初始化Listener的初始化过程首先生成ActiveListener,通过ActiveListener调用network包内的创建函数来对listener进行网络级别的初始化。3.3.5 启动worker线程,进入监听Listener绑定在worker上,当listener初始化完成后,需要启动worker服务才能真正进入监听流程。此处,为每个worker启动新线程,并调用libevent的event_base_loop进入监听,等待连接事件到达触发后,回调onAccept进入处理流程。4. 总结本文从程序入口main函数开始,分析了envoy如何启动,以及如何对listener、worker这些核心数据结构进行初始化,并详细阐述了从envoy主线程启动到worker线程进入监听行为的全过程。相关服务请访问https://support.huaweicloud.com/cce/index.html?cce_helpcenter_2019
  • [技术干货] 一个关于优雅停机关闭线程池的代码问题
    代码最后https://github.com/apache/servicecomb-java-chassis/blob/master/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java为什么只是shutdown了,而没有awaitTermination,这样不就无法保证线程池里面的任务一定能执行完成了吗?@Override     public void close() {        for (ExecutorService executorService : executorList) {           executorService.shutdown();        }        executorList.clear();     }
  • [产品体验官] 【华为开源镜像站产品体验官评测】 From 守护进程
    华为开源镜像站产品体验官评测        测试环境网络:WiFi(峰值1.7M/s,平均峰值1.5M/s)系统:Centos 7测试地点:深圳隔壁前言       最近了解华为在公测镜像站,我抱着试试的态度报名了评测,下面是我的一些测试数据和体验。首先,进入华为官方镜像站,映入眼帘的是50+的镜像,系统镜像,系统源和软件源,范围之广令人惊喜,最令我意外的是华为镜像有一些开源的软件镜像和EPEL;网站提供了快捷方便的系统下载,也提供了一些脚本方便用户替换。       速度测试(按首字母排序)        结果令人惊喜,不知道是不是开了个物理外挂,ε=ε=ε=┏(゜ロ゜;)┛        在接下来的使用中,我尝试下载一些大包(准确的来说是一堆包),速度可以到达平均峰值(1.5M/s),但是也会出现掉速(700K/s+),我很满意这个速度,某些源能掉到200K+,更有的掉到20K+,慢到怀疑人生。槽点        1.刚开始登陆这个网站,试了试华为的账号以及相关账号,到后面才发现,要重新注册。。。。。。          2.换软件源代码:刚开始我用网站上提供的命令去替换我的源,结果失败,后来检查发现我用的不是官方的源,我之前换过,后来换回官方的源再次敲命令才成功生效。建议        1.建议打通账号信息,方便用户上手。         2.换关键字可能会出现错误,建议官方提供源文件直接让用户下载替换文件,或提供一键替换的脚本降低上手难度和出错概率。         3.既然来了镜像站,又了解了相关操作方式,买台服务器玩玩?总结        通过这次的评测,我挺惊喜的,这个速度就不用让我们浪费更多的时间在下载安装包上面,也让我看到了华为云的应用与实力,这个源我也会推荐给我的同学以及考了华为认证的小伙伴,希望华为能给我们带来更多的惊喜,也祝华为云越来越好。
总条数:813 到第 页
上滑加载中