关于性能:性能优化下组织结构同步优化二全量同步增量同步断点续传实现方式

看到这一篇文章的 xdm ,应该对组织构造同步有一些想法了吧,如果没有,能够看后面两篇文章,能够通过如下地址查看一下: 【性能优化上】第三方组织构造同步优化一,你 get 到了吗?<!----> 坑爹,线上同步近 3w 个用户导致链路阻塞引入发的线上问题,你经验过吗?这类文章,次要是冀望能给 xdm 带来不一样的思考,如有表述不当的中央,还请不吝赐教,冀望对你有帮忙 这篇文章次要是论述将长期表中的用户组数据/用户数组,依照既定的步骤同步到咱们的正式表,过程中遇到异常中断,能够对咱们的正式平台无影响,可能保障下一次同步工作过去依然能够进行断点续传 首先全量同步和增量同步别离指什么? 全量同步简略了解,全量同步,咱们就是将对方所有的数据,全副同步到咱们外部零碎中,对于组织构造同步的时候,咱们没有必要每一次都是全量的,个别是第一次,无到有的时候会用到全量同步,能够了解为全量笼罩 增量同步那么增量同步就比拟好了解了,此处的增量同步指的是,第三方数据对于目前外部零碎数据来说,哪一些是减少或者变动的数据,那么就同步这一部分数据到外部零碎中 那么对于咱们本次同步组织构造来说,就看外部零碎是否曾经存在了 /IDaaS 组,如果存在了,那么就走增量同步,如果不存在,则走全量同步 全量同步根本流程全量同步的根本流程比较简单,再来回顾一下之前文章的一张总体图 能够看到全量同步和增量同步在咱们整个同步流程的第四个阶段,到这个阶段的时候,第三方组织构造的数据曾经全副正确的写入到了咱们的长期表中 这个时候,咱们就须要将长期表中的数据依照咱们的逻辑和步骤写入到正式表中 此处阶段,显示判断是否有 /IDaaS 组,如果没有,则在同步记录表中写入 同步类型为 full 全量同步,如果有 /IDaaS 组,则记录同步类型为 incr 增量同步 全量同步比较简单,总共分成两个阶段,一个阶段是全量同步组 full_sync_group,一个是全量同步用户 full_sync_user 序号步骤含意1full_sync_group全量同步长期表中的组到正式表2full_sync_user全量同步长期表中的用户到正式表此处比较简单,同步用户之前,天然是先要将组给同步过去,齐全分分明,对于正式表中,数据是从无到有,所以步骤绝对就简略一些 开始全量同步 在进行全量同步前,依然还是查看以后的同步状态是否是 sync_in,且同步步骤是否是sync_temp_user,若不是则不解决 查看用户数量是否超过平台最大限度 若过程中呈现 error,则敞开当前任务,不进行同步,并且将同步记录中同步状态设置为同步中断 sync_interrupt,同步记录表中重试次数 +1查看长期表无效用户 + 已有正式表中未删除用户的数量是否超过平台最大限度(个别平台会有对于一个租户最多包容多少用户的限度),更新同步状态为同步失败 sync_fail,并且清空长期数据,告诉其余服务解决失败,且敞开当前任务<!----> 校验以后同步步骤是 sync_temp_user 或者 full_sync_group ,则开始正式将长期表的组信息同步到正式表中,并将以后的同步步骤批改为 full_sync_group这次这样进行判断,如果是 sync_temp_user 阐明第一次解决到这里,如果是 full_sync_group 步骤,阐明这个步骤之前被中断了,此刻须要断点续传获取长期表中的组深度,且获取依照深度排序的组列表依照由浅到深的将组数据写入到正式表中删除长期用户表如果过程中呈现 error,则在该租户的同步记录中,同步状态标记为 sync_interrupt当同步步骤是 full_sync_group 或者 full_sync_user 的时候,则开始将用户从长期表退出到正式用户表中,且将同步步骤批改为 full_sync_user同理,此处这样的解决逻辑,也是为了断点续传,逻辑之外,对于一个步骤中数据库的解决都是开启事务的一层一层的去增加用户,先从长期表中查问同一个深度下对应的所有用户从正式表中读取曾经存在的用户从长期表中依照例如 1000 条每次去读取数据(无效非法用户),写到到正式表中,校验如果用户曾经存在于正式表中,则记录抵触用户,且不录入该用户,反之亦然删除长期表中曾经插入到正式表中的用户数据,并在长期表中更新指定用户是非法的如果过程中呈现 error,则在该租户的同步记录中,同步状态标记为 sync_interrupt同步完结,则将同步状态设为 sync_success ,同步步骤设置为 sync_end,同时将长期表中非法的组,非法的用户全副读书进去,将非法数据传出去最终革除长期用户组表,和长期用户表 ,在 redis 中记录下一次须要同步的工夫增量同步根本流程增量同步的话,绝对步骤就会多一些,看起来可能会感觉简单,实际上依照如下步骤走的话,会很清晰并不简单 ...

September 20, 2023 · 1 min · jiezi

关于性能:性能优化上第三方组织结构同步优化一分状态分步骤的设计你-get-到了吗

在工作中,云产品之间天然少不了各种零碎的对接,零碎对接天然会波及到各种鉴权,以及须要将对方零碎的组织构造同步到己方外部零碎中来 当然,有的产品可能会去对接理论的第三方认证源和同步源,然而老本绝对比拟高,因为对接一个不同的源就须要去实现一套接口和逻辑,尽管流程大同小异,可理论工作量可不小 因而,大多数产品为了不便和节俭人力,是会抉择对接 IDaaS,让 IDaaS 去对接各种第三方认证源和同步源 此处的 IDaaS 解释一下: IDaaS 即 Identity as a Service,直译为身份即服务,是一个构建在云上的身份服务 接下来是对于上篇文章谈到的组织构造同步优化的思考,心愿可能给 xdm 带来不一样的思考 坑爹,线上同步近 3w 个用户导致链路阻塞引入发的线上问题,你经验过吗?根本介绍同步流程之前是本身的零碎去和企微,钉钉这样的第三方认证源/同步源进行对接,耗时耗力 当初是将这些工作全副由一个叫做 IDaaS 的模块来实现,能够说他也是一个第三方零碎,只是集成了罕用的一些第三方平台的认证源和同步源,专门的人做专门的事效率是最高的 根本交互如下: 过来做的很 low 的同步做法一个音讯近 3 w 用户,数据量 6 M 左右 明天次要是分享对于同步的做法,看了上一篇文章,有一点任何和教训的 xdm 就晓得,一个音讯外面放近 3w 个用户,这种形式解决真的是几乎了,不靠谱,危险十分高,对性能也影响很大 平时数据量小的时候没啥感觉,起量了,劫难就来了,因而咱们做需要做性能,要思考在后面,预防这样灾难性的问题以及对真的呈现这种问题的时候,要有预案 第三方组织构造同步性能优化须要思考哪些点那么经验了上次事变,天然下来要认真思考如何解决这种组织构造同步的问题,并且反对的用户量要30 W 起步(此处指的是 一个租户能够承载 30W,而不是整个平台 30W) 那么咱们须要思考如下几个问题: 从 IDaaS 获取数据的程序,形式如何解决?<!----> 服务 A 与 服务 B 的通信形式,提供多少个 RPC 接口来实现一次顺利的组织构造同步?<!----> 在同步数据过程中呈现了问题,异常中断了咱们须要如何复原,之前曾经同步了的数据须要如何去解决?<!----> 如何能力达到同步 30 w 用户无异样,且能顺利同步胜利<!----> 同步的数据如果不合乎平台的规定,须要筛选进去,并注明抵触起因,返回给前端页面 其实将上述问题思考分明,残缺的答复结束,基本上这个优化计划就能够落地了,那么咱们开始吧 从 IDaaS 获取数据的程序,形式如何解决?服务 A 去找 IDaaS 进行数据同步的时候,咱们能够分成四个阶段 ...

September 19, 2023 · 2 min · jiezi

关于性能:纯jsvforvue函数式组件vue普通组件性能对比

先说论断dom 节点数量对内存影响没设想中大,js 变量才是内存占用的首恶内存占用及运行性能比照:原生 js < v-for div ≈ v-for 函数式组件 < v-for 一般组件去虚构 dom 化框架正在崛起,成为一种新的抉择dom 节点数量对内存影响没设想中大(十万 div 仅占用 400mb)测试示例生成 100,000(十万)个 div,内存占用仅 400mb,均匀每个 div 占用内存 400 * 1024 kb / 100,000 = 4kb留神,这里内存占用是动态状态内存,即 div 生成实现后期待一会(约1min)内存回收实现后的内存占用,div 生成过程中会产生两头变量,内存占用会比动态时高。如果短时间多少清空 div 再从新生成,会导致内存占用变大组件过多,或者才是 vue 我的项目内存占用大、运行慢的起因别离以「纯 js 渲染 div」、「v-for 渲染 div」、「v-for 渲染函数式组件」、「v-for 渲染一般组件」为例做性能比照测试 「纯 js 渲染 div」运行示例,对应源码 10,000(一万)个 div 渲染耗时:26ms,均匀单个div渲染耗时:0.0026ms100,000(十万)个 div 渲染耗时:265ms,均匀单个div渲染耗时:0.0027ms100,000(十万)个 div 占用内存:528mb,均匀单个占用内存:528 * 1024 / 100,000 = 5.4kb「v-for 渲染 div」运行示例,对应源码 10,000(一万)个 div 应用 key 缓存节点渲染耗时:61ms,均匀单个div渲染耗时:0.0061ms不应用 key 缓存节点渲染耗时:91ms,均匀单个div渲染耗时:0.0091ms100,000(十万)个 div ...

July 10, 2023 · 1 min · jiezi

关于性能:通过-HTTP2-协议案例学习-Java-Netty-性能调优工具技巧与方法论

摘要Dubbo3 Triple 协定是参考 gRPC、gRPC-Web、Dubbo2 等协定特点设计而来,它汲取各自协定特点,齐全兼容 gRPC、Streaming 通信、且无缝反对 HTTP/1 和浏览器。当你在 Dubbo 框架中应用 Triple 协定,而后你就能够间接应用 Dubbo 客户端、gRPC 客户端、curl、浏览器等拜访你公布的服务,不须要任何额定组件与配置。除易用性以外,Dubbo3 Triple 在性能调优方面做了大量工作,本文将偏重对 Triple 协定背地的高性能机密进行深刻解说,波及一些有价值的性能调优工具、技巧及代码实现;在下一篇文章中,咱们将具体开展 Triple 协定在易用性方面的一些具体应用场景。 为什么要优化 Triple 协定的性能?自 2021 年开始 Dubbo3 就曾经作为下一代服务框架逐渐开始取代阿里外部宽泛应用的 HSF 框架,截止目前,阿里以淘宝、天猫等电商为代表的绝大多数外围利用曾经胜利降级到 Dubbo3。作为过来两年撑持阿里双十一万亿级服务调用的要害框架,Triple 通信协议的性能间接影响整个零碎的运行效率。 残缺内容请点击下方链接查看: https://developer.aliyun.com/article/1217925?utm_content=g_10... 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

June 12, 2023 · 1 min · jiezi

关于性能:性能测试理论体系框架

一、性能测试理论体系1. 性能的两个方面1. 工夫及时性(软件运行速度快慢状况)2. 资源经济性(资源耗费状况(CPU、内存))2. 软件性能概述1. 性能指标2. 软件性能生命周期3. 性能影响因素4. 性能模型3. 性能测试概念4. 性能测试解决方案【性能测试罕用办法】1. 负载测试2. 压力测试3. 失效恢复测试4. 配置测试5. 并发测试6. 可靠性测试(稳定性测试/疲劳测试)7. 数据量测试5. 性能测试指标(同PTGM性能测试指标)6. 常用软件性能指标1. 响应工夫2. 并发用户数3. 吞吐量 1. RPS:每秒可能解决最大申请数 2. PPS:每秒显示页面数 3. PV:每天总的PAGE VIEW数 4. TPS:零碎每秒可能处理事务数量 5. QPS:每秒可能解决查问申请数量 6. 其余...4. 资源利用率7. 性能测试施行1. 性能测试过程模型 1. 通用模型PTGM(Performance Testing General Model ) 1. 测试后期筹备 2. 测试计划(包含但不限于以下内容) 1. 性能测试指标 1. 能力验证 1. 验证性能指标需要合乎状况 2. 取得零碎服务能力 3. 评估系统可靠性(成熟性、容错性、可恢复性) 2. 缺点发现 3. 能力布局 4. 性能优化 2. 用户流动剖析与业务建模 - 详见《性能测试业务模型及指标获取指南.docx》 3. 其余... 3. 测试工具引入 1. Jmeter 2. Gatling 3. LoadRunner 4. Locust 5. nGrinder 6. molotov 7. AB 8. twitter/iago 4. 测试设计与开发 1. 测试环境设计 2. 测试场景设计 3. 测试用例设计 4. 测试脚本开发 5. 测试执行与治理 6. 测试剖析 1. 疾速瓶颈定位 2. 性能降落曲线(性能拐点) 1. 单用户区:大量用户拜访区间,个别不作为参照基准; 2. 性能平缓:性能指标较好且安稳,可做负载测试参照规范; 3. 压力区:响应工夫有明显提高,增长率较低; 4. 性能拐点:响应工夫疾速回升,零碎可能呈现不稳固; 3. 内存剖析 4. 处理器剖析(CPU) 5. 磁盘I/O剖析 6. 过程/线程剖析 7. 网络分析 2. APTM(Agile Performance Testing Model) 1. 检查表,流动和倡议工具。适宜麻利开发,提供性能测试 3. 其余...二、其余参考文章如何做好性能压测 1-压测环境的设计和搭建如何做好性能压测 2-性能压测工具选型比照如何做好性能压测 3-阿里巴巴 PTS 在 JMeter 上的实际如何做好性能压测 4-并发模式与 RPS 模式之争,性能压测畛域的星球大战

January 18, 2022 · 1 min · jiezi

关于性能:性能工具之网络工具iperf3简单介绍

背景 在做性能测试网络带宽多大是听运维的还是本人测试过,明天介绍一款罕用测试网络工具iperf3,做性能测试所有用数据谈话。iperf3是用来测量一个网络最大带宽的工具。它反对调节各种参数比方发送持续时间,发送/接管缓存,通信协议。每次测试,它都会报告网络带宽,丢包率和其余参数。 装置:下载相应版本 https://iperf.fr/iperf-downlo... Linux之centos7装置办法 yum install iperf3.x86_64 -y win下载解压 在地址栏中敲cmd就能关上cmd窗口: 显示该目录: linux启动服务端 iper3 -s 查看linux中的ip地址为: 关上wind命令行 iperf3 -c 192.168.128.128 -p 5201 -i 1 -t 10 -w 111K 从下图可看出本机与虚拟机是1000MBytes局域网 client端输入: 客户端命令解释: -c示意服务器的IP地址; -p示意服务器的端口号; -i设置每次报告之间的工夫距离,单位为秒,如果设置为非零值,就会依照此工夫距离输入测试报告,默认值为零; -t设置传输的总工夫,Iperf在指定的工夫内,反复的发送指定长度的数据包,默认是10秒钟; -w设置套接字缓冲区为指定大小,对于TCP形式,此设置为TCP窗口大小,对于UDP形式,此设置为承受UDP数据包的缓冲区大小,限度能够承受数据包的最大值。 Linux窗口显示: server端输入: 以上是测试压力机与服务器之前的网络带宽简略办法,大家做性能测试是否也是这样确定网络带宽大小。 分享一个HashMap和Hashtable的区别 共同点: 底层都是哈希算法,都是双列汇合 区别: 1、HashMap是线程不平安的,效率高, Hashtable是线程平安的,效率低2、HashMap能够存储null键和null值 Hashtable不能够存储null键和null值

August 27, 2021 · 1 min · jiezi

关于性能:性能分析

testggggggggggggggggggggggggggggggggggg

July 19, 2021 · 1 min · jiezi

关于性能:PerfMa-社区全新升级为-HeapDump-性能社区整装再出发

作为开发者,咱们总会面临各种各样的性能问题。它们可能呈现在应用层、数据库层、框架层、虚拟机层。大部分问题能够借助咱们平时的教训解决,但有时候,咱们也会碰到一些难以定位、剖析和解决的疑难杂症。 纵观各大技术社区,无关性能的内容并不匮乏,但往往处于一种零散、无序的状态; 在搜索引擎搜寻某些经典性能问题,又难以在海量搜寻后果中疾速找到本人须要的答案,且这其中还存在大量反复的内容; 还有些非凡的问题,以本人一个人的智慧很难解决,这时候如果能有一位性能畛域的专家能够求助就好了。 PerfMa 社区就是在这样的背景下诞生的。 这里汇集了⼏⼗位业界顶级技术专家公益答疑,数百篇技术⼲货⽂章分享,上千例疑难案例深⼊分析,是数⼗万技术爱好者学习交换的乐园。 近两年来,随着越来越多小伙伴退出,PerfMa 社区一直壮大。但咱们深知咱们的网站在产品体验、内容组织等方面还有很大的提高空间。 随着 PerfMa 的疾速倒退,社区板块也取得了越来越多的关注和资源搀扶。所以,在激情似火的七月上,咱们富丽降级了!PerfMa 社区正式更名为 HeapDump 性能社区!Heap Dump 也叫堆转储文件,是一个 Java 过程在某个工夫点上的内存快照,对其进行剖析能够帮忙咱们更加正当地应用内存,这也是性能优化畛域十分重要的一课。咱们将其作为社区新名称,标记着咱们在性能畛域深耕的信心,以及成为性能畛域领军者的信念。 更新内容 1.社区首页改版 (1)热门资讯收罗了你们感兴趣的各种资讯,来 HeapDump 学习休闲两不误 (2)热门问答会集了热度最高的求助问答,一起来围观疑难问题的解决过程 (3)热门文章整顿了浏览互动量最高的经典文章,好内容值得重复咀嚼 (4)热门探讨在这里你能够探讨 JVM 参数应用领会,也能够吐槽或褒扬社区产品工具 (5)热门插件性能问题的乐高——XPocket 集成的多个开源性能插件等你来试用,还能够被动奉献插件哦 2.积分商城上线 自年初积分零碎上线以来,许多用户踊跃沉闷,积攒了大量积分,却苦于无处“生产”。排除了重重困难之后,积分商城终于跟大家见面了!大家在社区里通过签到、互动、奉献内容取得的积分,都能够在积分商城兑换各种礼品,有品牌定制的各种周边产品,有技术进阶书籍,还有热门电子产品哦! 3.其余降级 除了以上两处降级外,咱们还会继续地优化咱们社区的应用体验,除了产品体验,还有内容方向的优化,都在稳步推动中~当前,大家会看到咱们社区越来越好用! 精选性能文章: JVM 源码剖析之一个 Java 过程到底能创立多少线程 从Linux源码看Socket(TCP)的accept SafePoint 与 Stop The World 全解(基于OpenJDK 11版本) 跟Kafka学技术系列之工夫轮 一次残缺的JVM堆外内存透露故障排查记录 为什么容器内存占用居高不下,频频 OOM 一次百万长连贯压测 Nginx OOM 的问题排查剖析 一次压缩引发堆外内存过高的教训 JVM源码剖析之临门一脚的OutOfMemoryError齐全解读 Linux上TCP的几个内核参数调优 重大事故!IO问题引发线上20台机器同时解体 帮助美团kafka团队定位到的一个JVM Crash问题 精选性能问答: 利用集群个别实例 young gc 工夫忽然飙升 javax.net.ssl.SSLException: Server key这个报错原理有大神能科普一下吗? ...

July 19, 2021 · 1 min · jiezi

关于性能:第41问组提交是怎样提高性能的

问在图解系列中, 咱们介绍过组提交的概念 (https://mp.weixin.qq.com/s/_W..., 这次咱们通过试验来察看其作用 试验照例宽油起一个数据库: 这里咱们调整了刷盘节奏, 使得景象会更显著一些. 同时, 双一的刷盘配置也是生产环境保持数据牢靠的举荐配置. 咱们进行一次压测: 开启压测的同时, 应用 pt-ioprofile 监控 IO : 咱们再进行一次压测, 这次将压力并发调大一倍: 压测的同时, 还是应用 pt-ioprofile 监控 IO 次数: 咱们能够看到: 在压力扩充一倍的状况下, 总体运行时长从55s进步到67s (进步20%), 但 MySQL 对各文件的 IO 次数并没有晋升太多 (此处以"对 binlog 的刷盘次数"为例, 进步了1%) 咱们再更直观的看一下组提交的状况: 通过 binlog 中的 last_committed , 能够确定事务被分到了哪个提交组里 接下来别离剖析一下两次测试的事务数: 能够看到两次测试的提交组数进步了25%, 事务数进步了100%, 大部分事务都合并到了提交组里进行提交. 这就是组提交的作用: 将多个事务放在一个 IO 内进行提交, 以节俭 IO 次数 小贴士 如果大家应用 MySQL 8.0 做试验, 并将 binlog_transaction_dependency_tracking 配置成 WRITESET 或者 WRITESET_SESSION , 那依照 last_commit 字段辨认提交组的办法会不精确. ...

July 2, 2021 · 1 min · jiezi

关于性能:性能基础之浅谈常见接口性能压测

背景随着支流零碎的服务化设计,特地是SOA架构和微服务架构的风行,接口曾经成为各零碎间通信的桥梁。所以,接口的性能压测也变得越来越重要。 SOA(Server OrientedArchitecture,面向服务架构)是目前通用的组件模型。它将软件系统的不同功能模块(被称为服务)通过接口的模式分割起来。这里的接口能够是具体的接口服务也能够是连贯两个模块通信的中间件。一个大型项目通常是由多个零碎开发组成的,每个零碎都有专门的研发团队来负责,单个零碎的性能被称作一个模块。而模块的性能是按后盾的接口实现和UI出现来划分。 微服务架构在某种程度上是面向服务的架构SOA持续倒退的下一步。基本上,这种架构类型是开发软件,网络或挪动应用程序作为独立服务套件(又称微服务)的一种非凡形式。这些服务的创立仅限于一个特定的业务性能,如用户治理、用户角色、电子商务车、搜索引擎、社交媒体登录等。此外,它们是齐全独立的,也就是说它们能够写入不同的编程语言并应用不同的数据库。集中式服务治理简直不存在,微服务应用轻量级HTTP、REST或Thrift API进行通信。 次要指标取得单接口/单业务容量 发现应用程序的性能瓶颈 发现数据库的性能瓶颈 测试策略概述很多时候咱们在设计接口性能压测脚本须要调用零碎接口平台,如果接口平台没有提供可用的UI界面,那么就须要咱们本人写代码联合压测工具连贯接口平台,依照对应的协定调用接口。接口办法运行过程中须要调用很多依赖的办法。依赖办法不同的返回内容也是须要思考的。然而依赖接口内容咱们是不可控的。有时候是依赖模块不能失常运行,有时是依赖模块还未开发完,有时是咱们是主观上无条件让依赖模块返回想要的异样值。这时候咱们在压测的时候就须要Mock零碎实现了,在Mock零碎(挡板)定义好接口相干数据后,填入指标返回后果,就能模仿依赖接口返回想要的内容 联合开源的性能测试工具Jmeter(本文只讲此工具),只须要把URL通过模仿HTTP并发申请就能够失去对应的执行后果,再依据返回后果判断接口执行是否正确。 所以相对来说工夫老本比拟低,一个场景转化成测试脚本也是比较简单的事件。接口公布上线后,参数很少发生变化。因为接口做为服务公布后会有多个调用方,如果参数发生变化将告诉所有调用方做响应的批改,否则将呈现调用方无奈应用的状况。接口定义稳固不太容易发生变化,所以接口性能压测的前期保护工作也就不多。 次要类型类型具体内容效率(性能)并发数、响应工夫 、TPS、错误率、资源占用稳定性单用户长时间下的重复操作、多用户长时间并发操作、异样值下的长时间重复操作、最大故障时长压力超规格负载下的规格内的解决恢复性负载失常后,零碎是否失常复原常见接口目前支流零碎的接口大抵可分为HTTP接口和自研RPC(Remote Procedure call,近程过程调用)接口,而HTTP接口可能更为广泛一些。 HTTP接口个别压测WEB零碎都会接触HTTP接口 常见有HTTP和HTTPS两种协定 HTTP 超文本传输协定,默认80端口 HTTPS 平安超文本传输协定,能够了解为http协定的平安版,默443端口 HTTP常见两种申请办法:GET和POST 与Server进行申请/响应时,两种最常被用到的两种办法 GET 从指定的资源申请数据 POST向指定的资源提交要被解决的数据 RPC接口RPC(Remote Procedure Call Protocol)——近程过程调用协定,它是一种通过网络从近程计算机程序上申请服务,而不须要理解底层网络技术的协定。 其实简略的说,就是象调用本地的类的办法样来调用服务器端的办法实现。 须要要对RMI(Remote Method Invoke,近程办法调用)中的stu(桩)和skeleton(骨架)的概念有一点理解。RMI的代理模式是通过代理对象将办法传递给理论对象的。Stub驻留客户端,承当着代理近程对象实现者的角色。skeleton类帮忙近程对象与sttub连贯进行通信。 次要组成元素: 次要原理: 实体对象和业务接口由客户端和服务端专用。 接口实现是由服务端对定义好的业务接口进行性能实现,并将接口实例注册服务中提供给客户端调用。 目前咱们接触到RPC接口次要有Hession、Dubbo、HTTP、Thrift、Hprose等 Hession、Dubbo、Thrift、Hprose都是近程办法调用的一种实现,客户端须要保留stub来调用接口。这就是为什么咱们性能压测的时候须要Jmeter援用jar包。 Hession:是一个轻量级的remoting onhttp工具,应用简略的办法提供了RMI的性能,近程办法调用的一种,采纳二进制RPC协定(基于Http协定),适宜发送二进制数据,不适宜简单对象类型的传输。 Dubbo:阿里巴巴开源的一个高性能优良的服务框架,一个近程办法调用的框架。Dubbo注册核心负责服务地址的注册和查找,相当于服务目录:Dubbo监控核心负责统计各服务调用次数、调用工夫 Thrift:facebook开源的一个可互操作和可伸缩服务的框架一个近程办法调用的框架,可扩大且跨语言的服务的开发。容许你定义一个简略的定义文件中的数据类型和服务接口(IDL)。之后生成服务器骨架和客户端调用代理 Hprose:国人开发的一个近程办法调用的开源框架。它是一个先进的轻量级的跨语言跨平台面向对象的高性能近程动静通信中间件。 HTTP:常见于WEB利用,基于HTTP协定传输文本。当一个URL发送申请时,服务端doGet或者doPost办法会被调用,获取相应的参数。压测HTTP接口时,只须要通过定位URL接口并传参断言,绝对比较简单。 上面针对几种接口简要阐明: Hession接口压测: 通过接口URL获取接口,如果复制接口定义及其自定义类,包名尽量跟开发包保持一致,不倡议应用间接复制代码的形式,因为这样不便于保护,应用Maven在pom文件引入接口和Hessian依赖的jar包。通常借助HessianSpringFactoryBean获取,再联合Jmeter自定义JAVA类申请和Stub的形式接入测试工具进行压测 HessianProxy是hessian client解决客户端申请的外围类,采纳proxy模式,代理客户端对近程接口的调用,hessian client的主程序的时序图如下: Dubbo接口压测测试: 如果通过Dubbo注册核心获取服务接口,那么搭建测试环境的时候须要指定Dubbo注册核心的地址,扩大Jmeter也须要配置Dubbo注册核心地址以及对外提供服务的接口名称。直连的形式在Jmeter须要导入Dubbo框架相干的JAR包。当初有现成的Jemter dubbo插件,间接引入即可 节点阐明: Provider:裸露服务的服务提供方 Consumer:调用近程服务的服务生产方 Registry:服务注册与发现的注册核心 Monitor:统计服务调用次数和服务调用工夫的监控核心 Container:服务运行容器 Jemter Dubbo插件地址:https://github.com/dubbo/jmet... Jemter DubboSample截图 Thrift接口压测: 通过Stub驻留客户端调用用接口描述语言来写接口(IDL),生成服务器skeleton(骨架)类和客户端调用代理,skeleton类帮忙近程对象与sttubb驻留客户端连贯进行通信,客户端和服务器端是紧耦合在一起的,你不能独自批改任何一端的接口(不是说服务器端代码的实现),服务器和客户端传递的数据类型是严格匹配的 具体的办法能够参照上面这篇文章: 性能工具之Jmeter压测Thrift RPC服务 ...

May 23, 2021 · 2 min · jiezi

关于性能:数据传输-DTLE-在弱网络环境下的性能报告

作者:刘安爱可生测试团队成员,次要负责 DTLE 开源我的项目相干测试工作,善于 Python 自动化测试开发,最近醉心于 Linux 性能剖析优化的相干常识。 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 背景条件:应用 sysbench 压力工具对 10 张 1 万记录表进行增改删操作应用 TC 工具来模仿高延时,低带宽场景工具筹备:1.tc # 模仿网络带宽受限以及减少提早 2.iperf3 # 验证网络带宽 3.sysbench # 制作数据压力 环境筹备:1.DTLE 版本 3.20.10.0 2.服务器 | IP | 用处 | | ------------- | ------------ | | 10.186.18.123 | 源端数据库 | | 10.186.18.117 | 指标端数据库 | | 10.186.63.20 | 源端DTLE | | 10.186.63.145 | 指标端DTLE | 3.在两台 DTLE 服务器上增加网络带宽限度以及减少提早(经测试网络提早配置只对发送无效,故须要在源端和指标端同时增加 TC 规定,每端提早配置为预期提早的一半)。 #!/usr/bin/env bash# Name of the traffic control command.TC=`which tc`# The network interface we're planning on limiting bandwidth.IF=eth0 # Interface# Download limitDNLD=2mbit # DOWNLOAD Limit# Upload limitUPLD=2mbit # UPLOAD Limit# IP address of the machine we are controllingIP=10.186.63.145 # Host IP#IP=10.186.63.20# Network latencyDELAY=125ms# Filter options for limiting the intended interface.U32="$TC filter add dev $IF protocol ip parent 1:0 prio 1 u32"$TC qdisc add dev $IF root handle 1: htb default 1$TC class add dev $IF parent 1: classid 1:10 htb rate $DNLD$TC class add dev $IF parent 1: classid 1:20 htb rate $UPLD$TC qdisc add dev $IF parent 1:10 handle 10: netem delay $DELAY$TC qdisc add dev $IF parent 1:20 handle 20: netem delay $DELAY$U32 match ip dst $IP/32 flowid 1:10$U32 match ip src $IP/32 flowid 1:204.验证配置失效 ...

March 22, 2021 · 2 min · jiezi

关于性能:译-Nodejs的性能监控-Part-1-监控的指标

translate from 《Node.js Performance Monitoring - Part 1: The Metrics to Monitor》 在深入研究Node.js应用程序的性能以及如何进步性能时,有几个比拟外围的指标十分重要和有帮忙,让咱们从以下几个要害的指标来学习剖析Node.js的性能。 【cpu usage】Node利用个别不会耗费很多的CPU,如果有高CPU那么阐明你的app有很多同步操作。而他们会block事件循环,这会导致你的异步工作回调也被阻止了。 而CPU使用率高的程序不肯定是谬误的程序,如果你要在一个web-server里运行一个cpu密集型的程序,这个工作最好是放到一个独自过程里,否则它会让你的服务变得异样迟缓影响你的用户。找到引起cpu升高的问题,是理解node应用程序的良好开端。 【堆内存应用、内存透露、垃圾收集】Node.js对内存有一个独特的限度 - 一个过程的最大堆容量为1.5GB(cyj注: 这个应该跟机器cpu架构无关),无论运行该过程的机器上有多少可用内存。理解这一点对于架构设计和测试你的利用至关重要。 内存透露是Node.js中的常见问题,并且是在对象被援用太长时引起的 - 换句话说,一个变量即便不再须要了但还是存储着它的援用。 失常状况下,垃圾收集器会开释不被应用的内存,以释放出来给利用持续应用。然而,垃圾收集器无奈开释这些在过期日期之后挂起的变量所应用的内存。如果您的应用程序内存使用量稳步增长而不是通过垃圾回收周期性地缩小,那么您可能会遇到内存透露问题 【事件循环的滞后 Lag in EventLoop】Node.js的外围劣势之一就是速度快。它被构建为疾速和异步地解决多个事件.这种劣势来自事件循环,它容许应用程序疾速响应这些事件。在优化应用程序以进步速度时,理解事件循环减慢的工夫和起因十分重要。 随着事件循环的每个循环变慢,每个事件将破费更长的工夫来解决和操作。 从性能上讲,这能够将Node.js升高到无响应的水平。 一些拖慢事件循环的case: 长时间运行的同步工作在事件循环的单个滴答中破费太多工夫也可能是性能问题的本源。 您无奈打消工作执行cpu所消耗的工夫,但咱们须要留神在任何给定工夫内破费的工夫。 如果工作工夫超过咱们可承受的响应工夫,那么在不同的过程中执行该工作可能是有意义的。 每个循环的工作一直减少Node.js跟踪须要在事件循环的各个阶段解决的所有函数和回调。当您的服务器看到负载减少时,每个循环的工作数量开始减少。当此计数过高时,您的用户将开始看到响应工夫的减少。好消息是扩充运行应用程序的过程数量通常能够缓解这种状况,并将您的网站性能复原到失常程度

March 3, 2021 · 1 min · jiezi

关于性能:使用Maven创建Gatling性能测试项目

简介Gatling是一款基于Scala 开发的高性能服务器性能测试工具,它次要用于对服务器进行负载等测试,并剖析和测量服务器的各种性能指标。Gatling次要用于测量基于HTTP的服务器,比方Web应用程序,RESTful服务等,除此之外它领有以下特点: 反对Akka Actors 和 Async IO,从而能达到很高的性能反对实时生成Html动静轻量报表,从而使报表更易浏览和进行数据分析反对DSL脚本,从而使测试脚本更易开发与保护反对录制并生成测试脚本,从而能够不便的生成测试脚本反对导入HAR(Http Archive)并生成测试脚本反对Maven,Eclipse,IntelliJ等,以便于开发反对Jenkins,以便于进行继续集成反对插件,从而能够扩大其性能,比方能够扩大对其余协定的反对开源收费下载地址:Gatling官网 收费版本 Gatling Open Source免费版本 Gatling FrontLine装置Gatlingscoop install gatling装置胜利后目录构造如下: bin: gatling也就两种组件-录制的组件和运行的组件;这个目录外面有两种脚本,一个是运行recorder的,也就是录制组件启动脚本;一个是运行组件的启动脚本;conf: 放配置文件的目录。个别状况下你想要批改一些运行参数,都可来这里搞定;lib: 外面是一些jar包,gatling的运作全靠他们了;咱们仅作为使用者临时不用去理睬;results: 测试报告目录;target: 你启动运行组件后,gatling会为你编译好所有的.scala脚本,而编译后的class文件就会在这里;user-files: 寄存你录制后的.scala脚本;<br/> 应用maven创立Gatling我的项目环境筹备装置jdk scoop install adopt8-hotspot装置maven scoop install maven装置scala scoop install scala装置idea scoop install idea创立工程idea创立骨架 创立骨架GroupId: io.gatling.highchartsArtifactId: gatling-highcharts-maven-archetypeVersion: 3.5.0点击查看最新版本 创立工程 期待上述工程编译,而后装置scala插件 官网测试工程 自定义工程,援用jar包 运行测试 查看测试报告 参考文档https://www.jianshu.com/p/90a... https://blog.csdn.net/qunyaoa...

January 26, 2021 · 1 min · jiezi

关于性能:性能之巅定位和优化程序CPU内存IO瓶颈

摘要:性能优化指在不影响零碎运行正确性的前提下,使之运行得更快,实现特定性能所需的工夫更短,或领有更弱小的服务能力。一、思维导图 二、什么是性能优化?性能优化指在不影响零碎运行正确性的前提下,使之运行得更快,实现特定性能所需的工夫更短,或领有更弱小的服务能力。 关注不同程序有不同的性能关注点,比方科学计算关注运算速度,游戏引擎重视渲染效率,而服务程序谋求吞吐能力。 服务器个别都是可程度扩大的分布式系统,零碎解决能力取决于单机负载能力和程度扩大能力,所以,晋升单机性能和晋升程度扩大能力是两个次要方向,实践上零碎程度方向能够有限扩大,但程度扩大后往往导致通信老本飙升(甚至瓶颈),同时面临单机解决能力降落的问题。 指标掂量单机性能有很多指标,比方:QPS(Query Per Second)、TPS、OPS、IOPS、最大连接数、并发数等评估吞吐的指标。 CPU为了进步吞吐,会把指令执行分为多个阶段,会搞指令Pipeline,同样,软件系统为了晋升解决能力,往往会引入批处理(攒包),跟CPU流水线会引起指令执行Latency减少一样,随同着零碎负载减少也会导致提早(Latency)减少,可见,零碎吞吐和提早是两个抵触的指标。 显然,过高的提早是不能承受的,所以,服务器性能优化的指标往往变成:谋求可容忍提早(Latency)下的最大吞吐(Throughput)。 提早(也叫响应工夫:RT)不是固定的,通常在一个范畴内稳定,咱们能够用均匀时延去评估零碎性能,但有时候,均匀时延是不够的,这很容易了解,比方80%的申请都在10毫秒以内失去响应,但20%的申请时延超过2秒,而这20%的高提早可能会引发投诉,同样不可承受。 一个改良措施是应用TP90、TP99之类的指标,它不是取均匀,而是需确保排序后90%、99%申请满足时延的要求。 通常,执行效率(CPU)是咱们的重点关注,但有时候,咱们也须要关注内存占用、网络带宽、磁盘IO等,影响性能的因素很多,它是一个简单而乏味的问题。 三、基础知识能编写运行正确的程序不肯定能做性能优化,性能优化有更高的要求,这样讲并不是想要吓阻想做性能优化的工程师,而是捕风捉影讲,性能优化既须要扎实的零碎常识,又须要丰盛的实践经验,只有这样,你能力具备case by case剖析问题解决问题的能力。 所以,相比间接给出论断,我更违心多花些篇幅讲一些基础知识,我保持认为底层根底是了解并把握性能优化技能的前提,值得破费一些工夫钻研并把握这些根技术。 CPU架构你须要理解CPU架构,了解运算单元、记忆单元、管制单元是如何既各司其职又相互配合实现工作的。 你须要理解CPU如何读取数据,CPU如何执行工作。你须要理解数据总线,地址总线和管制总线的区别和作用。你须要理解指令周期:取指、译指、执行、写回。你须要理解CPU Pipeline,超标量流水线,乱序执行。你须要理解多CPU、多外围、逻辑核、超线程、多线程、协程这些概念。存储金字塔CPU的速度和访存速度相差200倍,高速缓存是逾越这个鸿沟的桥梁,你须要了解存储金字塔,而这个层次结构思维基于着一个称为局部性原理(principle of locality)的思维,它对软硬件零碎的设计和性能有着极大的影响。 局部性又分为工夫局部性和空间局部性。 缓存 古代计算机系统个别有L1-L2-L3三级缓存。 比方在我的零碎,我通过进入 /sys/devices/system/cpu/cpu0/cache/index0 1 2 3目录下查看。 size对应大小、type对应类型、coherency_line_size对应cache line大小。 每个CPU外围有独立的L1、L2高速缓存,所以L1和L2是on-chip缓存;L3是多个CPU外围共享的,它是off-chip缓存。 L1缓存又分为i-cache(指令缓存)和d-cache(数据缓存),L1缓存通常只有32K/64KB,速度高达4 cycles。L2缓存能到256KB,速度在8 cycles左右。L3则高达30MB,速度32 cycles左右。而内存高达数G,访存时延则在200 cycles左右。所以CPU->寄存器->L1->L2->L3->内存->磁盘形成存储层级构造:越凑近CPU,存储容量越小、速度越快、单位成本越高,越远离CPU,存储容量越大、速度越慢、单位成本越低。 虚拟存储器(VM)过程和虚拟地址空间是操作系统的2个外围形象。 零碎中的所有过程共享CPU和主存资源,虚拟存储是对主存的形象,它为每个过程提供一个大的、统一的、公有的地址空间,咱们gdb调试的时候,打印进去的变量地址是虚拟地址。 操作系统+CPU硬件(MMU)严密单干实现虚拟地址到物理地址的翻译(映射),这个过程总是缄默的主动的进行,不须要利用程序员的任何干涉。 每个过程有一个独自的页表(Page Table),页表是一个页表条目(PTE)的数组,该表的内容由操作系统治理,虚拟地址空间中的每个页(4K或者8K)通过查找页表找到物理地址,页表往往是层级式的,多级页表缩小了页表对存储的需要,命失(Page Fault)将导致页面调度(Swapping或者Paging),这个惩办很重,所以,咱们要改善程序的行为,让它有更好的局部性,如果一段时间内访存的地址过于发散,将导致平稳(Thrashing),从而重大影响程序性能。 为了减速地址翻译,MMU中减少了一个对于PTE的小的缓存,叫翻译后备缓冲器(TLB),地址翻译单元做地址翻译的时候,会先查问TLB,只有TLB命失才会查问高速缓存(L1-2-3)。 汇编根底尽管写汇编的场景越来越少,但读懂汇编仍然很有必要,了解高级语言的程序是怎么转化为汇编语言有助于咱们编写高质量高性能的代码。 对于汇编,至多须要理解几种寻址模式,理解数据操作、分支、传送、管制跳转指令。 了解C语言的if else、while/do while/for、switch case、函数调用是怎么翻译成汇编代码。了解ebp+esp寄存器在函数调用过程中是如何构建和撤销栈帧的。了解函数参数和返回值是怎么传递的。异样和零碎调用异样会导致控制流渐变,异样控制流产生在计算机系统的各个档次,异样能够分为四类: 中断(interrupt):中断是异步产生的,来自处理器内部IO设施信号,中断处理程序分上下部。 陷阱(trap):陷阱是无意的异样,是执行一条指令的后果,零碎调用是通过陷阱实现的,陷阱在用户程序和内核之间提供一个像过程调用一样的接口:零碎调用。 故障(fault):故障由谬误状况引起,它有可能被故障处理程序修复,故障产生,处理器将管制转移到故障处理程序,缺页(Page Fault)是经典的故障实例。 终止(abort):终止是不可复原的致命谬误导致的后果,通常是硬件谬误,会终止程序的执行。 零碎调用: 内核态和用户态 你须要理解操作系统的一些概念,比方内核态和用户态,应用程序在用户态运行咱们编写的逻辑,一旦调用零碎调用,便会通过一个特定的陷阱陷入内核,通过零碎调用号标识性能,不同于一般函数调用,陷入内核态和从内核态返回须要做上下文切换,须要做环境变量的保留和复原工作,它会带来额定的耗费,咱们编写的程序应防止频繁做context swap,晋升用户态的CPU占比是性能优化的一个指标。 过程、线程、协程在linux内核中,过程和线程是同样的零碎调用(clone),过程跟线程的区别:线程是共享存储空间的,每个执行流有一个执行控制结构体,这外面会有一个指针,指向地址空间结构,一个过程内的多个线程,通过指向同一地址构造实现共享同一虚拟地址空间。 通过fork创立子过程的时候,不会马上copy一份数据,而是推延到子过程对地址空间进行改写,这样做是正当的,此即为COW(Copy On Write),在利用开发中,也有大量的相似借鉴。 协程是用户态的多执行流,C语言提供makecontext/getcontext/swapcontext系列接口,很多协程库也是基于这些接口实现的,微信的协程库libco(已开源)通过hook慢速零碎调用(比方write,read)做到静默替换,十分奇妙。 ...

December 9, 2020 · 1 min · jiezi

关于性能:第24问一主多从的半同步复制到底是哪个-slave-拖慢了性能

问题咱们都晓得,半同步复制中,如果 slave 比较慢,会拖慢 master 的提交性能。 那么,在一主多从的半同步架构中,如果 master 的提交性能慢,如何判断是哪个 slave 拖慢了性能? 试验先通过 dbdeployer 疾速搭建一主两从半同步集群: 上面给 master 施加一些压力: 而后咱们用 strace,拖慢 slave2 的运行速度。 因为半同步复制的起因,当初 slave2 拖慢了 master 的提交性能。咱们开始诊断,设置半同步插件的日志级别为 16: 查看 master 的 error log: 大略扫一下 error log,如图举例,发现大部分半同步阻塞,最初收到的都是 server_id 为 300 的 slave。而在咱们的环境中,slave2 的 server_id 恰好是 300。最初,记得将调整的日志级别调回来: 半同步插件并没有提供方便的办法查看各个 slave 谁拖慢了性能,所以咱们通过调试日志来查看最初一个返回的 ack 都来自于哪台 slave。 大家应用此办法时,要留神调试日志的量比拟大,不要开启太久以防占用过多磁盘。 对于 MySQL 的技术内容,你们还有什么想晓得的吗?连忙留言通知小编吧!

October 23, 2020 · 1 min · jiezi

关于性能:必须收藏20个开发技巧教你开发高性能计算代码

摘要:华为云专家从优化布局 / 执行 / 多过程 / 开发心理等20个要点,教你如何开发高性能代码。高性能计算,是一个十分宽泛的话题,能够从专用硬件/处理器/体系结构/GPU,说到操作系统/线程/过程/并行/并发算法,再到集群/网格计算,最初到天河二号(TH-1)。 咱们这次的分享会从集体的实际我的项目摸索登程,与大家分享本人摸爬滚打得出的心得体会,判若两人的保持原创。其中内容波及到优化布局 / 执行 / 多过程 / 开发心理等约20个要点,其中例子代码片段,应用Python。 高性能计算,在商业软件利用开发过程中,要解决的外围问题,用很文言的形式来说,“在无限的硬件条件下,如何让一段本来跑不动的代码,跑起来,甚至飞起来。” 性能晋升教训举2个例子,随便感触下。 (1)635万条用户浏览文档的历史行为数据,数据处理工夫,由50小时,优化到15秒。(是的,你没有看错) (2)基于Mongo的宽表创立,由20小时,优化到进来打杯水的功夫。 在大数据的时代,一个优良的程序员,能够写出性能比其他人的程序高出数百倍,甚至数千倍,具备这样的技能,对产品的奉献无疑是很大的,对集体而言,也是本人履历上亮点和加分项。 聊聊历史2000年前后,因为PC硬件限度,那一代的程序员,比方,国内的求伯君 / 雷军,国外的比尔盖茨 / 卡马特,都是能够从机器码 / 汇编的角度来晋升程序性能。 到2005年前后,PC硬件性能倒退迅速,高性能优化经常听到,来自嵌入式设施和挪动设施。那个年代的挪动设施支流应用J2ME开发,可用内存128KB。那个年代的程序员,须要对程序大小(OTA下载,有数据流量限度,如128KB),内存应用都精打细算,真的是掐着指头算。比方,通常一个程序,只有一个类,因为新增一个类,会多应用几K内存。数据文件会合并为一个,缩小文件数,这样须要算,比方从第几个字节开始,是什么数据。 2008年前后,第一代iOS / Android智能手机上市,App可用内存达到1GB,App能够通过WIFI下载,App大小也能够达到一百多MB。我方才看了下我的P30,就存储空间而言,QQ应用了4G,而微信应用了10G。设施性能晋升,可用内存和存储空间大了,程序员们终于“解放”了,直到–大数据时代的到来。 在大数据时代下,数据量疯狂增长,一个大的数据集操作,你的程序跑一早晨才出后果,是常有的事。 基础知识本次分享假如读者曾经理解了线程/过程/GIL这些概念,如果不理解,也没有关系,能够读下以下的摘要,并记住上面3点基础知识小结即可。 什么是过程?什么是线程?两者的差异?以下内容来自Wikipedia: https://en.wikipedia.org/wiki... Threads differ from traditional multitasking operating-system processes in several ways: processes are typically independent, while threads exist as subsets of a processprocesses carry considerably more state information than threads, whereas multiple threads within a process share process state as well as memory and other resourcesprocesses have separate address spaces, whereas threads share their address spaceprocesses interact only through system-provided inter-process communication mechanismscontext switching between threads in the same process typically occurs faster than context switching between processes驰名的GIL (Global interpreter lock)以下内容来自 wikipedia. ...

October 20, 2020 · 3 min · jiezi

关于性能:前端面试每日-31-第526天

明天的知识点 (2020.09.23) —— 第526天 (我也要出题)[html] 网页中的友情链接有什么作用?[css] 应用纯css布局中一个“王”字[js] 写一个办法获取滚动条间隔窗口顶部的间隔[软技能] 如何进行网站性能优化?《论语》,曾子曰:“吾日三省吾身”(我每天屡次检查本人)。前端面试每日3+1题,以面试题来驱动学习,每天提高一点!让致力成为一种习惯,让奋斗成为一种享受!置信 保持 的力量!!!欢送在 Issues 和敌人们一起探讨学习! 我的项目地址:前端面试每日3+1【举荐】欢送跟 jsliang 一起折腾前端,零碎整顿前端常识,目前正在折腾 LeetCode,打算买通算法与数据结构的任督二脉。GitHub 地址 微信公众号欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个Star, 同时欢送微信扫码关注 前端剑解 公众号,并退出 “前端学习每日3+1” 微信群互相交换(点击公众号的菜单:交换)。 学习不打烊,充电加油只为遇到更好的本人,365天无节假日,每天早上5点纯手工公布面试题(死磕本人,愉悦大家)。心愿大家在这虚夸的前端圈里,放弃沉着,保持每天花20分钟来学习与思考。在这变幻无穷,类库层出不穷的前端,倡议大家不要等到找工作时,才狂刷题,提倡每日学习!(不忘初心,html、css、javascript才是基石!)欢送大家到Issues交换,激励PR,感激Star,大家有啥好的倡议能够加我微信一起交换探讨!心愿大家每日去学习与思考,这才达到来这里的目标!!!(不要为了谁而来,要为本人而来!)交换探讨欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个[Star]

September 23, 2020 · 1 min · jiezi

Locust-参数化

Locust 参数化

July 12, 2020 · 1 min · jiezi

应用-Locust-快速上手写压测

利用 Locust 疾速上手写压测

July 12, 2020 · 1 min · jiezi

Locust压力测试使用总结

Locust压力测试应用总结

July 12, 2020 · 1 min · jiezi

Locust-noweb-模式与参数详解

Locust no-web 模式与参数详解

July 9, 2020 · 1 min · jiezi

实验利用Locust完成测试登录

实验:利用Locust完成测试登录

July 8, 2020 · 1 min · jiezi

测试最大在线用户数

测试最大在线用户数

July 8, 2020 · 1 min · jiezi

用curl模拟夹带cookie的http请求

用curl模拟夹带cookie的http请求

July 8, 2020 · 1 min · jiezi

Apache-ab测试工具使用方法无参get传参post传参

Apache ab测试工具使用方法(无参、get传参、post传参)

July 7, 2020 · 1 min · jiezi

深度融合POLARDB与SuperMap联合构建首个云原生时空平台

10月30日,以“地理智慧 深度进化”为主题的2019 GIS软件技术大会(简称GTC 2019)在北京国际会议中心开幕。超图联合阿里云在主论坛完成产品合作发布。超图软件SuperMap GIS与阿里巴巴新一代自研云数据库POLARDB实现 “引擎级”深度对接,构建了自治、弹性、高可用的云原生时空数据管理平台联合解决方案,推出了业界首个“云原生数据库+云原生GIS”的全国产化平台。 阿里云POLARDB POLARDB是阿里云自主研发的国内首个云原生数据库,兼容三种数据库引擎:MySQL、PostgreSQL、Oracle。采用了存储计算分离、软硬一体化等创新设计,满足大规模业务场景上云需求。POLARDB集成Ganos时空引擎,基于属性-时间-空间一体化数据管理、4D移动对象建模以及空间异构计算并行加速构建时空PaaS基础服务。 超图SuperMap GIS SuperMap GIS是北京超图软件股份有限公司研发的,具有完全自主知识产权的大型地理信息系统软件平台。最新推出的SuperMap GIS 10i产品构建形成大数据GIS、人工智能GIS、新型三维GIS、云原生GIS和跨平台GIS“BitCC”五大技术体系。在GIS基础软件领域的中国市场份额稳居第一,用户遍布100多个国家和地区。 1+3+N联合方案 双方联合开发的云原生时空数据管理平台包含1+3+N布局: 1:统一云原生数据库和云原生GIS,完成近600个GIS标准案例库严格测试,获得双方兼容性认证,为用户构建自治、弹性、高可用的云原生时空大数据管理平台提供了强有力的基础软件平台支撑。3:三个重磅特性:a. 支持全空间建模与时空多模,可综合管理地表地下、室内室外、空/天以及时空各类地物对象;b. 独具一库多平台兼容性,平台高度兼容Oracle和PostgreSQL,而GIS空间数据同时兼容Ganos和SuperMap接口访问,已有应用程序均可平滑迁移;c. 双引擎融合性能加速,SuperMap空间数据引擎与Ganos时空引擎通过深度融合获得极致性能提升。N:多形态输出:支持公有云和私有IDC环境部署,其中IDC支持专有云、纯软许可或POLARDB BOX一体机方式输出,充分满足安全合规需求。 不一样:“引擎级”深度对接 为提升GIS平台在千万以及亿级空间记录下的查询分析性能,此次双方平台的对接采用了“计算下推”的深度融合模式。SuperMap GIS平台的空间数据引擎SDX+将空间查询和分析计算直接下推到POLARDB, POLARDB利用空间并行查询优化以及云原生时空引擎Ganos的调度,实现模型的高效映射、空间索引快速过滤以及近百个查询分析的计算下推。经某时空信息云平台实际应用环境验证,较传统商用数据库对接方式有成倍的性能提升。 结语 POLARDB与SuperMap的深度融合,践行了阿里云的被集成战略,顺应了“一横一竖”的平台策略。通过“一竖”完成垂直整合,即SuperMap借助技术集成POLARDB提升了系统整体性能和跨数据库平台兼容性,而POLARDB借助SuperMap拓宽了空间业务能力宽度。一横是通过品牌叠加,共同打造了时空数据服务的平台生态,为联合开拓数字政府、城市大脑、智慧城市/园区/建筑等强GIS数字化领域应用提供了地上地下、室内室外领先、专业的全空间数字化解决方案能力。 “一横一竖”整合,扩大了空间数据服务的“面积”。 阿里云双11领亿元补贴,拼手气抽iPhone 11 Pro、卫衣等好礼,点此参与:http://t.cn/Ai1hLLJT 本文作者:ganos 阅读原文 本文为云栖社区原创内容,未经允许不得转载。

November 4, 2019 · 1 min · jiezi

为什么-K8s-集群达万级规模阿里购物体验还能如丝顺滑

阿里妹导读:本文主要介绍阿里巴巴和蚂蚁金服在大规模生产环境中落地 Kubernetes 的过程中,在集群规模上遇到的典型问题以及对应的解决方案,内容包含对 etcd、kube-apiserver、kube-controller 的若干性能及稳定性增强,这些关键的增强是阿里巴巴和蚂蚁金服内部上万节点的 Kubernetes 集群能够平稳支撑 2019 年天猫 618 大促的关键所在。文内藏福利,向下滑滑滑,免费课程立刻领取~背景从阿里巴巴最早期的 AI 系统(2013)开始,集群管理系统经历了多轮的架构演进,到 2018 年全面的应用 Kubernetes ,这期间的故事是非常精彩的。这里忽略系统演进的过程,不去讨论为什么 Kubernetes 能够在社区和公司内部全面的胜出,而是将焦点关注到应用 Kubernetes 中会遇到什么样的问题,以及我们做了哪些关键的优化。 在阿里巴巴和蚂蚁金服的生产环境中,容器化的应用超过了 10k 个,全网的容器在百万的级别,运行在十几万台宿主机上。支撑阿里巴巴核心电商业务的集群有十几个,最大的集群有几万的节点。在落地 Kubernetes 的过程中,在规模上面临了很大的挑战,比如如何将 Kubernetes 应用到超大规模的生产级别。 罗马不是一天就建成的,为了了解 Kubernetes 的性能瓶颈,我们结合阿里和蚂蚁的生产集群现状,估算了在 10k 个节点的集群中,预计会达到的规模: 20w pods100w objects 我们基于 Kubemark 搭建了大规模集群模拟的平台,通过一个容器启动多个(50个)Kubemark 进程的方式,使用了 200 个 4c 的容器模拟了 10k 节点的 kubelet。在模拟集群中运行常见的负载时,我们发现一些基本的操作比如 Pod 调度延迟非常高,达到了惊人的 10s 这一级别,并且集群处在非常不稳定的状态。 当 Kubernetes 集群规模达到 10k 节点时,系统的各个组件均出现相应的性能问题,比如: etcd 中出现了大量的读写延迟,并且产生了拒绝服务的情形,同时因其空间的限制也无法承载 Kubernetes 存储大量的对象;API Server 查询 pods/nodes 延迟非常的高,并发查询请求可能地址后端 etcd oom;Controller 不能及时从 API Server 感知到在最新的变化,处理的延时较高;当发生异常重启时,服务的恢复时间需要几分钟;Scheduler 延迟高、吞吐低,无法适应阿里业务日常运维的需求,更无法支持大促态的极端场景。etcd improvements为了解决这些问题,阿里云容器平台在各方面都做了很大的努力,改进 Kubernetes 在大规模场景下的性能。 ...

October 17, 2019 · 5 min · jiezi

蚂蚁金服OceanBase挑战TPCCTPCC基准测试之数据库事务引擎挑战

蚂蚁金服自研数据库 OceanBase 登顶 TPC-C 引起业内广泛关注,为了更清楚的展示其中的技术细节,我们特意邀请 OceanBase 核心研发人员对本次测试进行技术解读,共包括五篇: 1)TPC-C基准测试介绍2)OceanBase如何做TPC-C测试3)TPC-C基准测试之SQL优化4)TPC-C基准测试之数据库事务引擎的挑战5)TPC-C基准测试之存储优化 本文为第四篇,其它文章已同步发布,详情请在“蚂蚁金服科技”公众号查看。 OceanBase 这次 TPC-C 测试与榜单上 Oracle 和 DB2 等其他数据库在硬件使用上有非常大的不同,OceanBase 的数据库服务器使用的是 204+3 台型号是 ecs.i2.16xlarge 阿里云 ECS 服务器,其中 204 台作为 data node,还有 3 台作为 root node,每位读者都可以在阿里云网站上轻松按需购买。如果读者翻看 Oracle 和 DB2 的 TPC-C 测试报告会发现,这些数据库都会使用专用的存储设备,例如前最高记录保持者 Oracle 在 2010 年的测试,使用了 97 台 COMSTAR 专用的存储设备,其中 28 台用来存储数据库的重做日志(Redo Log)。 硬件的差异给软件架构提出了完全不同的挑战,专用的存储设备其内部通过硬件冗余实现了设备自身的可靠保证,数据库软件在使用这样的存储设备时就天然的预设了数据不会丢失。但是,这种方式带来了成本的极大消耗,专用的存储设备的价格都是特别昂贵的。 OceanBase 使用通用的 ECS 服务器提供数据库服务,并且只使用 ECS 机器自带的本地硬盘做数据存储,这是最通用的硬件条件。但是这种方式对软件架构提出了很大的挑战,因为单个 ECS 服务器的不如专用的存储设备可靠性高。这也对 OceanBase 的事务引擎提出了很大的挑战,OceanBase 是在普通的 ECS 服务器上就可以实现 ACID 特性。 TPC-C 测试是对事务 ACID 特性有完整并且严格的要求。下面分别介绍 OceanBase 针对事务 ACID 的特性的解决方案。 ...

October 9, 2019 · 1 min · jiezi

Alibaba-Cloud-Linux-2阿里云Linux操作系统全面解析

2019年9月26日星期四,在杭州云栖大会的阿里云系统软件开发者专场上,来自阿里云智能基础软件技术专家贾正华(花名:晓贾)做了主题为《Alibaba Cloud Linux 2-阿里云Linux操作系统》的技术分享,主要就Alibaba Cloud Linux 2(原Aliyun Linux 2)如何为云上客户提供一个优秀的的操作系统展开了全面的解析。 很多观众肯定会想知道为什么阿里云要做一个Linux操作系统,其实阿里巴巴从09年开始就在做操作系统相关的研究、开发工作,只是以前定制的操作系统主要是用在内部的一些应用上,而随着阿里巴巴技术的演进,我们觉得有必要将内部这么多年的经验和积累与客户一起分享,而且现在云上用户也对操作系统反馈了很多诉求,所以我们为阿里云和阿里云上的客户量身定制了Alibaba Cloud Linux 2。 在谈到云上用户对于操作系统的安全诉求时,贾正华谈到Alibaba Cloud Linux 2主要通过三个措施来保障操作系统安全: 通过Alibaba Cloud Linux 2 CIS benchmark(注1)可以帮助用户选择配置最适合自己的系统安全,包括但不限于系统初始化安全配置、系统服务安全配置、网络安全配置、日志监控安全配置等等,涵盖了整个系统安全配置的方方面面;通过对操作系统kernel、服务、配置等方面进行精细化的裁剪,大大降低了系统的受攻击面;最后得益于阿里巴巴安全技术团队为操作系统提供全方位的安全保障,包括:漏洞挖掘、全域CVE监测、环境模拟攻击等等措施保障系统安全。而且安全团队还可以从组织或者参与的安全社区从全社会获取安全情报,提前发现威胁、消除威胁;在性能方面,Alibaba Cloud Linux 2也做了非常多的工作,使启动时长下降了30%,运行时性能提升10%~30%: 在介绍Alibaba Cloud Linux 2为什么能够提升如此多的性能,贾正华谈到,启动时长得益于操作系统团队对OS的精细化裁剪和对内核启动阶段部分流程的代码优化,而运行时性能得益于我们选择了更优的4.19 LTS内核版本,并且结合阿里云基础设施对内核进行了多方面的优化,包括调度、内存、网络等。 在生态方面,Alibaba Cloud Linux 2为了让用户得到一个功能丰富全面、云上配置使用简单的OS,首先在保证全面兼容CentOS和多个upstream社区的同时,也天生搭载阿里巴巴技术例如Alibaba Cloud CLI、Alibaba Cloud OPENAPI,方便用户开箱即用,而且Alibaba Cloud Linux 2是一个完全开源(注2)、开放的系统,代码人人可见,人人可以用: 对于很多企业都关心的稳定性,Alibaba Cloud Linux 2依托于多个活跃稳定的强大社区base、阿里巴巴经济体海量应用的规模化验证、操作系统团队端到端的维护,极大的保证了线上操作系统的质量: 操作系统对于很多上层应用软件来说太底层了,甚至于应用可能完全没法感知的到,所以可能很多企业或者个人没法对其实施很多运维动作,肯定希望我们能够提供操作系统的服务,所以Alibaba Cloud Linux 2向用户提供包括但不限于问题支持、系统优化、特性订制等服务。 阿里云的用户可以通过阿里云工单系统来直接与我们联系,其他用户可以通过Alibaba Cloud Linux 2社区和钉钉来与我们联系。最重要的一点是操作系统团队所提供的服务都是“免费”的。 用户可以使用以下的方式与操作系统团队联系: Linux操作系统是一个非常庞大而复杂的系统,没有任何一个团体或者个人说我们已经能够完全掌控住他了,整个系统的持续演进不仅仅需要专业的团队进行维护,也希望有更多的企业、个人、社区一起参与共建。 注释:1、针对Alibaba Cloud Linux 2定制的CIS benchmark,详情参见:https://workbench.cisecurity.org/benchmarks/22282、Alibaba Cloud Linux 2代码完全开源,github地址:https://alibaba.github.io/cloud-kernel/zh/os.html ...

September 30, 2019 · 1 min · jiezi

Timestream开发最佳实践

背景Timestream模型是针对时序场景设计的特有模型,可以让用户快速完成业务代码的开发,实现相关业务需求。但是,如果业务系统不仅想实现基础的相关业务功能,还要达到最佳的性能,并且兼顾到未来的扩展性的话,就不是一件特别容易的事情。 本文会以共享汽车管理平台为例,介绍一系列的timestream最佳设计和使用,给业务设计和使用提供一些参考。关于共享汽车管理平台的场景,细节请参考:《基于Tablestore的共享汽车管理平台》。 场景和模型简介 在共享汽车管理平台这个场景中,主要是对车辆的状态轨迹监控、车俩元数据以及订单元数据进行管理。另外,还会对相关的数据进行计算分析并存储相关结果: 车辆状态轨迹:记录了车辆的状态监控,比如车速、位置、续航等数据,另外还需要记录车辆行驶过程中的违章记录,比如:是否超速、是否闯红灯等等;车辆元数据:记录车辆的基本属性信息,便于用户进行车辆检索,比如:车型、车牌、颜色等;订单元数据:订单相关信息记录,包含行程的起止时间、车辆、用户、费用等信息业务主要是对上面三部分数据进行查询和检索,满足业务场景的需求。其中车辆元数据以及状态轨迹数据是典型的时序序列,可以很方便的映射到Timestream模型中。 下图是数据模型的映射: 下面介绍一下模型设计的细节以及设计中需要注意的一些优化点,这些优化点对于业务功能以及性能上都有一定的提升。 业务模型设计在Timestream模型中,主要包含了元数据和数据点两部分数据,分别使用一个元数据表以及若干个数据表进行存储。下面介绍这两类数据在存储设计的关键点。 元数据表设计在共享汽车这个场景下,元数据表主要存储两类数据:车辆的基本信息、车辆的最近状态数据(位置、续航、状态、违章统计等),业务会根据各类信息进行多条件的组合查询符合条件的车辆。 如上图所示,Timestream的元数据表会通过多元索引来提供丰富的数据检索能力。在Timestream模型的元数据中,包含了name、tags、attributes三类数据,其中name、tags默认会提供数据检索能力,attributes则需要在创建Meta表的时候指定需要索引的attributes字段以及相关信息,默认attributes并不支持检索。需要注意的是,目前并不支持动态修改Meta表的索引字段,所以最好能在设计之初能够考虑到当前以及未来的功能需求,下面介绍一下相关信息是如何映射到模型以及相关的设计。 name设计name字段的选取是很关键的,是数据检索性能的一个重要影响因素,不同的name字段设计可能会导致查询延时相差一个数量级。name字段的选取建议满足以下条件: 绝大多数查询场景都会对该字段进行精确查询该字段单个取值下的最大记录数不宜过多,比如说不超过一千万条记录在共享汽车管理平台这个场景下,管理的是各个平台的车辆,而在车辆检索的时候,一定会指定的条件是平台的名字,并且某个平台的车辆其实也不会太多,一般也就百万量级,所以这里可以将平台作为name。 tags设计在Timestream模型中,Name和Tags可以唯一确定某个元数据,在这个场景中,唯一确定某辆车的信息是:平台、车辆ID,其中平台是name,所以,tags中只需要存储ID即可。tags设计需要注意: tags的总长度尽可能的短,只把唯一确定主体的信息放到tag中,其余信息均放到attributes中tag只支持string类型的数据,如果业务字段是数值类型,需要将其转成string进行存储attributes设计attributes是主体的可变属性,也可以用来存储主体的非唯一属性。在这个场景中,车辆的基本信息以及当前状态都是用attributes来进行存储。attributes设计关键点: 创建meta表的时候需要指定有检索需求的attributes以及相关属性,默认attributes是不支持索引的数值型数据尽可能使用int来存储,attribute支持多类型的数据,但在数据检索过程中,int类型的数据检索效率比string类型高的多,建议使用int,索引类型为LONG考虑未来系统的扩展性,可以预留一列作为扩展字段,索引类型为KEYWORD,并且是Array索引创建示例代码: public void createMetaTable() { db.createMetaTable(Arrays.asList( new AttributeIndexSchema("地区", AttributeIndexSchema.Type.KEYWORD), ... // 数值类型索引 new AttributeIndexSchema("座位", AttributeIndexSchema.Type.LONG), ... // 扩展字段,数组类型索引 new AttributeIndexSchema("配置1", AttributeIndexSchema.Type.KEYWORD).isArray() ));数据表设计Timestream可以支持多个数据表的存储,来满足不同的业务场景需求。另外,为了能够利用底层引擎所做的性能优化,我们推荐append的写入方式,即不会对已有数据进行修改,所以在以下场景中,我们建议业务将数据分到不同数据表中进行存储: 数据精度不同,特别是在监控场景下,这个需求更加突出。按数据精度分表便于后续数据的查询,如果查询长周期的数据可以去查询低精度的表,减少查询的数据量,提高查询效率需要对数据进行加工处理,也就是会对数据进行更新,建议将处理之后的数据写到另外一张表中在共享汽车这个场景中,需要对车辆的状态轨迹数据进行流式处理,比如检测是否超速等违章、车辆状态轨迹是否异常等,然后将处理之后的数据写到另外一张表中,提供给业务进行查询。 sdk使用前面介绍了业务模型设计需要注意的地方,对业务功能拓展能力以及性能都有一定的提升。下面介绍一下timestream sdk使用的一些性能优化点。 数据写入元数据元数据写入支持两种方式:put和update。其中put会删除老的记录,并且插入一个全新行;update则是对原有记录的部分attributes进行更新。建议尽量使用Put的方式进行写入。示例代码: public void writeMeta() { TimestreamIdentifier identifier = new TimestreamIdentifier.Builder("*滴") .addTag("ID", carNo) .build(); TimestreamMeta meta = new TimestreamMeta(identifier) .addAttribute("地区", "杭州") .addAttribute("座位", 4) ... .addAttribute("状态", "闲置"); // 插入车辆信息 metaWriter.put(meta);}数据点数据点写入也提供了两种方式:同步和异步。其中异步接口底层是通过TableStoreWriter进行异步写入,其写入吞吐能力更高,对写入延时不是特别敏感的业务建议使用异步方式。示例代码: ...

August 28, 2019 · 1 min · jiezi

阿里巴巴在应用性能测试场景设计和实现上的实践

本文是《Performance Test Together》(简称PTT)系列专题分享的第5期,该专题将从性能压测的设计、实现、执行、监控、问题定位和分析、应用场景等多个纬度对性能压测的全过程进行拆解,以帮助大家构建完整的性能压测的理论体系,并提供有例可依的实战。 该系列专题分享由阿里巴巴 PTS 团队出品,欢迎在文末处加入性能压测交流群,参与该系列的线上分享。 第1期:《压测环境的设计和搭建》 第2期:《性能压测工具选型对比》 第3期:《阿里巴巴在开源压测工具 JMeter 上的实践和优化》 第4期:《并发模式与 RPS 模式之争,性能压测领域的星球大战》 本文将介绍应用性能测试场景的设计和实现,旨在借助阿里巴巴在这方面的沉淀帮助您更准确的找到性能瓶颈,文章将围绕以下: 性能测试的常见分类应用性能测试场景的设计应用性能测试场景的设计实践应用性能测试场景的实现性能测试的常见分类负载测试:一种验证性测试,它的目的是验证预设负载条件下的性能表现是否达到性能目标(可用性、并发数/RPS、响应时间等),在达到性能目标之后不会继续增加负载。 稳定性测试:负载测试的一个子集,侧重于发现、验证只有经过长时间的运行才会暴露的问题。比如内存泄漏、FGC 等。 压力测试:一种破坏性测试,尝试探测应用或者基础设施的极限能力。因此压力测试过程中会一直增加负载直到部分性能指标不再符合性能预期。压力测试能发现仅在高负载条件下出现的同步问题、竞争条件、内存泄漏等。通过压力测试我们还可以确定应用服务在什么条件下会变得不可用,不可用的现象,以及可以通过哪些监控指标来监控即将发生的不可用,压测结果通常可以为限流等管控系统提供数据支撑。 容量测试:往往与容量规划一起进行,是在保证用户体验不受影响(稳定性)的前提下,使有限的资源的利用率最大化(成本)。也可以用它来预估未来用户量增长到某个量级的情况下,需要多少资源(例如处理器、内存、磁盘、网络带宽)来支持。 应用性能测试场景的设计在了解了相关背景之后,我们开始进入正题。性能场景的设计主要包括:业务场景建模、测试数据准备、监控指标确认三个关键步骤。下面我们用实战的方式说明每个步骤的常见做法。 业务场景建模确定压测场景范围:人类是不可预测的,在性能测试中模拟每个用户可能的操作场景基本上是不可能实现的。一般情况下我们必须要关注的性能场景包括但不限于: 高频使用的场景关键的业务场景最耗性能的场景曾经出现过问题的场景……在测试具有大量新功能的业务时,往往需要与业务方一起确认预期内有哪些功能点可能会被高频使用,需要与研发人员确认哪些功能虽然使用频率不高,但是存在性能隐患、容易引起雪崩效应;在测试已经上线的功能时,还可以通过业务监控、系统日志来分析现有用户的行为模式,得到更加逼近真实用户行为的业务场景。 业务场景的操作路径:业务场景的操作路径可以借助一些可视化的工具来描述,这部分工作相对比较简单,不再详细深入。我们详细说明一下比较常见的延时策略。 思考时间:思考时间模拟的是用户在等待响应、阅读页面内容、表单填写等延迟操作的场景。每个人的阅读速度、输入速度都存在非常大的差异,决定了每个人的思考时间也是不一样的,在性能测试配置中有常见的四种延时模型覆盖了绝大部分的延时场景: 固定时间:顾名思义,设置一个固定的思考时间。均匀分布:均匀分布在范围的上限和下限之间的随机数。正态分布:根据中心极限定理,如果一个事物受到多种因素的影响,不管每个因素本身是什么分布,它们加总后,结果的平均值就是正态分布。负指数分布:该模型将延迟时间的频率强烈地偏向该范围的一端。双驼峰正态分布:双峰驼正态分布可以模拟第一次访问时把页面说明整个仔细的阅读一遍,但下次访问时直接扫过页面,点击页面深处的操作链接。我们通常可以通过以下方式对思考时间进行建模: 如果是已上线系统,可以从线上日志统计分析出来平均值以及标准方差没有线上日志,可以从内部人员的使用模式中收集响应的数据可以计算自己和同事访问的时候,在不同页面停留的时间如果没有更好的来源,也可以从第三方统计数据获取延时数据集合点集合点模拟的是大量的用户在同一时刻一起做同样的操作(加购、付款等),集合的方式通常包括按时间集合和按量集合。一般只有具备秒杀特性的业务才会使用到。虽然直接在压测工具中设置巨大的起步量级看似也能模拟秒杀的行为,但是压测工具一般都存在一个不太稳定的预热的过程,因此不推荐超高的起步量级模拟秒杀。 确定场景的施压参数施压模式:常见的施压模式有以下两种, 并发模式与 RPS 模式没有优劣,各自有各自适用的场景。 1、并发模式(虚拟用户模式)并发是指虚拟并发用户数,从业务角度,也可以理解为同时在线的用户数。如果需要从客户端的角度出发,摸底业务系统各节点能同时承载的在线用户数,可以使用该模式设置目标并发。 2、RPS 模式(吞吐量模式)RPS(Requests Per Second)是指每秒请求数。RPS 模式即“吞吐量模式”,通过设置每秒发出的请求数,从服务端的角度出发,直接衡量系统的吞吐能力,免去并发到 RPS 的繁琐转化,一步到位。 目标量级:目标量级往往来自于对项目计划、目标,业务方要求,或者是技术文档的量化。 场景的负载占比:已上线应用,尽量使用线上的日志、埋点数据结合业务运营的预期目标确保分配比例尽可能的符合实际情况;新上线的应用一般依靠事前预期分配虚拟用户,在测试的执行过程中可以逐步的调整。 测试数据准备高质量的测试数据应当能真实的反映用户的使用场景。我们一般会选择以线上真实数据作为数据源,经过采样、过滤、脱敏,作为性能测试的测试数据。低质量的测试数据也许能够测试出一些问题,但是更大的可能性是无效的测试结果。压测数据至少包括基础数据和运行时数据两种。基础数据,主要是应用系统存储的元数据,比如用户信息、产品信息、商品信息等;基础数据的数据量、数据分布应当与线上运行的数据量相当,否则容易引起无效测试。运行时数据,主要是虚拟用户操作过程中需要使用的表单数据,比如虚拟用户的用户名、密码、搜索关键词等;运行数据的逼真度也是至关重要的。 确认监控指标在性能测试执行过程中,往往需要实时观察各项指标是否正常,包括客户端指标、应用服务器、数据库、中间件、网络入口等各方面的指标。更重要的是,监控的过程是发现系统瓶颈的过程,监控数据是性能基线管理、容量规划甚至是高可用架构的重要基础。我们通常需要关注的监控指标包括: 业务接口指标,响应时间、RPS、成功率等;网络指标,数据吞吐量、数据错误率等;服务器指标,连接数、CPU、内存、I/O、磁盘等;……最理想的状态是,这些监控指标能够与性能测试工具集成,在一个操作界面上展示各个维度的监控数据,并能够基于策略来智能化、自动化识别指标异常。这对快速、准确的定位压测过程中可能出现的各种问题是至关重要的。 应用性能测试场景设计的实践JPetStore 是一个开源的简单的Java语言开发的在线宠物商店,可以从 GitHub 获取到源码。为了方便演示,我们用阿里云 EDAS 部署了一套 JPetStore 宠物购物网站。 业务场景建模在这次的实战演示中,我们通过实际操作体验的方式来获取所有的业务场景、操作路径、思考时间。我们先用文字的方式来描述场景和操作路径。 用户登录,访问首页->进登录页->登录操作购买流程1,访问首页->选择产品大类->进入产品列表->进入型号列表->查看型号详情->加购物车->思考(3s-5s)->提交订单->确认订单购买流程2,访问首页->搜索产品->进入产品列表->进入型号列表->查看型号详情>加购物车->思考(3s-5s)->提交订单->确认订单购买流程3,访问首页->搜索商品->进入产品列表->进入型号列表->加购物车->思考(3s-5s)->提交订单->确认订单我们的目的是做压力测试。我们选择 RPS 模式,梯度递增,漏斗模型; 与并发模式相比,RPS 模式可以实现更加精准的流量控制;常见的限流设施都是基于TPS设置阈值的;因此我们首选 RPS 模式。我们使用手动递增的方式,逐步的逼近系统极限。在真实的业务中,用户会由于各种原因(网络、库存、不喜欢、付款失败等)而放弃购买,在此我们构造一个漏斗模型,我们假定100个人查看详情之后有30个人加入了购物车,15个人提交订单,最终10个人确认订单,购买成功;在真实的场景中我们可以从线上用户行为中采集到这些信息。假定用户登录容量足够,不是这次压力测试的重点业务。我们基于线上日志和产品运营分析得出以下结论: 使用购买流程1的用户占比10%使用购买流程2的用户占比60%使用购买流程3的用户占比为30%最终,我们得到的业务模型如下图所示: 测试数据准备因为该应用专为测试而生,不用考虑数据污染,我们免去采样、过滤、脱敏步骤,直接使用线上的基础数据作为压测的基础数据。基础数据的结构如下图所示,当然真实系统的基础数据,比这个复杂的多: 常见的压测工具都支持 CSV 格式(可以简单理解为逗号分隔值,但是实际上更加复杂)的数据源。我们构造的用户登录的运行时数据格式如下图所示: ...

August 20, 2019 · 1 min · jiezi

MongoDB-sharding-集合不分片性能更高

最近云上用户用户遇到一个 sharding 集群性能问题的疑惑,比较有代表性,简单分享一下 测试配置mongos x 2、shard x 3测试1:集合不开启分片,批量 insert 导入数据,每个 batch 100 个文档测试2:集合开启分片,随机生成 shardKey,chunk 已提前 split 好,能确保写入均分到3个shard测试结果测试1:单个 shard cpu 跑满,insert qps 在 6w 左右测试2:3个 shard cpu 跑满,insert qps 在 7w 左右(平均每个分片2.4w左右)注:两个测试里,mongos 都不是瓶颈,能力足够 从测试结果看,每个shard都承担 1/3 的负载,的确达到横向扩张的目的,但为啥分片之后,单个shard的能力就下降了呢?如果是这样,sharding的扩展能力如何体现? 结果分析这里核心的问题在于 batch insert 在 mongos 和 mongod 上处理行为的差别 导入数据时,一次 insert 一条数据,和一次 insert 100 条数据,性能差距是很大的;首先减少了client、server 端之间的网络交互;同时 server 可以将 batch insert 放到一个事务里,降低开销;mongos 在收到 batch insert 时,因为一个 batch 里的数据需要根据 shardKey 分布到不同的shard,所以一个 batch 实际上需要被拆开的;这里 mongos 也做了优化,会尽量将连续的分布在一个shard上的文档做 batch 发到后端 shard。在集合不开启分片的情况,mongos 收到的 batch 肯定是转发给 primary shard,所以转发过去还是一整个 batch 操作; 而在集合开启分片的情况下,因为用户测试时,shardKey 是随机生成的,基本上整个 batch 被打散成单条操作,逐个往后端 shard 上发送,请求到后端 shard 基本已经完全没有合并了。所以在上述测试中,不分片的单个 shard 6w qps、与分片后每个 shard 2.4w qps,实际上就是请求是否 batch 执行的差别。 ...

July 11, 2019 · 1 min · jiezi

译保持Nodejs的速度创建高性能Nodejs-Servers的工具技术和提示

pre-tips本文翻译自: Keeping Node.js Fast: Tools, Techniques, And Tips For Making High-Performance Node.js Servers 原文地址:https://www.smashingmagazine.... 中文标题:保持Node.js的速度-创建高性能Node.js Servers的工具、技术和提示 快速摘要Node 是一个非常多彩的平台,而创建network服务就是其非常重要的能力之一。在本文我们将关注最主流的: HTTP Web servers. 引子如果你已经使用Node.js足够长的时间,那么毫无疑问你会碰到比较痛苦的速度问题。JavaScript是一种事件驱动的、异步的语言。这很明显使得对性能的推理变得棘手。Node.js的迅速普及使得我们必须寻找适合这种server-side javacscript的工具、技术。 当我们碰到性能问题,在浏览器端的经验将无法适用于服务器端。所以我们如何确保一个Node.js代码是快速的且能达到我们的要求呢?让我们来动手看一些实例 工具我们需要一个工具来压测我们的server从而测量性能。比如,我们使用 autocannon npm install -g autocannon // 或使用淘宝源cnpm, 腾讯源tnpm其他的Http benchmarking tools 包括 Apache Bench(ab) 和 wrk2, 但AutoCannon是用Node写的,对前端来说会更加方便并易于安装,它可以非常方便的安装在 Windows、Linux 和Mac OS X. 当我们安装了基准性能测试工具,我们需要用一些方法去诊断我们的程序。一个很不错的诊断性能问题的工具便是 Node Clinic 。它也可以用npm安装: npm install -g clinic这实际上会安装一系列套件,我们将使用 Clinic Doctor和 Clinic Flame (一个 ox 的封装) 译者注: ox是一个自动剖析cpu并生成node进程火焰图的工具; 而clinic Flame就是基于ox的封装。另一方面, clinic工具本身其实是一系列套件的组合,它不同的子命令分别会调用到不同的子模块,例如:医生诊断功能。The doctor functionality is provided by Clinic.js Doctor.气泡诊断功能。The bubbleprof functionality is provided by Clinic.js Bubbleprof.火焰图功能。 The flame functionality is provided by Clinic.js Flame.)tips: 对于本文实例,需要 Node 8.11.2 或更高版本 ...

July 8, 2019 · 5 min · jiezi

Fundebug前端异常监控插件更新至-190支持监控-HTTP-慢请求

摘要: 1.9.0新增 httpTimeout 配置选项,支持监控 HTTP 慢请求,同时修复了记录的 HTTP 响应时间偏小的 BUG。 Fundebug提供专业的前端异常监控服务,可以第一时间捕获线上环境中小程序的异常、错误或者 BUG,及时给开发者推送报警,帮助您快速修复 BUG。 Fundebug的微信小程序BUG监控插件更新至1.9.0,新增httpTimeout配置选项,支持监控 HTTP 慢请求,同时修复了记录的 HTTP 响应时间偏小的 BUG,请大家及时更新! 监控 HTTP 慢请求Fundebug 专注于程序异常监控,暂时无意于提供全面的性能监控服务。但是,当 HTTP 请求过慢,导致用户体验很糟糕时,也可以理解为一种广义的 BUG。HTTP 请求的性能问题,可能是代码的算法不够好导致的,可能是业务逻辑有问题,可能是应用架构不合理,有可能是数据库的索引不合理导致的,还有可能是其他原因,这些都是技术层面的”BUG“,需要开发者及时处理。 当然,监控所有 HTTP 请求的响应时间不是我们 Fundebug 需要做的事情,因此我们只支持监控慢请求。用户只需要配置一个阈值httpTimeout,所有响应时间超过阈值的 HTTP 请求都会上报的 Fundebug,这样可以帮助开发者发现一些慢请求,及时优化性能。 互联网由粗放式发展逐渐转向精细化发展,这也要求开发者对线上应用进行更加严格的监控,尽量优化性能、减少BUG,这也才能提高产品质量,赢得客户的信任,欢迎大家免费试用Fundebug的前端异常监控服务。 httpTimeout监控 HTTP 慢请求的正确方式是通过 Fundebug 的配置选项httpTimeout来实现。 httpTimeout 类型为 Number,单位为毫秒(ms)。 如果你希望监控较慢的 HTTP 请求,则可以通过httpTimeout配置阈值,比如 1000: if ("fundebug" in window) { fundebug.httpTimeout = 1000;}则所有响应时间超过 1000ms 的请求都会上报到 Fundebug。 例如,Fundebug上传Source Map的接口比较慢,这是因为source map文件太大导致的,这个问题也需要进一步优化,比如可以在前端压缩source map文件之后再上传。 最后,感谢 Fundebug 用户yaoqi的反馈。 ...

July 5, 2019 · 1 min · jiezi

数据人看Feed流架构实践

背景Feed流:可以理解为信息流,解决的是信息生产者与信息消费者之间的信息传递问题。我们常见的Feed流场景有:1 手淘,微淘提供给消费者的首页商品信息,用户关注店铺的新消息等2 微信朋友圈,及时获取朋友分享的信息3 微博,粉丝获取关注明星、大V的信息4 头条,用户获取系统推荐的新闻、评论、八卦 关于Feed流的架构设计,包括以上场景中的很多业内专家给出了相应的思考、设计和实践。本人是大数据方向出身的技术人,所在的团队参与了阿里手淘、微淘Feed流的存储层相关服务,我们的HBase/Lindorm数据存储产品在公有云上也支持着Soul、趣头条、惠头条等一些受欢迎的新媒体、社交类产品。我们在数据存储产品的功能、性能、可用性上的一些理解,希望对真实落地一个Feed流架构可以有一些帮助,以及一起探讨Feed流的未来以及数据产品如何帮助Feed流进一步迭代。 本文希望可以提供两点价值: 1 Feed流当前的主流架构以及落地方案2 一个初创公司如何选择Feed流的架构演进路径 业务分析Feed流参与者的价值信息生产者希望信息支持格式丰富(文字、图片、视频),发布流畅(生产信息的可用性),订阅者及时收到消息(时效性),订阅者不漏消息(传递的可靠性) 信息消费者希望及时收到关注的消息(时效性),希望不错过朋友、偶像的消息(传递的可靠性),希望获得有价值的消息(解决信息过载) 平台希望吸引更多的生产者和消费者(PV、UV),用户更长的停留时间,广告、商品更高的转化率 Feed信息传递方式一种是基于关系的消息传递,关系通过加好友、关注、订阅等方式建立,可能是双向的也可能是单向的。一种是基于推荐算法的,系统根据用户画像、消息画像利用标签分类或者协同过滤等算法向用户推送消息。微信和微博偏向于基于关系,头条、抖音偏向于基于推荐。 Feed流的技术难点互联网场景总是需要一定规模才能体现出技术的瓶颈,下面我们先看两组公开数据: 新浪微博为例,作为移动社交时代的重量级社交分享平台,2017年初日活跃用户1.6亿,月活跃用户近3.3亿,每天新增数亿条数据,总数据量达千亿级,核心单个业务的后端数据访问QPS高达百万级(来自 Feed系统架构与Feed缓存模型) 截止2016年12月底,头条日活跃用户7800W,月活跃用户1.75亿,单用户平均使用时长76分钟,用户行为峰值150w+msg/s,每天训练数据300T+(压缩后),机器规模万级别(来自 今日头条推荐系统架构设计实践) 上面还是两大巨头的历史指标,假设一条消息1KB那么千亿消息约93TB的数据量,日增量在几百GB规模且QPS高达百万,因此需要一个具备高读写吞吐,扩展性良好的分布式存储系统。用户浏览新消息期望百毫秒响应,希望新消息在秒级或者至少1分钟左右可见,对系统的实时性要求很高,这里需要多级的缓存架构。系统必须具备高可用,良好的容错性。最后这个系统最好不要太贵。 因此我们需要一个高吞吐、易扩展、低延迟、高可用、低成本的Feed流架构 主流架构图1是对Feed流的最简单抽象,完成一个从生产者向消费者传递消息的过程。 消息和关系首先,用户在APP侧获得的是一个Feed ID列表,这个列表不一定包含了所有的新消息,用户也不一定每一个都打开浏览,如果传递整个消息非常浪费资源,因此产生出来的消息首先生成主体和索引两个部分,其中索引包含了消息ID和元数据。其次一个应用总是存在关系,基于关系的传递是必不可少的,也因此一定有一个关系的存储和查询服务。 消息本身应该算是一种半结构化数据(包含文字,图片,短视频,音频,元数据等)。其读写吞吐量要求高,读写比例需要看具体场景。总的存储空间大,需要很好的扩展性来支撑业务增长。消息可能会有多次更新,比如内容修改,浏览数,点赞数,转发数(成熟的系统会独立一个counter模块来服务这些元数据)以及标记删除。消息一般不会永久保存,可能要在1年或者3年后删除。 综上,个人推荐使用HBase存储 HBase支持结构化和半结构化数据;具有非常好的写入性能,特别对于Feed流场景可以利用批量写接口单机(32核64GB)达到几十万的写入效率;HBase具备非常平滑的水平扩展能力,自动进行Sharding和Balance;HBase内置的BlockCache加上SSD盘可以提供ms级的高并发读;HBase的TTL特性可以自动的淘汰过期数据;利用数据复制搭建一个冷热分离系统,新消息存储在拥有SSD磁盘和大规格缓存的热库,旧数据存储在冷库。运用编码压缩有效的控制存储成本,见HBase优化之路-合理的使用编码压缩 对于关系服务,其写入操作是建立关系和删除关系,读取操作是获取关系列表,逻辑上仅需要一个KV系统。如果数据量较少可以使用RDS,如果数据量较大推荐使用HBase。如果对关系的QPS压力大可以考虑用Redis做缓存。 消息传递讲到Feed流一定会有关于推模式和拉模式的讨论,推模式是把消息复制N次发送到N个用户的收信箱,用户想看消息时从自己的收信箱直接获取。拉模式相反,生产者的消息写入自己的发信箱,用户想看消息时从关注的M个发信箱中收集消息。 推模式实现相对简单,时效性也比较好。拉模式要想获得好的性能需要多级的缓存架构。推模式重写,拉模式重读,Feed流场景下写的聚合效果要优于读,写可以大批量聚合。N越大,写入造成的数据冗余就越大。M越大,读消耗的资源越大。 随着业务的增长,推模式资源浪费会越发严重。原因在于两点:第一存在着大量的僵尸账号,以及大比例的非活跃用户几天或者半个月才登陆一次;第二信息过载,信息太多,重复信息太多,垃圾信息太多,用户感觉有用的信息少,消息的阅读比例低。这种情况下推模式相当一部分在做无用功,白白浪费系统资源。 是推?是拉?还是混合?没有最好的架构,只有适合的场景~ 基于关系的传递图6是纯推模式的架构,该架构有3个关键的部分 异步化。生产者提交消息首先写入一个队列,成功则表示发布成功,Dispatcher模块会异步的处理消息。这一点非常关键,首先生产者的消息发布体验非常好,不需要等待消息同步到粉丝的收信箱,发布延迟低成功率高;其次Dispatcher可以控制队列的处理速度,可以有效的控制大V账号造成的脉冲压力。多级队列。Dispatcher可以根据消费者的状态,信息的分类等划分不同的处理方式,分配不同的资源。比如对于大V账号的消息,当前活跃用户选择直接发送,保障消息的时效性,非活跃用户放入队列延迟发送。比如转发多的消息可以优先处理等。队列里的消息可以采用批量聚合写的方式提高吞吐。收信箱。假如有两亿用户,每个用户保留最新2000条推送消息。即便存储的是索引也是千亿的规模。收信箱一般的表结构为用户ID+消息序列 + 消息ID + 消息元数据,消息序列是一个递增的ID,需要存储一个偏移量表示上次读到的消息序列ID。用户读取最新消息 select * from inbox where 消息序列 > offset。 推荐使用HBase实现收信箱 HBase单机批量写能力在几十万并且可以水平扩展。HBase的高效前缀扫描非常适合读取最新的消息。HBase的TTL功能可以对数据定义生命周期,高效的淘汰过期数据。HBase的Filter过滤器和二级索引可以有效的实现Inbox的搜索能力。消费者收信箱hbase表设计如下,其中序列号要保证递增,一般用时间戳即可,特别高频情况下可以用一个RDS来制造序列号 Rowkey消息元数据列状态列其它列MD5(用户ID)+用户ID+序列号消息ID、作者、发布时间、关键字等已读、未读 图7是推拉结合的模式 增加发信箱,大V的发布进入其独立的发信箱。非大V的发布直接发送到用户的收信箱。其好处是解决大量的僵尸账号和非活跃账号的问题。用户只有在请求新消息的时候(比如登陆、下拉消息框)才会去消耗系统资源。发信箱的多级缓存架构。一个大V可能有百万粉丝,一条热点消息的传播窗口也会非常短,即短时间内会对发信箱中的同一条消息大量重复读取,对系统挑战很大。终态下我们可能会选择两级缓存,收信箱数据还是要持久化的,否则升级或者宕机时数据就丢失了,所以第一层是一个分布式数据存储,这个存储推荐使用HBase,原因和Inbox类似。第二层使用redis缓存加速,但是大V过大可能造成热点问题还需要第三层本地缓存。缓存层的优化主要包括两个方向:第一提高缓存命中率,常用的方式是对数据进行编码压缩,第二保障缓存的可用性,这里涉及到对缓存的冗余。 基于推荐的传递图8是基于推荐的模型,可以看出它是在推拉结合的模式上融合了推荐系统。 引入画像系统,保存用户画像、消息画像(简单情况下消息画像可以放在消息元数据中)。画像用于推荐系统算法的输入。引入了临时收信箱,在信息过载的场景中,非大V的消息也是总量很大,其中不免充斥着垃圾、冗余消息,所以直接进入用户收信箱不太合适。收信箱和发信箱都需要有良好的搜索能力,这是推荐系统高效运行的关键。Outbox有缓存层,索引可以做到缓存里面;Inbox一般情况下二级索引可以满足大部分需求,但如果用户希望有全文索引或者任意维度的检索能力,还需要引入搜索系统如Solr/ES 用户画像使用HBase存储 画像一般是稀疏表,画像总维度可能在200+甚至更多,但单个用户的维度可能在几十,并且维度可能随业务不断变化。那么HBase的Schema free和稀疏表的能力非常适合这个场景,易用且节省大量存储空间。对画像的访问一般是单行读,hbase本身单行Get的性能就非常好。阿里云HBase在这个方向上做了非常多的优化,包括CCSMAP、SharedBucketCache、MemstoreBloomFilter、Index Encoding等,可以达到平均RT=1-2ms,单库99.9% <100ms。阿里内部利用双集群Dual Service可以做到 99.9% < 30ms,这一能力我们也在努力推到公有云。hbase的读吞吐随机器数量水平扩展。临时收信箱使用云HBase HBase的读写高吞吐、低延迟能力,这里不再重复。HBase提供Filter和全局二级索引,满足不同量级的搜索需求。阿里云HBase融合HBase与Solr能力,提供低成本的全文索引、多维索引能力。初创公司的迭代路径在业务发展的初期,用户量和资源都没有那么多,团队的人力投入也是有限的,不可能一上来就搞一个特别复杂的架构,“够用”就行了,重要的是 可以快速的交付系统要稳定未来可以从容的迭代,避免推倒重来本人水平有限,根据自身的经验向大家推荐一种迭代路径以供参考,如有不同意见欢迎交流 ...

July 3, 2019 · 1 min · jiezi

性能压测工具选型对比

本文是《Performance Test Together》(简称PTT)系列专题分享的第二期,该专题将从性能压测的设计、实现、执行、监控、问题定位和分析、应用场景等多个纬度对性能压测的全过程进行拆解,以帮助大家构建完整的性能压测的理论体系,并提供有例可依的实战。 该系列专题分享由阿里巴巴 PTS 团队出品。 第一期:《压测环境的设计和搭建》,点击这里。 本文致力于给出性能压测的概念与背景介绍,同时针对市场上的一些性能压测工具,给出相应的对比,从而帮助大家更好地针对自身需求实现性能压测。 为什么要做性能压测在介绍性能压测概念与背景之前,首先解释下为什么要做性能压测。从09年的淘宝双十一大促导致多家合作银行后台系统接连宕机,到春运期间12306购票难,再到前不久聚美优品促销活动刚开始就遭秒杀。根据Amazon统计,每慢100毫秒,交易额下降1%。这些事件和统计数据为大家敲响了警钟,也客观说明了性能压测对于企业应用的重要性。 从具体的作用上讲,性能压测可以用于新系统上线支持、技术升级验证、业务峰值稳定性保障、站点容量规划以及性能瓶颈探测。 1. 新系统上线支持在新系统上线前,通过执行性能压测能够对系统的负载能力有较为清晰的认知,从而结合预估的潜在用户数量保障系统上线后的用户体验。 2. 技术升级验证在系统重构过程中,通过性能压测验证对比,可以有效验证新技术的高效性,指导系统重构。 3. 业务峰值稳定性保障在业务峰值到来前,通过充分的性能压测,确保大促活动等峰值业务稳定性,保障峰值业务不受损。 4. 站点容量规划通过性能压测实现对站点精细化的容量规划,指导分布式系统机器资源分配。 5. 性能瓶颈探测通过性能压测探测系统中的性能瓶颈点,进行针对性优化,从而提升系统性能。 综上所述,性能压测伴随着系统开发、重构、上线到优化的生命周期,因此有效的性能压测对系统的稳定性具有重要的指导意义,是系统生命周期中不可或缺的一部分。 性能压测概念性能压测是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。从测试目的上性能压测又可以划分为负载测试、压力测试、并发测试、配置测试以及可靠性测试。 负载测试是测试当负载逐渐增加时,系统各项性能指标的变化情况。压力测试是通过确定一个系统的瓶颈或者不能接受的性能点,来获得系统能提供的最大服务级别的测试。并发测试通过模拟用户并发访问,测试多用户并发访问同一个软件、同一个模块或者数据记录时是否存在死锁等性能问题。配置测试是通过对被测系统的软/硬件环境的调整,了解各种不同方法对软件系统的性能影响的程度,从而找到系统各项资源的最优分配原则。可靠性测试是在给系统加载一定业务压力的情况下,使系统运行一段时间,以此检测系统是否稳定。总的来说,性能压测是在对系统性能有一定程度了解的前提下,在确定的环境下针对压测需求进行的一种测试。 如何选取性能压测工具在选取合适的性能压测工具之前,我们需要先先了解执行一次完整的性能压测所需要的步骤: 1. 确定性能压测目标:性能压测目标可能源于项目计划、业务方需求等 2. 确定性能压测环境:为了尽可能发挥性能压测作用,性能压测环境应当尽可能同线上环境一致 3. 确定性能压测通过标准:针对性能压测目标以及选取的性能压测环境,制定性能压测通过标准,对于不同于线上环境的性能压测环境,通过标准也应当适度放宽 4. 设计性能压测:编排压测链路,构造性能压测数据,尽可能模拟真实的请求链路以及请求负载 5. 执行性能压测:借助性能压测工具,按照设计执行性能压测 6. 分析性能压测结果报告:分析解读性能压测结果报告,判定性能压测是否达到预期目标,若不满足,要基于性能压测结果报告分析原因 由上述步骤可知,一次成功的性能压测涉及到多个环节,从场景设计到施压再到分析,缺一不可。工欲善其事,必先利其器,而一款合适的性能工具意味着我们能够在尽可能短的时间内完成一次合理的性能压测,达到事半功倍的效果。 工具选型对比在论述了性能压测必要性之后,如何选取性能压测工具成为一个重要的议题?本文选取了市场上主流性能压测工具:(ab)Apache Bench、LoadRunner、JMeter、阿里云PTS,并从多个方面出发分析了各个工具的优缺点,汇总后的优缺点如下表所示: 压测工具Apache Bench(ab)LoadRunnerJMeterPTS学习成本低高高低安装部署成本低高高低是否免费是否是否是否支持多协议否是是是压测结果是否能够图形化展示否是是是是否支持TPS模式否否否是是否有链路、场景编排管理支持否是是是是否支持场景录制否是是是生态环境强弱弱弱弱强监控指标是否完备否否否是是否原生支持流量地域定制否否否是Apache Bench(ab) ab是一款用来针对HTTP协议做性能压测的命令行工具,支持在本地环境发起测试请求,验证服务器的处理性能。它主要具有以下特点: 首先,作为一款开源工具,ab具有较好的扩展性,测试开发人员可以基于自身需求对其进行二次开发,同时它对HTTP协议支持度较好,比如支持设定HTTP请求头、支持Cookie以及HTTP的多种方法。此外,使用ab时还可以通过指定性能压测产生的总请求数、并发数与压测时长控制性能压测,结合其能够输出性能压测过程中的TPS(每秒事务数)、RT(响应时延)等信息的特点,ab具有简单易上手的特点。但ab也存在一些缺点,如无图形化界面支持,支持协议较为单一,只支持HTTP协议,缺少对HTTPS协议、WebSocket等协议的支持,对于较为复杂的性能压测场景,ab缺少链路编排、场景管理等支持,只能够对单一地址发起性能压测,此外,它的性能压测统计指标纬度较少,缺少性能压测过程中的数据统计,只能够在压测结束后获取相关的统计数据,无法实时获取系统负载等指标,难以应用于生产环境下的性能压测。 总的来说,ab作为一款命令行测试工具,适用于本地对支持HTTP协议的单一地址进行性能压测,但缺少相应的链路编排、场景管理、数据可视化等大规模性能压测基础功能,无法应用于生产环境。 LoadRunner LoadRunner,是一款发布于1993年11月的预测系统行为和性能的负载测试工具。通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题,LoadRunner作为一款历史悠久的商业性能压测工具,能够对整个企业架构进行测试。企业使用LoadRunner能最大限度地缩短测试时间,优化性能和加速应用系统的发布周期。 LoadRunner可适用于各种体系架构的自动负载测试,能预测系统行为并评估系统性能。 LoadRunner从组件上可划分为四部分: 负载生成器:模拟用户对服务器发起请求虚拟用户生成器:捕捉用户业务流,用于录制和生成脚本控制器:用于提供场景设计与场景监控,能够实时监控脚本的运行情况分析器:汇集来自各种负载生成器的日志并格式化报告,以便可视化运行结果数据和监控数据从组件划分上可以看出 LoadRunner 对于性能压测拥有较为系统的支持,结合多个组件的功能特性,用户可以较为方便地设计复杂背景下的性能压测场景,例如结合场景设计设置虚拟用户数量、设置执行时间等,结合虚拟用户生成器实现复杂链路、场景的高效设计与编排。此外,LoadRunner支持设置思考时间、集合点,还可以结合分析器实现压测报告统计数据、指标的可视化,助力测试人员理解性能压测结果。但 LoadRunner 作为一款商业软件,价格较高,需要本地安装,安装过程较复杂,在实际设计执行压测时需要编写相应的脚本,对使用人员来说学习成本比较高,此外缺少监控告警等支持,性能压测过程中难以实时发现问题。 总的来说,LoadRunner 作为一款性能压测商业软件,功能较为齐全,使用者能够借助 LoadRunner 达到简单的性能压测场景编排、施压目标;但它也存在学习成本居高不下、扩展性差等缺点,此外支持的协议有限,不适合复杂的性能压测环境。 JMeter Apache JMeter是Apache组织开发的基于Java的压力测试工具。它可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、CGI 脚本、Java 对象、数据库、FTP 服务器等等。另外,JMeter能够对应用程序做功能/回归测试,通过创建带有断言的脚本来验证你的程序返回了你期望的结果。为了最大限度的灵活性,JMeter允许使用正则表达式创建断言。同时JMeter支持对性能压测结果做图形分析。 JMeter 作为一款开源软件,扩展性强,具有强大的开源社区支持,社区内开发者活跃程度高,也正是在开源社区的积极发展下,JMeter 具有性能压测的诸多特性,如支持场景编排、断言设置,支持对多种资源施压,有图形化界面支持,支持脚本录制,使用人员能够较为简单的设计并发起性能压测,此外 JMeter 提供资源监控、性能压测报告生成等功能。但在需要高负载施压的场景下,JMeter 需要部署分布式环境,部署成本比较高,在使用时,需要编写相应的脚本,而每个脚本文件只能保存一个测试用例,学习门槛居高不下的同时也不利于脚本的维护,此外它缺少监控告警等支持,在性能压测过程中使用人员难以借助 JMeter 实时发现问题。 ...

July 2, 2019 · 1 min · jiezi

坚持探索与落地并重阿里巴巴云原生之路全景揭秘

阿里妹导读:阿里云已经成功地规模化落地云原生,26日的 KubeCon 大会上,CNCF TOC 和阿里云资深技术专家李响发表主题演讲,分享了阿里巴巴在规模扩展、可靠性、开发效率、迁移策略等方面的经验,并探讨云原生的落地及应对若干技术挑战。为什么要做云原生?云原生究竟能带来什么价值?从最初的独自摸索到如今拥抱开源回馈社区,阿里巴巴走过了怎样的云原生旅程?又有哪些技术心得?今天,将全部分享出来。 多年沉淀,坚持探索与落地并重阿里巴巴从2011年开始通过容器实践云原生技术体系,在整个业界都还没有任何范例可供参考的大背境下,逐渐摸索出了一套比肩全球一线技术公司并且服务于整个阿里集团的容器化基础设施架构。这个探索历程虽然孤独,但却被始终如一的坚持至今。这正是在这个孤注一掷的技术探索与奋进的过程中,阿里巴巴的技术团队完整的经历了云原生技术浪潮里的所有关键节点,不仅成为了这次技术革命的重要见证者,也逐渐成为中国云原生技术体系当之无愧的推动者与引领者之一。 阿里的体量大、业务复杂,推动云原生要找到合适的切入点。在双十一成本压力的推动下,资源成本与效率优化成了阿里云原生的起点。 阿里从容器入手,研究低成本虚拟化与调度技术:提供灵活、标准的部署单元;将静态资源分配更换为动态按需调度,进一步提升部署效率,解决资源碎片化问题,提高部署密度;通过存储网络虚拟化和存储计算分离等技术,增强任务的可迁移性,进一步提高了资源的可靠性,降低了资源成本。 在资源成本的推动下,阿里完成了全面容器化,资源分配也被高效调度平台接管。阿里的云原生并未止步于此。提高研发效率与加快迭代周期是推动阿里业务增强的秘密武器。阿里希望通过云原生让开发者效率更高。 为了降低应用部署难度,提高部署自动化程度,阿里开始采用 Kubernetes 作为容器编排平台,并且持续推动 Kubernetes 的性能与可扩展性。具体 Kubernetes,阿里持续对研发、部署流程进行改进。为了构建更云原生化的 CI/CD,进一步做到标准化和自动化,从研发到上线流程,阿里引入了诸如 Helm 的应用标准化管理,也尝试了 GitOps 这样的部署流程,还推动了 PaaS 层的面向终态自动化改造。于此同时,阿里也开始探索服务网格,致力于进一步提高服务治理的普适性与标准性,降低开发者采用门槛,进一步推动微服务在多语言和多环境下的普及。 今年,阿里也展开了全站上云。经过云原生的探索与改造,阿里基础架构体系是现代化和标准化的。利用容器技术,应用与宿主机运行时完成了解耦;利用 Kubernetes 对 Pod 与 Volume 等的抽象,完成了对多种资源实现的统一化;通过智能调度与 PaaS 平台,让自动迁移应用,修复不稳定因素成为了可能,阿里通过云原生技术大大降低了上云的难度。 在这个提高资源和人员效率的过程中,阿里巴巴的整个基础设施也变得更加开放,连通开源生态,在交流互动中不断吸收和贡献好的理念、技术、思想。如今,阿里云不仅支撑着中国最大的云原生应用双11,而且拥有国内最大的公共云集群和镜像仓库。作为唯一入选 Gartner 的公有云容器服务竞争格局的厂商,阿里云也积累了最为丰富和宝贵的客户实践。 追求极致,优化扩展性和规模性弹性和规模性,这是支撑阿里巴巴各种类型的复杂场景以及流量高峰的关键因素。 经过不断打磨,阿里巴巴在 Kubernetes 规模与性能上取得了显著的成果:将存储object 的数量提升25倍,支持的节点数从5000提升到上万,在端到端调度延迟从5s变为100ms等等。其中有不少工作在阿里巴巴和社区中共同开展,而这些研发成果都已经贡献给社区,我们期望其他企业及开发者也可以享受阿里巴巴规模所带来的技术红利。 阿里巴巴持续优化性能,可以分为以下四个维度:工作负载追踪、性能分析、定制化调度、大规模镜像分发。首先对工作负载调度有完整的追踪、重放机制,其次将所有性能问题的进行细致分析,逐一攻克技术瓶颈。Kubernetes 本身的可定制性很强,阿里巴巴针对自身业务场景沉淀了定制化的调度能力和镜像分发系统。开源Dragonfly 项目脱胎于双十一,具备极强的镜像分发能力。数十个超级集群,每个超级集群具有数万节点,数百万的容器。 阿里巴巴落地 Kubernetes 可以分为三个阶段:首先通过 Kubernetes 提供资源供给,但是不过多干扰运维流程,这系统容器是富容器,将镜像标准化与轻量级虚拟化能力带给了上面的 PaaS 平台。第二步,通过 Kubernetes controller 的形式改造PaaS 平台的运维流程,给 PaaS 带来更强的面向终态的自动化能力。最后把运行环境等传统重模式改成原生容器与 pod 的轻量模式,同时将 PaaS 能力完全移交给Kubernetes controller,从而形成一个完全云原生的架构体系。 如何解决云原生的关键难点阿里巴巴云原生的探索,起步于自研容器和调度系统,到如今拥抱开源的标准化技术。对于当下开发者的建议是:如果想构建云原生架构,建议直接从 Kubernetes 入手即可。一方面,Kubernetes 为平台建设者而生,已经成为云原生生态的中流砥柱,它不仅向下屏蔽了底层细节,而且向上支撑各种周边业务生态;另一方面,更重要的是社区中有着越来越多围绕 Kubernetes 构建的开源项目,比如Service Mesh、Kubeflow。 ...

June 28, 2019 · 1 min · jiezi

Flutter-for-Web-详细预研

首先感谢@栖冰 @祖建国 一起对FFW的预研做的投入! 背景Google在最新的Google I/O上推出了Flutter for Web,旨在进一步解决一次代码,多端运行的问题。Flutter for Web还处于早期试验版,官方不建议在生产环境上使用。那么到底它的实际情况怎么样呢? 我们做了一次预研。期望这次预研的结果可以帮你决定是用,还是不用FFW。 Flutter for Web原理 Flutter for Web和Flutter在上层都是Dart环境,两者不同的是,Flutter的Dart代码运行在Dart虚拟机中,界面由Flutter引擎处理,通过Skia绘图引擎经由GPU绘制到屏幕上。而Flutter for Web的Dart代码编译成JavaScript,界面上部分转换成标准的html标签,部分转换成通过Canvas绘制的自定义标签,最终构成一个dom树。这个原理上的差异非常重要,这直接可以让我们通过原理得出下面的结论: Flutter for Web的一致性和体验上存在矛盾如果Flutter for Web追求(和Flutter)完美的一致性,势必需要大量使用Canvas去绘制,而Canvas去绘制组件的性能(尤其在移动端)至少不会比html标签好。如果FFW追求性能极限而使用大量标准的html标签,这就会带来和Weex、RN等一样的一致性问题:对于Flutter所有的控件都是一套代码在绘图引擎上绘制,对Flutter for Web如果要使用大量html标签,那如何保证一致性呢?只能靠大量精细的打磨工作了。所以FFW必须要处理好这个平衡。 为啥使用canvas绘制性能不优于手写html呢,定性的从几个角度分析: FFW在canvas上绘制的组件带有很多MD特色的视觉和动画,比如阴影、Z轴变化等,这部分对性能的消耗要大于普通html标签FFW是通过Dart的DSL转成的dom树结构,转化后的dom树十分复杂,不太可能比手写的dom树更简洁使用canvas的控件,其手势事件的捕获分发都是靠FFW框架自己实现的,emmmm虽然不排除Google大力出奇迹的情况,但是不管怎样,相同素质的开发人员,相同的界面,性能上也不可能优于html+css+js另外一点,如果FFW在原理上涉及大量HTML标签的转化,那就势必会涉及到碎片化的处理中,浏览器的碎片化程度可一点都不比Android系统的碎片化小。像Flutter本身之所以被那么多人看重,就是因为其通过绘图引擎这一层,完美的避开了碎片化,保证一致性。所以最好的平衡就是,只有有限的一部分标准的html标签可以被FFW复用,其必须有几点性质: 标签本身的功能简单又直观最好不要有直接图形化的展示,或者只负责简单的图形化展示(比如画方形)那几个比较典型的标签就是<p>、<div>这种了Flutter官方就是这么做的,所以我的结论是:一致性上大体不会存在问题,性能上,FFW应该不会优于纯手写html标签界面。 官方现状&建议根据官网和Github repo上的说法,我们整理了一下: Flutter for Web和Flutter目前暂时是两个仓库,官方正在进行合并,没有给出结论。这一点在工程上非常重要,它说明了几个问题: 目前官方对FFW的成熟度没有信心,同时FFW的迭代速度也很快。目前FFW和Flutter最多保证API一样,实现原理差异可能非常大,同时不保证所有控件都已经在FFW上实现。官方不建议应用在生产环境目前插件能力十分有限,和系统交互的一些能力缺失,比如拍照等。性能无法保证,运行会慢,可能会有掉帧FFW中针对桌面的UI部分没有完成(跟我无线有什么关系?)开发中只能在Chrome中调试(又有什么关系?),release版是可以运行在任意浏览器中(除了IE,另支持的最低版本存疑)。实践对于这么新的东西,官网上的内容的确不多,而且简单来看这些问题好像也没什么,所以对于到底能不能用,我们还是需要抱着吃螃蟹的心态具体进去预研一下,为了尽快弄清,我计划找一个我们app已经做好的flutter页面,把它迁移成FFW,对整个迁移过程做个评估,再看下页面效果,基本上就能得出结论了。具体的迁移细节就不提了,官网也有迁移文档,大体上就是这么几个步骤 安装Flutter for Web的工具webdev改SDK依赖,新增Web文件夹(和之前存在的android、ios文件夹同级),新增一些其他文件(index.html, main.dart等)。将所有flutter代码中依赖的flutter包,改成flutter_web包去掉所有不兼容的代码,比如多语言、路由、Platform.isAndroid等等编译运行实践的主要目的,有以下几个: 对整体坑的深度和广度有个认知,方便推算出填坑成本对FFW整体的性能和体验有个把握,尤其是我们自己的页面跑在FFW上是什么体验。对FFW和JS相互调用有具体的了解,如果可行,那复用集团已有能力(比如mtop)的坑就会小很多 删了一万行代码跑成功之后,最终在工程、开发体验、用户体验上得到一些结论,以下的结论中,体验部分是我在我的mix2s上的感受: 工程支持debug和release模式,后者比前者性能高(差异很明显)。 debug模式支持代码修改后自动重新编译,和其他的前端框架(我只用过Django)一致支持hotreload,暂时没有尝试支持webdev build命令编译出index.html+js,可以通过nginx做反向代理。非常重要 编译出来的代码,gzip压缩前,最简单的helloworld的main.dart.js大小约为500k左右,sample中的gallery大小约为2M,阉割版的纯展示用的订单列表大小约为1.3M。gzip对文本的压缩率一般是80%,压缩后也要动辄几百k的大小。而且main.dart.js不加载完,界面是不会展示的。开发体验非常重要flutter for web使用flutter_web库,且不支持其他许多插件,这会带来几个问题 工程上无法优雅的解决flutter和flutter_web共存的情况,最多搞个dart2的conditional import(这个特性可不在官方文档中哦)依赖flutter sdk的几个库,尤其是多语言库无法应用在flutter_web上,并且Google肯定不会再单独为flutter_web适配,而是在合并时做支持。这就意味着现阶段所有依赖flutter sdk的库不能被flutter_web使用。调试困难 错误日志可以打印到浏览器的console中,但是try catch部分的堆栈不好拿浏览器中的堆栈很复杂,但是基本上能找到出错的dart代码目前没有发现单步调试的能力Platform.isAndroid全部报错,针对Android和iOS做差异化展示目前还不知道有没有其他方法可以做到。目前没有发现控件的api不一致的地方,不过有些控件的行为十分异常,比如下拉刷新,在Android手机上经常卡死、失效。猜测重交互的一些控件都有可能存在类似的问题,但是测一遍的成本太高。图片控件NetworkImage可以直接用,但是现在来看有些糊,不确定是官方控件的问题,还是我们做的cdn url策略有问题。dart代码可以调用js,开发体验和反射类似,并且需要处理JS类型和Dart类型的转换。Dart和JS的交互速度未知。只能说哪怕FFW有很多浏览器的API不支持,也可以通过JS来扩展能力。Dart和Js语言本身的差异会带来测试和兼容成本的增加,虽然Dart可以编译成Js,但是跑在DartVM上的Dart的表现,和其编译成Js运行在浏览器中的表现并不完全相同。比如对于如下代码 Map<String, String> query = null;val b = query["abc"];在DartVM中该代码可以执行,结果是b=null;但是编译成Js以后运行时因为query为null,会报空指针。 用户体验使用了chrome、uc、小米自带浏览器分别试了一下订单列表和官方的sample-gallery界面,体验如下: 加载速度差不多,因为是局域网环境,感知不到差异。非常重要打出的唯一的main.dart.js和部分资源文件(比如MaterialIcons)没有加载出来的时候,界面不会展示。帧率或者流畅度上,chrome > uc >> 小米自带浏览器,其中chrome算是最流畅的体验,但是sample中的一些动画和页面转场也可以看到明显的卡顿。文字展示上,看flutter for web的界面,chrome对大部分文字处理的很清晰,小米自带浏览器看文字明显模糊。对比下面两张图,点开放大之后查看,FFW页面的文字模糊的很明显。 ...

June 27, 2019 · 1 min · jiezi

GMTC2019闲鱼基于Flutter的架构演进与创新

2012年应届毕业加入阿里巴巴,主导了闲鱼基于Flutter的新混合架构,同时推进了Flutter在闲鱼各业务线的落地。未来将持续关注终端技术的演变及趋势 Flutter的优势与挑战 Flutter是Google开源的跨端便携UI工具包,除了具有非常优秀的跨端渲染一致性,还具备非常高效的研发体验,丰富的开箱即用的UI组件,以及跟Native媲美的性能体验。由于它的众多优势,也使得Flutter成为了近些年来热门的新技术。 通过以上的特点可以看出,Flutter可以极大的加速客户端的研发效率,与此同时得到优秀的性能体验,基于我的思考,Flutter会为以下团队带来较大的收益: 中小型的客户端团队非常适合Flutter开发,不仅一端编写双端产出,还有效的解决了小团队需要双端人员(iOS:Android)占比接近1:1的限制,在项目快速推进过程中,能让整个团队的产能最大化。App在Android市场占比远高于iOS的团队,比如出海东南亚的一些App,Android市场整体占比在90%以上,通过Flutter可以将更多的人力Focus在Android市场上,同时通过在iOS端较小的投入,在结果上达到买一送一的效果。以量产App为主要策略的团队,不论是量产ToB的企业App,还是有针对性的产出不同领域的ToC的App的公司,都可以通过一端开发多端产出的Flutter得到巨大的产能提升。 闲鱼在以上的场景中属于第一种场景,服务3亿用户的闲鱼App的背后,开发资源投入很少,与竞对相比,我们是一只再小不过的团队,在这种场景下,Flutter为闲鱼业务的稳定发展以及提供更多的创新产品给予了很大的帮助。 但与此同时,Flutter在设计上带来的优势同时又会带来新的问题。所有的新技术都是脱胎于老技术的,Flutter也不例外,其身上带有很多Chrome的影子。我们再做一层简化,如果我们认为Flutter是一个使用Dart语言的浏览器容器,请大家思考一下两个问题如何解决。 如果在一个已经存在的App中加入Flutter,如何让Native与Flutter进行无缝的衔接,同时保证相互开发之间的隔离性如果在Flutter的容器中,使用已有的Native UI组件,在Flutter与Native渲染机制不同的情况下,怎么保证两者的无缝衔接以及高性能。闲鱼的架构演进与创新带着上面两个问题,我们来到闲鱼场景下的具体Case以及解决方案的演进过程。 已有App+Flutter容器 在这种情况下,闲鱼需要考虑的是首先要考虑引入Flutter容器后的内存压力,保证不要产生更多的内存溢出。与此同时我们希望能让Flutter和Native之间的页面切换是顺畅的,对不同技术栈之间的同学透明。因此我们有针对性的进行了多次迭代。 在没有任何改造的情况下以iOS为例,你可以通过创建新的FlutterViewController来创建一个新的Flutter容器,这个方案下,当创建多个FlutterViewController时会同时在内存中创建多个Flutter Engine的Runtime(虽然底层Dart VM依然只有一个),这对内存消耗是相当大的,同时多个Flutter Engine的Runtime会造成每个Runtime内的数据无法直接共享,造成数据同步困难。 这种情况下,闲鱼选择了全局共享同一个FlutterViewController的方式保证了内存占用的最小化,同时通过基础框架Flutter Boost提供了Native栈与Flutter栈的通信与管理,保证了当Native打开或关闭一个新的Flutter页面时,Dart侧的Navigator也做到自动的打开或关闭一个新的Widget。目前Google官方的提供的方案上就是参考闲鱼早先的这个版本进行的实现的。 然而在这种情况下,如果出现如闲鱼图中所示多个Tab的场景下,整个堆栈逻辑就会产生混乱,因此闲鱼在这个基础上对Flutter Boost的方案进行了升级并开源,通过在Dart侧提供一个BoostContainerManager的方式,提供了对多个Navigator的管理能力,如果打比方来看这件事,就相当于,针对Flutter的容器提供了一个类似WebView的OpenWindow的能力,每做一次OpenWindow的调用,就会产生一个新的Navigator,这样开发者就可以自由的选择是在Navigator里进行Push和Pop,还是直接通过Flutter Boost新开一个Navigator进行独立管理。 Flutter Boost目前已在github开源,由于闲鱼目前线上版本只支持Flutter 1.2的版本,因此需要支持1.5的同学等稍等,我们会在近期更新支持1.5的Flutter Boost版本。 Flutter页面+Native UI 由于闲鱼是一个闲置交易社区,因此图片和视频相对较多,对图片视频的线上性能以及内存占用有较严格的要求。目前Flutter已提供的几种方案中(Platform View以及Flutter Plugin),不论是对内存的占用还是整个的线上流畅度上还存在一定的问题,这就造成了当大部分同学跟闲鱼一样实现一个复杂的图文Feed推荐场景的时候,非常容易产生内存溢出。而实际上,闲鱼在以上的场景下有针对性的做出了较大的优化。 在整个的Native UI到Flutter渲染引擎桥接的过程中,我们选用了Flutter Plugin中提供的FlutterTextureRegistry的能力,在去年上半年我们优先针对视频的场景进行了优化,优化的思路主要是针对Flutter Engine底层的外接纹理接口进行修改,将原有接口中必须传入一个PixelBuffer的内存对象这一限制做了扩展,增加一个新的接口保证其可以传入一个GPU对象的TextureID。 如图中所示,优化后的整个链路Flutter Engine可以直接通过Native端已经生成好的TextureID进行Flutter侧的渲染,这样就将链路从Native侧生成的TextureID->copy的内存对象PixelBuffer->生成新的TextureID->渲染,转变为Native侧生成的TextureID->渲染。整个链路极大的缩短,保证了整个的渲染效率以及更小的内存消耗。闲鱼在将这套方案上线后,又尝试将该方案应用于图片渲染的场景下,使得图片的缓存,CDN优化,图片裁切等方案与Native归一,在享受已有集团中间件的性能优化的同时,也得到了更小的内存消耗,方案落地后,内存溢出大幅减少。 目前该方案由于需要配合Flutter Engine的修改,因此暂时无法提供完整的方案至开源社区,我们正在跟google积极沟通整个修改方案,相信在这一两个月内会将试验性的Engine Patch开源至社区,供有兴趣的同学参考。 复杂业务场景的架构创新实践 将以上两个问题解决以后,闲鱼开始了Flutter在业务侧的全面落地,然而很快又遇到新的问题,在多人协作过程中: 如何提供一些标准供大家进行参考保证代码的一致性如何将复杂业务进行有效的拆解变成子问题如何保证更多的同学可以快速上手并写出性能和稳定性都不错的代码 在方案的前期,我们使用了社区的Flutter Redux方案,由于最先落地的详情,发布等页面较为复杂,因此我们有针对性的对View进行了组件化的拆分,但由于业务的复杂性,很快这套方案就出现了问题,对于单个页面来说,State的属性以及Reducer的数量都非常多,当产生新需求堆叠的时候,修改困难,容易产生线上问题。 针对以上的情况,我们进行了整个方案的第二个迭代,在原有Page的基础上提供了Component的概念,使得每个Component具备完整的Redux元素,保证了UI,逻辑,数据的完整隔离,每个Component单元下代码相对较少,易于维护和开发,但随之而来的问题是,当页面需要产生数据同步时,整个的复杂性飙升,在Page的维度上失去了统一状态管理的优势。 在这种情况下闲鱼换个角度看端侧的架构设计,我们参考React Redux框架中的Connect的思想,移除掉在Component的Store,随之而来的是新的Connector作为Page和Component的数据联通的桥梁,我们基于此实现了Page State到Component State的转换,以及Component State变化后对Page State的自动同步,从而保证了将复杂业务有效的拆解成子问题,同时享受到统一状态管理的优势。与此同时基于新的框架,在统一了大家的开发标准的情况下,新框架也在底层有针对性的提供了对长列表,多列表拼接等case下的一些性能优化,保证了每一位同学在按照标准开发后,可以得到相对目前市面上其他的Flutter业务框架相比更好的性能。 目前这套方案Fish Redux已经在github开源,目前支持1.5版本,感兴趣的同学可以去github进行了解。 研发智能化在闲鱼的应用闲鱼在去年经历了业务的快速成长,在这个阶段上,我们同时进行了大量的Flutter的技术改造和升级,在尝试新技术的同时,如何能保证线上的稳定,线下的有更多的时间进行新技术的尝试和落地,我们需要一些新的思路和工作方式上的改变。 以我们日常工作为例,Flutter的研发同学,在每次开发完成后,需要在本地进行Flutter产物的编译并上传到远端Repo,以便对Native同学透明,保证日常的研发不受Flutter改造的干扰。在这个过程中,Flutter侧的业务开发同学面临着很多打包上传更新同步等繁琐的工作,一不小心就会出错,后续的排查等让Flutter前期的开发变成了开发5分钟,打包测试2小时。同时Flutter到底有没有解决研发效率快的问题,以及同学们在落地过程中有没有Follow业务架构的标准,这一切都是未知的。 在痛定思痛以后,我们认为数据化+自动化是解决这些问题的一个较好的思路。因此我们首先从源头对代码进行管控,通过commit,将代码与后台的需求以及bug一一关联,对于不符合要求的commit信息,不允许进行代码合并,从而保证了后续数据报表分析的数据源头是健康的。 在完成代码和任务关联后,通过webhook就可以比较轻松的完成后续的工作,将每次的commit有效的关联到我们的持续集成平台的任务上来,通过闲鱼CI工作平台将日常打包自动化测试等流程变为自动化的行为,从而极大的减少了日常的工作。粗略统计下来,在去年自动化体系落地的过程中单就自动打Flutter包上传以及触发最终的App打包这一流程就让每位同学每天节省一个小时以上的工作量,效果非常明显。另外,基于代码关联需求的这套体系,可以相对容易的构建后续的数据报表对整个过程和结果进行细化的分析,用数据驱动过程改进,保证新技术的落地过程的收益有理有据。 总结与展望回顾一下上下文 Flutter的特性非常适合中小型客户端团队/Android市场占比较高的团队/量产App的团队。同时由于Flutter的特性导致其在混合开发的场景下面存在一定劣势。闲鱼团队针对混合开发上的几个典型问题提供了对应的解决方案,使整个方案达到上线要求,该修改会在后续开放给google及社区。为全面推动Flutter在业务场景下的落地,闲鱼团队通过多次迭代演进出Fish Redux框架,保证了每位同学可以快速写出相对优秀的Flutter代码。新技术的落地过程中,在过程中通过数据化和自动化的方案极大的提升了过程中的效率,为Flutter在闲鱼的落地打下了坚实的基础。除了本文提及的各种方案外,闲鱼目前还在多个方向上发力,并对针对Flutter生态的未来进行持续的关注,分享几个现在在做的事情 Flutter整个上层基础设施的标准化演进,混合工程体系是否可以在上层完成类似Spring-boot的完整体系构架,帮助更多的Flutter团队解决上手难,无行业标准的问题。动态性能力的扩展,在符合各应用商店标准的情况下,助力业务链路的运营效率提升,保证业务效果。目前闲鱼已有的动态化方案会后续作为Fish-Redux的扩展能力提供动态化组件能力+工具链体系。Fish-Redux + UI2Code,打通代码生成链路和业务框架,保证在团队标准统一的情况下,将UI工作交由机器生成。Flutter + FaaS,让客户端同学可以成为全栈工程师,通过前后端一体的架构设计,极大的减少协同,提升效率。让工程师去从事更多创造性的工作,是我们一直努力的目标。闲鱼团队也会在新的一年更多的完善Flutter体系的建设,将更多已有的沉淀回馈给社区,帮助Flutter社区一起健康成长。 ...

June 26, 2019 · 1 min · jiezi

一条数据的漫游奇遇记

阿里妹导读:数据库存储引擎是一个有历史的技术,经过数十年的发展,已经出现很多优秀成熟的产品。阿里巴巴 X-Engine 团队撰写的论文 "X-Engine: An Optimized Storage Engine for Large-scale E-Commerce Transaction Processing",详细讲述了团队在数据库存储引擎上所做的原创性工作,今年早些时候已经被 SIGMOD'19 Industrial Track 接收(SIGMOD 是数据库领域最重要也是最有影响力的会议之一)。本文将对这篇论文做一个前导性分析。背景X-Engine 是阿里数据库产品事业部自研的 OLTP 数据库存储引擎,作为自研数据库POLARDB X 的存储引擎,已经广泛应用在阿里集团内部诸多业务系统中,其中包括交易历史库,钉钉历史库等核心应用,为业务大幅缩减了成本,同时也作为双十一大促的关键数据库技术,挺过了数百倍平时流量的冲击。 数据库存储引擎是一个有历史的技术,经过数十年的发展,已经出现很多优秀成熟的产品。各式存储引擎已经在索引组织,缓存管理,事务处理,查询优化方方面面都做过细致的研究。即便如此,这个领域的演进仍在持续,每年都会涌现很多的新技术。 近年来,LSM (Log-Structured Merge-Tree)结构受到越来越多的关注,虽然这个技术本身出现很多年了,不算什么新事物,不过早先在 KV 存储系统中被应用的更多一些,近年开始在数据库存储引擎领域崭露头角,RocksDB 即是典型代表。 LSM 之所以变得流行,一是因为其简单,二是特点鲜明。写入模型是简单的追加,不会更新既有的数据,数据组织为简单的逻辑排序,由此带来的特点是写强而读弱,持久化数据只读的特点便于压缩。但是大多数数据库的应用场景其实都是读多写少的,直接使用 LSM 结构未必合适,想要另辟蹊径,须得扬长辟短。 架构X-Engine 使用了 LSM 作为基础架构,目标是作为一个通用的高性能低成本存储引擎,追求读写性能更为均衡,因此在其上做了大量的改进,主要围绕几个方向进行: 利用先天优势,持续优化写性能。优化 compaction 降低对系统性能的冲击,使得系统性能表现趋于平稳。利用持久化数据层只读特点,发挥压缩优势降低成本。利用天然分层结构,结合硬件能力使用冷热分层结构,降低综合成本。利用精细化访问机制和缓存技术,弥补读性能短板。X-Engine 的整体架构如下图,根据数据冷热进行分层代替 LSM 本身的持久化数据分层,热数据层和数据更新使用内存存储,利用了大量内存数据库的技术(Lock-Free index structure/append only)提高事务处理的性能,设计了一套事务处理流水线处理机制,把事务处理的几个阶段并行起来,提升吞吐。而访问频度低的冷(温)数据逐渐淘汰或是合并到持久化的存储层次中,结合当前丰富的存储设备层次体系(NVM/SSD/HDD)进行存储。 我们对性能影响比较大的 compaction 过程做了大量优化,主要是拆分数据存储粒度,利用数据更新热点较为集中的特征,尽可能的在合并过程中复用数据,精细化控制 LSM 的形状,减少 I/O 和计算代价,并同时极大的减少了合并过程中的空间放大。同时使用更细粒度的访问控制和缓存机制,优化读的性能。 既然 X-Engine 是以 LSM 为基础架构的,所以一切还要从 LSM 本身说起。 LSM基本逻辑一条数据在 LSM 结构中的旅程,从写入 WAL(Write Ahead Log) 开始,然后进入MemTable,这是 Ta 整个生命周期的第一处落脚点。随后,flush 操作将 Ta 刻在更稳固的介质上,compaction 操作将Ta带往更深远的去处,或是在途中丢弃,取决于 Ta 的继任者何时到来。 ...

June 25, 2019 · 2 min · jiezi

MySQL单表数据不要超过500万行是经验数值还是黄金铁律

原文地址:梁桂钊的博客博客地址:http://blog.720ui.com 今天,探讨一个有趣的话题:MySQL 单表数据达到多少时才需要考虑分库分表?有人说 2000 万行,也有人说 500 万行。那么,你觉得这个数值多少才合适呢? 曾经在中国互联网技术圈广为流传着这么一个说法:MySQL 单表数据量大于 2000 万行,性能会明显下降。事实上,这个传闻据说最早起源于百度。具体情况大概是这样的,当年的 DBA 测试 MySQL性能时发现,当单表的量在 2000 万行量级的时候,SQL 操作的性能急剧下降,因此,结论由此而来。然后又据说百度的工程师流动到业界的其它公司,也带去了这个信息,所以,就在业界流传开这么一个说法。 再后来,阿里巴巴《Java 开发手册》提出单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。对此,有阿里的黄金铁律支撑,所以,很多人设计大数据存储时,多会以此为标准,进行分表操作。 那么,你觉得这个数值多少才合适呢?为什么不是 300 万行,或者是 800 万行,而是 500 万行?也许你会说这个可能就是阿里的最佳实战的数值吧?那么,问题又来了,这个数值是如何评估出来的呢?稍等片刻,请你小小思考一会儿。 事实上,这个数值和实际记录的条数无关,而与 MySQL 的配置以及机器的硬件有关。因为,MySQL 为了提高性能,会将表的索引装载到内存中。InnoDB buffer size 足够的情况下,其能完成全加载进内存,查询不会有问题。但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降。当然,这个还有具体的表结构的设计有关,最终导致的问题都是内存限制。这里,增加硬件配置,可能会带来立竿见影的性能提升哈。 那么,我对于分库分表的观点是,需要结合实际需求,不宜过度设计,在项目一开始不采用分库与分表设计,而是随着业务的增长,在无法继续优化的情况下,再考虑分库与分表提高系统的性能。对此,阿里巴巴《Java 开发手册》补充到:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。那么,回到一开始的问题,你觉得这个数值多少才合适呢?我的建议是,根据自身的机器的情况综合评估,如果心里没有标准,那么暂时以 500 万行作为一个统一的标准,相对而言算是一个比较折中的数值。 本文作者:白岳阅读原文 本文为云栖社区原创内容,未经允许不得转载。

June 24, 2019 · 1 min · jiezi

揭秘每秒千万级的实时数据处理是怎么实现的

1、设计背景闲鱼目前实际生产部署环境越来越复杂,横向依赖各种服务盘宗错节,纵向依赖的运行环境也越来越复杂。当服务出现问题的时候,能否及时在海量的数据中定位到问题根因,成为考验闲鱼服务能力的一个严峻挑战。 线上出现问题时常常需要十多分钟,甚至更长时间才能找到问题原因,因此一个能够快速进行自动诊断的系统需求就应用而生,而快速诊断的基础是一个高性能的实时数据处理系统。 这个实时数据处理系统需要具备如下的能力: 1、数据实时采集、实时分析、复杂计算、分析结果持久化。2、可以处理多种多样的数据。包含应用日志、主机性能监控指标、调用链路图。3、高可靠性。系统不出问题且数据不能丢。4、高性能,底延时。数据处理的延时不超过3秒,支持每秒千万级的数据处理。 本文不涉及问题自动诊断的具体分析模型,只讨论整体实时数据处理链路的设计。 2、输入输出定义为了便于理解系统的运转,我们定义该系统整体输入和输出如下:  输入: 服务请求日志(包含traceid、时间戳、客户端ip、服务端ip、耗时、返回码、服务名、方法名)        环境监控数据(指标名称、ip、时间戳、指标值)。比如cpu、 jvm gc次数、jvm gc耗时、数据库指标。 输出: 一段时间内的某个服务出现错误的根因,每个服务的错误分析结果用一张有向无环图表达。(根节点即是被分析的错误节点,叶子节点即是错误根因节点。叶子节点可能是一个外部依赖的服务错误也可能是jvm异常等等)。 3、架构设计 在实际的系统运行过程中,随着时间的推移,日志数据以及监控数据是源源不断的在产生的。每条产生的数据都有一个自己的时间戳。而实时传输这些带有时间戳的数据就像水在不同的管道中流动一样。 如果把源源不断的实时数据比作流水,那数据处理过程和自来水生产的过程也是类似的: 自然地,我们也将实时数据的处理过程分解成采集、传输、预处理、计算、存储几个阶段。 整体的系统架构设计如下: 采集采用阿里自研的sls日志服务产品(包含logtail+loghub组件),logtail是采集客户端,之所以选择logtail是因为其优秀的性能、高可靠性以及其灵活插件扩展机制,闲鱼可以定制自己的采集插件实现各种各样数据的实时采集。 传输loghub可以理解为一个数据发布订阅组件,和kafka的功能类似,作为一个数据传输通道其更稳定、更安全,详细对比文章参考:https://yq.aliyun.com/articles/35979?spm=5176.10695662.1996646101.searchclickresult.6f2c7fbe6g3xgP 预处理实时数据预处理部分采用blink流计算处理组件(开源版本叫做flink,blink是阿里在flink基础上的内部增强版本)。目前常用的实时流计算开源产品有Jstorm、SparkStream、Flink。Jstorm由于没有中间计算状态的,其计算过程中需要的中间结果必然依赖于外部存储,这样会导致频繁的io影响其性能;SparkStream本质上是用微小的批处理来模拟实时计算,实际上还是有一定延时;Flink由于其出色的状态管理机制保证其计算的性能以及实时性,同时提供了完备SQL表达,使得流计算更容易。  计算与持久化数据经过预处理后最终生成调用链路聚合日志和主机监控数据,其中主机监控数据会独立存储在tsdb时序数据库中,供后续统计分析。tsdb由于其针对时间指标数据的特别存储结构设计,非常适合做时序数据的存储与查询。调用链路日志聚合数据,提供给cep/graph service做诊断模型分析。cep/graph service是闲鱼自研的一个应用,实现模型分析、复杂的数据处理以及外部服务进行交互,同时借助rdb实现图数据的实时聚合。 最后cep/graph service分析的结果作为一个图数据,实时转储在lindorm中提供在线查询。lindorm可以看作是增强版的hbase,在系统中充当持久化存储的角色。 4、设计细节与性能优化采集日志和指标数据采集使用logtail,整个数据采集过程如图: 其提供了非常灵活的插件机制,共有四种类型的插件: inputs: 输入插件,获取数据。processors: 处理插件,对得到的数据进行处理。aggregators: 聚合插件,对数据进行聚合。flushers: 输出插件,将数据输出到指定 sink。由于指标数据(比如cpu、内存、jvm指标)的获取需要调用本地机器上的服务接口获取,因此应尽量减少请求次数,在logtail中,一个input占用一个goroutine。闲鱼通过定制input插件和processors插件,将多个指标数据(比如cpu、内存、jvm指标)在一个input插件中通过一次服务请求获取(指标获取接口由基础监控团队提供),并将其格式化成一个json数组对象,在processors插件中再拆分成多条数据,以减少系统的io次数同时提升性能。 传输数据传输使用LogHub,logtail写入数据后直接由blink消费其中的数据,只需设置合理的分区数量即可。分区数要大于等于bink读取任务的并发数,避免blink中的任务空转。 预处理预处理主要采用bink实现,主要的设计和优化点: 1:编写高效的计算流程blink是一个有状态的流计算框架,非常适合做实时聚合、join等操作。在我们的应用中只需要关注出现错误的的请求上相关服务链路的调用情况,因此整个日志处理流分成两个流:a、服务的请求入口日志作为一个单独的流来处理,筛选出请求出错的数据。b、其他中间链路的调用日志作为另一个独立的流来处理,通过和上面的流join on traceid实现出错服务依赖的请求数据塞选。  如上图所示通过双流join后,输出的就是所有发生请求错误相关链路的完整数据。 2:设置合理的state生存周期blink在做join的时候本质上是通过state缓存中间数据状态,然后做数据的匹配。而如果state的生命周期太长会导致数据膨胀影响性能,如果state的生命周期太短就会无法正常关联出部分延迟到来的数据,所以需要合理的配置state生存周期,对于该应用允许最大数据延迟为1分钟。 使用niagara作为statebackend,以及设定state数据生命周期,单位毫秒state.backend.type=niagarastate.backend.niagara.ttl.ms=600003:开启 MicroBatch/MiniBatchMicroBatch 和 MiniBatch 都是微批处理,只是微批的触发机制上略有不同。原理上都是缓存一定的数据后再触发处理,以减少对 state 的访问从而显著提升吞吐,以及减少输出数据量。 开启joinblink.miniBatch.join.enabled=true使用 microbatch 时需要保留以下两个 minibatch 配置blink.miniBatch.allowLatencyMs=5000防止OOM,每个批次最多缓存多少条数据blink.miniBatch.size=200004:动态负载使用 Dynamic-Rebalance 替代 Rebalanceblink任务在运行是最忌讳的就是存在计算热点,为保证数据均匀使用Dynamic Rebalance,它可以根据当前各subpartition中堆积的buffer的数量,选择负载较轻的subpartition进行写入,从而实现动态的负载均衡。相比于静态的rebalance策略,在下游各任务计算能力不均衡时,可以使各任务相对负载更加均衡,从而提高整个作业的性能。 开启动态负载task.dynamic.rebalance.enabled=true5:自定义输出插件数据关联后需要将统一请求链路上的数据作为一个数据包通知下游图分析节点,传统的方式的是通过消息服务来投递数据。但是通过消息服务有两个缺点:1、其吞吐量和rdb这种内存数据库相比比还是较大差距(大概差一个数量级)。2、在接受端还需要根据traceid做数据关联。 ...

June 21, 2019 · 1 min · jiezi

分布式数据库选型数据水平拆分方案

概述水平拆分的概念随着分布式数据库的推广已为大部分人熟知。分库分表、异构索引、小表广播、这些功能几乎是产品功能需求标配。然而有些客户使用分布式数据库后的体验不尽如意。本文尝试从数据的角度总结分布式数据的复制(replication)和分区(partition)技术原理和方案,其中分区也有称为分片(sharding),希望能引起读者一些思考,在分布式数据库选型中能注意这些细节的区别,选择适合业务的数据水平拆分方案。 分布式数据库架构分布式数据库以集群形式存在,有多个节点。集群架构有共享磁盘架构(shared-disk)和无共享架构(shared-nothing)。后者有时也称为水平扩展(horizontal scale)或向外扩展(scale out),本文主要总结无共享架构方案。 无共享架构的各个节点之间的通信都是软件层面使用网络实现,不同产品在架构不同导致这个细节也不同。有些架构是计算与存储分离。计算节点特点是无状态(即数据不要求持久化),通过集群方式管理,可以水平扩展;存储节点有数据,使用复制和分区技术,节点间任务集中调度或者独立交互。了解这个架构细节都可用性分析会更加具体。具体分布式数据库架构有哪些请参考《一些关系数据库的架构总结》。 这里节点的实际体现形式可以是一个机器,也可以是机器上的一个实例。比如说有些数据库支持单机安装多个实例,如MySQL。每个节点具备一定的资源和能力。资源指的是CPU、内存和磁盘,能力是提供数据读写和存储能力。分布式数据库需要把多个节点的能力聚集到一起集中管理,只是不同分布式数据库产品对资源的管理能力各有特点。 在分布式数据库里,数据随处可见,这是最容易让人混淆的地方。因为数据经过复制和分区后会有两种存在形式:副本(replica)和分区(partition)。 数据的复制(replication)复制(replication)指在几个不同的节点上保存数据的相同副本(replica)。复制提供了冗余的能力。其作用一是提供高可用能力:如果一个节点不可用,剩余的节点可以快速提供数据服务。作用二是提供读写分离能力。常见的有两副本和三副本架构。 多个副本内容相同,角色会有区分。常见的是一个副本是Leader角色(有的也称主副本),默认提供读写服务;其他副本是Follower角色(有的也称备副本),默认不提供服务。这种架构也称为基于单Leader的(Single Leader-based)。还有其他架构是多Leader的,每个Leader都有数据要复制到其他Leader或Follower,这种架构会有个明显的问题就是数据冲突处理。如果产品层面不处理,用户直接使用风险会很高。 后面讨论的是前者:基于单Leader副本架构。 多副本之间数据同步不是依赖业务多写,而是采用副本间复制事务日志(Redo)技术。复制的方式有同步复制和异步复制。使用同步复制方式,备副本要收到Redo并落盘主副本才能提交,也叫强同步;使用异步复制方式,Follower副本相对Leader副本内容会有延时,具体延时多少取决于Leader副本上事务量、网络传输速度、Follower副本所在节点的负载和能力。强同步的缺点时主副本写性能会下降,同时如果备副本不可用主副本也不能提供服务(变相的解决方案是复制方式降级为异步复制)。 传统关系型数据库还有一种用法一主两备架构,使用同步复制,只要任何一个备副本收到Redo,主副本的事务就可以提交。这个方案优点是保障了数据在多个副本中存在,高可用时有候选副本,也不用担心挂掉一个备副本会影响主副本。它的缺点是不能自动知道哪个候选副本拥有主副本最新最全的数据,也不强制要求两个备副本都要拥有全部数据。 还有一类三副本架构在复制时使用的是Paxos协议,三副本会就Redo落盘事件进行投票,有两个副本成功了Leader副本的事务即可提交。这个表面上跟上面传统一主两备的三副本效果一样,实际上还是有区别的。区别一是使用Paxos协议时,如果Leader副本自身投票慢了,两个Follower副本投票成功,Leader副本的事务也是能提交的;区别二是第三个副本最终也必须写Redo成功,否则其状态就是异常,产品自身可以发现并自动修复(如重新创建一个副本);区别三是使用Paxos协议时,在Leader副本不可用时还可以自动选出新的Leader副本并且拥有老Leader副本的最新数据。这里其实说的是高可用机制。同样,这里对用户而言也不知道哪个Follower副本拥有最新最全的数据,如果访问Follower副本(读写分离),也可能发现数据有延时。 大部分数据库做副本复制使用的是Redo,也称为物理同步。在应用Redo的时候直接是数据块变更。使用物理同步机制的备副本是不提供写服务,不能修改。还有一类复制使用的是Binlog,也称为逻辑同步。Binlog里只包含已提交的事务,并且在应用的时候是通过执行SQL。使用逻辑同步的备副本通常也可能是主副本,可以修改(如MySQL的双向复制架构Master-Master)。如果目标端数据不对,应用SQL会失败,这个复制就会中断需要人介入处理。这也进一步加深了主备副本不一致的概率。 关于副本角色的粒度,有多种实现方案。 传统关系数据库主备架构,主副本或备副本的粒度就是实例。对于主实例(Primary)而言,里面所有数据库(或SCHEMA)的所有表的角色都是主;备实例(Standby)里数据则都是备副本。如果发生高可用切换,业务会中断几十秒或几分钟然后恢复(需要人工处理或自动化脚本处理)。 还有一种粒度是到表。即一个节点内有些表是Leader副本,有些表是Follower副本,这样这个节点就不能简单的说是主节点(实例)或备节点(实例)。这个副本角色细节业务也是可以获取的,如果发生高可用切换,业务会中断十几秒然后恢复。 还有一种粒度是存储级别的定长块。即一个节点的存储里,部分数据块是Leader副本,部分数据块是Follower副本。这种对业务就完全透明,业务基本不感知高可用切换。 数据的分区(partition)上面总结的是数据的复制(冗余,多副本),对于非常大的数据集(表)或者非常高的访问量(QPS),仅仅靠复制是不够的,还需要对数据进行分区(partition),也称为分片(sharding)。 分区粒度首先这里的分区(partition)是一种抽象概念,在不同数据库产品里这个体现是不一样的。如在MongoDB, Elasticsearch中体现为分片(shard),在HBase中体现为区域块(Region),Bigtable中体现为表块(tablet),ORACLE中体现为分区(partition),Couchbase中体现为虚拟桶(vBucket)。可见不同的数据库产品数据分区的粒度不同。在分布式关系数据库中间件中,分片的粒度是分表(物理表);在真正的分布式关系数据库里,分片的粒度有分区(partition,同ORACLE)或者区域块(Region)。 分区粒度对业务研发的使用体验影响很大。 比如说中间件常用分库分表方案,使用时对开发和运维会有一些要求。如建很多同构的表并后期维护、要求SQL带上拆分键,还有一些功能限制(如跨库JOIN问题)、底层存储节点用的数据库自身高可用和多副本的数据一致问题等等。不同的中间件产品能力上也有区别,互联网大厂的产品由于内部场景培育很久,做的相对成熟一些。 体验最好的分区粒度就是存储级别的Region,业务研发完全不用关心分片细节,也无法干预分片细节。当有些场景追求性能需要干预数据分布特点时就不好处理。 介入这两种策略之间的就是分区。物理上业务只要创建一个分区表,根据业务特点指定分区策略(包含分区列、拆分算法、分区数目等)。 数据复制是为了冗余和高可用,数据分区主要是为了可扩展性。不管使用哪种分区方案,业务的每条数据(记录)属于且仅属于一个分区(或分片sharding),同一个分区(分片)只会存在于一个节点。前面说了每个节点代表了一定的资源和能力。当复制和分区(分片)一起使用的时候,注意区分你看到的数据。 分区策略分区的目标是将大量数据和访问请求均匀分布在多个节点上。如果每个节点均匀承担数据和请求,那么理论上10个节点就应该能承担10倍于单节点的数据量和访问量。这个理论是忽略了复制产生的Follower副本的存在。Follower副本的空间和内存是不可能跟其他Leader副本共享的,但是计算能力(CPU)是可以的。当所有节点都提供服务的时候(多活),是计算资源最大利用。 然而如果分区是不均匀的,一些分区的数据量或者请求量会相对比较高,出现数据偏斜(skew),这个可能导致节点资源利用率和负载也不均衡。偏斜集中的数据我们又称为热点数据。避免热点数据的直接方法就是数据存储时随机分配(没有规则)给节点,缺点是读取的时候不知道去哪个分区找该记录,只有扫描所有分区了,所以这个方法意义不大。实际常用的分区策略都是有一定的规则。 这个规则可以是业务规则,也可以不是。 业务规则的分区首先是选取一个或一组列作为分区键,然后选取拆分方法。比如说根据键的范围(Range)分区,分区数量和边界时确定的(后期还可以新增分区)。好处时针对分区键的范围扫描性能会比较好。分布式数据库中间件的分库分表、分区表的分区都支持RANGE 拆分函数。各个产品拆分细节上面会有一些创新。Range分区的缺点是某些特定的访问模式会导致热点。比如说根据时间列做RANGE分区,业务写入和读写数据集中在最近的时间,就可能导致各个分区负载不均衡。这只是一个缺点,业务层面还要考虑这样做的好处。比如说删除历史分区比较快。 还有种拆分方法是散列(HASH)分区,分区数量和边界是确定的(后期可以做分区分裂)。这时各个数据的分布是否均衡就取决于各个产品实现机制。大部分做法是使用一个散列(HASH)函数对Key计算一个值,然后针分段存储。 有的产品会使用这个HASH值对分区数取模,这个方法可能引起分区数据分布不均匀(若MySQL的Key分区)。此外如果要调整分区数,则需要移动所有数据。ORACLE的HASH分区时会先选取最接近分区数的一个2的幂值,对于分区数大于这个值的分区,会从前面分区里调过来。所以ORACLE 建议HASH分区数为2的幂。M有SQL建议Key分区数为奇数时数据分布最均匀。 此外在现有分区下还可以再做一次分区,分区键和分区方法都可以不一样。通常称为两级分区。比如说分库分表时,分库和分表策略不一样就是两级分区;分区表也支持两级分区。 有业务规则的分区方案的特点就是使用上。SQL如果要性能好建议带上分区键,这样分布式数据库才可以直接定位到所访问数据所在的分片;否则,数据库就要扫描所有分区去查询数据。通常分区键只能选取一个或一组业务字段,代表的是一个业务维度,那么另外一种业务维度的SQL请求性能就会不好。个别分布式数据库产品在HASH 方法上支持两种维度的分区列,其前提是在业务构造数据时让这两个列有着内部一致的分区逻辑。 详情可以参考《说说分库分表的一个最佳实践》。 另外一种分区策略就是无业务规则的,在存储级别按块的大小切分为多个定长块(Region)。这个分区对业务而言就是透明的,所以使用体验上会相对好一些。 不过,分布式数据库里的数据分区除了存储数据还要提供读写服务。业务读写数据的SQL本身是带业务逻辑的,如果一次SQL请求访问的数据分散到多个分区,而这些分区又散落在不同的节点上,不可避免的会发生跨节点的请求。如果是多表连接,这种情形更容易出现。如果这个业务请求有事务,那这就产生了分布式事务。分布式事务解决方案有两种,强一致的两阶段提交(XA)方案和最终一致的TCC方案。详情请参考《说说数据库事务和开发(下)—— 分布式事务》。 这里主要提示跨节点的请求带来的性能衰减。当然,硬件方面万兆网卡加RDMA技术下网络延时已经缩小很多,但是当分布式数据库的请求量(QPS)非常高时,或者分布式数据库是多机房部署(比如说两地三中心)时,跨机房的网络延时还是不可忽视,跨节点的请求带来的性能衰减也会很明显。所以有业务规则的分区策略可以提供策略给业务控制自己的数据分区分布特点,非常适合做异地多活和单元化类业务。此外还有个常用的规避跨节点请求读的方法就是小表广播,即将个别没有分区的表的数据复制到其他分区所在的节点,这样相关业务数据分区的JOIN就是在本地节点内部完成。这里就看复制使用的是物理同步还是逻辑同步,以及同步的延时是否满足业务需求。 分区数量关于分区数量也需要评估。如果是无规则的分区策略,由于每个分区(分片)是定长块,那么分区数量就由总数据大小除以定长块大小,对业务也是透明的。这里总结的是有业务规则的分区的数量。 使用分区的目的是为了扩展性,具体就是能将不同分区分散多多个节点上,发挥多个节点的资源和能力。所以分区数一定要大于可用的资源节点数,为了考虑到将来分布式数据库可能会扩容,分区数应该是数倍于当前规划的节点数。这是一个总的指导思想。由于不同的分布式数据库其节点的表示方法不一样,实施的时候会略有不同。 比如说在分布式数据库中间件架构里,数据存储的节点是实例,数据分区的粒度是分表(物理表),中间还有一层分库的维度。分布式数据库实例:总物理实例数:总物理分库数:总物理分表数=1:M:N:X 。X是分区的数量,N 是总分库数。X 是固定的,如果要调整分区数,成本非常高,所以一般都是提前规划好。N 是总分库数,是2的幂。 M 是实例的数量,也建议是2的幂,决定了最大能用多少节点的资源。 N/M 的结果决定了未来能扩容的倍数。分布式数据库中间件由于数据分区落在具体的节点后就不能自由移动,其扩容方式多是对每个实例一分为二,最好的途径就是利用数据库(MySQL)自身的主从复制搭建新的备实例扩容节点数。 此外分区数还要考虑到单个分区的容量和请求量是否满足需求。即分区是否到位。这个也是需要业务评估的。在使用分区表的分区方案的分布式数据库里,分区数也是结合上面两点考虑的。 当然分区数太大了,可能会增加分布数据库内部管理成本。分区数量跟分区粒度恰好是相反关系,二者都需要取一个合适的值。 分区数量一旦确定后,调整的成本非常高,通常会引起数据重分布。有些产品可以针对特定类型的分区做分区分裂。如RANGE分区可以分裂为两个RANGE, HASH分区也可以一分为二。只要这个分区分裂的逻辑是数据库内部逻辑实现,保证数据不丢,且对业务透明的,那么风险就很低值得考虑。 分区负载均衡随着时间的推移,数据库一直在发生各种变化。如QPS增加,数据集更大,或者新增/替换机器等。无论哪种都需要将部分数据分区和相应的请求从一个节点移动到另外一个节点,这个过程称为分区的再平衡(rebalance)。业务对再平衡的要求就是平衡过程中对业务当前读写影响要可控,数据读写服务不能中断。还有一点就是为了再平衡应尽可能少的迁移数据。 前面两个要求都不难满足,最后一个要求就考验各个分区方案的灵活度了。当分区粒度是存储级别的Region时,分区迁移的粒度就是Region,这个对业务也是透明的;分区粒度是分区时,这个取决于各个产品对节点资源管理的设计。比如说有的设计可以做到只需要迁移分区就可以调整各个节点的资源利用率和负载;如果分区方案是分库分表,此时分区粒度是分表。但是数据迁移的单位通常还是实例,利用数据库原生复制能力搭建新的级联备实例,然后新老实例分别删除一半分库数据。这里就迁移了不必要的很多数据分区。 分区访问路由 ...

June 20, 2019 · 1 min · jiezi

时延敏感业务低概率超时问题分析

前言作为阿里云底层提供的基础设施,内部的物理网络和许多网络产品在数据平面给客户的可操作性并不高,从一定程度上来说是个黑盒。当然,在传统的IDC环境,业务和物理网络之间也存在同样的隔阂。所以在遇到业务卡顿、延迟、不通等问题的时候,很容易怀疑到网络。因此如何抽丝拨茧,找到正确的方向对症下药才能够真正的解决问题。毕竟“真相只有一个”。 在进行问题排查和处理的时候,难度最高的场景就是极度偶发,复现频率极低的问题。尤其在网络排查的领域,通常为了性能和控制资源消耗,不会将每一个数据包的情况都一一记录下来,对于一次偶发的应用层记录的超时,网络层通常没有明确的对应此次应用层调用的包交互记录,因此排查起来非常困难。 在这次的案例中,我们通过一个客户端查询redis集群偶发超时的小案例,来说明一些诊断思路、排查手段,进而引出一些在网络方面提高业务稳定性的最佳实践。 问题环境这次的问题是一个交互性web应用中的一个子模块,主要进行redis查询,可以简单将其理解为视频弹幕网站中“查询弹幕”的小模块。这个模块的拓扑非常简单: 在上面的拓扑中,客户使用ECS构建了一个Redis集群,前面用Codis实现了一层Redis Proxy (为了通用性,后面均用Redis proxy来描述),并将这组Redis proxy挂载在一个SLB后,通过SLB的单一入口提供服务。 问题现象客户的主要问题是访问其自建Redis系统的客户端会不定期出现超时报错,尽管总体概率不高,但是报错概率高于其业务运行在原有机房的水平。超时报错主要有两种情况: 一般情况下超时数量与业务量呈正相关,非业务高峰期但是SLB、ECS的资源使用率均较低。会存在突发性的大量超时。诊断思路作为问题排查的第一步,首先要了解到问题本身所处的上下文和环境。在平时诊断问题收集信息的时候,为了有条理的、全面的收集信息,笔者将需要收集的信息分为两种类型:资源因素和环境因素。 资源因素:即发生问题的系统的拓扑。比如涉及的各种应用程序、主机、转发设备、链路资源等,并且要充分理解这些资源组建在拓扑中起到的作用。环境因素:即描述这个问题所需要的信息,比如报错日志,问题发生时间、频率,应用层设置的超时时间等等。了解资源因素和环境因素后,可以将问题的定义明确为:在哪些资源上发生了什么样的问题,然后根据该定义收集与问题相关的信息,并在解读和分析的时候通过数据排除所有的不可能,这样才能进行高效和准确的问题排查。 在本案例中,资源因素已经在上文的拓扑中阐述,问题所涉及的环境因素包括:客户设置的是50ms超时,在客户端的报错是read timeout(代表排除了tcp的三次握手超时),报错频率为非业务高峰期一个小时10个左右,业务高峰期1小时上百个。但是偶尔(一周内偶尔发生一次到两次)无论业务高峰还是非业务高峰都会有较多的,上百次的read timeout和connect timeout。客户已经排查过redis,其查询时间每次耗时不超过10ms,而redis proxy没有记录转发的日志。 排查方法因为所有可用的日志仅能定位到这个系统的两端(客户端、Redis),需要收集进一步的信息。面对这种超时类的问题,最直接、有效的办法就是进行抓包。而该问题发生的频率不高,整个系统流量也比较大,一直开着抓包很容易将磁盘撑满,因此需要使用循环抓包: tcpdump -i <接口|any> -C <每文件大小> -W <文件个数> -w <保存文件名> 抓包过滤条件该命令的意思是针对某个接口,按照过滤条件进行抓包,保存指定文件名前缀的文件下,最多占用每文件大小*文件个数 的磁盘空间并循环覆盖。开启循环抓包后,等待客户端报错再次出现,即可抓到现场的包交互过程。 抓包的结果文件可以使用wireshark打开,但是使用循环抓包抓到的数据包文件较大、数量较多,可以使用下面的小技巧进行快速的过滤: //在安装了wireshark的电脑上都会有capinfos和tshark两个命令,以笔者使用的macOS为例~$ capinfos -a -e *cap //使用capinfos查看抓包文件的其实时间和结束时间,选取包含报错时间+-超时时间的文件,其他文件就不需要了File name: colasoft_packets.capPacket size limit: inferred: 66 bytes - 1518 bytes (range)First packet time: 2019-06-12 09:00:00.005519934Last packet time: 2019-06-12 09:59:59.998942048File name: colasoft_packets1.capPacket size limit: inferred: 60 bytes - 1518 bytes (range)First packet time: 2019-06-12 09:00:00.003709451Last packet time: 2019-06-12 09:59:59.983532957//如果依然有较多文件,则可以使用tshark命令进行筛选。比如报错中提到Redis查询一个key超时,则可以用以下脚本找到这次查询请求具体在哪个文件中:~$ for f in ./*; do echo $f; tshark -r $f 'tcp.payload contains "keyname"'; done找到对应的请求之后,再用wireshark打开该文件,找到对应数据包,跟踪对应流来找到五元组和整个流的上下文交互。 ...

June 18, 2019 · 1 min · jiezi

HSFDubbo序列化时的LocalDateTime-Instant的性能问题

来源在对Dubbo新版本做性能压测时,无意中发现对用例中某个TO(Transfer Object)类的一属性字段稍作修改,由Date变成LocalDateTime,结果是吞吐量由近5w变成了2w,RT由9ms升指90ms。 在线的系统,拼的从来不仅仅是吞吐量,而是在保证一定的RT基础上,再去做其他文章的, 也就是说应用的RT是我们服务能力的基石所在, 拿压测来说, 我们能出的qps/tps容量, 必须是应用能接受的RT下的容量,而不是纯理论的数据,在集团云化的过程中计算过,底层服务的RT每增加0.1ms,在应用层就会被放大,整体的成本就会上升10%以上。 要走向异地,首先要面对的阿喀琉斯之踵:延时,长距离来说每一百公里延时差不多在1ms左右,杭州和上海来回的延迟就在5ms以上,上海到深圳的延迟无疑会更大,延时带来的直接影响也是响应RT变大,用户体验下降,成本直线上升。 如果一个请求在不同单元对同一行记录进行修改, 即使假定我们能做到一致性和完整性, 那么为此付出的代价也是非常高的,想象一下如果一次请求需要访问10 次以上的异地 HSF 服务或 10 次以上的异地 DB调用, 服务再被服务调用,延时就形成雪球,越滚越大了。 普遍性关于时间的处理应该是无处不在,可以说离开了时间属性,99.99%的业务应用都无法支持其意义,特别是像监控类的系统中更是面向时间做针对性的定制处理。 在JDK8以前,基本是通过java.util.Date来描述日期和时刻,java.util.Calendar来做时间相关的计算处理。JDK8引入了更加方便的时间类,包括Instant,LocalDateTime、OffsetDateTime、ZonedDateTime等等,总的说来,时间处理因为这些类的引入而更加直接方便。 Instant存的是UTC的时间戳,提供面向机器时间视图,适合用于数据库存储、业务逻辑、数据交换、序列化。LocalDateTime、OffsetDateTime、ZonedDateTime等类结合了时区或时令信息,提供了面向人类的时间视图,用于向用户输入输出,同一个时间面向不同用户时,其值是不同的。比如说订单的支付、发货时间买卖双方都用本地时区显示。可以把这3个类看作是一个面向外部的工具类,而不是应用程序内部的工作部分。简单说来,Instant适用于后端服务和数据库存储,而LocalDateTime等等适用于前台门面系统和前端展示,二者可以自由转换。这方面,国际化业务的同学有相当多的体感和经验。 在HSF/Dubbo的服务集成中,无论是Date属性还是Instant属性肯定是普遍的一种场景。 问题复现Instant等类的性能优势以常见的格式化场景举例 @Benchmark @BenchmarkMode(Mode.Throughput) public String date_format() { Date date = new Date(); return new SimpleDateFormat("yyyyMMddhhmmss").format(date); } @Benchmark @BenchmarkMode(Mode.Throughput) public String instant_format() { return Instant.now().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern( "yyyyMMddhhmmss")); }在本地通过4个线程来并发运行30秒做压测,结果如下。 Benchmark Mode Cnt Score Error UnitsDateBenchmark.date_format thrpt 4101298.589 ops/sDateBenchmark.instant_format thrpt 6816922.578 ops/s可见,Instant在format时性能方面是有优势的,事实上在其他操作方面(包括日期时间相加减等)都是有性能优势,大家可以自行搜索或写代码测试来求解。 Instant等类在序列化时的陷阱针对Java自带,Hessian(淘宝优化版本)两种序列化方案,压测序列化和反序列化的处理性能。 Hessian是集团内应用的HSF2.2和开源的Dubbo中默认的序列化方案。 @Benchmark @BenchmarkMode(Mode.Throughput) public Date date_Hessian() throws Exception { Date date = new Date(); byte[] bytes = dateSerializer.serialize(date); return dateSerializer.deserialize(bytes); } @Benchmark @BenchmarkMode(Mode.Throughput) public Instant instant_Hessian() throws Exception { Instant instant = Instant.now(); byte[] bytes = instantSerializer.serialize(instant); return instantSerializer.deserialize(bytes); } @Benchmark @BenchmarkMode(Mode.Throughput) public LocalDateTime localDate_Hessian() throws Exception { LocalDateTime date = LocalDateTime.now(); byte[] bytes = localDateTimeSerializer.serialize(date); return localDateTimeSerializer.deserialize(bytes); }结果如下。可以看出,在Hessian方案下,无论还是Instant还是LocalDateTime,吞吐量相比较Date,都出现“大跌眼镜”的下滑,相差100多倍;通过通过分析,每一次把Date序列化为字节流是6个字节,而LocalDateTime则是256个字节,这个放到网络带宽中的传输代价也是会被放大。 在Java内置的序列化方案下,有稍微下滑,但没有本质区别。 ...

June 18, 2019 · 1 min · jiezi

Blink-有何特别之处菜鸟供应链场景最佳实践

作者:晨笙、缘桥菜鸟供应链业务链路长、节点多、实体多,使得技术团队在建设供应链实时数仓的过程中,面临着诸多挑战,如:如何实现实时变Key统计?如何实现实时超时统计?如何进行有效地资源优化?如何提升多实时流关联效率?如何提升实时作业的开发效率? 而 Blink 能否解决这些问题?下面一起来深入了解。背景菜鸟从2017年4月开始探索 Blink(即 Apache Flink 的阿里内部版本),2017年7月开始在线上环境使用 Blink,作为我们的主流实时计算引擎。 为什么短短几个月的探索之后,我们就选择Blink作为我们主要的实时计算引擎呢? 在效率上,Blink 提供 DataStream、TableAPI、SQL 三种开发模式,强大的 SQL 模式已经满足大部分业务场景,配合半智能资源优化、智能倾斜优化、智能作业压测等功能,可以极大地提升实时作业的开发效率;在性能上,诸如MiniBatch&MicroBatch、维表 Async&Cache、利用 Niagara 进行本地状态管理等内部优化方案,可以极大地提升实时作业的性能;在保障上,Blink 自带的 Failover 恢复机制,能够实现线程级的恢复,可以做到分钟级恢复,配合 Kmonitor 监控平台、烽火台预警平台,可以有效地实现实时作业的数据保障。 接下来,我将结合供应链业务的一些业务场景,简要说明,Blink 如何解决我们遇到的一些实际问题。 回撤机制订单履行是供应链业务中最常见的物流场景。什么是订单履行呢?当商家 ERP 推单给菜鸟之后,菜鸟履行系统会实时计算出每笔订单的出库、揽收、签收等节点的预计时间,配送公司需要按照各节点的预计时间进行订单的配送。为了保证订单的准点履约,我们经常需要统计每家配送公司每天各个节点的预计单量,便于配送公司提前准备产能。 看似很简单的实时统计加工,我们在开发过程中遇到了什么问题呢?履行重算!当物流订单的上游某个节点延迟时,履行系统会自动重算该笔订单下游所有节点的预计时间。比如某个物流订单出库晚点后,其后的预计揽收时间、预计签收时间都会重算。而对于大部分的实时计算引擎来说,并不能很友好的支持这种变 Key 统计的问题。以前,数据量没那么大的时候,还可以通过 OLAP 数据库来解决这类场景,当量上来后, OLAP 方案的成本、性能都是很大的问题。 除了 OLAP 方案,我们提倡采用 Blink 已经内置的 Retraction 机制,来解决这类变 Key 统计的问题,这也是我们在2017年初就开始尝试 Blink 的重要原因。Blink 的Retraction 机制,使用 State 在内存或者外部存储设备中对数据进行统计处理,当上游数据源对某些汇总 Key 的数据做更新时,Blink 会主动给下游下发一个删除消息从而“撤回”之前的那条消息,并用最新下发的消息对表做更新操作。Retraction的实现细节,可以参见:Retraction for Flink Streaming。 下面是一个简化后的案例,供了解Blink Retraction的内部计算过程: 对于上述案例,可以通过 Blink 提供的强大的、灵活的、简易的 SQL 开发模式来实现,只需要几行 SQL 即可完成。 ...

June 17, 2019 · 2 min · jiezi

如何设计和实现自适应的负载均衡

本文是第五届中间件性能挑战赛的赛题解析,参与比赛,赢取最高10万元奖金。 在现代分布式应用中,服务请求是由物理机或虚拟机组成的 server 池进行处理的。 通常,server 池规模巨大且服务容量各不相同,受网络、内存、CPU、下游服务等各种因素影响,一个 server 的服务容量始终处于动态变动和趋于稳定的状态,如何设计和实现这种系统的负载均衡算法是一个极具挑战的难题。 阿里巴巴中间件公众号对话框发送“挑战赛”,获取上一届优秀选手的解题思路,点击这里。 自适应负载均衡的需求背景负载均衡有两个主要目标: 保持较短的请求响应时间和较小的请求阻塞概率;负载均衡算法的 overhead 在可控级别,不占用过多的 CPU 、网络等资源。自适应负载均衡是指无论系统处于空闲、稳定还是繁忙状态,负载均衡算法都会自动评估系统的服务能力,进行合理的流量分配,使整个系统始终保持较好的性能,不产生饥饿或者过载、宕机。 这种算法对于现在的电商系统、数据中心、云计算等领域都很有必要,使用自适应负载均衡能够更合理的利用资源,提高性能。例如,在双十一零点,用户集中下单支付,整个电商系统的请求速率到达峰值。如果将这些请求流量只分配给少部分 server,这些机器接收到的请求速率会远超过处理速率,新来的任务来不及处理,产生请求任务堆积。 对用户而言,一旦产生任务堆积,请求会变慢甚至超时,体验严重下降,甚至导致服务不可用。而处理请求的机器也会由于堆积的任务越来越多而发生严重过载,直到被打垮。剩余的尚未宕机的其它机器会逐渐重复这个过程,直至整个应用不可用,发生系统故障。 为了避免这种情况发生,我们可能会想到一种常用的办法:在服务上线前提前进行压测,使用压测的容量作为限流值,当线上服务的请求速率大于限流值的时候,服务拒绝新到的服务,从而保障服务始终可用。但是这种方式也存在问题:压测时测试的容量进行限流通常会趋于保守,不能充分发挥异构系统的全部性能;也无法智能地应对由于网络、下游服务变化而导致的容量下降等问题,系统仍然存在宕机风险。 因此,我们需要具备自适应能力的负载均衡算法,来更好的进行流量分配调度以及稳定性保障,追求极致性能,挑战大促等场景下的流量洪峰。 结合中间件性能挑战赛的赛题 我们结合「第五届中间件性能挑战赛」中的初赛场景,来一起探讨一下设计和实现一个自适应的负载均衡的基本思路。 本次挑战赛的场景由施压程序(阿里云性能测试PTS)、服务调用方(Consumer)和三个规格不同的服务提供方(Provider) 组成。在评测过程中,每个程序都部署在不同的物理机上,以避免因 CPU、网络资源的竞争,导致评测程序抖动,影响最终评测成绩。 Becnhmarker 负责请求 Consumer, Consumer 收到请求后,从三台物理规格不同、服务响应时间和最大并发都不同的 Provider 中选择一个进行调用并返回结果。选择哪一个 Provider 进行调用的流程就是本次挑战赛需要实现的负载均衡算法。 为了简化环境部署和提升性能,本次挑战赛没有使用服务注册和发现机制。三个 Provider 对应的 URL 都已经被直接配置在了 Consumer 中,选手在开发和测试时可直接通过 Provider-small 等 hostname 访问相应的 Provider。 赛题分析题目描述很简单,不考虑 Consumer 直接拒绝的情况下,场景可以简化为 3 选 1 的问题,但如何进行这个决策则是本次挑战赛考察的难点和重点。 官方题目组提供了Random算法作为默认实现:从 3 个 Provider 中随机取任意一个。对于单 dispatcher (在本次赛题中是 Consumer) 同构系统的场景,Random可以达到渐近负载均衡, 每个 Provider 接收到的总请求数接近。但是对于多 dispatcher 或异构系统而言,Random 算法由于缺少全局状态,无法保证全局随机,极端条件下,多个 dispatcher 可能将请求同时分配到一台 Provider 上,导致系统存在服务过载和宕机的风险;异构系统中,不同 Provider 服务容量实际是不同的,即使每个 Provider 请求速率相同也会产生空闲、稳定、过载等不同的服务状态,无法实现最优流量分配,更不能做到响应时间最小。显而易见,Random并不是符合赛题要求的自适应算法。 ...

June 14, 2019 · 1 min · jiezi

一键托管阿里云全链路追踪服务正式商用成本仅自建15或更少

随着互联网架构的扩张,分布式系统变得日趋复杂,越来越多的组件开始走向分布式化,如微服务、消息收发、分布式数据库、分布式缓存、分布式对象存储、跨域调用,这些组件共同构成了繁杂的分布式网络。 在一次800多人的开发者调研中,当回答“现阶段构建一个高可用的分布式系统,您遇到的三个最大的难题是什么?”时,57%的开发者选择了全链路追踪。 6月12日,阿里云发布了链路追踪服务 Tracing Analysis,提供分布式系统的全链路追踪能力,帮助客户快速发现和定位分布式系统下的各类性能瓶颈,成本仅自建链路追踪系统的1/5甚至更少。 微服务架构下的分布式应用架构虽然满足了应用横向扩展需求,但是如何进行分布式应用诊断成为挑战。虽然,业内有链路追踪相关的开源解决方案,但存在着研发投入较高、自建成本较高、技术风险较大、运维难度较大的挑战。 链路追踪 Tracing Analysis源自阿里巴巴内部的经过大规模实战验证过的 EagleEye,基于 Opentracing 标准,全面兼容开源社区,可实现 Jaeger, Zipkin 和 Skywalking等开源方案在阿里云上的托管,客户无需搭建基础设施,节省运维投入和技术风险。同时,支持多语言客户端将应用的链路数据上报至链路追踪控制台,实现链路追踪的目的。 据介绍,链路追踪 Tracing Analysis 可用于链路拓扑分析,慢请求、异常请求、流量异常的问题发现和定位,并可以根据业务Tag 对业务进行统计。以某教育行业客户为例,链路追踪 Tracing Analysis 帮助客户将异常请求数从原先的3%降低到0.1%,排查5个以上线上问题。 此外,链路追踪 Tracing Analysis可帮助用户收集所有分布式微服务应用和相关PaaS产品的分布式调用信息,查看应用的依赖路径,用于业务分析和稳定性评估。以某金融行业客户为例,链路追踪 Tracing Analysis 帮助客户将将应用的平均响应时间从2秒降低到500毫秒。 值得注意的是,链路追踪 Tracing Analysis 省去了客户自建基础设施的本地存储费用,仅通过云端日志存储收取存储费用,总体的机器成本是自建全链路追踪系统的1/5或更少,并提供了每天1000请求数的免费使用额度。 目前,阿里云链路追踪 Tracing Analysis已应用于金融、游戏、教育、零售、人工智能等多个行业,帮助开发者高效的分析和诊断分布式应用架构下的性能瓶颈。 Q&A: Q1:可以通过 API 拉取链路追踪的数据吗?A1:支持,收集的链路可以通过OpenAPI的方式获取,也可以嵌入链路追踪的页面展示,也可以直接在日志服务中查看。 Q2:非阿里云服务,可以接入链路追踪?A2:链路是追踪是开放的,只要客户的应用可以访问公网,就可以接入,和有没部署在阿里云上没关系。 Q3:埋点对性能的影响有相关分析么?A3:埋点数据是异步批量上报的,会对性能有影响有限,一般在1%左右,主要看埋点的量,埋的多会影响大一点。从目前的压测数据来看,对性能影响比较小。 本文作者:中间件小哥原文链接  本文为云栖社区原创内容,未经允许不得转载。

June 13, 2019 · 1 min · jiezi

五年从P5到P8在阿里学做个靠谱的人

师兄文化,是阿里的老传统,新人入职都要认个师兄。 不是江湖上这种师兄哈,但帅是一样帅的 今天和大家聊聊我在阿里当师兄的故事。 我是“改之”,不是“有则改之无则加勉”的改之,而是“杨过,字改之”的那个改之。 为什么要用杨过的字做花名? 哈哈,因为帅呗! 我的主业是蚂蚁中间件的程序员,额~还有个副业是“师兄”。 先来说说我的师兄5年前,我以实习生的身份来到蚂蚁,现任蚂蚁金服技术总监杨冰,是我当年的师兄。 有次我在周报里写“这周又新学了两个知识点,非常开心”。师兄杨冰很快回复道:“你不该仅仅为了学会两个知识点开心,而应该将这个知识运用到业务,并拿到结果的时候才感到开心。”这句点拨,让我印象非常深刻。 师兄人很忙,但是只要在他身边,都能被他那种激情和热血所感染。 从师兄身上,我体会到:是“师”也是“兄”。任何时候都要担当,并往前再多想一步。这些前辈们言传身教的经验,也是我在阿里这些年习得的思维方式。 带的第一个师弟,跑了说来不怕人笑话,我第一年做“师兄”,当时带过一个实习生,不过非常失败啊,带了一个月人家就跑了。 为了做个好师兄,我在内网翻看了很多阿里师兄文化的讨论帖和经验分享。一边摸索,一边领悟。 师兄,本质是做导师,用一种更亲切更贴近校园的方式引导新人,帮助他们尽快落地。 在阿里当师兄,不仅要授业解惑,还要陪伴和保护新人,共同成长进步。在为期一年的上手过程中,如果师弟的代码出了岔子,“帐”是要记在师兄头上的。 “我说你听,我做你看;你说我听,你做我看”是阿里师兄文化的十六字箴言。 改之在蚂蚁内部大学已经带过6-7期课程,上课人数超过百人 程序员的傲娇和闷骚有时候看到谁写代码,如果他用的是上、下、左、右的键盘按键,就会忍不住告诉他整套操作快捷键。快捷键用得溜,也能处于鄙视链前端。 所谓“闷骚”,则是程序员对代码的完美主义追求,每次都想憋个大招。 曾经有个同学,写了一段自己很满意的代码,大概性能能提升个10倍,得意之余加了很长的注释,巴拉巴拉夸了自己的代码一堆。提交到我这边,却发现他如果能再改一下,性能能提升个一千倍。 但师弟们这些“攀比”,在我眼中都是好事儿。阿里有句话说,为过程鼓掌,为结果买单嘛! 在阿里,学做一个靠谱的人话痨、热情、会玩……大概是我留给同事们的印象。 但说实话,我并不是个内心深处有很多“热爱”的人。做事不是凭兴趣,什么事情交到你手里了,当下这件事就要要求自己做到最好。这是我自己的一点体会。 在阿里摸爬滚打的五年里,我“做丢”过项目,最后不得不转交给更适合的团队。 也曾经吭哧吭哧做了几个月,做完后发现业务需求不匹配,有技术和拿到业务结果是两回事。努力,更要找对方法,才能持续稳定地把业务落下来。 在阿里,我自己总结经验是首先要靠谱,做个靠谱儿的人,有需要了大家第一反应想到就是你。然后再进一步——在靠谱的过程中做出彩。 道理其实大家都懂,但就看谁把事情先做出来,并且持续地做下去。有时候必须趟过坑,再爬出来,才能真正体会个中滋味。 对于师弟们,我和他们说的最多的,可能也就是这个。 本文作者: 阿里味儿阅读原文 本文来自云栖社区合作伙伴“阿里味儿”,如需转载请联系原作者。

June 13, 2019 · 1 min · jiezi

如何将深度学习训练速度提升一百倍PAISoar-来了

阿里妹导读:得力于数据规模增长、神经网络结构的演进和计算能力的增强,深度学习的图像处理、语音识别等领域取得了飞速发展。随着训练数据规模和模型复杂度的不断增大,如何充分利用分布式集群的计算资源加快训练速度,提升业务支持能力成为用户非常关注的问题。今天,我们就来分享阿里工程师的实践成果:将深度学习模型的大规模分布式训练框架 PAISoar应用于绿网模型(多层CNN网络)后,绿网模型在128 GPU卡上取得101倍的计算加速比,效果显著。1. 概述近几年来深度学习发展迅速,图像处理、语音识别等领域都取得了飞速发展。例如在图片识别上,神经网络结构快速演进,分类的网络结构从 AlexNet、VGG、Inception V1 发展到了 Inception V4、Inception-ResNet、SENet。随着模型层次越来越深,参数越来越多,模型能力也越来越强,ImageNet 数据集 Top-5 的错误率越来越低,目前降到了2.25%(人眼5.1%)。 随着模型复杂度不断增长、训练样本的指数级增长,分布式进行高效并行的神经网络训练已经变得越发重要。在社区版 Tensorflow 中,分布式训练是基于 Parameter Server 模式进行多机训练的。在这种训练方式下训练任务通常会遇到以下挑战: Variable placement 策略,常用的 replica_device_setter 的策略是 round-robin over all ps tasks,这种策略并没有考虑 Variable 大小,会导致参数分配不均衡,某些 ps 上分配的 Variable size 大就会成为通信瓶颈;多个 Worker 访问同一个 PS 节点时,受 PS 节点带宽限制和 TCP 的拥塞窗口控制,会导致通信效率大幅降低,并且规模越大,效率越差;分布式扩展后,模型需要精细调参才能收敛,需要用户有丰富的调参经验。对此,我们基于 PAI Tensorflow 研发了针对于深度学习模型的高速分布式训练框架 PAISoar,从硬件到软件打造一套分布式训练场景 E2E 的解决方案: 在硬件上,我们和 AIS 网络团队一起搭建了集团内部第一套基于 RoCE 的大规模 RDMA 集群,并针对于深度学习场景进行了相应的参数调优,支持低延迟、高吞吐的无损传输网络;在软件上,我们基于 Ring AllReduce 算法在 RDMA 网络上实现了高度优化的 PAISoar 分布式训练框架,通过软硬件一体的深度优化大大提高了多机的计算加速比;在 API 层面,我们提供了简化用户构建分布式 TF 模型训练的ReplicatedVarsOptimizer,极大地方便了将单机模型扩展成分布式模型,降低了用户构建分布式TF训练代码的难度,同时提供支持 warm up 的 learning rate 动态调节方法,帮助模型训练更容易的收敛。PAISoar 在 Tensorflow 官方 benchmarks 模型上取得了非常不错的加速效果。同时我们还和安全部团队合作,将该研发成果成功的在安全部图像模型业务上落地。安全部的绿网模型训练样本280万张图片,单机两卡训练需要12天才能收敛,因此有着非常强的分布式训练需求,希望能达到一天内完成训练,提升模型迭代能力。借助于 PAISoar,绿网模型分布式训练取得非常明显的加速效果,模型收敛时间从原先的12天降低到目前的一天以内,大大加速了业务的快速迭代。 ...

June 12, 2019 · 3 min · jiezi

Logtail提升采集性能

默认性能限制为防止滥用消耗过多机器资源,我们对默认安装的Logtail进行了一系列的资源限制。默认安装的Logtail最多日志采集速度为20M/s,20个并发发送。 其他资源限制请参考:启动参数 https://help.aliyun.com/docum... 中的默认配置。 采集能力单核能力 如果放开发送流控,Logtail默认单核的能力大致如下(具体根据不同正则、日志类型、采集提取的key数量、机器配置等会有一定浮动): 备注:测试环境 CPU :Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz MEM : 64GB OS : Linux version 2.6.32-220.23.2.ali1113.el5.x86_64 多核能力 Logtail默认只开一个线程处理数据,如果开启多核,性能会有提升,但并不是线性关系,实测最多开到8个线程后,性能几乎没有上涨。 极简模式最高性能可达:440MB/s正则最高性能可达:70MB/s分隔符最高性可达:75MB/sJSON最高性能可达:75MB/s日志格式建议根据您的使用目的,合理选择对应的日志格式 搬数据:使用极简模式,性能最高数据分析:多字符分隔符>单字符分隔符>JSON模式>正则模式Java堆栈类型数据:正则模式注意:正则模式采集性能和正则优化有非常大关系。如何放开资源限制可通过调整Logtail的启动参数来放开默认的资源限制,下面我们推荐2种配置方式: 注意:Logtail使用短连接发送数据,如果发送并发过高,建议调整服务器的tcp参数,防止过多time_wait调整方式:sudo sysctl -w net.ipv4.tcp_tw_timeout=5单核小资模式 在配置文件末尾追加以下两个参数,注意JSON需合法。 多核极致模式 在配置文件末尾追加以下几个参数,需保证,注意JSON需合法。 注意:需保证 cpu_usage_limit > process_thread_count 本文作者:元乙 原文链接 本文为云栖社区原创内容,未经允许不得转载。

June 6, 2019 · 1 min · jiezi

达摩院首席数据库科学家李飞飞云原生新战场我们如何把握先机

阿里妹导读:云计算大潮来袭,传统数据库市场正面临重新洗牌的情境,包括云数据库在内的一批新生力量崛起,动摇了传统数据库的垄断地位,而由云厂商主导的云原生数据库则将这种“改变”推向了高潮。云时代的数据库将面临怎样的变革?云原生数据库有哪些独特优势?在 DTCC 2019大会上,阿里巴巴副总裁 李飞飞博士就《下一代云原生数据库技术与趋势》进行了精彩分享。 李飞飞(花名:飞刀),阿里巴巴集团副总裁,高级研究员,达摩院首席数据库科学家,阿里云智能事业群数据库产品事业部负责人,ACM 杰出科学家。 大势所趋:云数据库市场份额增速迅猛如下图所示的是 Gartner 关于全球数据库市场份额的报告,该报告指出目前全球数据库市场份额大约为400亿美金,其中,中国数据库市场份额占比为3.7%,大约为14亿美金。 具体到数据库市场分布,传统五大数据库厂商 Oracle、Microsoft、IBM、SAP、Teradata 占比达到了80%,云数据库的份额占比接近10%,并且云数据库市场份额占比每年也在快速增长,因此, Oracle、MongoDB 等也在大力布局其在云数据库市场的竞争态势。 根据 DB-Engines 数据库市场分析显示,数据库系统正朝着多样化、多元化的方向发展,从传统的 TP 关系型数据库发展到今天的多源异构的数据库形态。目前,处于主流位置的还是大家耳熟能详的数据库系统,比如商业数据库 Oracle、SQL Server以及开源的 MySQL、PostgreSQL 等。而一些比较新的数据库系统,比如MongoDB、Redis 则开辟了一个新的赛道。数据库 License 的传统销售方式在逐渐走下坡路,而开源以及云上数据库 License 的流行程度却在不断提升。 数据库:云上应用关键的一环正如 AWS 创始人 Jeff Bezos 所说:“The real battle will be in databases”。因为云最早是从 IaaS 做起来的,从虚拟机、存储、网络,到现在如火如荼的语音识别、计算机视觉以及机器人等智能化应用,都是基于 IaaS 的,而数据库就是连接 IaaS 与智能化应用 SaaS 最为关键的一环。从数据产生、存储到消费的各个环节,数据库都至关重要。 数据库主要包括四大板块,即 OLTP、OLAP、NoSQL 以及数据库服务和管理类工具,也是云数据库厂商发力的四个方向。对于 OLTP 而言,技术发展已经历经了40年,而如今大家还在做的一件事情就是“加10元和减10元”,也就是所谓的事务处理。当数据量变得越来越大和读写冲突的原因,对数据进行在线实时分析的需求衍生出了 OLAP。由于需要 Scale out,而数据强一致性不能够得到保证,就有了NoSQL 。而最近又出现了一个新名词—— NewSQL,这是因为 NoSQL 也有所不足,故将传统 OLTP 的 ACID 保证与 NoSQL 的 Scale out 能力进行了整合,变成了NewSQL。 ...

June 6, 2019 · 2 min · jiezi

MySQL-80-技术详解

MySQL 8.0 简介MySQL 5.7 到 8.0,Oracle 官方跳跃了 Major Version 版本号,随之而来的就是在 MySQL 8.0 上做了许多重大更新,在往企业级数据库的路上大步前行,全新 Data Dictionary 设计,支持 Atomic DDL,全新的版本升级策略,安全和账号管理加强,InnoDB 功能增强等,目前小版本已经 release 到 8.0.16,新的功能仍然在持续推出。RDS MySQL 8.0 产品是阿里云推出的 MySQL 系列云产品之一,使用完全兼容 MySQL 8.0 的阿 里云 AliSQL 8.0 分支,除了官方在 MySQL 8.0 推出的全新功能外,AliSQL 沉淀了许多在 Alibaba 集团电商业务和云上几十万客户在使用 MySQL 过程中遇到的问题和需求,以此来加固AliSQL, 提升 AliSQL 的性能和稳定性。下面分别对 MySQL 8.0 和 AliSQL 8.0 相关的版本和功能做简短的介绍: MySQL 8.0 版本更新1. 数据字典 MySQL 8.0 摒弃了 Server Layer 定义的 FRM 文件和其它非事务表,使用了一组 InnoDB 表来 保存数据字典,支持事务特性。 ...

May 30, 2019 · 2 min · jiezi

把握数据库发展趋势-DBA应如何避免踩坑

摘要:在DTCC 2019大会上,阿里云智能数据库产品事业部高级产品专家萧少聪做了题为《如何构建云时代DBA的知识体系》的演讲,进行云时代以后,IT行业各工种的职责都在发生变化,云数据库使得日常DBA管理实现更多的自动化,大大提高日常管理效率,同时也对于企业整体投资产出可以更快获得成效。面对云数据库的发展趋势,DBA应如何避免“踩坑”呢?本文就为大家揭晓答案。 专家简介:萧少聪(花名:铁庵),阿里云智能数据库产品事业部高级产品专家,PostgreSQL中国社区常委。 直播回放链接:https://yq.aliyun.com/live/1046 议题PPT下载,戳这里!https://yq.aliyun.com/download/3562 本文将主要围绕以下四个方面进行分享: 管理模式的变化云数据库VS.自建数据库云DBA知识体系构成如何成为优秀的云DBA一、管理模式的变化对于数据库技术而言,“云”已经成为大家无法忽视的技术趋势。在Gartner 2018年的数据库魔力四象限里面,云计算数据库厂商已经占LEADERS及VISIONARIES领域的绝对比例,这也代表了业界对于云的认可。 那么,云和传统架构有什么不同呢?对于传统数据库系统而言,需要搭建很多的硬件,连接很多的网线,在自己搭建的私有云里面可能会有一些虚拟化或者容器化的架构,再往上对于DBA而言其实需要的就是一个数据库,需要能够连接进去进行操作。当然了,在传统架构下,DBA能够对数据库有更多的操作和配置,但是在云上可能只会提供一部分数据库配置文件的修改权限,并不会允许修改全部配置,这是因为云为DBA提供的是SLA,也就是说云数据库提供的是服务。针对于服务而言,不太可能允许DBA去对操作系统进行改变,因为这样可能会破坏HA,因此会有一些限制,但是对于数据库操作而言,依旧是通过一个端口就连接进去的。 除了数据库架构设计之外,传统架构和云架构在做安装配置的时候也会有所不同。在传统架构下,DBA需要去规划数据库所有的一切,包括操作系统、硬件以及各种安装准备以及验收、切换等一系列演练。在云架构之下,整体的配置、安装以及部署是不需要DBA敲各种命令或者安装各种业务系统的,操作系统、参数优化以及整体的HA只需要在云控制台上点击几下就可以配置完成,无论使用阿里的公共云还是私有云都是这样的状态。这些就是在管理模式方面或者在系统创建过程中已经能够看到的变化。 二、云数据库VS.自建数据库有很多人存在这样一个疑问。那就是“云数据库和自建数据库有哪些区别?”。这里首先澄清一个概念,在阿里巴巴看来,真正云托管的数据库才是云数据库,而如果只是使用ECS云服务器来自行搭建的数据库并不算是真正的云数据库。 实际上,云数据库最终提供的是一个服务,其包括了系统的可靠性、可用性、安全、备份等一系列的东西,当建立完云数据库这些都是配置完成的,无需DBA进行二次配置。当然,如果DBA有自己配置的需求,阿里云所提供的云数据库服务也会提供API接口进行调配,或者也可以通过阿里云的管理平台进行操作,而不像传统情况下需要非常高的数据库初始建设费用。 成本模式的变迁 对于成本而言,传统情况下自己建设数据中心需要规划好未来3到5年到底需要多少资源,所以成本是一次性提供的。此外,对于DBA而言,一般将其分为业务DBA和运维DBA,前者为数据库业务解决问题,发挥功用,后者纯粹地负责运维工作,比如安装、部署、定期进行各种类型的巡检。未来,运维DBA会因为云架构的体现慢慢地减少,而业务DBA却不会消亡,因此DBA应该更加关注于企业在做什么业务,数据架构应该如何优化,帮助企业改变本身的运营状态。 以往成本的开支,一下子就是一台服务器,但是如今在云上或者互联网上有很多的创业公司,所谓的“独角兽”就是从很小规模开始起步,突然之间变成很大。当这些创业公司小的时候或许并不需要购买一台服务器,通过云架构,就可以从很小开始,逐渐弹性上去,这样的弹性能力使得IT实现资源的释放。如果今天还在使用传统的数据库服务器购买方式,而竞争对手或许就能够将节省下来的资金用于技术人员或者业务上去,因为没有了固定资产初期的开销,对于创业公司而言,其运行的资金链也会更加健康,发展的速度也会更快。 三、云DBA知识体系构成随着数据库技术的发展,企业对于DBA的需求也不断提高。从对于OLTP这样的SQL数据库和NoSQL数据的掌握,进一步演进,为了解决性能问题可能需要Key-Value缓存数据库,之后建立OLAP数据仓库,再之后实现大数据离线分析。 而对于初创公司而言,就会发现在最开始可能三两台机器就搞定了,只需要一个兼职的DBA。 进一步当开始使用Key-Value缓存数据库之后,业务越来越重,单台服务器无法搞定,需要实现HA。此时就比较困难了,因此需要一个比较神奇的DBA,需要DBA什么都懂。 当企业进一步发展到更大的时候,可能不仅仅需要解决一套系统的问题,可能需要解决多套系统的问题。此时可能需要一个DBA团队,分工会变得更为细致,不仅有专业的DBA,还应该有顶尖的架构级别DBA来解决整体问题。 更进一步,可能需要做数据仓库和大数据,那么整个DBA团队的分工就会更加明细。 在企业的实际运行过程中,DBA需要做大量的工作,有的时候甚至是操作系统的各种细节都需要了解清楚才能将数据库调优好。 云数据库的理论基础 而当进入云数据库时代,需要看到的是另外一种景象。这里有一些云计算的新名词,比如Region地域、AZ可用区、VPC以及VSwith等,这些都是云DBA需要了解和掌握的。从数据库的角度来看,云数据库的确出现了很多新名词,但是数据库基础理论依然是不变的,依然会有实例、高可用、分布式、SQL、ACID和CAP等理论。 运维简化:自动化部署 以往都会说需要部署一个主备集群,而今天如果想要部署主备集群也会在一个IDC中心进行部署。如果想要部署跨IDC的主备集群,在传统架构下往往需要购买光纤、光缆,并且需要确定光纤、光缆的延迟情况,判断其所造成的延迟是否能够接受。而在云数据库架构之下,这些信息都不需要进行管理,所需要管理的就是在购买云数据库时进行选择,比如选择跨中心的主备就可以直接建立起来,因此这种复杂架构的构建并不需要自己来规划,可以节省DBA去做传统底层业务处理的时间。 运维简化:跨地域部署及切换 除了对于传统架构比较容易的同一个城市跨AZ之外,其实如果想要实现跨省就会变得非常复杂了。然而,在云上就会变得非常容易,如果想要实现跨Region的搭建就可以利用阿里云上的DTS工具将数据拉过去,需要进行数据复制的时候才会收费,平时不用的时候甚至可以直接将其关闭掉。 当搭建了跨Region的数据中心之后,后面就会有更多的事情。比如到底敢不敢进行主备切换,以往做主备切换的时候都需要配置一大堆的DNS,自己写很多脚本做确认,而在云架构底下,只需要通过一个按钮就可以实现。因此,大家一定要清楚,作为云DBA应该去学习哪些东西,同时需要放弃哪些东西的学习。因此当有云架构之后,DBA可以将重心放到学习如何优化SQL以及各种不同的数据库特性以及它们之间的组合架构如何解决业务上的问题,而底层的业务架构可以交给云去做。 运维简化:定期全/增量备份 在云上面,如果需要做定期增量备份也仅仅需要点击几个按钮进行构建即可。 运维简化:恢复到时间点 无论针对于哪个数据库,阿里云的服务都可以做到任意时间点的秒级恢复。这一功能并不只是为了帮助用户找回数据,很多用户的DBA和开发的互动越来越频繁,如果开发收到某个时间段系统运行较慢的反馈,就可以直接克隆一个那个时间段的新实例出来,并且只需要按需购买即可,克隆出来实例调试完程序之后直接将其关闭掉即可,一切的成本都在DBA的掌握之中。 运维简化:按需横向扩展 DBA对于数据库的横向扩展也会做很多动作,传统的方式通过只读实例可以做相应的扩展,同时还有像阿里云的DRDS分布式数据库分片的运行方案,也能够比较容易地搭建出来,进一步地还可以走向PolarDB,通过分布式的一写多读来简化业务规则。未来,DBA需要重点关注的点在于什么时候使用什么样的架构。举例而言,如果需要解决某个大促时间段大量的读请求问题,应该通过只读实例来实现。而如果老旧业务完全可以基于互联网改写,就可以选择直接通过DRDS做整个系统的分库分表操作。如果需要非常强的与关系型数据库一致性的业务,并且与此同时数据量非常大,可能需要选择PolarDB的架构,因此DBA需要对于不同的数据库架构以及其背后原理有自己的理解。 运维简化:自动读写分离阿里云数据库帮助用户实现了读写分离,DBA不需要再进行应用程序上的业务改写,比如对于读写分离的设置都可以实现自动化。通过对于请求的分析来判断应该分发到读实例还是写实例。 以上这些都是云数据库能够提供的能力,大家会发现以往的管理模型已经都覆盖到了。未来运维方面的DBA工作可能减轻,因此DBA应该跳到业务方向上进行发展。 四、如何成为优秀的云DBA在云数据库的背景下,DBA是否还需要学习每一部分的数据库管理知识呢?因为人的时间是有限的,未来除非真的要做类似于阿里云的整体管控系统时需要深入底层进行分析,而如果不是,那么这些数据库管理就可以交给云管控平台来实现。但是数据库优化却需要DBA知道和掌握,这里并不是指修改哪些参数能够优化成什么样子,因为这些在云平台上就已经配置好了,但是DBA需要知道的是针对于某个数据库,什么样的索引对它更加有效,表与表之间的关系应该如何建立才能使得数据库性能更好。 云数据库提供了很多的集群架构,也并不一定需要全部学习。无论是单节点、双节点还是三节点,通过阿里云都可以实现一键式部署。因此作为DBA更加需要了解不同的数据库实例之间应该如何进行互动,从而产生对业务有效的架构方案和规划方案,这正是DBA需要深入思考的,而不是每天都在备份服务器,部署数据库,检修各种硬件。 云服务支持边界 基于云的运行环境,云数据库服务和DBA的边界会发生改变。资源调度、基础优化、平台能力以及准确输出都是由云来提供的,而企业的DBA需要做这样几件事情:对于表结构需要花费更多的时间来规划,定义自己企业的SQL标准来规范开发模型,对于SQL以及结构进行优化来提升业务性能。此外,DBA不仅应该关注于数据库,实际上也应该做企业成本的控制,通过不同的数据模型组合来解决不同的业务问题,也需要了解云数据库日志的不同,并通过故障检测自查或者发起服务需求。 性能问题甄别 对于云DBA而言,如果出现了数据库性能问题应该怎么做呢?其实任何的云厂商都会有自己成熟的一整套监控以及性能分析方案,比如阿里云的方案就源自于阿里巴巴内部的经验,能够帮助DBA发现故障并提供解决方案,使用起来非常方便。 云服务支持边界 此外,阿里云也提供了一种能力,就是阿里云后端的DBA会帮助用户解决数据库相关的问题。以往情况下,如果数据库出现了问题,需要打电话给服务商来约时间解决,存在一定的延迟。而今天在阿里云上面,DBA随时可以进入。并且阿里云还提供了安全保障,具有完善的授权机制,只有用户授权阿里云的DBA访问用户数据库或者进行服务的时候,阿里云的DBA才有权限为用户提供服务,而如果没有得到授权,阿里云的DBA是不能够进入的。 高危SQL预防 阿里巴巴具有自己的一整套数据库开发规范,而用户的DBA也可以自己定义一套数据库开发规范,比如可以定义某一个字段是否可以以某种方式编写,这样就从系统设计和规范的层面避免烂SQL进入系统,进而造成系统故障。 跨云管理 今天,阿里云本身在运营云,而其实阿里云也会提供跨云的管理工具。无论用户使用的是哪里的云,只要管理的是MySQL、MongoDB、Redis数据库都会提供HDM工具来协助用户管理跨云数据库。 总结一下,云数据库带来了标准化部署、自动化运维、按需扩容以及工具化调优等优势。对于企业而言,不要再让DBA为部署和备份等琐碎的运维工作所缠绕了,他们应该将精力投入到优化架构、写好SQL以及做好数据库的整体构造上,进而为企业输出核心技术生产力。 本文作者:七幕 阅读原文 ...

May 29, 2019 · 1 min · jiezi

达摩院首席数据库科学家李飞飞云原生新战场我们如何把握先机

阿里妹导读:云计算大潮来袭,传统数据库市场正面临重新洗牌的情境,包括云数据库在内的一批新生力量崛起,动摇了传统数据库的垄断地位,而由云厂商主导的云原生数据库则将这种“改变”推向了高潮。云时代的数据库将面临怎样的变革?云原生数据库有哪些独特优势?在 DTCC 2019大会上,阿里巴巴副总裁 李飞飞博士就《下一代云原生数据库技术与趋势》进行了精彩分享。 李飞飞(花名:飞刀),阿里巴巴集团副总裁,高级研究员,达摩院首席数据库科学家,阿里云智能事业群数据库产品事业部负责人,ACM 杰出科学家。 大势所趋:云数据库市场份额增速迅猛如下图所示的是 Gartner 关于全球数据库市场份额的报告,该报告指出目前全球数据库市场份额大约为400亿美金,其中,中国数据库市场份额占比为3.7%,大约为14亿美金。 具体到数据库市场分布,传统五大数据库厂商 Oracle、Microsoft、IBM、SAP、Teradata 占比达到了80%,云数据库的份额占比接近10%,并且云数据库市场份额占比每年也在快速增长,因此, Oracle、MongoDB 等也在大力布局其在云数据库市场的竞争态势。 根据 DB-Engines 数据库市场分析显示,数据库系统正朝着多样化、多元化的方向发展,从传统的 TP 关系型数据库发展到今天的多源异构的数据库形态。目前,处于主流位置的还是大家耳熟能详的数据库系统,比如商业数据库 Oracle、SQL Server以及开源的 MySQL、PostgreSQL 等。而一些比较新的数据库系统,比如MongoDB、Redis 则开辟了一个新的赛道。数据库 License 的传统销售方式在逐渐走下坡路,而开源以及云上数据库 License 的流行程度却在不断提升。 数据库:云上应用关键的一环正如 AWS 创始人 Jeff Bezos 所说:“The real battle will be in databases”。因为云最早是从 IaaS 做起来的,从虚拟机、存储、网络,到现在如火如荼的语音识别、计算机视觉以及机器人等智能化应用,都是基于 IaaS 的,而数据库就是连接 IaaS 与智能化应用 SaaS 最为关键的一环。从数据产生、存储到消费的各个环节,数据库都至关重要。 数据库主要包括四大板块,即 OLTP、OLAP、NoSQL 以及数据库服务和管理类工具,也是云数据库厂商发力的四个方向。对于 OLTP 而言,技术发展已经历经了40年,而如今大家还在做的一件事情就是“加10元和减10元”,也就是所谓的事务处理。当数据量变得越来越大和读写冲突的原因,对数据进行在线实时分析的需求衍生出了 OLAP。由于需要 Scale out,而数据强一致性不能够得到保证,就有了NoSQL 。而最近又出现了一个新名词—— NewSQL,这是因为 NoSQL 也有所不足,故将传统 OLTP 的 ACID 保证与 NoSQL 的 Scale out 能力进行了整合,变成了NewSQL。 ...

May 29, 2019 · 2 min · jiezi

分布式系统一致性协议

一致性模型本质上是进程与数据存储的约定,通过一致性模型我们可以理解和推理在分布式系统中数据复制需要考虑的问题和基本假设。那么,一致性模型的具体实现有一些呢?本文会介绍一致性协议实现的主要思想和方法。 什么是一致性协议一致性协议描述了特定一致性模型的实际实现。一致性模型就像是接口,而一致性协议就像是接口的具体实现。一致性模型提供了分布式系统中数据复制时保持一致性的约束,为了实现一致性模型的约束,需要通过一致性协议来保证。 一致性协议根据是否允许数据分歧可以分为两种: 单主协议(不允许数据分歧):整个分布式系统就像一个单体系统,所有写操作都由主节点处理并且同步给其他副本。例如主备同步、2PC、Paxos 都属于这类协议。多主协议(允许数据分歧):所有写操作可以由不同节点发起,并且同步给其他副本。例如 Gossip、POW。可以发现,它们的核心区别在于是否允许多个节点发起写操作,单主协议只允许由主节点发起写操作,因此它可以保证操作有序性,一致性更强。而多主协议允许多个节点发起写操作,因此它不能保证操作的有序性,只能做到弱一致性。 值得注意的是,一致性协议的分类方式有很多种,主要是看从哪个角度出发进行归类,常用的另一个归类方式是根据同步/异步复制来划分,这里就不多做讨论了。下面对单主协议和多主协议分别做一些共性的分析,篇幅所限,不会深入到协议细节。 单主协议单主协议的共同点在于都会用一个主节点来负责写操作,这样能够保证全局写的顺序一致性,它有另一个名字叫定序器,非常的形象。 主备复制主备复制可以说是最常用的数据复制方法,也是最基础的方法,很多其他协议都是基于它的变种。 主备复制要求所有的写操作都在主节点上进行,然后将操作的日志发送给其他副本。可以发现由于主备复制是有延迟的,所以它实现的是最终一致性。 主备复制的实现方式:主节点处理完写操作之后立即返回结果给客户端,写操作的日志异步同步给其他副本。这样的好处是性能高,客户端不需要等待数据同步,缺点是如果主节点同步数据给副本之前数据缺失了,那么这些数据就永久丢失了。MySQL 的主备同步就是典型的异步复制。 两阶段提交两阶段提交(2PC)是关系型数据库常用的保持分布式事务一致性的协议,它也属于同步复制协议,即数据都同步完成之后才返回客户端结果。可以发现 2PC 保证所有节点数据一致之后才返回给客户端,实现了顺序一致性。 2PC 把数据复制分为两步: 表决阶段:主节点将数据发送给所有副本,每个副本都要响应提交或者回滚,如果副本投票提交,那么它会将数据放到暂存区域,等待最终提交。提交阶段:主节点收到其他副本的响应,如果副本都认为可以提交,那么就发送确认提交给所有副本让它们提交更新,数据就会从暂存区域移到永久区域。只要有一个副本返回回滚就整体回滚。可以发现 2PC 是典型的 CA 系统,为了保证一致性和可用性,2PC 一旦出现网络分区或者节点不可用就会被拒绝写操作,把系统变成只读的。由于 2PC 容易出现节点宕机导致一直阻塞的情况,所以在数据复制的场景中不常用,一般多用于分布式事务中(注:实际应用过程中会有很多优化)。 分区容忍的一致性协议分区容忍的一致性协议跟所有的单主协议一样,它也是只有一个主节点负责写入(提供顺序一致性),但它跟 2PC 的区别在于它只需要保证大多数节点(一般是超过半数)达成一致就可以返回客户端结果,这样可以提高了性能,同时也能容忍网络分区(少数节点分区不会导致整个系统无法运行)。分区容忍的一致性算法保证大多数节点数据一致后才返回客户端,同样实现了顺序一致性。 下面用一个简单的示例来说明这类算法的核心思想。假设现在有一个分布式文件系统,它的文件都被复制到 3 个服务器上,我们规定:要更新一个文件,客户端必须先访问至少 2 个服务器(大多数),得到它们同意之后才能执行更新,同时每个文件都会有版本号标识;要读取文件的时候,客户端也必须要访问至少 2 个服务器获取该文件的版本号,如果所有的版本号一致,那么该版本必定是最新的版本,因为如果前面的更新操作要求必须要有大多数服务器的同意才能更新文件。 以上就是我们熟知的 Paxos、ZAB、Raft 等分区容忍的一致性协议的核心思想:一致性的保证不一定非要所有节点都保持一致,只要大多数节点更新了,对于整个分布式系统来说数据也是一致性的。上面只是一个简单的阐述,真正的算法实现是比较复杂的,这里就不展开了。 分区容忍的一致性协议如 Paxos 是典型的 CP 系统,为了保证一致性和分区容忍,在网络分区的情况下,允许大多数节点的写入,通过大多数节点的一致性实现整个系统的一致性,同时让少数节点停止服务(不能读写),放弃整体系统的可用性,也就是说客户端访问到少数节点时会失败。 值得注意的是,根据 CAP 理论,假设现在有三个节点 A、B、C,当 C 被网络分区时,有查询请求过来,此时 C 因为不能和其他节点通信,所以 C 无法对查询做出响应,也就不具备可用性。但在工程实现上,这个问题是可以被绕过的,当客户端访问 C 无法得到响应时,它可以去访问 A、B,实际上对于整个系统来说还是部分可用性的,并不是说 CP 的系统一定就失去可用性。详细的分析参考分布式系统:CAP 理论的前世今生 多主协议相比单主协议为了实现顺序一致性,不允许多个节点并发写,多主协议恰恰相反,只保证最终一致性,允许多个节点并发写,能够显著提升系统性能。由于多主协议一般提供的都是最终一致性,所以常用在对数据一致性要求不高的场景中。 Gossip 协议就是一种典型的多主协议,很多分布式系统都使用它来做数据复制,例如比特币,作为一条去中心化的公链,所有节点的数据同步都用的是 Gossip 协议。此外,Gossip 协议也在一些分布式数据库中如 Dynamo 中被用来做分布式故障检测的状态同步,当有节点故障离开集群时,其他节点可以快速检测到。 ...

May 28, 2019 · 1 min · jiezi

3分钟干货之Java性能优化的几个细节

△尽量使用局部变量调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快;其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。 △尽量处理好包装类型和基本类型两者的使用场所虽然包装类型和基本类型在使用过程中是可以相互转换,但它们两者所产生的内存区域是完全不同的,基本类型数据产生和处理都在栈中处理,包装类型是对象,是在堆中产生实例。在集合类对象,有对象方面需要的处理适用包装类型,其他的处理提倡使用基本类型。 △慎用synchronized,尽量减小synchronize的方法都知道,实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。synchronize方法被调用时,直接会把当前对象锁了,在方法执行完之前其他线程无法调用当前对象的其他方法。所以,synchronize的方法尽量减小,并且应尽量使用方法同步代替代码块同步。

May 27, 2019 · 1 min · jiezi

Blink-有何特别之处菜鸟供应链场景最佳实践

阿里妹导读:菜鸟供应链业务链路长、节点多、实体多,使得技术团队在建设供应链实时数仓的过程中,面临着诸多挑战,如:如何实现实时变Key统计?如何实现实时超时统计?如何进行有效地资源优化?如何提升多实时流关联效率?如何提升实时作业的开发效率? 而 Blink 能否解决这些问题?下面一起来深入了解。背景菜鸟从2017年4月开始探索 Blink(即 Apache Flink 的阿里内部版本),2017年7月开始在线上环境使用 Blink,作为我们的主流实时计算引擎。 为什么短短几个月的探索之后,我们就选择Blink作为我们主要的实时计算引擎呢? 在效率上,Blink 提供 DataStream、TableAPI、SQL 三种开发模式,强大的 SQL 模式已经满足大部分业务场景,配合半智能资源优化、智能倾斜优化、智能作业压测等功能,可以极大地提升实时作业的开发效率;在性能上,诸如MiniBatch&MicroBatch、维表 Async&Cache、利用 Niagara 进行本地状态管理等内部优化方案,可以极大地提升实时作业的性能;在保障上,Blink 自带的 Failover 恢复机制,能够实现线程级的恢复,可以做到分钟级恢复,配合 Kmonitor 监控平台、烽火台预警平台,可以有效地实现实时作业的数据保障。 接下来,我将结合供应链业务的一些业务场景,简要说明,Blink 如何解决我们遇到的一些实际问题。 回撤机制订单履行是供应链业务中最常见的物流场景。什么是订单履行呢?当商家 ERP 推单给菜鸟之后,菜鸟履行系统会实时计算出每笔订单的出库、揽收、签收等节点的预计时间,配送公司需要按照各节点的预计时间进行订单的配送。为了保证订单的准点履约,我们经常需要统计每家配送公司每天各个节点的预计单量,便于配送公司提前准备产能。 看似很简单的实时统计加工,我们在开发过程中遇到了什么问题呢?履行重算!当物流订单的上游某个节点延迟时,履行系统会自动重算该笔订单下游所有节点的预计时间。比如某个物流订单出库晚点后,其后的预计揽收时间、预计签收时间都会重算。而对于大部分的实时计算引擎来说,并不能很友好的支持这种变 Key 统计的问题。以前,数据量没那么大的时候,还可以通过 OLAP 数据库来解决这类场景,当量上来后, OLAP 方案的成本、性能都是很大的问题。 除了 OLAP 方案,我们提倡采用 Blink 已经内置的 Retraction 机制,来解决这类变 Key 统计的问题,这也是我们在2017年初就开始尝试 Blink 的重要原因。Blink 的Retraction 机制,使用 State 在内存或者外部存储设备中对数据进行统计处理,当上游数据源对某些汇总 Key 的数据做更新时,Blink 会主动给下游下发一个删除消息从而“撤回”之前的那条消息,并用最新下发的消息对表做更新操作。 下面是一个简化后的案例,供了解Blink Retraction的内部计算过程: 对于上述案例,可以通过 Blink 提供的强大的、灵活的、简易的 SQL 开发模式来实现,只需要几行 SQL 即可完成。 select plan_tms_sign_time ,sum(1) as plan_tms_sign_lgtord_cntfrom (select lg_order_code ,last_value(plan_tms_sign_time) as plan_tms_sign_time from dwd_csn_whc_lgt_fl_ord_ri group by lg_order_code ) ssgroup by plan_tms_sign_time;维表关联供应链业务的实体角色非常多(仓、配、分拨、站点、小件员、货主、行业、地区等),实体繁多,这意味着我们在建设实时明细中间层的时候,会使用大量的维表关联,这对 Blink 在维表关联的性能上提出了更高的要求——如何提升大量的大小维表的关联性能?Blink 从来没让用户失望,Blink SQL 模式在维表关联的性能上,也做了大量的优化: ...

May 24, 2019 · 2 min · jiezi

阿里云PolarDB发布重大更新-支持Oracle等数据库一键迁移上云

5月21日,阿里云PolarDB发布重大更新,提供传统数据库一键迁移上云能力,可以帮助企业将线下的MySQL、PostgreSQL和Oracle等数据库轻松上云,最快数小时内迁移完成。据估算,云上成本不到传统数据库的1/6。目前,已有约40万个数据库迁移到阿里云上。 阿里云方面表示,该产品实现了两大技术突破:通过自研超低延迟文件系统PolarFS大幅降低数据跨网络的延迟,并且开发了一种全新的共识协议ParallelRaft,提升系统吞吐量。在此之前,PolarFS的研究成果已发表在数据库顶级会议 VLDB 2018 《PolarFS: An Ultra-low Latency and Failure Resilient Distributed File System for Shared Storage Cloud Database》。 企业在使用传统商业数据库时,经常会面临授权费用贵、硬件成本高、架构与运维复杂、迁移难度大等问题,此次云原生数据库PolarDB的重大更新针对此类痛点,实现一键快速迁移,并提供云上的完整生态服务。 此外,产品还具备三大亮点,满足企业在数字时代海量的数据存储需求:1、单实例规格最大达88核710GB;2、业内最快单节点读写速度100万QPS;3、实现三份数据存储实时同步0延迟。除性能领先外,云上数据库成本也低于传统数据库,不到后者的1/6。 PolarDB是阿里云在2018年正式商业化的云原生数据库,采用存储计算分离、软硬一体化设计,具备快速弹性能力、超大规格、超高可靠性以及两倍于AWS Aurora的性能。目前已是阿里云上增长最快的数据库产品。 随着移动互联网、物联网的发展,数据量剧增。企业上云也成大势所趋,企业上云意愿达84%,云也对数据库提出了更高的扩展性和可用性要求。 因昂贵、扩展性差、技术复杂、迭代慢,传统商业数据库饱受诟病。与传统数据库相反,云原生数据库天然拥有云计算的弹性能力,兼具开源数据库的易用、开放特点,及传统数据库的管理和处理性能优势,是云时代下企业数据库的最佳选择。 在全球范围内,一场替换传统数据库的行动正在进行。早在2013年,阿里巴巴就将最后一台Oracle数据库从其淘宝核心系统中下线。亚马逊也公开表示,将在2020年彻底放弃Oracle数据库。 阿里云智能数据库事业部负责人李飞飞表示,云原生数据库在成本、灵活度、安全、技术进化层面都优于传统数据库,传统数据库会像马车一样被淘汰。 阿里云拥有国内最丰富的云数据库产品,目前已有约40万个数据库迁移到阿里云上,涵盖金融、电信、制造、物流等领域的龙头企业。在Gartner发布的2018年数据库魔力象限中,阿里云是国内首个进入挑战者象限的中国公司。 阿里云数据库的技术研究也在国际上崭露头角。PolarDB相关论文入选今年的国际顶级数据库会议SIGMOD,这是阿里云数据库连续第二年入选该学术会议。 本文作者:阿里云头条阅读原文 本文为云栖社区原创内容,未经允许不得转载。

May 21, 2019 · 1 min · jiezi

Apache-Cassandra-在-Facebook-的应用

摘要: 在 Instagram (Instagram是Facebook公司旗下一款免费提供在线图片及视频分享的社交应用软件,于2010年10月发布。)上,我们拥有世界上最大的 Apache Cassandra 数据库部署。谁说 Facebook 弃用 Cassandra?相反 Facebook 拥有全世界最大的单个 Cassandra 集群部署,而且他们对 Cassandra 做了很多性能优化,包括 Cassandra on RocksDB 以提升 Cassandra 的响应时间。 在 Instagram (Instagram是Facebook公司旗下一款免费提供在线图片及视频分享的社交应用软件,于2010年10月发布。)上,我们拥有世界上最大的 Apache Cassandra 数据库部署。我们在 2012 年开始使用 Cassandra 取代 Redis ,在生产环境中支撑欺诈检测,Feed 和 Direct inbox 等产品。起初我们在 AWS 环境中运行了 Cassandra 集群,但是当 Instagram 架构发生变化时,我们将 Cassandra 集群迁移到Facebook 的基础架构中。我们对 Cassandra 的可靠性和可用性有了非常好的体验,但是在读取数据延迟方面我们觉得他需要改进。 去年 Instagram 的 Cassandra 团队开始着手这个项目,以显着减少 Cassandra 的读取延迟,我们称之为 Rocksandra。 在这篇文章中,我将描述该项目的动机,我们克服的挑战以及内部和公共云环境中的性能指标。 动机在 Instagram 上,我们大量使用 Apache Cassandra 作为通用键值存储服务。Instagram 的大多数 Cassandra 请求都是在线的,因此为了向数亿 Instagram 用户提供可靠且响应迅速的用户体验,我们有很高的 SLA 要求。 ...

May 9, 2019 · 2 min · jiezi

MySQL-InnoDB特性-Buffer-Pool漫谈

缓存管理是DBMS的核心系统,用于管理数据页的访问、刷脏和驱逐;虽然操作系统本身有page cache,但那不是专门为数据库设计的,所以大多数数据库系统都是自己来管理缓存。由于几乎所有的数据页访问都涉及到Buffer Pool,因此buffer pool的并发访问控制尤为重要,可能会影响到吞吐量和响应时间,本文主要回顾一下MySQL的buffer Pool最近几个版本的发展(若有遗漏,欢迎评论补充), 感受下最近几年这一块的进步 MySQL5.5之前只能设置一个buffer pool, 通过innodb_buffer_pool_size来控制, 刷脏由master线程承担,扩展性差。 MySQL 5.5引入参数innodb_buffer_pool_instances,将buffer pool拆分成多个instance,从而减少对buffer pool的访问控制,这时候的刷脏还是由Master线程来承担。 MySQL 5.6引入了buffer Pool page Id转储和导入特性,也就是说可以随时把内存中的page no存下来到文件里,在重启时会自动把这些Page加载到内存中,使内存保持warm状态. 此外该版本第一次引入了page cleaner,将flush list/lru上的刷脏驱逐工作转移到单独线程,减少了master线程的负担 MySQL 5.7这个版本发布了一个重要特性:online buffer pool resize. 当然是否是online需要打一个问号,因为在resize的过程中需要拿很多全局大锁,在高负载场景下很容易导致实例Hang住(81615)。 和之前不同,buffer pool被分成多个instance,每个instance又由多个chunk组成,每个chunk的大小受到参数innodb_buffer_pool_chunk_size控制,默认128MB, buffer pool resize都是以chunk为单位增加或减少的。另外一个需要注意的点是:你配置的Buffer Pool Size可能比你实际使用的内存要大,尤其对于大Bp而言,这是因为内部做了对齐处理, buffer pool size必须以 innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances来做向上对齐(80350) 我们知道通常数据文件的IO都被设置成O_DIRECT, 但每次修改后依然需要去做fsync,来持久化元数据信息,而对于某些文件系统而言是没必要做fsync的,因此加入了新选项O_DIRECT_NO_FSYNC,这个需求来自于facebook. 他们也对此做了特殊处理:除非文件size变化,否则不做fsync。(最近在buglist上对这个参数是否安全的讨论也很有意思,官方文档做了新的说明,感兴趣的可以看看 [94912:O_DIRECT_NO_FSYNC possible write hole](https://bugs.mysql.com/bug.php?id=94912))) 再一个重要功能是终于引入了multiple page cleaner, 可以多个后台线程并发刷脏页,提供了更好的刷脏性能,有效避免用户线程进入single page flush。当然这还不够完美,主要有四点: 用户线程依然会进入single page flush,而一旦大量线程进入,就会导致严重性能下降:超频繁的fsync,激烈的dblwr竞争,线程切换等等当redo空间不足时,用户线程也会进入page flush,这在高负载场景下是很常见的,你会发现系统运行一段时间后,性能急剧下降。这是因为redo产生太快,而page flush又跟不上,导致checkpoint无法推进。那么用户线程可能就要过来做fuzzy checkpoint了。那时候性能基本上没法看了。dblwr成为重要的单点瓶颈。 如果你的服务器不支持原子写的话,必须打开double write buffer。写入Ibdata一段固定区域,这里是有锁包含的,区分为两部分:single page flush和batch flush, 但无论如何,即使拆分了多个page cleaner,最终扩展性还是受限于dblwr没有专用的lru evict线程,都是Page cleaner键值的。举个简单的例子,当buffer pool占满,同时又有很多脏页时,Page cleaner可能忙于刷脏,而用户线程则得不到free page,从而陷入single page flush如果你对上述几个问题极不满意,可以尝试percona server, 他们向来擅长优化Io bound场景的性能,并且上述几个问题都解决了,尤其是dblwr,他们做了多分区的改进。 ...

April 29, 2019 · 1 min · jiezi

选择阿里云数据库HBase版十大理由

根据Gartner的预计,全球非关系型数据库(NoSQL)在2020~2022预计保持在30%左右高速增长,远高于数据库整体市场。 阿里云数据库HBase版也是踏着技术发展的节奏,伴随着NoSQL和大数据技术的兴起和发展,从2010年开始研究和发展。时光荏苒,日月如梭,转眼九年时间,在阿里云上直接开放提供服务也有1年多时间,并在去年的12月份全新发布X-Pack,将单一的HBase演进到一个完整的数据处理平台的能力。我们注意到还有很多同学和客户不清楚HBase X-Pack是什么,什么场景下合适选择HBase X-Pack。 首先我们先来看下HBase X-Pack的定位: HBase X-Pack是基于HBase及HBase生态构建的 低成本一站式数据处理平台。HBase X-Pack支持:HBase API(包括RestServerThriftServer)、关系Phoenix SQL、时序OpenTSDB、全文Solr、时空GeoMesa、图HGraph、分析Spark on HBase,是阿里云首个支持多模式的分布式数据库,且协议100%兼容开源协议。HBase X-Pack实现数据从处理、存储到分析全流程闭环,让客户用最低成本实现一站式数据处理。接下来一起来梳理一下阿里云HBase X-Pack关键能力,一起看看选择阿里云HBase X-Pack的十个理由。 理由一:一体化数据处理平台,提供一站式能力企业数字化转型时代,业务越来越复杂,需要一个平台可以提供一站式处理能力。传统大数据各个组件非常多,各个组件分层发展,给扩展性带来非常大的便利,但同时也引入了非常高的技术门槛,云HBase X-Pack通过集成Spark,Solr,HBase,时序,时空,图等组件,打通各个组件之间的数据同步,通过数据工作台提供统一一体化交互式的操作体验,实现计算、存储、分析、检索、机器学习五位一体的一站式能力,极大的降低了使用门槛,轻松上手,同时提供全托管的服务,避免各种复杂的运维和技术坑。 云HBase X-Pack详细的能力可以访问云HBase的帮助,里面有各个能力详细的介绍: 理由二:深厚的技术积累企业决策选择云服务,最核心的一个因素就是降低TCO,最看重的核心因素就是背后的技术力量,服务能力。阿里云HBase X-Pack经过9年的发展,积累强大的专家团队,目前拥有国际认证7个committer,4个PMC,拥有国内独一无二的技术实力。我们拥有集团超过万台的服务经验,对各种异常场景,数据可靠性,可用性,性能,数据迁移各个方面有全套的服务和工具。 理由三:独家企业版本,以及最新2.0版本阿里云HBase提供的版本是经过、千锤百炼的企业版本,在稳定性和性能上远胜于开源的版本,并且全球首家提供最新2.0版本。关于阿里云HBase发展历程,可以看这里详细介绍:https://yq.aliyun.com/articles/601531。阿里云HBase和开源版本的关键区别,可以查看:https://help.aliyun.com/document_detail/49502.html。 理由四:开发效率最高的数据库Gartner在2017年数据库厂商推荐报告中就明确指出多模是发展趋势阿里云新发布X-Pack更是将多模推上新高度,KV的基础上,同时支持时序、时空、图、文档等多种数据模型。我们知道,大数据时代,业务多样性是大数据的本质之一,强制使用单一模型只会降低生产效率,HBase X-Pack提供KV、SQL、时序、时空、图丰富的多模多模能力,帮助客户可以根据不同的业务选择不同的数据处理模型,支持业务灵活选择,从而实现最高效率的开发和生产。 理由五:做成本最低的数据库HBase诞生于Google的bigtable论文,天然是为了存储海量互联网数据而诞生,低成本能力是其天然的属性。云HBase X-Pack在继承HBase自身能力的同时,为了给客户节省成本做了很多努力。体现在内核,整体方案各个方面,主要有: 云HBase版本的内核是经过优化的,性能平均高出自建版本30%~300%:如果对性能有要求的场景,就可以节省更少的CPU资源,获取更大的效果,具体可以参考https://yq.aliyun.com/articles/198654。齐全的产品形态,满足各种业务场景,提供最高性价比:HBase X-Pack支持单节点,集群版本,跨可用区/跨地域双集群版本,满足用户从测试,生产环境,高可用各种使用环境,平衡能力和成本,提供高性价比的选择,具体可以参考https://help.aliyun.com/document_detail/71538.html。提供数据全生命周期管理功能,数据冷热分离,存储成本下降3.5倍:很多场景里面,数据有冷热的需求,我们提供不同的存储介质,包括OSS,本地盘,云盘,高性能云盘,帮助客户实现最佳的存储成本,详细的可以看下https://yq.aliyun.com/articles/646983。客户基于ECS自建,存储选择云盘,hdfs副本数天然是3副本:HBase服务通过和云盘深度集成2副本就可以同样的性能和可靠性。在存储上天然节省1/3,详细的可以访问https://yq.aliyun.com/articles/646983。全托管服务,提供代维,99.9%的SLA:运维在日常数据库工作中占了很大的比重,而且数据库的稳定性关系到整个系统,牵一发和动全身,云HBase X-Pack提供全托管的服务,给客户节省运维费用,以及极大的避免故障带来的损失。提供一体化的方案节省成本:云HBase X-Pack通过把各个组件深度集成和融合,通过组合各个产品之间的能力,给很多场景带来增效,解决了性能瓶颈的同时,带来成本的下降。这里举2个典型的例子:很多人工智能,多媒体场景,在线教育里面,大量图片、小视频文件。传统的使用方法都是存在OSS里面,OSS天然并发和时延处理能力有限,同时读写都是要收费的,读写次数越多,费用越高,使用HBase X-Pack没有这部分的费用,可以解决性能的瓶颈的同时,带来综合成本的降低。 碰到非结构化数据查询的诉求时,大家一般会想起ES。ES适合文本查询,入库会比较差(一般就几百条/S),查询函数也有限。HBase X-Pack通过支持Solr完全补齐了文本查询的能力。同时Phoneix+solr组合结合了HBase和搜索的两者的优势,在吞吐和并发上有优势。对SQL的易用性也有优势。尤其是在新零售等场景,一张表中混杂结构化字段和非结构化字段,可以根据需求,自动创建索引,融合两者的优势。倒排膨胀率很高,入库会极速下降。大部分客户只是部分字段有模糊查询的需求,ES强制所有的用单一技术。Phoneix非常适合并发高的查询,条件不多。搜索技术补充了索引技术,适合各种条件。通过结合phoneix+solr成功平滑查询和存储性能,提高性能的同时,存储成本也下降几倍,非常适合结构化+非结构化混合的场景。 理由六:力争做最好用的数据处理平台HBase主要提供在线查询能力,沉淀下来的数据需要使用Spark来做复杂分析,HBase X-Pack中的Spark为了让用户更便捷的做数据处理,产品上面提供了以下能力: 1)数据工作台:支持交互式、作业管理、工作流、资源管理、元数据管理,从测试、开发、上线一站式开发体验2)spark内置connector:一键关联hbase、mongo、redis、rds等集群,免去调试的烦恼,更加便捷的分析其他数据库的数据3)支持多语言:可以选择习惯的语言进行编程4)可维护性:支持小版本升级、监控、报警,免去Spark集群维护5)离线数仓能力:一键归档在线库rds、polardb、mongo、hbase、cassandra数据到Spark数仓6)成本:集群默认存储为集群版本HDFS,同时支持数据存储在oss降成本 使用HBase X-Pack Spark能够构建业界成熟的一体化数据处理平台,支撑推荐、风控、离线数仓、实时处理及计算、大数据运营、日志分析、去oracle复杂分析等业务场景: 理由七:数据可靠性作为重中之重对大多数公司来说数据的安全性以及可靠性是非常重要的,如何保障数据的安全以及数据的可靠是大多数数据库必须考虑的。2016 IDC的报告表示数据的备份(data-protection)和数据恢复(retention)是NoSQL的最基础的需求之一,阿里云NoSQL数据库也一直把怎么保障客户的数据安全放在首位。以云HBase为例,传统数据库备份恢复的能力都是TB级别,在交易等场景下面是足够的,但面向大数据场景就捉襟见肘了。云HBase通过垂直整合高压缩、内核级优化等能力,将备份恢复的量级成功推高百倍以上,做到 百TB级别甚至更高 ,让客户在大数据量场景下也无后顾之忧。云HBase支持全量(备份集)备份、全量(备份集)恢复、增量(实时)备份、增量(时间点)恢复完整备份恢复能力。 理由八:单集群3个9高可用,双集群4个9高可用HBase通过内核加固,一系列自动运维修复工具,单集群可以提供3个9的可用性,为了满足很多场景下面更高可用性的要求,云HBase支持跨可用区或者跨地域双集群主备同步,可以让多个HBase集群保持同步关系。在一个集群出现故障的时候,迅速地将业务切换至另外一个集群从而避免故障。HBase主备之间数据的同步基于异步链路实现,遵循最终一致性协议,典型的主备同步延迟在200ms左右。 理由九:大量场景验证,久经考验阿里云HBase从10年上线以来,在阿里集团内部久经考验,超过12000台服务器,单集群超过2000台的规模应用。云HBase自发布以来,通过丰富的能力,优秀的全托管能力,全面超越同类产品的技术能力得到金融、社交、多媒体、新零售、车联网网、制作业、政企等等多个行业,多上千个客户的信赖,积累了大量的使用经验。欢迎我们的新老客户访问首页获取更多的信息: 理由十:提供不停机迁移服务,让自建迁移无忧客户已经使用ECS自建服务,想使用云HBase服务,最担心的应该还是迁移过程中对业务的影响,技术团队充分考虑这一点,提供免费的不停机迁移服务,对在线业务0影响,数据迁移一行不丢。当前业界有能力提供不提供不停机迁移HBase服务的仅此一家。 本文简单梳理了阿里云HBase X-Pack十大理由,希望能对大家理解云HBase有一个帮助,另外也给您选型做一个充分的参考。当能我们还有很多改进的空间,我们还在成长的路上持续努力,也欢迎大家联系我们提出宝贵的意见,最后福利,欢迎使用云HBase X-Pack版本,针对首次购买的用户推出了云数据库HBase单节点独享规格,欢迎大家申请试用:https://promotion.aliyun.com/ntms/act/hbasefree.html 本文作者:所在jason阅读原文 本文为云栖社区原创内容,未经允许不得转载。

April 24, 2019 · 1 min · jiezi

阿里云POLARDB如何助力轻松筹打造5亿用户信赖的大病筹款平台

轻松筹首创了“大病救助”模式,帮助了众多病患在第一时间解決了医疗资金等问题,为了从源头解决了医疗资金问题。而在轻松筹这样全球5.5亿用户信赖的大病筹款平台的背后,是日益增长的各种数据。面对这样数据量所造成的巨大挑战,阿里云POLARDB是如何帮助轻松筹践行“善DNA”的呢?本文就为大家分享。 关于轻松筹2014年9月,轻松筹成立。“轻松筹”作为公司旗下的首要产品,“善DNA”可谓贯穿了整个发展历程。轻松筹将目标聚焦在公众健康保障领域,各功能板块都与百姓的健康息息相关。由轻松筹首创的“大病救助”模式帮助众多病患在第一时间解決了医疗资金等问题。 为了从源头解决医疗资金问题,轻松筹于2016年4月推出了“轻松互助”业务,其目的在于抱团抵抗大病风险,一人患病,众人均推救助金。并与多家保险公司达成合作,推出多款会员定制的保险产品,至此,轻松筹“全民健康保障体系”正式建成。 目前,轻松筹在自主研发的“区块链”技术的加持下,再一次开创了行业先河。“阳光链”将大病救助、公益机构及互助行动的捐赠记录、资金流向公开透明,为公益事业及大病救助的发展指明了新方向。历时4年,轻松筹体系(包含大病救助、轻松互助、轻松e保、轻松公益、轻松健康)在全球183个国家和地区的用户总数超过5.5亿、筹款总额超过255亿元。 轻松筹的“大病救助”场景由轻松筹首创的“大病救助”模式,通过社交强关系为大病患者提供高效便捷的筹款渠道,目前已经帮助235万个大病家庭,筹集了255亿元善款。 轻松筹大病救助平台能够为多达千万的用户提供筹款服务,每周增加的相关数据量多达10GB,包括发起筹款的项目信息、用户分享信息、订单数据等,不断增加的数据,很容易在目前的RDS数据库上,达到存储的上限。轻松筹通过将数据迁移至阿里云POLARDB,很好的解决了存储容量和性能的瓶颈。 轻松筹基于阿里云POLARDB的简单架构设计轻松筹最为看重就是阿里云POLARDB存储容量大和免分库分表的特性。因为阿里云POLARDB采用了集群架构,并且采用了计算和存储分离以及读写分离的机制,所以其存储容量最高能够支持100TB,用户无需因为单机容量的天花板而去购买多个MySQL实例做分片,并且也不需要考虑分库分表,因此就简化应用的开发,同时也降低了运维的负担。 其次,轻松筹还看中了POLARDB强大的读写分离能力。当应用程序使用集群地址时,POLARDB通过内部的代理层对外提供服务,应用程序的请求都先经过代理,然后才访问到数据库节点。Proxy不仅可以做安全认证和保护,还可以解析SQL,把写操作发送到主节点,把读操作均衡地分发到多个只读节点,实现自动的读写分离。对于轻松筹的小程序而言,在后台使用POLARDB集群就像使用一个单点的MySQL数据库一样简单。 此外,在性能方面,阿里云POLARDB利用基于Redo的物理复制代替基于Binlog的逻辑复制,提升主备复制的效率和稳定性,即使对大表进行加索引、加字段等DDL操作,也不会造成数据库的延迟,能够实现毫秒级延迟。此外,POLARDB内置并行查询引擎,对执行时长超过1分钟的复杂分析类SQL加速效果明显。这样的性能优势能够很好地满足轻松筹的需求。 POLARDB助力“大病救助”平台数据管理效率提升 在阿里云POLARDB的强大能力的基础之上,轻松筹的“大病救助”平台的数据管理效率有了非常大的提升,其主要体现在以下三个方面: 自适应数据增长 轻松筹的大病筹款项目随着时间的累积,每年以上T以上的结构化数据进行新增进行存储。每年新增数据表达到数百个,单表数据量更是达到亿级别。由于POLARDB采用分布式存储服务,能够根据数据增长自适应增加存储空间,按照实际数据使用量进行计费,不必为数据容量的限制和升级所担忧。 724 高可用服务 阿里云POLARDB采用自带读写分离的Active-Active多活高可用集群架构 ,能够更好的监测故障和进行快速故障自动恢复,确保99.95%的高可用服务的同时,集群自带只读节点,使得系统的聚合读取性能成倍提升。 即时数据检索和查询 大病筹款的数据需要周期性批量写入到POLARDB,而同时又需要支持即时的检索查询和分析处理,POLARDB的读写分离架构,很好的支撑了这类场景。同时,POLARDB还能够在几分钟以内在线增加只读节点,进一步提升系统的吞吐处理能力,结合读写分离连接地址,自动进行请求的识别转发,通过自适应负载均衡处理,让集群的计算力能够发挥到最大,消除了计算瓶颈。 本文作者:桐碧2018阅读原文 本文为云栖社区原创内容,未经允许不得转载。

April 24, 2019 · 1 min · jiezi

主流微服务注册中心浅析和对比

开源产品受开发者热捧,是因为其代码透明、可以参与共建、有社区进行交流和学习,当然更重要的是开源产品的接入成本低。个人开发者或者中小型公司往往会将开源产品作为选型首选。 开发者通过阅读源代码,理解产品的功能设计和架构设计,同时也可以通过本地部署来测试性能,随之而来的是对各类开源产品的对比,用以选型。不过当前关于微服务注册中心的对比,大多聚焦在功能上的对比,对架构或者性能的深入探讨,比较少见。 另一方面,作为默默支持的产品,服务注册中心往往隐藏在服务框架背后。优秀的服务框架往往会支持多种配置中心,但是注册中心的选择依然与服务框架强关联,普遍的情况是一种服务框架会带一个默认的服务注册中心。这样虽然免去了用户在选型上的烦恼,但是单个注册中心的局限性,导致用户使用多个服务框架时,必须部署多套完全不同的注册中心,这些注册中心之间的数据协同是一个问题。 本文来自Nacos社区,作者是 Nacos PMC 朱鹏飞,作者力求公正和客观的去看待主流微服务注册中心的各个维度。本文不仅仅包含常见服务注册中心产品的对比,也试图从Nacos的经验和调研中总结并阐述服务注册中心产品设计上应该去遵循和考虑的要点,文章篇幅较长,若您有不同的看法,欢迎在文末留言,或到Nacos @GitHub 提issue。 前言服务发现是一个古老的话题,当应用开始脱离单机运行和访问时,服务发现就诞生了。目前的网络架构是每个主机都有一个独立的IP地址,那么服务发现基本上都是通过某种方式获取到服务所部署的IP地址。DNS协议是最早将一个网络名称翻译为网络IP的协议,在最初的架构选型中,DNS+LVS+Nginx基本可以满足所有的RESTful服务的发现,此时服务的IP列表通常配置在Nginx或者LVS。后来出现了RPC服务,服务的上下线更加频繁,人们开始寻求一种能够支持动态上下线并且推送IP列表变化的注册中心产品。 ZooKeeper是一款经典的服务注册中心产品(虽然它最初的定位并不在于此),在很长一段时间里,它是国人在提起RPC服务注册中心时心里想到的唯一选择,这很大程度上与Dubbo在中国的普及程度有关。Consul和Eureka都出现于2014年,Consul在设计上把很多分布式服务治理上要用到的功能都包含在内,可以支持服务注册、健康检查、配置管理、Service Mesh等。而Eureka则借着微服务概念的流行,与SpringCloud生态的深度结合,也获取了大量的用户。去年开源的Nacos,则携带着阿里巴巴大规模服务生产经验,试图在服务注册和配置管理这个市场上,提供给用户一个新的选择。 当市面上有多种相似功能的产品出现时,人们往往希望知道这些产品相比较的优劣。产品本身的定位会决定它包含了哪些功能,而产品架构的设计,则会影响产品的性能和可用性等。开源产品的一个优势是开发人员可以去阅读源代码,理解产品的功能设计和架构设计,同时也可以通过本地部署来测试性能,随之而来的是各种产品的对比文章。不过当前关于注册中心的对比,往往停留在表面的功能对比上,对架构或者性能并没有非常深入的探讨。 另一个现象是服务注册中心往往隐藏在服务框架背后,作为默默支持的产品。优秀的服务框架往往会支持多种配置中心,但是注册中心的选择依然强关联与服务框架,一种普遍的情况是一种服务框架会带一个默认的服务注册中心。这样虽然免去了用户在选型上的烦恼,但是单个注册中心的局限性,导致用户使用多个服务框架时,必须部署多套完全不同的注册中心,这些注册中心之间的数据协同也是一个问题。 本文是一篇来自Nacos项目组的文章,虽然是来自Nacos,我们依然力求公正和客观的去看待服务发现所有产品的各个维度。本文不仅仅包含常见服务注册中心产品的对比,还试图从我们的经验和调研中总结和阐述服务注册中心产品设计上应该去遵循和考虑的要点。 数据模型注册中心的核心数据是服务的名字和它对应的网络地址,当服务注册了多个实例时,我们需要对不健康的实例进行过滤或者针对实例的一些特征进行流量的分配,那么就需要在实例上存储一些例如健康状态、权重等属性。随着服务规模的扩大,渐渐的又需要在整个服务级别设定一些权限规则、以及对所有实例都生效的一些开关,于是在服务级别又会设立一些属性。再往后,我们又发现单个服务的实例又会有划分为多个子集的需求,例如一个服务是多机房部署的,那么可能需要对每个机房的实例做不同的配置,这样又需要在服务和实例之间再设定一个数据级别。 Zookeeper没有针对服务发现设计数据模型,它的数据是以一种更加抽象的树形K-V组织的,因此理论上可以存储任何语义的数据。而Eureka或者Consul都是做到了实例级别的数据扩展,这可以满足大部分的场景,不过无法满足大规模和多环境的服务数据存储。Nacos在经过内部多年生产经验后提炼出的数据模型,则是一种服务-集群-实例的三层模型。如上文所说,这样基本可以满足服务在所有场景下的数据存储和管理。 Nacos的数据模型虽然相对复杂,但是它并不强制你使用它里面的所有数据,在大多数场景下,你可以选择忽略这些数据属性,此时可以降维成和Eureka和Consul一样的数据模型。 另外一个需要考虑的是数据的隔离模型,作为一个共享服务型的组件,需要能够在多个用户或者业务方使用的情况下,保证数据的隔离和安全,这在稍微大一点的业务场景中非常常见。另一方面服务注册中心往往会支持云上部署,此时就要求服务注册中心的数据模型能够适配云上的通用模型。Zookeeper、Consul和Eureka在开源层面都没有很明确的针对服务隔离的模型,Nacos则在一开始就考虑到如何让用户能够以多种维度进行数据隔离,同时能够平滑的迁移到阿里云上对应的商业化产品。 Nacos提供了四层的数据逻辑隔离模型,用户账号对应的可能是一个企业或者独立的个体,这个数据一般情况下不会透传到服务注册中心。一个用户账号可以新建多个命名空间,每个命名空间对应一个客户端实例,这个命名空间对应的注册中心物理集群是可以根据规则进行路由的,这样可以让注册中心内部的升级和迁移对用户是无感知的,同时可以根据用户的级别,为用户提供不同服务级别的物理集群。再往下是服务分组和服务名组成的二维服务标识,可以满足接口级别的服务隔离。 Nacos 1.0.0介绍的另外一个新特性是:临时实例和持久化实例。在定义上区分临时实例和持久化实例的关键是健康检查的方式。临时实例使用客户端上报模式,而持久化实例使用服务端反向探测模式。临时实例需要能够自动摘除不健康实例,而且无需持久化存储实例,那么这种实例就适用于类Gossip的协议。右边的持久化实例使用服务端探测的健康检查方式,因为客户端不会上报心跳,那么自然就不能去自动摘除下线的实例。 在大中型的公司里,这两种类型的服务往往都有。一些基础的组件例如数据库、缓存等,这些往往不能上报心跳,这种类型的服务在注册时,就需要作为持久化实例注册。而上层的业务服务,例如微服务或者Dubbo服务,服务的Provider端支持添加汇报心跳的逻辑,此时就可以使用动态服务的注册方式。 数据一致性数据一致性是分布式系统永恒的话题,Paxos协议的艰深更让数据一致性成为程序员大牛们吹水的常见话题。不过从协议层面上看,一致性的选型已经很长时间没有新的成员加入了。目前来看基本可以归为两家:一种是基于Leader的非对等部署的单点写一致性,一种是对等部署的多写一致性。当我们选用服务注册中心的时候,并没有一种协议能够覆盖所有场景,例如当注册的服务节点不会定时发送心跳到注册中心时,强一致协议看起来是唯一的选择,因为无法通过心跳来进行数据的补偿注册,第一次注册就必须保证数据不会丢失。而当客户端会定时发送心跳来汇报健康状态时,第一次的注册的成功率并不是非常关键(当然也很关键,只是相对来说我们容忍数据的少量写失败),因为后续还可以通过心跳再把数据补偿上来,此时Paxos协议的单点瓶颈就会不太划算了,这也是Eureka为什么不采用Paxos协议而采用自定义的Renew机制的原因。 这两种数据一致性协议有各自的使用场景,对服务注册的需求不同,就会导致使用不同的协议。在这里可以发现,Zookeeper在Dubbo体系下表现出的行为,其实采用Eureka的Renew机制更加合适,因为Dubbo服务往Zookeeper注册的就是临时节点,需要定时发心跳到Zookeeper来续约节点,并允许服务下线时,将Zookeeper上相应的节点摘除。Zookeeper使用ZAB协议虽然保证了数据的强一致,但是它的机房容灾能力的缺乏,无法适应一些大型场景。 Nacos因为要支持多种服务类型的注册,并能够具有机房容灾、集群扩展等必不可少的能力,在1.0.0正式支持AP和CP两种一致性协议并存。1.0.0重构了数据的读写和同步逻辑,将与业务相关的CRUD与底层的一致性同步逻辑进行了分层隔离。然后将业务的读写(主要是写,因为读会直接使用业务层的缓存)抽象为Nacos定义的数据类型,调用一致性服务进行数据同步。在决定使用CP还是AP一致性时,使用一个代理,通过可控制的规则进行转发。 目前的一致性协议实现,一个是基于简化的Raft的CP一致性,一个是基于自研协议Distro的AP一致性。Raft协议不必多言,基于Leader进行写入,其CP也并不是严格的,只是能保证一半所见一致,以及数据的丢失概率较小。Distro协议则是参考了内部ConfigServer和开源Eureka,在不借助第三方存储的情况下,实现基本大同小异。Distro重点是做了一些逻辑的优化和性能的调优。 负载均衡负载均衡严格的来说,并不算是传统注册中心的功能。一般来说服务发现的完整流程应该是先从注册中心获取到服务的实例列表,然后再根据自身的需求,来选择其中的部分实例或者按照一定的流量分配机制来访问不同的服务提供者,因此注册中心本身一般不限定服务消费者的访问策略。Eureka、Zookeeper包括Consul,本身都没有去实现可配置及可扩展的负载均衡机制,Eureka的负载均衡是由ribbon来完成的,而Consul则是由Fabio做负载均衡。 在阿里巴巴集团内部,却是使用的相反的思路。服务消费者往往并不关心所访问的服务提供者的负载均衡,它们只关心以最高效和正确的访问服务提供者的服务。而服务提供者,则非常关注自身被访问的流量的调配,这其中的第一个原因是,阿里巴巴集团内部服务访问流量巨大,稍有不慎就会导致流量异常压垮服务提供者的服务。因此服务提供者需要能够完全掌控服务的流量调配,并可以动态调整。 服务端的负载均衡,给服务提供者更强的流量控制权,但是无法满足不同的消费者希望使用不同负载均衡策略的需求。而不同负载均衡策略的场景,确实是存在的。而客户端的负载均衡则提供了这种灵活性,并对用户扩展提供更加友好的支持。但是客户端负载均衡策略如果配置不当,可能会导致服务提供者出现热点,或者压根就拿不到任何服务提供者。 抛开负载均衡到底是在服务提供者实现还是在服务消费者实现,我们看到目前的负载均衡有基于权重、服务提供者负载、响应时间、标签等策略。其中Ribbon设计的客户端负载均衡机制,主要是选择合适现有的IRule、ServerListFilter等接口实现,或者自己继承这些接口,实现自己的过滤逻辑。这里Ribbon采用的是两步负载均衡,第一步是先过滤掉不会采用的服务提供者实例,第二步是在过滤后的服务提供者实例里,实施负载均衡策略。Ribbon内置的几种负载均衡策略功能还是比较强大的,同时又因为允许用户去扩展,这可以说是一种比较好的设计。 基于标签的负载均衡策略可以做到非常灵活,Kubernetes和Fabio都已经将标签运用到了对资源的过滤中,使用标签几乎可以实现任意比例和权重的服务流量调配。但是标签本身需要单独的存储以及读写功能,不管是放在注册中心本身或者对接第三方的CMDB。 在Nacos 0.7.0版本中,我们除了提供基于健康检查和权重的负载均衡方式外,还新提供了基于第三方CMDB的标签负载均衡器,具体可以参考CMDB功能介绍文章。使用基于标签的负载均衡器,目前可以实现同标签优先访问的流量调度策略,实际的应用场景中,可以用来实现服务的就近访问,当您的服务部署在多个地域时,这非常有用。使用这个标签负载均衡器,可以支持非常多的场景,这不是本文要详细介绍的。虽然目前Nacos里支持的标签表达式并不丰富,不过我们会逐步扩展它支持的语法。除此以外,Nacos定义了Selector,作为负载均衡的统一抽象。关于Selector,由于篇幅关系,我们会有单独的文章进行介绍。 理想的负载均衡实现应该是什么样的呢?不同的人会有不同的答案。Nacos试图做的是将服务端负载均衡与客户端负载均衡通过某种机制结合起来,提供用户扩展性,并给予用户充分的自主选择权和轻便的使用方式。负载均衡是一个很大的话题,当我们在关注注册中心提供的负载均衡策略时,需要注意该注册中心是否有我需要的负载均衡方式,使用方式是否复杂。如果没有,那么是否允许我方便的扩展来实现我需求的负载均衡策略。 健康检查Zookeeper和Eureka都实现了一种TTL的机制,就是如果客户端在一定时间内没有向注册中心发送心跳,则会将这个客户端摘除。Eureka做的更好的一点在于它允许在注册服务的时候,自定义检查自身状态的健康检查方法。这在服务实例能够保持心跳上报的场景下,是一种比较好的体验,在Dubbo和SpringCloud这两大体系内,也被培养成用户心智上的默认行为。Nacos也支持这种TTL机制,不过这与ConfigServer在阿里巴巴内部的机制又有一些区别。Nacos目前支持临时实例使用心跳上报方式维持活性,发送心跳的周期默认是5秒,Nacos服务端会在15秒没收到心跳后将实例设置为不健康,在30秒没收到心跳时将这个临时实例摘除。 不过正如前文所说,有一些服务无法上报心跳,但是可以提供一个检测接口,由外部去探测。这样的服务也是广泛存在的,而且以我们的经验,这些服务对服务发现和负载均衡的需求同样强烈。服务端健康检查最常见的方式是TCP端口探测和HTTP接口返回码探测,这两种探测方式因为其协议的通用性可以支持绝大多数的健康检查场景。在其他一些特殊的场景中,可能还需要执行特殊的接口才能判断服务是否可用。例如部署了数据库的主备,数据库的主备可能会在某些情况下切换,需要通过服务名对外提供访问,保证当前访问的库是主库。此时的健康检查接口,可能就是一个检查数据库是否是主库的MYSQL命令了。 客户端健康检查和服务端健康检查有一些不同的关注点。客户端健康检查主要关注客户端上报心跳的方式、服务端摘除不健康客户端的机制。而服务端健康检查,则关注探测客户端的方式、灵敏度及设置客户端健康状态的机制。从实现复杂性来说,服务端探测肯定是要更加复杂的,因为需要服务端根据注册服务配置的健康检查方式,去执行相应的接口,判断相应的返回结果,并做好重试机制和线程池的管理。这与客户端探测,只需要等待心跳,然后刷新TTL是不一样的。同时服务端健康检查无法摘除不健康实例,这意味着只要注册过的服务实例,如果不调用接口主动注销,这些服务实例都需要去维持健康检查的探测任务,而客户端则可以随时摘除不健康实例,减轻服务端的压力。 Nacos既支持客户端的健康检查,也支持服务端的健康检查,同一个服务可以切换健康检查模式。我们认为这种健康检查方式的多样性非常重要,这样可以支持各种类型的服务,让这些服务都可以使用到Nacos的负载均衡能力。Nacos下一步要做的是实现健康检查方式的用户扩展机制,不管是服务端探测还是客户端探测。这样可以支持用户传入一条业务语义的请求,然后由Nacos去执行,做到健康检查的定制。 性能与容量虽然大部分用户用到的性能不高,但是他们仍然希望选用的产品的性能越高越好。影响读写性能的因素很多:一致性协议、机器的配置、集群的规模、存量数据的规模、数据结构及读写逻辑的设计等等。在服务发现的场景中,我们认为读写性能都是非常关键的,但是并非性能越高就越好,因为追求性能往往需要其他方面做出牺牲。Zookeeper在写性能上似乎能达到上万的TPS,这得益于Zookeeper精巧的设计,不过这显然是因为有一系列的前提存在。首先Zookeeper的写逻辑就是进行K-V的写入,内部没有聚合;其次Zookeeper舍弃了服务发现的基本功能如健康检查、友好的查询接口,它在支持这些功能的时候,显然需要增加一些逻辑,甚至弃用现有的数据结构;最后,Paxos协议本身就限制了Zookeeper集群的规模,3、5个节点是不能应对大规模的服务订阅和查询的。 在对容量的评估时,不仅要针对企业现有的服务规模进行评估,也要对未来3到5年的扩展规模进行预测。阿里巴巴的中间件在内部支撑着集团百万级别服务实例,在容量上遇到的挑战可以说不会小于任何互联网公司。这个容量不仅仅意味着整体注册的实例数,也同时包含单个服务的实例数、整体的订阅者的数目以及查询的QPS等。Nacos在内部淘汰Zookeeper和Eureka的过程中,容量是一个非常重要的因素。 Zookeeper的容量,从存储节点数来说,可以达到百万级别。不过如上面所说,这并不代表容量的全部,当大量的实例上下线时,Zookeeper的表现并不稳定,同时在推送机制上的缺陷,会引起客户端的资源占用上升,从而性能急剧下降。 Eureka在服务实例规模在5000左右的时候,就已经出现服务不可用的问题,甚至在压测的过程中,如果并发的线程数过高,就会造成Eureka crash。不过如果服务规模在1000上下,几乎目前所有的注册中心都可以满足。毕竟我们看到Eureka作为SpringCloud的注册中心,在国内也没有看到很广泛的对于容量或者性能的问题报告。 Nacos在开源版本中,服务实例注册的支撑量约为100万,服务的数量可以达到10万以上。在实际的部署环境中,这个数字还会因为机器、网络的配置与JVM参数的不同,可能会有所差别。图9展示了Nacos在使用1.0.0版本进行压力测试后的结果总结,针对容量、并发、扩展性和延时等进行了测试和统计。 完整的测试报告可以参考Nacos官网:https://nacos.io/en-us/docs/nacos-naming-benchmark.htmlhttps://nacos.io/en-us/docs/nacos-config-benchmark.html 易用性易用性也是用户比较关注的一块内容。产品虽然可以在功能特性或者性能上做到非常先进,但是如果用户的使用成本极高,也会让用户望而却步。易用性包括多方面的工作,例如API和客户端的接入是否简单,文档是否齐全易懂,控制台界面是否完善等。对于开源产品来说,还有一块是社区是否活跃。在比较Nacos、Eureka和Zookeeper在易用性上的表现时,我们诚邀社区的用户进行全方位的反馈,因为毕竟在阿里巴巴集团内部,我们对Eureka、Zookeeper的使用场景是有限的。从我们使用的经验和调研来看,Zookeeper的易用性是比较差的,Zookeeper的客户端使用比较复杂,没有针对服务发现的模型设计以及相应的API封装,需要依赖方自己处理。对多语言的支持也不太好,同时没有比较好用的控制台进行运维管理。 Eureka和Nacos相比较Zookeeper而言,已经改善很多,这两个产品有针对服务注册与发现的客户端,也有基于SpringCloud体系的starter,帮助用户以非常低的成本无感知的做到服务注册与发现。同时还暴露标准的HTTP接口,支持多语言和跨平台访问。Eureka和Nacos都提供官方的控制台来查询服务注册情况。不过随着Eureka 2.0宣布停止开发,Eureka在针对用户使用上的优化后续应该不会再有比较大的投入,而Nacos目前依然在建设中,除了目前支持的易用性特性以外,后续还会继续增强控制台的能力,增加控制台登录和权限的管控,监控体系和Metrics的暴露,持续通过官网等渠道完善使用文档,多语言SDK的开发等。 从社区活跃度的角度来看,目前由于Zookeeper和Eureka的存量用户较多,很多教程以及问题排查都可以在社区搜索到,这方面新开源的Nacos还需要随着时间继续沉淀。 集群扩展性集群扩展性和集群容量以及读写性能关系紧密。当使用一个比较小的集群规模就可以支撑远高于现有数量的服务注册及访问时,集群的扩展能力暂时就不会那么重要。从协议的层面上来说,Zookeeper使用的ZAB协议,由于是单点写,在集群扩展性上不具备优势。Eureka在协议上来说理论上可以扩展到很大规模,因为都是点对点的数据同步,但是从我们对Eureka的运维经验来看,Eureka集群在扩容之后,性能上有很大问题。 集群扩展性的另一个方面是多地域部署和容灾的支持。当讲究集群的高可用和稳定性以及网络上的跨地域延迟要求能够在每个地域都部署集群的时候,我们现有的方案有多机房容灾、异地多活、多数据中心等。 首先是双机房容灾,基于Leader写的协议不做改造是无法支持的,这意味着Zookeeper不能在没有人工干预的情况下做到双机房容灾。在单机房断网情况下,使机房内服务可用并不难,难的是如何在断网恢复后做数据聚合,Zookeeper的单点写模式就会有断网恢复后的数据对账问题。Eureka的部署模式天然支持多机房容灾,因为Eureka采用的是纯临时实例的注册模式:不持久化、所有数据都可以通过客户端心跳上报进行补偿。上面说到,临时实例和持久化实例都有它的应用场景,为了能够兼容这两种场景,Nacos支持两种模式的部署,一种是和Eureka一样的AP协议的部署,这种模式只支持临时实例,可以完美替代当前的Zookeeper、Eureka,并支持机房容灾。另一种是支持持久化实例的CP模式,这种情况下不支持双机房容灾。 在谈到异地多活时,很巧的是,很多业务组件的异地多活正是依靠服务注册中心和配置中心来实现的,这其中包含流量的调度和集群的访问规则的修改等。机房容灾是异地多活的一部分,但是要让业务能够在访问服务注册中心时,动态调整访问的集群节点,这需要第三方的组件来做路由。异地多活往往是一个包含所有产品线的总体方案,很难说单个产品是否支持异地多活。 多数据中心其实也算是异地多活的一部分。从单个产品的维度上,Zookeeper和Eureka没有给出官方的多数据中心方案。Nacos基于阿里巴巴内部的使用经验,提供的解决方案是才有Nacos-Sync组件来做数据中心之间的数据同步,这意味着每个数据中心的Nacos集群都会有多个数据中心的全量数据。Nacos-Sync是Nacos生态组件里的重要一环,不仅会承担Nacos集群与Nacos集群之间的数据同步,也会承担Nacos集群与Eureka、Zookeeper、Kubernetes及Consul之间的数据同步。 ...

April 22, 2019 · 1 min · jiezi

开发跨平台app推荐React Native还是flutter?

嗯。。。这个问题十分不好回答啊(捋下鱼须)。闲鱼作为flutter领域的先驱者,以及fish_redux、flutter_boost等当红flutter库的作者,当然是欢迎广大的开发者多多使用flutter相关技术栈 逃~:)。咳咳,不过呢,我们还是正经得聊一下React Native(下面简称RN)和flutter之前的异同:0x00 简单介绍一下React NativeReact Native是Facebook开源的一款基于react思想、使用JS、能够给移动平台带来native般体验的框架,官网最新的版本是0.5.9。flutterflutter来自Google,上层使用dart语言构建跨平台应用,通过平台相关的embedded层接入到使用c++编写的engine层,再通过skia库直接与GPU进行交互。通过对dart代码的AOT编译,拥有优异的计算(CPU)、渲染(GPU)性能。官网最新的版本为1.2。0x01 跨平台性开发者们使用跨平台技术栈,首要的目的是为了能够省事儿,所以跨平台能力是首先要被衡量的指标。Build native mobile apps using JavaScript and React这意味着开发者可以复用庞大的JavaScript生态和优雅的react思想来书写RN的代码,给开发提供很多的便利性。从实现原理上来说,RN进行完排版之后会把最终的渲染交给native view,这种方式带来的是如native般的UI性能,但同时也给给平台一致性带来了一些问题。除开渲染层的不一致,在iOS和Android没有使用同一个JavaScript虚拟机也会带来一些暗坑。手势的处理上两个平台不好统一,RN官方也没有提供一个抹平差异的库,虽然开源社区有react-native-gesture-handler。Beautiful native apps in record timeflutter官方的口气很大,说自己是”空前“的。是不是”空前“,我们得来评估一下。编程语言层面,flutter使用dart语言构建应用,这门语言对大多数人来说应该是比较陌生,好在dart的语法并不复杂,与Java等强类型oop语言非常相似,还加入了函数式的特性,使用起来还是挺方便的。flutter提供类似React思想的响应性UI编程模型,让UI开发变得更加fancy。原理上来说,flutter在各个平台上使用统一的vm(dart vm),自带GDI(skia)。skia是一个已经发展多年成熟度相当高的2D图形库,也是Android系统和Chrome一直在使用的图形库。flutter从逻辑计算到渲染绘图,都是自己的,使得它在跨平台一致性上有良好的表现。dart提供的AOT特性也可以保证应用在线上有一个好的性能表现。多平台支持RN目前支持iOS和Android两个平台,另外有个非官方的ReactNativeX的项目旨在让RN运行到其他平台。flutter早期支持iOS和Android,desktop的支持目前尚不完善。近期flutter团队发布了Hummingbird,旨在让flutter编写的应用可以运行在浏览器端。从多平台支持的角度看,两边差距不大。相比RN,flutter在desktop的支持上有些优势,但目前都是不怎么可用状态。0x02 开发便利性工具链RN在打包发布方面有被前端广泛使用的webpack支持,官方自己提供了基于浏览器的debug工具,与前端同学管用的调试方式并无二致。flutter基于iOS和Android已有的打包工具添加了flutter产物打包功能,同样debug工具也由官方自己提供,除了刚发布的基于浏览器的调试工具外,flutter团队提供的调试工具可以直接在Android Studio或者VScode这类IDE上直接使用。调试便利性JS的调试方式已经很成熟了,这里不多做展开。flutter在debug阶段可以使用集成于IDE插件中的hot reload功能做到亚秒级的新代码加载速度,十分适合与设计师坐在一起结(ya)对(li)编(tiao)程(shi) :)。第三方库在RN上你可以使用JS的大部分库,平台相关的plugin也相对丰富。flutter在这方面稍显欠缺,库的数量上无法与JS生态相比较。flutter/plugins项目提供了大量的平台相关插件供开发者使用,倒也是满足了日常开发的需求,另外dart pubs上的公开库数量也日趋上升。在混合开发和大型app业务框架上,闲鱼技术开源的flutter_boost提供了与native混合开发的可能,而fish_redux使得大型app中的复杂页面的开发在flutter中变得更加容易。0x03 未来的发展开发者选择一个技术,都是压了”身家性命“在上面,谁也不想刚入门就发现这门技术即将被淘汰。RN是个很好的项目,在发布之初给移动开发带来了一阵旋风。但不得不说,Airbnb宣布放弃使用RN技术栈对于整个社区有不小的打击,而文章中对原因的阐述也相当有说服力。flutter在1.0发布之后趋于成熟,被钦定为Google Fuchsia系统的应用层框架。从团队2019 roadmap中可以看到,flutter当前重点在于完善一些现有功能上的细节与bugfix,另外对于广受期待的动态化特性,flutter团队也在开发code push功能。从flutter团队目前的方向和笔者在闲鱼开发中实际使用的flutter的感受来看,整体上flutter在框架层面目前已经基本上稳定。从桌面端跨平台框架发展的历程来看,Java GUI从最初使用peer(对等设计模式)的AWT,到基于Java图形绘制接口性能巨慢无比的Swing,再到公认性能最好目前应用最广泛的基于目标平台绘制接口的SWT,我们可以从中窥见一些历史规律。peer(对等设计模式),即AWT中的一个控件,对应目标平台(如Windows)上的一个控件(是不是看起来跟RN有一些相似),最终AWT被放弃是因为peer模式传输层级过多造成效率低下,GUI部分为了保证可移植性只能保留各个平台公共的接口。SWT与QT(另一个被广泛使用的桌面端跨平台GUI框架),牺牲了一部分可移植性(主要是因为直接调用了目标平台的图形绘制接口),带来了GUI的高性能。flutter也采用了类似技术栈,skia来抹平各个平台的绘制接口差异,并向上提供统一的图形接口。从这个角度来说,无疑flutter可能会是一个更有未来的跨平台框架。0x04 写在最后当然Facebook官方对于RN正在进行重构,包括把大部分逻辑移动到c++层来减少线程切换的开销提升性能等。选择一个框架需要考虑的实际情况比框架的优劣比较更加重要,比如你的项目大小、开发人员构成等,RN和flutter作为目前移动平台上炙手可热的框架,两者并不是孰优孰劣的对立关系。纸上得来终觉浅,如果你是个对新技术感兴趣,抑或是希望在移动平台上有所突破的开发者,何不尝试一下Google最新的成果咧?本文作者:闲鱼技术-海猪阅读原文本文为云栖社区原创内容,未经允许不得转载。

April 19, 2019 · 1 min · jiezi

为什么说流处理即未来?

作者|Stephan Ewen整理|秦江杰本文整理自 Flink 创始公司 Ververica 联合创始人兼 CTO - Stephan Ewen 在 Flink Forward China 2018 上的演讲《Stream Processing takes on Everything》。这个演讲主题看似比较激进:流处理解决所有问题。很多人对于 Flink 可能还停留在最初的认知,觉得 Flink 是一个流处理引擎,实际上 Flink 可以做很多其他的工作,比如批处理、应用程序。在这个演讲中,Stephan 首先会简单说明他对 Flink 功能的观点,然后深入介绍一个特定领域的应用和事件处理场景。这个场景乍看起来不是一个流处理的使用场景,但是在 Stephan 看来,它实际上就是一个很有趣的流处理使用场景。Flink社区专刊下载地址第一期:不仅仅是流计算第二期:重新定义计算上图对为什么流处理可以处理一切作出诠释,将数据看做流是一个自然而又十分强大的想法。大部分数据的产生过程都是随时间生成的流,比如一个 Petabyte 的数据不会凭空产生。这些数据通常都是一些事件的积累,比如支付、将商品放入购物车,网页浏览,传感器采样输出。基于数据是流的想法,我们对数据处理可以有相应的理解。比如将过去的历史数据看做是一个截止到某一时刻的有限的流,或是将一个实时处理应用看成是从某一个时刻开始处理未来到达的数据。可能在未来某个时刻它会停止,那么它就变成了处理从开始时刻到停止时刻的有限数据的批处理。当然,它也有可能一直运行下去,不断处理新到达的数据。这个对数据的重要理解方式非常强大,基于这一理解,Flink 可以支持整个数据处理范畴内的所有场景。最广为人知的 Flink 使用场景是流分析、连续处理(或者说渐进式处理),这些场景中 Flink 实时或者近实时的处理数据,或者采集之前提到的历史数据并且连续的对这些事件进行计算。晓伟在之前的演讲中提到一个非常好的例子来说明怎么样通过对 Flink 进行一些优化,进而可以针对有限数据集做一些特别的处理,这使得 Flink 能够很好的支持批处理的场景,从性能上来说能够与最先进的批处理引擎相媲美。而在这根轴的另一头,是我今天的演讲将要说明的场景 – 事件驱动的应用。这类应用普遍存在于任何服务或者微服务的架构中。这类应用接收各类事件(可能是 RPC 调用、HTTP 请求),并且对这些事件作出一些响应,比如把商品放进购物车,或者加入社交网络中的某个群组。在我进一步展开今天的演讲之前,我想先对社区在 Flink 的传统领域(实时分析、连续处理)近期所做的工作做一个介绍。Flink 1.7 在 2018 年 11 月 30 日已经发布。在 Flink 1.7 中为典型的流处理场景加入了一些非常有趣的功能。比如我个人非常感兴趣的在流式 SQL 中带时间版本的 Join。一个基本想法是有两个不同的流,其中一个流被定义为随时间变化的参照表,另一个是与参照表进行 Join 的事件流。比如事件流是一个订单流,参照表是不断被更新的汇率,而每个订单需要使用最新的汇率来进行换算,并将换算的结果输出到结果表。这个例子在标准的 SQL 当中实际上并不容易表达,但在我们对 Streaming SQL 做了一点小的扩展以后,这个逻辑表达变得非常简单,我们发现这样的表达有非常多的应用场景。另一个在流处理领域十分强大的新功能是将复杂事件处理(CEP)和 SQL 相结合。CEP 应用观察事件模式。比如某个 CEP 应用观察股市,当有两个上涨后紧跟一个下跌时,这个应用可能做些交易。再比如一个观察温度计的应用,当它发现有温度计在两个超过 90 摄氏度的读数之后的两分钟里没有任何操作,可能会进行一些操作。与 SQL 的结合使这类逻辑的表达也变得非常简单。第三个 Flink 1.7 中做了很多工作的功能是 Schema 升级。这个功能和基于流的应用紧密相关。就像你可以对数据库进行数据 Schema 升级一样,你可以修改 Flink 表中列的类型或者重新写一个列。另外我想简单介绍的是流处理技术不仅仅是简单对数据进行计算,这还包括了很多与外部系统进行事务交互。流处理引擎需要在采用不同协议的系统之间以事务的方式移动数据,并保证计算过程和数据的一致性。这一部分功能也是在 Flink 1.7 中得到了增强。以上我对 Flink 1.7 的新功能向大家做了简单总结。下面让我们来看看今天我演讲的主要部分,也就是利用 Flink 来搭建应用和服务。我将说明为什么流处理是一个搭建应用和服务或者微服务的有趣技术。我将从左边这个高度简化的图说起,我们一会儿将聊一些其中的细节。首先我们来看一个理解应用简单的视角。如左图所示,一个应用可以是一个 Container,一个 Spring 应用,或者 Java 应用、Ruby 应用,等等。这个应用从诸如 RPC,HTTP 等渠道接收请求,然后依据请求进行数据库变更。这个应用也可能调用另一个微服务并进行下一步的处理。我们可以非常自然的想到进入到应用的这些请求可以看做是个事件组成的序列,所以我们可以把它们看做是事件流。可能这些事件被缓存在消息队列中,而应用会从消息队列中消费这些事件进行处理,当应用需要响应一个请求时,它将结果输出到另一个消息队列,而请求发送方可以从这个消息队列中消费得到所发送请求的响应。在这张图中我们已经可以看到一些有趣的不同。第一个不同是在这张图中应用和数据库不再是分开的两个实体,而是被一个有状态的流处理应用所代替。所以在流处理应用的架构中,不再有应用和数据库的连接了,它们被放到了一起。这个做法有利有弊,但其中有些好处是非常重要的。首先是性能上的好处是明显的,因为应用不再需要和数据库进行交互,处理可以基于内存中的变量进行。其次这种做法有很好并且很简单的一致性。这张图被简化了很多,实际上我们通常会有很多个应用,而不是一个被隔离的应用,很多情况下你的应用会更符合这张图。系统中有个接收请求的接口,然后请求被发送到第一个应用,可能会再被发到另一个应用,然后得到相应。在图中有些应用会消费中间结果的流。这张图已经展示了为什么流处理是更适合比较复杂的微服务场景的技术。因为很多时候系统中不会有一个直接接收用户请求并直接响应的服务,通常来说一个微服务需要跟其他微服务通信。这正如在流处理的架构中不同应用在创建输出流,同时基于衍生出的流再创建并输出新的流。到目前为止,我们看到的内容多少还比较直观。而对基于流处理技术的微服务架构而言,人们最常问的一个问题是如何保证事务性?如果系统中使用的是数据库,通常来说都会有非常成熟复杂的数据校验和事务模型。这也是数据库在过去许多年中十分成功的原因。开始一个事务,对数据做一些操作,提交或者撤销一个事务。这个机制使得数据完整性得到了保证(一致性,持久性等等)。那么在流处理中我们怎么做到同样的事情呢?作为一个优秀的流处理引擎,Flink 支持了恰好一次语义,保证了每个事件只会被处理一遍。但是这依然对某些操作有限制,这也成为了使用流处理应用的一个障碍。我们通过一个非常简单流处理应用例子来看我们可以做一些什么扩展来解决这个问题。我们会看到,解决办法其实出奇的简单。让我们以这个教科书式的事务为例子来看一下事务性应用的过程。这个系统维护了账户和其中存款余额的信息。这样的信息可能是银行或者在线支付系统的场景中用到的。假设我们想要处理类似下面的事务:如果账户 A 中的余额大于 100,那么从账户 A 中转账 50 元到账户 B。这是个非常简单的两个账户之间进行转账的例子。数据库对于这样的事务已经有了一个核心的范式,也就是原子性,一致性,隔离性和持久性(ACID)。这是能够让用户放心使用事务的几个基本保证。有了他们,用户不用担心钱在转账过程中会丢失或者其他问题。让我们用这个例子来放到流处理应用中,来让流处理应用也能提供和数据相同的 ACID 支持:原子性要求一个转账要不就完全完成,也就是说转账金额从一个账户减少,并增加到另一个账户,要不就两个账户的余额都没有变化。而不会只有一个账户余额改变。否则的话钱就会凭空减少或者凭空增加。一致性和隔离性是说如果有很多用户同时想要进行转账,那么这些转账行为之间应该互不干扰,每个转账行为应该被独立的完成,并且完成后每个账户的余额应该是正确的。也就是说如果两个用户同时操作同一个账户,系统不应该出错。持久性指的是如果一个操作已经完成,那么这个操作的结果会被妥善的保存而不会丢失。我们假设持久性已经被满足。一个流处理器有状态,这个状态会被 checkpoint,所以流处理器的状态是可恢复的。也就是说只要我们完成了一个修改,并且这个修改被 checkpoint 了,那么这个修改就是持久化的。让我们来看看另外三个例子。设想一下,如果我们用流处理应用来实现这样一个转账系统会发生什么。我们先把问题简化一些,假设转账不需要有条件,仅仅是将 50 元从账户 A 转到账户,也就是说账户 A 的余额减少 50 元而账户 B 的余额增加 50 元。我们的系统是一个分布式的并行系统,而不是一个单机系统。简单起见我们假设系统中只有两台机器,这两台机器可以是不同的物理机或者是在 YARN 或者 Kubernetes 上不同的容器。总之它们是两个不同的流处理器实例,数据分布在这两个流处理器上。我们假设账户 A 的数据由其中一台机器维护,而账户 B 的数据有另一台机器维护。现在我们要做个转账,将 50 元从账户 A 转移到账户 B,我们把这个请求放进队列中,然后这个转账请求被分解为对账户 A 和 B 分别进行操作,并且根据键将这两个操作路由到维护账户 A 和维护账户 B 的这两台机器上,这两台机器分别根据要求对账户 A 和账户 B 的余额进行改动。这并不是事务操作,而只是两个独立无意义的改动。一旦我们将转账的请求改的稍微复杂一些就会发现问题。下面我们假设转账是有条件的,我们只想在账户 A 的余额足够的情况下才进行转账,这样就已经有些不太对了。如果我们还是像之前那样操作,将这个转账请求分别发送给维护账户 A 和 B 的两台机器,如果 A 没有足够的余额,那么 A 的余额不会发生变化,而 B 的余额可能已经被改动了。我们就违反了一致性的要求。我们看到我们需要首先以某种方式统一做出是否需要更改余额的决定,如果这个统一的决定中余额需要被修改,我们再进行修改余额的操作。所以我们先给维护 A 的余额的机器发送一个请求,让它查看 A 的余额。我们也可以对 B 做同样的事情,但是这个例子里面我们不关心 B 的余额。然后我们把所有这样的条件检查的请求汇总起来去检验条件是否满足。因为 Flink 这样的流处理器支持迭代,如果满足转账条件,我们可以把这个余额改动的操作放进迭代的反馈流当中来告诉对应的节点来进行余额修改。反之如果条件不满足,那么余额改动的操作将不会被放进反馈流。这个例子里面,通过这种方式我们可以正确的进行转账操作。从某种角度上来说我们实现了原子性,基于一个条件我们可以进行全部的余额修改,或者不进行任何余额修改。这部分依然还是比较直观的,更大的困难是在于如何做到并发请求的隔离性。假设我们的系统没有变,但是系统中有多个并发的请求。我们在之前的演讲中已经知道,这样的并发可能达到每秒钟几十亿条。如图,我们的系统可能从两个流中同时接受请求。如果这两个请求同时到达,我们像之前那样将每个请求拆分成多个请求,首先检查余额条件,然后进行余额操作。然而我们发现这会带来问题。管理账户 A 的机器会首先检查 A 的余额是否大于 50,然后又会检查 A 的余额是否大于 100,因为两个条件都满足,所以两笔转账操作都会进行,但实际上账户 A 上的余额可能无法同时完成两笔转账,而只能完成 50 元或者 100 元的转账中的一笔。这里我们需要进一步思考怎么样来处理并发的请求,我们不能只是简单地并发处理请求,这会违反事务的保证。从某种角度来说,这是整个数据库事务的核心。数据库的专家们花了一些时间提供了不同解决方案,有的方案比较简单,有的则很复杂。但所有的方案都不是那么容易,尤其是在分布式系统当中。在流处理中怎么解决这个问题呢?直觉上讲,如果我们能够让所有的事务都按照顺序依次发生,那么问题就解决了,这也被成为可序列化的特性。但是我们当然不希望所有的请求都被依次顺序处理,这与我们使用分布式系统的初衷相违背。所以我们需要保证这些请求最后的产生的影响看起来是按照顺序发生的,也就是一个请求产生的影响是基于前一个请求产生影响的基础之上的。换句话说也就是一个事务的修改需要在前一个事务的所有修改都完成后才能进行。这种希望一件事在另一件事之后发生的要求看起来很熟悉,这似乎是我们以前在流处理中曾经遇到过的问题。是的,这听上去像是事件时间。用高度简化的方式来解释,如果所有的请求都在不同的事件时间产生,即使由于种种原因他们到达处理器的时间是乱序的,流处理器依然会根据他们的事件时间来对他们进行处理。流处理器会使得所有的事件的影响看上去都是按顺序发生的。按事件时间处理是 Flink 已经支持的功能。那么详细说来,我们到底怎么解决这个一致性问题呢?假设我们有并行的请求输入并行的事务请求,这些请求读取某些表中的记录,然后修改某些表中的记录。我们首先需要做的是把这些事务请求根据事件时间顺序摆放。这些请求的事务时间不能够相同,但是他们之间的时间也需要足够接近,这是因为在事件时间的处理过程中会引入一定的延迟,我们需要保证所处理的事件时间在向前推进。因此第一步是定义事务执行的顺序,也就是说需要有一个聪明的算法来为每个事务制定事件时间。在图上,假设这三个事务的事件时间分别是 T+2, T 和 T+1。那么第二个事务的影响需要在第一和第三个事务之前。不同的事务所做的修改是不同的,每个事务都会产生不同的操作请求来修改状态。我们现在需要将对访问每个行和状态的事件进行排序,保证他们的访问是符合事件时间顺序的。这也意味着那些相互之间没有关系的事务之间自然也没有了任何影响。比如这里的第三个事务请求,它与前两个事务之间没有访问共同的状态,所以它的事件时间排序与前两个事务也相互独立。而当前两个事务之间的操作的到达顺序与事件时间不符时,Flink 则会依据它们的事件时间进行排序后再处理。必须承认,这样说还是进行了一些简化,我们还需要做一些事情来保证高效执行,但是总体原则上来说,这就是全部的设计。除此之外我们并不需要更多其他东西。为了实现这个设计,我们引入了一种聪明的分布式事件时间分配机制。这里的事件时间是逻辑时间,它并不需要有什么现实意义,比如它不需要是真实的时钟。使用 Flink 的乱序处理能力,并且使用 Flink 迭代计算的功能来进行某些前提条件的检查。这些就是我们构建一个支持事务的流处理器的要素。我们实际上已经完成了这个工作,称之为流式账簿(Streaming Ledger),这是个在 Apache Flink 上很小的库。它基于流处理器做到了满足 ACID 的多键事务性操作。我相信这是个非常有趣的进化。流处理器一开始基本上没有任何保障,然后类似 Storm 的系统增加了至少一次的保证。但显然至少一次依然不够好。然后我们看到了恰好一次的语义,这是一个大的进步,但这只是对于单行操作的恰好一次语义,这与键值库很类似。而支持多行恰好一次或者多行事务操作将流处理器提升到了一个可以解决传统意义上关系型数据库所应用场景的阶段。Streaming Ledger 的实现方式是允许用户定义一些表和对这些表进行修改的函数。Streaming Ledger 会运行这些函数和表,所有的这些一起编译成一个 Apache Flink 的有向无环图(DAG)。Streaming Ledger 会注入所有事务时间分配的逻辑,以此来保证所有事务的一致性。搭建这样一个库并不难,难的是让它高性能的运行。让我们来看看它的性能。这些性能测试是几个月之前的,我们并没有做什么特别的优化,我们只是想看看一些最简单的方法能够有什么样的性能表现。而实际性能表现看起来相当不错。如果你看这些性能条形成的阶梯跨度,随着流处理器数量的增长,性能的增长相当线性。在事务设计中,没有任何协同或者锁参与其中。这只是流处理,将事件流推入系统,缓存一小段时间来做一些乱序处理,然后做一些本地状态更新。在这个方案中,没有什么特别代价高昂的操作。在图中性能增长似乎超过了线性,我想这主要是因为 JAVA 的 JVM 当中 GC 的工作原因导致的。在 32 个节点的情况下我们每秒可以处理大约两百万个事务。为了与数据库性能测试进行对比,通常当你看数据库的性能测试时,你会看到类似读写操作比的说明,比如 10% 的更新操作。而我们的测试使用的是 100% 的更新操作,而每个写操作至少更新在不同分区上的 4 行数据,我们的表的大小大约是两亿行。即便没有任何优化,这个方案的性能也非常不错。另一个在事务性能中有趣的问题是当更新的操作对象是一个比较小的集合时的性能。如果事务之间没有冲突,并发的事务处理是一个容易的事情。如果所有的事务都独立进行而互不干扰,那这个不是什么难题,任何系统应该都能很好的解决这样的问题。当所有的事务都开始操作同一些行时,事情开始变得更有趣了,你需要隔离不同的修改来保证一致性。所以我们开始比较一个只读的程序、一个又读又写但是没有写冲突的程序和一个又读又写并有中等程度写冲突的程序这三者之间的性能。你可以看到性能表现相当稳定。这就像是一个乐观的并发冲突控制,表现很不错。那如果我们真的想要针对这类系统的阿喀琉斯之踵进行考验,也就是反复的更新同一个小集合中的键。在传统数据库中,这种情况下可能会出现反复重试,反复失败再重试,这是一种我们总想避免的糟糕情况。是的,我们的确需要付出性能代价,这很自然,因为如果你的表中有几行数据每个人都想更新,那么你的系统就失去了并发性,这本身就是个问题。但是这种情况下,系统并没崩溃,它仍然在稳定的处理请求,虽然失去了一些并发性,但是请求依然能够被处理。这是因为我们没有冲突重试的机制,你可以认为我们有一个基于乱序处理天然的冲突避免的机制,这是一种非常稳定和强大的技术。我们还尝试了在跨地域分布的情况下的性能表现。比如我们在美国、巴西,欧洲,日本和澳大利亚各设置了一个 Flink 集群。也就是说我们有个全球分布的系统。如果你在使用一个关系型数据库,那么你会付出相当高昂的性能代价,因为通信的延迟变得相当高。跨大洲的信息交互比在同一个数据中心甚至同一个机架上的信息交互要产生大得多的延迟。但是有趣的是,流处理的方式对延迟并不是十分敏感,延迟对性能有所影响,但是相比其它很多方案,延迟对流处理的影响要小得多。所以,在这样的全球分布式环境中执行分布式程序,的确会有更差的性能,部分原因也是因为跨大洲的通信带宽不如统一数据中心里的带宽,但是性能表现依然不差。实际上,你可以拿它当做一个跨地域的数据库,同时仍然能够在一个大概 10 个节点的集群上获得每秒几十万条事务的处理能力。在这个测试中我们只用了 10 个节点,每个大洲两个节点。所以 10 个节点可以带来全球分布的每秒 20 万事务的处理能力。我认为这是很有趣的结果,这是因为这个方案对延迟并不敏感。我已经说了很多利用流处理来实现事务性的应用。可能听起来这是个很自然的想法,从某种角度上来说的确是这样。但是它的确需要一些很复杂的机制来作为支撑。它需要一个连续处理而非微批处理的能力,需要能够做迭代,需要复杂的基于事件时间处理乱序处理。为了更好地性能,它需要灵活的状态抽象和异步 checkpoint 机制。这些是真正困难的事情。这些不是由 Ledger Streaming 库实现的,而是 Apache Flink 实现的,所以即使对这类事务性的应用而言,Apache Flink 也是真正的中流砥柱。至此,我们可以说流处理不仅仅支持连续处理、流式分析、批处理或者事件驱动的处理,你也可以用它做事务性的处理。当然,前提是你有一个足够强大的流处理引擎。这就是我演讲的全部内容。本文作者:apache_flink阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 18, 2019 · 2 min · jiezi

Node.js 应用故障排查手册 —— 类死循环导致进程阻塞

楔子在实践篇一中我们看到了两个表象都是和 CPU 相关的生产问题,它们基本也是我们在线上可能遇到的这一类问题的典型案例,而实际上这两个案例也存在一个共同点:我们可以通过 Node.js 性能平台 导出进程对应的 CPU Profile 信息来进行分析定位问题,但是实际在线上的一些极端情况下,我们遇到的故障是没有办法通过轻量的 V8 引擎暴露的 CPU Profile 接口(仅部分定制的 AliNode runtime 版本支持,详见下文)来获取足够的进程状态信息进行分析的,此时我们又回到了束手无策的状态。本章节将从一个生产环境下 Node.js 应用出现进程级别阻塞导致的不再提供服务的问题场景来给大家展示下如何处理这类相对极端的应用故障。本书首发在 Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区会同步更新。最小化复现代码这个例子稍微有些特殊,我们首先给出生产案例的最小化复现代码,有兴趣的同学可以亲自运行一番,这样结合下文的此类问题的排查过程,能更加清晰的看到我们面对这样的问题时的排查思路,问题最小代码如下,基于 Egg.js :‘use strict’;const Controller = require(’egg’).Controller;class RegexpController extends Controller { async long() { const { ctx } = this; // str 模拟用户输入的问题字符串 let str = ‘<br/> ’ + ’ 早餐后自由活动,于指定时间集合自行办理退房手续。’; str += ‘<br/> <br/>’ + ’ <br/> ’ + ’ <br/>’; str += ’ <br/>’ + ’ ’ + ’ ’ + ’ <br/>’; str += ’ <br/> <br/>’; str += ’ ’ + ’ ’ + ’ 根据船班时间,自行前往暹粒机场,返回中国。<br/>’; str += ‘如需送机服务,需增加280/每单。<br/>’; const r = str.replace(/(^(\s*?<br[\s/]?>*?)+|(\s?<br[\s/]?>\s?)+?$)/igm, ‘’); ctx.body = r; }}module.exports = RegexpController;问题应用状态其实这个例子对应的问题场景可能很多 Node.js 开发者都遇到过,它非常有意思,我们首先来看下出现这类故障时我们的 Node.js 应用的状态。当我们收到在平台配置的 CPU 告警信息后,登录性能平台进入对应的告警应用找到出问题的 CPU 非常高的进程:然后点击 数据趋势 按钮查看此进程当前的状态信息:可以看到进程的 CPU 使用率曲线一直处于近乎 100% 的状态,此时进程不再响应其余的请求,而且我们通过跳板机进入生产环境又可以看到进程其实是存活的,并没有挂掉,此时基本上可以判断:此 Node.js 进程因为在执行某个同步函数处于阻塞状态,且一直卡在此同步函数的执行上。Node.js 的设计运行模式就是单主线程,并发靠的是底层实现的一整套异步 I/O 和事件循环的调度。简单的说,具体到事件循环中的某一次,如果我们在执行需要很长时间的同步函数(比如需要循环执行很久才能跳出的 while 循环),那么整个事件循环都会阻塞在这里等待其结束后才能进入下一次,这就是不推荐大家在非初始化的逻辑中使用诸如 fs.readFileSync 等同步方法的原因。排查方法这样的问题其实非常难以排查,原因在于我们没办法知道什么样的用户输入造成了这样的阻塞,所以本地几乎无法复现问题。幸运的是,性能平台目前有不止一种解决办法处理这种类死循环的问题,我们来详细看下。I. CPU Profile这个分析方法可以说是我们的老朋友了,因为类死循环的问题本质上也是 CPU 高的问题,因此我们只要对问题进程抓取 CPU Profile,就能看到当前卡在哪个函数了。需要注意的是,进程假死状态下是无法直接使用 V8 引擎提供的抓取 CPU Profile 文件的接口,因此工具篇章节的 正确打开 Chrome devtools 一节中提到的 v8-profiler 这样的第三方模块是无法正常工作的。不过定制过的 AliNode runtime 采用了一定的方法规避了这个问题,然而遗憾的是依旧并不是所有的 AliNode runtime 版本都支持在类死循环状态下抓取 CPU Profile,这里实际上对大家使用的 Runtime 版本有要求:AliNode V3 版本需要 >= v3.11.4AliNode V4 版本需要 >= v4.2.1AliNode V1 和 V2 版本不支持如果你的线上 AliNode runtime 版本恰好符合需求,那么可以按照前面 Node.js 性能平台使用指南 提到的那样,对问题进程抓取 3 分钟的 CPU Profile,并且使用 AliNode 定制的火焰图分析:这里可以看到,抓取到的问题进程 3 分钟的 CPU 全部耗费在 long 函数里面的 replace 方法上,这和我们提供的最小化复现代码一致,因此可以判断 long 函数内的正则存在问题进行修复。II. 诊断报告诊断报告也是 AliNode 定制的一项导出更多更详细的 Node.js 进程当前状态的能力,导出的信息也包含当前的 JavaScript 代码执行栈以及一些其它进程与系统信息。它与 CPU Profile 的区别主要在两个地方:诊断报告主要针对此刻进程状态的导出,CPU Profile 则是一段时间内的 JavaScript 代码执行状态诊断报告除了此刻 JavaScript 调用栈信息,还包含了 Native C/C++ 栈信息、Libuv 句柄和部分操作系统信息当我们的进程处于假死状态时,显然不管是一段时间内还是此时此刻的 JavaScript 执行状况,必然都是卡在我们代码中的某个函数上,因此我们可以使用诊断报告来处理这样的问题,当然诊断报告功能同样也对 AliNode runtime 版本有所要求:AliNode V2 版本需要 >= v2.5.2AliNode V3 版本需要 >= v3.11.8AliNode V4 版本需要 >= v4.3.0AliNode V1 版本不支持且要求:Agenthub/Egg-alinode 依赖的 Commandx 版本 >= v1.5.3如果你使用的 AliNode runtime 版本符合要求,即可进入平台应用对应的实例信息页面,选中问题进程:然后点击 诊断报告 即可生成此刻问题进程的状态信息报告:诊断报告虽然包含了很多的进程和系统信息,但是其本身是一个相对轻量的操作,故而很快就会结束,此时继续点击 转储 按钮将生成的诊断报告上传至云端以供在线分析展示:继续点击 分析 按钮查看 AliNode 定制的分析功能,展示结果如下:结果页面上面的概览信息比较简单,我们来看下 JavaScript 栈 页面的内容,这里显然也告诉我们当前的 JS 函数卡在 long 方法里面,并且比 CPU Profile 更加详细的是还带上了具体阻塞在 long 方法的哪一行,对比我们提供给大家的最小复现代码其实就是执行 str.replace 这一行,也就是问题的正则匹配操作所在的地方。III. 核心转储分析其实很多朋友看到这里会有疑惑:既然 CPU Profile 分析和诊断报告已经能够找到问题所在了,为什么我们还要继续介绍相对比较重的核心转储分析功能呢?其实道理也很简单,不管是类死循环状态下的 CPU Profile 抓取还是诊断报告功能的使用,都对问题进程的 AliNode runtime 版本有所要求,而且更重要的是,这两种方法我们都只能获取到问题正则的代码位置,但是我们无法知道什么样的用户输入在执行这样的正则时会触发进程阻塞的问题,这会给我们分析和给出针对性的处理造成困扰。因此,这里最后给大家介绍对 AliNode runtime 版本没有任何要求,且能拿到更精准信息的核心转储分析功能。首先按照预备章节的核心转储一节中提到的 手动生成 Core dump 文件的方法,我们对问题进程进行 sudo gcore <pid> 的方式获取到核心转储文件,然后在平台的详情页面,将鼠标移动到左边 Tab 栏目的 文件 按钮上,可以看到 Coredump 文件 的按钮:点击后可以进入 Core dump 文件列表页,然后点击上方的 上传 按钮进行核心转储文件的上传操作:这里需要注意的是,请将 Core dump 文件以 .core 结尾重命名,而对应的 Node 可执行文件以 .node 结尾重命名,推荐的命名方式为 <os info>-<alinode/node>-<version>.node,方便以后回顾,比如 centos7-alinode-v4.7.2.node 这种。最后 Core dump 文件和 Node 可执行文件之间必须是 一一对应 的关系。这里一一对应指的是:这份 Core dump 文件必须是由这个 Node 可执行文件启动的进程生成的,如果这两者没有一一对应,分析结果往往是无效信息。因为 Core dump 文件一般来说都比较大,所以上传会比较慢,耐心等待至上传完毕后,我们就可以使用 AliNode 定制的核心转储文件分析功能进行分析了,点击 分析 按钮即可:此时我们在新打开的分析结果页面可以看到如下的分析结果展示信息:这个页面的各项含义在工具篇的 Node.js 性能平台使用指南的 最佳实践——核心转储分析 一节已经解释过,这里不再赘述,这里直接展开 JavaScript 栈信息:这里可以看到得到的结论和前面的 CPU Profile 分析以及诊断报告分析一致,都能定位到提供的最小复现代码中的 long 方法中的异常正则匹配,但是核心转储文件分析比前面两者多了导致当前 Node.js 进程产生问题的异常字符串: "<br/> 早餐后自由活动,于指定时间集合自行办理退房手续。<br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> 根据船班时间,自行前往暹粒机场,返回中国。<br/>如需送机服务,需增加280/每单。<br/>" ,有了这个触发正则执行异常的问题字符串,我们无论是构造本地复现样例还是进一步分析都有了重要的信息依靠。分析问题上一节中我们采用了 Node.js 性能平台提供的三种不同的方式分析定位到了线上应用处于假死状态的原因,这里来简单的解释下为什么字符串的正则匹配会造成类死循环的状态,它实际上异常的用户输入触发了 正则表达式灾难性的回溯,会导致执行时间要耗费几年甚至几十年,显然不管是那种情况,单主工作线程的模型会导致我们的 Node.js 应用处于假死状态,即进程依旧存活,但是却不再处理新的请求。关于正则回溯的原因有兴趣的同学可以参见 小心别落入正则回溯陷阱 一文。结尾其实这类正则回溯引发的进程级别阻塞问题,本质上都是由于不可控的用户输入引发的,而 Node.js 应用又往往作为 Web 应用直接面向一线客户,无时不刻地处理千奇百怪的用户请求,因此更容易触发这样的问题。相似的问题其实还有一些代码逻辑中诸如 while 循环的跳出条件在一些情况下失效,导致 Node.js 应用阻塞在循环中。之前我们就算知道是进程阻塞也难以方便的定位到具体的问题代码以及产生问题的输入,现在借助于 Node.js 性能平台 提供的核心转储分析能力,相信大家可以比较容易地来解决这样的问题。本文作者:奕钧阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 15, 2019 · 2 min · jiezi

Node.js 应用故障排查手册 —— 大纲与常规问题指标简介

楔子你是否想要尝试进行 Node.js 应用开发但是又总听人说它不安全、稳定性差,想在公司推广扩张大前端的能力范畴和影响又说服不了技术领导。JavaScript 发展到今天,早已脱离原本浏览器的战场,借助于 Node.js 的诞生将其触角伸到了服务端、PC 跨平台客户端方案等各个领域,但是与此同时,JS Runtime 对于绝大部分的开发者来说又一如既往的处于黑盒状态——开发者无法感知其运行状态,出现一些性能、内存问题时也没有很好的工具链进行更深入的支持。本书将在基于 Node.js 性能平台 的基础上,从多个大家开发上线过程中可能遇到的疑难杂症的视角,观察如何去发现、定位和解决这些问题,帮助读者构建对 Node.js 这门语言的更多信心。因为本书将属于 Node.js 开发进阶的内容,因此我们希望本书的读者具备以下的基本技能:常规的 Node.js 应用开发的能力常规的服务器性能指标参数的理解,比如 CPU、Memory、Load、文件打开数等常见的数据库、缓存等操作负载均衡、多进程模型如果使用容器,容器的基本知识,资源管理等本书首发在 Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区会同步更新。常规排查的指标当我们第一次遇到线上异常时,很多人会感觉无从下手。本节作为预备篇,将从服务器异常时常见的排查指标开始,帮助大家建立一个更加直观的问题处理体系。毕竟如果我们面对线上异常时,如果连系统哪里有问题都不知道,那么后续的借助 Node.js 性能平台 更深入定位问题代码就更加无从谈起了。错误日志当我们的应用出现问题时,首先需要去查看我们应用的错误日志,观察在这段时间内是不是有错误在一直抛出,导致了我们的服务不稳定。这一块的信息显然是因各个应用而异的,当我们的项目比较大(Ecs/Docker 节点比较多)的时候,就需要对错误日志的进行统一的采集收集来保证出问题时的快速定位。一个比较简单的统一日志平台可以设计如下:其中的采集服务器和 Agent 上报之间一般会采用消息队列(Kafka)来作为缓冲区减轻双方的负载,ELK 就是一个比较成熟的日志服务。有了统一的日志平台后,当我们的应用出现问题时,首先应该去日志平台上查看当前的错误日志信息,特别是对于那些在 频繁出现 的错误日志应当引起警惕,需要去仔细地结合产生错误的代码段进行回溯确认是否是造成当前服务不稳定的元凶,Node.js 性能平台 也实现了一个简单的错误日志回溯 + 告警的系统,本书第二部分会更详细说明。系统指标如果在上述的错误日中没有看到可疑的信息(实际上错误日志以及本节的系统指标排查先后顺序并无固定,大家可以视自己的需求进行),那么接下来我们就应该关注下问题是不是因为服务器或者 Node.js 应用本身的负载到了极限导致的问题。一些比较常见的大家需要关注的系统指标如下所示:CPU & MemoryDisk 磁盘占用率I/O 负载TCP 连接状态下面逐一讲解这些可能存在问题的系统指标。I. CPU & Memory使用 top 命令来观察和 Node.js 应用进程的 CPU 和 Memory 负载情况。一般来说,对于 CPU 很高 Node.js 进程,我们可以使用 Node.js 性能平台 提供的 CPU Profiling 工具来在线 Dump 出当前的 Javascript 运行情况,进而找到热点代码进行优化,具体在本书第二部分会有更详细地说明。那么对于 Memory 负载很高的情况,正常来说就是发生了内存泄漏(或者有预期之外的内存分配导致溢出),那么同样的我们可以用性能平台提供的工具来在线 Dump 出当前的 Javascript 堆内存和服务化的分析来结合你的业务代码找到产生泄漏的逻辑。这里需要注意的是,目前性能平台能够进行详尽分析的地方集中在你的 JS 代码上,对于完全是 C++ 扩展执行的或者完全的 V8/Libuv 底层执行(这部分功能后面会补上)的逻辑,以及不分配在 V8 Heap 上的内存,性能平台目前没有更好的办法来进行分析处理。而实际上在我们遇到的案例中,大家编写的 JS 代码出问题占了绝大部分,也就是性能平台目前针对 JS 部分比较完善的在线 Dump + 服务化分析基本上能够解决开发者 95% 甚至以上的问题了。II. Disk 磁盘占用率使用 df 命令可以观察当前的磁盘占用情况,这个也是非常常见的问题,很多开发者会忽略对服务器磁盘的监控告警,当我们的日志/核心转储等大文件逐渐将磁盘打满到 100% 的时候,Node.js 应用很可能会无法正常运行,Node.js 性能平台 目前也提供了对磁盘的监控,在本书第二部分同样会有更详细地说明。III. I/O 负载使用 top/iostat 和 cat /proc/${pid}/io 来查看当前的 I/O 负载,这一项的负载很高的话,也会使得 Node.js 应用出现卡死等情况。IV. TCP 连接状态绝大部分的 Node.js 应用实际上是 Web 应用,每个用户的连接都会创建一个 Socket 连接,在一些异常情况下(比如遭受半连接攻击或者内核参数设置不合理),服务器上会有大量的 TIME_WAIT 状态的连接,而大量的 TIME_WAIT 积压会导致 Node.js 应用的卡死(内核无法为新的请求分配创建新的 TCP 连接),我们可以使用 netstat -ant|awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}’ 命令来确认这个问题。核心转储(Core dump)线上 Node.js 应用故障往往也伴随着进程的 Crash,借助于一些守护进程的自检重启拉起,我们的服务依旧在运行,但是我们不应该去忽略这些意外的 Crash —— 当流量增大或者造成服务器的问题用户访问被别有用心之人抓住时,我们集群就变得岌岌可危了。绝大部分情况下,会造成 Node.js 应用 Crash 掉的错误日志往往并不会记录到我们的错误日志文件中,幸运的是,服务器内核提供了一项机制帮助我们在应用 Crash 时自动地生成核心转储(Core dump)文件,让开发者可以在事后进行分析还原案发现场。核心转储核心转储(Core dump)实际上是我们的应用意外崩溃终止时,计算机自动记录下进程 Crash 掉那一刻的内存分配信息、Program counter 以及堆栈指针等关键信息来生成核心转储文件,因此获取到核心转储文件后,我们可以通过 MDB、GDB、LLDB 等工具即可实现解析诊断实际进程的 Crash 原因。生成文件触发核心转储生成转储文件目前主要有两种方式:I. 设置内核参数使用 ulimit -c unlimited 打开内核限制,并且考虑到默认运行模式下,Node.js 对 JS 造成的 Crash 是不会触发核心转储动作的,因此我们可以在 Node 应用启动时加上参数 –abort-on-uncaught-exception 来对出现未捕获的异常时也能让内核触发自动的核心转储动作。II. 手动调用手动调用 gcore <pid> (可能需要 sudo 权限)的方式来手动生成,因为此时 Node.js 应用依旧在运行中,所以实际上这种方式一般用于 「活体检验」,用于 Node.js 进程假死状态 下的问题定位。这里需要注意的是,以上的生成核心转储的操作都 并没有那么安全务必记得对服务器磁盘进行监控和告警**。获取到 Node.js 应用生成的核心转储文件后,我们可以借助于 Node.js 性能平台 提供的在线 Core dump 文件分析功能进行分析定位进程 Crash 的原因了,具体用法会在本书第二部分进行说明。小结本节从常见的几个服务器问题点,给大家对线上 Node.js 应用出现故障时如何去排查定位有了一些大概的印象,本章也是后续内容的一个预备知识,了解了这部分内容,才能在后面的一些实战案例中明白为何我们忽略了其它而选择详尽地服务化分析其中的一些要点。而核心转储的深入分析则能够帮助我们解决 Node.js 应用的绝大部分底层故障,因为其可以还原出问题 JavaScript 代码和引发问题的参数,功能非常地强大。本文作者:奕钧阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 11, 2019 · 1 min · jiezi

Dart编译技术在服务端的探索和应用

前言最近闲鱼技术团队在Flutter+Dart的多端一体化的基础上,实现了FaaS研发模式。Dart吸取了其它高级语言设计的精华,例如Smalltalk的Image技术、JVM的HotSpot和Dart编译技术又师出同门。由Dart实现的语言容器,它可以在启动速度、运行性能有不错的表现。Dart提供了AoT、JIT的编译方式,JIT拥有Kernel和AppJIT的运行模式,此外服务端应用有各自不同的运行特点,那么如何选择合理的编译方法来提升应用的性能?接下来我们用一些有典型特点的案例来引入我们在Dart编译方案的实践和思考。案例详情相应的,我们准备了短周期应用(EmptyMain & Fibonnacci & faas_tool),长周期应用(HttpServer)分别来说明不同的编译方法在各种场景下的性能表现测试环境参考#实验机1Mac OS X 10.14.3 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz * 4 / 16GB RAM#实验机2Linux x86_64Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz * 4 / 8GB RAM#Dart版本Dart Ver. 2.2.1-edge.eeb8fc8ccdcef46e835993a22b3b48c0a2ccc6f1 #Java HotSpot版本Java build 1.8.0_121-b13 Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)#GCC版本Apple LLVM version 10.0.1 (clang-1001.0.46.3)Target: x86_64-apple-darwin18.2.0Thread model: posix短周期应用Case1. EmptyMain例子是一个空函数实现,以此来评估语言平台本身的启动性能,我们使用默认参数编译一个snapshot#1.默认条件下的app-jit snapshot生成dart snapshot-kind=app-jit snapshot=empty_main.snapshot empty_main.dart测试结果作为现代高级语言Dart和Java在启动速度上在同一水平线C语言的启动速度是其它语言的20x,基本原因是C没有Java、Dart语言平台的RuntimeKernel和AppJIT方式运行有稳定的微小差异,总体AppJIT优于KernelCase2. Fibonacci数列我们分别用C、Java、Dart用递归实现Fibonacci(50)数列,来考察编译工作对性能的影响。long fibo(long n){ if(n < 2){ return n; } return fibo(n - 1) + fibo(n - 2);}AppJIT使用优化阈值实现激进优化,这样编译器在Training Run中立即获得生成Optimized代码#2.执行激进优化 dart –no-background-compilation \ –optimization-counter-threshold=1 \ –snapshot-kind=app-jit \ –snapshot=fibonacci.snapshot fibonacci.dart将Fibonacci编译成Kernel#3.生成Kernel snapshotdart –snapshot=fibonacci.snapshot fibonacci.dartAoT的Runtime不在Dart SDK里,需要自行编译AoT Runtime#4.AoT编译pkg/vm/tools/precompiler2 fibonacci.dart fibonacci.aot#5.AoT的方式执行out/ReleaseX64/dart_precompiled_runtime fibonacci.aot测试结果Dart JIT对比下,AppJIT在激进优化后性能稍好于Kernel,差距微小,编译的成本占比可以忽略不计Dart AoT模式下的性能约为JIT的1/6不到JIT运行模式下,HotSpot的执行性能最优,优于Dart AppJIT 25%以上包括C语言在内的AoT运行模式性能均低于JIT,Dart AppJIT性能优于25%问题AoT由于自身的特性(和语言无关),无法在运行时基于Profile实现代码优化,峰值性能在此场景下要差很多,但是为何Dart VM比HotSpot有25%的差距?接下来我们针对Fibonacci做进一步优化#6.编译器调优,调整递归内联深度dart –inlining_recursion_depth_threshold=5 fibonacci.snapshot 50#7.编译器调优,HotSpot调整递归内联深度java -XX:MaxRecursiveInlineLevel=5 Fabbonacci 50测试结果HotSpot VM性能全面领先于Dart VM;两者在最优情况下HotSpot VM的性能优于Dart 9%左右Dart VM 借助JIT调优,性能有大幅提升,相比默认情况有40%左右的提升Dart AppJIT 性能微弱领先Kernel也许也不难想象JVM HotSpot目前在服务器开发领域上的相对Dart成熟,相比HotSpot,DartVM的“出厂设置”比较保守,当然我们也可以大胆猜测,在服务端应用下应该还有除JIT的其它优化空间;和Case1相同,Kernel模式的性能依然低于AppJIT,主要原因是Kernel在运行前期需要把AST转换为堆数据结构、经历Compile、Compile Optimize等过程,而在适当Training run后的AppJIT snapshot在VM启动时以优化后的IL(中间代码)执行,但很快Kernel会追上App-jit,最后性能保持持平。有兴趣的读者可以参阅Vyacheslav Egorov Dart VM的文章。Case3. FaaS容器编译工具在前面我们提到过Dart版本的FaaS语言容器,为追求极致的研发体验,我们需要缩短用户Function打包到部署运行的时间。就语言容器层面而言,Dart提供的Snapshot技术可以大大提升启动速度,但是从用户Function到Snapshot(如下图)生成所产生的编译时间在不做优化的情况下超过10秒,还远远达不到极致体验的要求。我们这里通过一些测试,来寻找提升性能的途径faas_tool是一个完全用Dart编写的代码编译、生成工具。依托于faas_tool, Function的编写者不用关心如何打包、接入中间件,faas_tool提供一系列的模版及代码生成工具可以将用户的使用成本降低,此外faas_tool还提供了HotReload机制可以快速响应变更。这次我们提供了基于AoT、Kernel、AppJIT的用例来执行Function构建流程,分别记录时间消耗、中间产物大小、产物生成时间。为了验证在JIT场景下DartVM是否可通过调整Complier的行为带来性能提升,我们增加了JIT的测试分组测试结果AoT>AppJIT>kernel,其中AoT比优化后的AppJIT有3倍左右性能提升,性能是Source的1000倍JIT(Kernel, AppJIT)分组下,通过在运行时减少CompilerOptimize或暂停PGO可以提升性能很显然faas_tool最终选择了AoT编译,但是性能结果和Case2大相径庭,为了搞清楚原因我们进一步做一下CPU ProfileCPU ProfileAppJITDart App-jit模式 43%以上的时间参与编译,当然取消代码优化,可以让编译时间大幅下降,在优化情况下可以将这个比率下降到13%KernelKernel模式有61%以上的CPU时间参与编译工作, 如果关闭JIT优化代码生成,性能有15%左右提升,反之进行激进优化将有1倍左右的性能损耗AoT下的编译成本AoT模式下在运行时几乎编译和优化成本(CompileOptimized、CompileUnoptimized、CompileUnoptimized 占比为0),直接以目标平台的代码执行,因此性能要好很多。P.S. DartVM 的Profile模块在后期的版本升级更改了Tag命名, 有需要进一步了解的读者参考VM Tags附:DartVM调优和命令代码#8.模拟单核并执行激进优化 dart –no-background-compilation \ –optimization-counter-threshold=1 \ tmp/faas_tool.snapshot.kernel #9.JIT下关闭优化代码生成dart –optimization-counter-threshold=-1 \ tmp/faas_tool.snapshot.kernel #10. Appjit verbose snapshotdart –print_snapshot_sizes \ –print_snapshot_sizes_verbose \ –deterministic \ –snapshot-kind=app-jit \ –snapshot=/tmp/faas_tool.snapshot faas_tool.dart #11.Profile CPU 和 timeline dart –profiler=true \ –startup_timeline=true \ –timeline_dir=/tmp \ –enable-vm-service \ –pause-isolates-on-exit faas_tool.snapshot长周期应用HttpServer我们用一个简单的Dart版的HttpServer作为典型长周期应用的测试用例,该用例中有JsonToObject、ObjectToJson的转换,然后response输出。我们分别用Source、Kernel以及AppJIT的方式在一定的并发量下运行一段时间void processReq(HttpRequest request){ try{ final List<Map<String,dynamic>> buf = <Map<String,dynamic>>[]; final Boss boss = new Boss(numOfEmployee: 10); //Json反序列化对象 getHeadCount(max: 20).forEach((hc){ boss.hire(hc.idType, hc.docId); buf.add(hc.toJson()); }); request.response.headers.add(‘cal’,’${boss.calc()}’); //Json对象转JsonString request.response.write(jsonEncode(buf)); request.response.close() .then((v) => counter_success ++) .timeout(new Duration(seconds:3)) .catchError((e) => counter_fail ++)); } catch(e){ request.response.statusCode = 500; counter_fail ++; request.response.close(); }}测试结果 上面三种无论是何种方式启动,最终的运行时性能趋向一致,编译成本在后期可以忽略不计,这也是JIT的运行特点在AppJIT模式下在应用启动起初就有接近峰值的性能,即使在Kernel模式下也需要时间预热达到峰值性能,Source模式下VM启动需要2秒以上,因此需要相对更长时间达到峰值性能。从另一方面看应用很快完成了预热,不久达到了峰值性能P.S. 长周期的应用Optimize Compiler会经过Optimize->Deoptimize->Reoptimize的过程, 由于此案例比较简单,没体现Deoptimize到Reoptimize的表现附:VM调优脚本#12.调整当前isolate的新生代大小,默认2M最大32M的新生代大小造成频繁的YGCdart –new_gen_semi_max_size=512 \ –new_gen_semi_initial_size=512 \ http_server.dart \ –interval=2 总结和展望Dart编译方式的选择编译成本为主导的应用,应优先考虑AoT来提高应用性能长周期的应用在启动后期编译成本可忽略,应该选择JIT方式并开启Optimize Compiler,让优化器介入长周期的应用可以选择Kernel的方式来提升启动速度,通过AppJIT的方式进一步缩短warmup时间AppJIT减少了编译预热的成本,这个特性非常适合对一些高并发应用在线扩容。Kernel作为Dart编译技术的前端,其平台无关性将继续作为整个Dart编译工具链的基础。在FaaS构建方案的选择通过CPU Profile得出faas_tool是一个编译成本主导的应用,最终选择了AoT编译方案,结果大大提升了语言容器的构建的构建速度,很好满足了faas对开发效率的诉求仍需改进的地方从JIT性能表现来看,DartVM JIT的运行时性和HotSpot相比有提升余地,由于Dart语言作为服务端开发的历史不长,也许随着Dart在服务端的技术应用全面推广,相信DarVM在编译器后端技术上对服务器级的处理器架构做更多优化。本文作者:闲鱼技术-无浩阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 11, 2019 · 2 min · jiezi

“龙井”开箱评测 |Alibaba Dragonwell 新手上路指南

作者|阿里云智能事业群 高级技术专家 陆传胜阿里巴巴有着最丰富的 Java 应用场景,覆盖电商,金融,物流等众多领域,是世界上最大的 Java 用户之一。 2019 年 3 月 21 日,阿里巴巴在北京阿里云峰会上正式宣布开源了 Alibaba Dragonwell 8 产品,并建立了 Alibaba Dragonwell 社区来为全球 Java 用户,特别是中文社区的 Java 用户提供长期支持的 JDK 产品。自宣布开源以来,Alibaba Dragonwell 8 受到了国内外 Java 开发者的关注,今天这篇文章就来详解 Alibaba Dragonwell8 的快速安装和使用,同时列出了参与社区建设的几种方式,期望为那些即将安装及使用 Alibaba Dragonwell 8 的开发者提供参考。Alibaba Dragonwell 8 介绍Alibaba Dragonwell 8 是一款免费的 OpenJDK 发行版。它提供长期支持,包括性能增强和安全修复。Alibaba Dragonwell 8 目前支持 X86-64/Linux 平台,在数据中心大规模 Java 应用部署情况下, 可以大幅度提高稳定性、效率以及性能。Alibaba Dragonwell 8 是 OpenJDK 的下游(friendly fork),使用了和 OpenJDK 一样的 licensing。Alibaba Dragonwell 8 与 Java SE 标准兼容,用户可以使用 Alibaba Dragonwell 8 开发和运行 Java 应用程序。此次开源的 Alibaba Dragonwell 8 是阿里巴巴内部 OpenJDK 定制版 AJDK 的开源版本, AJDK 为在线电商,金融,物流做了结合业务场景的优化,运行在超大规模的,100,000+ 服务器的阿里巴巴数据中心。安装 Alibaba Dragonwell 8目前 Alibaba Dragonwell 8 只支持 Linux x86-64 平台,并且提供了二进制的预编译 JDK 包,您可以通过下面的简单两步安装 Alibaba Dragonwell 8。从 Github 上面 Alibaba Dragonwell 8 项目的下载页面下载预编译的二进制 JDK 包。下载页面链接 https://github.com/alibaba/dragonwell8/releases 将下载下来的 tar 包解压到目标安装目录即可。安装完毕后,只需要将应用引用 的 JAVA_HOME 指向 Alibaba Dragonwell 8 的安装目录就可以使用了。我们以Tomcat8.5.39 版本为例,为了让 Tomcat 运行在 Alibaba Dragonwell 8上面,只需要在启动Tomcat时使用如下命令:JAVA_HOME=/path/to/dragonwell8/installation sh tomcat/bin/catalina.sh start为了确认是运行在 Alibaba Dragonwell 8上面,可以进一步通过给 java 命令添加 -showversion 参数来打印 JDK 版本信息加以判断。JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-showversion" sh tomcat/bin/catalina.sh start启动完毕后在 tomcat/logs/catalina.out 文件的开头,可以看到 Alibaba Dragonwell 8 的版本信息使用 Alibaba Dragonwell 8 专有特性在 8.0-preview 这个版本中, Alibaba Dragonwell 8 提供了两个在阿里巴巴的生产环境中进行过广泛验证的特性:JWarmUp 和 Java Flight Recorder。这两个特性都已经在上游 OpenJDK 社区提交了 JEP 或 patch,在上游合并完成之前,我们希望让 Alibaba Dragonwell 8 的用户可以提前使用到这两个特性。JWarmUp 快速预热 Java 应用OpenJDK 使用了 JIT(Just-in-time) 即时编译技术,可以动态的把 Java 字节码编译成高度优化过机器码,提高执行效率,但在编译之前,Java 代码是以相对低效的解释器模式执行的。在应用启动完成后、业务流量刚进来的短时间内,容易出现的状况是大量 Java 方法开始被 JIT 编译,同时业务请求被较慢的解释器模式执行,最终的结果就是系统负载飙高,可能导致很多用户请求超时。为了解决这个问题,之前的很多做法是使用模拟流量来提前预热应用,JWarmUp 特性提供了一个新的选择,就是利用 Java 虚拟机前一次执行编译得记录来预热本次应用的执行。JWarmUp 的原理如下图所示:一个典型的应用场景是当应用需要发布新版本的时候,首先 JWarmUp 在 Beta 环境(或者有着和生产环境类似流量的其他场景)的单台机器上短时间执行 Java 应用,并记录、收集这段时间里面 JIT 编译器所做动作的一些元数据。然后会把这些元数据复制到生产环境的每一台包含了新版本代码的机器/容器里面。最后在生产环境机器中通过 JWarmUp 参数加载 beta 环境生成的元数据,来指导生产环境的机器在启动应用的过程中就完成 JIT 预热。这样当用户请求进入的时候,应用就会处于性能最高的峰值状态。收集预热数据还以 Tomcat 应用为例,可以添加下面的命令行参数来在 beta 环境中收集 JIT 编译时生成的元数据,其中参数 -XX:CompilationWarmUpLogfile= 指定的就是生成的 JWarmUp 文件的路径。JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:ReservedCodeCacheSize=512m -XX:CompilationWarmUpLogfile=$PWD/jwarmup.log -XX:+CompilationWarmUpRecording -XX:+CompilationWarmUp -XX:-TieredCompilation -XX:+DeoptimizeBeforeWarmUp -XX:CompilationWarmUpDeoptTime=30 -XX:+PrintCompilationWarmUpDetail" sh bin/catalina.sh start生成之后可以把这个文件通过 OSS、SFTP 等方式传输到生产环境的机器上。使用记录的数据来预热 Java 应用在生产环境的机器上,只需要使用下面的参数,就可以利用之前的预热数据来启动一个新的 Tomcat 实例,其中参数 -XX:CompilationWarmUpLogfile= 制定的就是需要被加载的 JWarmUp 文件路径,这个文件应该是上一步收集预热数据时从 beta 环境复制过来的。JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:ReservedCodeCacheSize=512m -XX:CompilationWarmUpLogfile=$PWD/jwarmup.log -XX:+CompilationWarmUp -XX:-TieredCompilation -XX:+DeoptimizeBeforeWarmUp -XX:CompilationWarmUpDeoptTime=30 -XX:+PrintCompilationWarmUpDetail" sh bin/catalina.sh start使用 Java Flight Recorder 分析 Java 应用性能JFR(Java Flight Recorder)是 JVM 内置的基于事件的性能分析特性,这是 Oracle JDK7u4 版本开始提供的商业特性,2018 年的时候在 JDK11 上开源了这个特性,但是一直没有针对 JDK8 版本的支持。阿里巴巴和 RedHat、Azul、Amazon 等公司一起合作尝试把这个特性移植回 JDK8上,不过该 patch 暂时还没有合并回 OpenJDK8u仓库,我们在 Alibaba Dragonwell 8 中提供了 Alibaba 移植的 JFR 版本,用于帮助用户提前获取这方面的支持。JFR 的用法很简单,用户使用命令行参数或者 jcmd 命令控制 HotSpot 输出性能数据到文件中,然后就能使用开源的 jmc 工具在图形界面中打开、分析生成的数据文件了。使用 JFR 收集性能数据在 Alibaba Dragonwell 8中,默认情况下 JFR 特性是处于关闭的状态,必须添加命令行参数 -XX:+EnableJFR 来允许使用 JFR 特性。 Alibaba Dragonwell 8 提供了不同的方式来使用 JFR 采集性能数据。应用可以可以通过命令行参数指定 JFR 再启动后立马开始采集数据,这个对于诊断启动阶段的问题会很有帮助。下面的示例命令会在 Java 进程的 JFR 模块初始化后就开始收集 JFR 数据,持续一分钟,并且把数据都输出到名为rec.jfr 的文件中。JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:+EnableJFR -XX:StartFlightRecording=duration=1m,filename=rec.jfr" sh bin/catalina.sh start应用也可以只添加 -XX:+EnableJFR ,然后通过 jcmd 命令在应用启动后的任意时间点开始采集数据,这种情况更加灵活可控,可以满足随时随地进行分析的需求。以 Tomcat 为例,启动 Tomcat 的命令可以是:JAVA_HOME=/path/to/dragonwell8/installation JAVA_OPTS="-XX:+EnableJFR" sh bin/catalina.sh start当需要收集数据进行分析时,只需要使用目标 Tomcat 进程的 PID 来执行 JFR 对应的 jcmd 命令即可。以 Tomcat 为例,如果需要在指定时刻开始收集 10 秒的数据,那么触发的命令如下:$ ps ax | grep tomcat 77522 pts/18 Sl+ 0:08 /home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/../j2sdk-image/bin/java -Djava.util.logging.config.file=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -XX:+EnableJFR -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/bin/bootstrap.jar:/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/bin/tomcat-juli.jar -Dcatalina.base=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39 -Dcatalina.home=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39 -Djava.io.tmpdir=/home/chuansheng.lcs/dw_test/apache-tomcat-8.5.39/temp org.apache.catalina.startup.Bootstrap start 98451 pts/22 S+ 0:00 grep –color=auto tomcat$ dragonwell8_home/bin/jcmd 77522 JFR.start duration=10s filename=$PWD/rec3.jfr77522:Started recording 3. The result will be written to:/home/my/workdir/rec3.jfr10 秒之后可以看到生成了 JFR 数据文件/home/my/workdir/rec3.jfr,使用 JMC 即可进行分析。也可以不指定收集数据的时间,直接启动 JFR 收集,并且在需要的时候手动把所有生成的数据一次性 dump 到文件,$ dragonwell8_home/bin/jcmd 2823 JFR.start filename=$PWD/rec4.jfr2823:Started recording 4. No limit specified, using maxsize=250MB as default.Use JFR.dump name=4 to copy recording data to file.$ dragonwell8_home/bin/jcmd 2823 JFR.dump name=4 filename=rec4.jfr2823:Dumped recording “Recording-4”, 332.7 kB written to:/path/to/my/workdir/rec4.jfr使用 JMC 分析性能JFR 记录 Java 应用性能数据的输出是一个二进制的文件,我们借助于 JMC(Java Mission Control) 工具可以在图形化界面里面分析具体的性能数据。这个工具是开源产品,没有包括在 Alibaba Dragonwell 8里面,需要到 OpenJDK 的官方网站下载使用,https://jdk.java.net/jmc/。请注意, Alibaba Dragonwell8 生成的JFR数据文件需要 7.0 或更高版本的 JMC 工具来分析。打开 JMC 后,可以点击左侧的详细类别来详细分析采样时间段内发生的各种事件。诊断调试支持 Alibaba Dragonwell 8 还内置一些方便的诊断特性,主要包括大对象分配报警可以通过新的 JVM 参数"-XX:ArrayAllocationWarningSize=",比如下面代码中分配了比较大的数组public static void main(String[] args) { doAlloc(32 * 1024 * 1024 + 1);}private static Object doAlloc(int size) { return new byte[size];}执行时如果添加 ArrayAllocationWarningSize 选项就会打印出分配该数组时的 Java 堆栈详细的 ParNew GC 日志支持 Alibaba Dragonwell 8 默认使用了 CMS (Concurrent Mark Sweep) 算法,新生代使用了 ParNew 算法,所以内置两个针对 ParNew GC 日志的增强可以通过 jinfo 工具设置 PrintYoungGenHistoAfterParNewGC 选项来在下一次 Young GC 结束的时候打印新生代的对象类型直方图。命令如下jinfo -flag +PrintYoungGenHistoAfterParNewGC <pid>打印完成后,这个选项会被重置回 false 状态,防止过多的输出,一个典型的输出例子如下:可以通过-XX:+PrintGCRootsTraceTime 来打印处理每一类 GC 根集所花费的详细 CPU 时间,输出示例如下:精简版 HeapDump 支持 Alibaba Dragonwell 8 的 jmap 工具支持一个新的 dump 选项“mini”可以在做 heapdump 的时候跳过所有的原始类型数组的内容,从而大大减小生成的 heapdump 文件大小,这对于只需要排查类型、对象关系的场景会比较有帮助。示例命令如下:本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 9, 2019 · 3 min · jiezi

Node.js 应用故障排查手册 —— 综合性 GC 问题和优化

楔子本章前面两节生产案例分别侧重于单一的 CPU 高和单一的内存问题,我们也给大家详细展示了问题的定位排查过程,那么实际上还有一类相对更复杂的场景——它本质上是 V8 引擎的 GC 引发的问题。简单的给大家介绍下什么是 GC,GC 实际上是语言引擎实现的一种自动垃圾回收机制,它会在设定的条件触发时(比如堆内存达到一定值)时查看当前堆上哪些对象已经不再使用,并且将这些没有再使用到的对象所占据的空间释放出来。许多的现代高级语言都实现了这一机制,来减轻程序员的心智负担。本书首发在 Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区会同步更新。GC 带来的问题虽然上面介绍中现代语言的 GC 机制解放了程序员间接提升了开发效率,但是万事万物都存在利弊,底层的引擎引入 GC 后程序员无需再关注对象何时释放的问题,那么相对来说程序员也就没办法实现对自己编写的程序的精准控制,它带来两大问题:代码编写问题引发的内存泄漏程序执行的性能降低内存泄漏问题我们已经在上一节的生产案例中体验了一下,那么后者是怎么回事呢?其实理解起来也很简单:原本一个程序全部的工作都是执行业务逻辑,但是存在了 GC 机制后,程序总会在一定的条件下耗费时间在扫描堆空间找出不再使用的对象上,这样就变相降低了程序执行业务逻辑的时间,从而造成了性能的下降,而且降低的性能和耗费在 GC 上的时间,换言之即 GC 的次数 * 每次 GC 耗费的时间成正比。问题现象与原始分析现在大家应该对 GC 有了一个比较整体的了解,这里我们可以看下 GC 引发的问题在生产中的表现是什么样的。在这个案例中,表象首先是 Node.js 性能平台 上监控到进程的 CPU 达到 100%,但是此时服务器负载其实并不大,QPS 只有 100 上下,我们按照前面提到的处理 CPU 问题的步骤抓取 CPU Profile 进行分析可以看到:这次的问题显然是 Garbage Collector 耗费的 CPU 太多了,也就是 GC 的问题。实际上绝大部分的 GC 机制引发的问题往往表象都是反映在 Node.js 进程的 CPU 上,而本质上这类问题可以认为是引擎的 GC 引起的问题,也可以理解为内存问题,我们看下这类问题的产生流程:堆内存不断达到触发 GC 动作的预设条件进程不断触发 GC 操作进程 CPU 飙高而且 GC 问题不像之前的 ejs 模板渲染引发的问题,就算我们在 CPU Profile 中可以看到这部分的耗费,但是想要优化解决这个问题基本是无从下手的,幸运的是 Node.js 提供了(其实是 V8 引擎提供的)一系列的启动 Flag 能够输出进程触发 GC 动作时的相关日志以供开发者进行分析:–trace_gc:一行日志简要描述每次 GC 时的时间、类型、堆大小变化和产生原因–trace_gc_verbose: 结合 –trace_gc 一起开启的话会展示每次 GC 后每个 V8 堆空间的详细状况–trace_gc_nvp: 每一次 GC 的一些详细键值对信息,包含 GC 类型,暂停时间,内存变化等信息加粗的 Flag 意味着我们需要在启动应用前加上才能在运行时生效,这部分的日志实际上是一个文本格式,可惜的是 Chrome devtools 原生并不支持 GC 日志的解析和结果展示,因此需要大家获取到以后进行对应的按行解析处理,当然我们也可以使用社区提供 v8-gc-log-parser 这个模块直接进行解析处理,对这一块有兴趣的同学可以看 @joyeeCheung 在 JS Interactive 的分享: Are Your V8 Garbage Collection Logs Speaking To You?,这里不详细展开。更好的 GC 日志展示虽然 Chrome devtools 并不能直接帮助我们解析展示 GC 日志的结果,但是 Node.js 性能平台 其实给大家提供了更方便的动态获取线上运行进程的 GC 状态信息以及对应的结果展示,换言之,开发者无需在运行你的 Node.js 应用前开启上面提到的那些 Flag 而仍然可以在想要获取到 GC 信息时通过控制台拿到 3 分钟内的 GC 数据。对应在这个案例中,我们可以进入平台的应用实例详情页面,找到 GC 耗费特别大的进程,然后点击 GC Trace 抓取 GC 数据:这里默认会抓取 3 分钟的对应进程的 GC 日志信息,等到结束后生成的文件会显示在 文件 页面:此时点击 转储 即可上传到云端以供在线分析展示了,如下图所示:最后点击这里的 分析 按钮,即可看到 AliNode 定制后的 GC 信息分析结果的展现:结果展示中,可以比较方便的看到问题进程的 GC 具体次数,GC 类型以及每次 GC 的耗费时间等信息,方便我们进一步的分析定位。比如这次问题的 GC Trace 结果分析图中,我们可以看到红圈起来的几个重要信息:GC 总暂停时间高达 47.8s,大头是 Scavenge3min 的 GC 追踪日志里面,总共进行了 988 次的 Scavenge 回收每次 Scavenge 耗时均值在 50 ~ 60ms 之间从这些解困中我们可以看到此次 GC 案例的问题点集中在 Scavenge 回收阶段,即新生代的内存回收。那么通过翻阅 V8 的 Scavenge 回收逻辑可以知道,这个阶段触发回收的条件是:Semi space allocation failed。这样就可以推测,我们的应用在压测期间应该是在新生代频繁生成了大量的小对象,导致默认的 Semi space 总是处于很快被填满从而触发 Flip 的状态,这才会出现在 GC 追踪期间这么多的 Scavenge 回收和对应的 CPU 耗费,这样这个问题就变为如何去优化新生代的 GC 来提升应用性能。优化新生代 GC通过平台提供的 GC 数据抓取和结果分析,我们知道可以去尝试优化新生代的 GC 来达到提升应用性能的目的,而新生代的空间触发 GC 的条件又是其空间被占满,那么新生代的空间大小由 Flag –max-semi-space-size 控制,默认为 16MB,因此我们自然可以想到要可以通过调整默认的 Semi space 的值来进行优化。这里需要注意的是,控制新生代空间的 Flag 在不同的 Node.js 版本下并不是一样的,大家具体可以查看当前的版本来进行确认使用。在这个案例中,显然是默认的 16M 相对当前的应用来说显得比较小,导致 Scavenge 过于频繁,我们首先尝试通过启动时增加 –max-semi-space-size=64 这个 Flag 来将默认的新生代使用到的空间大小从 16M 的值增大为 64M,并且在流量比较大而且进程 CPU 很高时抓取 CPU Profile 观察效果:调整后可以看到 Garbage collector 阶段 CPU 耗费占比下降到 7% 左右,再抓取 GC Trace 并观察其展示结果确认是不是 Scavenge 阶段的耗费下降了:显然,Semi space 调大为 64M 后,Scavenge 次数从近 1000 次降低到 294 次,但是这种状况下每次的 Scavenge 回收耗时没有明显增加,还是在 50 ~ 60ms 之间波动,因此 3 分钟的 GC 追踪总的停顿时间从 48s 下降到 12s,相对应的,业务的 QPS 提升了约 10% 左右。那么如果我们通过 –max-semi-space-size 这个 Flag 来继续调大新生代使用到的空间,是不是可以进一步优化这个应用的性能呢?此时尝试 –max-semi-space-size=128 来从 64M 调大到 128M,在进程 CPU 很高时继续抓取 CPU Profile 来查看效果:此时 Garbage collector 耗费下降相比上面的设置为 64M 并不是很明显,GC 耗费下降占比不到 1%,同样我们来抓取并观察下 GC Trace 的结果来查看具体的原因:很明显,造成相比设置为 64M 时 GC 占比提升不大的原因是:虽然此时进一步调大了 Semi space 至 128M,并且 Scavenge 回收的次数确实从 294 次下降到 145 次,但是每次算法回收耗时近乎翻倍了,因此总收益并不明显。按照这个思路,我们再使用 –max-semi-space-size=256 来将新生代使用的空间进一步增大到 256M 再来进行最后一次的观察:这里和调整为 128M 时是类似的情况: 3 分钟内 Scavenge 次数从 294 次下降到 72 次,但是相对的每次算法回收耗时波动到了 150ms 左右,因此整体性能并没有显著提升。借助于性能平台的 GC 数据抓取和结果展示,通过以上的几次尝试改进 Semi space 的值后,我们可以看到从默认的 16M 设置到 64M 时,Node 应用的整体 GC 性能是有显著提升的,并且反映到压测 QPS 上大约提升了 10%;但是进一步将 Semi space 增大到 128M 和 256M 时,收益确并不明显,而且 Semi space 本身也是作用于新生代对象快速内存分配,本身不宜设置的过大,因此这次优化最终选取对此项目 最优的运行时 Semi space 的值为 64M。结尾在本生产案例中,我们首先可以看到,项目使用的三方库其实也并不总是在所有场景下都不会有 Bug 的(实际上这是不可能的),因此在遇到三方库的问题时我们要敢于去从源码的层面来对问题进行深入的分析。最后实际上在生产环境下通过 GC 方面的运行时调优来提升我们的项目性能是一种大家不那么常用的方式,这也有很大一部分原因是应用运行时 GC 状态本身不直接暴露给开发者。通过上面这个客户案例,我们可以看到借助于 Node.js 性能平台,实时感知 Node 应用 GC 状态以及进行对应的优化,使得不改一行代码提升项目性能变成了一件非常容易的事情。本文作者:奕钧阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 9, 2019 · 2 min · jiezi

Node.js 应用故障排查手册 —— Node.js 性能平台使用指南

楔子前一节中我们借助于 Chrome devtools 实现了对线上 Node.js 应用的 CPU/Memory 问题的排查定位,但是在实际生产实践中,大家会发现 Chrome devtools 更加偏向本地开发模式,因为显然 Chrome devtools 不会负责去生成分析问题所需要的 Dump 文件,这意味着开发者还得额外在线上项目中设置好 v8-profiler 和 heapdump 这样的工具,并且通过额外实现的服务来能够去对线上运行的项目进行实时的状态导出。加上实际上预备章中除了 CPU/Memory 的问题,我们还会遇到一些需要分析错误日志、磁盘和核心转储文件等才能定位问题的状况,因此在这些场景下,仅仅靠 Chrome devtools 显然会有一些力不从心。正是为了解决广大 Node.js 开发者的这些痛点,我们在这里推荐大家在使用 Node.js 性能平台,即原来的 AliNode,它已经在阿里巴巴集团内部承载了几乎所有的 Node.js 应用线上运行监控和问题排查,因此大家可以放心在生产环境部署使用。本节将从 Node.js 性能平台 的设计架构、核心能力以及最佳实践等角度,帮助开发者更好地使用这一工具来解决前面提到的异常指标分析和线上 Node.js 应用故障定位。本书首发在 Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区会同步更新。架构Node.js 性能平台其实简单的说由三部分组成:云控制台 + AliNode runtime + Agenthub,如下图所示:具体的部署步骤可以查看官方文档:自助式部署 Runtime。借助于 Node.js 性能平台的整套解决方案,我们可以很方便地实现预备章节中提到的绝大部分异常指标的告警分析的能力。在生产实践过程中,实际上在笔者看来,Node.js 性能平台解决方案其实仅仅是提供了三个最核心却也是最有效的能力:异常指标告警,即预备节中一些异常指标出现异常时能通过短信/钉钉通知到开发者导出线上 Node.js 应用状态,包括但不限于即前面 Chrome devtools 一节中提到的 CPU/Memory 状态导出在线分析结果和更好的 UI 展示,定制化解析应用导出状态和展示,更符合国内开发者习惯换言之,Node.js 性能平台作为一个产品本身功能也在不断迭代新增修改中,但是以上的三个核心能力一定是第一优先级保障的,其它边边角角的功能则相对来说响应优先级没有那么高。实际上我们也理解作为使用平台的开发者希望能在一个地方看到 Node.js 线上应用从底层到业务层的所有细节,然而我个人感觉不同的工具都有应该有其核心的能力输出,很多时候不断做加法容易让产品本身定位模糊化以及泛而不精,Node.js 性能平台实际上始终在致力于让原本线上黑盒的运行时状态,能更加直观地反馈给开发者,让 Node.js 应用的开发者面对一些偏向底层的线上疑难杂症能够不再无所适从。最佳实践I.配置合适的告警线上应用的告警实际上是一种自我发现问题的保护机制,如果没有告警能力,那每次都会等到问题暴露到用户侧导致其反馈才能发现问题,这显然对用户体验非常的不友好。因此部署完成一个项目后,开发者首先需要去配置合适的告警,而在我们的生产实践中,线上问题通过错误日志、Node.js 进程 CPU/Memory 的分析、核心转储(Core dump)的分析以及磁盘分析能够得出结论,因此我们需要的基本的告警策略也是源自以上五个部分。幸运的是平台已经给我们预设好了这些告警,大家只需要选择一下即可完整这里的告警配置,如下图所示:在 Node.js 性能平台 的告警页面上有 快速添加规则,点开选中后会自动生成告警规则的阈值表达式模板和报警说明模板,我们可以按照项目实际监控需求进行修改,比如想要对 Node.js 进程的堆内存进行监控,可以选中 Memory 预警 选项,如下图所示:此时点击 添加报警项 即完整了对进程堆内存的告警,并且将出现告警时需要点击 通知设置->添加到联系人列表 来添加的联系人加入此条规则,如下图所示:那么在例子中的这条默认的规则里,当我们的 Node.js 进程分配的堆内存超过堆上线的 80%(默认 64 位机器上堆上限是 1.4G)时会触发短信通知到配置绑定到此条规则的联系人。实际上快速添加规则列表中给大家提供的是最常见的一些预配置好的告警策略,如果这些尚不能满足你的需求,更多定制化的自定义的服务告警策略配置方法可以看官方文档 报警设置。并且除了短信告警,也支持钉钉机器人推送告警消息到群,方便群发感知线上 Node.js 应用态势。II. 按照告警类型进行分析按照 I 节中配置完成合适的告警规则后,那么当收到告警短信时就可以按照策略类型进行对应的分析了。本节将按照预备节中比较常见的五大类问题来逐一讲解。a. 磁盘监控这个是比较好处理的问题,在快速添加的规则里实际上我们会在服务器的磁盘使用超过 85% 时进行告警,那么收到磁盘告警后,可以连接到服务器,使用如下命令查看那个目录占用比较高:sudo du -h –max-depth=1 /找到占比比较高的目录和文件后,看看是否需要备份后删除来释放出磁盘空间。b. 错误日志收到特定的错误日志告警后,只需要去对应的项目的 Node.js 性能平台 控制台找到问题 实例 去查看其 异常日志 即可,如下图所示:这里会按照错误类型进行规整,大家可以结合展示的错误栈信息来进行对应的问题定位。注意这里的错误日志文件需要你在部署 Agenthub 的时候写入配置文件,详细可以参见文档 配置和启动 Agenthub 一节中的 详细配置 内容。c. 进程 CPU 高终于到了前一节中借助 v8-profiler 导出 CPU Profile 文件再使用 Chrome devtools 进行分析的异常类型了。那么在 Node.js 性能平台 的整套解决方案下,我们并不需要额外的去依赖类似 v8-profiler 这样的第三方库来实现进程状态的导出,与此相对的,当我们收到 Node.js 应用进程的 CPU 超过我们设置的阈值告警时,我们只需要在控制台对应的 实例 点击 CPU Profile 按钮即可:默认会给抓取的进程生成 3 分钟的 CPU Profile 文件,等到结束后生成的文件会显示在 文件 页面:此时点击 转储 即可上传到云端以供在线分析展示了,如下图所示:这里可以看到有两个 分析 按钮,其实第二个下标带有 (devtools) 的分析按钮实际上就是前一节中提到的 Chrome devtools 分析,这里不再重复讲解了,如果有遗忘的同学可以再去回顾下本大章前一节的内容。我们重点看下第一个 AliNode 定制的分析,点击第一个分析按钮后,可以在新页面看到如下所示内容:这里其实也是火焰图,但与 Chrome devtools 提供的火焰图不一样的地方在于,这里是将抓取的 3 分钟内的 JS 函数执行进行了聚合展示出来的火焰图,在一些存在多次执行同一个函数(可能每次执行非常短)的情况下,聚合后的火焰图可以很方便的帮助我们找到代码的执行瓶颈来进行对应的优化。值得一提的是,如果你使用的 AliNode runtime 版本在 v3.11.4 或者 v4.2.1 以上(包含这两个版本)的话,当你的应用出现类死循环问题,比如由于异常的用户参数导致的正则回溯(即执行完这个正则要十几年,类似于 Node.js 进程发生了死循环)这类问题时,可以通过抓取 CPU Profile 文件来很方便地定位到问题代码,详细信息有兴趣的同学可以看下 Node.js 性能平台支持死循环和正则攻击定位。d. 内存泄漏与 CPU 高的问题一样,当我们收到 Node.js 应用进程的堆内存占据堆上限的比率超过我们设置的阈值时,我们也不需要类似 heapdump 这样的第三方模块来导出堆快照进行分析,我们还是在控制台对应的 实例 点击 堆快照 按钮即可生成对应 Node.js 进程的堆快照:生成的堆快照文件同样会显示在 文件 列表页面,点击 转储 将堆快照上传至云端以供接下来的分析:与上面一样,下标带有 (devtools) 的分析按钮还是前一节中提到的 Chrome devtools 分析,这里还是着重解析下 AliNode 定制的第一个分析按钮,点击后新页面如下图所示:首先解释下上面的总览栏目的内容信息:文件大小: 堆快照文件本身的大小Shallow Szie 总大小: 回顾下上一节中的内容,GC 根的 Retained Size 大小其实就是堆大小,也等于堆上分配的所有对象的 Shallow Size 总大小,因此这里其实就是已使用的堆空间对象个数: 当前堆上分配的 Heap Object 总个数对象边个数: 这个稍微抽象一些,假如 Object A.b 指向另一个 Object B,我们则认为表示指向关系的 b 属性就是一条边GC Roots 个数: V8 引擎实现的堆分配,其实并不是我们之前为了帮助大家理解简化的只有一个 GC 根的情况,在实际的运行模型下,堆空间内存在许多的 GC 根,这里是真实的 GC 根的个数这部分的信息旨在给大家一个概览,部分信息需要深入解读堆快照才能彻底理解,如果你实在无法理解其中的几个概览指标信息,其实也无伤大雅,因为这并不影响我们定位问题代码。简单了解了概览信息的含义后,接着我们来看对于定位 Node.js 应用代码段非常重要的信息,第一个是默认展示的 可疑点 信息,上图中的内容表示 @15249 这个对象占据了堆空间 97.41% 的内存,那么它很可能就是一个泄漏对象,这里又存在两种可能:此对象本身应该被释放但是却没有释放,造成堆空间占用如此大此对象的某些属性应该被释放但是却没有释放,造成表象是此对象占据大量的堆空间要判断是哪一种情况,以及追踪到对应的代码段,我们需要点击图中的 簇视图 链接进行进一步观察:这里继续解释下什么是簇视图,簇视图实际上是支配树的一个别名,也就是这个视图下我们看到的正是前面一节中提到的从可疑泄漏对象出发的支配树视图,它的好处是,在这个视图下,父节点的 Retained Size 可以直接由其子节点的 Retained Size 累加后再加上父节点自身的 Shallow Size 得到,换言之,在这个视图下我们层层展开即可以看到可疑泄漏对象的内存究竟被哪些子节点占用了。并且结合前一节的支配树描述,我们可以知道支配树下的父子节点关系,并不一定是真正的堆上空间内的对象父子关系,但是对于那些支配树下父子关系在真正的堆空间内也存在父子节点关系的簇节点,我们将真正的 边 也用浅紫色标识出来,这部分的 边 信息对于我们映射到真正的代码段非常有帮助。在这个简单的例子中,我们可以很清晰的看到可疑泄漏对象 @15249 实际上是下属的 test-alinode.js 中存在一个 array 变量,其中存储了四个 45.78 兆的数组导致的问题,这样就找到了问题代码可以进行后续优化。而在实际生产环境的堆快照分析下,很多情况下簇视图下的父子关系在真实的堆空间中并不存在,那么就不会有这些紫色的边信息展示,这时候我们想要知道可疑泄漏对象如何通过 JavaScript 生成的对象间引用关系引用到后面真正占据掉堆空间的对象(比如上图中的 40 多兆的 Array 对象),我们可以点击 可疑节点自身的地址链接 :这样就进入到以此对象为起点的堆空间内真正的对象引用关系视图 Search 视图:这个视图因为反映的是堆空间内各个 Heap Object 之间真正的引用连接关系,因此父对象的 Retained Size 并不能直接由子节点的 Retained Size 累加获取,如上图红框内的内容,显然这里的三个子节点 Retained Size 累加已经超过 100%,这也是 Search 视图和簇视图很大的不同点。借助于 Search 视图,我们可以根据其内反映出来的对象和边之间的关系来定位可疑泄漏对象具体是在我们的 JavaScript 代码中的哪一部分生成。其实看到这边,一些读者应该意识到了这里的 Search 视图实际上对应的就是前一节中提到的 Chrome devtools 的 Containment 视图,只不过这里的起始点是我们选中的对象本身罢了。最后就是需要提一下 Retainers 视图,它和前一节中提到的 Chrome devtools 中解析堆快照展示结果里面的 Retainers 含义是一致的,它表示对象的父引用关系链,我们可以来看下:这里 globa@1279 对象的 clearImmediate 属性指向 timers.js()@15325,而 timers.js()@15325 的 context 属性指向了可疑的泄漏对象 system / Context@15249。在绝大部分的情况下,通过结合 Search 视图 和 Retainers 视图 我们可以定位到指定对象在 JavaScript 代码中的生成位置,而 簇视图 下我们又可以比较方便的知道堆空间被哪些对象占据掉了,那么综合这两部分的信息,我们就可以实现对线上内存泄漏的问题进行分析和代码定位了。e. 出现核心转储最后就是收到服务器生成核心转储文件(Core dump 文件)的告警了,这表示我们的进程已经出现了预期之外的 Crash,如果你的 Agenthub 配置正常的话,在 文件 -> Coredump 文件 页面会自动将生成的核心转储文件信息展示出来:和之前的步骤类似,我们想要看到服务端分析和结果展示,首先需要将服务器上生成的核心转储文件转储到云端,但是与之前的 CPU Profile 和堆快照的转储不一样的地方在于,核心转储文件的分析需要我们提供对应 Node.js 进程的启动执行文件,即 AliNode runtime 文件,这里简化处理为只需要设置 Runtime 版本即可:点击 设置 runtime 版本 即可进行设置,格式为 alinode-v{x}.{y}.{z} 的形式,比如 alinode-v3.11.5,版本会进行校验,请务必填写你的应用真实在使用的 AliNode runtime 版本。版本填写完成后,我们就可以点击 转储 按钮进行文件转储到云端的操作了:显然对于核心转储文件来说,Chrome devtools 是没有提供解析功能的,所以这里只有一个 AliNode 定制的分析按钮,点击这个 分析 按钮,即可以看到结果:这里第一栏的概览信息看文字描述就能理解其含义,所以这里就不再多做解释了,我们来看下比较重要的默认视图 BackTrace 信息视图,此视图下展示的实际上是 Node.js 应用在 Crash 时刻的线程信息,许多开发者认为 Node.js 是单线程的运行模型,其实这句话也不是完全错误,更准确的说法是 单主 JavaScript 工作线程,因为实际上 Node.js 还会开启一些后台线程来处理诸如 GC 里的部分任务。绝大部分的情况下,应用的 Crash 都是由 JavaScript 工作线程引发的,因此我们需要关注的也仅仅是这个线程,这里显然 BackTrace 信息视图中将 JavaScript 工作线程做了标红和置顶处理,展开后可以看到 Node.js 应用 Crash 那一刻的错误堆栈信息:因为就算在 JavaScript 的工作线程中,也会存在 Native C/C++ 代码的穿透,但是在问题排查中我们往往只需要去看同样标红的 JavaScript 栈信息即可,像在这个简单的例子中,显然 Crash 是因为视图去启动一个不存在的 JS 文件导致的。值得一提的是,核心转储文件的分析功能非常的强大,因为在预备节中我们提到其生成的途径除了 Node.js 应用 Crash 的时候由系统内核控制输出外,还可以由 gcore 这样的命令手动强制输出,而本小节我们又看到核心转储文件的分析实际上可以看到此刻的 JavaScript 栈信息以及其入参,结合这两点,我们可以在线上出现 CPU Profile 一节中提到的类死循环问题时直接采用 gcore 生成核心转储文件,然后上传至平台云端进行分析,这样不仅可以看到我们的 Node.js 应用是阻塞在哪一行的 JavaScript 代码,甚至引发阻塞的参数我们也能完整获取到,这对本地复现定位问题的帮助无疑是无比巨大的。结尾本节其实给大家介绍了 Node.js 性能平台 的整套面向 Node.js 应用开发的监控、告警、分析和定位问题的解决方案的架构和最佳实践,希望能让大家对平台的能力和如何更好地结合自身项目进行使用有一个整体的理解。限于篇幅,最佳实践中的 CPU Profile、堆快照和核心转储文件的分析例子都非常的简单,这部分的内容也更多的是旨在帮助大家理解平台提供的工具如何使用以及其分析结果展示的指标含义,那么本书的第三节中,我们会通过一些实际的生产遇到的案例问题借助于 Node.js 性能平台 提供的上述工具分析过程,来帮助大家更好的理解这部分信息,也希望大家在读完这些内容后能有所收获,能对 Node.js 应用在生产中的使用更有信心。本文作者:奕钧阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 3, 2019 · 3 min · jiezi

Node.js 应用故障排查手册 —— 利用 CPU 分析调优吞吐量

楔子在我们想要新上线一个 Node.js 应用之前,尤其是技术栈切换的第一个 Node.js 应用,由于担心其在线上的吞吐量表现,肯定会想要进行性能压测,以便对其在当前的集群规模下能抗住多少流量有一个预估。本案例实际上正是在这样的一个场景下,我们想要上线 Node.js 技术栈来做前后端分离,那么刨开后端服务的响应 QPS,纯使用 Node.js 进行的模板渲染能有怎么样的表现,这是大家非常关心的问题。本书首发在 Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区会同步更新。优化过程集群在性能压测下反映出来的整体能力其实由单机吞吐量就可以测算得知,因此这次的性能压测采用的 4 核 8G 内存的服务器进行压测,并且页面使用比较流行的 ejs 进行服务端渲染,进程数则按照核数使用 PM2 启动了四个业务子进程来运行。1. 开始压测完成这些准备后,使用阿里云提供的 PTS 性能压测工具进行压力测试,此时大致单机 ejs 模板渲染的 QPS 在 200 左右,此时通过 Node.js 性能平台 监控可以看到四个子进程的 CPU 基本都在 100%,即 CPU 负载达到了瓶颈,但是区区 200 左右的 QPS 显然系统整体渲染非常的不理想。2. 模板缓存因为是 CPU 达到了系统瓶颈导致整体 QPS 上不去,因此按照第二部分工具篇章节的方法,我们在平台上抓了 压测期间 的 3 分钟的 CPU Profile,展现的结果如下图所示:这里就看到了很奇怪的地方,因为压测环境下我们已经打开了模板缓存,按理来说不会再出现 ejs.compile 函数对应的模板编译才对。仔细比对项目中的渲染逻辑代码,发现这部分采用了一个比较不常见的模块 koa-view,项目开发者想当然地用 ejs 模块地入参方式传入了 cache: true,但是实际上该模块并没有对 ejs 模板做更好的支持,因此实际压测情况下模板缓存并没有生效,而模板地编译动作本质上字符串处理,它恰恰是一个 CPU 密集地操作,这就导致了 QPS 达不到预期的状况。了解到原因之后,首先我们将 koa-view 替换为更好用的 koa-ejs 模块,并且按照 koa-ejs 的文档正确开启缓存:render(app, { root: path.join(__dirname, ‘view’), viewExt: ‘html’, cache: true});再次进行压测后,单机下的 QPS 提升到了 600 左右,虽然大约提升了三倍的性能,但是仍然达不到预期的目标。3. include 编译为了继续优化进一步提升服务器的渲染性能,我们继续在压测期间抓取 3 分钟的 CPU Profile 进行查看:可以看到,我们虽然已经确认使用 koa-ejs 模块且正确开启了缓存,但是压测期间的 CPU Profile 里面竟然还有 ejs 的 compile 动作!继续展开这里的 compile,发现是 includeFile 时引入的,继续回到项目本身,观察压测的页面模板,确实使用了 ejs 注入的 include 方法来引入其它模板:<%- include("../xxx") %>对比 ejs 的源代码后,这个注入的 include 函数调用链确实也是 include -> includeFile -> handleCache -> compile,与压测得到的 CPU Profile 展示的内容一致。那么下面红框内的 replace 部分也是在 compile 过程中产生的。到了这里开始怀疑 koa-ejs 模块没有正确地将 cache 参数传递给真正负责渲染地 ejs 模块,导致这个问题地发生,所以继续去阅读 koa-ejs 的缓存设置,以下是简化后的逻辑(koa-ejs@4.1.1 版本):const cache = Object.create(null);async function render(view, options) { view += settings.viewExt; const viewPath = path.join(settings.root, view); // 如果有缓存直接使用缓存后的模板解析得到的函数进行渲染 if (settings.cache && cache[viewPath]) { return cache[viewPath].call(options.scope, options); } // 没有缓存首次渲染调用 ejs.compile 进行编译 const tpl = await fs.readFile(viewPath, ‘utf8’); const fn = ejs.compile(tpl, { filename: viewPath, _with: settings._with, compileDebug: settings.debug && settings.compileDebug, debug: settings.debug, delimiter: settings.delimiter }); // 将 ejs.compile 得到的模板解析函数缓存起来 if (settings.cache) { cache[viewPath] = fn; } return fn.call(options.scope, options);}显然,koa-ejs 模板的模板缓存是完全自己实现的,并没有在调用 ejs.compile 方法时传入的 option 参数内将用户设置的 cache 参数传递过去而使用 ejs 模块提供的 cache 能力。但是偏偏项目在模板内又直接使用了 ejs 模块注入的 include 方法进行模板间的调用,产生的结果就是只缓存了主模板,而主模板使用 include 调用别的模板还是会重新进行编译解析,进而造成压测下还是存在大量重复的模板编译动作导致 QPS 升不上去。再次找到了问题的根源,为了验证是否是 koa-ejs 模块本身的 bug,我们在项目中将其渲染逻辑稍作更改:const fn = ejs.compile(tpl, { filename: viewPath, _with: settings._with, compileDebug: settings.debug && settings.compileDebug, debug: settings.debug, delimiter: settings.delimiter, // 将用户设置的 cache 参数传递给 ejs 而使用到其提供的缓存能力 cache: settings.cache});然后打包后进行压测,此时单机 QPS 从 600 提升至 4000 左右,基本达到了上线前的性能预期,为了确认压测下是否还有模板的编译动作,我们继续在 Node.js 性能平台 上抓取压测期间 3 分钟的 CPU Profile:可以看到上述对 koa-ejs 模板进行优化后,ejs.compile 确实消失了,而压测期间不再有大量重复且耗费 CPU 的编译动作后,应用整体的性能比最开始有了 20 倍左右的提升。文中 koa-ejs 模块缓存问题已经在 4.1.2 版本(包含)之后被修复了,详情可以见 cache include file,如果大家使用的 koa-ejs 版本 >= 4.1.2 就可以放心使用。结尾CPU Profile 本质上以可读的方式反映给开发者运行时的 JavaScript 代码执行频繁程度,除了在线上进程出现负载很高时能够用来定位问题代码之外,它在我们上线前进行性能压测和对应的性能调优时也能提供巨大的帮助。这里需要注意的是:仅当进程 CPU 负载非常高的时候去抓取得到的 CPU Profile 才能真正反馈给我们问题所在。在这个源自真实生产的案例中,我们也可以看到,正确和不正确地去使用 Node.js 开发应用其前后运行效率能达到二十倍的差距,Node.js 作为一门服务端技术栈发展至今日,其本身能够提供的性能是毋庸置疑的,绝大部分情况下执行效率不佳是由我们自身的业务代码或者三方库本身的 Bug 引起的,Node.js 性能平台 则可以帮助我们以比较方便的方式找出这些 Bug。本文作者:奕钧阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 2, 2019 · 2 min · jiezi

寻找 K8s 1.14 Release 里的“蚌中之珠”

摘要: K8s 1.14 发布了,Release Note那么长,我们该从何读起?本文由张磊、心贵、临石、徙远、衷源、浔鸣等同学联合撰写。Kubernetes 1.14.0 Release 已经于3月25日正式发布。相信你也已经注意到,相比于1.13 和 1.12 版本,这次发布包含的重要变更非常多,其对应的 Release Note 的篇幅长度也创下了“新高”。面对这样一份“海量信息”的 Release Note,我们该如何从这份文档里进行高效的信息过滤和挖掘,帮助团队更精准、快速的梳理出这次发布最主要的技术脉络呢?在本篇文章中,我们将 1.14 的Release Note 按照主题进行了重新归纳和梳理,按照类别对重要变更进行了技术剖析和讨论。希望这种“分类解读”的方式,能够帮助大家更好的理解 1.14 这个发布的核心内容。Windows Node 正式生产可用随着1.14的发布,Kubernetes 对windows节点的生产级支持无疑是一个重要的里程碑。具体来说,1.14 版本针对 Windows 做了大量增强;Pod:Pod内支持readiness和liveness探针;支持进程隔离和volume共享的多容器Pod;Pod支持原生configmap和sercret;Pod支持emptyDir;支持对Pod进行资源配额等;但是像优雅删除、Termination message、Privileged Containers、HugePages、Pod驱逐策略等部分特性还未在1.14版本提供;Service:支持服务环境变量提供DNS解析;支持NodePort、ClusterIP、LoadBalancer、Headless service;暂不支持Pod的hostnetwork模式;常规 Workload controller:RS、deployment、statefulset、daemonset、job、cronjob均支持windows容器;除此之外,支持Pod和container维度的metrics、HPA、“kubectl exec”、调度抢占、resource quotas、CNI 网络支持等多种特性让windows workload更加云原生;由于windows的特殊兼容性,目前 host OS的版本必须和容器镜像OS版本一致,1.14版本支持win server 2019;未来版本中会考虑使用Hyper-V隔离机制来解决版本兼容性问题。而伴随着 Windows 容器的生态正在慢慢壮大,能够在生产级别支持 Windows 节点的容器服务开始见诸各大云厂商。阿里云容器服务(ACK)近期已经推出了 Windows Container 的支持,提供了linux/windows应用混合部署的统一管理能力。参见:Support for Windows Nodes is Graduating to Stable (#116 )本地持久化数据卷(Local PV) 正式可用长期以来,能够让 Kubernetes 直接用宿主机的本地存储设备(比如:本地 SSD 硬盘)来提供持久化数据卷(即:Local PV 功能),一直是社区里非常强烈的一个诉求。这个原因很容易理解:相对于远程存储(网络存储),Local PV 在时延性、易用性、稳定性和费用上具有独特的优势,尤其是对于相关特性比较敏感的应用,如数据库应用和搜索引擎应用来说,有着重要的意义。而在 1.14 中,Local PV 终于正式宣布 GA,为云上的持久化存储选择增加了一种重要的的可能。不过,必须要明确的是, 选择使用 Local PV,也意味着用户必须自己承担一些潜在的风险,这包括:目前社区的开源方案无法动态创建卷调度器需要由额外的调度逻辑工作,以确保调度的节点可以分配出足够的磁盘容量容错性差,如果pod正在运行的宿主机宕机或者磁盘发生异常,那么它的持久化卷里的信息可能丢失第一个问题,可以通过比如阿里云的 local-volume-provisioner 实现本地 SSD Nvme实例自动创建数据卷来解决,但对于容错性和健壮性的问题,就是比较棘手的了。参见:Durable Local Storage Management is Now GA (#121)Pod 优先级与抢占机制稳定可用Kubernetes 里的任务优先级(priority)和抢占机制(preemption)的目的十分明确:保证高优先级的任务可以在需要的时候通过抢占低优先级任务的方式得到运行。这其中,优先级定义了一个Pod在集群中的重要程度,这个重要程度体现且仅体现在两个地方:(1)高优先级的Pod在调度阶段更容易被优先调度(K8s采用队列调度模型),注意这里并不保证高优先级Pod永远被优先调度,实际影响调度顺序的因素有很多;(2)在集群整体负载较高时,如果出现高优先级Pod无法被调度的情况(集群中没有满足条件的Node供Pod运行),K8s会启动抢占机制,通过抢占已经运行的低优先级的Pod的方式,让高优先级的Pod可以运行起来。抢占机制便是在这里引入的。抢占机制指当调度器发现某个Pod(如Pod-A)无法在集群中找到合适的节点部署时(所有节点Predicates全部失败),会试图通过删除一些优先级低于Pod-A的Pod来“腾出空间”部署Pod-A,这样Pod-A就可以被调度了。这样一个“看似简单”的需求在分布式环境中实施起来有很多细节,例如:如何决定删除哪个节点的哪些Pod、如何保证为Pod-A腾出的空间不被其它Pod占用、如何保证Pod-A不被饿死(Starvation)、如何处理有亲和性需求的Pod调度约束、是否需要支持跨节点Preemption以支持某些特定的约束(例如某Failure Domain的反亲和约束)等等。这些内容,可以参见:Pod Priority and Preemption in Kubernetes (#564) 你一定要知道什么是 Pod Ready++在 1.14 版本之前,Kubernetes 判断一个Pod是否Ready,就是检查这个Pod的容器是否全部正常运行。但是这里有个问题,那就是容器或者说里面的主进程Ready,并不一定意味着这个应用副本就一定是就绪的。为了确认Pod确实可以正常可用,我们希望给它增加一些外部指标(比如,该 Pod 需要的 Service,DNS,存储等服务全部就绪),来反应这个Pod是否“真正”Ready。这个特性,就是1.14 里一个叫做“Pod Readiness Gates”、也叫做 Pod Ready ++ 的特性。它为pod的“Ready 状态” 提供了一个非常强大的扩展点。需要注意的是,用户需要编写一个外部控制器(Controller)来为这个Pod Readiness Gates 字段对应的指标设置值。参见:Pod Ready++ (#580) Kubernetes 原生应用管理能力1.14之后,Kubernetes 项目本身开始具备了原生的应用管理能力,这其中最重要的一个功能,就是 Kustomize。Kustomize 允许用户从一个基础 YAML 文件,通过 overlay 的方式生成最终部署应用所需的 YAML 文件,而不是像 Helm 那样通过字符串替换的方式来直接修改基础 YAML 文件(模板)。这样,在一个用户通过 overlay 生成新的 YAML 文件的同时,其他用户可以完全不受影响的使用任何一个基础 YAML 或者某一层生成出来的 YAML 。这使得每一个用户,都可以通过 fork/modify/rebase 这样 Git 风格的流程来管理海量的 YAML 文件。这种 PATCH 的思想跟 Docker 镜像是非常类似的,它既规避了“字符串替换”对 YAML 文件的入侵,也不需要用户学习蹩脚的 DSL 语法(比如 Lua)。在1.14之后,Kustomize 已经成为了 kubectl 的一个内置命令。不难看到,Kubernetes 社区正在探索一种 Helm 之外的、更加 Kubernetes 原生的应用管理方法。具体效果如何,我们不妨拭目以待。参见:Added Kustomize as a subcommand in kubectl (#73033, @Liujingfang1)用户友好度进一步提升随着大家对Kubernetes越来越熟悉,对kubectl依赖也越来越强烈,需求也越来越多样化。而在 1.14 中,kubectl 着重在以下几个方面,提升用户体验,加强对日常运维能力的支持。之前 kubectl cp 操作每次只能 copy 一个文件,没办法使用通配符拷贝一批文件,非常不方便。在1.14中,蚂蚁金服的工程师提交了一个拷贝操作的通配符功能,方便对容器中的文件进行操作。参见:#72641以往,用户通常无法方便的知道自己被管理员通过 RBAC 配置的权限到底有哪些。而从v1.14开始,用户可以通过 kubectl auth can-i –list –namespace=ns1 来查看自己在 ns1 这个namespace下可以访问哪些资源 (比如Pod,Service等),并有哪些操作的权限(比如Get,List,Patch,Delete等)了。参见:#64820Kubernetes 用户需要删除的API 资源,往往分散在多个namespace中,删除非常不方便。在v1.14新版本中,用户终于可以借助于 kubectl delete xxx –all-namespaces 来进行统一的删除操作了(这里 XXX 可以是Pod,Services,Deployment,自定义的CRD等等),并且还可以配合 -l 和 –field-selector 可以更精确地删除满足特定条件的资源。参见:#73716稳定性进一步提升和之前每个版本一样,Kubernetes 的新版本发布对稳定性和可靠性增强的关注一直是重中之重,下面我们列举出一些值得注意的修复和升级。在做Pod驱逐时,会优先尝试使用优雅删除模式,而不是暴力删除etcd内的Pod数据。这个修复能够使被驱逐的 Pod更加优雅的退出。参见:#72730Kubelet要重建Pod的容器时,如果旧容器是unknown状态,现在Kubelet会首先尝试Stop容器。这避免了一个 Pod的同一个容器申明会有多个实例同时运行的风险。参见:#73802在大规模集群中,节点因为个别Pod使用了大量磁盘 IO,可能会导致节点频繁的在Ready/NotReady状态之间变化。这种状态会引起大规模的、不可预期的 Pod Eviction,导致线上故障。蚂蚁金服的工程师针对 Docker 环境下的这个问题提交了修复,建议大家也排查一下其它运行时的集群里是否有同样的问题。参见:#74389当 Kubelet在压力较大情况下,可能会发生 Kubelet 的Pod 生命周期事件消费频次弱于事件产生频次,导致负责这个事件的 Channel 被占满,这种情况持续一段时间后会直接导致Kubelet 死锁。阿里巴巴的工程师针对修这个问题提交了修复。参见:#72709大规模场景下的性能提升与优化在 Kubernetes 的主干功能日趋稳定之后,社区已经开始更多的关注大规模场景下 Kubernetes 项目会暴露出来的各种各样的问题。在v1.14中,Kubernetes 社区从面向最终用户的角度做出了很多优化,比如:kubectl 在实现中会顺序遍历 APIServer暴露出的全部资源的Group/Version/Kind,直到查找到需要处理的资源。这种遍历方式导致了用户在大规模集群下使用 kubectl 的性能体验受到很大影响。在v1.14版本中,kubectl的顺序遍历行为终于改为了并行,极大地提升了kubectl的使用体验(经过测试,性能指标提升了10倍以上)。参见: #73345在 1.14 中,APIServer 里的一个重要变更,是对单次 PATCH 请求内容里的操作个数做出了限制,不能超过10000个,否则就不处理此请求。这样做的目的,是防止 APIServer 因为处理海量的甚至是恶意PATCH 请求导致整个集群瘫痪。这也其实也是社区的 CVE-2019-1002100 主要的修复方法。参见:#74000Kubernetes 的 Aggregated API允许 k8s 的开发人员编写一个自定义服务,并把这个服务注册到k8s的 API 里面像原生 API 一样使用。在这个情况下,APIServer 需要将用户自定义 API Spec 与原生的 API Spec 归并起来,这是一个非常消耗CPU 的性能痛点。而在v1.14中,社区大大优化了这个操作的速率,极大地提升了APIServer 归并 Spec 的性能(提升了不止十倍)。参见:#71223文中相关链接一览Release Note :https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.14.md#kubernetes-v114-release-notesSupport for Windows Nodes is Graduating to Stable (#116 ):https://github.com/kubernetes/enhancements/issues/116Durable Local Storage Management is Now GA (#121):https://github.com/kubernetes/enhancements/issues/121#issuecomment-457396290Pod Priority and Preemption in Kubernetes (#564) :https://github.com/kubernetes/enhancements/issues/564Pod Ready++ (#580) :https://github.com/kubernetes/enhancements/issues/580Added Kustomize as a subcommand in kubectl (#73033, @Liujingfang1):https://github.com/kubernetes/kubernetes/pull/73033https://github.com/Liujingfang1用户友好度:72641:https://github.com/kubernetes/kubernetes/pull/7264164820:https://github.com/kubernetes/kubernetes/pull/6482073716:https://github.com/kubernetes/kubernetes/pull/73716稳定性:72730:https://github.com/kubernetes/kubernetes/pull/7273073802:https://github.com/kubernetes/kubernetes/pull/7380274389:https://github.com/kubernetes/kubernetes/pull/7438972709:https://github.com/kubernetes/kubernetes/pull/72709大规模场景下的性能提升与优化:73345:https://github.com/kubernetes/kubernetes/pull/7334574000:https://github.com/kubernetes/kubernetes/pull/7400071223:https://github.com/kubernetes/kubernetes/pull/71223阿里云和CNCF联合开发推出的免费公开课,讲解以Kubernetes主体的云原生技术知识。一线技术专家精心打造,期待各位的学习反馈。 更多课程信息可以一步:官宣|《CNCF x Alibaba 云原生技术公开课》即将重磅上线本文作者:木环阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 1, 2019 · 2 min · jiezi

Node.js 应用故障排查手册 —— 冗余配置传递引发的内存溢出

楔子前面一小节我们以一个真实的压测案例来给大家讲解如何利用 Node.js 性能平台 生成的 CPU Profile 分析来进行压测时的性能调优。那么与 CPU 相关的问题相比,Node.js 应用中由于不当使用产生的内存问题是一个重灾区,而且这些问题往往都是出现在生产环境下,本地压测都难以复现,实际上这部分内存问题也成为了很多的 Node.js 开发者不敢去将 Node.js 这门技术栈深入运用到后端的一大阻碍。本节将以一个开发者容易忽略的生产内存溢出案例,来展示如何借助于性能平台实现对线上应用 Node.js 应用出现内存泄漏时的发现、分析、定位问题代码以及修复的过程,希望能对大家有所启发。本书首发在 Github,仓库地址:https://github.com/aliyun-node/Node.js-Troubleshooting-Guide,云栖社区会同步更新。最小化复现代码因为内存问题相对 CPU 高的问题来说比较特殊,我们直接从问题排查的描述可能不如结合问题代码来看比较直观,因此在这里我们首先给出了最小化的复现代码,大家运行后结合下面的分析过程应该能更有收获,样例基于 Egg.js:如下所示:‘use strict’;const Controller = require(’egg’).Controller;const DEFAULT_OPTIONS = { logger: console };class SomeClient { constructor(options) { this.options = options; } async fetchSomething() { return this.options.key; }}const clients = {};function getClient(options) { if (!clients[options.key]) { clients[options.key] = new SomeClient(Object.assign({}, DEFAULT_OPTIONS, options)); } return clients[options.key];}class MemoryController extends Controller { async index() { const { ctx } = this; const options = { ctx, key: Math.random().toString(16).slice(2) }; const data = await getClient(options).fetchSomething(); ctx.body = data; }}module.exports = MemoryController;然后在 app/router.js 中增加一个 Post 请求路由:router.post(’/memory’, controller.memory.index);造成问题的 Post 请求 Demo 这里也给出来,如下所示:‘use strict’;const fs = require(‘fs’);const http = require(‘http’);const postData = JSON.stringify({ // 这里的 body.txt 可以放一个比较大 2M 左右的字符串 data: fs.readFileSync(’./body.txt’).toString()});function post() { const req = http.request({ method: ‘POST’, host: ’localhost’, port: ‘7001’, path: ‘/memory’, headers: { ‘Content-Type’: ‘application/json’, ‘Content-Length’: Buffer.byteLength(postData) } }); req.write(postData); req.end(); req.on(’error’, function (err) { console.log(12333, err); });}setInterval(post, 1000);最后我们在启动完成最小化复现的 Demo 服务器后,再运行这个 Post 请求的客户端,1s 发起一个 Post 请求,在平台控制台可以看到堆内存在一直增加,如果我们按照本书工具篇中的 Node.js 性能平台使用指南 - 配置合适的告警 一节中配置了 Node.js 进程堆内存告警的话,过一会就会收到平台的 短信/邮件 提醒。问题排查过程收到性能平台的进程内存告警后,我们登录到控制台并且进入应用首页,找到告警对应实例上的问题进程,然后参照工具篇中的 Node.js 性能平台使用指南 - 内存泄漏 中的方法抓取堆快照,并且点击 分析 按钮查看 AliNode 定制后的分解结果展示:这里默认的报表页面顶部的信息含义已经提到过了,这里不再重复,我们重点来看下这里的可疑点信息:提示有 18 个对象占据了 96.38% 的堆空间,显然这里就是我们需要进一步查看的点。我们可以点击 对象名称 来看到这18 个 system/Context 对象的详细内容:这里进入的是分别以这 18 个 system/Context 为根节点起始的支配树视图,因此展开后可以看到各个对象的实际内存占用情况,上图中显然问题集中在第一个对象上,我们继续展开查看:很显然,这里真正吃掉堆空间的是 451 个 SomeClient 实例,面对这样的问题我们需要从两个方面来判断这是否真的是内存异常的问题:当前的 Node.js 应用在正常的逻辑下,是否单个进程需要 451 个 SomeClient 实例如果确实需要这么多 SomeClient 实例,那么每个实例占据 1.98MB 的空间是否合理对于第一个判断,在对应的实际生产面临的问题中,经过代码逻辑的重新确认,我们的应用确实需要这么多的 Client 实例,显然此时排查重点集中在每个实例的 1.98MB 的空间占用是否合理上,假如进一步判断还是合理的,这意味着 Node.js 默认单进程 1.4G 的堆上限在这个场景下是不适用的,需要我们来通过启动 Flag 调大堆上限。正是基于以上的判断需求,我们继续点开这些 SomeClient 实例进行查看:这里可以很清晰的看到,这个 SomeClient 本身只有 1.97MB 的大小,但是下面的 options 属性对应的 Object@428973 对象一个就占掉了 1.98M,进一步展开这个可疑的 Object@428973 对象可以看到,其 ctx 属性对应的 Object@428919 对象正是 SomeClient 实例占据掉如此大的对空间的根本原因所在!我们可以点击其它的 SomeClient 实例,可以看到每一个实例均是如此,此时我们需要结合代码,判断这里的 options.ctx 属性挂载到 SomeClient 实例上是否也是合理的,点击此问题 Object 的地址:进入到这个 Object 的关系图中:Search 展示的视图不同于 Dom 结果图,它实际上展示的是从堆快中解析出来的原始对象关系图,所以边信息是一定会存在的,靠边名称和对象名称,我们比较容易判断对象在代码中的位置。但是在这个例子中,仅仅依靠以 Object@428973 为起始点的内存原始关系图,看不到很明确的代码位置,毕竟不管是 Object.ctx 还是 Object.key 都是相当常见的 JavaScript 代码关系,因此我们继续点击 Retainer 视图:得到如下信息:这里的 Retainer 信息和 Chrome Devtools 中的 Retainer 含义是一样的,它代表了节点在堆内存中的原始父引用关系,正如本文的内存问题案例中,仅靠可疑点本身以及其展开无法可靠地定位到问题代码的情况下,那么展开此对象的 Retainer 视图,可以看到它的父节点链路可以比较方便的定位到问题代码。这里我们显然可以通过在 Retainer 视图下的问题对象父引用链路,很方便地找到代码中创建此对象的代码:function getClient(options) { if (!clients[options.key]) { clients[options.key] = new SomeClient(Object.assign({}, DEFAULT_OPTIONS, options)); } return clients[options.key];}结合看 SomeClient 的使用,看到用于初始化的 options 参数中实际上只是用到了其 key 属性,其余的属于冗余的配置信息,无需传入。代码修复与确认知道了原因后修改起来就比较简单了,单独生成一个 SomeClient 使用的 options 参数,并且仅将需要的数据从传入的 options 参数上取过来以保证没有冗余信息即可:function getClient(options) { const someClientOptions = Object.assign({ key: options.key }, DEFAULT_OPTIONS); if (!clients[options.key]) { clients[options.key] = new SomeClient(someClientOptions); } return clients[options.key];}重新发布后运行,可以到堆内存下降至只有几十兆,至此 Node.js 应用的内存异常的问题完美解决。结尾本节中也比较全面地给大家展示了如何使用 Node.js 性能平台 来排查定位线上应用内存泄漏问题,其实严格来说本次问题并不是真正意义上的内存泄漏,像这种配置传递时开发者图省事直接全量 Assign 的场景我们在写代码时或多或少时都会遇到,这个问题带给我们的启示还是:当我们去编写一个公共组件模块时,永远不要去相信使用者的传入参数,任何时候都应当只保留我们需要使用到的参数继续往下传递,这样可以避免掉很多问题。本文作者:奕钧 阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 1, 2019 · 2 min · jiezi

蚂蚁金服开源 SOFAJRaft:生产级 Java Raft 算法库

什么是 SOFAJRaft?SOFAJRaft 是一个基于 Raft 一致性算法的生产级高性能 Java 实现,支持 MULTI-RAFT-GROUP,适用于高负载低延迟的场景。 使用 SOFAJRaft 你可以专注于自己的业务领域,由 SOFAJRaft 负责处理所有与 Raft 相关的技术难题,并且 SOFAJRaft 非常易于使用,你可以通过几个示例在很短的时间内掌握它。SOFAJRaft 是从百度的 braft 移植而来,做了一些优化和改进,感谢百度 braft 团队开源了如此优秀的 C++ Raft 实现。基础知识:分布式共识算法 (Consensus Algorithm)如何理解分布式共识?多个参与者某一件事一致 :一件事,一个结论已达成一致的结论,不可推翻有哪些分布式共识算法?Paxos:被认为是分布式共识算法的根本,其他都是其变种,但是 Paxos 论文中只给出了单个提案的过程,并没有给出复制状态机中需要的 multi-paxos 的相关细节的描述,实现 Paxos 具有很高的工程复杂度(如多点可写,允许日志空洞等)。Zab:被应用在 Zookeeper 中,业界使用广泛,但没有抽象成通用的 library。Raft:以容易理解著称,业界也涌现出很多 Raft 实现,比如大名鼎鼎的 etcd, braft, tikv 等。什么是 Raft?Raft 是一种更易于理解的分布式共识算法,核心协议本质上还是师承 Paxos 的精髓,不同的是依靠 Raft 模块化的拆分以及更加简化的设计,Raft 协议相对更容易实现。模块化的拆分主要体现在:Raft 把一致性协议划分为 Leader 选举、MemberShip 变更、日志复制、Snapshot 等几个几乎完全解耦的模块。更加简化的设计则体现在:Raft 不允许类似 Paxos 中的乱序提交、简化系统中的角色状态(只有 Leader、Follower、Candidate 三种角色)、限制仅 Leader 可写入、使用随机化的超时时间来设计 Leader Election 等等。特点:Strong Leader系统中必须存在且同一时刻只能有一个 Leader,只有 Leader 可以接受 Clients 发过来的请求;Leader 负责主动与所有 Followers 通信,负责将“提案”发送给所有 Followers,同时收集多数派的 Followers 应答;Leader 还需向所有 Followers 主动发送心跳维持领导地位(保持存在感)。一句话总结 Strong Leader: “你们不要 BB! 按我说的做,做完了向我汇报!"。另外,身为 Leader 必须保持一直 BB(heartbeat) 的状态,否则就会有别人跳出来想要 BB 。Raft 中的基本概念篇幅有限,这里只对 Raft 中的几个概念做一个简单介绍,详细请参考 Raft paper。Raft-node 的 3 种角色/状态Follower:完全被动,不能发送任何请求,只接受并响应来自 Leader 和 Candidate 的 Message,每个节点启动后的初始状态一定是 Follower;Leader:处理所有来自客户端的请求,以及复制 Log 到所有 Followers;Candidate:用来竞选一个新 Leader (Candidate 由 Follower 触发超时而来)。Message 的 3 种类型RequestVote RPC:由 Candidate 发出,用于发送投票请求;AppendEntries (Heartbeat) RPC:由 Leader 发出,用于 Leader 向 Followers 复制日志条目,也会用作 Heartbeat (日志条目为空即为 Heartbeat);InstallSnapshot RPC:由 Leader 发出,用于快照传输,虽然多数情况都是每个服务器独立创建快照,但是Leader 有时候必须发送快照给一些落后太多的 Follower,这通常发生在 Leader 已经丢弃了下一条要发给该Follower 的日志条目(Log Compaction 时清除掉了) 的情况下。任期逻辑时钟时间被划分为一个个任期 (term),term id 按时间轴单调递增;每一个任期的开始都是 Leader 选举,选举成功之后,Leader 在任期内管理整个集群,也就是 “选举 + 常规操作”;每个任期最多一个 Leader,可能没有 Leader (spilt-vote 导致)。本图出自《Raft: A Consensus Algorithm for Replicated Logs》什么是 SOFAJRaft?SOFAJRaft 是一个基于 Raft 一致性算法的生产级高性能 Java 实现,支持 MULTI-RAFT-GROUP,适用于高负载低延迟的场景。 使用 SOFAJRaft 你可以专注于自己的业务领域,由 SOFAJRaft 负责处理所有与 Raft 相关的技术难题,并且 SOFAJRaft 非常易于使用,你可以通过几个示例在很短的时间内掌握它。SOFAJRaft 是从百度的 braft 移植而来,做了一些优化和改进,感谢百度 braft 团队开源了如此优秀的 C++ Raft 实现。SOFAJRaft 整体功能&性能优化功能支持1.Leader election:Leader 选举,这个不多说,上面已介绍过 Raft 中的 Leader 机制。2.Log replication and recovery:日志复制和日志恢复。Log replication 就是要保证已经被 commit 的数据一定不会丢失,即一定要成功复制到多数派。Log recovery 包含两个方面:Current term 日志恢复:主要针对一些 Follower 节点重启加入集群或者是新增 Follower 节点后如何追日志;Prev term 日志恢复:主要针对 Leader 切换前后的日志一致性。3.Snapshot and log compaction:定时生成 snapshot,实现 log compaction 加速启动和恢复,以及 InstallSnapshot 给 Followers 拷贝数据,如下图:本图出自《In Search of an Understandable Consensus Algorithm》4.Membership change:用于集群线上配置变更,比如增加节点、删除节点、替换节点等。5.Transfer leader:主动变更 leader,用于重启维护,leader 负载平衡等。6.Symmetric network partition tolerance:对称网络分区容忍性。如上图 S1 为当前 leader,网络分区造成 S2 不断增加本地 term,为了避免网络恢复后 S2 发起选举导致正在良心 工作的 leader step-down,从而导致整个集群重新发起选举,SOFAJRaft 中增加了 pre-vote 来避免这个问题的发生。SOFAJRaft 中在 request-vote 之前会先进行 pre-vote(currentTerm + 1, lastLogIndex, lastLogTerm),多数派成功后才会转换状态为 candidate 发起真正的 request-vote,所以分区后的节点,pre-vote 不会成功,也就不会导致集群一段时间内无法正常提供服务。7.Asymmetric network partition tolerance:非对称网络分区容忍性。如上图 S1 为当前 leader,S2 不断超时触发选主,S3 提升 term 打断当前 lease,从而拒绝 leader 的更新。在 SOFAJRaft 中增加了一个 tick 的检查,每个 follower 维护一个时间戳记录下收到 leader 上数据更新的时间(也包括心跳),只有超过 election timeout 之后才允许接受 request-vote 请求。8.Fault tolerance:容错性,少数派故障不影响系统整体可用性,包括但不限于:机器掉电强杀应用慢节点(GC, OOM 等)网络故障其他各种奇葩原因导致 raft 节点无法正常工作9.Workaround when quorate peers are dead:多数派故障时,整个 grop 已不具备可用性,安全的做法是等待多数节点恢复,只有这样才能保证数据安全;但是如果业务更加追求系统可用性,可以放弃数据一致性的话,SOFAJRaft 提供了手动触发 reset_peers 的指令以迅速重建整个集群,恢复集群可用。10.Metrics:SOFAJRaft 内置了基于 Metrics 类库的性能指标统计,具有丰富的性能统计指标,利用这些指标数据可以帮助用户更容易找出系统性能瓶颈。11.Jepsen:除了几百个单元测试以及部分 chaos 测试之外, SOFAJRaft 还使用 jepsen 这个分布式验证和故障注入测试框架模拟了很多种情况,都已验证通过:随机分区,一大一小两个网络分区随机增加和移除节点随机停止和启动节点随机 kill -9 和启动节点随机划分为两组,互通一个中间节点,模拟分区情况随机划分为不同的 majority 分组性能优化除了功能上的完整性,SOFAJRaft 还做了很多性能方面的优化,这里有一份 KV 场景(get/put)的 Benchmark 数据, 在小数据包,读写比例为 9:1,保证线性一致读的场景下,三副本最高可以达到 40w+ 的 ops。这里挑重点介绍几个优化点:Batch: 我们知道互联网两大优化法宝便是 Cache 和 Batch,SOFAJRaft 在 Batch 上花了较大心思,整个链路几乎都是 Batch 的,依靠 disruptor 的 MPSC 模型批量消费,对整体性能有着极大的提升,包括但不限于:批量提交 task批量网络发送本地 IO batch 写入要保证日志不丢,一般每条 log entry 都要进行 fsync 同步刷盘,比较耗时,SOFAJRaft 中做了合并写入的优化。批量应用到状态机需要说明的是,虽然 SOFAJRaft 中大量使用了 Batch 技巧,但对单个请求的延时并无任何影响,SOFAJRaft 中不会对请求做延时的攒批处理。Replication pipeline:流水线复制,通常 Leader 跟 Followers 节点的 Log 同步是串行 Batch 的方式,每个 Batch 发送之后需要等待 Batch 同步完成之后才能继续发送下一批(ping-pong),这样会导致较长的延迟。SOFAJRaft 中通过 Leader 跟 Followers 节点之间的 pipeline 复制来改进,非常有效降低了数据同步的延迟, 提高吞吐。经我们测试,开启 pipeline 可以将吞吐提升 30% 以上,详细数据请参照 Benchmark。Append log in parallel:在 SOFAJRaft 中 Leader 持久化 log entries 和向 Followers 发送 log entries 是并行的。Fully concurrent replication:Leader 向所有 Follwers 发送 Log 也是完全相互独立和并发的。Asynchronous:SOFAJRaft 中整个链路几乎没有任何阻塞,完全异步的,是一个完全的 callback 编程模型。ReadIndex:优化 Raft read 走 Raft log 的性能问题,每次 read,仅记录 commitIndex,然后发送所有 peers heartbeat 来确认 Leader 身份,如果 Leader 身份确认成功,等到 appliedIndex >= commitIndex,就可以返回 Client read 了,基于 ReadIndex Follower 也可以很方便的提供线性一致读,不过 commitIndex 是需要从 Leader 那里获取,多了一轮 RPC;关于线性一致读文章后面会详细分析。Lease Read:SOFAJRaft 还支持通过租约 (lease) 保证 Leader 的身份,从而省去了 ReadIndex 每次 heartbeat 确认 Leader 身份,性能更好,但是通过时钟维护 lease 本身并不是绝对的安全(时钟漂移问题,所以 SOFAJRaft 中默认配置是 ReadIndex,因为通常情况下 ReadIndex 性能已足够好。SOFAJRaft 设计Node:Raft 分组中的一个节点,连接封装底层的所有服务,用户看到的主要服务接口,特别是 apply(task)用于向 raft group 组成的复制状态机集群提交新任务应用到业务状态机。存储:上图靠下的部分均为存储相关。Log 存储,记录 Raft 用户提交任务的日志,将日志从 Leader 复制到其他节点上。LogStorage 是存储实现,默认实现基于 RocksDB 存储,你也可以很容易扩展自己的日志存储实现;LogManager 负责对底层存储的调用,对调用做缓存、批量提交、必要的检查和优化。Metadata 存储,元信息存储,记录 Raft 实现的内部状态,比如当前 term、投票给哪个节点等信息。Snapshot 存储,用于存放用户的状态机 snapshot 及元信息,可选:SnapshotStorage 用于 snapshot 存储实现;SnapshotExecutor 用于 snapshot 实际存储、远程安装、复制的管理。状态机StateMachine:用户核心逻辑的实现,核心是 onApply(Iterator) 方法, 应用通过 Node#apply(task) 提交的日志到业务状态机;FSMCaller:封装对业务 StateMachine 的状态转换的调用以及日志的写入等,一个有限状态机的实现,做必要的检查、请求合并提交和并发处理等。复制Replicator:用于 Leader 向 Followers 复制日志,也就是 Raft 中的 AppendEntries 调用,包括心跳存活检查等;ReplicatorGroup:用于单个 Raft group 管理所有的 replicator,必要的权限检查和派发。RPC:RPC 模块用于节点之间的网络通讯RPC Server:内置于 Node 内的 RPC 服务器,接收其他节点或者客户端发过来的请求,转交给对应服务处理;RPC Client:用于向其他节点发起请求,例如投票、复制日志、心跳等。KV Store:KV Store 是各种 Raft 实现的一个典型应用场景,SOFAJRaft 中包含了一个嵌入式的分布式 KV 存储实现(SOFAJRaft-RheaKV)。SOFAJRaft Group单个节点的 SOFAJRaft-node 是没什么实际意义的,下面是三副本的 SOFAJRaft 架构图:SOFAJRaft Multi Group单个 Raft group 是无法解决大流量的读写瓶颈的,SOFAJRaft 自然也要支持 multi-raft-group。SOFAJRaft 实现细节解析之高效的线性一致读什么是线性一致读? 所谓线性一致读,一个简单的例子就是在 t1 的时刻我们写入了一个值,那么在 t1 之后,我们一定能读到这个值,不可能读到 t1 之前的旧值 (想想 Java 中的 volatile 关键字,说白了线性一致读就是在分布式系统中实现 Java volatile 语义)。如上图 Client A、B、C、D 均符合线性一致读,其中 D 看起来是 stale read,其实并不是,D 请求横跨了 3 个阶段,而读可能发生在任意时刻,所以读到 1 或 2 都行。重要:接下来的讨论均基于一个大前提,就是业务状态机的实现必须是满足线性一致性的,简单说就是也要具有 Java volatile 的语义。要实现线性一致读,首先我们简单直接一些,是否可以直接从当前 Leader 节点读?仔细一想,这显然行不通,因为你无法确定这一刻当前的 “Leader” 真的是 Leader,比如在网络分区的情况下,它可能已经被推翻王朝却不自知。最简单易懂的实现方式:同 “写” 请求一样,“读” 请求也走一遍 Raft 协议 (Raft Log)。本图出自《Raft: A Consensus Algorithm for Replicated Logs》这一定是可以的,但性能上显然不会太出色,走 Raft Log 不仅仅有日志落盘的开销,还有日志复制的网络开销,另外还有一堆的 Raft “读日志” 造成的磁盘占用开销,这在读比重很大的系统中通常是无法被接受的。ReadIndex Read这是 Raft 论文中提到的一种优化方案,具体来说:Leader 将自己当前 Log 的 commitIndex 记录到一个 Local 变量 ReadIndex 里面;接着向 Followers 发起一轮 heartbeat,如果半数以上节点返回了对应的 heartbeat response,那么 Leader 就能够确定现在自己仍然是 Leader (证明了自己是自己);Leader 等待自己的状态机执行,直到 applyIndex 超过了 ReadIndex,这样就能够安全的提供 Linearizable Read 了,也不必管读的时刻是否 Leader 已飘走 (思考:为什么等到 applyIndex 超过了 ReadIndex 就可以执行读请求?);Leader 执行 read 请求,将结果返回给 Client。通过ReadIndex,也可以很容易在 Followers 节点上提供线性一致读:Follower 节点向 Leader 请求最新的 ReadIndex;Leader 执行上面前 3 步的过程(确定自己真的是 Leader),并返回 ReadIndex 给 Follower;Follower 等待自己的 applyIndex 超过了 ReadIndex;Follower 执行 read 请求,将结果返回给 Client。(SOFAJRaft 中可配置是否从 Follower 读取,默认不打开)ReadIndex小结:相比较于走 Raft Log 的方式,ReadIndex 省去了磁盘的开销,能大幅度提升吞吐,结合 SOFAJRaft 的 batch + pipeline ack + 全异步机制,三副本的情况下 Leader 读的吞吐可以接近于 RPC 的吞吐上限;延迟取决于多数派中最慢的一个 heartbeat response,理论上对于降低延时的效果不会非常显著。Lease ReadLease Read 与 ReadIndex 类似,但更进一步,不仅省去了 Log,还省去了网络交互。它可以大幅提升读的吞吐也能显著降低延时。基本的思路是 Leader 取一个比 election timeout 小的租期(最好小一个数量级),在租约期内不会发生选举,这就确保了 Leader 不会变,所以可以跳过 ReadIndex 的第二步,也就降低了延时。可以看到 Lease Read 的正确性和时间是挂钩的,因此时间的实现至关重要,如果时钟漂移严重,这套机制就会有问题。实现方式:定时 heartbeat 获得多数派响应,确认 Leader 的有效性 (在 SOFAJRaft 中默认的 heartbeat 间隔是 election timeout 的十分之一);在租约有效时间内,可以认为当前 Leader 是 Raft Group 内的唯一有效 Leader,可忽略 ReadIndex 中的 heartbeat 确认步骤(2);Leader 等待自己的状态机执行,直到 applyIndex 超过了 ReadIndex,这样就能够安全的提供 Linearizable Read 了 。在 SOFAJRaft 中发起一次线性一致读请求的代码展示:// KV 存储实现线性一致读public void readFromQuorum(String key, AsyncContext asyncContext) { // 请求 ID 作为请求上下文传入 byte[] reqContext = new byte[4]; Bits.putInt(reqContext, 0, requestId.incrementAndGet()); // 调用 readIndex 方法, 等待回调执行 this.node.readIndex(reqContext, new ReadIndexClosure() { @Override public void run(Status status, long index, byte[] reqCtx) { if (status.isOk()) { try { // ReadIndexClosure 回调成功,可以从状态机读取最新数据返回 // 如果你的状态实现有版本概念,可以根据传入的日志 index 编号做读取 asyncContext.sendResponse(new ValueCommand(fsm.getValue(key))); } catch (KeyNotFoundException e) { asyncContext.sendResponse(GetCommandProcessor.createKeyNotFoundResponse()); } } else { // 特定情况下,比如发生选举,该读请求将失败 asyncContext.sendResponse(new BooleanCommand(false, status.getErrorMsg())); } } });}应用场景Leader 选举;分布式锁服务,比如 Zookeeper,在 SOFAJRaft 中的 RheaKV 模块提供了完整的分布式锁实现;高可靠的元信息管理,可直接基于 SOFAJRaft-RheaKV 存储;分布式存储系统,如分布式消息队列、分布式文件系统、分布式块系统等等。使用案例RheaKV:基于 SOFAJRaft 实现的嵌入式、分布式、高可用、强一致的 KV 存储类库。AntQ Streams QCoordinator:使用 SOFAJRaft 在 Coordinator 集群内做选举、使用 SOFAJRaft-RheaKV 做元信息存储等功能。Schema Registry:高可靠 schema 管理服务,类似 kafka schema registry,存储部分基于 SOFAJRaft-RheaKV。SOFA 服务注册中心元信息管理模块:IP 数据信息注册,要求写数据达到各个节点一致,并且在少数派节点挂掉时保证不影响数据正常存储。实践一、基于 SOFAJRaft 设计一个简单的 KV Store二、基于 SOFAJRaft 的 RheaKV 的设计功能名词PD全局的中心总控节点,负责整个集群的调度,不需要自管理的集群可不启用 PD (一个 PD 可管理多个集群,基于 clusterId 隔离)。Store集群中的一个物理存储节点,一个 Store 包含一个或多个 Region。Region最小的 KV 数据单元,每个 Region 都有一个左闭右开的区间 [startKey, endKey), 可根据请求流量/负载/数据量大小等指标自动分裂以及自动副本搬迁。特点嵌入式强一致性自驱动自诊断, 自优化, 自决策以上几点(尤其2、3) 基本都是依托于 SOFAJRaft 自身的功能来实现,详细介绍请参考 SOFAJRaft 文档 。致谢感谢 braft、etcd、tikv 贡献了优秀的 Raft 实现,SOFAJRaft 受益良多。招聘蚂蚁金服中间件团队持续在寻找对于基础中间件(如消息、数据中间件以及分布式计算等)以及下一代高性能面向实时分析的时序数据库等方向充满热情的小伙伴加入,有意者请联系 boyan@antfin.com。参考资料SOFAJRaft 源码SOFAJRaft 详细文档RaftRaft paperRaft: A Consensus Algorithm for Replicated LogsPaxos/Raft:分布式一致性算法原理剖析及其在实战中的应用braft 文档线性一致性和 RaftStrong consistency modelsetcd raft 设计与实现《一》MetricsjepsenBenchmark本文作者:s潘潘阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 15, 2019 · 5 min · jiezi

分布式系统:一致性模型

分布式系统中一个重要的问题就是数据复制,数据复制一般是为了增强系统的可用性或提高性能。而实现数据复制的一个主要难题就是保持各个副本的一致性。本文首先讨论数据复制的场景中一致性模型如此重要的原因,然后讨论一致性模型的含义,最后分析常用的一致性模型。为什么需要一致性模型数据复制主要的目的有两个:可用性和性能。首先数据复制可以提高系统的可用性。在保持多副本的情况,有一个副本不可用,系统切换到其他副本就会恢复。常用的 MySQL 主备同步方案就是一个典型的例子。另一方面,数据复制能够提供系统的性能。当分布式系统需要在服务器数量和地理区域上进行扩展时,数据复制是一个相当重要的手段。有了多个数据副本,就能将请求分流;在多个区域提供服务时,也能通过就近原则提高客户端访问数据的效率。常用的 CDN 技术就是一个典型的例子。但是数据复制是要付出代价的。数据复制带来了多副本数据一致性的问题。一个副本的数据更新之后,其他副本必须要保持同步,否则数据不一致就可能导致业务出现问题。因此,每次更新数据对所有副本进行修改的时间以及方式决定了复制代价的大小。全局同步与性能实际上是矛盾的,而为了提高性能,往往会采用放宽一致性要求的方法。因此,我们需要用一致性模型来理解和推理在分布式系统中数据复制需要考虑的问题和基本假设。什么是一致性模型首先我们要定义一下一致性模型的术语:1. 数据存储:在分布式系统中指分布式共享数据库、分布式文件系统等。2. 读写操作:更改数据的操作称为写操作(包括新增、修改、删除),其他操作称为读操作。下面是一致性模型的定义:一致性模型本质上是进程与数据存储的约定:如果进程遵循某些规则,那么进程对数据的读写操作都是可预期的。上面的定义可能比较抽象,我们用常见的强一致性模型来通俗的解释一下:在线性一致性模型中,进程对一个数据项的读操作,它期待数据存储返回的是该数据在最后一次写操作之后的结果。这在单机系统里面很容易实现,在 MySQL 中只要使用加锁读的方式就能保证读取到数据在最后一次写操作之后的结果。但在分布式系统中,因为没有全局时钟,导致要精确定义哪次写操作是最后一次写操作是非常困难的事情,因此产生了一系列的一致性模型。每种模型都有效限制了在对一个数据项执行读操作所应该返回的值。举个例子:假设记录值 X 在节点 M 和 N 上都有副本,当客户端 A 修改了副本 M 上 X 的值,一段时间之后,客户端 B 从 N 上读取 X 的值,此时一致性模型会决定客户端 B 是否能够读取到 A 写入的值。一致性模型主要可以分为两类:能够保证所有进程对数据的读写顺序都保持一致的一致性模型称为强一致性模型,而不能保证的一致性模型称为弱一致性模型。强一致性模型线性一致性(Linearizable Consistency)线性一致性也叫严格一致性(Strict Consistency)或者原子一致性(Atomic Consistency),它的条件是:1. 任何一次读都能读取到某个数据最近的一次写的数据。2. 所有进程看到的操作顺序都跟全局时钟下的顺序一致。线性一致性是对一致性要求最高的一致性模型,就现有技术是不可能实现的。因为它要求所有操作都实时同步,在分布式系统中要做到全局完全一致时钟现有技术是做不到的。首先通信是必然有延迟的,一旦有延迟,时钟的同步就没法做到一致。当然不排除以后新的技术能够做到,但目前而言线性一致性是无法实现的。顺序一致性(Sequential Consistency)顺序一致性是 Lamport(1979)在解决多处理器系统共享存储器时首次提出来的。参考我之前写的文章《分布式系统:Lamport 逻辑时钟》。它的条件是:任何一次读写操作都是按照某种特定的顺序。所有进程看到的读写操作顺序都保持一致。首先我们先来分析一下线性一致性和顺序一致性的相同点在哪里。他们都能够保证所有进程对数据的读写顺序保持一致。线性一致性的实现很简单,就按照全局时钟(可以简单理解为物理时钟)为参考系,所有进程都按照全局时钟的时间戳来区分事件的先后,那么必然所有进程看到的数据读写操作顺序一定是一样的,因为它们的参考系是一样的。而顺序一致性使用的是逻辑时钟来作为分布式系统中的全局时钟,进而所有进程也有了一个统一的参考系对读写操作进行排序,因此所有进程看到的数据读写操作顺序也是一样的。那么线性一致性和顺序一致性的区别在哪里呢?通过上面的分析可以发现,顺序一致性虽然通过逻辑时钟保证所有进程保持一致的读写操作顺序,但这些读写操作的顺序跟实际上发生的顺序并不一定一致。而线性一致性是严格保证跟实际发生的顺序一致的。弱一致性模型因果一致性(Causal Consistency)因果一致性是一种弱化的顺序一致性模型,因为它将具有潜在因果关系的事件和没有因果关系的事件区分开了。那么什么是因果关系?如果事件 B 是由事件 A 引起的或者受事件 A 的影响,那么这两个事件就具有因果关系。举个分布式数据库的示例,假设进程 P1 对数据项 x 进行了写操作,然后进程 P2 先读取了 x,然后对 y 进行了写操作,那么对 x 的读操作和对 y 的写操作就具有潜在的因果关系,因为 y 的计算可能依赖于 P2 读取到 x 的值(也就是 P1 写的值)。另一方面,如果两个进程同时对两个不同的数据项进行写操作,那么这两个事件就不具备因果关系。无因果关系的操作称为并发操作。这里只是简单陈述了一下,深入的分析见我之前写的文章《分布式系统:向量时钟》。因果一致性的条件包括:1. 所有进程必须以相同的顺序看到具有因果关系的读写操作。2. 不同进程可以以不同的顺序看到并发的读写操作。下面我们来分析一下为什么说因果一致性是一种弱化的顺序一致性模型。顺序一致性虽然不保证事件发生的顺序跟实际发生的保持一致,但是它能够保证所有进程看到的读写操作顺序是一样的。而因果一致性更进一步弱化了顺序一致性中对读写操作顺序的约束,仅保证有因果关系的读写操作有序,没有因果关系的读写操作(并发事件)则不做保证。也就是说如果是无因果关系的数据操作不同进程看到的值是有可能是不一样,而有因果关系的数据操作不同进程看到的值保证是一样的。最终一致性(Eventual Consistency)最终一致性是更加弱化的一致性模型,因果一致性起码还保证了有因果关系的数据不同进程读取到的值保证是一样的,而最终一致性只保证所有副本的数据最终在某个时刻会保持一致。从某种意义上讲,最终一致性保证的数据在某个时刻会最终保持一致就像是在说:“人总有一天会死”一样。实际上我们更加关心的是:1. “最终”到底是多久?通常来说,实际运行的系统需要能够保证提供一个有下限的时间范围。2. 多副本之间对数据更新采用什么样的策略?一段时间内可能数据可能多次更新,到底以哪个数据为准?一个常用的数据更新策略就是以时间戳最新的数据为准。由于最终一致性对数据一致性的要求比较低,在对性能要求高的场景中是经常使用的一致性模型。以客户端为中心的一致性(Client-centric Consistency)前面我们讨论的一致性模型都是针对数据存储的多副本之间如何做到一致性,考虑这么一种场景:在最终一致性的模型中,如果客户端在数据不同步的时间窗口内访问不同的副本的同一个数据,会出现读取同一个数据却得到不同的值的情况。为了解决这个问题,有人提出了以客户端为中心的一致性模型。以客户端为中心的一致性为单一客户端提供一致性保证,保证该客户端对数据存储的访问的一致性,但是它不为不同客户端的并发访问提供任何一致性保证。举个例子:客户端 A 在副本 M 上读取 x 的最新值为 1,假设副本 M 挂了,客户端 A 连接到副本 N 上,此时副本 N 上面的 x 值为旧版本的 0,那么一致性模型会保证客户端 A 读取到的 x 的值为 1,而不是旧版本的 0。一种可行的方案就是给数据 x 加版本标记,同时客户端 A 会缓存 x 的值,通过比较版本来识别数据的新旧,保证客户端不会读取到旧的值。以客户端为中心的一致性包含了四种子模型:1. 单调读一致性(Monotonic-read Consistency):如果一个进程读取数据项 x 的值,那么该进程对于 x 后续的所有读操作要么读取到第一次读取的值要么读取到更新的值。即保证客户端不会读取到旧值。2. 单调写一致性(Monotonic-write Consistency):一个进程对数据项 x 的写操作必须在该进程对 x 执行任何后续写操作之前完成。即保证客户端的写操作是串行的。3. 读写一致性(Read-your-writes Consistency):一个进程对数据项 x 执行一次写操作的结果总是会被该进程对 x 执行的后续读操作看见。即保证客户端能读到自己最新写入的值。4. 写读一致性(Writes-follow-reads Consistency):同一个进程对数据项 x 执行的读操作之后的写操作,保证发生在与 x 读取值相同或比之更新的值上。即保证客户端对一个数据项的写操作是基于该客户端最新读取的值。总结数据复制导致了一致性的问题,为了保持副本的一致性可能会严重地影响性能,唯一的解决办法就是放松一致性的要求。通过一致性模型我们可以理解和推理在分布式系统中数据复制需要考虑的问题和基本假设,便于结合具体的业务场景做权衡。每种模型都有效地限制了对一个数据项执行度操作应返回的值。通常来说限制越少的模型越容易应用,但一致性的保证就越弱。参考资料《分布式系统原理与范型》Distributed systems for fun and profitConsistency_model本文作者:肖汉松阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 13, 2019 · 1 min · jiezi

Dubbo Mesh 在闲鱼生产环境中的落地实践

本文作者至简曾在 2018 QCon 上海站以《Service Mesh 的本质、价值和应用探索》为题做了一次分享,其中谈到了 Dubbo Mesh 的整体发展思路是“借力开源、反哺开源”,也讲到了 Service Mesh 在阿里巴巴的发路径将经历以下三大阶段:撬动做透价值渗透实现技术换代Dubbo Mesh 在闲鱼生产环境的落地,分享的是以多语言为撬动点的阶段性总结。文章首发于「QCon」,阿里巴巴中间件授权转载。闲鱼场景的特点闲鱼采用的编程语言是 Dart,思路是通过 Flutter 和 Dart 实现 iOS、Android 两个客户端以及 Dart 服务端,以“三端一体”的思路去探索多端融合的高效软件开发模式。更多细节请参考作者同事陈新新在 2018 QCon 上海站的主题分享《Flutter & Dart 三端一体化开发》。本文将关注三端中的 Dart 服务端上运用 Dubbo Mesh 去解耦 Dubbo RPC 框架的初步实践成果。Dart 服务端是一个服务调用胶水层,收到来自接入网关发来的 HTTP 请求后,通过 C++ SDK 调用集团广泛提供的 Dubbo 服务完成业务逻辑处理后返回结果。然而,C++ SDK 的功能与 Java 的存在一定的差距,比如缺失限流降级等对于保障大促稳定性很重要的功能。从长远发展的角度,闲鱼团队希望通过 Dubbo Mesh 能屏蔽或简化不同技术栈使用中间件(比如,RPC、限流降级、监控、配置管理等)的复杂性。这一诉求的由来,是闲鱼团队通过几个月的实践,发现在 Dart 语言中通过 C++ SDK 逐个接入不同中间件存在定制和维护成本高的问题。值得说明,所谓的“定制”是因为 C++ SDK 的能力弱于 Java SDK 而要做补齐所致。Dart 服务端自身的业务逻辑很轻且在一些场景下需要调用 20 多次 Dubbo 服务,这对于 Dubbo Mesh 的技术挑战会显得更大。在 Dubbo Mesh 还没在生产环境落地过而缺乏第一手数据的情形下,其性能是否完全满足业务的要求是大家普遍关心的。架构与实现图中的虚框代表了一个Pouch容器(也可以是一台物理机或虚拟机)。左边两个容器部署了 Dubbo Mesh,剩下最右边的则没有。目前 Dubbo Mesh 主要包含 Bonder、Pilot、Envoy 三个进程,以及被轻量化的 Thin SDK。其中:Envoy 承担了数据平面的角色,所有 mesh 流量将由它完成服务发现与路由而中转。Envoy 由 Lyft 初创且目前成为了 CNCF 的毕业项目,我们在之上增加了对 Dubbo 协议的支持,并将之反哺到了开源社区(还有不少代码在等待社区 review 通过后才能进到 GitHub 的代码仓库)。Pilot 和 Bonder 共同承担控制平面的角色,实现服务注册、进程拉起与保活、集群信息和配置推送等功能。Pilot 进程的代码源于开源 Istio 的 pilot-discovery 组件,我们针对阿里巴巴集团环境做了一定的改造(比如,与Nacos进行适配去访问服务注册中心),且采用下沉到应用机器的方式进行部署,这一点与开源的集群化部署很不一样。背后的思考是,Pilot 的集群化部署对于大规模集群信息的同步是非常大的一个挑战,今天开源的 Istio 并不具备这一能力,未来需要 Nacos 团队对之进行增强,在没有完全准备好前通过下沉部署的方式能加速 Service Mesh 的探索历程。Thin SDK 是 Fat SDK 经过裁剪后只保留了对 Dubbo 协议进行编解码的能力。为了容灾,当 Thin SDK 位于 Consumer 侧时增加了一条容灾通道,细节将在文后做进一步展开。数据链路全部采用单条 TCP 长连接,这一点与非 mesh 场景是一致的。Pilot 与 Envoy 两进程间采用的是 gRPC/xDS 协议进行通讯。图中同时示例了 mesh 下的 Consumer 能同时调用 mesh 下的服务(图中以 www.mesh.com 域名做示例)和非 mesh 下的服务(图中以 www.non-mesh.com 域名做示例)。闲鱼落地的场景为了避免对 20 多个依赖服务进行改造,流量走的是 mesh 下的 Consumer 调用非 mesh 下的 Provider 这一形式,读者可以理解为图中最左边的容器部署的是 Dart 服务端,它将调用图中最右边容器所提供的服务去实现业务逻辑。容灾从 Dubbo Mesh 下的 Provider 角度,由于通常是集群化部署的,当一个 Provider 出现问题(无论是 mesh 组件引起的,还是 Provider 自身导致的)而使服务无法调通时,Consumer 侧的 Envoy 所实现的重试机制会将服务请求转发到其他 Provider。换句话说,集群化部署的 Provider 天然具备一定的容灾能力,在 mesh 场景下无需特别处理。站在 Dubbo Mesh 的 Consumer 立场,如果完全依赖 mesh 链路去调用 Provider,当 mesh 链路出现问题时则会导致所有服务都调不通,这往往会引发业务可用性问题。为此,Thin SDK 中提供了一个直连 Provider 的机制,只不过实现方式比 Fat SDK 轻量了许多。Thin SDK 会定期从 Envoy 的 Admin 接口获取所依赖服务的 Provider 的 IP 列表,以备检测到 mesh 链路存在问题时用于直连。比如,针对每一个依赖的服务获取最多 10 个 Provider 的 IP 地址,当 mesh 链路不通时以 round robin 算法向这些 Provider 直接发起调用。由于容灾是针对 mesh 链路的短暂失败而准备的,所以 IP 地址的多少并不是一个非常关键的点。Thin SDK 检测 mesh 链路的异常大致有如下场景:与 Envoy 的长连接出现中断,这是 Envoy 发生 crash 所致。所发起的服务调用收到 No Route Found、No Healthy Upstream 等错误响应。优化在闲鱼落地 Dubbo Mesh 的初期我们走了一个“弯路”。具体说来,最开始为了快速落地而采用了 Dubbo over HTTP 1.1/2 的模式,也即,将 Dubbo 协议封装在 HTTP 1.1/2 的消息体中完成服务调用。这一方案虽然能很好地享受 Envoy 已完整支持 HTTP 1.1/2 协议而带来的开发工作量少的好处,但性能测试表明其资源开销并不符合大家的预期。体现于,不仅 Consumer 侧使用 mesh 后带来更高的 CPU 开销,Provider 侧也因为要提供通过 HTTP 1.1/2 进行调用的能力而导致多出 20% 的 CPU 开销且存在改造工作。最终,我们回到让 Envoy 原生支持 Dubbo 协议的道路上来。Envoy 支持 Dubbo 协议经历了两大阶段。第一个阶段 Envoy 与上游的通讯并没有采用单条长连接,使得 Provider 的 CPU 开销因为多连接而存在不可忽视的递增。第二个阶段则完全采用单条长连接,通过多路复用的模式去除了前一阶段给 Provider 所带去的额外 CPU 开销。Dubbo Mesh 在闲鱼预发环境上线进行性能与功能验证时,我们意外地发现,Istio 原生 Pilot 的实现会将全量集群信息都推送给处于 Consumer 侧的 Envoy(Provider 侧没有这一问题),导致 Pilot 自身的 CPU 开销过大,而频繁的全量集群信息推送也使得 Envoy 不时会出现 CPU 负荷毛刺并遭受没有必要的内存开销。为此,我们针对这一问题做了集群信息按需加载的重大改造,这一优化对于更大规模与范围下运用 Dubbo Mesh 具有非常重要的意义。优化的大致思路是:Thin SDK 提供一个 API 供 Consumer 的应用在初始化时调用,周知其所需调用的服务列表。Thin SDK 通过 HTTP API 将所依赖的服务列表告诉 Bonder,Bonder 将之保存到本地文件。Envoy 启动时读取 Bonder 所保存的服务列表文件,将之当作元信息转给 Pilot。Pilot 向 Nacos 只订阅服务列表中的集群信息更新消息且只将这些消息推送给 Envoy。监控可观测性(observability)是 Service Mesh 非常重要的内容,在服务调用链路上插入了 Envoy 的情形下,愈加需要通过更强的监控措施去治理其上的所有微服务。Dubbo Mesh 的监控方案并没有使用 Istio/Mixer 这样的设计,而是沿用了阿里巴巴集团内部的方式,即信息由各进程以日志的形式输出,然后通过日志采集程序将之送到指定的服务端进行后期加工并最终展示于控制台。目前 Dubbo Mesh 通过 EagleEye 去跟踪调用链,通过ARMS去展示其他的监控信息。性能评估为了评估 Dubbo Mesh 的性能能否满足闲鱼业务的需要,我们设计了如下图所示的性能比对测试方案。其中:测试机器是阿里巴巴集团生产环境中的 3 台 4 核 8G 内存的 Pouch 容器。蓝色方框代表的是进程。测试数据全部从部署了 DartServer 和 Envoy 两进程的测试机 2 上获得。性能数据分别在非 mesh(图中红色数据流)和 mesh(图中蓝色数据流)两个场景下获得。显然,Mesh 场景下的服务流量多了 Envoy 进程所带来的一跳。DartServer 收到来自施压的 Loader 进程所发来的一个请求后,将发出 21 次到 Provider 进程的 RPC 调用。在评估 Dubbo Mesh 的性能时,这 21 次是串行发出的(下文列出的测试数据是在这一情形下收集的),实际闲鱼生产环境上线时考虑了进行并行发送去进一步降低整体调用时延(即便没有 mesh 时,闲鱼的业务也是这样实现的)。Provider 进程端并没有部署 Envoy 进程。这省去了初期引入 Dubbo Mesh 对 Provider 端的改造成本,降低了落地的工作量和难度。设计测试方案时,我们与闲鱼的同学共创了如何回答打算运用 Dubbo Mesh 的业务方一定会问的问题,即“使用 Dubbo Mesh 后对 RT(Response Time)和 CPU 负荷的影响有多大”。背后的动机是,业务方希望通过 RT 这一指标去了解 Dubbo Mesh 对用户体验的影响,基于 CPU 负荷的增长去掌握运用新技术所引发的成本。面对这一问题通常的回答是“在某某 QPS 下,RT 增加了 x%,CPU 负荷增加了 y%”,但这样的回答如果不进行具体测试是无法给出的(会出现“鸡和蛋的问题”)。因为每个业务的天然不同使得一个完整请求的 RT 会存在很大的差别(从几毫秒到几百毫秒),而实现业务逻辑所需的计算量又最终决定了机器的 CPU 负荷水平。基于此,我们设计的测试方案在于评估引入 Dubbo Mesh 后,每经过一跳 Envoy 所引入的 RT 和 CPU 增量。当这一数据出来后,业务方完全可以基于自己业务的现有数据去计算出引入 Dubbo Mesh 后的而掌握大致的影响情况。显然,背后的逻辑假设是“Envoy 对于每个 Dubbo 服务调用的计算量是一样的”,事实也确实如此。测试数据以下是 Loader 发出的请求在并发度为 100 的情形下所采集的数据。表中:Envoy 的 QPS 是 Loader 的 21 倍,原因在上面测试方案部分有交代。“单跳”的数据是从“21 跳合计”直接除以 21 所得,其严谨性值得商榷,但用于初步评估仍具参考价值(有数据比没有数据强)。“整机负荷”代表了在 mesh 场景下测试机器 2 上 DartServer 和 Envoy 两进程的 CPU 开销总和。测试表明,CPU 负荷高时 Envoy 带来的单跳 RT 增幅更大(比如表中 Loader 的 QPS 是 480 时)。给出整机负荷是为了提醒读者关注引入 mesh 前业务的正常单机水位,以便更为客观地评估运用 Dubbo Mesh 将带来的潜在影响。“CPU 负荷增幅”是指 CPU 增加的幅度。由于测试机是 4 核的,所以整机的 CPU 负荷是 400。从表中数据来看,随着机器整体负荷的增加“CPU 负荷增幅”在高段存在波动,这与 RT 在高段的持续增大存在相关,从 RT 在整体测试中完全符合线性增长来看整体数据合理。当然, 后面值得深入研究数据背后的隐藏技术细节以便深入优化。线上数据Dubbo Mesh 正式生产环境上线后,我们通过对上线前后的某接口的 RT 数据进行了全天的比对,以便大致掌握 mesh 化后的影响。2019-01-14 该接口全面切成了走 Dubbo Mesh,我们取的是 2019-01-20 日的数据。图中蓝色是 mesh 化后的 RT 表现(RT 均值 3.3),而橙色是 mesh 化前的 RT 表现(RT 均值 3.27,取的是 2019-01-13 的数据)。由于线上每天的环境都有所不同,要做绝对的比较并不可能。但通过上面的比较不难看出,mesh 化前后对于整体 RT 的影响相当的小。当整体 RT 小于 5 毫秒是如此,如果整体 RT 是几十、几百毫秒则影响就更小。为了帮助更全面地看待业务流量的波动特点,下面分别列出了两天非 mesh(2019-01-06 和 2019-01-13)和两天 mesh(2019-01-20 和 2019-01-23)的比对数据。总之,生产环境上的数据表现与前面性能评估方案下所获得的测试数据能很好地吻合。洞见Dubbo Mesh 在闲鱼生产环境的落地实践让我们收获了如下的洞见:服务发现的时效性是 Service Mesh 技术的首要关键。 以集群方式提供服务的情形下(这是分布式应用的常态),因为应用发布而导致集群中机器状态的变更如何及时准确地推送到数据平面是极具挑战的问题。对于阿里巴巴集团来说,这是 Nacos 团队致力于解决的问题。开源版本的 Istio 能否在生产环境中运用于大规模分布式应用也首先取决于这一能力。频繁的集群信息推送,将给控制平面和数据平面都带去负荷扰动,如何通过技术手段控制好扰动是需要特别关注的,对于数据平面来说编程语言的“确定性”(比如,没有 VM、没有 GC)在其中将起到不可忽视的作用。数据平面的软件实现最大程度地减少内存分配与释放将显著地改善性能。有两大举措可以考虑:逻辑与数据相分离。 以在 Envoy 中实现 Dubbo 协议为例,Envoy 每收到一个 RPC 请求都会动态地创建 fitler 去处理,一旦实现逻辑与数据相分离,filter 的创建对于每一个 worker 线程有且只有一次,通过这一个 filter 去处理所有的 RPC 请求。使用内存池。 Envoy 的实现中基本没有用到内存池,如果采用内存池对分配出来的各种 bufffer 通过链表进行缓存,这将省去大量的内存分配与释放而改善性能。再则,对于处理一个 RPC 请求而多次分散分配的动作整合成集中一次性分配也是值得运用的优化技巧。数据平面的 runtime profiling 是关键技术。 Service Mesh 虽然对业务代码没有侵入性,但对服务流量具有侵入性,如何在出现业务毛刺的情形下,快速地通过 runtime profiling 去发现问题或自证清白是非常值得关注的点。心得一年不到的探索旅程,让团队更加笃定“借力开源,反哺开源”的发展思路。随着对 Istio 和 Envoy 实现细节的更多掌握,团队很强列地感受到了走“站在巨人的肩膀上”发展的道路少走了很多弯路,除了快速跟进业界的发展步伐与思路,还将省下精力去做更有价值的事和创新。此外,Istio 和 Envoy 两个开源项目的工程质量都很高,单元测试等质量保证手段是日常开发工作中的基础环节,而我们也完全采纳了这些实践。比如,内部搭建了 CI 环境、每次代码提交将自动触发单元测试、代码经过 code review 并完成单元测试才能入库、自动化性能测试等。展望在 2019 年接下来的日子,我们将着手:与 Sentinel 团队形成合力,将 Sentinel 的能力纳入到 Dubbo Mesh 中补全对 HTTP 和 Dubbo 协议的限流、降级和熔断能力。在阿里巴巴集团大范围 Kubernetes(Sigma 3.1)落地的背景下,与兄弟团队探索更加优雅的服务流量透明拦截技术方案。迎合 Serverless 的技术发展趋势,深化通过 Dubbo Mesh 更好地轻量化应用,以及基于 Dubbo Mesh 对服务流量的天然敏感性去更好地实现 auto-scaling。在产品的易用性和工程效率方面踏实进取。未来,我们将及时与读者分享阿里巴巴集团在 Service Mesh 这一新技术领域的探索成果,也期待与大家有更多的互动交流。本文作者:至简,阿里巴巴中间件高级技术专家,是阿里巴巴集团 Service Mesh 方向的重要参与者和推动者。关于 Dubbo Mesh 的首次公开分享本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 11, 2019 · 3 min · jiezi

前台性能优化总结

场景某天上完课,走在路上,突然想起来,一个企业中,计算机量可能很大,500到2000左右。分组时,可能会很耗时,前台能不能承受的住。模拟加了1000台计算机,前台直接炸,将近4秒才能出来,并且选择的时候也很卡。学习了很多数据量多时性能优化的方法,目前前台经过一系列优化,能保证在Chrome浏览器环境、1000台测试机的条件下,,2s内完成页面渲染。优化组件介绍前台多选使用的是NZ为我们提供的多选框组件,该组件要求输入信息必须满足一定格式。/** * NZ 多选框 * nz-checkbox-group * 数据格式规范 */export class NzCheckBoxSpec<T> { label: string; value: T; checked: boolean;}所以,计算机多选组件整体逻辑如下:获取外部传入的计算机列表(考虑到默认选中的问题)。从后台查询所有的计算机列表。根据所有计算机构造NZ多选框组件的输入,checked一项根据当前遍历的计算机是否在传入的计算机列表中判断。性能问题看着问题不大:this.hostService.getAllHosts().subscribe((hosts) => { this.hostListValues = []; // 使用主机信息构造多选框绑定数据 hosts.forEach((host) => { this.hostListValues.push({ label: host.name, value: host, checked: HostCheckboxComponent.existIn(host, this._hostList) }); });});查阅相关资料,原来一直都用的有问题,forEach虽然很好使,但是是性能最低的一种循环。性能测试我亲自写测试代码测试三种循环方式的性能(数据量2000,内部执行同样业务操作):实验次数 / 实验方法forEachfor offor1654ms524ms517ms2604ms571ms563ms3550ms506ms508ms4621ms495ms522ms5506ms562ms470ms平均时间587ms531.6ms516ms我这里只是部分少量数据,结果具有随机性,不具有普遍性。反正这里我是总结出几点:当数据量少的时候,我使用forEach,方便。当数据量大的时候,我是用for循环,性能略好。当需要在循环中return时,使用for of,方便。学习过程中还查到了Duff’s Device,这是目前性能最好的循环方式。有人测试,Duff’s Device需要达到30万的数据量才能显示其算法高效的性能。组件的错误使用最开始,组件是这样使用的。<app-host-checkbox [hostList]=“hostGroup.hostList” (hostCheck)=“bindHostList($event)"></app-host-checkbox>看着没啥毛病啊,把计算机组的hostList传进去,然后一个输出事件,绑定hostCheck事件,当选中的计算机有改动的时候就调用bindHostList()方法。bindHostList(hostList: Array<Host>): void { this.hostGroup.hostList = hostList;}注意看这张图:页面把hostGroup的hostList传给组件。组件初始化,当用户选择的时候,再把选中的值回传给hostGroup。hostGroup的hostList变了,又传给组件了。所以,组件初始化执行了两次,本来for循环就已经很耗时了,更何况执行两次。之前测试数据少没发现。新建临时变量,该变量只用于传输入的hostList。总结在校学习过的东西,我们总是很久以后用到。如果不是华软项目,也用不到学过的计算机网络,没有大数据量,学的算法复杂度也用不上。当编码不是问题的时候,我们开始考虑设计与用户体验。

March 8, 2019 · 1 min · jiezi

阿里云TSDB在大数据集群监控中的方案与实战

目前大部分的互联网企业基本上都有搭建自己的大数据集群,为了能更好让我们的大数据集群更加高效安全的工作,一个优秀的监控方案是必不可少的;所以今天给大家带来的这篇文章就是讲阿里云TSDB在上海某大型互联网企业中的大数据集群监控方案中的实战案例,希望能为感兴趣的同学提供一些帮助。背景和需求阿里云时序时空数据库 (原阿里云时间序列数据库, 简称 TSDB) 是一种高性能,低成本,稳定可靠的在线时序数据库服务;提供高效读写,高压缩比存储、时序数据插值及聚合计算,广泛应用于物联网(IoT)设备监控系统 ,企业能源管理系统(EMS),生产安全监控系统,电力检测系统等行业场景。 TSDB 提供百万级时序数据秒级写入,高压缩比低成本存储、预降采样、插值、多维聚合计算,查询结果可视化功能;解决由于设备采集点数量巨大,数据采集频率高,造成的存储成本高,写入和查询分析效率低的问题。Elastic MapReduce(EMR)是阿里云提供的一种大数据处理的系统解决方案。EMR基于开源生态,包括 Hadoop、Spark、Kafka、Flink、Storm等组件,为企业提供集群、作业、数据管理等服务的一站式企业大数据平台。上海某大型互联网企业是阿里云EMR的Top客户,在阿里云上购买的EMR实例有近千台hadoop机器,这些机器目前除了阿里云本身ECS级别的监控以外,没有一套成熟的这对大数据的监控运维告警系统,对大数据业务来讲存在很大的风险。现在客户的需求是对购买的EMR集群做监控和告警,单台有20多个监控指标,采集精度可以根据客户需求调整,另外还要求对原有的业务无侵入,不需要业务层做太多的配置重启类操作。痛点和挑战该大型互联网企业客户最初计划采用的是Prometheus作为监控和告警解决方案,并且基于Prometheus的监控方案也在该企业内部其他系统应用了。这里提到了Prometheus,就多说几句。随着业内基于Kubernetes的微服务的盛行,其生态兼容的开源监控系统Prometheus也逐渐被大家热捧。Prometheus是一个开源监控系统,它前身是SoundCloud的监控系统,在2016年继Kurberntes之后,加入了Cloud Native Computing Foundation。目前许多公司和组织开始使用Prometheus,该项目的开发人员和用户社区非常活跃,越来越多的开发人员和用户参与到该项目中。下图就是prometheus方案的架构:这个方案在实际部署过程中发现Prometheus在存储和查询上存在性能的问题,主要是Prometheus本身采用的local storage方案在大数据量下的扩展性写入查询性能存在瓶颈。另外在这个方案的适配性不强,要改很多参数重启才行,这对于线上正在运行的业务来说,是不可接受的,需要重新设计解决方案。阿里云TSDB解决方案监控和告警整体上来说包括三个环节:1.采集指标2.存储指标3.查询告警因此基本方案就可以简化为:采集工具 + 数据库 + 查询告警。其中,数据库可以通过阿里云TSDB来解决存储和查询上的性能问题,查询告警可以通过成熟的开源工具Grafana。由于该互联网企业客户的要求对原有的业务无侵入,不需要业务层做太多的配置重启类操作,因此解决方案的调研就重点落在了采集工具的调研上了。对于采集工具而言,结合该互联网企业客户已经部署的Prometheus,且阿里云TSDB兼容开源时序数据库OpenTSDB的写入和查询协议,因此从减少成本和工作量的角度来看,可以考虑的方式是有两种:1. 使用Prometheus官方提供的开源的OpenTSDB Adapter 对接原生的Prometheus ,实现数据写入到TSDB。基本架构为:这种方案和该互联网企业客户的开发同学沟通后,发现满足不了对业务无侵入,不重启的需求,因此选择放弃;2. 采用其他开源工具,实现数据采集写入到TSDB。开源社区较为活跃,已经提供了不少开源的采集工具,因此我门评估了以下几个开源的采集工具:Collectd,https://collectd.orgtelegraf, https://github.com/influxdata/telegraf* statsd, https://github.com/etsy/statsdtcollector, http://opentsdb.net/docs/build/html/user_guide/utilities/tcollector.html从开发语言、部署方式以及是否支持定制开发等角度,我们初步选择tcollector作为采集工具。tcollector是一个客户端程序,用来收集本机的数据,并将数据发送到OpenTSDB。tcollector可以为你做下面几件事:运行所有的采集者并收集数据;完成所有发送数据到TSDB的连接管理任务;不必在你写的每个采集者中嵌入这些代码;是否删除重复数据;处理所有有线协议,以后今后的改进;因此,基于tcollector + TSDB + Grafana的监控告警架构如下,其中tcollector以http协议从目标结点上拉取监控指标,并以http的OpenTSDB协议将指标推送至阿里云TSDB。这个方案在不修改tcollector源码的基础上,能够满足客户对hadoop的监控。但是在PoC后,客户增加了对EMR实例中其他大数据组件的监控需求,如Hive, Spark, Zookeeper, HBase, Presto, Flink, azkaban, kafka, storm等。经过我们调研,tcollector对于这些组件的支持程度如下:原生支持:hbase;需定制化开发,不重启实例:Hive, Spark, Zookeeper;需定制化开发,需重启实例:Flink, azkaban, kafka, storm;经过一定工作量的制化开发,基于tcollector的方案基本可以满足用户的需求。最终我们在该互联网企业客户的EMR大数据集群的监控告警方案架构为:tcollector非常简单易部署,可以简单高效地完成了客户的需求。而且配置部署时,可以不用区分大数据组件的角色,解决了之前开源采集工具需要针对不同角色,来手动配置并启动相应插件的问题。至此,TSDB完美得解决了该互联网企业客户大数据集群监控接入TSDB的案例,让TSDB在迈向完善生态的路上更进一步了。另外值得一提的是,为了解决目前广泛使用的Prometheus开源系统在大量时序数据的存储、写入和查询存在性能瓶颈问题,阿里云TSDB也已经开始兼容了Prometheus生态,并且已经在多个客户场景进行了实战。后面我们会推出针对Prometheus的系列文章,对Prometheus感兴趣或者已经是Prometheus用户但是遇到性能问题的同学可以持续关注我们。阿里云时序时空数据库TSDB 1元购!立即体验:https://promotion.aliyun.com/ntms/act/tsdbtry.html?spm=5176.149792.775960.1.dd9e34e2zgsuEM&wh_ttid=pc本文作者:焦先阅读原文本文为云栖社区原创内容,未经允许不得转载。

March 6, 2019 · 1 min · jiezi

基于Blink构建亲听项目以及全链路debug项目实时响应能力

案例与解决方案汇总页:阿里云实时计算产品案例&解决方案汇总本文全面总结了大数据项目组在亲听项目以及全链路debug项目上进行的实时流处理需求梳理,架构选型,以及达成效果一、背景介绍1.1亲听项目亲听项目专注于帮助用户收集、展示、监控和处理用户体验问题,是保证产品的主观评价质量的利器,关于其具体功能可参考在ata搜索"亲听"查看系列文章。目前亲听项目的实时流处理需求来自算法效果监控,算法效果监控需要对上游TimeTunnel日志进行解析后经过处理得到一些关键指标,亲听通过对这些指标的前端展示和阈值监控报警达到算法效果监控目的。需求要点可以总结如下:上游需要处理的TimeTunnel日志的实时数据量大约在日常峰值每秒数万条记录,大促峰值每秒几十万条记录从用户搜索行为到亲听系统得到搜索行为指标数据秒级的低延时数据的处理逻辑较为复杂且会随着算法迭代需要发生变化1.2全链路debug全链路debug专注于帮助用户在线上搜索结果出现异常和问题时帮助开发者复现搜索后端各子系统的中间结果,定位并解决子系统存在的问题,是系统层级质量保证和测试的有力工具。关于其具体功能可参考在ata搜索"全链路debug"查看系列文章。全链路debug的实时流处理需求是实时从TimeTunnel日志中提取出帮助排除搜索线上问题的关键内容,全链路debug利用这些内容帮助进行问题排查。全链路debug的实时流处理需求模型可以用下图描述:需求要点可以总结如下:上游需要处理的TimeTunnel日志的实时数据量大约在日常峰值每秒数万条记录,大促峰值每秒几十万条记录需要保存的单条记录较大,平均达到几K左右对上游TimeTunnel日志解析逻辑大部分为字段提取和透传且不会频繁变化二、解决方案2.1整体架构应对以上需求,亲听以及全拉链路debug的实时流处理系统的最终架构如下:亲听:全链路debug:对于亲听和全链路debug的实时流处理需求最终选择上述架构主要出于实时性和扩展性两方面考虑2.2实时性亲听和全链路debug的实时流处理需求在实时性要求上是类似的,即要对接tt日志,在tt日志记录写入到对于亲听和全链路debug的使用方可见延时要控制在秒级,这种实时性的需求可以分解为两个部分,第一是对实时流数据的处理,而是对实时流数据处理结果的存储和查询服务。对于实时流数据的处理,目前公司内的中间件产品blink能很好满足我们的需求,blink提供对接TimeTunnel的api接口,同时具备很好的实时流处理性能和运维体验;对于实时流处理结果的存储和查询,需要支持几万到几十万qps的写压力以及在每天累计几十T数据量情况下毫秒级延时的读性能,hbase能够基本满足对读写的需求,但是druid和drill能够在满足读写性能的同时提供更好的数据查询体验和实时流处理逻辑的可扩展性,所以对于实时流数据处理结果的存储和查询服务我们是优先考虑druid和drill的,但是全链路debug的实时流处理结果有一个特点就是单条记录数据大小平均为几K左右,这么大的单条记录的大小将导致druid需要的内存量过大且查询性能低下而不可用,所以对于全链路debug的实时流处理结果的存储和查询服务选择了hbase。2.3扩展性在亲听实时流处理系统的下游引入tt->druid,然后使用drill查询druid提供查询服务,是出于对扩展性的考虑。druid是一种支持实时流处理和查询的olap系统(ATA),对接druid使得可以把一部分实时流数据的处理逻辑交给druid,这样当实时流处理逻辑需要修改时,很多情况下就可以通过修改查询逻辑(只要修改一个请求druid时的json配置文件)而不需要修改blink任务(需要修改代码、打包、编译、调参、上线)实现,大幅提升实时流处理系统的扩展性,而亲听实时流处理需求频繁变化的业务特点非常需要这种扩展性;drill是高性能的SQL查询引擎,通过drill对接druid提供查询服务不但使查询语法从druid的json文件变为sql可读性大幅增强,同时drill对druid查询结果具有的二次处理能力也进一步增强了通过修改查询逻辑可以满足的实时流处理逻辑变化,进一步增强系统可扩展性。在blink和druid之间增加了TimeTunnel进行数据中转以保证blink产出流数据被转化为下游druid支持的流数据源形式。2.4经验总结使用table api编写stream api作为blink的底层api,具有较高的灵活性,但是可读性很不好,进而非常影响代码的可维护性和扩展性,当要在实时任务中加入新需求时经常要改动很多地方并且很容易出错,所有实时任务我们选择使用table api编写,table api使用类sql语法描述实时流处理逻辑,使得数据流处理逻辑变得非常清晰,可读性大幅增强,进而节约代码的维护和扩展成本。进行字段归类合并我们通过梳理业务方最终需要使用的字段内容,将blink任务输出到TimeTunnel中记录的字段进行了分类合并,除了出于druid查询性能考虑将若干需要进行group by以及count distinct查询的原有字段保留,其余全部按照诸如搜索请求相关信息、用户相关信息、搜索返回宝贝相关信息这样的概念将原有字段分组后合并为多值字段,而每个合并后的多值字段又会在blink代码中用一个udtf函数统一处理。这样做的好处在于代码逻辑上变得更清晰,当实时流处理需求发生变化,需要产出新的内容或修改现有内容产出逻辑时,只需找到新增内容或待修改内容对应的多值字段,修改对应udtf逻辑并重新上线blink任务即可,下游的druid build无需进行任何修改;同时用有限的几个udtf对整个实时流输出记录的处理逻辑进行归类,避免了记录处理逻辑频繁变化可能导致的代码中过时字段和udf泛滥,可读性下降,修改易出错的问题。drill处理逻辑前移请看下面这个sql:select * from druid.sqa_wireless_search_pv where INSTR(auction_tag, ‘15’)这个sql drill的处理逻辑是从druid表中召回druid.sqa_wireless_search_pv表中全部记录后逐条进行auction_tag字段的比对,过滤出包含‘15’字符串的记录,这种召回全部记录进行处理的操作对于drill来说会造成很大的性能问题,占用集群资源急剧上升,查询延时大幅提高,甚至导致集群oom使查询服务中断服务。在使用drill进行查询时应尽量避免执行类似召回大量记录进行处理的sql,我们对亲听算法效果监控现有sql进行了梳理,找到召回记录数目可能会过高的sql,通过将处理逻辑前移到blink任务阶段大幅优化drill查询性能(例如上面的sql只要将比对auction_tag字段是否含有‘15’的逻辑交给blink处理,并让blink任务新增产出一个tag字段,这样druid就可以针对tag字段建索引,通过where tag==‘true’这样的语句就可以直接召回需要的记录)三、成果总结目前tt->blink->hbase和tt->blink->tt->druid是在公司内使用非常广泛的两种实时流处理架构,能以秒级延时完成线上实时日志处理,这两种实时流处理架构比较好地满足了亲听和全链路debug项目的实时数据处理需求,极大提升了项目价值四、作者简介鸷鸟,来自搜索事业部-工程效率&技术质量-算法工程平台-实时大数据平台15年加入阿里,主要从事电商体系实时数据研发以及实时大数据平台研发本文作者:付空阅读原文本文为云栖社区原创内容,未经允许不得转载。

March 4, 2019 · 1 min · jiezi

刚刚,阿里开源 iOS 协程开发框架 coobjc!

阿里妹导读:刚刚,阿里巴巴正式对外开源了基于 Apache 2.0 协议的协程开发框架 coobjc,开发者们可以在 Github 上自主下载。coobjc是为iOS平台打造的开源协程开发框架,支持Objective-C和Swift,同时提供了cokit库为Foundation和UIKit中的部分API提供了协程化支持,本文将为大家详细介绍coobjc的设计理念及核心优势。开源地址https://github.com/alibaba/coobjciOS异步编程问题从2008年第一个iOS版本发布至今的11年时间里,iOS的异步编程方式发展缓慢。基于 Block 的异步编程回调是目前 iOS 使用最广泛的异步编程方式,iOS 系统提供的 GCD 库让异步开发变得很简单方便,但是基于这种编程方式的缺点也有很多,主要有以下几点:容易进入"嵌套地狱"错误处理复杂和冗长容易忘记调用 completion handler条件执行变得很困难从互相独立的调用中组合返回结果变得极其困难在错误的线程中继续执行(如子线程操作UI)难以定位原因的多线程崩溃(手淘中多线程crash已占比60%以上)锁和信号量滥用带来的卡顿、卡死针对多线程以及尤其引发的各种崩溃和性能问题,我们制定了很多编程规范、进行了各种新人培训,尝试降低问题发生的概率,但是问题依然很严峻,多线程引发的问题占比并没有明显的下降,异步编程本来就是很复杂的事情,单靠规范和培训是难以从根本上解决问题的,需要有更加好的编程方式来解决。解决方案上述问题在很多系统和语言开发中都可能会碰到,解决问题的标准方式就是使用协程,C#、Kotlin、Python、Javascript 等热门语言均支持协程极其相关语法,使用这些语言的开发者可以很方便的使用协程及相关功能进行异步编程。2017 年的 C++ 标准开始支持协程,Swift5 中也包含了协程相关的标准,从现在的发展趋势看基于协程的全新的异步编程方式,是我们解决现有异步编程问题的有效的方式,但是苹果基本已经不会升级 Objective-C 了,因此使用Objective-C的开发者是无法使用官方的协程能力的,而最新 Swift 的发布和推广也还需要时日,为了让广大iOS开发者能快速享受到协程带来的编程方式上的改变,手机淘宝架构团队基于长期对系统底层库和汇编的研究,通过汇编和C语言实现了支持 Objective-C 和 Swift 协程的完美解决方案 —— coobjc。核心能力提供了类似C#和Javascript语言中的Async/Await编程方式支持,在协程中通过调用await方法即可同步得到异步方法的执行结果,非常适合IO、网络等异步耗时调用的同步顺序执行改造。提供了类似Kotlin中的Generator功能,用于懒计算生成序列化数据,非常适合多线程可中断的序列化数据生成和访问。提供了Actor Model的实现,基于Actor Model,开发者可以开发出更加线程安全的模块,避免由于直接函数调用引发的各种多线程崩溃问题。提供了元组的支持,通过元组Objective-C开发者可以享受到类似Python语言中多值返回的好处。内置系统扩展库提供了对NSArray、NSDictionary等容器库的协程化扩展,用于解决序列化和反序列化过程中的异步调用问题。提供了对NSData、NSString、UIImage等数据对象的协程化扩展,用于解决读写IO过程中的异步调用问题。提供了对NSURLConnection和NSURLSession的协程化扩展,用于解决网络异步请求过程中的异步调用问题。提供了对NSKeyedArchieve、NSJSONSerialization等解析库的扩展,用于解决解析过程中的异步调用问题。coobjc设计最底层是协程内核,包含了栈切换的管理、协程调度器的实现、协程间通信channel的实现等。中间层是基于协程的操作符的包装,目前支持async/await、Generator、Actor等编程模型。最上层是对系统库的协程化扩展,目前基本上覆盖了Foundation和UIKit的所有IO和耗时方法。核心实现原理协程的核心思想是控制调用栈的主动让出和恢复。一般的协程实现都会提供两个重要的操作:Yield:是让出cpu的意思,它会中断当前的执行,回到上一次Resume的地方。Resume:继续协程的运行。执行Resume后,回到上一次协程Yield的地方。我们基于线程的代码执行时候,是没法做出暂停操作的,我们现在要做的事情就是要代码执行能够暂停,还能够再恢复。 基本上代码执行都是一种基于调用栈的模型,所以如果我们能把当前调用栈上的状态都保存下来,然后再能从缓存中恢复,那我们就能够实现yield和 resume。实现这样操作有几种方法呢?第一种:利用glibc 的 ucontext组件(云风的库)。第二种:使用汇编代码来切换上下文(实现c协程),原理同ucontext。第三种:利用C语言语法switch-case的奇淫技巧来实现(Protothreads)。第四种:利用了 C 语言的 setjmp 和 longjmp。第五种:利用编译器支持语法糖。上述第三种和第四种只是能过做到跳转,但是没法保存调用栈上的状态,看起来基本上不能算是实现了协程,只能算做做demo,第五种除非官方支持,否则自行改写编译器通用性很差。而第一种方案的 ucontext 在iOS上是废弃了的,不能使用。那么我们使用的是第二种方案,自己用汇编模拟一下 ucontext。模拟ucontext的核心是通过getContext和setContext实现保存和恢复调用栈。需要熟悉不同CPU架构下的调用约定(Calling Convention). 汇编实现就是要针对不同cpu实现一套,我们目前实现了 armv7、arm64、i386、x86_64,支持iPhone真机和模拟器。Show me the code说了这么多,还是看看代码吧,我们从一个简单的网络请求加载图片功能来看看coobjc到底是如何使用的。下面是最普通的网络请求的写法:下面是使用coobjc库协程化改造后的代码:原本需要20行的代码,通过coobjc协程化改造后,减少了一半,整个代码逻辑和可读性都更加好,这就是coobjc强大的能力,能把原本很复杂的异步代码,通过协程化改造,转变成逻辑简洁的顺序调用。coobjc还有很多其他强大的能力,本文对于coobjc的实际使用就不过多介绍了,感兴趣的朋友可以去官方github仓库自行下载查看。性能提升我们在iPhone7 iOS11.4.1的设备上使用协程和传统多线程方式分别模拟高并发读取数据的场景,下面是两种方式得到的压测数据。测试机器:iPhone7 iOS11.4.1数据文件大小:20M协程最多使用线程数:4数据测试结果(统计的是所有并发访问结束的总耗时):从上面的表格我们可以看到使用在并发量很小的场景,由于多线程可以完全使用设备的计算核心,因此coobjc总耗时要比传统多线程略高,但是由于整体耗时都很小,因此差异并不明显,但是随着并发量的增大,coobjc的优势开始逐渐体现出来,当并发量超过1000以后,传统多线程开始出现线程分配异常,而导致很多并发任务并没有执行,因此在上表中显示的是大于20秒,实际是任务已经无法正常执行了,但是coobjc仍然可以正常运行。我们在手机淘宝这种超级App中尝试了协程化改造,针对部分性能差的页面,我们发现在滑动过程中存在很多主线程IO调用、数据解析,导致帧率下降严重,通过引入coobjc,在不改变原有业务代码的基础上,通过全局hook部分IO、数据解析方法,即可让原来在主线程中同步执行的IO方法异步执行,并且不影响原有的业务逻辑,通过测试验证,这样的改造在低端机(iPhone6及以下的机器)上的帧率有20%左右的提升。优势简明概念少:只有很少的几个操作符,相比响应式几十个操作符,简直不能再简单了。原理简单:协程的实现原理很简单,整个协程库只有几千行代码。易用使用简单:它的使用方式比GCD还要简单,接口很少。改造方便:现有代码只需要进行很少的改动就可以协程化,同时我们针对系统库提供了大量协程化接口。清晰同步写异步逻辑:同步顺序方式写代码是人类最容易接受的方式,这可以极大的减少出错的概率。可读性高:使用协程方式编写的代码比block嵌套写出来的代码可读性要高很多。性能调度性能更快:协程本身不需要进行内核级线程的切换,调度性能快,即使创建上万个协程也毫无压力。减少卡顿卡死: 协程的使用以帮助开发减少锁、信号量的滥用,通过封装会引起阻塞的IO等协程接口,可以从根源上减少卡顿、卡死,提升应用整体的性能。总结程序是写来给人读的,只会偶尔让机器执行一下。——Abelson and Sussman基于协程实现的编程范式能够帮助开发者编写出更加优美、健壮、可读性更强的代码。协程可以帮助我们在编写并发代码的过程中减少线程和锁的使用,提升应用的性能和稳定性。本文作者:淘宝技术阅读原文本文来自云栖社区合作伙伴“ 阿里技术”,如需转载请联系原作者。

March 1, 2019 · 1 min · jiezi

node遇上c++ --- 爱情来的太快(一) (没有文章)

文章后续更新,存入草稿时间长会被删, 希望谅解

February 24, 2019 · 1 min · jiezi

能用机器完成的,千万别堆工作量|持续集成中的性能自动化测试

1.背景当前闲鱼在精益开发模式下,整个技术团队面临了诸多的能力落地和挑战,尤其是效能方面的2-1-1的目标(2周需求交付周期,1周需求开发周期,1小时达到发布标准),具体可见 闲鱼工程师是如何构建持续集成流水线,让研发效率翻倍的 ,在这个大目标下,就必须把每个环节都做到极致。自动化的建设是决定CI成败的关键能力,今天分享一下闲鱼Android客户端性能自动化环节的实践。2.面临的问题2.1 主要是两个方面的问题工具缺失:目前淘宝系,对于线上性能水位的监控有一套完善的体系,但是针对新功能的性能测试,每个业务团队都有对应的性能专项小组,产出的工具都是根据自己业务特点的定制开发的,闲鱼客户端目前使用Flutter做为客户端主开发语言,对于Flutter性能数据的获取及UI自动化测试支撑工具目前是缺失的,同时业界对Flutter自动化和性能相关的实践几乎没有;测试工作量翻N倍(N=一个版本周期内的分支数):原先的开发模式是功能测试集成测试一起进行的,所以性能测试只需要针对集成后的包进行测试即可,到现在转变为泳道的开发模式,一个版本内会一般包含十几个左右的泳道分支甚至更多,我们必须确保每个泳道的分支的性能是达标的,如果有性能问题需要第一时间反馈出来,如果遗留到集成阶段,问题的排查(十几个分支中筛查),问题的解决将会耗费大量的时间,效率很难得到大的提升;2.2 问题思考体系化解决,要让每个泳道分支都得到有效测试覆盖,测试件能够自动化执行,持续反馈结果3. 解决方案综合上述问题,梳理如下解决方案:针对Flutter性能数据的获取(比如,Flutter有自己的SurfaceView,原有Native计算FPS的方式无法直接使用)针对Flutter UI自动化的实现(Flutter/Native UI混合栈的处理)性能自动化脚本 / 性能数据自动采集、上报 融入CI流程性能问题的通知 / 报表展示 / 分析3.1 性能数据[FPS]解析处理 adb shell dumpsys SurfaceFlinger –latency 的数据,详细请见文末参考链接(该方式兼容Flutter及Native的解决方案,已在Android4.x-9.x验证可行),处理SurfaceFlinger核心代码如下:dumpsys SurfaceFlinger –latency-clear#echo “dumpsys SurfaceFlinger…“if [[ $isflutter = 0 ]];then window=dumpsys window windows | grep mCurrent | $bb awk '{print $3}'|$bb tr -d '}' # Get the current window echo $windowfiif [[ $isflutter = 1 ]];then window=dumpsys SurfaceFlinger --list |grep '^SurfaceView'|$bb awk 'NR==1{print $0}'if [ -z “$window” ]; then window=“SurfaceView"fiecho $windowfi$bb usleep $sleep_tdumpsys SurfaceFlinger –latency “$window”|$bb awk -v time=$uptime -v target=$target -v kpi=$KPI ‘{if(NR==1){r=$1/1000000;if(r<0)r=$1/1000;b=0;n=0;w=1}else{if(n>0&&$0==”")O=1;if(NF==3&&$2!=0&&$2!=9223372036854775807){x=($3-$1)/1000000/r;if(b==0){b=$2;n=1;d=0;D=0;if(x<=1)C=r;if(x>1){d+=1;C=int(x)r;if(x%1>0)C+=r};if(x>2)D+=1;m=r;o=0}else{c=($2-b)/1000000;if(c>1000){O=1}else{n+=1;if(c>=r){C+=c;if(c>kpi)o+=1;if(c>=m)m=c;if(x>1)d+=1;if(x>2)D+=1;b=$2}else{C+=r;b=sprintf(”%.0f”,b+r1000000)}}};if(n==1)s=sprintf("%.3f",$2/1000000000)};if(n>0&&O==1){O=0;if(n==1)t=sprintf("%.3f",s+C/1000);else t=sprintf("%.3f",b/1000000000);T=strftime("%F %T",time+t)".“sprintf(”%.0f",(time+t)%11000);f=sprintf("%.2f",n1000/C);m=sprintf("%.0f",m);g=f/target;if(g>1)g=1;h=kpi/m;if(h>1)h=1;e=sprintf("%.2f",g60+h20+(1-o/n)*20);print s",“t”,“T”,“f+0”,“n”,“d”,“D”,“m”,“o”,“e”,“w;n=0;if($0==”"){b=0;w+=1}else{b=$2;n=1;d=0;D=0;if(x<=1)C=r;if(x>1){d+=1;C=int(x)*r;if(x%1>0)C+=r};if(x>2)D+=1;m=r;o=0}}}}’ >>$file[CPU]使用的是top的命令获取(该方式获取性能数据时,数据收集带来的损耗最少)export bb="/data/local/tmp/busybox"$bb top -b -n 1|$bb awk ‘NR==4{print NF-1}’[内存]解析 dumpsys meminfo $package 拿到 Java Heap,Java Heap Average,Java Heap Peak,Native Heap,Native Heap Average,Native Heap Peak,Graphics,Unknown,Pss 数据do_statistics() { ((COUNT+=1)) isExist="$(echo $OUTPUT | grep “Dalvik Heap”)" if [[ ! -n $isExist ]] ; then old_dumpsys=true else old_dumpsys=false fi if [[ $old_dumpsys = true ]] ; then java_heap="$(echo “$OUTPUT” | grep “Dalvik” | $bb awk ‘{print $6}’ | $bb tr -d ‘\r’)" else java_heap="$(echo “$OUTPUT” | grep “Dalvik Heap[^:]” | $bb awk ‘{print $8}’ | $bb tr -d ‘\r’)" fi echo “1."$JAVA_HEAP_TOTAL “2."$java_heap “3."$JAVA_HEAP_TOTAL ((JAVA_HEAP_TOTAL+=java_heap)) ((JAVA_HEAP_AVG=JAVA_HEAP_TOTAL/COUNT)) if [[ $java_heap -gt $JAVA_HEAP_PEAK ]] ; then JAVA_HEAP_PEAK=$java_heap fi if [[ $old_dumpsys = true ]] ; then native_heap="$(echo “$OUTPUT” | grep “Native” | $bb awk ‘{print $6}’ | $bb tr -d ‘\r’)” else native_heap="$(echo “$OUTPUT” | grep “Native Heap[^:]” | $bb awk ‘{print $8}’ | $bb tr -d ‘\r’ | $bb tr -d ‘\n’)” fi ((NATIVE_HEAP_TOTAL+=native_heap)) ((NATIVE_HEAP_AVG=NATIVE_HEAP_TOTAL/COUNT)) if [[ $native_heap -gt $NATIVE_HEAP_PEAK ]] ; then NATIVE_HEAP_PEAK=$native_heap fi g_Str=“Graphics” if [[ $OUTPUT == $g_Str ]] ; then echo “Found Graphics…” Graphics="$(echo “$OUTPUT” | grep “Graphics” | $bb awk ‘{print $2}’ | $bb tr -d ‘\r’)” else echo “Not Found Graphics…” Graphics=0 fi Unknown="$(echo “$OUTPUT” | grep “Unknown” | $bb awk ‘{print $2}’ | $bb tr -d ‘\r’)" total="$(echo “$OUTPUT” | grep “TOTAL”|$bb head -1| $bb awk ‘{print $2}’ | $bb tr -d ‘\r’)"}[流量]通过 dumpsys package packages 解析出当前待测试包来获取流量信息uid="$(dumpsys package packages|$bb grep -E “Package |userId”|$bb awk -v OFS=" " ‘{if($1==“Package”){P=substr($2,2,length($2)-2)}else{if(substr($1,1,6)==“userId”)print P,substr($1,8,length($1)-7)}}’|grep $package|$bb awk ‘{print $2}’)“echo “Net:"$uidinitreceive=$bb awk -v OFS=" " 'NR&gt;1{if($2=="wlan0"){wr[$4]+=$6;wt[$4]+=$8}else{if($2=="rmnet0"){rr[$4]+=$6;rt[$4]+=$8}}}END{for(i in wr){print i,wr[i]/1000,wt[i]/1000,"wifi"};for(i in rr){print i,rr[i]/1000,rt[i]/1000,"data"}}' /proc/net/xt_qtaguid/stats | grep $uid|$bb awk '{print $2}'inittransmit=$bb awk -v OFS=" " 'NR&gt;1{if($2=="wlan0"){wr[$4]+=$6;wt[$4]+=$8}else{if($2=="rmnet0"){rr[$4]+=$6;rt[$4]+=$8}}}END{for(i in wr){print i,wr[i]/1000,wt[i]/1000,"wifi"};for(i in rr){print i,rr[i]/1000,rt[i]/1000,"data"}}' /proc/net/xt_qtaguid/stats | grep $uid|$bb awk '{print $3}'echo “initnetarray”$initreceive”,"$inittransmitgetnet(){ local data_t=date +%Y/%m/%d" "%H:%M:%S netdetail=$bb awk -v OFS=, -v initreceive=$initreceive -v inittransmit=$inittransmit -v datat="$data_t" 'NR&gt;1{if($2=="wlan0"){wr[$4]+=$6;wt[$4]+=$8}else{if($2=="rmnet0"){rr[$4]+=$6;rt[$4]+=$8}}}END{for(i in wr){print datat,i,wr[i]/1000-initreceive,wt[i]/1000-inittransmit,"wifi"};for(i in rr){print datat,i,rr[i]/1000-initreceive,rt[i]/1000-inittransmit,"data"}}' /proc/net/xt_qtaguid/stats | grep $uid echo $netdetail>>$filenet}3.2 性能自动化脚本基于Appium的自动化用例,这个技术业界已经有非常多的实践了,这里我不再累述,如果不了解的同学,可以到Appium官网 http://appium.ioFlutter和Native页面切换使用App内的Schema跳转Flutter页面的文本输入等交互性较强的场景使用基于Flutter框架带的Integration Test来操作An integration testGenerally, an integration test runs on a real device or an OS emulator, such as iOS Simulator or Android Emulator. The app under test is typically isolated from the test driver code to avoid skewing the results.Flutter的UI自动化及Flutter/Native混合页面的处理在测试上的应用后续单独开文章介绍,原理相关可以先参考 千人千面录制回放技术3.3 性能自动化CI流程3.4 性能数据报表FPS相关Framediff: 绘制帧的开始时间和结束时间差FPS: 每秒展示的帧数Frames: 一个刷新周期内所有的帧jank: 一帧开始绘制到结束超过16.67ms 就记一次jank,jank非零代表硬件绘制掉帧,和屏幕硬件性能及相关驱 动性能有关jank2: 一帧开始绘制到结束超过33.34ms 就记一次jank2MFS: 在一个刷新周期内单帧最大耗时(每两行垂直同步的时间差代表两帧绘制的帧间隔)OKT: 在一个刷新周期内,帧耗时超过16.67ms的次数SS: 流畅度,通过FPS,MFS,OKT计算出来,流畅度 = 实际帧率比目标帧率比值60【目标帧率越高越好】 + 目标时间和两帧时间差比值20【两帧时间差越低越好】 + (1-超过16ms次数/帧数)*20【次数越少越好】CPUMemory4. 成果展示4.1 指定泳道分支性能监控泳道分支出现了性能问题再报表上一目了然4.2 性能专项支撑1、Flutter商品详情页重构 14轮测试2、客户端图片统一资源测试 4轮测试5. 总结性能自动化只是整个CI流程中的一个环节,为了极致效率的大目标,闲鱼质量团队还产出了很多支撑工具,CI平台,遍历测试,AI错误识别,用例自动生成等等,后续也会分享给大家。6. 参考https://testerhome.com/topics/2232https://testerhome.com/topics/4775本文作者:闲鱼技术-灯阳阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 22, 2019 · 3 min · jiezi

蚂蚁金服核心技术:百亿特征实时推荐算法揭秘

小叽导读:文章提出一整套创新算法与架构,通过对TensorFlow底层的弹性改造,解决了在线学习的弹性特征伸缩和稳定性问题,并以GroupLasso和特征在线频次过滤等自研算法优化了模型稀疏性。在支付宝核心推荐业务获得了uvctr的显著提升,并较大地提升了链路效率。0.综述在线学习(Online learning)由于能捕捉用户的动态行为,实现模型快速自适应,进而成为提升推荐系统性能的重要工具。然而它对链路和模型的稳定性,训练系统的性能都提出了很高的要求。但在基于原生TensorFlow,设计Online推荐算法时,我们发现三个核心问题:一些资讯推荐场景,需要大量长尾词汇作为特征,需使用featuremap对低频特征频次截断并连续性编码,但耗时且方法aggressive。使用流式数据后,无法预知特征规模,而是随训练逐渐增长。因此需预留特征空间训练几天后重启,否则会越界。模型稀疏性不佳,体积达到数十GB,导致上传和线上加载耗时长且不稳定。更重要的是,在线学习如火如荼,当流式特征和数据都被打通后,能按需增删特征,实现参数弹性伸缩的新一代训练平台成为大势所趋。为了解决这些问题,从2017年底至今,蚂蚁金服人工智能部的同学,充分考虑蚂蚁的业务场景和链路,对TensorFlow进行了弹性改造, 解决了以上三大痛点,简化并加速离线和在线学习任务。其核心能力如下:弹性特征伸缩体系,支持百亿参数训练。group_lasso优化器和频次过滤,提高模型稀疏性,明显提升线上效果。模型体积压缩90%,完善的特征管理和模型稳定性监控。在与业务线团队的共同努力下,目前已在支付宝首页的多个推荐场景全流量上线。其中某推荐位的个性化online learning桶最近一周相比线上多模型融合最优桶提升4.23% , 相比随机对照提升达34.67% 。 某个性化资讯推荐业务最近一周,相比DNN基准uv-ctr提升+0.77%,pv-ctr提升+4.78%,模型体积压缩90%,链路效率提升50%。1. 弹性改造及优势背景:在原生TensorFlow中,我们通过Variable来声明变量,若变量超过了单机承载的能力,可使用partitioned_variables来将参数分配到不同机器上。 但必须指定shape,声明后即不可改变,通过数组索引查找。由于推荐系统中大量使用稀疏特征,实践中一般采取embedding_lookup_sparse一类的方法在一个巨大的Dense Variable中查找向量并求和,来代替矩阵乘法。开源Tensorflow限定了Variable使用前必须声明维度大小,这带来了两个问题:1)需要预先计算特征到维度范围内的int值的映射表,这一步操作通常在ODPS上完成。因为需要扫描所有出现的特征并编号,计算非常缓慢;2)在online learning场景下,为了容纳新出现的特征,需要预留一部分维度空间,并在线上不断修改映射表,超过预留空间则需要重新启动在线任务。为了突破固定维度限制,实现特征的动态增加和删除,最朴素的优化想法是在TensorFlow底层实现模拟字典行为的Variable,并在此基础上重新实现Tensorflow上层API。由此我们进行了优化,在server新增了基于HashMap的HashVariable,其内存结构如下:在声明该变量时,只需增加一句,其他训练代码皆不需改动:每个特征都通过hash函数映射到一个2的64次方大小的空间内。当需要计算该特征时,PS会按需惰性创建并返回之。但其上层行为与原生TF一致。由于去掉了featuremap转ID的过程,我们内部形象地将其称为“去ID化”。在此之上我们实现了Group Lasso FTRL,频次过滤和模型压缩等一系列算法。备注:弹性特征带来一个显著的优势:只要用足够强的L1稀疏性约束,在单机上就能调试任意大规模的特征训练,带来很多方便。我们的hashmap实现是KV化的,key是特征,value是vector的首地址。离线训练优化经过这样的改造后,在离线批量学习上,带来了以下变化:在线训练优化online learning上,能带来如下变化:除了性能有明显的提升之外,其最大的优势是不需提前申请空间,训练可以无缝稳定运行。2. 特征动态增删技术弹性架构,主要目的就是特征优选,让模型自适应地选择最优特征,进而实现稀疏化,降低过拟合。本节介绍特征优选的两个核心技术:使用流式频次过滤, 对特征进入进行判定。使用Group Lasso优化器,对特征进行筛选和删除。2.1 Group Lasso 优化器稀疏化是算法追求的重要模型特性,从简单的L1正则化和Truncated Gradient[9], 再到讨论累积梯度平均值的RDA(Regularized Dual Averaging)[10], 再到目前常见的 FTRL[2] 。 然而它们都是针对广义线性模型优化问题提出的稀疏性优化算法,没有针对sparse DNN中的特征embedding层做特殊处理。把embedding参数向量当做普通参数进行稀疏化,并不能达到在线性模型中能达到的特征选择效果,进而无法有效地进行模型压缩。例如:当包含新特征的样本进入时,一个特征对应的一组参数(如embedding size为7,则参数数量为7)被激活,FTRL判定特征中的部分参数无效时,也不能安全地将该特征删除。如图:因此,在L1和L2正则的基础上,人们引入L21正则(group lasso)和L2正则(exclusive sparsity),分别表示如下:L21早在2011年已经引入,它最初目的是解决一组高度关联特征(如男女)应同时被保留或删除的问题,我们创新地扩展到embedding的表示上,以解决类似的问题。在L21中,由于内层L2正则将一个特征的所有参数施加相同的约束,能将整组参数清除或保留,由此决定embedding层中的某些特征对应的embedding向量是否完全删除,提升模型泛化性。因此称为group lasso。而L12则正好相反,它迫使每组参数中的非0参数数量一致但值又尽可能不同,但使输出神经元互相竞争输入神经元,进而使特征对目标更具区分性。对于DNN分类网络,底层表示要求有足够的泛化性和特征抽象能力,上层接近softmax层,需要更好的区分性。因此我们通常在最底层的embedding层使用group lasso。即如下的优化目标:直接将L21正则项惩罚加入loss,模型最终也能收敛,但并不能保证稀疏性。因此Group lasso优化器参考了FTRL,将梯度迭代分成两个半步,前半步按梯度下降,后半步微调实现稀疏性。通过调节L1正则项(即公式中的),能有效地控制模型稀疏性。Group lasso是弹性计算改造后,模型性能提升和压缩的关键。值得指出:在我们实现的优化器中,Variable,以及accum和linear两个slot也是KV存储。L12和L21正则相结合的方法也已经有论文讨论[8],但我们还未在业务上尝试出效果。由于篇幅限制,本节不打算详细介绍Group lasso的原理和推导2.2 流式频次过滤讨论完特征动态删除的方法后,我们再分析特征的准入策略。2.2.1 频次过滤的必要性在Google讨论FTRL的文章1中提到, 在高维数据中大部分特征都是非常稀疏的,在亿级别的样本中只出现几次。那么一个有趣的问题是,FTRL或Group FTRL优化器能否能删除(lasso)极低频特征?在RDA的优化公式中,满足以下条件的特征会被置0:若在t步之前,该特征只出现过几次,未出现的step的梯度为0,随着步数增大,满足上述条件变得越来越容易。由此RDA是可以直观处理极稀疏特征的。 但对于FTRL,要满足:其中,不仅和历史梯度有关,还与历史学习率和权重w有关。 因此FTRL虽然也能处理极稀疏特征,但并没有RDA那么aggressive(此处还待详细地分析其下界,Group FTRL与此类似)。由于FTRL在设计和推导时并未明确考虑极低频特征,虽然通过增大,确实能去除大量极低频特征,但由于约束太强,导致部分有效特征也被lasso,在离线实验中被证明严重影响性能。其次,对这些巨量极低频特征,保存历史信息的工程代价是很高昂的(增加几倍的参数空间和存储需求),如下图:因此我们提出,能否在实时数据流上模拟离线频次过滤,为特征提供准入门槛,在不降低模型性能的基础上,尽量去除极低频特征,进一步实现稀疏化?2.2.2 频次过滤的几种实现注意: 由于默认的embedding_lookup_sparse对特征执行了unique操作(特征归一化以简化计算),因此在PS端是不可能获取真实特征和label频次的。需要Python端对placeholder统计后,上传给server端指定的Variable,优化器通过slot获得该Variable后作出联合决策。最naive的思路是模拟离线频次过滤,对特征进行计数,只有达到一定阈值后再进入训练,但这样破坏了数据完整性:如总频次6,而阈值过滤为5,则该特征出现的前5次都被忽略了。为此我们提出了两种优化方案:基于泊松分布的特征频次估计在离线shuffle后的特征满足均匀分布,但对在线数据流,特征进入训练系统可看做泊松过程,符合泊松分布:其中n为当前出现的次数,t为当前的步数,为单位时间发生率,是泊松分布的主要参数,T为训练总步数。为特征最低门限(即最少在T时间内出现的次数)。因此我们能通过前t步的特征出现的次数n,将t时刻当做单位时间,则。 根据泊松分布,我们可以算出剩余时间内事件发生大于等于次的概率。 每次该特征出现时,都可按该概率做伯努利采样,特征在t步进入系统的概率用下式计算:通过真实线上数据仿真,它能接近离线频次过滤的效果,其是随每次特征进入时动态计算的。它的缺陷是:当t越小时,事件发生在t内的次数的variance越大,所以会以一定概率误加或丢弃特征。未来总的训练步数T在在线学习中是未知的。频次过滤与优化器相分离,导致不能获得优化器的统计信息。动态调L1正则方案在经典的FTRL实现中,L1正则对每个特征都是一致的。这导致了2.2.1 中提到的问题:过大的L1虽然过滤了极低频特征,但也影响的了模型的性能。参考各类优化器(如Adam)对learning_rate的改进,我们提出:通过特征频次影响L1正则系数,使得不同频次的特征有不同的lasso效果。特征频次和基于MLE的参数估计的置信度相关,出现次数越低置信度越低。如果在纯频率统计基础上加入一个先验分布(正则项),当频率统计置信度越低的时候,越倾向于先验分布,相应的正则系数要更大。我们经过多个实验,给出了以下的经验公式:其中c是惩罚倍数,为特征最低门限,这两者皆为超参,是当前特征出现的频次。我们在线上环境,使用了动态调节L1正则的方案 。在uvctr不降甚至有些微提升的基础上,模型特征数比不使用频次过滤减少75%,进而从实验证明了频次过滤对稀疏化的正向性。它的缺点也很明显:特征频次和正则系数之间的映射关系缺少严谨证明。频次过滤作为特征管理的一部分,目前还少有相关论文研究,亟待我们继续探索。3. 模型压缩和稳定性3.1 模型压缩在工程上,由于做了优化,如特征被优化器lasso后,只将其置0,并不会真正删除;在足够多步数后才删除。同时引入内存池,避免特征的反复创建和删除带来的不必要的性能损失。 这就导致在训练结束后,模型依然存在大量0向量。导出时要进一步做模型压缩。由于引入了HashPull和HashPush等非TF原生算子,需要将其裁剪后转换为原生TF的op。 我们将这些步骤统称图裁剪(GraphCut), 它使得线上inference引擎,不需要做任何改动即可兼容弹性改造。由于有效特征大大减少,打分速度相比原引擎提升50%以上。我们将图裁剪看做TF-graph的静态优化问题,分为3个步骤:第一遍遍历Graph,搜索可优化子结构和不兼容的op。第二遍遍历,记录节点上下游和元数据,裁剪关键op,并将Variable的非0值转存至Tensorflow原生的MutableDenseHashTable。本步骤将模型体积压缩90%。拼接新建节点,重建依赖关系,最后递归回溯上游节点,去除与inference无关的子图结构我们实现了完整简洁的图裁剪工具,在模型热导出时调用, 将模型从原先的8GB左右压缩到几百兆大小,同时保证模型打分一致。3.2 模型稳定性和监控online learning的稳定性非常重要。我们将线上真实效果,与实时模型生成的效果,进行了严密的监控,一旦样本偏差过多,就会触发报警。由于需捕捉时变的数据变化,因而不能用固定的离线数据集评估模型结果。我们使用阿里流式日志系统sls最新流入的数据作为评估样本,以滑动窗口先打分后再训练,既维持了不间断的训练,不浪费数据,同时尽可能高频地得到最新模型效果。我们对如下核心指标做了监控:样本监控: 正负比例,线上打分值和online-auc(即线上模型打分得到的auc),产出速率,消费速率。训练级监控: AUC, User-AUC(参考备注),loss, 模型打分均值(与样本的正负比例对齐),异常信息。特征级管理: 总特征规模,有效/0/删除特征规模,新增/插入/删除的速率。整体模型和调度:模型导出的时间,大小,打分分布是否正常,是否正常调度。业务指标:uvctr,pvctr(小时级更新,T+1报表)。线上与训练指标之间的对应关系如下表:通过http接口,每隔一段时间发送监控数据,出现异常会及时产生钉钉和邮件报警。下图是对9月20日到27号的监控,从第二张图表来看,模型能较好的适应当前数据流的打分分布。User-AUC:传统的AUC并不能完全描述uvctr,因为模型很可能学到了不同用户间的偏序关系,而非单个用户在不同offer下的点击偏序关系。为此,我们使用了User-AUC,它尽可能地模拟了线上uvctr的计算过程,在真实实验中,监控系统的uvctr小时报表,与实时模型输出的User-AUC高度一致。4. 工程实现和效果目前算法已经在支付宝首页的多个推荐位上线。推荐系统根据用户的历史点击,融合用户画像和兴趣,结合实时特征,预估用户CTR,进而提升系统整体点击率。我们以推荐位业务为例说明,其采用了经典的wide&deep的网络结构,其sparse部分包含百级别的group(见下段备注1)。 一天流入约百亿样本,label的join窗口为固定时长。由于负样本占大多数,上游链路对正负样本做了1:8的降采样(见下文备注2)。训练任务采用蚂蚁统一训练平台构建,并使用工作流进行定时调度,离线和在线任务的其他参数全部一致。Batchsize为512,每200步(即20万样本)评估结果,定时将模型通过图裁剪导出到线上系统。当任务失败时,调度系统会自动拉起,从checkpoint恢复。该推荐业务的online learning桶最近一周相比线上多模型融合最优桶提升4.23% , 相比随机对照提升达34.67% 。 另一资讯推荐业务其最近一周,相比DNN基准uv-ctr提升+0.77%,pv-ctr提升+4.78%。实验效果相比有较大的提升。备注1: group embedding是将相似emb特征分组,各自lookup求和后再concat,使得特征交叉在更高层进行。其设计是考虑到不同group的特征差异很大(如user和item),不应直接对位求和。备注2: inference打分仅做pointwise排序,采样虽改变数据分布但不改变偏序关系,因此并未在训练上做补偿。5. 未来工作弹性特征已经成为蚂蚁实时强化深度学习的核心要素。它只是第一步,在解决特征空间按需创建问题后,它会带来一个充满想象力的底层架构,众多技术都能在此基础上深挖: 在工程上,可继续从分钟级向秒级优化,进一步提升链路实时性并实现模型增量更新; 在算法上,我们正在探索如样本重要性采样,自动特征学习,在线线性规划与DNN的结合,实现优化器联合决策等技术。由于在线学习是个复杂的系统工程,我们在开发和调优时遇到了大量的困难,涉及样本回流,训练平台,模型打分,线上评估等一系列问题,尤其是稳定性,但基本都一一克服。为了保证线上结果稳定可信,我们在观察和优化两三个月后才发布这篇文章。本文作者为蚂蚁金服人工智能部认知计算组的基础算法团队,团队涉及图像、NLP、推荐算法和知识图谱等领域,带头人为国家知名算法专家褚崴,拥有定损宝和理赔宝等核心业务。本文作者:墨眀阅读原文本文来自云栖社区合作伙伴“ 阿里巴巴机器智能”,如需转载请联系原作者。 ...

February 22, 2019 · 1 min · jiezi

独家揭秘!阿里大规模数据中心的性能分析

阿里妹导读:数据中心已成为支撑大规模互联网服务的标准基础设施。随着数据中心的规模越来越大,数据中心里每一次软件(如 JVM)或硬件(如 CPU)的升级改造都会带来高昂的成本。合理的性能分析有助于数据中心的优化升级和成本节约,而错误的分析可能误导决策、甚至造成巨大的成本损耗。本文整理自阿里巴巴高级技术专家郭健美(花名:希伯)在Java相关行业会议的分享,主要介绍阿里大规模数据中心性能监控与分析的挑战与实践,希望对你有所启发。作者简介:郭健美(花名:希伯),阿里巴巴高级技术专家,目前主要从事数据中心的性能分析和软硬件结合的性能优化。CCF 系统软件专委和软件工程专委的委员。大家好,很高兴有机会与 Java 社区的开发者交流。我的研究领域在软件工程,主要集中在系统配置和性能方面。软件工程一个比较常见的活动是找 bug,当然找 bug 很重要,但后来也发现,即便 bug-free 的程序也会被人配置错,所以就衍生出了软件配置问题。很多软件需要配置化,比如 Java 程序或 JVM 启动时可以配置很多参数。通过配置,一套软件可以灵活地提供各种定制化的功能,同时,这些配置也会对软件整体性能产生不同的影响。当然这些还在软件配置方面,来了阿里以后,我有机会把这方面工作扩展到了硬件,会更多地结合硬件比如 CPU,来看系统的配置变更和升级改造对性能、可靠性以及业务上线效果的影响。今天主要谈谈我在这方面的一点工作。阿里最有代表性的事件是“双 11”。这里还是用的17年的数据,左上角是双十一的销售额,17年大概是 253 亿美金,比美国同期 Thanksgiving、Black Friday、Cyber Monday 加起来的销售额还要多。当然这是从业务层面去看数据,技术同学会比较关注右边的数据,17年双十一的交易峰值达到 32.5 万笔/秒、支付峰值达到 25.6 万笔/秒。对于企业来说,这么高的峰值性能意味着什么?意味着成本!我们之所以关注性能,就是希望通过持续的技术创新,不断地提高性能、同时节省成本。双十一零点的峰值性能不是一个简单的数字,其背后需要一个大规模数据中心来支撑。 简单来说,阿里的基础架构的上层是各种各样的应用,比如淘宝、天猫、菜鸟、钉钉,还有云计算和支付宝等,这也是阿里的一个特色,即具有丰富的业务场景。底层是上百万台机器相连的大规模数据中心,这些机器的硬件架构不同、分布地点也不同,甚至分布在世界各地。中间这部分我们称之为中台,最贴近上层应用的是数据库、存储、中间件以及计算平台,然后是资源调度、集群管理和容器,再下面是系统软件,包括操作系统、JVM 和虚拟化等。中台这部分的产品是衔接社区与企业的纽带。这两年阿里开源了很多产品,比如 Dubbo、PouchContainer 等,可以看出阿里非常重视开源社区,也非常重视跟开发者对话。现在很多人都在讲开源社区和生态,外面也有各种各样的论坛,但是像今天这样与开发者直接对话的活动并不是那么多,而推动社区发展最终还是要依赖开发者。这样大规模的基础架构服务于整个阿里经济体。从业务层面,我们可以看到 253 亿美金的销售额、32.5 万笔交易/秒这样的指标。然而,这些业务指标如何分解下来、落到基础架构的各个部分就非常复杂了。比如,我们在做 Java 中间件或 JVM 开发时,都会做性能评估。大部分技术团队开发产品后都会有个性能提升指标,比如降低了 20% 的 CPU 利用率,然而这些单个产品的性能提升放到整个交易链路、整个数据中心里面,占比多少?对数据中心整体性能提升贡献多少?这个问题很复杂,涉及面很广,包括复杂关联的软件架构和各种异构的硬件。后面会提到我们在这方面的一些思考和工作。阿里的电商应用主要是用 Java 开发的,我们也开发了自己的 AJDK,这部分对 OpenJDK 做了很多定制化开发,包括:融入更多新技术、根据业务需要及时加入一些 patches、以及提供更好的 troubleshooting 服务和工具。大家也知道,18年阿里入选并连任了 JCP EC(Java Community Process - Executive Committee) 职位,有效期两年,这对整个 Java 开发者社区、尤其是国内的 Java 生态都是一件大事。但是,不是每个人都了解这件事的影响。记得之前碰到一位同仁,提到 JCP EC 对阿里这种大业务量的公司是有帮助,对小公司就没意义了。其实不是这样的,参选 JCP EC 的时候,大公司、小公司以及一些社区开发者都有投票资格,小公司或开发者有一票,大公司也只有一票,地位是一样的。很多国外的小公司更愿意参与到社区活动,为什么?举个简单例子,由于业务需要,你在 JVM 8 上做了一个特性,费了很大的力气开发调试完成、业务上线成功,结果社区推荐升级到 JVM11 上,这时你可能又需要把该特性在 JVM 11 上重新开发调试一遍,可能还要多踩一些新的坑,这显然增加了开发代价、拉长了上线周期。但如果你能影响社区标准的制定呢?你可以提出将该特性融入社区下一个发布版本,有机会使得你的开发工作成为社区标准,也可以借助社区力量完善该特性,这样既提高了技术影响力也减少了开发成本,还是很有意义的。过去我们做性能分析主要依赖小规模的基准测试。比如,我们开发了一个 JVM 新特性, 模拟电商的场景,大家可能都会去跑 SPECjbb2015 的基准测试。再比如,测试一个新型硬件,需要比较 SPEC 或 Linpack 的基准测试指标。这些基准测试有必要性,因为我们需要一个简单、可复现的方式来衡量性能。但基准测试也有局限性,因为每一次基准测试都有其限定的运行环境和软硬件配置,这些配置设定对性能的影响可能很大,同时这些软硬件配置是否符合企业需求、是否具有代表性,都是需要考虑的问题。阿里的数据中心里有上万种不同的业务应用,也有上百万台分布在世界各地的不同服务器。当我们考虑在数据中心里升级改造软件或硬件时,一个关键问题是小规模基准测试的效果是否能扩展到数据中心里复杂的线上生产环境?举个例子,我们开发了 JVM 的一个新特性,在 SPECjbb2015 的基准测试中看到了不错的性能收益,但到线上生产环境灰度测试的时候,发现该特性可以提升一个 Java 应用的性能、但会降低另一个 Java 应用的性能。同时,我们也可能发现即便对同一个 Java 应用,在不同硬件上得到的性能结果大不相同。这些情况普遍存在,但我们不可能针对每个应用、每种硬件都跑一遍测试,因而需要一个系统化方法来估计该特性对各种应用和硬件的整体性能影响。对数据中心来说,评估每个软件或硬件升级的整体性能影响非常重要。比如,“双11”的销售额和交易峰值,业务层面可能主要关心这两个指标,那么这两个指标翻一倍的时候我们需要买多少台新机器?需要多买一倍的机器么?这是衡量技术能力提升的一个手段,也是体现“新技术”对“新商业”影响的一个途径。我们提出了很多技术创新手段,也发现了很多性能提升的机会,但需要从业务上也能看出来。为了解决上面提到的问题,我们开发了 SPEED 平台。首先是估计当前线上发生了什么,即 Estimation,通过全域监控采集数据,再进行数据分析,发现可能的优化点。比如,某些硬件整体表现比较差,可以考虑替换。然后,我们会针对软件或硬件的升级改造做线上评估,即 Evaluation。比如,硬件厂商推出了一个新硬件,他们自己肯定会做一堆评测,得到一组比较好的性能数据,但刚才也提到了,这些评测和数据都是在特定场景下跑出来的,这些场景是否适合用户的特定需求?没有直接的答案。通常,用户也不会让硬件厂商到其业务环境里去跑评测。这时候就需要用户自己拿这个新硬件做灰度测试。当然灰度规模越大评测越准确,但线上环境都直接关联业务,为了降低风险,实际中通常都是从几十台甚至几台、到上百台、上千台的逐步灰度。SPEED 平台要解决的一个问题就是即便在灰度规模很小时也能做一个较好的估计,这会节约非常多的成本。随着灰度规模增大,平台会不断提高性能分析质量,进而辅助用户决策,即 Decision。这里的决策不光是判断要不要升级新硬件或新版软件,而且需要对软硬件全栈的性能有一个很好的理解,明白什么样的软硬件架构更适合目标应用场景,这样可以考虑软硬件优化定制的方向。比如,Intel 的 CPU 从 Broadwell 到 Skylake,其架构改动很大,但这个改动的直接效果是什么?Intel 只能从基准测试中给答案,但用户可能根据自己的应用场景给出自己的答案,从而提出定制化需求,这对成本有很大影响。最后是 Validation,就是通常规模化上线后的效果来验证上述方法是否合理,同时改进方法和平台。数据中心里软硬件升级的性能分析需要一个全局的性能指标,但目前还没有统一的标准。Google 今年在 ASPLOS 上发表了一篇论文,提出了一个叫 WSMeter 的性能指标,主要是基于 CPI 来衡量性能。在 SPEED 平台里,我们也提出了一个全局性能指标,叫资源使用效率 RUE。基本思想很简单,就是衡量每个单位 Work Done 所消耗的资源。这里的 Work Done 可以是电商里完成的一个 Query,也可以是大数据处理里的一个 Task。而资源主要涵盖四大类:CPU、内存、存储和网络。通常我们会主要关注 CPU 或内存,因为目前这两部分消费了服务器大部分的成本。RUE 的思路提供了一个多角度全面衡量性能的方法。举个例子,业务方反映某台机器上应用的 response time 升高了,这时登录到机器上也看到 load 和 CPU 利用率都升高了。这时候你可能开始紧张了,担心出了一个故障,而且很可能是由于刚刚上线的一个新特性造成的。然而,这时候应该去看下 QPS 指标,如果 QPS 也升高了,那么也许是合理的,因为使用更多资源完成了更多的工作,而且这个资源使用效率的提升可能就是由新特性带来的。所以,性能需要多角度全面地衡量,否则可能会造成不合理的评价,错失真正的性能优化机会。下面具体讲几个数据中心性能分析的挑战,基本上是线上碰到过的具体问题,希望能引起大家的一些思考。首先是性能指标。可能很多人都会说性能指标我每天都在用,这有什么好说的。其实,真正理解性能指标以及系统性能本身并不是那么容易。举个例子,在数据中心里最常用的一个性能指标是 CPU 利用率,给定一个场景,数据中心里每台机器平均 CPU 利用率是 50%,假定应用需求量不会再增长、并且软件之间也不会互相干扰,那么是否可以把数据中心的现有机器数量减半呢?这样,理想情况下 CPU 利用率达到 100% 就可以充分利用资源了,是否可以这样简单地理解 CPU 利用率和数据中心的性能呢?肯定不行。就像刚才说的,数据中心除了 CPU,还有内存、存储和网络资源,机器数量减半可能很多应用都跑不起来了。再举个例子,某个技术团队升级了其负责的软件版本以后,通过线上测试看到平均 CPU 利用率下降了 10%,因而声明性能提升了 10%。这个声明没有错,但我们更关心性能提升以后是否能节省成本,比如性能提升了 10%,是否可以把该应用涉及的 10% 的机器关掉?这时候性能就不应该只看 CPU 利用率,而应该再看看对吞吐量的影响。所以,系统性能和各种性能指标,可能大家都熟悉也都在用,但还需要更全面地去理解。刚才提到 SPEED 的 Estimation 会收集线上性能数据,可是收集到的数据一定对吗?这里讲一个 Hyper-Threading 超线程的例子,可能对硬件了解的同学会比较熟悉。超线程是 Intel 的一个技术,比如我们的笔记本,一般现在都是双核的,也就是两个 hardware cores,如果支持超线程并打开以后,一个 hardware core 就会变成两个 hardware threads,即一台双核的机器会有四个逻辑 CPU。来看最上面一张图,这里有两个物理核,没有打开超线程,两边 CPU 资源都用满了,所以从任务管理器报出的整台机器平均 CPU 利用率是 100%。左下角的图也是两个物理核,打开了超线程,每个物理核上有一个 hardware thread 被用满了,整台机器平均 CPU 利用率是 50%。再看右下角的图,也是两个物理核,也打开了超线程,有一个物理核的两个hardware threads 都被用满了,整台机器平均 CPU 利用率也是 50%。左下角和右下角的 CPU 使用情况完全不同,但是如果我们只是采集整机平均 CPU 利用率,看到的数据是一样的!所以,做性能数据分析时,不要只是想着数据处理和计算,还应该注意这些数据是怎么采集的,否则可能会得到一些误导性的结果。数据中心里的硬件异构性是性能分析的一大挑战,也是性能优化的一个方向。比如这里左边的 Broadwell 架构,是 Intel 过去几年服务器 CPU 的主流架构,近几年在推右边的 Skylake 架构,包含最新的 Cascade Lake CPU。Intel 在这两个架构上做了很大的改动,比如,Broadwell 下访问内存还是保持多年的环状方式,而到了 Skylake 改为网格状方式。再比如,L2 Cache 到了Skylake 上扩大了四倍,通常来说这可以提高 L2 Cache 的命中率,但是 cache 越大也不代表性能就一定好,因为维护 cache coherence 会带来额外的开销。这些改动有利有弊,但我们需要衡量利和弊对整体性能的影响,同时结合成本来考虑是否需要将数据中心的服务器都升级到 Skylake。了解硬件的差异还是很有必要的,因为这些差异可能影响所有在其上运行的应用,并且成为硬件优化定制的方向。现代互联网服务的软件架构非常复杂,比如阿里的电商体系架构,而复杂的软件架构也是性能分析的一个主要挑战。举个简单的例子,图中右边是优惠券应用,左上角是大促主会场应用,左下角是购物车应用,这三个都是电商里常见的业务场景。从 Java 开发的角度,每个业务场景都是一个 application。电商客户既可以从大促主会场选择优惠券,也可以从购物车里选择优惠券,这是用户使用习惯的不同。从软件架构角度看,大促主会场和购物车两个应用就形成了优惠券应用的两个入口,入口不同对于优惠券应用本身的调用路径不同,性能影响也就不同。所以,在分析优惠券应用的整体性能时需要考虑其在电商业务里的各种错综复杂的架构关联和调用路径。像这种复杂多样的业务场景和调用路径是很难在基准测试中完全复现的,这也是为什么我们需要做线上性能评估。这是数据分析里著名的辛普森悖论,在社会学和医学领域有很多常见案例,我们在数据中心的性能分析里也发现了。这是线上真实的案例,具体是什么 App 我们不用追究。假设还用前面的例子,比如 App 就是优惠券应用,在大促的时候上线了一个新特性 S,灰度测试的机器占比为 1%,那么根据 RUE 指标,该特性可以提升性能 8%,挺不错的结果。但是如果优惠券应用有三个不同的分组,分组假设就是关联刚才提到的不同入口应用,那么从每个分组看,该特性都降低了应用的性能。同样一组数据、同样的性能评估指标,通过整体聚集分析得到的结果与通过各部分单独分析得到的结果正好相反,这就是辛普森悖论。既然是悖论,说明有时候应该看总体评估结果,有时间应该看部分评估结果。在这个例子里面,我们选择看部分评估、也就是分组上的评估结果,所以看起来这个新特性造成了性能下降,应该继续修改并优化性能。所以,数据中心里的性能分析还要预防各种可能的数据分析陷阱,否则可能会严重误导决策。最后,还有几分钟,简单提一下性能分析师的要求。这里通常的要求包括数学、统计方面的,也有计算机科学、编程方面的,当然还有更重要的、也需要长期积累的领域知识这一块。这里的领域知识包括对软件、硬件以及全栈性能的理解。其实,我觉得每个开发者都可以思考一下,我们不光要做功能开发,还要考虑所开发功能的性能影响,尤其是对数据中心的整体性能影响。比如,JVM 的 GC 开发,社区里比较关心 GC 暂停时间,但这个指标与 Java 应用的 response time 以及所消耗的 CPU 资源是什么关系,我们也可以有所考虑。本文作者:郭健美阅读原文本文来自云栖社区合作伙伴“ 阿里技术”,如需转载请联系原作者。 ...

February 21, 2019 · 2 min · jiezi

如何合理的规划jvm性能调优

JVM性能调优涉及到方方面面的取舍,往往是牵一发而动全身,需要全盘考虑各方面的影响。但也有一些基础的理论和原则,理解这些理论并遵循这些原则会让你的性能调优任务将会更加轻松。为了更好的理解本篇所介绍的内容。你需要已经了解和遵循以下内容:1、已了解jvm 垃圾收集器2、已了解jvm 性能监控常用工具3、能够读懂gc日志4、确信不为了调优而调优,jvm调优不能解决一切性能问题如果对这些不了解不建议读本篇文章。本篇文章基于jvm性能调优,结合jvm的各项参数对应用程序调优,主要内容有以下几个方面:1、jvm调优的一般流程2、jvm调优所要关注的几个性能指标3、jvm调优需要掌握的一些原则4、调优策略&示例一、性能调优的层次为了提升系统性能,我们需要对系统的各个角度和层次来进行优化,以下是需要优化的几个层次。从上面我们可以看到,除了jvm调优以外,还有其他几个层面需要来处理,所以针对系统的调优不是只有jvm调优一项,而是需要针对系统来整体调优,才能提升系统的性能。本篇只针对jvm调优来讲解,其他几个方面,后续再介绍。在进行jvm调优之前,我们假设项目的架构调优和代码调优已经进行过或者是针对当前项目是最优的。这两个是jvm调优的基础,并且架构调优是对系统影响最大的 ,我们不能指望一个系统架构有缺陷或者代码层次优化没有穷尽的应用,通过jvm调优令其达到一个质的飞跃,这是不可能的。另外,在调优之前,必须得有明确的性能优化目标, 然后找到其性能瓶颈。之后针对瓶颈的优化,还需要对应用进行压力和基准测试,通过各种监控和统计工具,确认调优后的应用是否已经达到相关目标。二、jvm调优流程调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐。jvm的调优也不例外,jvm调优主要是针对垃圾收集器的收集性能优化,令运行在虚拟机上的应用能够使用更少的内存以及延迟获取更大的吞吐量。当然这里的最少是最优的选择,而不是越少越好。1、性能定义要查找和评估器性能瓶颈,首先要知道性能定义,对于jvm调优来说,我们需要知道以下三个定义属性,依作为评估基础:吞吐量:重要指标之一,是指不考虑垃圾收集引起的停顿时间或内存消耗,垃圾收集器能支撑应用达到的最高性能指标。延迟:其度量标准是缩短由于垃圾啊收集引起的停顿时间或者完全消除因垃圾收集所引起的停顿,避免应用运行时发生抖动。内存占用:垃圾收集器流畅运行所需要 的内存数量。这三个属性中,其中一个任何一个属性性能的提高,几乎都是以另外一个或者两个属性性能的损失作代价,不可兼得,具体某一个属性或者两个属性的性能对应用来说比较重要,要基于应用的业务需求来确定。2、性能调优原则在调优过程中,我们应该谨记以下3个原则,以便帮助我们更轻松的完成垃圾收集的调优,从而达到应用程序的性能要求。1. MinorGC回收原则: 每次minor GC 都要尽可能多的收集垃圾对象。以减少应用程序发生Full GC的频率。2. GC内存最大化原则:处理吞吐量和延迟问题时候,垃圾处理器能使用的内存越大,垃圾收集的效果越好,应用程序也会越来越流畅。3. GC调优3选2原则: 在性能属性里面,吞吐量、延迟、内存占用,我们只能选择其中两个进行调优,不可三者兼得。3、性能调优流程以上就是对应用程序进行jvm调优的基本流程,我们可以看到,jvm调优是根据性能测试结果不断优化配置而多次迭代的过程。在达到每一个系统需求指标之前,之前的每个步骤都有可能经历多次迭代。有时候为了达到某一方面的指标,有可能需要对之前的参数进行多次调整,进而需要把之前的所有步骤重新测试一遍。另外调优一般是从满足程序的内存使用需求开始的,之后是时间延迟的要求,最后才是吞吐量的要求,要基于这个步骤来不断优化,每一个步骤都是进行下一步的基础,不可逆行之。以下我们针对每个步骤进行详细的示例讲解。在JVM的运行模式方面,我们直接选择server模式,这也是jdk1.6以后官方推荐的模式。在垃圾收集器方面,我们直接采用了jdk1.6-1.8 中默认的parallel收集器(新生代采用parallelGC,老生代采用parallelOldGC)。三、确定内存占用在确定内存占用之前,我们需要知道两个知识点:应用程序的运行阶段jvm内存分配1、运行阶段应用程序的运行阶段,我可以划分为以下三个阶段:1、初始化阶段 : jvm加载应用程序,初始化应用程序的主要模块和数据。2、稳定阶段:应用在此时运行了大多数时间,经历过压力测试的之后,各项性能参数呈稳定状态。核心函数被执行,已经被jit编译预热过。3、总结阶段:最后的总结阶段,进行一些基准测试,生成响应的策报告。这个阶段我们可以不关注。确定内存占用以及活跃数据的大小,我们应该是在程序的稳定阶段来进行确定,而不是在项目起初阶段来进行确定,如何确定,我们先看以下jvm的内存分配。2、jvm内存分配&参数jvm堆中主要的空间,就是以上新生代、老生代、永久代组成,整个堆大小=新生代大小 + 老生代大小 + 永久代大小。 具体的对象提升方式,这里不再过多介绍了,我们看下一些jvm命令参数,对堆大小的指定。如果不采用以下参数进行指定的话,虚拟机会自动选择合适的值,同时也会基于系统的开销自动调整。在设置的时候,如果关注性能开销的话,应尽量把永久代的初始值与最大值设置为同一值,因为永久代的大小调整需要进行FullGC 才能实现。3、计算活跃数据大小计算活跃数据大小应该遵循以下流程:如前所述,活跃数据应该是基于应用程序稳定阶段时,观察长期存活与对象在java堆中占用的空间大小。计算活跃数据时应该确保以下条件发生:1.测试时,启动参数采用jvm默认参数,不人为设置。2.确保Full GC 发生时,应用程序正处于稳定阶段。采用jvm默认参数启动,是为了观察应用程序在稳定阶段的所需要的内存使用。如何才算稳定阶段?一定得需要产生足够的压力,找到应用程序和生产环境高峰符合状态类似的负荷,在此之后达到峰值之后,保持一个稳定的状态,才算是一个稳定阶段。所以要达到稳定阶段,压力测试是必不可少的,具体如何如何对应用压力测试,本篇不过多说明,后期会有专门介绍的篇幅。在确定了应用出于稳定阶段的时候,要注意观察应用的GC日志,特别是Full GC 日志。GC日志指令: -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename>GC日志是收集调优所需信息的最好途径,即便是在生产环境,也可以开启GC日志来定位问题,开启GC日志对性能的影响极小,却可以提供丰富数据。必须得有FullGC 日志,如果没有的话,可以采用监控工具强制调用一次,或者采用以下命令,亦可以触发jmap -histo:live pid在稳定阶段触发了FullGC我们一般会拿到如下信息:从以上gc日志中,我们大概可以分析到,在发生fullGC之时,整个应用的堆占用以及GC时间,当然了,为了更加精确,应该多收集几次,获取一个平均值。或者是采用耗时最长的一次FullGC来进行估算。在上图中,fullGC之后,老年代空间占用在93168kb(约93MB),我们以此定为老年代空间的活跃数据。其他堆空间的分配,基于以下规则来进行。基于以上规则和上图中的FullGC信息,我们现在可以规划的该应用堆空间为:java 堆空间: 373Mb (=老年代空间93168kb4)新生代空间:140Mb(=老年代空间93168kb1.5)永久代空间:5Mb(=永久代空间3135kb1.5)老年代空间: 233Mb=堆空间-新生代看空间=373Mb-140Mb对应的应用启动参数应该为:java -Xms373m -Xmx373m -Xmn140m -XX:PermSize=5m -XX:MaxPermSize=5m四、延迟调优在确定了应用程序的活跃数据大小之后,我们需要再进行延迟性调优,因为对于此时堆内存大小,延迟性需求无法达到应用的需要,需要基于应用的情况来进行调试。在这一步进行期间,我们可能会再次优化堆大小的配置,评估GC的持续时间和频率、以及是否需要切换到不同的垃圾收集器上。1、系统延迟需求在调优之前,我们需要知道系统的延迟需求是那些,以及对应的延迟可调优指标是那些。应用程序可接受的平均停滞时间: 此时间与测量的Minor GC持续时间进行比较。可接受的Minor GC频率:Minor GC的频率与可容忍的值进行比较。可接受的最大停顿时间: 最大停顿时间与最差情况下FullGC的持续时间进行比较。可接受的最大停顿发生的频率:基本就是FullGC的频率。以上中,平均停滞时间和最大停顿时间,对用户体验最为重要,可以多关注。基于以上的要求,我们需要统计以下数据:MinorGC的持续时间;统计MinorGC的次数;FullGC的最差持续时间;最差情况下,FullGC的频率;2、优化新生代的大小比如如上的gc日志中,我们可以看到Minor GC的平均持续时间=0.069秒,MinorGC 的频率为0.389秒一次。如果,我们系统的设置的平均停滞时间为50ms,当前的69ms明显是太长了,就需要调整。我们知道新生代空间越大,Minor GC的GC时间越长,频率越低。如果想减少其持续时长,就需要减少其空间大小。如果想减小其频率,就需要加大其空间大小。为了降低改变新生代的大小对其他区域的最小影响。在改变新生代空间大小的时候,尽量保持老年代空间的大小。比如此次减少了新生代空间10%的大小,应该保持老年代和持代的大小不变化,第一步调优后的参数如下变化:java -Xms359m -Xmx359m -Xmn126m -XX:PermSize=5m -XX:MaxPermSize=5m新生代的大小有140m变为126,堆大小顺应变化,此时老年代是没有变化的。3、优化老年代的大小同上一步一样,在优化之前,也需要采集gc日志的数据。此次我们关注的是FullGC的持续时间和频率。上图中,我们可以看到FullGC 平均频率 =5.8sFullGC 平均持续时间=0.14s(以上为了测试,真实项目的fullGC 没有这么快)如果没有FullGC的日志,有办法可以评估么?我们可以通过对象提升率进行计算。对象提升率比如上述中启动参数中,我们的老年代大小=233Mb。那么需要多久才能填满老年代中这233Mb的空闲空间取决于新生代到老年代的提升率。每次提升老年代占用量=每次MinorGC 之后 java堆占用情况 减去 MinorGC后新生代的空间占用对象提升率=平均值(每次提升老年代占用量) 除以 老年代空间有了对象提升率,我们就可以算出填充满老年代空间需要多少次minorGC,大概一次fullGC的时间就可以计算出来了。比如:上图中:第一次minor GC 之后,老年代空间:13740kb - 13732kb =8kb第二次minor GC 之后,老年代空间:22394kb - 17905kb =4489kb第三次minor GC 之后,老年代空间:34739kb - 17917kb =16822kb第四次minor GC 之后,老年代空间:48143kb - 17913kb =30230kb第五次minor GC 之后,老年代空间:62112kb - 17917kb =44195kb老年代每次minorGC提升率4481kb 第二次和第一次minorGC之间12333kb 第3次和第2次minorGC之间13408kb 第4次和第3次minorGC之间13965kb 第5次和第4次minorGC之间我们可以测算出:每次minorGC 的平均提升为12211kb,约为12Mb上图中,平均minorGC的频率为 213ms/次提升率=12211kb/213ms=57kb/ms老年代空间233Mb ,占满大概需要2331024/57=4185ms 约为4.185s。FullGC的预期最差频率时长可以通过以上两种方式估算出来,可以调整老年代的大小来调整FullGC的频率,当然了,如果FullGC持续时间过长,无法达到应用程序的最差延迟要求,就需要切换垃圾处理器了。具体如何切换,下篇再讲,比如切换为CMS,针对CMS的调优方式又有会细微的差别。五、吞吐量调优经过上述漫长 调优过程,最终来到了调优的最后一步,这一步对上述的结果进行吞吐量测试,并进行微调。吞吐量调优主要是基于应用程序的吞吐量要求而来的,应用程序应该有一个综合的吞吐指标,这个指标基于真个应用的需求和测试而衍生出来的。当有应用程序的吞吐量达到或者超过预期的吞吐目标,整个调优过程就可以圆满结束了。如果出现调优后依然无法达到应用程序的吞吐目标,需要重新回顾吞吐要求,评估当前吞吐量和目标差距是否巨大,如果在20%左右,可以修改参数,加大内存,再次从头调试,如果巨大就需要从整个应用层面来考虑,设计以及目标是否一致了,重新评估吞吐目标。对于垃圾收集器来说,提升吞吐量的性能调优的目标就是就是尽可能避免或者很少发生FullGC 或者Stop-The-World压缩式垃圾收集(CMS),因为这两种方式都会造成应用程序吞吐降低。尽量在MinorGC 阶段回收更多的对象,避免对象提升过快到老年代。六、最后据Plumbr公司对特定垃圾收集器使用情况进行了一次调查研究,研究数据使用了84936个案例。在明确指定垃圾收集器的13%的案例中,并发收集器(CMS)使用次数最多;但大多数案例没有选择最佳垃圾收集器。这个比例占用在87%左右。JVM调优是一个系统而又复杂的工作,目前jvm下的自动调整已经做的比较优秀,基本的一些初始参数都可以保证一般的应用跑的比较稳定了,对部分团队来说,程序性能可能优先级不高,默认垃圾收集器已经够用了。调优要基于自己的情况而来。本文作者:wier_ali阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 18, 2019 · 1 min · jiezi

可应用于实际的14个NLP突破性研究成果(四)

摘要:最好的论文是可以直接走出实验室!NLP年度最佳应用论文大集锦!可应用于实际的14个NLP突破性研究成果(一) 可应用于实际的14个NLP突破性研究成果(二) 可应用于实际的14个NLP突破性研究成果(三)11.对序列建模的通用卷积和递归网络的实证评估作者:SHAOJIE BAI,J。ZICO KOLTER,VLADLEN KOLTUN论文摘要对于大多数深度学习实践者来说,序列建模与循环网络是同义词。然而,最近的研究结果表明,卷积架构在语音合成和机器翻译等任务上的表现优于循环网络。给定一个新的序列建模任务或数据集,应该使用哪种架构?我们对序列建模的一般卷积和循环架构进行了系统的评价。我们在广泛的标准任务中评估这些模型。我们的结果表明,一个简单的卷积架构在不同的任务和数据集上的表现优于LSTM等典型的循环网络。我们的结论是,需要重新考虑序列建模和循环网络之间的共同关联,卷积网络应该被视为序列建模任务的一个自然起点我们提供了相关代码:http://github.com/locuslab/TCN。总结本文的作者质疑了一个常见假设,即循环架构应该是序列建模任务的默认起点。他们的结果表明,时间卷积网络(TCN)在多个序列建模任务中明显优于长短期记忆网络(LSTMs)和门控循环单元网络等典型的循环架构。论文的核心思想是什么?1、时间卷积网络(TCN)是基于最近提出的最佳实践(如扩张卷积和残差连接)设计的,它在一系列复杂的序列建模任务中表现得明显优于通用的循环架构。2、TCN表现出比循环架构更长的记忆,因此更适合需要较长的历史记录的任务。关键成就是什么?在序列建模任务上提供了卷积架构和循环体系结构系统的比较。设计了卷积体系结构,它可以作为序列建模任务的方便且强大的起点。AI社区的对其评价?在使用RNN之前,一定要先从CNN开始。未来的研究领域是什么?为了提高TCN在不同序列建模任务中的性能,需要进一步精化架构和算法。可能应用的商业领域?引入TCN可以提高依赖于循环架构进行序列建模的AI系统的性能。其中包括以下任务:§机器翻译;§语音识别;§音乐和语音生成。你在哪里可以得到代码?1、如论文摘要所述,研究人员通过GitHub存储库提供了官方代码。2、你还可以查看PhilippeRémy提供的Keras实施的TCN。12.用于文本分类的通用语言模型微调-ULMFiT作者:JEREMY HOWARD和SEBASTIAN RUDER论文摘要迁移学习在计算机视觉方面取得了很多成功,但是同样的方法应用在NLP领域却行不通。所以我们提出了通用语言模型微调(ULMFiT),这是一种有效的转移学习方法,可以应用于NLP中的任何任务。该方法在6个文本分类任务上的性能明显优于现有的文本分类方法,在大部分的数据集上测试使得错误率降低了18-24%。此外,仅有100个标记样本训练的结果也相当不错。我们已经开源我们的预训练模型和代码。总结Howard和Ruder建议使用预先训练的模型来解决各种NLP问题。使用这种方法的好处是你无需从头开始训练模型,只需对原始模型进行微调。通用语言模型微调(ULMFiT)的方法优于最先进的结果,它将误差降低了18-24%。更重要的是,ULMFiT可以只使用100个标记示例,就能与10K标记示例中从头开始训练的模型的性能相匹配。论文的核心思想是什么?为了解决缺乏标记数据的难题,研究人员建议将转移学习应用于NLP问题。因此,你可以使用另一个经过训练的模型来解决类似问题作为基础,然后微调原始模型以解决你的特定问题,而不是从头开始训练模型。但是,这种微调应该考虑到几个重要的考虑因素:§不同的层应该进行不同程度地微调,因为它们捕获不同类型的信息。§如果学习速率首先线性增加然后线性衰减,则将模型的参数调整为任务特定的特征将更有效。§微调所有层可能会导致灾难性的遗忘;因此,从最后一层开始逐渐微调模型可能会更好。关键成就是什么?显著优于最先进的技术:将误差降低18-24%;所需的标记数据要少得多,但性能可以保障。AI社区对其的看法是什么?预先训练的ImageNet模型的可用性已经改变了计算机视觉领域,ULMFiT对于NLP问题可能具有相同的重要性。此方法可以应用于任何语言的任何NLP任务。未来的研究领域的方向是什么?改进语言模型预训练和微调。将这种新方法应用于新的任务和模型(例如,序列标记、自然语言生成、蕴涵或问答)。可能应用的商业领域?ULMFiT可以更有效地解决各种NLP问题,包括:§识别垃圾邮件、机器人、攻击性评论;§按特定功能对文章进行分组;§对正面和负面评论进行分类;§查找相关文件等你在哪里可以得到实现代码?Fast.ai提供ULMFiT的官方实施,用于文本分类,并作为fast.ai库的一部分。13.用非监督学习来提升语言理解,作者:ALEC RADFORD,KARTHIK NARASIMHAN,TIM SALIMANS,ILYA SUTSKEVER论文摘要自然语言理解包括各种各样的任务,例如文本蕴涵、问答、语义相似性评估和文档分类。虽然大量未标记的文本语料库很丰富,但用于学习这些特定任务的标记数据很少。我们证明,通过对多种未标记文本语料库中的语言模型进行生成预训练,然后对每项特定任务进行辨别性微调,可以实现这些任务的巨大收益。与以前的方法相比,我们在微调期间利用任务感知输入转换来实现有效传输,同时对模型架构进行最少的更改。我们证明了我们的方法在广泛的自然语言理解基准上的有效性。例如,我们在常识推理(Stories Cloze Test)上获得8.9%的性能改善,在问答(RACE)上达到5.7%,在文本蕴涵(MultiNLI)上达到1.5%。总结OpenAI团队建议通过在多种未标记文本语料库中预先训练语言模型,然后使用标记数据集对每个特定任务的模型进行微调,从而可以显著改善了语言理解。他们还表明,使用Transformer模型而不是传统的递归神经网络可以显著提高模型的性能,这种方法在所研究的12项任务中有9项的表现优于之前的最佳结果。论文的核心思想是什么?通过在未标记数据上学习神经网络模型的初始参数,然后使用标记数据使这些参数适应特定任务,结合使用无监督预训练和监督微调。通过使用遍历样式方法避免跨任务对模型体系结构进行大量更改:§预训练模型是在连续的文本序列上训练的,但是问题回答或文本蕴涵等任务具有结构化输入。§解决方案是将结构化输入转换为预先训练的模型可以处理的有序序列。使用Transformer模型而不是LSTM,因为这些模型提供了更加结构化的内存,用于处理文本中的长期依赖关系。取得了什么关键成就?对于自然语言推理(NLI)的任务,通过在SciTail上获得5%的性能改进和在QNLI上获得5.8%的性能改进。对于QA和常识推理的任务,表现优于以前的最佳结果-在Story Cloze上高达8.9%,在RACE上高达5.7%。通过在QQP上实现4.2%的性能改善,刷新了3个语义相似性任务中的2个的最新结果。对于分类任务,获得CoLA的45.4分,而之前的最佳结果仅为35分。AI社区对其看法是什么?该论文通过使用基于Transformer模型而非LSTM扩展了ULMFiT研究,并将该方法应用于更广泛的任务。“这正是我们希望我们的ULMFiT工作能够发挥作用的地方!”Jeremy Howard,fast.ai的创始人。未来的研究领域是什么?进一步研究自然语言理解和其他领域的无监督学习,以便更好地理解无监督学习的时间和方式。可能应用的商业领域?OpenAI团队的方法通过无监督学习增强了自然语言理解,因此可以帮助标记数据集稀疏或不可靠的NLP应用。 在哪里可以得到实现代码?Open AI团队在GitHub上的公开了代码和模型。14.语境化词向量解析:架构和表示,作者:MATTHEW E. PETERS,MARK NEUMANN,LUKE ZETTLEMOYER,WEN-TAU YIH论文摘要最近研究显示从预训练的双向语言模型(biLM)导出的上下文词表示为广泛的NLP任务提供了对现有技术的改进。然而,关于这些模型如何以及为何如此有效的问题,仍然存在许多问题。在本文中,我们提出了一个详细的实证研究,探讨神经结构的选择(例如LSTM,CNN)如何影响最终任务的准确性和所学习的表征的定性属性。我们展示了如何在速度和准确性之间的权衡,但所有体系结构都学习了高质量的上下文表示,这些表示优于四个具有挑战性的NLP任务的字嵌入。此外,所有架构都学习随网络深度而变化的表示,从基于词嵌入层的专有形态学到基于较低上下文层的局部语法到较高范围的语义。总之,这些结果表明,无人监督的biLM正在学习更多关于语言结构的知识。总结今年早些时候艾伦人工智能研究所的团队介绍了ELMo嵌入,旨在更好地理解预训练的语言模型表示。为此,他们精心设计了无监督和监督任务上广泛研究学习的单词和跨度表示。研究结果表明,独立于体系结构的学习表示随网络深度而变化。论文的核心思想是什么?预训练的语言模型大大提高了许多NLP任务的性能,将错误率降低了10-25%。但是,仍然没有清楚地了解为什么以及如何在实践中进行预训练。为了更好地理解预训练的语言模型表示,研究人员凭经验研究神经结构的选择如何影响:§直接终端任务准确性;§学习表示的定性属性,即语境化词表示如何编码语法和语义的概念。什么是关键成就?确认在速度和准确度之间存在权衡,在评估的三种架构中-LSTM,Transformer和Gated CNN:§LSTM获得最高的准确度,但也是最慢的;§基于Transformer和CNN的模型比基于LSTM的模型快3倍,但也不太准确。证明由预先训练的双向语言模型(biLM)捕获的信息随网络深度而变化:§深度biLM的词嵌入层专注于词形态,与传统的词向量形成对比,传统的词向量在该层也编码一些语义信息;§biLM的最低上下文层只关注本地语法;证明了biLM激活可用于形成对语法任务有用的短语表示。AI社区对其看法是什么?该论文在EMNLP 2018上发表。 “对我来说,这确实证明了预训练的语言模型确实捕获了与在ImageNet上预训练的计算机视觉模型相似的属性。”AYLIEN的研究科学家Sebastian Ruder。未来的研究领域是什么?使用明确的句法结构或其他语言驱动的归纳偏见来增强模型。 将纯无监督的biLM训练目标与现有的注释资源以多任务或半监督方式相结合。可能应用的商业领域?1、通过更好地理解预训练语言模型表示所捕获的信息,研究人员可以构建更复杂的模型,并增强在业务环境中应用的NLP系统的性能。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。

February 18, 2019 · 1 min · jiezi

for-loop 与 json.Unmarshal 性能分析概要

原文地址:for-loop 与 json.Unmarshal 性能分析概要前言在项目中,常常会遇到循环交换赋值的数据处理场景,尤其是 RPC,数据交互格式要转为 Protobuf,赋值是无法避免的。一般会有如下几种做法:forfor rangejson.Marshal/Unmarshal这时候又面临 “选择困难症”,用哪个好?又想代码量少,又担心性能有没有影响啊…为了弄清楚这个疑惑,接下来将分别编写三种使用场景。来简单看看它们的性能情况,看看谁更 “好”功能代码…type Person struct { Name string json:"name" Age int json:"age" Avatar string json:"avatar" Type string json:"type"}type AgainPerson struct { Name string json:"name" Age int json:"age" Avatar string json:"avatar" Type string json:"type"}const MAX = 10000func InitPerson() []Person { var persons []Person for i := 0; i < MAX; i++ { persons = append(persons, Person{ Name: “EDDYCJY”, Age: i, Avatar: “https://github.com/EDDYCJY", Type: “Person”, }) } return persons}func ForStruct(p []Person, count int) { for i := 0; i < count; i++ { _, _ = i, p[i] }}func ForRangeStruct(p []Person) { for i, v := range p { _, _ = i, v }}func JsonToStruct(data []byte, againPerson []AgainPerson) ([]AgainPerson, error) { err := json.Unmarshal(data, &againPerson) return againPerson, err}func JsonIteratorToStruct(data []byte, againPerson []AgainPerson) ([]AgainPerson, error) { var jsonIter = jsoniter.ConfigCompatibleWithStandardLibrary err := jsonIter.Unmarshal(data, &againPerson) return againPerson, err}测试代码…func BenchmarkForStruct(b *testing.B) { person := InitPerson() count := len(person) b.ResetTimer() for i := 0; i < b.N; i++ { ForStruct(person, count) }}func BenchmarkForRangeStruct(b *testing.B) { person := InitPerson() b.ResetTimer() for i := 0; i < b.N; i++ { ForRangeStruct(person) }}func BenchmarkJsonToStruct(b *testing.B) { var ( person = InitPerson() againPersons []AgainPerson ) data, err := json.Marshal(person) if err != nil { b.Fatalf(“json.Marshal err: %v”, err) } b.ResetTimer() for i := 0; i < b.N; i++ { JsonToStruct(data, againPersons) }}func BenchmarkJsonIteratorToStruct(b *testing.B) { var ( person = InitPerson() againPersons []AgainPerson ) data, err := json.Marshal(person) if err != nil { b.Fatalf(“json.Marshal err: %v”, err) } b.ResetTimer() for i := 0; i < b.N; i++ { JsonIteratorToStruct(data, againPersons) }}测试结果BenchmarkForStruct-4 500000 3289 ns/op 0 B/op 0 allocs/opBenchmarkForRangeStruct-4 200000 9178 ns/op 0 B/op 0 allocs/opBenchmarkJsonToStruct-4 100 19173117 ns/op 2618509 B/op 40036 allocs/opBenchmarkJsonIteratorToStruct-4 300 4116491 ns/op 3694017 B/op 30047 allocs/op从测试结果来看,性能排名为:for < for range < json-iterator < encoding/json。接下来我们看看是什么原因导致了这样子的排名?性能对比for-loop在测试结果中,for range 在性能上相较 for 差。这是为什么呢?在这里我们可以参见 for range 的 实现,伪实现如下:for_temp := rangelen_temp := len(for_temp)for index_temp = 0; index_temp < len_temp; index_temp++ { value_temp = for_temp[index_temp] index = index_temp value = value_temp original body}通过分析伪实现,可得知 for range 相较 for 多做了如下事项ExpressionRangeClause = [ ExpressionList “=” | IdentifierList “:=” ] “range” Expression .在循环开始之前会对范围表达式进行求值,多做了 “解” 表达式的动作,得到了最终的范围值Copy…value_temp = for_temp[index_temp]index = index_tempvalue = value_temp…从伪实现上可以得出,for range 始终使用值拷贝的方式来生成循环变量。通俗来讲,就是在每次循环时,都会对循环变量重新分配小结通过上述的分析,可得知其比 for 慢的原因是 for range 有额外的性能开销,主要为值拷贝的动作导致的性能下降。这是它慢的原因那么其实在 for range 中,我们可以使用 _ 和 T[i] 也能达到和 for 差不多的性能。但这可能不是 for range 的设计本意了json.Marshal/Unmarshalencoding/jsonjson 互转是在三种方案中最慢的,这是为什么呢?众所皆知,官方的 encoding/json 标准库,是通过大量反射来实现的。那么 “慢”,也是必然的。可参见下述代码:…func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { … switch t.Kind() { case reflect.Bool: return boolEncoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intEncoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintEncoder case reflect.Float32: return float32Encoder case reflect.Float64: return float64Encoder case reflect.String: return stringEncoder case reflect.Interface: return interfaceEncoder case reflect.Struct: return newStructEncoder(t) case reflect.Map: return newMapEncoder(t) case reflect.Slice: return newSliceEncoder(t) case reflect.Array: return newArrayEncoder(t) case reflect.Ptr: return newPtrEncoder(t) default: return unsupportedTypeEncoder }}既然官方的标准库存在一定的 “问题”,那么有没有其他解决方法呢?目前在社区里,大多为两类方案。如下:预编译生成代码(提前确定类型),可以解决运行时的反射带来的性能开销。缺点是增加了预生成的步骤优化序列化的逻辑,性能达到最大化接下来的实验,我们用第二种方案的库来测试,看看有没有改变。另外也推荐大家了解如下项目:json-iterator/gomailru/easyjsonpquerna/ffjsonjson-iterator/go目前社区较常用的是 json-iterator/go,我们在测试代码中用到了它它的用法与标准库 100% 兼容,并且性能有较大提升。我们一起粗略的看下是怎么做到的,如下:reflect2利用 modern-go/reflect2 减少运行时调度开销…type StructDescriptor struct { Type reflect2.Type Fields []*Binding}…type Binding struct { levels []int Field reflect2.StructField FromNames []string ToNames []string Encoder ValEncoder Decoder ValDecoder}type Extension interface { UpdateStructDescriptor(structDescriptor *StructDescriptor) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder CreateMapKeyEncoder(typ reflect2.Type) ValEncoder CreateDecoder(typ reflect2.Type) ValDecoder CreateEncoder(typ reflect2.Type) ValEncoder DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder}struct Encoder/Decoder Cache类型为 struct 时,只需要反射一次 Name 和 Type,会缓存 struct Encoder 和 Decodervar typeDecoders = map[string]ValDecoder{}var fieldDecoders = map[string]ValDecoder{}var typeEncoders = map[string]ValEncoder{}var fieldEncoders = map[string]ValEncoder{}var extensions = []Extension{}….fieldNames := calcFieldNames(field.Name(), tagParts[0], tag)fieldCacheKey := fmt.Sprintf("%s/%s”, typ.String(), field.Name())decoder := fieldDecoders[fieldCacheKey]if decoder == nil { decoder = decoderOfType(ctx.append(field.Name()), field.Type())}encoder := fieldEncoders[fieldCacheKey]if encoder == nil { encoder = encoderOfType(ctx.append(field.Name()), field.Type())}文本解析优化小结相较于官方标准库,第三方库 json-iterator/go 在运行时上做的更好。这是它快的原因有个需要注意的点,在 Go1.10 后 map 类型与标准库的已经没有太大的性能差异。但是,例如 struct 类型等仍然有较大的性能提高总结在本文中,我们首先进行了性能测试,再分析了不同方案,得知为什么了快慢的原因。那么最终在选择方案时,可以根据不同的应用场景去抉择:对性能开销有较高要求:选用 for,开销最小中规中矩:选用 for range,大对象慎用量小、占用小、数量可控:选用 json.Marshal/Unmarshal 的方案也可以。其重复代码少,但开销最大在绝大多数场景中,使用哪种并没有太大的影响。但作为工程师你应当清楚其利弊。以上就是不同的方案分析概要,希望对你有所帮助 :) ...

February 17, 2019 · 3 min · jiezi

可应用于实际的14个NLP突破性研究成果(二)

摘要: 最好的论文是可以直接走出实验室!NLP年度最佳应用论文大集锦!可应用于实际的14个NLP突破性研究成果(一)4.What you can cram into a single vector: Probing sentence embeddings for linguistic properties,作者:ALEXIS CONNEAU,KRUSZEWSKI,GUILLAUME LAMPLE,LOÏCBARRAULT,MARCO BARONI论文摘要尽管最近在训练高质量的句子嵌入上做出了很多的努力,但是大家仍然对它们所捕捉的内容缺乏了解。基于句子分类的‘Downstream’tasks通常用于评估句子表示的质量。然而任务的复杂性使得它很难推断出句子表示中出现了什么样的信息。在本文将介绍10个probing tasks,旨在捕捉句子的简单语言特征,并用它们来研究由三种不同编码器产生的句子嵌入,这些编码器以八种不同的方式进行训练,揭示了编码器和训练方法的有趣特性。总结Facebook AI研究团队试图更好地理解句子嵌入所捕获的内容。因为任务的复杂性不允许我们直接获得理解。因此,论文介绍了10个旨在捕捉句子简单语言特征的探究任务。通过这些探测任务获得的结果可以揭示编码器和训练方法的一些有趣特性。论文的核心思想是什么?我们有许多句子嵌入方法,表现出非常好的表现,但我们仍然缺乏对它们如何捕获的内容的理解。研究人员通过引入10个探测任务来研究由3种不同编码器(BiLSTM-last,BiLSTM-max和Gated ConvNet)生成的嵌入来解决这个问题,这些编码器以8种不同的方式进行训练。探测任务测试句子嵌入保留的程度:1. 表面信息(句子中的单词数、单词内容);2. 句法信息(词序、句子的层次结构、最高成分的顺序);3. 语义信息(主句动词的时态、主语和宾语的数量、随机替换的单词)。什么是关键成就?对现代句子编码器进行广泛的评估。揭示编码器和训练方法的一些有趣属性:1. 由于自然语言输入的冗余,Bag-of-Vectors所擅长得捕获句子级属性令人惊讶。2. 相似性能的不同编码器架构可导致不同的嵌入。3. 卷积架构的整体探测任务性能与最佳LSTM架构的性能相当。4. BiLSTM-max在探测任务中优于BiLSTM。此外,即使没有经过任何训练,它也能实现非常好的性能。未来的研究领域是什么?将探测任务扩展到其他语言和语言域。调查多任务训练如何影响探测任务的性能。通过引入的探测任务,找到更多具有语言意识的通用编码器。什么是可能的商业应用?1、更好地理解不同预训练编码器捕获的信息将有助于研究人员构建更多具有语言意识的编码器。反过来,这将改善将会被应用在NLP系统中。你在哪里可以得到实现代码?1、GitHub上提供了本研究论文中描述的探测任务。5.SWAG:一个用于给定信息的常识推理的大规模对抗性数据集,作者:ROWAN ZELLERS,YONATAN BISK,ROY SCHWARTZ,YEJIN CHOI论文摘要人类可以因为一些描述从而推断出下面要发生什么,例如“她打开汽车的引擎盖”,“然后,她检查了发动机”。在本文中,我们介绍并整理了基础常识推理。我们提出SWAG,一个新的数据集,包含113k多项选择问题,涉及丰富的基础推理。为了解决许多现有数据集中发现的注释工件和人类偏见的反复出现的挑战,我们提出了一种新颖的过程,它通过迭代训练一组风格分类器构建一个去偏见的数据集,并使用它们来过滤数据。为了解释对抗性过滤,我们使用最先进的语言模型来大量过滤一组不同的潜在反事实。实证结果表明,虽然人类可以高精度地解决由此产生的推理问题(88%),但各种竞争模型仍在努力完成我们的任务。总结当你读到“他将生鸡蛋面糊倒入锅中时,他…”你可能会这样选择“提起锅并移动它来搅拌。”我们可以发现,答案并不明显,这需要常识推理。SWAG是支持研究自然语言推理(NLI)与常识推理大规模数据集。它是使用一种新颖的方法——对抗性过滤创建的,它可以以最经济有效的方式构建未来的大规模数据集。论文的核心思想是什么?SWAG包含113K多项选择题,大多是视频字幕:1、上下文的句子来自于视频字幕。2、正确的答案是实际视频中的下一个字幕。3、使用对抗过滤(AF)生成错误的答案。Adversarial Filtering背后的想法:1、大量生成错误答案,然后选择哪些看起来像真正答案的回答。2、过滤模型确定哪些回答似乎是机器生成的。这些回答被删除并替换为模型认为是人为编写的新回答。最后,整个数据集由众包工作者验证。什么关键成就?提出一个新的具有挑战性的大规模数据集来测试NLI系统。引入Adversarial Filtering,这种方法可用于经济高效地构建大型数据集,具有以下几个优点:1、句子的多样性不受人类创造力的限制;2、数据集创建者可以在数据集构建期间任意提高难度;3、人类不会写回答但只会验证它们,这样更经济;AI社区的想法是什么?该论文在2018年一个自然语言处理领域领先的会议上被发表。即使在此重要的NLP会议上发布之前,该数据集也是通过Google的新BERT模型解决的,该模型的准确度达到了86.2%并且非常接近人类的准确度(88%)。未来的研究领域是什么?使用更好的Adversarial Filtering和语言模型创建更具对抗性的SWAG版本。什么是可能的商业应用?该数据集可以帮助构建具有常识推理的NLI系统,从而改善Q&A系统和会话AI的开发。你在哪里可以获得实现代码?1、SWAG数据集可在GitHub上获得。6.(ELMO词向量模型)作者:MATTHEW E. PETERS,MARK NEUMANN,MOHIT IYYER,MATT GARDNER,CHRISTOPHER CLARK,KENTON LEE,LUKE ZETTLEMOYER论文摘要本文推出了一种新的基于深度学习框架的词向量表征模型,这种模型不仅能够表征词汇的语法和语义层面的特征,也能够随着上下文语境的变换而改变。简单来说,本文的模型其实本质上就是基于大规模语料训练后的双向语言模型内部隐状态特征的组合。实验证明,新的词向量模型能够很轻松的与NLP的现有主流模型相结合,并且在六大NLP任务的结果上有着巨头的提升。同时,作者也发现对模型的预训练是十分关键的,能够让下游模型去融合不同类型的半监督训练出的特征。总结艾伦人工智能研究所的团队引入了一种新型的深层语境化词汇表示:语言模型嵌入(ELMo)。在ELMO增强模型中,每个单词都是根据使用它的整个上下文进行矢量化的。将ELMo添加到现有NLP系统可以实现:1:相对误差减少范围从6-20%;2:显著降低训练模型所需的时期数量;3:显著减少达到基线性能所需的训练数据量。论文的核心思想是什么?生成词嵌入作为深度双向语言模型(biLM)的内部状态的加权和,在大文本语料库上预训练。包括来自biLM的所有层的表示,因为不同的层表示不同类型的信息。基于角色的ELMo表示,以便网络可以使用形态线索来“理解”在训练中看不到的词汇外令牌。取得了什么关键成就?将ELMo添加到模型中会创造新的记录,在诸如问答、文本蕴涵、语义角色标记、共指解析、命名实体提取、情绪分析等NLP任务中相对误差降低6-20%。使用ELMo增强模型可显著着降低达到最优性能所需的训练次数。因此,具有ELMo的语义角色标签(SRL)模型仅需要10个时期就可以超过在486个训练时期之后达到的基线最大值。将ELMo引入模型还可以显著减少实现相同性能水平所需的训练数据量。例如,对于SRL任务,ELMo增强模型仅需要训练集的1%即可获得与具有10%训练数据的基线模型相同的性能。AI社区对其的评价?该论文被NAACL评为优秀论文,NAACL是世界上最具影响力的NLP会议之一。本文介绍的ELMo方法被认为是2018年最大的突破之一,也是NLP未来几年的主要趋势。未来的研究领域是什么?1、通过将ELMos与不依赖于上下文的词嵌入连接起来,将此方法合并到特定任务中。可能的商业应用的范围是什么?ELMo显著提高了现有NLP系统的性能,从而增强了:1. 聊天机器人将更好地理解人类和回答问题;2. 对客户的正面和负面评论进行分类;3. 查找相关信息和文件等;你在哪里可以得到实现代码?艾伦研究所提供英语和葡萄牙语预训练的ELMo模型,你还可以使用TensorFlow代码重新训练模型。7.用于低资源神经机器翻译的元学习,作者:JIATAO GU,WANG WANG,YUN YUN,KYUNGHYUN CHO,VICTOR OK LI论文摘要在本文中,我们建议扩展最近引入的模型:不可知元学习算法(MAML),用于低资源神经机器翻译(NMT)。我们将低资源翻译构建为元学习问题,并且我们学习基于多语言高资源语言任务来适应低资源语言。我们使用通用词汇表示来克服不同语言的输入输出不匹配的问题。我们使用十八种欧洲语言(Bg,Cs,Da,De,El,Es,Et,Fr,Hu,It,Lt,Nl,Pl,Pt,Sk,Sl,Sv和Ru)评估所提出的元学习策略,源任务和五种不同的语言(Ro,Lv,Fi,Tr和Ko)作为目标任务。我们证实了,所提出的方法明显优于基于多语言迁移学习的方法,这能够使我们只用一小部分训练样例来训练有竞争力的NMT系统。例如,通过通过16000个翻译单词(约600个并行句子),用所提出的方法在罗马尼亚语-英语WMT'16上实现高达22.04 BLEU。总结香港大学和纽约大学的研究人员使用模型无关的元学习算法(MAML)来解决低资源机器翻译的问题。特别是,他们建议使用许多高资源语言对来查找模型的初始参数,然后,这种初始化允许仅使用几个学习步骤在低资源语言对上训练新的语言模型。论文的核心思想是什么?介绍了一种新的元学习方法MetaNMT,该方法假设使用许多高资源语言对来找到良好的初始参数,然后从找到的初始参数开始在低资源语言上训练新的翻译模型。只有在所有源和目标任务之间共享输入和输出空间时,元学习才能应用于低资源机器翻译。然而,由于不同的语言具有不同的词汇。为了解决这个问题,研究人员使用键值存储网络动态地构建了针对每种语言的词汇表。关键成就是什么?为极低资源语言找到了神经机器翻译的新方法,其中:1、能够在高资源和极低资源语言对之间共享信息;2、仅使用几千个句子来微调低资源语言对上的新翻译模型;实验证明:1、元学习始终比多语言迁移学习好;2、元学习验证集语言对的选择会影响结果模型的性能。例如,当使用罗马尼亚语-英语进行验证时,芬兰语-英语受益更多,而土耳其语-英语则更喜欢拉脱维亚语-英语的验证。AI社区对它的看法?该论文在自然语言处理领域领先的会议EMNLP上被发表。所提出的方法获得了Facebook的低资源神经机器翻译奖。未来的研究领域是什么?半监督神经机器翻译的元学习或单语语料库的学习。当学习多个元模型且新语言可以自由选择适应的模型时,进行多模态元学习。什么是可能的商业应用?MetaNMT可用于改善可用并行语料库非常小的语言对的机器翻译结果。你在哪里可以得到实现代码?1、MetaNMT的PyTorch实施可以在Github上找到。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 30, 2019 · 1 min · jiezi

阿里资深技术专家:优秀的数据库存储引擎应具备哪些能力?

摘要: 作为数据库的底盘,一个成熟的存储引擎如何实现高效数据存取?导读本文作者是阿里巴巴OLTP数据库团队资深技术专家——曲山。作为自研高性能、低成本存储引擎X-Engine的负责人,曲山眼中的优秀关系型数据库存储引擎应该具备哪些能力呢?正文数据库内核按层次来分,就是两层:SQL & Storage。SQL Layer负责将你输入的SQL statement通过一系列步骤(parse/resolve/rewrite/optimize…)转换成物理执行计划,同时负责计划的执行,执行计划通常是一颗树的形式,其中树的叶子节点(执行器算子)部分往往负责单表的数据操作,这些操作算子就要在storage layer来执行了。因此,一个数据库存储引擎的主要工作,简单来讲就是存取数据,但是前提是保证数据库的ACID(atomicity/consistency/isolation/durability)语义。存储引擎对外提供的接口其实比较简单,主要就是数据写入/修改/查询,事务处理(start transaction/commit/rollback…),修改schema对象/数据字典(可选), 数据统计,还有一些周边的运维或数据导入导出功能。仅仅从功能上来说,要实现一个存储引擎似乎并不困难,如今也有很多Key-Value Store摇身一变就成为了数据库存储引擎,无非是加上一套事务处理机制罢了。但是作为数据库的底盘,一个成熟的存储引擎必须要考虑效率,如何高效(性能/成本最大化)的实现数据存取则成了在设计上做出种种权衡的主要考量。可以从存储引擎的几个主要组件来讨论:数据组织数据在内存和磁盘中的组织方式很大程度上决定了存取的效率,不同的应用场景选择也不同,典型的如:数据按行存储(NSM),对事务处理比较友好,因为事务数据总是完整行写进来, 多用于OLTP场景。按列存储(DSM),把tuples中相同的列值物理上存储在一起,这样只需要读取需要的列,在大规模数据扫描时减少大量I/O。另外列存做压缩的效果更好,适合OLAP场景,但是事务处理就不那么方便,需要做行转列。所以大部分AP数据库事务处理效率都不怎么高,某些甚至只支持批量导入。混合存储(FSM),行列混合布局,有把数据先按行分组(Segment, SubPage),组内使用DSM组织,如PAX, RCFile,也有先按列分组(Column Group),组内指定的列按NSM组织,如Peloton的Tile。此种格式试图结合NSM和DSM两者的优点,达到处理混合负载(HTAP)的目的,但是同时也继承了两者的缺点。所以做存储引擎,一开始就要面临选择何种存储格式的问题。即便选定了大类,每种格式中也有无数的细节需要考虑,每种数据类型的字段如何编码(Encoding),行存中null/not null如何存储,是否需要列索引加快project operation,是否需要对列值进行重排,列存如何进行数据压缩,等等,都要存储空间和存取速度中做平衡。现代数据库为了应对复杂的应用场景,往往使用不只一种存储格式,比如Oracle有In-memory Column Store在内存中将行存的页面转换为列存方式的page,用来加速复杂查询。当数据选定存储格式以后,还要选择数据在磁盘和内存中的聚集方式。以按行存储为例,大部分存储引擎使用固定大小的页面(page)来存储连续的若干行。当然,数据行如何连续排列,有堆表(随机)和索引组织表(按索引序)两种,现在较为流行的LSM-Like的存储引擎使用不定大小的页面(称为DataBlock),只支持按主键索引序聚集;这两种方式主要区别在于前者被设计为可更新的,每个page中会留有空间,后者是只读的,数据紧密存储不带padding,便于压缩。两者的区别实际上是因为事务处理机制有较大的区别导致的,后面再论。对于In-Memory Database来说,数据组织的方式会有较大区别,因为不需要在内存和持久化存储中交换数据,内存中一般不会使用page形式,而是直接使用索引存储结构(比如B+Tree)直接索引到记录(tuples),无需page这一层间接引用,减少cpu cache miss。缓存管理缓存的粒度一般是page,关键在于缓存替换算法。目前用的比较广泛的LRU,LFU,ARC..以及各种变种的算法都有在数据库中使用。另外还有一个是如何更有效的管理内存的问题,这点上,定长的page会比不定长的更有优势。当然还要考虑各种query pattern对cache的影响,如果单行查询较多,选用更细粒度(比如row)的cache会更有效率,但是淘汰的策略会更复杂,很多新的研究开始尝试引入机器学习的方法来优化cache淘汰算法,以及有效的管理cache.事务处理存储引擎之核心,保证数据库的ACID。要保证D,大家的做法差不多,都是写WAL(Write Ahead Log)来做recovery,关键是如何高效的实现ACI,也就是所谓的多版本并发控制(MVCC)机制。MVCC的完整实现比较复杂,暂不详细阐述,这里面的关键在于如何处理并发执行过程中的数据冲突(data race),包括写写冲突,读写冲突;因为数据库的负载一般是读多写少的,要做到高效,只读事务不能被读写事务阻塞,这就要求我们的写不能直接去更新当前的数据,而是要有一套维护多版本数据的能力,当前的存储引擎管理多版本数据的办法无非两种:写入数据原地更新,被更新的旧版本写到undo链中,写入代价大,事务处理复杂, 但是回收旧版本数据高效。写入数据不直接更新原来的数据,而是追加为新版本,写入代价小,但是读,尤其是扫描需要读取层次较多,更为严重的问题是回收旧版本的数据需要做compact,代价很大。前一种称为ARIES算法比大多数主流数据库存储引擎使用,后一种称为LSM-Tree的结构也被很多新存储引擎使用,受到越来越多的关注。Catalog与KV store有区别的是,数据库是有严格的schema的,所以多数存储引擎中的记录都是有结构的,很多KV store在作为数据库存储引擎时,都是在中间做一层转换,将上层处理的tuples以特定的编码方式转换为binary key-value,写入KVStore,并在读取到上层后,依靠schema解释为tuples格式供上层处理。这种方法当然可以工作,但是诸多优化无法实施:a. 数据迭代必须是整行,即便只需要其中一列,序列化/反序列化开销是免不了的。b. project和filter的工作无法下放到存储层内部进行处理; c. 没有列信息,做按列编码,压缩也不可能。d. schema change只能暴力重整数据… 因此要做到真正的高效,越来越多的存储引擎选择完全感知schema,存储细微结构。总结以上所探讨的,还只是单机数据库的存储引擎几个大的问题,而现代数据库对存储引擎提出了更高的要求,可扩展,高可用已经成为标配,现在要考虑的是如何给你的存储引擎加上分布式的能力,而这又涉及到高可用一致性保证,自动扩展,分布式事务等一系列更为复杂的问题,这已远超出本文的范畴,需要另开篇章。最后介绍下我们正在开发的阿里自研分布式数据库X-DB,其中的存储引擎就使用了我们自研的X-Engine。X-Engine使用了一种对数据进行分层的存储架构,因为目标是面向大规模的海量数据存储,提供高并发事务处理能力和尽可能降低成本。我们根据数据访问频度(冷热)的不同将数据划分为多个层次,针对每个层次数据的访问特点,设计对应的存储结构,写入合适的存储设备。X-Engine使用了LSM-Tree作为分层存储的架构基础,并在这之上进行了重新设计。简单来讲,热数据层和数据更新使用内存存储,利用了大量内存数据库的技术(Lock-Free index structure/append only)提高事务处理的性能,我们设计了一套事务处理流水线处理机制,把事务处理的几个阶段并行起来,极大提升了吞吐。而访问频度低的冷(温)数据逐渐淘汰或是合并到持久化的存储层次中,结合当前丰富的存储设备层次体系(NVM/SSD/HDD)进行存储。我们对性能影响比较大的compaction过程做了大量优化,主要是拆分数据存储粒度,利用数据更新热点较为集中的特征,尽可能的在合并过程中复用数据,精细化控制LSM的形状,减少I/O和计算代价,并同时极大的减少了合并过程中的空间放大。同时使用更细粒度的访问控制和缓存机制,优化读的性能。当然优化是无止境的,得益于丰富的应用场景,我们在其中获得了大量的工程经验。X-Engine现在已经不只一个单机数据库存储引擎,结合我们的X-Paxos(分布式强一致高可用框架), GMS(分布式管理服务), 和X-Trx(分布式事务处理框架),已经演变为一个分布式数据库存储系统。本文作者:七幕阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 29, 2019 · 1 min · jiezi

可应用于实际的14个NLP突破性研究成果(一)

摘要: 最好的论文是可以直接走出实验室!NLP年度最佳应用论文大集锦!语言理解对计算机来说是一个巨大的挑战。幼儿可以理解的微妙的细微差别仍然会使最强大的机器混淆。尽管深度学习等技术可以检测和复制复杂的语言模式,但机器学习模型仍然缺乏对我们的语言真正含义的基本概念性理解。但在2018年确实产生了许多具有里程碑意义的研究突破,这些突破推动了自然语言处理、理解和生成领域的发展。我们总结了14篇研究论文,涵盖了自然语言处理(NLP)的若干进展,包括高性能的迁移学习技术,更复杂的语言模型以及更新的内容理解方法。NLP,NLU和NLG中有数百篇论文,由于NLP对应用和企业AI的重要性和普遍性,所以我们从数百篇论文中寻找对NLP影响最大的论文。2018年最重要的自然语言处理(NLP)研究论文1.BERT:对语言理解的深度双向变换器的预训练,作者:JACOB DEVLIN,MING-WEI CHANG,KENTON LEE和KRISTINA TOUTANOVA论文摘要:我们引入了一种名为BERT的新语言表示模型,它是Transformer的双向编码器表示。与最近的语言表示模型不同,BERT旨在通过联合调节所有层中的左右上下文来预训练深度双向表示。因此,预训练的BERT表示可以通过一个额外的输出层进行微调,以创建适用于广泛任务的最先进模型,例如问答和语言推理,而无需实质性的具体的架构修改。BERT在概念上简单且经验丰富,它获得了11项自然语言处理任务的最新成果,包括将GLUE基准推至80.4%(提升了7.6%)、MultiNLI准确度达到86.7%(提升了5.6%)、SQuAD v1.1问题回答测试F1到93.2%(提升了1.5%)。总结谷歌AI团队提出了自然语言处理(NLP)的新前沿模型-BERT,它的设计允许模型从每个词的左侧和右侧考虑上下文。BERT在11个NLP任务上获得了新的最先进的结果,包括问题回答,命名实体识别和与一般语言理解相关的其他任务。论文的核心思想是什么?通过随机屏蔽一定比例的输入token来训练深度双向模型-从而避免单词间接“看到自己”的周期。通过构建简单的二进制分类任务来预训练句子关系模型,以预测句子B是否紧跟在句子A之后,从而允许BERT更好地理解句子之间的关系。训练一个非常大的模型(24个Transformer块,1024个隐藏层,340M参数)和大量数据(33亿字语料库)。什么是关键成就?刷新了11项NLP任务的记录,包括:获得80.4%的GLUE分数,这比之前最佳成绩提高了7.6%;在SQuAD 1.1上达到93.2%的准确率。预训练的模型不需要任何实质的体系结构修改来应用于特定的NLP任务。AI社区对其看法?BERT模型标志着NLP的新时代;两个无人监督的任务在一起为许多NLP任务提供了很好的性能;预训练语言模型成为一种新标准;未来的研究领域是什么?在更广泛的任务上测试该方法。收集BERT可能捕获或未捕获的语言现象。最可能的商业应用是什么?BERT可以帮助企业解决各种NLP问题,包括:提供更好的聊天机器人客服体验;客户评论分析;搜索相关信息;你在哪里可以代码?Google Research发布了一个官方Github存储库,其中包含Tensorflow代码和BERT预训练模型。BIT的PyTorch实现也可以在GitHub上获得。2.人类注意力的序列分类,作者:MARIA BARRETT,JOACHIM BINGEL,NORA HOLLENSTEIN,MAREK REI,ANDERSSØGAARD论文摘要学习注意力函数需要非常大规模的数据,不过有很多自然语言处理任务都是对人类行为的模拟,在这篇论文中作者们就表明人类的注意力确实可以为 NLP 中的许多注意力函数提供一个不错的归纳偏倚。具体来说,作者们根据人类阅读语料时的眼睛动作追踪数据估计出了「人类注意力」,然后用它对 RNN 网络中的注意力函数进行正则化。作者们的实验表明,人类注意力在大量不同的任务中都带来了显著的表现提升,包括情感分析、语法错误检测以及暴力语言检测。总结Maria Barrett和她的同事建议使用从眼动(eye-tracking)追踪语料库中获取的人类注意力来规范循环神经网络(RNN)中的注意力。通过利用公开可用的眼动追踪语料库,即通过眼睛跟踪测量(例如注视持续时间)增强的文本,它们能够在NLP任务中显着提高RNN的准确性,包括情绪分析、滥用语言检测和语法错误检测。论文的核心思想是什么?使用人的注意力,从眼动追踪语料库中估计,以规范机器注意力。模型的输入是一组标记序列和一组序列,其中每个标记与标量值相关联,该标量值表示人类读者平均专注于该标记的注意力。RNN联合学习循环参数和注意力功能,但可以在来自标记序列的监督信号和眼睛跟踪语料库中的注意力轨迹之间交替。建议的方法不要求目标任务数据带有眼睛跟踪信息。什么是关键成就?在注意力序列分类任务中引入循环神经结构。证明使用人眼注意力(从眼动追踪语料库中估计)来规范注意力功能可以在一系列NLP任务中实现显著改善,包括:§ 情绪分析,§ 检测语言检测,§ 语法错误检测。性能比基线平均误差减少4.5%。这些改进主要是由于召回率(recall)增加。AI社区对其看法?该论文获得了关于计算自然语言学习顶级会议的CoNLL 2018人类语言学习和处理启发的最佳研究论文特别奖。未来的研究领域是什么?在学习人类相关任务时,探索利用人类注意力作为机器注意力的归纳偏见的其他可能性。什么是可能的商业应用?RNN结合人类注意力信号,可应用于商业环境:§ 加强客户评论的自动分析;§ 过滤掉滥用的评论,回复。你在哪里可以得到实现代码?本研究论文的代码可在GitHub上获得。3.基于短语和神经元的无监督机器翻译,作者:GUILLAUME LAMPLE,MYLE OTT,ALEXIS CONNEAU,LUDOVIC DENOYER,MARC’AURELIO RANZATO论文摘要机器翻译系统在某些语言上实现了接近人类的性能,但其有效性强烈依赖于大量并行句子的可用性,这阻碍了它们适用于大多数语言。本文研究了如何在只能访问每种语言的大型单语语料库时学习翻译。我们提出了两种模型变体,一种神经模型,另一种基于短语的模型。两个版本都利用参数的初始化、语言模型的去噪效果以及通过迭代反向翻译自动生成并行数据。这些模型明显优于文献中的方法,同时更简单且具有更少的超参数。在广泛使用的WMT'14英语-法语和WMT'16德语-英语基准测试中,我们的模型不使用单个平行句的情况下分别获得28.1和25.2 BLEU分数,超过现有技术水平11 BLEU分。在英语-乌尔都语和英语-罗马尼亚语等低资源语言中,我们的方法比半监督和监督方法获得的效果都要好,我们的NMT和PBSMT代码是公开的。总结Facebook AI研究人员承认了缺乏用于训练机器翻译系统的大型并行语料库,并提出了一种更好的方法来利用单语数据进行机器翻译(MT)。特别是,他们认为通过适当的翻译模型初始化、语言建模和迭代反向翻译,可以成功地完成无监督的MT。研究人员提出了两种模型变体,一种是神经模型,另一种是基于短语的模型,它们的性能都极大地超越了目前最先进的模型。论文的核心思想是什么?无监督的MT可以通过以下方式完成:§ 合适的翻译模型初始化(即字节对编码);§ 在源语言和目标语言中训练语言模型,以提高翻译模型的质量(例如,进行本地替换,单词重新排序);§ 用于自动生成并行数据的迭代反向转换。有两种模型变体:神经和基于短语:§ 神经机器翻译一个重要的属性:跨语言共享内部表示。§ 基于短语的机器翻译在低资源语言对上优于神经模型,且易于解释和快速训练。什么是关键成就?基于神经和短语的机器翻译模型显著优于以前的无监督,例如:§ 对于英语-法语任务,基于短语的翻译模型获得的BLEU分数为28.1(比之前的最佳结果高出11 BLEU分);§ 对于德语-英语任务,基于神经和短语的翻译模型组合得到BLEU得分为25.2(高出基线10个BLEU点)。无监督的基于短语的翻译模型实现了超过使用10万个并行句子训练的监督模型对应的性能。AI社区的对其想法?该论文在自然语言处理领域的领先会议被评为EMNLP 2018评为最佳论文奖。未来的研究领域是什么?寻找更有效的原则实例或其他原则。扩展到半监督模型。什么是可能的商业应用?改进其中没有足够的并行语料库来训练受监督的机器翻译系统的机器翻译结果。你在哪里可以得到实现代码?Facebook团队在GitHub上提供了本研究论文的原始实现代码。未完待续……本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 29, 2019 · 1 min · jiezi

特征工程自动化如何为机器学习带来重大变化

摘要: 机器学习中最重要的领域之一是特征工程,却被严重地忽视了。这个重要领域中最成熟的工具就是Featuretools,一个开源的Python库。在本文中,我们将使用这个库来了解一下特征工程自动化将如何改变你进行机器学习的方式。随着技术的快速发展,在数据科学领域中,包括库、工具和算法等总会不断地变化的。然而,一直都有这么一个趋势,那就是自动化水平不断地提高。近些年来,在模型的自动化选择和超参数调整方面取得了一些进展,但是机器学习中最重要的领域 — 特征工程,却被严重地忽视了。这个重要领域中最成熟的工具就是Featuretools,一个开源的Python库。在本文中,我们将使用这个库来了解一下特征工程自动化将如何改变你更好地进行机器学习的方式。特征工程自动化是一种相对较新的技术,但是,它解决了许多实际数据集的使用问题。在这里,我们将用GitHub上的Jupyter Notebooks提供的代码来看看其中两个项目的结果和最终结论。每个项目都强调了特征工程自动化的一些好处:贷款偿还能力预测:与人工特征工程相比,自动化的特征工程可以将机器学习开发的时间缩短10倍,同时提供更好的建模性能表现;(笔记)消费支出预测:自动化的特征工程通过内部的处理时间序列过滤器来创建有实际意义的特征,同时防止数据泄漏,从而实现成功的模型部署;(笔记)特征工程:人工与自动特征工程是获取数据集并构造可解释的变量—特征的过程,用于训练预测问题的机器学习模型。通常,数据分布在多个表中,并且必须汇集到一个表之中,其中的行包含观察结果和列中的特征。传统的特征工程方法是使用相关领域知识创建一个特征,这是一个冗长、耗时且容易出错的过程,称为人工特征工程。人工特征工程是依赖于具体问题的,必须为每个新数据集重新编写代码。特征工程自动化通过自动从一组相关的数据表中提取有用且有意义的特征,并使用一个可应用于任何问题的框架,来改进这个标准工作流。它不仅减少了在特征工程上花费的时间,而且还创建了可解释的特征,并通过过滤具有时间依赖性的数据来防止数据泄漏。贷款偿还:建立更快更好的模型当数据科学家在处理家庭信贷贷款问题的时候,所面临的主要难题是数据的大小和分布。看看完整的数据集,你会发现面对的是分布在7个表中的5800万行数据。我曾经使用传统的人工特征工程花了10个小时创建了一组特征。首先,我查阅了其他数据科学家的成果,还查看了相关的数据,并研究了问题域,以获得必要的相关领域知识。然后我将这些知识翻译成代码,一次创建一个特征。作为单一的人工特征的一个例子,我找到了客户以前贷款的逾期还款总数,这一操作需要用到3个不同的表。最终人工设计的特征表现的相当好,比基线特征提高了65%,表明了正确特征设计的重要性。然而,效率却非常低下。对于人工特征工程,我最终花了超过15分钟来完成每个特征,因为我使用传统的方法一次生成一个特性。除了单调乏味和耗时之外,人工特征工程还有以下问题:用于特定问题:我花费了很长时间编写的代码并不能应用于任何其它的问题;易错:每一行代码都会有可能导致其它的错误;另外,最终的人工设计的特征受到了人类创造力和耐心方面的限制:我们只能考虑创建这么多的特征,并且只能花费这么多的时间。特征工程自动化的承诺是通过获取一组相关的表,并使用可以应用于所有问题的代码,来自动创建数以百计有用的特征,进而跨越这些限制。从人工到自动化特征工程特征工程自动化甚至允许像我这样的新手,在一组相关的数据表中可以创建数以千计的相关特征。我们只需要知道表的基本结构以及它们之间的关系,我们在一个称为实体集的单一数据结构中来跟踪它们。一旦我们有了一个实体集,使用一个称为深度特征合成(Deep Feature Synthesis,DFS)的方法,我们就能够在一个函数调用中创建数以千计的特征了。DFS使用称为“primitives”的函数来进行聚合和转换数据。这些primitives可以简单到仅获取一个平均值或列的最大值,也可以复杂到基于主题的专业知识,因为FeatureTools允许我们定义自己的primitives。特征primitives包括许多人工操作,但是通过使用FeatureTools,我们可以在任何关系数据库中使用相同准确的语法,而不是再重新编写代码并在不同的数据集中使用相同的操作。此外,当我们将primitives相互堆叠在一起来创建深层次的特征时,DFS的威力就来了。深度特征合成是灵活的,它被允许应用于任何数据科学领域的问题。它同时也是很强大的,通过创建深度特征来揭示我们对数据的推断。我会为你省去环境设置所需的几行代码,但DFS只在一行中运行。在这里,我们使用数据集中的所有7个表为每个客户生成数千个特征:# Deep feature synthesisfeature_matrix, features = ft.dfs(entityset=es, target_entity=‘clients’, agg_primitives = agg_primitives, trans_primitives = trans_primitives)下面是我们自动从FeatureTools获得的1820个特征中的一部分:客户以前贷款的最高总额。这是在3个表中使用1个MAX 和1个SUM 的primitive得来的;客户以前的信用卡平均债务的百分比排名,这在两个表中使用了百分比(PERCENTILE)和平均值(MEAN)的primitive;在申请过程中,客户是否提交了两份文件,这将使用1个AND 转换primitive和1个表;这些特征中的任何一个都是用简单的聚合创建的。FeatureTools创建了许多与我手工创建的相同的特征,但也有数千个是我从未考虑过的。并不是每一个特征都与问题相关,有些特征是高度相关的,然而,拥有太多的特征是一个比拥有太少的特征更好解决的问题。在进行了一些功能选择和模型优化之后,与人工特征相比,预测模型中的这些特征要稍好一些,总体开发时间为1小时,与人工的过程相比减少了10倍。FeatureTools速度更快,这是因为它需要的领域知识更少,而且要编写的代码行也少的相当多。我承认学习Featuretools需要一点时间成本,但这是一项有回报的投资。在花了一个小时左右的时间学习Featuretools之后,你就可以将其应用于任何机器学习问题了。以下的图表总结了我在贷款偿还问题上的经验:开发时间:10小时人工与1小时自动;该方法创建的特征数量: 30个人工特征与1820个自动特征;相对于基线提高了的百分比是:65% 人工 vs 66% 自动我的结论是,特征工程自动化不会取代数据科学家,而是通过显著地提高效率,使他们在机器学习的其它方面可以花费更多的时间。另外,我为第一个项目编写的Featuretools代码可以应用于任何数据集,而人工工程的代码则没法再利用。消费支出:创建有意义的特征并防止数据泄漏第二个数据集,在线时间戳的客户交易记录,预测问题是将客户分为两个部分,消费超过500美元的客户和消费不会超过500美元的客户。但是,不是对所有标签使用一个月,而是每个客户多次使用一个标签。我们可以把他们5月份的消费支出作为一个标签,然后6月份的,等等。在部署中,我们永远不会有未来的数据,因此无法将其用于训练模型。企业通常会遇到这个问题,并且经常部署一个在实际应用中比在开发中更糟糕的模型,因为这是使用无效的数据来进行训练的。幸运的是,要确保我们的数据在时间序列问题中是有效的,这在FeatureTools中很简单。在深度特征合成函数中,我们传递一个如上图所示的dataframe,其中截止时间表示我们不能使用任何标签数据中过去的时间点,FeatureTools在创建特征时会自动考虑时间。客户在指定月份的特征是使用过滤到该月份之前的数据来创建的。请注意,用于创建特征集的调用与添加截止时间的贷款偿还问题的调用相同。# Deep feature synthesisfeature_matrix, features = ft.dfs(entityset=es, target_entity=‘customers’, agg_primitives = agg_primitives, trans_primitives = trans_primitives, cutoff_time = cutoff_times)执行深度特征合成的结果是一个特征表,每个客户一个月一个。我们可以使用这些特征来训练一个带有标签的模型,然后可以对任何月份进行预测。此外,我们可以放心,模型中的特征不会使用导致不公平优势的未来信息,并产生误导训练的分数。有了自动化特征,我能够创建一个机器学习模型,在预测一个月内客户消费支出类别的时候,与已知为0.69的基线相比,ROC AUC达到0.90。除了提供令人印象深刻的预测能力之外,FeatureTools的实现还为我提供了一些同样有价值的东西:可解释的特征。看一下随机森林模型中的15个最重要的特征:特征的重要性告诉我们,预测客户将在下个月花多少钱的最重要素是他们之前花了多少钱SUM,以及购物的数量SUM。这些是可以手工创建的特征,但是我们不得不担心数据泄漏的问题,并创建在开发中比部署中效果要好的模型。如果可以创建有意义的特征工具已经存在了,而无需担心任何特征的有效性,那么为什么要人工实现呢?另外,自动化特征在问题的上下文中是完全明确的,并且可以为我们的实际推理提供信息。自动化特征工程识别出最重要的信号,实现了数据科学的主要目标:揭示隐藏在海量数据中的规律。即使在人工特征工程上花费的时间比我用FeatureTools花的时间多得多,那么我也无法开发出一组性能表现接近的特征。下图显示了使用在两个数据集上训练的模型对未来一个月的客户销售情况进行分类的ROC曲线。左上方的曲线表示更准确的预测:我甚至不能完全确定人工特征是否使用了有效的数据,但是通过FeatureTools我不必担心时间依赖性问题中的数据泄漏。我们在日常生活中使用自动安全系统,Featuretools中的特征工程自动化是在时间序列问题中创建有意义的机器学习特征的安全方法,同时提供了卓越的预测性能表现。结论我经过了这些项目之后,确信特征工程自动化应该是机器学习工作流程中不可或缺的一部分。这项技术并不完美,但依旧能显著地提高效率。主要的结论就是特征工程自动化:将执行时间缩短了10倍;在同一级别或更高级别上实现的建模性能;交付的具有实际意义的可解释性特征;防止使用不正确的数据而导致的模型无效;适应现有的工作流程和机器学习模型;本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 28, 2019 · 1 min · jiezi

终于等到你!阿里正式向 Apache Flink 贡献 Blink 源码

阿里妹导读:如同我们去年12月在 Flink Forward China 峰会所约,阿里巴巴内部 Flink 版本 Blink 将于 2019 年 1 月底正式开源。今天,我们终于等到了这一刻。阿里资深技术专家大沙,将为大家详细介绍本次开源的Blink主要功能和优化点,希望与业界同仁共同携手,推动Flink社区进一步发展。Blink简介Apache Flink是德国柏林工业大学的几个博士生和研究生从学校开始做起来的项目,早期叫做Stratosphere。2014年,StratoSphere项目中的核心成员从学校出来开发了Flink,同时将Flink计算的主流方向定位为流计算,并在同年将Flink捐赠Apache,后来快速孵化成为Apache的顶级项目。现在Flink是业界公认的最好的大数据流计算引擎。阿里巴巴在2015年开始尝试使用Flink。但是阿里的业务体量非常庞大,挑战也很多。彼时的Flink不管是规模还是稳定性尚未经历实践,成熟度有待商榷。为了把这么大的业务体量支持好,我们不得不在Flink之上做了一系列的改进,所以阿里巴巴维护了一个内部版本的Flink,它的名字叫做Blink。基于Blink的计算平台于2016年正式上线。截至目前,阿里绝大多数的技术部门都在使用Blink。Blink一直在阿里内部错综复杂的业务场景中锻炼成长着。对于内部用户反馈的各种性能、资源使用率、易用性等诸多方面的问题,Blink都做了针对性的改进。虽然现在Blink在阿里内部用的最多的场景主要还是在流计算,但是在批计算场景也有不少业务上线使用了。例如,在搜索和推荐的算法业务平台中,它使用Blink同时进行流计算和批处理。Blink被用来实现了流批一体化的样本生成和特征抽取这些流程,能够处理的特征数达到了数千亿,而且每秒钟处理数亿条消息。在这个场景的批处理中,我们单个作业处理的数据量已经超过400T,并且为了节省资源,我们的批处理作业是和流计算作业以及搜索的在线引擎运行在同样的机器上。所以大家可以看到流批一体化已经在阿里巴巴取得了极大的成功,我们希望这种成功和阿里巴巴内部的经验都能够带回给社区。Blink开源的背景其实从我们选择Flink的第一天开始我们就一直和社区紧密合作。过去的这几年我们也一直在把阿里对Flink 的改进推回社区。从2016年开始我们已经将流计算SQL的大部分功能,针对runtime的稳定性和性能优化做的若干重要设计都推回了社区。但是Blink本身发展迭代的速度非常快,而社区有自己的步伐,很多时候可能无法把我们的变更及时推回去。对于社区来说,一些大的功能和重构,需要达成共识后,才能被接受,这样才能更好地保证开源项目的质量,但是同时就会导致推入的速度变得相对较慢。经过这几年的开发迭代,我们这边和社区之间的差距已经变得比较大了。Blink 有一些很好的新功能,比如性能优越的批处理功能,在社区的版本是没有的。在过去这段时间里,我们不断听到有人在询问Blink的各种新功能。期望Blink尽快开源的呼声越来越大。我们一直在思考如何开源的问题,一种方案就是和以前一样,继续把各种功能和优化分解,逐个和社区讨论,慢慢地推回Flink。但这显然不是大家所期待的。另一个方案,就是先完整的尽可能的多的把代码开源,让社区的开发者能够尽快试用起来。第二个方案很快收到社区广大用户的支持。因此,从2018年年中开始我们就开始做开源的相关准备。经过半年的努力,我们终于把大部分Blink的功能梳理好,开源了出来。Blink开源的方式我们把代码贡献出来,是为了让大家能先尝试一些他们感兴趣的功能。Blink永远不会单独成为一个独立的开源项目来运作,他一定是Flink的一部分。开源后我们期望能找到办法以最快的方式将Blink merge到Flink中去。Blink开源只有一个目的,就是希望 Flink 做得更好。Apache Flink 是一个社区项目,Blink以什么样的形式进入 Flink 是最合适的,怎么贡献是社区最希望的方式,我们都要和社区一起讨论。在过去的一段时间内,我们在Flink社区征求了广泛的意见,大家一致认为将本次开源的Blink代码作为Flink的一个branch直接推回到Apache Flink项目中是最合适的方式。并且我们和社区也一起讨论规划出一套能够快速merge Blink到Flink master中的方案(具体细节可以查看Flink社区正在讨论的FLIP32)。我们期望这个merge能够在很短的时间内完成。这样我们之后的Machine Learning等其他新功能就可以直接推回到Flink master。相信用不了多久,Flink 和 Blink 就完全合二为一了。在那之后,阿里巴巴将直接使用Flink用于生产,并同时协助社区一起来维护Flink。本次开源的Blink的主要功能和优化点本次开源的Blink代码在Flink 1.5.1版本之上,加入了大量的新功能,以及在性能和稳定性上的各种优化。主要贡献包括,阿里巴巴在流计算上积累的一些新功能和性能的优化,一套完整的(能够跑通全部TPC-H/TPC-DS,能够读取Hive meta和data)高性能Batch SQL,以及一些以提升易用性为主的功能(包括支持更高效的interactive programming, 与zeppelin更紧密的结合, 以及体验和性能更佳的Flink web)。未来我们还将继续给Flink贡献在AI,IoT以及其他新领域的功能和优化。更多的关于这一版本Blink release的细节,请参考Blink代码根目录下的README.md文档。下面,我来分模块介绍下Blink主要的新的功能和优化点。Runtime为了更好的支持batch processing,以及解决阿里巴巴大规模生产场景中遇到的各种挑战,Blink对Runtime架构、效率、稳定性方面都做了大量改进。在架构方面,首先Blink引入了Pluggable ShuffleArchitecture,开发者可以根据不同的计算模型或者新硬件的需要实现不同的shuffle策略进行适配。此外Blink还引入新的调度架构,容许开发者根据计算模型自身的特点定制不同调度器。为了优化性能,Blink可以让算子更加灵活的chain在一起,避免了不必要的数据传输开销。在Pipeline Shuffle模式中,使用了ZeroCopy减少了网络层内存消耗。在BroadCast Shuffle模式中,Blink优化掉了大量的不必要的序列化和反序列化开销。此外,Blink提供了全新的JM FailOver机制,JM发生错误之后,新的JM会重新接管整个JOB而不是重启JOB,从而大大减少了JM FailOver对JOB的影响。最后,Blink也开发了对Kubernetes的支持。不同于Standalone模式在Kubernetes上的拉起方式,在基于Flink FLIP6的架构上基础之上,Blink根据job的资源需求动态的申请/释放Pod来运行TaskExecutor,实现了资源弹性,提升了资源的利用率。SQL/TableAPISQL/TableAPI架构上的重构和性能的优化是Blink本次开源版本的一个重大贡献。首先,我们对SQL engine的架构做了较大的调整。提出了全新的Query Processor(QP), 它包括了一个优化层(Query Optimizer)和一个算子层(Query Executor)。这样一来,流计算和批计算的在这两层大部分的设计工作就能做到尽可能的复用。另外,SQL和TableAPI的程序最终执行的时候将不会翻译到DataStream和DataSet这两个API上,而是直接构建到可运行的DAG上来,这样就使得物理执行算子的设计不完全依赖底层的API,有了更大的灵活度,同时执行代码也能够被灵活的codegen出来。唯一的一个影响就是这个版本的SQL和TableAPI不能和DataSet这个API进行互相转换,但仍然保留了和DataStream API互相转换的能力(将DataStream注册成表,或将Table转成DataStream后继续操作)。未来,我们计划把dataset的功能慢慢都在DataStream和TableAPI上面实现。到那时DataStream和SQL以及tableAPI一样,是一个可以同时描述bounded以及unbounded processing的API。除了架构上的重构,Blink还在具体实现上做了较多比较大的重构。首先,Blink引入了二进制的数据结构BinaryRow,极大的减少了数据存储上的开销以及数据在序列化和反序列化上计算的开销。其次,在算子的实现层面,Blink在更广范围内引入了CodeGen技术。由于预先知道算子需要处理的数据的类型,在QP层内部就可以直接生成更有针对性更高效的执行代码。Blink的算子会动态的申请和使用资源,能够更好的利用资源,提升效率,更加重要的是这些算子对资源有着比较好的控制,不会发生OutOfMemory 的问题。此外,针对流计算场景,Blink加入了miniBatch的执行模式,在aggregate、join等需要和state频繁交互且往往又能先做部分reduce的场景中,使用miniBatch能够极大的减少IO,从而成数量级的提升性能。除了上面提到的这些重要的重构和功能点,Blink还实现了完整的SQL DDL,带emit策略的流计算DML,若干重要的SQL功能,以及大量的性能优化策略。有了上面提到的诸多架构和实现上的重构。Blink的SQL/tableAPI在功能和性能方面都取得了脱胎换骨的变化。在批计算方面,首先Blink batch SQL能够完整的跑通TPC-H和TPC-DS,且性能上有着极大的提升。如上图所示,是这次开源的Blink版本和spark 2.3.1的TPC-DS的benchmark性能对比。柱状图的高度代表了运行的总时间,高度越低说明性能越好。可以看出,Blink在TPC-DS上和Spark相比有着非常明显的性能优势。而且这种性能优势随着数据量的增加而变得越来越大。在实际的场景这种优势已经超过 Spark的三倍。在流计算性能上我们也取得了类似的提升。我们线上的很多典型作业,它的性能是原来的3到5倍。在有数据倾斜的场景,以及若干比较有挑战的TPC-H query,流计算性能甚至得到了数十倍的提升。除了标准的Relational SQL API。TableAPI在功能上是SQL的超集,因此在SQL上所有新加的功能,我们在tableAPI也添加了相对应的API。除此之外,我们还在TableAPI上引入了一些新的功能。其中一个比较重要是cache功能。在批计算场景下,用户可以根据需要来cache计算的中间结果,从而避免不必要的重复计算。它极大的增强了interactive programming体验。我们后续会在tableAPI上添加更多有用的功能。其实很多新功能已经在社区展开讨论并被社区接受,例如我们在tableAPI增加了对一整行操作的算子map/flatMap/aggregate/flatAggregate(Flink FLIP29)等等。Hive的兼容性我们这次开源的版本实现了在元数据(meta data)和数据层将Flink和Hive对接和打通。国内外很多公司都还在用 Hive 在做自己的批处理。对于这些用户,现在使用这次Blink开源的版本,就可以直接用Flink SQL去查询Hive的数据,真正能够做到在Hive引擎和Flink引擎之间的自由切换。为了打通元数据,我们重构了Flink catalog的实现,并且增加了两种catalog,一个是基于内存存储的FlinkInMemoryCatalog,另外一个是能够桥接Hive metaStore的HiveCatalog。有了这个HiveCatalog,Flink作业就能读取Hive的metaData。为了打通数据,我们实现了HiveTableSource,使得Flink job可以直接读取Hive中普通表和分区表的数据。因此,通过这个版本,用户可以使用Flink SQL读取已有的Hive meta和data,做数据处理。未来我们将在Flink上继续加大对Hive兼容性的支持,包括支持Hive特有的query,data type,和Hive UDF等等。Zeppelin for Flink为了提供更好的可视化和交互式体验,我们做了大量的工作让Zeppelin能够更好的支持Flink。这些改动有些是在Flink上的,有些是在Zeppelin上的。在这些改动全部推回Flink和Zeppelin社区之前,大家可以使用这个Zeppelin image(具体细节请参考Blink代码里的docs/quickstart/zeppelin_quickstart.md)来测试和使用这些功能。这个用于测试的Zeppelin版本,首先很好的融合和集成了Flink的多种运行模式以及运维界面。使用文本SQL和tableAPI可以自如的查询Flink的static table和dynamic table。此外,针对Flink的流计算的特点,这一版Zeppelin也很好的支持了savepoint,用户可以在界面上暂停作业,然后再从savepoint恢复继续运行作业。在数据展示方面,除了传统的数据分析界面,我们也添加了流计算的翻牌器和时间序列展示等等功能。为了方便用户试用,我们在这一版zeppelin中提供3个built-in的Flink tutorial的例子: 一个是做StreamingETL的例子, 另外两个分别是做Flink Batch,Flink Stream的基础样例。Flink Web我们对Flink Web的易用性与性能等多个方面做了大量的改进,从资源使用、作业调优、日志查询等维度新增了大量功能,使得用户可以更方便的对Flink作业进行运维。在资源使用方面,新增了Cluster、TaskManager与Job三个级别的资源信息,使得资源的申请与使用情况一目了然。作业的拓扑关系及数据流向可以追溯至 Operator 级别,Vertex 增加了InQueue,OutQueue等多项指标,可以方便的追踪数据的反压、过滤及倾斜情况。TaskManager 和 JobManager 的日志功能得到大幅度加强,从Job、Vertex、SubTask 等多个维度都可以关联至对应日志,提供多日志文件访问入口,以及分页展示查询和日志高亮功能。另外,我们使用了较新的Angular 7.0 对Flink web进行了全面重构,页面运行性能有了一倍以上的提升。在大数据量情况下也不会发生页面卡死或者卡顿情况。同时对页面的交互逻辑进行了整体优化,绝大部分关联信息在单个页面就可以完成查询和比对工作,减少了大量不必要的跳转。未来的规划Blink迈出了全面开源的第一步,接下来我们会和社区合作,尽可能以最快的方式将Blink的功能和性能上的优化merge回Flink。本次的开源版本一方面贡献了Blink多年在流计算的积累,另一方面又重磅推出了在批处理上的成果。接下来,我们会持续给Flink社区贡献其他方面的功能。我们期望每过几个月就能看到技术上有一个比较大的亮点贡献到社区。下一个亮点应该是对机器学习的支持。要把机器学习支持好,有一系列的工作要做,包括引擎的功能,性能,和易用性。这里面大部分的工作我们已经开发完成,并且很多功能都已经在阿里巴巴内部服务上线了。除了技术上创新以及新功能之外,Flink的易用性和外围生态也非常重要。我们已经启动了若干这方面的项目,包括Python以及Go等多语言支持,Flink集群管理,Notebook,以及机器学习平台等等。这些项目有些会成为Flink自身的一部分贡献回社区,有些不是。但它们都基于Flink,是Flink生态的一个很好的补充。独立于Flink之外的那些项目,我们都也在认真的考虑开源出来。总之,Blink在开源的第一天起,就已经完全all-in的融入了Flink社区,我们希望所有的开发者看到我们的诚意和决心。未来,无论是功能还是生态,我们都会在Flink社区加大投入,我们也将投入力量做 Flink 社区的运营,让 Flink 真正在中国、乃至全世界大规模地使用起来。我们衷心的希望更多的人加入,一起把Apache Flink开源社区做得更好!本文作者:大沙阅读原文本文来自云栖社区合作伙伴“阿里技术”,如需转载请联系原作者。 ...

January 28, 2019 · 1 min · jiezi

如何使用阿里云ARMS轻松重现用户浏览器问题

客户投诉不断,本地却无法重现?页面加载较慢是用户经常会反馈的问题,也是前端非常关注的问题之一。但定位、排查解决这类问题就通常会花费非常多的时间,主要原因如下:页面是在用户端的浏览器上加载执行,复现困难页面上线前,开发同学都会进行测试,在测试环境下页面加载一般都是正常的才会正式上线。用户在访问页面时,页面的加载是在用户端的浏览器上进行的,由于页面的加载耗时与地域、网络情况、浏览器或者运营商等有关系,想知道用户在访问页面时的具体情况,复现是非常困难的。监控信息缺少,导致无法深入排查大部分前端监控会通过PerformanceTiming对象,获取完整的页面加载耗时信息,但这类监控就缺失了页面静态资源的加载情况,无法直接复现现场,从而无法深入定位性能瓶颈。为了方便用户更快地定位性能瓶颈,阿里云ARMS前端监控推出一新功能: 会话追踪,提供页面静态资源加载的性能瀑布图,根据页面性能数据可深入定位页面资源加载情况。如何通过会话追踪帮助你快速定位问题在阿里云ARMS前端监控SDK上将sendResource配置为true,重新部署应用后,在页面onload时会上报当前页面加载的静态资源信息。从而在阿里云前端监控平台即可以对慢页面加载问题快速进行定位。SDK配置在阿里云ARMS前端监控SDK部分,默认是不上报页面加载的静态资源信息的,如果想获取页面加载的静态资源信息,只需在SDK的config部分将sendResource配置为true,重新部署后,就可以上报相关信息。具体配置如下:<script>!(function(c,b,d,a){c[a]||(c[a]={});c[a].config={pid:“atc889zkcf@8cc3f63543da641”,imgUrl:“https://arms-retcode.aliyuncs.com/r.png?",sendResource:true};with(b)with(body)with(insertBefore(createElement("script"),firstChild))setAttribute("crossorigin","",src=d)})(window,document,"https://retcode.alicdn.com/retcode/bl.js","__bl");</script>注意:静态资源加载信息的上报是在页面onload时会触发,上报信息量较大,如果对于页面性能要求很高的应用,可以不开启该配置。问题排查过程1. 发现问题进入访问速度菜单后,发现页面的性能较差,11点钟的页面完全加载时间达到35s,如下:2. 慢页面会话追踪在慢页面会话追踪模块,提供该页面在指定时间段内加载较慢的TOP20,这样可以快速发现哪些会话加载较慢,如下图所示。在该模块,你可以快速发现在11点钟有一次会话的页面加载时间在36.72s,这次访问应该是直接导致页面加载时间详情中折线图突然暴增的原因了。其中在在模块有7次会话访问的页面加载时间在7s以上,点击对应的页面,可以直接进入到会话详情页面,从而直观查看页面静态资源加载的瀑布图。通过页面资源加载的瀑布图,可以快速定位到资源加载的性能瓶颈,同时可以查看本次访问的客户端IP地址、浏览器、操作系统等UA信息,从而进一步确认是由于网络原因还是其他原因导致的,针对性进行相应的优化。3. 其他发现问题入口会话追踪也可以进入“会话追踪”菜单,可以看到该应用下的会话列表。会话列表中会根据页面完全加载时间排序,展示TOP100,帮助用户可以快速发现耗时较长的会话信息。同时支持按照页面、会话Id、浏览器、浏览器版本号进行过滤,展示相关的会话信息。点击操作后,是该会话的页面资源加载详情。访问明细如果当前会话列表中无法找到你要排查的会话信息,可以通过访问明细查找到相应的日志详细信息,在param中找到对应的sid即会话Id,然后在会话列表中查找相应的会话Id,即可以定位到想排查的会话信息。例如:在已知用户的客户端IP的情况下,想定位相应的会话信息,即可以在访问明细中,通过t=res and 117.136.32.110 进行搜索,找到对应的会话Id。根据查找到的会话Id, 就可以在会话列表中进行过滤,定位到具体的会话内容。使用入口指南进入访问速度菜单,如果发现页面性能较差,可以在"慢页面会话追踪Top20"中查看访问较慢的会话情况点击详情后,可以查看具体的页面资源加载瀑布图如果Top20不满足,可以点击"更多”,从而进入"会话列表"进入会话追踪菜单,展示的是TOP100的会话列表信息,根据页面完全加载时间从高到底排序,排查页面资源加载情况至此,慢页面会话追踪功能及使用方法介绍完成。该功能可以帮助你复现用户在访问页面时的页面资源加载情况,快速定位性能瓶颈问题。附录官网文档介绍阿里云ARMS前端监控官网本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 23, 2019 · 1 min · jiezi

JUC包中的分而治之策略-为提高性能而生

一、前言本次分享我们来共同探讨JUC包中一些有意思的类,包含AtomicLong & LongAdder,ThreadLocalRandom原理。二、AtomicLong & LongAdder2.1 AtomicLong 类AtomicLong是JUC包提供的原子性操作类,其内部通过CAS保证了对计数的原子性更新操作。大家可以翻看源码发现内部是通过UnSafe(rt.jar)这个类的CAs操作来保证对内部的计数器变量 long value进行原子性更新的,比如JDK8中: public final long incrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L; }其中unsafe.getAndAddLong的代码如下: public final long getAndAddLong(Object paramObject, long paramLong1, long paramLong2) { long l; do { l = getLongVolatile(paramObject, paramLong1);//(1) } while (!compareAndSwapLong(paramObject, paramLong1, l, l + paramLong2));//(2) return l; }可知最终调用的是native 方法compareAndSwapLong原子性操作。当多个线程调用同一个AtomicLong实例的incrementAndGet方法后,多个线程都会执行到unsafe.getAndAddLong方法,然后多个线程都会执行到代码(1)处获取计数器的值,然后都会去执行代码(2),如果多个线程同时执行了代码(2),由于CAS具有原子性,所以只有一个线程会更新成功,然后返回true从而退出循环,整个更新操作就OK了。其他线程则CAS失败返回false,则循环一次在次从(1)处获取当前计数器的值,然后在尝试执行(2),这叫做CAS的自旋操作,本质是使用Cpu 资源换取使用锁带来的上下文切换等开销。2.2 LongAdder类AtomicLong类为开发人员使用线程安全的计数器提供了方便,但是AtomicLong在高并发下存在一些问题,如上所述,当大量线程调用同一个AtomicLong的实例的方法时候,同时只有一个线程会CAS计数器的值成功,失败的线程则会原地占用cpu进行自旋转重试,这回造成大量线程白白浪费cpu原地自旋转。在JDK8中新增了一个LongAdder类,其采用分而治之的策略来减少同一个变量的并发竞争度,LongAdder的核心思想是把一个原子变量分解为多个变量,让同样多的线程去竞争多个资源,这样竞争每个资源的线程数就被分担了下来,下面通过图形来理解下两者设计的不同之处:如上图AtomicLong是多个线程同时竞争同一个原子变量。如上图LongAdder内部维护多个Cell变量,在同等并发量的情况下,争夺单个变量更新操作的线程量会减少,这是变相的减少了争夺共享资源的并发量。下面我们首先看下Cell的结构: @sun.misc.Contended static final class Cell { volatile long value; Cell(long x) { value = x; } final boolean cas(long cmp, long val) { return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); } // Unsafe 机制 private static final sun.misc.Unsafe UNSAFE; private static final long valueOffset; static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> ak = Cell.class; valueOffset = UNSAFE.objectFieldOffset (ak.getDeclaredField(“value”)); } catch (Exception e) { throw new Error(e); } } }LongAdder维护了一个延迟初始化的原子性更新数组(默认情况下Cell数组是null)和一个基值变量base,由于Cells占用内存是相对比较大的,所以一开始并不创建,而是在需要时候在创建,也就是惰性 创建。当一开始判断cell数组是null并且并发线程较少时候所有的累加操作都是对base变量进行的,这时候就退化为了AtomicLong。cell数组的大小保持是2的N次方大小,初始化时候Cell数组的中Cell的元素个数为2,数组里面的变量实体是Cell类型。当多个线程在争夺同一个Cell原子变量时候如果失败并不是在当前cell变量上一直自旋CAS重试,而是会尝试在其它Cell的变量上进行CAS尝试,这个改变增加了当前线程重试时候CAS成功的可能性。最后获取LongAdder当前值的时候是把所有Cell变量的value值累加后在加上base返回的,如下代码: public long sum() { Cell[] as = cells; Cell a; long sum = base; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) sum += a.value; } } return sum; }如上代码可知首先把base的值赋值给sum变量,然后通过循环把每个cell元素的value值累加到sum变量上,最后返回sum.其实这是一种分而治之的策略,先把并发量分担到多个原子变量上,让多个线程并发的对不同的原子变量进行操作,然后获取计数时候在把所有原子变量的计数和累加。思考问题:何时初始化cell数组当前线程如何选择cell中的元素进行访问如果保证cell中元素更新的线程安全cell数组何时进行扩容,cell元素个数可以无限扩张?性能对比,这里有一个文章 http://blog.palominolabs.com/2014/02/10/java-8-performance-improvements-longadder-vs-atomiclong/三、 Random & ThreadLocalRandom3.1 Random类原理及其局限性在JDK7之前包括现在java.util.Random应该是使用比较广泛的随机数生成工具类,下面先通过简单的代码看看java.util.Random是如何使用的:public class RandomTest { public static void main(String[] args) { //(1)创建一个默认种子的随机数生成器 Random random = new Random(); //(2)输出10个在0-5(包含0,不包含5)之间的随机数 for (int i = 0; i < 10; ++i) { System.out.println(random.nextInt(5)); } }}代码(1)创建一个默认随机数生成器,使用默认的种子。代码(2)输出输出10个在0-5(包含0,不包含5)之间的随机数。 public int nextInt(int bound) { //(3)参数检查 if (bound <= 0) throw new IllegalArgumentException(BadBound); //(4)根据老的种子生成新的种子 int r = next(31); //(5)根据新的种子计算随机数 … return r; } 如上代码可知新的随机数的生成需要两个步骤首先需要根据老的种子计算生成新的种子。然后根据新的种子和bound变量通过一定的算法来计算新的随机数。下面看下next()代码: protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { //(6)获取当前原子变量种子的值 oldseed = seed.get(); //(7)根据当前种子值计算新的种子 nextseed = (oldseed * multiplier + addend) & mask; //(8)使用新种子替换老的种子 } while (!seed.compareAndSet(oldseed, nextseed)); //(9) return (int)(nextseed >>> (48 - bits)); }代码(6)使用原子变量的get方法获取当前原子变量种子的值代码(7)根据具体的算法使用当前种子值计算新的种子代码(8)使用CAS操作,使用新的种子去更新老的种子,多线程下可能多个线程都同时执行到了代码(6)那么可能多个线程都拿到的当前种子的值是同一个,然后执行步骤(7)计算的新种子也都是一样的,但是步骤(8)的CAS操作会保证只有一个线程可以更新老的种子为新的,失败的线程会通过循环从新获取更新后的种子作为当前种子去计算老的种子,这就保证了随机数的随机性。代码(9)则使用固定算法根据新的种子计算随机数,并返回。3.2 ThreadLocalRandomRandom类生成随机数原理以及不足:每个Random实例里面有一个原子性的种子变量用来记录当前的种子的值,当要生成新的随机数时候要根据当前种子计算新的种子并更新回原子变量。多线程下使用单个Random实例生成随机数时候,多个线程同时计算随机数计算新的种子时候多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新是CAS操作,同时只有一个线程会成功,那么CAS操作失败的大量线程进行自旋重试,而大量线程的自旋重试是会降低并发性能和消耗CPU资源的,为了解决这个问题,ThreadLocalRandom类应运而生。public class RandomTest { public static void main(String[] args) { //(10)获取一个随机数生成器 ThreadLocalRandom random = ThreadLocalRandom.current(); //(11)输出10个在0-5(包含0,不包含5)之间的随机数 for (int i = 0; i < 10; ++i) { System.out.println(random.nextInt(5)); } }}如上代码(10)调用ThreadLocalRandom.current()来获取当前线程的随机数生成器。下面来分析下ThreadLocalRandom的实现原理。从名字看会让我们联想到ThreadLocal类。ThreadLocal通过让每一个线程拷贝一份变量,每个线程对变量进行操作时候实际是操作自己本地内存里面的拷贝,从而避免了对共享变量进行同步。实际上ThreadLocalRandom的实现也是这个原理。Random的缺点是多个线程会使用原子性种子变量,会导致对原子变量更新的竞争,这个原理可以通过下面图来表达:那么如果每个线程维护自己的一个种子变量,每个线程生成随机数时候根据自己本地内存中的老的种子计算新的种子,并使用新种子更新老的种子,然后根据新种子计算随机数,就不会存在竞争问题,这会大大提高并发性能,如下图ThreadLocalRandom原理可以使用下图表达:Thread类里面有几个变量: /** The current seed for a ThreadLocalRandom / @sun.misc.Contended(“tlr”) long threadLocalRandomSeed; /* Probe hash value; nonzero if threadLocalRandomSeed initialized */ @sun.misc.Contended(“tlr”) int threadLocalRandomProbe;思考问题:每个线程的初始种子怎么生成的如果保障多个线程产生的种子不一样四、总结本文是对拙作 java并发编程之美 一书中有关章节的提炼。本次分享首先讲解了AtomicLong的内部实现,以及存在的缺点,然后讲解了 LongAdder采用分而治之的策略通过使用多个原子变量减小单个原子变量竞争的并发度。然后简单介绍了Random,和其缺点,最后介绍了ThreadLocalRandom借用ThreadLocal的思想解决了多线程对同一个原子变量竞争锁带来的性能损耗。其实JUC包中还有其他一些经典的组件,比如fork-join框架等。本文作者:加多阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 15, 2019 · 2 min · jiezi

重磅公开!阿里语音识别模型端核心技术,让你“听”见未来

阿里妹导读:语音识别技术作为人工智能技术中的重要组成部分,成为影响人机交互的核心组件之一,从各种智能家用IoT设备的语音交互能力,到公共服务、智慧政务等场合的应用,语音识别技术正在影响着人们生活的方方面面。本文将全面介绍阿里云语音识别技术中的重要模型端技术,希望和业界同仁交流探讨。声学模型、语言模型和解码器可以看作是现代语音识别系统最核心的三个组成部分。虽然最近有一些研究者尝试构建End2end的语音识别系统,但包含声学模型、语言模型和解码器的现代语音识别系统依然是当前最主流和使用最广泛的系统。在这其中,声学模型主要用来构建输入语音和输出声学单元之间的概率映射关系;语言模型用来描述不同字词之间的概率搭配关系,使得识别出的句子更像自然文本;解码器负责结合声学单元概率数值和语言模型在不同搭配上的打分进行筛选,最终得到最可能的识别结果。随着近几年深度学习的火热,语音识别领域也纷纷投入深度学习的大潮之中。将传统HMM-GMM声学模型替换成HMM-DNN声学模型后,可以获得超过20%的相对提升,在传统N-Gram语言模型基础上叠加NN-LM语言模型也可以获得进一步的提高。在这过程中,声学模型由于更适合采用深度神经网络模型,从而受到研究者更多的关注。本文主要介绍阿里云语音识别技术中采用的声学模型技术和语言模型技术,包括LC-BLSTM声学模型、LFR-DFSMN声学模型和NN-LM语言模型,其中LC-BLSTM是对传统BLSTM模型的一种改进,在保持了高准确率的同时,提供了低延时的特性;而DFSMN是一种新颖的非递归结构的神经网络却可以像RNN一样对信号的长时相关进行建模,同时可以获得更稳定的训练效果和更好的识别准确。NN-LM语言模型是近年来在传统N-Gram语言模型基础上获得的进一步改进。Latency-Controlled BLSTM模型DNN(即fully connected DNN)模型的优点在于通过增加神经网络的层数和节点数,扩展了网络对于复杂数据的抽象和建模能力,但同时DNN模型也存在一些不足,例如DNN中一般采用拼帧来考虑上下文相关信息对于当前语音帧的影响,这并不是反映语音序列之间相关性的最佳方法。自回归神经网络(RNN)在一定程度上解决了这个问题,它通过网络节点的自连接达到利用序列数据间相关性的目的。进一步有研究人员提出一种长短时记忆网络(LSTM-RNN),它可以有效减轻简单RNN容易出现的梯度爆炸和梯度消散问题,而后研究人员又对LSTM进行了扩展,使用双向长短时记忆网络(BLSTM-RNN)进行声学模型建模,以充分考虑上下文信息的影响。BLSTM模型可以有效地提升语音识别的准确率,相比于DNN模型,相对性能提升可以达到15%-20%。但同时BLSTM模型也存在两个非常重要的问题:1、句子级进行更新,模型的收敛速度通常较慢,并且由于存在大量的逐帧计算,无法有效发挥GPU等并行计算工具的计算能力,训练会非常耗时;2、由于需要用到整句递归计算每一帧的后验概率,解码延迟和实时率无法得到有效保证,很难应用于实际服务。对于这两个问题,学术界首先提出Context-Sensitive-Chunk BLSTM(CSC-BLSTM)的方法加以解决,而此后又提出了Latency Controlled BLSTM(LC-BLSTM)这一改进版本,更好、更高效地减轻了这两个问题。我们在此基础上采用LC-BLSTM-DNN混合结构配合多机多卡、16bit量化等训练和优化方法进行声学模型建模,取得了相比于DNN模型约17-24%的相对识别错误率下降。典型的LSTM节点结构由3个gate组成:input gate、forget gate、output gate和一个cell组成,输入、输出节点以及cell同各个门之间都存在连接;inputgate、forget gate同cell之间也存在连接,cell内部还有自连接。这样通过控制不同门的状态,可以实现更好的长短时信息保存和误差传播。LSTM可以像DNN一样逐层堆积成为DeepLSTM,为了更好地利用上下文信息,还可以使用BLSTM逐层堆积构造Deep BLSTM,其结构如下图所示,网络中沿时间轴存在正向和反向两个信息传递过程,每一个时间帧的计算都依赖于前面所有时间帧和后面所有时间帧的计算结果,对于语音信号这种时序序列,该模型充分考虑了上下文对于当前语音帧的影响,能够极大提高音素状态的分类准确率。然而由于标准的BLSTM是对整句语音数据进行建模,训练和解码过程存在收敛慢、延迟高、实时率低等问题,针对这些弊端我们采用了Latency Controlled BLSTM进行解决,与标准的BLSTM使用整句语音进行训练和解码不同,Latency Control BLSTM使用类似truncated BPTT的更新方式,并在cell中间状态处理和数据使用上有着自己的特点,如下图所示,训练时每次使用一小段数据进行更新,数据由中心chunk和右向附加chunk构成,其中右向附加chunk只用于cell中间状态的计算,误差只在中心chunk上进行传播。时间轴上正向移动的网络,前一个数据段在中心chunk结束时的cell中间状态被用于下一个数据段的初始状态,时间轴上反向移动的网络,每一个数据段开始时都将cell中间状态置为0。该方法可以很大程度上加快网络的收敛速度,并有助于得到更好的性能。解码阶段的数据处理与训练时基本相同,不同之处在于中心chunk和右向附加chunk的维度可以根据需求进行调节,并不必须与训练采用相同配置。LFR-DFSMN模型FSMN是近期被提出的一种网络结构,通过在前馈全连接神经网络(Feedforward Fully-connectedNeural Networks,FNN)的隐层添加一些可学习的记忆模块,从而可以有效地对信号的长时相关性进行建模。FSMN相比于LCBLSTM不仅可以更加方便的控制时延,而且往往也能获得更好的性能,需要的计算资源也更少。但是标准的FSMN很难训练非常深层的结构,由于梯度消失问题导致训练效果不好。而深层结构的模型目前在很多领域被证明具有更强的建模能力。因而针对此我们提出了一种改进的FSMN模型,称之为深层的FSMN(Deep FSMN, DFSMN)。进一步的我们结合低帧率(Low Frame Rate,LFR)技术构建了一种高效的实时语音识别声学模型,相比于去年我们上线的LFR-LCBLSTM声学模型可以获得超过20%的相对性能提升,同时可以获得2-3倍的训练以及解码的加速,可以显著的减少我们的系统实际应用时所需要的计算资源。最早提出的FSMN的模型结构如上图(a)所示,其本质上是一个前馈全连接神经网络,通过在网络的某些隐层旁添加一些记忆模块(memory block)来对当前时刻周边的上下文信息进行建模,从而使得模型可以对时序信号的长时相关性进行建模。记忆模块采用如上图(b)所示的抽头延迟结构将当前时刻以及之前 N 个时刻的隐层输出通过一组系数编码得到一个固定的表达。FSMN的提出是受到数字信号处理中滤波器设计理论的启发:任何无限响应冲击(Infinite Impulse Response, IIR)滤波器可以采用高阶的有限冲击响应(FiniteImpulseResponse, FIR)滤波器进行近似。从滤波器的角度出发,如上图(c)所示的RNN模型的循环层就可以看作如上图(d)的一阶IIR滤波器。而FSMN采用的采用如上图(b)所示的记忆模块可以看作是一个高阶的FIR滤波器。从而FSMN也可以像RNN一样有效的对信号的长时相关性进行建模,同时由于FIR滤波器相比于IIR滤波器更加稳定,因而FSMN相比于RNN训练上会更加简单和稳定。根据记忆模块编码系数的选择,可以分为:标量FSMN(sFSMN)矢量FSMN(vFSMN)sFSMN 和 vFSMN 顾名思义就是分别使用标量和矢量作为记忆模块的编码系数。以上的FSMN只考虑了历史信息对当前时刻的影响,我们可以称之为单向的FSMN。当我们同时考虑历史信息以及未来信息对当前时刻的影响时,我们可以将单向的FSMN进行扩展得到双向的FSMN。FSMN相比于FNN,需要将记忆模块的输出作为下一个隐层的额外输入,这样就会引入额外的模型参数。隐层包含的节点越多,则引入的参数越多。研究结合矩阵低秩分解(Low-rank matrix factorization)的思路,提出了一种改进的FSMN结构,称之为简洁的FSMN(Compact FSMN,cFSMN)。下图是一个第l个隐层包含记忆模块的cFSMN的结构框图。对于cFSMN,通过在网络的隐层后添加一个低维度的线性投影层,并且将记忆模块添加在这些线性投影层上。进一步的,cFSMN对记忆模块的编码公式进行了一些改变,通过将当前时刻的输出显式的添加到记忆模块的表达中,从而只需要将记忆模块的表达作为下一层的输入。这样可以有效的减少模型的参数量,加快网络的训练。上图是我们进一步提出的Deep-FSMN(DFSMN)的网络结构框图,其中左边第一个方框代表输入层,右边最后一个方框代表输出层。我们通过在cFSMN的记忆模块(红色框框表示)之间添加跳转连接(skip connection),从而使得低层记忆模块的输出会被直接累加到高层记忆模块里。这样在训练过程中,高层记忆模块的梯度会直接赋值给低层的记忆模块,从而可以克服由于网络的深度造成的梯度消失问题,使得可以稳定的训练深层的网络。相比于之前的cFSMN,DFSMN优势在于,通过跳转连接可以训练很深的网络。对于原来的cFSMN,由于每个隐层已经通过矩阵的低秩分解拆分成了两层的结构,这样对于一个包含4层cFSMN层以及两个DNN层的网络,总共包含的层数将达到13层,从而采用更多的cFSMN层,会使得层数更多而使得训练出现梯度消失问题,导致训练的不稳定性。我们提出的DFSMN通过跳转连接避免了深层网络的梯度消失问题,使得训练深层的网络变得稳定。需要说明的是,这里的跳转连接不仅可以加到相邻层之间,也可以加到不相邻层之间。跳转连接本身可以是线性变换,也可以是非线性变换。具体的实验我们可以实现训练包含数十层的DFSMN网络,并且相比于cFSMN可以获得显著的性能提升。从最初的FSMN到cFSMN不仅可以有效的减少模型的参数,而且可以获得更好的性能。进一步的在cFSMN的基础上,我们提出的DFSMN,可以更加显著的提升模型的性能。如下表是在一个2000小时的英文任务上基于BLSTM,cFSMN,DFSMN的声学模型性能对比。从上表中可以看到,在2000小时这样的任务上,DFSMN模型可以获得比BLSTM声学模型相对14%的错误率降低,显著提高了声学模型的性能。传统的声学模型,输入的是每帧语音信号提取的声学特征,每帧语音的时长通常为10ms,对于每个输入的语音帧信号会有相对应的一个输出目标。最近有研究提出一种低帧率(Low Frame Rate,LFR)建模方案:通过将相邻时刻的语音帧进行绑定作为输入,去预测这些语音帧的目标输出得到的一个平均输出目标。具体实验中可以实现三帧(或更多帧)拼接而不损失模型的性能。从而可以将输入和输出减少到原来的三分之一甚至更多,可以极大的提升语音识别系统服务时声学得分的计算以及解码的效率。我们结合LFR和以上提出的DFSMN,构建了基于LFR-DFSMN的语音识别声学模型,经过多组实验我们最终确定了采用一个包含10层cFSMN层+2层DNN的DFSMN作为声学模型,输入输出则采用LFR,将帧率降低到原来的三分之一。识别结果和去年我们上线的最好的LCBLSTM基线比较如下表所示。通过结合LFR技术,我们可以获得三倍的识别加速。从上表中可以看到,在实际工业规模应用上,LFR-DFSMN模型比LFR-LCBLSTM模型可以获得20%的错误率下降,展示了对大规模数据更好的建模特性。NN-LM语言模型语言模型,顾名思义,对语言进行建模的模型。语言表达可以看作一串字符序列,不同的字符序列组合代表不同的含义,字符的单位可以是字或者词。语言模型的任务,可以看作是给定字符序列,如何估计该序列的概率,或者说,如何估计该序列的合理性。P(上海 的 工人 师傅 有 力量)>P(上海 的 工人 食腐 有 力量)拿这句话做个例子。比如到底应该是“工人师傅有力量”,还是“工人食腐有力量”,哪句话更“合适”。我们容易判断左边这句的概率大一点。于是我们希望通过语言模型的建模,可以给出符合人类预期的概率分配。就像这句,“工人师傅”的概率,大于“工人食腐”的概率。基于统计词频的传统N元文法模型,通过马尔可夫假设简化了模型结构和计算,通过计数的方式计算,通过查找的方式使用。拥有估计简单、性能稳定、计算快捷的优势,有超过三十年的使用历史。然而其马尔科夫假设强制截断建模长度,使得模型无法对较长的历史建模;基于词频的估计方式也使得模型不够平滑,对于低词频词汇估计不足。随着神经网络(Neural Networks,NNs)的第三次崛起,人们开始尝试通过NN来进行语言模型建模。一个典型的建模结构是递归神经网络(recurrentneural networks,RNNs),其递归的结构理论上可以对无穷长序列进行建模,弥补了N元文法对于序列长度建模的不足;同时其各层间的全向连接也保证了建模的平滑。此外为了提升模型的性能,研究者们还尝试了通过长短时记忆(Long Short-Term Memory,LSTM)结构来提升基本RNN本身建模能力的不足,进一步提升模型性能。NN用于大规模语言建模的系统中,需要面对一些问题,例如大词表带来的存储和计算增加。实际线上系统的词表往往比较大,而随着词表的增加,基本RNN结构的存储和计算量都会几何级数爆炸式增长。为此,研究者们进行了一些尝试,压缩词典尺寸成了一个最直接的解决方案,一个经典的方法是词表聚类。该方法可以大幅压缩词表尺寸,但往往也会带来一定的性能衰减。更直接的一个想法是直接过滤掉低频词汇,这样依然会带来一定的性能衰减,据此有一个改进策略,我们发现真正制约速度性能的主要是输出层节点,输入层节点大,借助projection层可以很好解决,于是输入层采用大辞典,而仅对输出层词表进行抑制,这样不仅尽可能地降低了损失,同时过滤掉过低的词频,也有利于模型节点的充分训练,性能往往还会略有提升。词表的压缩可以提升建模性能,降低计算量和存储量,但仅限于一定的量级,不可以无限制压缩,如何继续降低计算量依然是一个问题。一些方法被提了出来。例如LightRNN,通过类似聚类的方式,利用embedding的思想,把词表映射到一个实值矩阵上,实际输出只需要矩阵的行加矩阵的列,计算量大概也能开个方。和节点数多一起造成计算量大的一个原因就是softmax输出,需要计算所有的节点求个和,然后得到分母。若是这个分母能保持一个常数,实际计算的时候就只算需要的节点,在测试环节就快的多了。于是就有了正则项相关的方法,Variance Regularization,如果训练速度可以接受的话,这种方法在基本不损失模型正确性的情况下可以大幅提升前向计算速度;如果训练的时候也想提速,还可以考虑基于采样,sampling的方法,比如NCE、Importance Sampling、Black Sampling等,本质上就是说,在训练的时候不计算全部节点,只计算正样本(也就是标签为1的节点),以及部分通过某种分布采样的到的负样本,避免高输出造成的计算缓慢。速度上提升还是很明显的。从阿里云获得开发者模型定制能力想象一个做智能电话客服或是智能会议系统的开发者,需要为他的系统接入语音识别(将语音转写为文字)的能力。摆在他面前的会是这样一个尴尬的局面:一个选择是自己从零开始学做语音识别,这可能要花费大量的时间和金钱。毕竟人工智能这种事情,各大互联网巨头投入大量的人力、物力、财力,也要花较长的时间才能积累下技术;第二个选择是用上述巨头们在互联网上提供的开箱即用的、one size fits all的语音识别接口,时间是省下了,但语音转文字的准确率嘛,只能碰碰运气,毕竟巨头们也很忙,没有精力为你关注的场景进行优化。那么问题来了:有没有一种手段能够以最小的投入获得业务上最佳的语音识别效果呢?答案是肯定的。阿里云依托达摩院业界领先的语音交互智能,打破传统语音技术提供商的供给模式,在云计算时代让普通开发者也能够通过阿里云提供的语音识别云端自学习技术,获得定制优化自己所关心的业务场景的成套手段。阿里云让广大的开发者站在巨头的肩膀上,通过自主可控的自学习,在短时间内实现对语音识别系统应用从入门到精通,并在开发者关心的场景下轻松拥有业界顶尖的语音识别准确率。这就是云计算时代的语音识别技术全新的供给模式。与其它人工智能技术一样,语音识别技术的关键在于算法、算力和数据三个方面。阿里云依托达摩院语音交互智能,近年来持续在世界前沿进行“算法”演进,近期还将最新的研究成果DFSMN声学模型开源,供全世界的研究者复现目前最佳的结果并进行持续提升。在“算力”方面自不用说,这本身就是云计算的天然强项。基于阿里云ODPS-PAI平台,我们构建了专为语音识别应用优化的CPU/GPU/FPGA/NPU训练和服务混布平台,每天服务于阿里云上巨量的语音识别请求。在“数据”方面,我们提供通过海量数据训练的、开箱即用的场景模型,包括电商、客服、政务、手机输入等等。同时应该看到,在具体的落地场景下往往会有一些非常特殊、领域相关的“说法”需要被识别,很多时候类似于“碎屑岩岩性地层”、“海相碳酸盐岩”这种特定说法对于通用场景模型的识别率提出了挑战。要获得开发者关心的具体场景下最佳的准确率,开箱即用的模型一般还需要一定的定制优化工作才可以达到。传统上,这样的定制是通过语音技术服务提供商来完成的,在成本、周期、可控性等方面都存在明显不足。阿里云提供的语音定制“自学习”平台服务,可以提供多种手段,在很短的时间内、以较低的成本,让开发者完全掌控模型定制优化及上线的工作。阿里云创新工具平台及服务技术,依托强大的基础设施,使得在云计算的大背景下进行大规模定制化语音服务成为可能。而开发者完全无需关心后台的技术和服务,只需要使用阿里云提供的简单易用的“自学习”工具,利用场景知识和数据,就可以获得该特定场景下最优的效果,并按需要持续迭代提升。阿里云的智能语音自学习平台具备以下优势:易:智能语音自学习平台颠覆性地提供一键式自助语音优化方案,极大地降低进行语音智能优化所需要的门槛,让不懂技术的业务人员也可以来显著提高自身业务识别准确率。快:自学习平台能够在数分钟之内完成业务专属定制模型的优化测试上线,更能支持业务相关热词的实时优化,一改传统定制优化长达数周甚至数月的漫长交付弊端。准:自学习平台优化效果在很多内外部合作伙伴和项目上得到了充分验证,很多项目最终通过自学习平台不光解决了效果可用性问题,还在项目中超过了竞争对手使用传统优化方式所取得的优化效果。举例来说,开发者可以使用下述多种“自学习”手段来定制自己关心领域的模型:a)业务热词定制在许多特定场所,要求快速对特定词的识别能力进行加强(注:包括两种模式,模式一为其他词易被识别成特定词;模式二为特定词易被识别成其他词),采用实时热词加载技术,可以在实时场景下,通过设置不同的档位,能够实现热词识别能力的加强。b)类热词定制很多时候,相同的发音相同的属性在不同上下文上会需要不同的识别效果。联系人和地名就是典型的案例,对于不同人的好友,“张阳”和“章扬”我们就必须能准确地识别出相应的名字。同样,相隔千里的安溪跟安西如果识别错误会给导航带来大麻烦。智能语音自学习平台相信“每个人都值得被尊重”,提供联系人类和地名类的定制能力,“让天下没有难识的路”。c)业务专属模型定制用户通过输入对应领域的相关文本,如行业或公司的基本介绍、客服聊天记录、领域常用词汇和专有名词等,即可快速自行生成该行业下的定制模型,整个定制过程无需用户人工干预。通过这些手段,阿里云使得开发者不必关心语音技术的算法和工程服务细节,专注于他们擅长的垂直领域的知识和数据收集,实现全新的语音技术云端供给模式,造福于广大的开发者及其业务结果。本文作者:鄢志杰、薛少飞、张仕良、郑昊、雷鸣阅读原文本文来自云栖社区合作伙伴“阿里技术”,如需转载请联系原作者。

January 15, 2019 · 1 min · jiezi

你应该要知道的重绘与重排

前言现代web框架大多都是数据驱动类的,比如 react, vue,所以开发者不需要直接接触 DOM,修改 data 便可以驱动界面更新。但是作为前端工程师,了解浏览器的重绘与重排还是很有必要的,可以帮助我们写出更好性能的 web 应用。浏览器的渲染CSS Tree: 浏览器将 CSS 解析成 CSSOM 的树形结构DOM Tree:浏览器将 HTML 解析成树形的数据结构Render Tree:将 DOM 与 CSSOM 合并成一个渲染树有了渲染树(Render Tree),浏览器就知道网页中有哪些节点,以及各个节点与 CSS 的关系,从而知道每个节点的位置和几何属性,然后绘制页面。重绘与重排当 DOM 的变化影响了元素的几何属性(比如 width 和 height ),就会导致浏览器重新计算元素的几何属性,同样受到该元素影响的其他元素也会发生重新计算。此时,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程被称为重排(也叫“回流”)(reflow),完成重排之后,浏览器会重新绘制受影响的部分到页面上,这个过程就是重绘(repaint)。所以重排一定会引起重绘,而重绘不一定会引起重排,比如一个元素的改变并没有影响布局的改变(background-color的改变),在这种情况下,只会发生一个重绘(不需要重排)。引起重排的因素可以总结出,当元素的几何属性或页面布局发生改变就会引起重排,比如:对可见 DOM 元素的操作(添加,删除或顺序变化)元素位置发生改变元素的几何属性发生改变(比如:外边距、内边距、边框宽度以及内容改变引起的宽高的改变)页面首次渲染伪类样式激活(hover等)浏览器视口尺寸发生改变(滚动或缩放)如何优化重绘与重排都是代价昂贵的操作(因为每次重排都会产生计算消耗),它们会导致 web 应用的 UI 反应迟钝,所以开发者在编写应用程序的时候应当尽量减少这类过程的发生。渲染树队列因为过多的重绘与重排可能会导致应用的卡顿,所以浏览器会对这个有一个优化的过程。大多数浏览器会通过队列化来批量执行(比如把脚本对 DOM 的修改放入一个队列,在队列所有操作都结束后再进行一次绘制)。但是开发者有时可能不知不觉的强制刷新渲染队列来立即进行重排重绘,比如获取页面布局信息会导致渲染队列的强制刷新,以下属性或方法会立即触发页面绘制:offsetTop、offsetLeft、offsetWidth、offsetHeightscrollTop、scrollLeft、scrollWidth、scrollHeightclientTop、clientLeft、clientWidth、clientHeightgetComputedStyle()以上属性和方法都是要浏览器返回最新的布局信息,所以浏览器会立刻执行渲染队列中的“待处理变化”, 并触发重排重绘然后返回最新的值。所以在修改样式的过程中,应该尽量避免使用以上属性和方法。减少重绘与重排为了减少重绘重排的发生次数,开发者应该合并多次对 DOM 的修改和对样式的修改,然后一次性处理。合并样式操作比如:var el = document.querySelector(‘div’);el.style.borderLeft = ‘1px’;el.style.borderRight = ‘2px’;el.style.padding = ‘5px’;可以合并成:var el = document.querySelector(‘div’);el.style.cssText = ‘border-left: 1px; border-right: 1px; padding: 5px;‘批量修改DOM使元素脱离文档流,再对其进行操作,然后再把元素带回文档中,这种办法可以有效减少重绘重排的次数。有三种基本办法可以使元素脱离文档流:隐藏元素,应用修改,重新显示var ul = document.querySelector(‘ul’);ul.style.display = ’none’;// code… 对ul进行DOM操作ul.style.display = ‘block’;使用文档片段(document fragment),构建一个空白文档进行 DOM 操作,然后再放回原文档中var fragment = document.createDocumentFragment();// code… 对fragment进行DOM操作var ul = document.querySelector(‘ul’);ul.appendChild(fragment)拷贝要修改的元素到一个脱离文档流的节点中,修改副本,然后再替换原始元素var ul = document.querySelector(‘ul’);var cloneUl = ul.cloneNode(true);// code… 对clone节点进行DOM操作ul.parentNode.replaceChild(cloneUl, ul)缓存布局信息前面已经知道,获取页面布局信息,会导致浏览器强制刷新渲染队列。所以减少这些操作是非常有必要的,开发者可以将第一次获取到的页面信息缓存到局部变量中,然后再操作局部变量,比如下面的伪代码示例:// 低效的element.style.left = 1 + element.offsetLeft + ‘px’;element.style.top = 1 + element.offsetTop + ‘px’;if (element.offsetTop > 500) { stopAnimation();}// 高效的var offsetLeft = element.offsetLeft;var offsetTop = element.offsetTop;offsetLeft++;offsetTop++;element.style.left = offsetLeft + ‘px’;element.style.top = offsetTop + ‘px’;if (offsetTop > 500) { stopAnimation();}总结为了减少重绘重排带来的性能消耗,可以通过以下几点改善 web 应用:批量修改 DOM 和样式“离线”操作 DOM 树,脱离文档流缓存到局部变量,减少页面布局信息的访问次数参考高性能JavaScript ...

January 14, 2019 · 1 min · jiezi

Mars 是什么、能做什么、如何做的——记 Mars 在 PyCon China 2018 上的分享

最近,在 PyCon China 2018 的北京主会场、成都和杭州分会场都分享了我们最新的工作 Mars,基于矩阵的统一计算框架。本文会以文字的形式对 PyCon 中国上的分享再进行一次阐述。听到 Mars,很多第一次听说的同学都会灵魂三问:Mars 是什么,能做什么,怎么做的。今天我们就会从背景,以及一个例子出发,来回答这几个问题。背景首先是 scipy 技术栈的全景图,numpy 是基础,它提供了多维数组的数据结构,并提供了它上面的各种计算。再往上,重要的有 scipy,主要面向各种科学计算的操作;pandas,其中核心的概念是 DataFrame,他提供对表类型数据的处理、清洗等功能。往上一层,比较经典的库,有 scikit-learn,它是最知名的机器学习框架之一。最上面一层,是各种垂直领域的库,如 astropy 主要面向天文,biopython 面向生物领域等。从 scipy 技术栈可以看出,numpy 是一个核心的地位,大量上层的库都使用了 numpy 的数据结构和计算。我们真实世界的数据,并不只是表这种二维类型数据那么简单,很多时候,我们要面对的往往是多维数据,比如我们常见的图片处理,首先我们有图片的个数,然后有图片的长宽,以及 RGBA 通道,这就是四维的数据;这样的例子不胜枚举。有这样多维的处理能力,就有处理各种更加复杂,甚至是科学领域的能力;同时,由于多维数据本身包含二维数据,所以,我们也因此具备表类型数据的处理能力。另外,如果我们需要探究数据的内在,光靠对表数据进行一些统计等操作是绝对不够的,我们需要更深层的“数学” 的方法,比如运用矩阵乘法、傅里叶变换等等的能力,来对数据进行更深层次的分析。而 numpy 由于是数值计算的库,加上各种上层的库,我们认为它们很适合用来提供这方面的能力。为什么要做 Mars,从一个例子开始那么,为什么要做 Mars 这个项目呢?我们不妨从一个例子来看。我们试图用蒙特卡洛方法来求解 pi,蒙特卡洛方法其实很简单,就是用随机数的方法来解决特定的问题。如图,这里我们有个半径为1的圆和边长为2的正方形,我们生成很多随机点的方式,通过右下角的公式,我们就可以计算出 pi 的值为 4 乘以落在圆里点的个数除以总的点个数。随机生成的点越多,计算出来的 pi 就越精确。用纯 Python 实现非常简单,我们只要遍历 N 次,生成 x 和 y 点,计算是不是落在圆内即可。运行1千万个点,需要超过10秒的时间。Cython 是常见加速 Python 代码的方式,Cython 定义了 Python 语言的超集,把这个语言翻译到 c/c++,然后再进行编译来加速执行。这里,我们增加了几个变量的类型,可以看到比纯 Python 提升了 40% 的性能。Cython 现在已经成为 Python 项目的标配,核心的 Python 三方库基本都使用 Cython 来加速 Python 代码的性能。我们这个例子里的数据都是一个类型,我们可以想到用专门的数值计算的库,通过矢量化的方式,能极快加速这个任务的性能。numpy 就是当仁不让的选择了,使用 numpy,我们需要的是面向 array 的思维方式,我们应当减少使用循环。这里先用 numpy.random.uniform 来生成 N2 的一个二维数组,然后 data ** 2 会对该数组里的所有数据做平方操作,然后 sum(axis=1) ,会对 axis=1 也就是行方向上求和,这个时候,得到的是长度为 N 的 vector,然后我们用 numpy.sqrt 来对这个 vector 的每一个值求开方,<1 会得到一个布尔值的 vector,即每个点是不是都是落在圆里,最后接一个 sum,就可以求出来总共的点的个数。初次上手 numpy 可能会不太习惯,但是用多了以后,就会发现这种写法的方便,它其实是非常符合直觉的。可以看到,通过使用 numpy,我们写出了更简单的代码,但是性能确大幅提升,比纯 Python 的写法性能提升超过 10 倍。那么 numpy 的代码还能够优化么,答案是肯定的,我们通过一个叫 numexpr 的库,来将 numpy 的多个操作合并成一个操作执行,来加速 numpy 的执行。可以看到,通过 numexpr 优化的代码,性能比纯 Python 代码提升超过 25 倍。此时的代码运行已经相当快了,如果我们手上有 GPU,那么我们可以利用硬件来加速任务执行。这里必须要安利一个库,叫 cupy,他提供了和 numpy 一致的 API,通过简单的 import 替换,就能让 numpy 代码跑在英伟达的显卡之上。这时可以看到,性能大幅提升超过 270 倍。真的非常夸张了。为了让蒙特卡洛方法计算的结果更加精确,我们把计算量扩大 1000 倍。会碰到什么情况呢?没错,这就是大家不时碰到的,OutOfMemory,内存溢出。更惨的是,在 jupyter 里,有时候内存溢出导致进程被杀,甚至会导致之前跑的全部结果都丢失。蒙特卡洛方法还是比较容易处理的,我把问题分解成 1000 个,每个求解1千万数据就好了嘛,写个循环,做个汇总。但此时,整个计算的时间来到了12分钟多,太慢了。此时我们会发现,整个运行过程中,其实只有一个 CPU 在干活,其他核都在原地吆喝。那么,我们怎么让 numpy 并行化呢?首先,numpy 里有一些操作是能并行的,比如 tensordot 来做矩阵乘法,其他大部分操作都不能利用多核。那么,要将 numpy 并行化,我们可以:采用多线程/多进程编写任务分布式化蒙特卡洛方法算 pi 改写成多线程和多进程实现还是非常容易的,我们写一个函数来处理1千万的数据,我们把这个函数通过 concurrent.futures 的 ThreadPoolExecutor 和 ProcessPoolExecutor 来分别提交函数 1000 遍用多线程和多进程执行即可。可以看到性能能提升到 2倍和3倍。但是呢,蒙特卡洛求解 pi 本身就很容易手写并行,考虑更复杂的情况。import numpy as npa = np.random.rand(100000, 100000)(a.dot(a.T) - a).std()这里创建了 10万10万的矩阵 a,输入就有大概 75G,我们拿 a 矩阵乘 a 的转置,再减去 a 本身,最终求标准差。这个任务的输入数据就很难塞进内存,后续的手写并行就更加困难。这里问题就引出来了,我们需要什么样框架呢?提供熟悉的接口,像 cupy 这样,通过简单的 import 替换,就能让原来 numpy 写的代码并行。具备可扩展性。小到单机,也可以利用多核并行;大到一个很大的集群,支持上千台机器的规模来一起分布式处理任务。支持硬件加速,支持用 GPU 等硬件来加速任务执行。支持各种优化,比如操作合并,能利用到一些库来加速执行合并的操作。我们虽然是内存计算的,但不希望单机或者集群内存不足,任务就会失败。我们应当让暂时用不到的数据 spill 到磁盘等等存储,来保证即使内存不够,也能完成整个计算。Mars 是什么,能做什么事Mars 就是这样一个框架,它的目标就是解决这几个问题。目前 Mars 包括了 tensor :分布式的多维矩阵计算。100亿大小的蒙特卡洛求解 pi的问题规模是 150G,它会导致 OOM。通过 Mars tensor API,只需要将 import numpy as np 替换成 import mars.tensor as mt,后续的计算完全一致。不过有一个不同,mars tensor 需要通过 execute 触发执行,这样做的好处是能够对整个中间过程做尽量多的优化,比如操作合并等等。不过这种方式对 debug 不太友好,后续我们会提供 eager mode,来对每一步操作都触发计算,这样就和 numpy 代码完全一致了。可以看到这个计算时间和手写并行时间相当,峰值内存使用也只是 1个多G,因此可以看到 Mars tensor 既能充分并行,又能节省内存的使用 。目前,Mars 实现了 70% 的常见 numpy 接口,完整列表见 这里。我们一致在努力提供更多 numpy 和 scipy 的接口,我们最近刚刚完成了对逆矩阵计算的支持。Mars tensor 也提供了对 GPU 和稀疏矩阵的支持。eye 是创建单位对角矩阵,它只有对角线上有值为1,如果用稠密的方式存储会浪费存储。不过目前,Mars tensor 还只支持二维稀疏矩阵。Mars 怎么做到并行和更省内存和所有的 dataflow 的框架一样,Mars 本身也有计算图的概念,不同的是,Mars 包含粗粒度图和细粒度图的概念,用户写的代码在客户端生成粗粒度图,在提交到服务端后,会有 tile 的过程,将粗粒度图 tile 成细粒度图,然后我们会调度细粒度图执行。这里,用户写下的代码,在内存里会表达成 Tensor 和 Operand 构成的粗粒度图。当用户调用 execute 方法时,粗粒度的图会被序列化到服务端,反序列化后,我们会把这个图 tile 成细粒度图。对于输入 10002000 的矩阵,假设指定每个维度上的 chunk 大小都是 500,那它会被 tile 成 24 一共 8 个chunk。后续,我们会对每个我们实现的 operand 也就是算子提供 tile 的操作,将一个粗粒度的图 tile 成细粒度图。这时,我们可以看到,在单机,如果有8个核,那么我们就可以并行执行整个细粒度图;另外给定 1/8 大小的内存,我们就可以完成整个图的计算。不过,我们在真正执行前,会对整个图进行 fuse 也就是操作合并的优化,这里的三个操作真正执行的时候,会被合并成一个算子。针对执行目标的不同,我们会使用 numexpr 和 cupy 的 fuse 支持来分别对 CPU 和 GPU 进行操作合并执行。上面的例子,都是我们造出来很容易并行的任务。如我们先前提到的例子,通过 tile 之后生成的细粒度图其实是非常复杂的。真实世界的计算场景,这样的任务其实是很多的。为了将这些复杂的细粒度图能够充分调度执行,我们必须要满足一些基本的准则,才能让执行足够高效。首先,初始节点的分配非常重要。比如上图,假设我们有两个 worker,如果我们把 1和3 分配到一个 worker,而将 2和4 分配到另一个 worker,这时当 5 或者 6 被调度的时候,他们就需要触发远程数据拉取,这样执行效率会大打折扣。如果我们一开始将 1和2 分配到一个 worker,将 3和4 分配到另一个 worker,这时执行就会非常高效。初始节点的分配对整体的执行影响是很大的,这就需要我们对整个细粒度的图有个全局的掌握,我们才能做到比较好的初始节点分配。另外,深度优先执行的策略也是相当重要的。假设这时,我们只有一个 worker,执行完 1和2 后,我们调度了 3 的话,就会导致 1和2 的内存不能释放,因为 5 此时还没有被触发执行。但是,如果我们执行完 1和2 后,调度了 5 执行,那么当 5 执行完后,1和2 的内存就可以释放,这样整个执行过程中的内存就会是最省的。所以,初始节点分配,以及深度优先执行是两个最基本的准则,光有这两点是远远不够的,mars 的整个执行调度中有很多具有挑战的任务,这也是我们需要长期优化的对象。Mars 分布式所以,Mars 本质上其实是一个细粒度的,异构图的调度系统。我们把细粒度的算子调度到各个机器上,在真正执行的时候其实是调用 numpy、cupy、numexpr 等等的库。我们充分利用了成熟的、高度优化的单机库,而不是重复在这些领域造轮子。在这个过程中,我们会遇到一些难点:因为我们是 master-slave 架构,我们 master 如何避免单点?我们的 worker 如何避免 Python 的 GIL(全局解释器锁)的限制?Master 的控制逻辑交错复杂,我们很容易写出来高耦合的,又臭又长的代码,我们如何将代码解耦?我们的解法是使用 Actor model。Actor模型定义了并行的方式,也就是一切皆 Actor,每个 Actor 维护一个内部状态,它们都持有邮箱,Actor 之间通过消息传递,消息收到会放在邮箱中,Actor 从邮箱中取消息进行处理,一个 Actor 同时只能处理一个消息。Actor 就是一个最小的并行单元,由于一个 Actor 同时只能处理一个消息,你完全不需要担心并发的问题,并发应当是 Actor 框架来处理的。而所有 Actor 是不是在同一台机器上,这在 Actor 模型里也变得不重要,Actor 在不同机器上,只要能完成消息的传递就可以了,这样 Actor 模型也天然支持分布式系统。因为 Actor 是最小的并行单元,我们在写代码的时候,可以将整个系统分解成很多 Actor,每个 Actor 是单一职责的,这有点类似面向对象的思想,这样让我们的代码得以解耦。另外,Master 解耦成 Actor 之后,我们可以让这些 Actor 分布在不同的机器上,这样就让 Master 不再成为单点。同时,我们让这些 Actor 根据一致性哈希来进行分配,后续如果有 scheduler 机器挂掉, Actor 可以根据一致性哈希重新分配并重新创建来达到容错的目的。最后,我们的 actors 是跑在多进程上的,每个进程里是很多的协程,这样,我们的 worker 也不会受到 GIL 的限制。像 Scala 或者 Java 这些 JVM 语言 可以使用 akka 这个 Actor 框架,对于 Python 来说,我们并没有什么标准做法,我们认为我们只是需要一个轻量的 Actor 框架就可以满足我们使用,我们不需要 akka 里面一些高阶的功能。因此,我们开发了 Mars actors,一个轻量的 Actor 框架,我们 Mars 整个分布式的 schedulers 和 workers 都在 Mars actors 层之上。这是我们 Mars actors 的架构图,在启动 Actor pool 的时候,我们子进程会根据并发启动若干子进程。主进程上有 socket handler 来接受远程 socket 连接传递消息,另外主进程有个 Dispatcher 对象,用来根据消息的目的地来进行分发。我们所有的 Actor 都在子进程上创建,当 Actor 收到一个消息处理时,我们会通过协程调用 Actor.on_receive(message) 方法。一个 Actor 发送消息到另一个 Actor,分三种情况。它们在同一个进程,那么直接通过协程调用即可。它们在一台机器不同进程,这个消息会被序列化通过管道送到主进程的 Dispatcher,dispatcher 通过解开二进制的头部信息得到目标的进程 ID,通过对应的管道送到对应子进程,子进程通过协程触发相应 Actor 的消息处理即可。它们在不同机器,那么当前子进程会通过 socket 把序列化的消息发送到对应机器的主进程,该机器再通过 Dispatcher 把消息送到对应子进程。由于使用协程作为子进程内的并行方式,而协程本身在 IO 处理上有很强的性能,所以,我们的 Actor 框架在 IO 方面也会有很好的性能。上图是裸用 Mars actors 来求解蒙特卡洛方法算 pi。这里定义两个 Actor,一个 Actor 是 ChunkInside,它接受一个 chunk 的大小,来计算落在圆内点的个数;另外一个 Actor 是 PiCaculator,它负责接受总的点个数,来创建 ChunkInside,这个例子就是直接创建 1000 个 ChunkInside,然后通过发送消息来触发他们计算。create_actor 时指定 address 可以让 Actor 分配在不同的机器上。这里可以看到,我们裸用 Mars actors 的性能是要快过多进程版本的。这里我们总结一下,通过使用 Mars actors,我们能不受 GIL 限制,编写分布式代码变得非常容易,它让我们 IO 变得高效,此外,因为 Actor 解耦,代码也变得更容易维护。现在让我们看下 Mars 分布式的完整执行过程。现在有1个 client,3个 scheduler 和 5个worker。用户创建一个 session,在服务端会创建一个 SessionActor 对象,通过一致性哈希,分配到 scheduler1 上。此时,用户运行了一个 tensor,首先 SessionActor 会创建一个 GraphActor,它会 tile 粗粒度图,图上假设有三个节点,则会创建三个 OperandActor,分别分配到不同的 scheduler 上。每个 OperandActor 会控制 operand 的提交、任务状态的监督和内存的释放等操作。此时 1 和 2 的 OperandActor 发现没有依赖,并且集群资源充足,那么他们会把任务提交到相应的 worker 执行,在执行完成后,向 3 通知任务完成,3 发现 1和2 都执行完成后,因为数据在不同 worker 执行,决定好执行 worker 后,先触发数据拉取操作,然后再执行。客户端这边,通过轮询 GraphActor 得知任务完成,则会触发数据拉取到本地的操作。整个任务就完成了。我们对 Mars 分布式做了两个 benchmark,第一个是对 36 亿数据的每个元素加一再乘以2,图中红色叉是 numpy 的执行时间,可以看到,我们比 numpy 有数倍提升,蓝色的虚线是理论运行时间,可以看到我们真实加速非常接近理论时间加速。第二个 benchmark,我们增加了数据量,来到 144 亿数据,对每个元素加1乘以2后,再求和,可以看到单机 numpy 已经不能完成任务了,此时,针对这个任务,我们也可以取得不错的加速比。未来计划Mars 已经在 Github 上源代码,让更多同学来一起参与共建 Mars:https://github.com/mars-project/mars 。在后续 Mars 的开发计划上,如上文说,我们会支持 eager mode,让每一步触发执行,提升对性能不敏感的任务开发以及 debug 时的使用体验;我们会支持更多 numpy 和 scipy 接口;后续很重要的一个是,我们会提供 100% 兼容 pandas 的接口,由于利用了 mars tensor 作为基础,我们也可以提供 GPU 的支持;我们会提供兼容 scikit-learn 的机器学习的支持;我们还会提供在细粒度图上调度自定义函数和自定义类的功能,增强灵活性;最后,因为我们客户端其实并不依赖 Python,任意语言都可以序列化粗粒度图,所以我们完全可以提供多语言的客户端版本,不过这点,我们会视需求而定。总之,开源对我们是很重要的,庞大的 scipy 技术栈的并行化,光靠我们的力量是不够的,需要大家来一起帮我们来共建。现场图片最后再 po 一点现场图片吧,现场观众对 Mars 的问题还是蛮多的。我大致总结下:Mars 在一些特定计算的性能,比如 SVD 分解,这里我们有和用户合作项目的一些测试数据,输入数据是 8亿*32的矩阵做 SVD 分解,分解完再矩阵乘起来和原矩阵做对比,这整个计算过程使用 100个 worker(8核),用7分钟时间算完Mars 何时开源,我们已经开源:https://github.com/mars-project/marsMars 开源后会不会闭源,答:不会Mars actors 的详细工作原理Mars 是静态图还是动态图,目前是静态图,eager mode 做完后可以支持动态图Mars 会不会涉及深度学习,答:目前不会本文作者:继盛阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 10, 2019 · 3 min · jiezi

OceanBase迁移服务:向分布式架构升级的直接路径

摘要: 数据库的架构升级就好像是在给一个高速运行的飞机更换引擎。2019年1月4日,OceanBase迁移服务解决方案在ATEC城市峰会中正式发布。蚂蚁金服资深技术专家师文汇和技术专家韩谷悦共同分享了OceanBase迁移服务的重要特性和业务实践。蚂蚁数据库架构的三代升级史在过去的十多年时间里,蚂蚁在整个基础数据库架构上一共经历了三代升级。第一代数据架构是构建在IOE的基础之上——IBM的小型机、Oracle的商业数据库,还有EMC的共享存储。基于第一代IOE架构的运维成本是非常高的,同时稳定性的挑战也是非常大的。随着业务的快速发展,这套架构已经完全没有办法适应业务发展的增速。随之诞生的是第二代架构,第二代架构的主体是OE——也就是Oracle和EMC,加上蚂蚁自身的分布式中间件,解决了业务的水平和垂直的弹性能力。这一代架构其实伴随着蚂蚁走了很多年。随着4G、5G时代的到来和金融的普及化,人们的生活越来越离不开移动支付,业务井喷式的发展给底层的数据库提出了更高的要求。这些要求包括更高的稳定性,快速恢复能力和极致的弹性能力等。于是最终演进到了我们如今的第三代架构。第三代架构是由OceanBase为代表的金融级云数据库和分布式中间件所构成。数据库架构升级的挑战伴随着整个蚂蚁的发展,整个数据库的架构也仅仅演进了三代。这其中一个很重要的原因就是对于任何企业而言,整个数据库的架构升级都是一件非常有挑战的事情。蚂蚁金服资深技术专家师文汇说道,“用一个我们内部经常说的比喻,就是数据库的架构升级就好像是在给一个高速运行的飞机更换引擎。”更换引擎的目的是为了拥有更好的动力,做更多技术上的创新。但是横亘在眼前的问题是,如何才能做到稳妥创新,保证驾驶中的飞机平稳顺利的运行,这其实是有非常大的挑战。在过去三代架构的演进中我们可以看到,本质上每一代架构的迭代基本上都是以两到三年为周期,这其中会有非常高的人力投入和成本开销。第二个挑战就是从传统的商业数据库迁移到OceanBase数据库之上,我们如何保证迁移过程中以及迁移以后的稳定性。另外一个非常大的挑战就是数据质量,在金融企业里,数据承载的不仅只是钱,更承载了数以亿计用户的信任。所以数据一条不能丢,一条不能错,这是我们做数据库的底线。当然,包括兼容性问题和性能风险也给数据库的架构升级带来重重挑战。OceanBase迁移服务:向分布式架构升级的直接路径基于上述问题和挑战,同时经过蚂蚁十年数据库架构升级的先进经验,蚂蚁金服为客户打造了这款一站式数据迁移解决方案——OceanBase迁移服务(OceanBaseMigration Service,简称OMS)。OMS的发展演进OMS的演进是以业务为驱动,并且与OceanBase的架构升级和不断发展密不可分。早在2014-2015年期间,蚂蚁主站上的一些核心业务,包括大家熟知的交易业务,支付业务和会员业务等,需要从Oracle迁移到OceanBase上。当时的OMS还是以一个工具类、模块化的形态支撑着这些项目。所以在2015年我们开始对OMS的方案进行全面的调研,力求沉淀出通用的系统化的解决方案。在2016年,OMS已经有了平台化的架构,引入了大规模编排的思想,将整个迁移特别是切换过程中繁琐易错的环节全部集成到平台。这一时期,OceanBase也完成了从0.5版本到1.0版本的架构升级,这一年OMS还支撑了网商银行、印度PayTM以及主站的核心业务升级到OceanBase 1.0版本。到了2018年的时候,无论在基础功能层面还是任务编排层面,OMS都已经被打磨得日趋完善。今年OMS已经支持了蚂蚁森林,蚂蚁商户平台以及众多大量核心及非核心的业务从MySQL迁移到OceanBase之上。与此同时,在外部业务包括很多已经上线OceanBase的商业银行,也已经验证了使用OMS一键迁移到OceanBase的能力。OMS的方案优势OceanBase迁移服务其实主要解决了五个重要的问题。1.负载回放验证:其中第一个核心的问题就是负载回放验证,通过采集源端数据库的SQL流量,在目标库OceanBase上回放,可以验证其在OceanBase上的功能是否兼容、性能是否出现问题等。同时基于蚂蚁DBA十多年的经验沉淀,OMS会为客户提供性能等方面的调优建议。2.秒级数据校验:第二点就是数据校验,OMS有三层数据校验,可以做到秒级的延迟。举一个例子,比如说我们想把传统商业数据库替换成OceanBase,如果在迁移过程中任何一条数据出现了错误,在一秒钟内就可以快速发现。校验的延迟可以完全保证在一秒以内,根据蚂蚁线上的经验,大概在100-200毫秒之间。3.分钟级即时回滚:第三点也是最重要的一点,就是OMS有随时回滚的能力,而且回滚是无损的。这也是我们前面所强调的稳妥创新的基石。4.多种数据库类型支持:目前OMS支持源端数据库类型有Oracle、MySQL、OceanBase等等,支持全量迁移和增量数据同步。5.一键完成迁移:整个数据迁移链路和回滚机制的搭建基本上都是通过一键操作完成,使用简便。OMS的技术架构OMS的核心方案其实非常简单,我们把OceanBase变成Oracle/MySQL的一个备库。传统的商业数据库一般都是有主库和备库的:主库承担写的流量,如果主库出现问题,我们会把数据切到备库,然后通过OMS提供的一整套虚拟主备库的解决方案完成切换。比如原来Oracle有一个主库一个备库,然后OceanBase其实变成了一个虚拟的备库。整个数据库架构的升级也会变得异常简单,简单到只是做了一个主备切换。回滚也会变得非常简单,其实也是做了一次主备切换。从OMS的整体架构来看,其实一个非常关键的点就是,我们在传统的商业数据库和OceanBase之间建立了一套虚拟的主备链路,整个OMS里用到的所有组件,其实都是在蚂蚁和阿里有很多年技术沉淀的,也都是基于真实场景所产生的。OMS的迁移流程OceanBase迁移服务的整体迁移流程其实只有七步。1.评估:首先第一步是通过负载回放工具做兼容性分析;2.PoC:接下来OceanBase云平台可以帮助客户部署一套PoC集群;3.预迁移:然后OMS把线上的Oracle的数据预迁移到一个测试库里;4.验证:在这个测试库里用负载回放工具去回放这些SQL,然后找到SQL里不兼容,性能或者数据质量不满足预期的部分,并提供优化建议;5.正式迁移:前四步做完了以后,业务需要调整或者需要优化的SQL已经完成优化,然后就可以正式迁移了。首先把原有的全量数据迁过来,然后再把增量变化的那部分数据实时同步过来;6.校验:等到所有的数据准备好以后,然后我们继续完成三级校验;7.切换和回滚:等到所有的校验都完成以后,可以一键完成切换和回滚功能。通过这七步就可以轻松完成从传统商业数据库到分布式数据库的完整迁移。蚂蚁商户平台基于OMS的业务实践蚂蚁商户平台承载着商户档案数据信息,订购关系、签约信息的数据和相应的服务能力。其中一部分业务使用的是MySQL数据库,还有一部分核心业务使用的是Oracle数据库。随着商户的快速增长以及业务场景的不断丰富,商户平台数据增长迅速,数据规模相当庞大。尤其是MySQL的单表瓶颈日益明显,DDL变更、DML更新的性能与风险已经无法承担。蚂蚁金服技术专家韩谷悦介绍道,“OceanBase能够支持数据的无限扩展,满足商户业务的容量与性能需求。那么如果我们换一种数据库底盘,其实所要面对的性能、稳定性和数据质量的风险同样不可避免。”从蚂蚁商户平台的业务实践来看,使用OMS迁移与传统迁移进行对比,我们可以看到:业务评估和改造过去通常一个业务少则花费1-2个月的时间去做改造和适配;那么基于OMS自动化的SQL兼容性评估和负载回放的能力,蚂蚁商务平台业务的改造大概只用了一个星期的时间。数据迁移和校验客观来讲,迁移的总时长主要取决于业务数据模型,数据量和网络环境。在提高迁移效率方面,OMS目前增量迁移的延迟仅为毫秒级,跨城情况下最长只需要3秒。并且针对校验出的数据差异提供补齐的SQL和订正方案,使得迁移和校验的整体效率有了大幅度的提升。业务切换其实在切换之前,往往需要制定严密的切流方案和Failover方案,整个切换过程中需要检查与校验的细节非常繁琐,任何一步疏忽都有可能造成数据不一致的问题。那么OMS通过引入大规模编排的思想,把所有繁琐复杂的环节通通落到平台当中。所以从原来业务切换需要用时1-2周时间, 使用OMS后蚂蚁商户平台业务无论是切读还是切写的过程中都只用了几分钟的时间。业务回滚在过去,迁移之后的业务回滚要担负重大的决策风险,OMS使得业务回滚就像一次主备切换,可以瞬间完成并且不丢数据,所以让业务回滚不再成为难题。商户业务整体迁移的过程中也发生过业务抖动,使用OMS回滚的时候从登陆系统到完成回滚也只用了几分钟的时间。所以全程下来蚂蚁商户平台这个业务的迁移时间大概在三个多星期的时间完成,那么无论从人力成本还是时间成本上,OMS都极大地提升了数据库的整体迁移效率。最后,韩谷悦为大家展示了OMS一键迁移的demo演示。当前, 越来越多的企业已经认识到分布式架构在实现业务灵活扩展以及敏捷开发等方面的巨大价值。OceanBase不断通过产品端的革新,为传统企业输送“互联网基因”,帮助更多客户向分布式架构转型。同时OceanBase也在不断提高服务客户的深度和广度。深度意味着在同样的业务场景下,随着业务的发展和体量的壮大,帮助更多企业承担起业务所带来的极致压力。广度则针对的是随着新型技术形态和业务场景的出现,帮助更多企业快速响应,通过技术创新而适应变化所带来的新的市场契机。OceanBase致力于将蚂蚁自身业务多年沉淀下来的最浓缩,最经典和最普世的方法论输出给广大的企业客户,同时做到深度和广度并存,真正帮助客户实现稳妥创新。本文作者:平生栗子阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 10, 2019 · 1 min · jiezi