
云栖运营小编~
本文介绍如何在 Alibaba Cloud Linux 2 上使用内核热补丁 (Kernel Live Patching) 功能。 1. 准备工作 1.1 内核版本 首先要确认当前运行的内核版本。在操作系统内部运行下列命令: uname -r 4.19.57-15.1.al7.x86_64 1.2 安装依赖包和内核相关工具 sudo yum install -y alinux-release-source alinux-release-kernels sudo yum install -y yum-utils sudo debuginfo-install -y kernel-$(uname -r) # 安装 Debuginfo 包 sudo yum install -y kpatch pesign zlib-devel \ binutils-devel newt-devel python-devel perl-ExtUtils-Embed \ audit-libs audit-libs-devel numactl-devel pciutils-devel bison patchutils \ kernel-devel-$(uname -r) # 安装依赖工具 sudo yum-builddep -y kernel-$(uname -r) # 检查 build 内核的时候的依赖 1.3 安装 kpatch-build 要想制作热补丁,需要用到一个还没有放进 YUM 源的工具 kpatch-build,需要从 GitHub 上获取源码。运行下列命令获取代码,并进行编译: sudo yum install -y git git clone https://github.com/dynup/kpatch.git cd kpatch make 1.4 准备内核源码 sudo yumdownloader --source kernel-$(uname -r) #安装内核源码 export VRDA=$(uname -r) rpm -ivh kernel-${VRDA/x86_64/src}.rpm rpmbuild --without debug \ --without doc \ --without perf \ --without tools \ --without bpftool \ --without debuginfo \ -bp ~/rpmbuild/SPECS/kernel.spec export SourceDir=$(ls -d ~/rpmbuild/BUILD/kernel-${VRDA/-*/}/linux-*) cp ~/rpmbuild/SOURCES/modsign_alinux.pem $SourceDir/certs cp ~/rpmbuild/SOURCES/x509.genkey $SourceDir/certs sed -i "s/CONFIG_LOCALVERSION=.*/CONFIG_LOCALVERSION=\"${VRDA/*-/-}\"/" $SourceDir/.config 2. 准备需要进行热补丁的补丁文件 请根据实际情况准备热补丁文件,一般来说从内核源码的 Git 树中获取的补丁文件就可以使用,但是不是所有补丁文件都可以用来制作热补丁,请确保事先对于热补丁的使用范围与限制有充分的了解,否则可能会出现不仅限于操作系统宕机等严重问题。 这里是一个样例补丁文件: diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index edda898..8a4a686 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -121,7 +121,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) seq_printf(m, "VmallocTotal: %8lu kB\n", (unsigned long)VMALLOC_TOTAL >> 10); show_val_kb(m, "VmallocUsed: ", 0ul); - show_val_kb(m, "VmallocChunk: ", 0ul); + show_val_kb(m, "VMALLOCCHUNK: ", 0ul); show_val_kb(m, "Percpu: ", pcpu_nr_pages()); 注意补丁文件一定要与当前下载的内核源码包解压缩之后的源码相匹配。 3. 执行热补丁制作 cd kpatch export VRDA=$(uname -r) export SourceDir=$(ls -d ~/rpmbuild/BUILD/kernel-${VRDA/-*/}/linux-*) ./kpatch-build/kpatch-build -v /usr/lib/debug/usr/lib/modules/$(uname -r)/vmlinux \ meminfo.patch \ -s $SourceDir \ -t vmlinux \ -j$(getconf _NPROCESSORS_ONLN) #meminfo.patch 是前面一步里的补丁文件 执行完毕后如果顺利,会在当前目录下产生 livepatch-meminfo.ko 文件 4. 加载热补丁 sudo kpatch load livepatch-meminfo.ko
阿里妹导读:缓存是搭建高性能高并发系统的必备手段之一,通常用来解决性能瓶颈,是程序员的必备知识点,也是面试必备考点。 尽管,产品经理大概率不会关注系统性能,但程序员在实现需求的时候必须思考系统承载的并发量和用户量。缓存主要用来解决性能瓶颈的问题,一旦错误使用反而会令系统崩溃。今天,我们就通过4W的方式系统化地总结缓存相关的理论知识。 随着互联网业务的快速迭代以及用户量激增,应用架构需要不断调整甚至重构以适应这种业务的快速发展。当数据量迅速增长,业务逻辑越复杂,服务链路不断增加等等一系列问题,会导致RT过长,服务性能需要逐渐提升以满足更优的用户体验。在优化系统架构时通常的所用的两种方式scale up以及scale out,scale out就是通常所说的水平扩展,将应用服务设计成无状态性,可以方便水平扩展通过增加硬件的方式分解访问压力。而scale up则是将单个服务链路性能提升,以提升QPS以及系统的吞吐量。在追求更优的性能时,大多数业务场景是读多写少的情况,一般会通过引入缓存的方式解决。 1. What——什么是缓存? 从定义上可以看出所谓缓存一定是针对已有数据的一个副本存在,也可以看出缓存的使用是为了解决快速访问数据(读数据)的场景。在现有的互联网应用中,缓存的使用是一种能够提升服务快速响应的关键技术,也是产品经理无暇顾及的非功能需求,需要在设计技术方案时对业务场景,具有一定的前瞻性评估后,决定在技术架构中是否需要引入缓存解决这种这种非功能需求。 缓存在计算机领域中实际案例存在很多,比如CPU的缓存是为了解决CPU的运算速度和内存的读取数据不平衡的问题,CPU的运算速度远快与内存的读写速度,为了降低CPU等待数据读写的时间,在CPU中引入L1/L2/L3多级缓存。 再比如Linux中的文件缓存,实际上我们在编程时,会谈论到数据的内存地址,但是我们接触的都是虚拟地址而不是真实的物理地址,计算机中的内存管理单元(MMU)和页表会将虚拟地址转换成物理地址。在计算机硬件领域中就已有很多关于缓存的应用案例,实际上在软件架构中关于缓存的设计会借鉴于很多传统且成熟的计算机硬件缓存设计的思想。 2. Why——为什么需要使用缓存? 软件服务能够得到用户的信赖,并将产品的价值带给用户,能够解决目标用户的痛点问题这是决定用户会不会一开始决定使用,也就是《增长黑客》中提到了产品能够带来给用户“啊哈时刻”,而决定用户会不会高频使用以及持续使用,用户体验则是被认为是软件产品提升用户黏性的关键影响因素。 2.1 什么是用户体验 用户体验被专业定义和推广需要推广到20世纪90年代,由Donald Norman布道推广。用户体验在人机交互领域上受到了重视,并一度和传统的三大可用性指标(即效率、效益以及基本满意度)不相上下。 ISO 9241-210标准将用户体验官方定义为:人们对正在使用或者期待使用的产品、系统或者服务的认知印象和回应。可以看出用户体验是用户的对软件产品的主观感受,具体包含了用户在使用之前、使用中以及使用后的情感、喜好、认知印象、心理反应以及情绪表达等等多种主观感受,每个用户对产品的主观感受的视角不同,关注点不同,也就导致软件产品让大多数用户都能够获得很好的用户体验本身就是一件很有挑战性的事情。 在业界大多数,将用户体验分为三类:使用者状态、软件产品的系统性能以及环境,使用者状态以及环境(使用者环境以及产品在同类产品中的产品大环境)这两个因素需要交互设计和用研等多个专业领域同学去攻克,软件开发者则需要解决系统性能的问题。对用户而言,最基本的需求就是在使用软件服务时,软件产品提供服务内容的及时性,也就是通常所说的在使用过程中持续的Loading(转菊花)一定会导致用户体验很差,内容的及时性也是系统性能的最低要求。 而系统性能的问题,是产品经理无暇顾及的点,也是非功能性需求,需要开发者去花心思去思考的地方。评估系统性能的指标有很多,在以提升用户体验为前提的情况下,我们需要着重关注的性能指标有哪些呢? 2.2 常见的性能指标 在设计软件架构时需要关注的几个常见指标:响应时间、延迟时间、吞吐量、并发用户数和资源利用率。 1)系统响应时间 :响应时间是指系统对用户请求做出响应的时间,不同的功能的链路长短不同,并且同一功能在不同数据量等这些情况都会导致响应时间的不同。因此,在衡量系统响应时间时,通常会关注软件产品所有功能的平均响应时间以及最大响应时间。 2)延迟时间 :在讨论系统响应时间时,更细粒度的划分可以划分为: 客户端在接受数据进行渲染的内容“呈现时间”; 服务端在接受用户请求发送至服务端以及服务端将数据返回到客户端这两个过程中涉及到的:网络传输时间以及应用延迟时间。应用延迟时间即是服务端在执行整个服务链路时所花费的时间,也是性能优化首要降低的就是这个时间。 3)吞吐量 :吞吐量指的是单位时间内能够处理请求的数量,对于无并发的应用来说,吞吐量和请求响应时间成反比,服务延迟更长则系统吞吐量更低。 4)并发用户数 :并发用户数指的是系统能够同时承载正常使用系统功能的用户数,相较于吞吐量,这个指标更为笼统但是对于非软件领域的人来说更容易理解。 5)资源利用率 :资源利用率反映的是在一段时间内资源被占用的情况。 2.3 缓存带来的优势 在追求更优的优化体验时,客观的来说需要不断提升以上这些性能指标,不断逼近系统体验的最优解。缓存到底具有什么样的优势,值得我们花费很大的精力去设计一套能很好的适应现在的业务场景的缓存结构呢? 1)极大的提升软件用户体验 软件产品主要围绕两个核心问题,一是解决目标用户的痛点问题,二是提升产品黏性。在提供软件服务时,抽象的来看是解决数据在整个链路上的流转问题,如何让数据流转更加高效、更加顺畅是在实现时着重关注的地方,事实上,无论是浏览器、负载均衡、应用服务器还是数据库等等各个环节都会应用到缓存,当数据离用户“更近”,比如数据副本在客户端上,也就意味着请求能够很快的进行响应,相应的给用户进行数据呈现的耗时就更短。现如今用户爸爸们“日理万机”,如果一个软件产品不能在很短时间就获取用户的注意力,很大可能性就意味着失败。因此,使用缓存能够让用户从主观上获取更优的用户体验。 2)提升吞吐量 试想,如果在服务链路上,请求能够在缓存中获取服务数据的话,也就意味着很多数据并不需要从源应用服务器进行获取,降低了源服务器网络传输的频率,在一定IDC带宽下,系统能够降低网络传输时间以及应用延迟时间,从而支撑更多的系统访问以提升系统整体吞吐量以及并发用户数,硬件的使用效率也会明显提升。 从实际场景下,在系统性能优化时大概率会优先选择使用缓存进行系统优化,也是一种被证明有效的手段,缓存也被认为是一种“空间换时间”的艺术。 3. Where——缓存存在链路中的哪些地方? 3.1 缓存分类 从一个请求到最终获取响应,会经过很多环节,缓存可以几乎存在整个链路的每个节点。缓存按照不同的维度可以有如下分类: 1)缓存所处链路节点的位置:客户端缓存 网络缓存 服务端缓存 2)缓存架构部署方式:单机缓存 缓存集群 分布式缓存 3)缓存的内存区域本地缓存/进程内缓存 进程间缓存 远程缓存 按照缓存在服务链路上的位置来划分,可以系统性的梳理下缓存的不同应用。 3.2 客户端缓存 客户端缓存是离用户“最近”的一种存储介质,经常和网络测和服务端缓存一起配合使用,常见的客户端缓存有如下几种: 1)页面缓存:页面缓存是指将静态页面获取页面中的部分元素缓存到本地,以便下次请求不需要重复资源文件,h5很好的支持的离线缓存的功能,具体实现可通过页面指定manifest文件,当浏览器访问一个带有manifest属性的文件时,会先从应用缓存中获取加载页面的资源文件,并通过检查机制处理缓存更新的问题。 2)浏览器缓存:浏览器缓存通常会专门开辟内存空间以存储资源副本,当用户后退或者返回上一步操作时可以通过浏览器缓存快速的获取数据,在HTTP 1.1中通过引入e-tag标签并结合expire、cache-control两个特性能够很好的支持浏览器缓存,关于浏览器缓存更为细节的知识可以查看该文章。 3)APP缓存:APP可以将内容缓存到内存或者本地数据库中,例如在一些开源的图片库中都具备缓存的技术特性,当图片等资源文件从远程服务器获取后会进行缓存,以便下一次不再进行重复请求,并可以减少用户的流量费用。 客户端缓存是前端性能优化的一个重要方向,毕竟客户端是距离“用户”最近的地方,是一个可以充分挖掘优化潜力的地方。 3.3 网络缓存 网络缓存位于客户端以及服务端中间,通过通过代理的方式解决数据请求的响应,降低数据请求的回源率。通常具有如下几种形式的网路缓存: 1)web代理缓存:常见的代理形式分为分为:正向代理、反向代理以及透明代理。web代理缓存通常是指正向代理,会将资源文件和热点数据放在代理服务器上,当新的请求到来时,如果在代理服务器上能获取数据,则不需要重复请求到应用服务器上; 2)边缘缓存:和正向代理一样,反向代理同样可以用于缓存,例如nginx就提供了缓存的功能。进一步,如果这些反向代理服务器能够做到和用户请求来自同一个网络,那么获取资源的速度进一步提升,这类的反向代理服务器可以称之为边缘缓存。常见的边缘缓存就是CDN(Content Delivery Network),可以将图片等静态资源文件放到CDN上。 3.4 服务端缓存 服务端缓存是后端开发中进行性能优化的发力点,常见的后端性能优化也是通过引入缓存来进行解决,常见的有数据库的查询缓存、缓存框架以及引入应用级缓存。 3.4.1 数据库查询缓存 例如,MySQL的缓存机制是通过将SELECT语句以及相应的ResultSet进行缓存,当后续接受到SELECT请求后,如果MySQL已经开启了Query Cache功能,会将SELECT语句以字符串的方式进行hash,然后去从缓存中进行查询,如果查询出数据,则直接进行返回,省去了后续的优化器以及存储引擎IO的操作,能够极大的提升响应时效。如何优化Query Cache需要从如下几个指标上进行考虑: query_cache_size:设置能够缓存ResultSet的内存区域大小query_cache_type:表示使用缓存的场景。0表示任何场景下都不使用Query Cache,1表示显式指定不使用Query Cache的查询都可以使用,2(DEMAND)表示只有明确指示使用Query Cache才会生效;Qcache hits:表示多少次查询命中Query CacheQcache inserts:表示多少次没有命中Query Cache而插入数据Qcahce lowmem prunes:表示多少条Query引入空间不足而被清除Qcache free memory:表示剩余内存大小Qcache free blocks:该值很大表示内存碎片很多,需要及时清理 在进行Qcache优化时,可以对以上指标综合进行分析,比如了解Qcache的缓存命中率 = Qcache hits/ Qcache hits + Qcache inserts,来判断当前Qcache的效率。也可以结合Qcahce lowmem prunes、Qcache free memory以及Qcache free blocks来判断当前Qcache的内存使用效率。 另外,如果使用Innodb存储引擎的话,也需要着重关注innodb_buffer_pool_size参数,该参数决定了innodb的索引以及数据是否有足够大的空间放入到缓存中。table_cache决定了能够缓存表的最大数量,也是需要关注的一个参数。 3.4.2 缓存框架 在功能开发时,会常用提供缓存特性的缓存框架或者实现缓存功能的类库来高效的完成开发,常见的缓存框架有Ehcache、Guava等,这些缓存框架配置简单,能够简单灵活的使用。这些开源的缓存框架不仅支持单机的本地缓存还能配置集群的方式达到灵活伸缩。 3.4.3 应用级缓存 当缓存框架不能满足需求的时候,就需要引入应用级缓存,比如Redis、MongoDB等NoSQL数据库,应用级缓存具备高可用性以及伸缩性的分布式架构能够支撑业务需求,当然,做好一款应用级缓存产品其中的挑战也是巨大。 4. When——什么时候需要使用缓存? 缓存不是架构设计的必选项,也不是业务开发中的必要功能点,只有在业务出现性能瓶颈,进行优化性能的时候才需要考虑使用缓存来提升系统性能。也不是所有的业务场景都适合使用缓存,读多写少且数据时效要求越低的场景越适合使用缓存,缓存并不是所有性能问题的灵丹妙药,如果滥用缓存反而会成为毒药,并且会引入维护缓存的操作成本,使得系统复杂度更高不利于维护。 另外把缓存当做存储来使用是一件极其致命的做法,这种错误的认识,将缓存引入系统的那一刻起就意味着已经让系统走上了危险的局面,对缓存的使用边界要有深刻的理解,才能尽可能保证做出引入缓存才是一个正确的决定。 在进行缓存结构设计的时候,需要考虑的点有很多: 1)业务流量量级以及应用规模:对于低并发低流量的应用而言,引入缓存并不会带来性能的显著提升,反而会带来应用的复杂度以及极高的运维成本。也不是任何数据都需要使用缓存,比如图片视频等文件使用分布式文件系统更合适而不是缓存。因此,在引入缓存前,需要对当前业务的流量进行评估,在高并发大流量的业务场景中引入缓存相对而言收益会更高; 2)缓存应用的选择:缓存应用有很多如Redis、Memcached以及tair等等,针对每一种分布式缓存应用的优缺点以及适用范围、内存效率、运维成本甚至团队开发人员的知识结构都需要了解,才能做好技术选型; 3)缓存影响因素的正确评估:在引入缓存前,需要着重评估value大小、缓存内存空间、峰值QPS、过期时间、缓存命中率、读写更新策略、key值分布路由策略、过期策略以及数据一致性方案等等多个因素,要做到心中有数; 4)缓存高可用架构:分布式缓存要高可用,这也是分布式系统追求的三高指标中的一个,缓存的集群设计,主从同步方案的设计等等,只有缓存足够可靠,才能服务于业务系统,为业务带来价值; 5)完善的监控平台:当缓存投入生产环境后,需要有一套监控系统能够显式的观测缓存系统的运行情况,才能更早的发现问题,同时对于预估不足的非预期热点数据,也需要热点发现系统去解决非预期的热点数据缓存问题。 6)缓存最近原则:将缓存数据放在离用户最近的地方,无疑会极大的提升响应的速度,这也是多级缓存设计的核心思想。 原文发布时间为:2019-11-25作者: 阿里技术本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
在各行各业中,一群有观点,敢于创新,勇于面对挑战的技术达人,正在全面地参与中国数字化转型,为中国在前沿科技实践中贡献自己一份力量! 拥抱新科技,大胆在商业中不断验证自己的技术想法,阿里云 MVP就是这样一群人,阿里云最具价值专家,简称MVP(Most Valuable Professional),是阿里云为感谢全球杰出技术人所作贡献,授予全球行业数字化转型的技术实践领袖的荣誉称号。他们懂技术,爱分享,愿意普惠更多开发者,推动行业进步。他们来自数字转型最热行业,他们如点点繁星,在各自领域发光发热,点亮更多技术人的前路。 马上申请成为阿里云 MVP,一起同行! 长按二维码 申请MVP 刘湘雯,阿里巴巴云智能事业群战略与合作部总经理,阿里巴巴达摩院院长助理曾说:“阿里云 MVP不是家人,胜似家人!” 别样人生:创业精神,薪火相传 脱去国际大公司的光环,转化角色成为引领者,将自己技术创新理念,转化为产品解决行业问题,带领同路人勇于面对行业挑战! 北京叶帆科技创始人兼CEO刘洪峰,冶金专业出身,出于对编程的热爱自学C语言。从微软中国的软件开发,到工控程序员到物联网创业公司CEO等多个角色,都坚持着“制造机器人的梦想”。十多年技术路上他逐渐成为技术布道师,《技术创业者如何绘制战略“一张图”》一文,吸引30+万的创业者、技术人。 赵舜东被圈内人士亲切地称为“赵班长”,曾在武警某部负责指挥自动化的架构和运维工作,2008年退役后一直从事互联网运维工作,当过站长维护服务器,早期拥抱云计算,从Devops、智能运维不断升级自己技术实力,形成了自己独特的方法论。现在已经成为运维圈知名的讲师! 思必驰VP、北京研发院院长、中科院女博士,初敏的背景漂亮得令人称叹,是女性开发者的典型代表。这位阿里校友成为阿里云MVP,继续与我们并肩作战,深耕人工智能,拓展语音技术在物联网外的新业务、新场景的落地。 也许他们的专业、性格相去甚远,但因同为“阿里云MVP”身份而得以相聚一堂,享受思想火花的碰撞,寻找同行人坚持前路。 数字中国:云计算助力行业转型 上海驻云科技CEO蒋烁淼,2013年创立了公司,这是缘自从小就玩互联网,在1994年还凭借着上网证的时代,就接触互联网了,1999年建立了个人网站。早年接触了阿里云,提出了很多意见和建议,他成为了试飞员,引领中国云计算的产业发展。像桃园结义那样,找到了创业中最合适的人与他共同前行。他认为云计算逐渐成为了一个从包含基础设施,到提供整套管理、软件解决方案的庞大体系;未来,下一个互联网时代中,将会是物联网的天下,而以云计算作为物联网的基石,才是制胜的关键。 90后 CTO花了6年,改变了你日常生活里的这件事。今年25岁的黄胜蓝接到了阿里云MVP认证通过的邮件,成为最年轻的阿里云MVP之一。黄胜蓝的创业经历堪称精彩:高中凭借奥赛保送武汉大学,大二一度中断学业开始投入创业。六年后的今天,极验验证已经为20多万家免费客户、近500家付费客户提供服务。黄胜蓝曾说:“在寒风下坚持自己、专注练内功,在春天来的时候,你就是第一个起跑的人。” 码上公益:用技术温暖他人 MVP共创公益项目,一直都在默默进行。从服务数万爱心教师支教,到推动中国渔业生态保护,阿里云MVP主动肩负起了更多的使命。 浙江银杏谷投资公司总裁技术助理戚俊,作为一场特殊“产品沟通会”的主持人,同时也是阿里云“码上公益”平台上的3000多名爱心极客之一,特意从南京赶到上海,为“最美食物包”系统三天后的产品上线,做最后一次沟通、调研。他在繁忙的日常工作之余,放弃了娱乐休闲活动,花了1个多月的时间,从初期沟通到撰写代码都亲自上阵,实在忙不过来的时候,也拉着公司同事一起帮忙。 戚俊曾说“即使最基本的,都不应该浪费。即使最贫穷的,都不应该挨饿。整个过程虽然辛苦,但能用自己写的代码真的为社会做点事情,帮助到需要帮助的人,这是IT工程师最大的荣幸。” 两年多来,阿里云MVP致力于用开放的心态,与非凡的伙伴,让天下没有难做的技术。我们举办了上百场 MVP TechShow技术布道,为在各行各业的MVPs提供丰富多彩的聚会和培训。 在这里,你既是技术先驱,又是意见领袖;是领域探索家,更是行业布道者。与其他有趣的灵魂一起讨论前沿技术,探索技术创新,实现个人进阶。我们邀请你,让科技更具力量,用技术连接世界。 长按识别下方二维码,马上申请成为阿里云 MVP,一起同行! 原文发布时间为:2019-11-25作者: 阿里技术本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
双11不光是购物狂欢节,更是对技术的一次“大考”,对于阿里巴巴企业内部运营的基础保障技术而言,亦是如此。 回溯双11历史,这背后也经历过“小米加步枪”的阶段:作战室从随处是网线,交换机放地上的“一地狼藉”;到如今媲美5G的wifi网速,到现场却看不到一根网线;从当年使用商用AP(无线路由器),让光明顶双11当天断网一分钟,到全部使用阿里自研AP……阿里巴巴企业智能事业部工程师们提供的基础保障也在不断升级。 媲美5G的Wifi网速 现场却看不到一根网线 网络是办公基础。对于负责网络的企业智能工程师来说,虽然很多工作做在平时,但在双11期间的保障也丝毫不会松懈。 为了确保阿里园区、猫晚现场、媒体中心、盒马门店等多地网络的顺滑体验,今年双11,从机房环境、接入层、骨干层和广域网端到端的整体网络稳定性得到全面提升,实现整体网络可用性。在人员密度和并发流量均创历史新高的同时,稳定支撑双11作战室各业务。 作为阿里双11最高指挥部的“光明顶”采用自研AP进行分布式网络架构部署,现场Wifi网速可媲美5G,却看不到一根网线。工程师们在光明顶进行测速,用这里的网络下载标清版的20集《长安十二时辰》,只需要40秒。下电影更是秒秒钟的事情,大概也就是一个眨眼的瞬间吧。 40G骨干可同时支撑数万名小二的在线办公,对于突发的直播、视频连线等大流量业务支撑也毫无压力。同时在线路监控分析领域还首次引入主动状态分析模块,提前对风险高危的模块或线路做出预警。 阿里巴巴企业智能自研AP 语音切换大屏作战室装备持续升级 在光明顶作战室内有一块30平方的大屏,承载了2019双11数字经济体作战指挥大屏系统。这是阿里巴巴数字经济体作战组织的技术、产品、服务串联起来的“作战指挥图”。今年,语音控制大屏的能力,也首次正式投入到双11作战中。 不需再手动操作,只需通过天猫精灵、话筒等设备发出命令,即可实现大屏上不同数据纬度的切换,不同业务板块的展示,协同作战指挥台,随时查看各战队数据情况,下发指令。包括与全球不同地区作战室实现音视频连线,今年也可以通过语音来控制与切换,跟指挥部成员进行异地作战与沟通。 工程师正在通过天猫精灵切换数据大屏 管够!数千名商家媒体感受阿里IT服务 今年,来自华为、海尔等全球品牌的上千名代表组成的“商家千人团”以及全球媒体也入驻阿里巴巴,与阿里小二们并肩作战,为全球消费者服务。 为了满足商家媒体,对电脑、IT配件突发的借用、使用需求,IT工程师们首次在媒体/商家中心增设IT服务台与自助领用柜。IT小哥在服务台提供电脑清洁、电脑问题排查等服务,自助领用柜内则配备了笔记本电脑、鼠标、键盘等物品,可以随到随领,随领随用。 此外,还首次将5G信号搬到了阿里西溪园区现场作为网络保障。同时,视频连线、一键切屏、无线投屏等技术也让媒体和商家感受到了阿里的办公黑科技。 自研大盘再升级核心应用问题精准定位 双11期间如何在第一时间定位员工应用与系统故障,企业智能的工程师们自研数据大屏可对核心应用进行观测。今年从业务全景、业务风险预警、核心应用服务、基础网络、核心系统水位等五层进行全面体系化升级,建立分层、高效、精细化的指挥体系,预警覆盖双11核心应用的所有故障点,可先于故障,做出处理。 同时,工程师们还提前展开预案演练,围绕故障点,风险预警、预案、演练形成闭环,更加聚焦。确保企业智能在本次双11丝般顺滑,稳如磐石。 网络信号无死角猫晚现场首次网络实时观测 在晚会活动等人多现场,观众最担心的就是“网不好”。今年双11猫晚现场,阿里IT工程师首次联合移动、联通、电信三大运营商,对猫晚晚会现场的4G网络容量、性能、用户体验分区进行精准的可视化动态合屏展示。晚会现场网络支持超1.7万台的手机,并发11000+,4G总流量超3T。三大运营商的基站遥测和现场实际客户端拨测的上下行速率均超过10M,现场用户4G体感十分流畅,全程上网互动无卡顿,远超同类大型晚会。 同时,猫晚现场的核心网络采用自用+自研网络设备,对核心业务、优酷、手淘业务采用3线并发+4G灾备的策略,在线路成本下降原来一半以上,同时保证网络可用性没有任何降低。在核心机房、优酷推流、手淘直播区域独立部站,4G下载速率达到百兆。 除了基础保障外,为了让小二们双11期间的工作更加便利,阿里巴巴企业智能事业部还连同行政部,为员工提供多样智能服务。 智能服务员工一键求助、一键领取物资…… “码上行政”可自动定位并推荐双11高频问题,为员工提供一键求助;“驿台驿阁”可一键领取备战物资和寄存;此外,“阿里访客小程序”提供的刷脸、身份证、二维码等多种入园验证方式,保障双11通行更便捷更安心;“活动平台”则可以根据双11作战室排班信息自动同步开通人脸门禁权限,保障安全。 大数据带来舒适园区环境体验 此外,通过人、物、场大数据计算并决策园区空调、新风运行策略,保证小二们在舒适环境下迎战双11。在作战室中部署了环境传感器,可随时监测温湿度、CO2异常。 而诸如园区安全、车辆通行、行政服务、环境能耗等情况,也通过行政数据指挥中心,清晰准确展示园区运行状况,全方位保障双11园区。 内外小蜜48小时主动贴身服务 阿里员工的智能办公助理“内外小蜜”,在双11期间为全国小二们,提供了包括衣食住行与IT支持相关的完整后勤保障信息超过12000服务人次,方便小二们随时查看。 同时在10号、11号两天,针对不同园区的参战同学在三餐、加油站、回家、休息区等高频服务需求场景下提供贴身主动服务,及时精准地传递后勤保障信息。 线上线下多渠道直播触达数万小二 今年双11,通过阿里内部的内外直播,超4万名阿里小二在线观看了猫晚及双11相关直播,主播与小二在线弹幕实时互动,边看直播边加购物车。而在线下通过视联网直播技术,天猫晚会被送达到全球阿里园区的电视机,小二们可以在各个园区的电梯厅、食堂、工位观看猫晚。 此外,阿里小二们还通过内部平台“阿里内外”通过线上KickOff,首次创新引入“双11心愿锦囊”活动,用趣味许愿卡给身边的小伙伴们助力打气,社交化提升组织文化运营。通过内外消息图文滚动播报,实时了解双11前线业务资讯解读,即使大家分隔多地,也能一起感受到双11浓浓氛围。 阿里小二参与双11打卡许愿活动 随着双11不断刷新的成交记录,背后的新消费的加速与升级。而创新技术也在新零售等新业务形态下发挥重要作用。 彩屏电子价签首亮相大幅提升拣货效率 部署于全国盒马门店的上百万个由阿里巴巴企业智能事业部自研的电子价签,通过秒级变价的能力,为盒马门店双11促销活动保驾护航。 为应对线上线下大量订单的激增和配送效率问题,今年双11,新款彩屏价签也参加到了电子价签大家庭,同时带来“拣货辅助”和“活动提醒”功能。 在有拣货任务时,拣货员只需远程按下“遥控器”,待拣货商品对应的电子价签就会亮起不同的颜色,引导拣货员快速找到目标,大大提升拣货效率。 原来一个新人拣货员要通过一个月的培训,拣货速度才能比上一个熟手,但现在一周就够了。 现在,双11已经成为一项举世瞩目的“奇迹工程”,这背后是技术和技术人十年如一日地不知疲倦地向前奔跑。对于,阿里巴巴企业智能事业部的工程师们来说,每一年双11的技术创新不仅是支撑自身继续向前发展的重要保障,更是对“稳定”的极致要求。 原文发布时间为:2019-11-25作者: 企业智能事业部本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:肉眼看计算机是由CPU、内存、显示器这些硬件设备组成,但大部分人从事的是软件开发工作。计算机底层原理就是连通硬件和软件的桥梁,理解计算机底层原理才能在程序设计这条路上越走越快,越走越轻松。从操作系统层面去理解高级编程语言的执行过程,会发现好多软件设计都是同一种套路,很多语言特性都依赖于底层机制,今天董鹏为你一一揭秘。 结合 CPU 理解一行 Java 代码是怎么执行的 根据冯·诺依曼思想,计算机采用二进制作为数制基础,必须包含:运算器、控制器、存储设备,以及输入输出设备,如下图所示。 enter image description here图片来源:http://m.elecfans.com/article/625138.html 我们先来分析 CPU 的工作原理,现代 CPU 芯片中大都集成了,控制单元,运算单元,存储单元。控制单元是 CPU 的控制中心, CPU 需要通过它才知道下一步做什么,也就是执行什么指令,控制单元又包含:指令寄存器(IR ),指令译码器( ID )和操作控制器( OC )。 当程序被加载进内存后,指令就在内存中了,这个时候说的内存是独立于 CPU 外的主存设备,也就是 PC 机中的内存条,指令指针寄存器IP 指向内存中下一条待执行指令的地址,控制单元根据 IP寄存器的指向,将主存中的指令装载到指令寄存器。 这个指令寄存器也是一个存储设备,不过他集成在 CPU 内部,指令从主存到达 CPU 后只是一串 010101 的二进制串,还需要通过译码器解码,分析出操作码是什么,操作数在哪,之后就是具体的运算单元进行算术运算(加减乘除),逻辑运算(比较,位移)。而 CPU 指令执行过程大致为:取址(去主存获取指令放到寄存器),译码(从主存获取操作数放入高速缓存 L1 ),执行(运算)。 enter image description here 这里解释下上图中 CPU 内部集成的存储单元 SRAM ,正好和主存中的 DRAM 对应, RAM 是随机访问内存,就是给一个地址就能访问到数据,而磁盘这种存储媒介必须顺序访问,而 RAM 又分为动态和静态两种,静态 RAM 由于集成度较低,一般容量小,速度快,而动态 RAM 集成度较高,主要通过给电容充电和放电实现,速度没有静态 RAM 快,所以一般将动态 RAM 做为主存,而静态 RAM 作为 CPU 和主存之间的高速缓存 (cache),用来屏蔽 CPU 和主存速度上的差异,也就是我们经常看到的 L1 , L2 缓存。每一级别缓存速度变低,容量变大。 下图展示了存储器的层次化架构,以及 CPU 访问主存的过程,这里有两个知识点,一个是多级缓存之间为保证数据的一致性,而推出的缓存一致性协议,具体可以参考这篇文章,另外一个知识点是, cache 和主存的映射,首先要明确的是 cahce 缓存的单位是缓存行,对应主存中的一个内存块,并不是一个变量,这个主要是因为 CPU 访问的空间局限性:被访问的某个存储单元,在一个较短时间内,很有可能再次被访问到,以及空间局限性:被访问的某个存储单元,在较短时间内,他的相邻存储单元也会被访问到。 而映射方式有很多种,类似于 cache 行号 = 主存块号 mod cache总行数 ,这样每次获取到一个主存地址,根据这个地址计算出在主存中的块号就可以计算出在 cache 中的行号。 enter image description here 下面我们接着聊 CPU 的指令执行。取址、译码、执行,这是一个指令的执行过程,所有指令都会严格按照这个顺序执行。但是多个指令之间其实是可以并行的,对于单核 CPU 来说,同一时刻只能有一条指令能够占有执行单元运行。这里说的执行是 CPU 指令处理 (取指,译码,执行) 三步骤中的第三步,也就是运算单元的计算任务。 所以为了提升 CPU 的指令处理速度,所以需要保证运算单元在执行前的准备工作都完成,这样运算单元就可以一直处于运算中,而刚刚的串行流程中,取指,解码的时候运算单元是空闲的,而且取指和解码如果没有命中高速缓存还需要从主存取,而主存的速度和 CPU 不在一个级别上,所以指令流水线 可以大大提高 CPU 的处理速度,下图是一个3级流水线的示例图,而现在的奔腾 CPU 都是32级流水线,具体做法就是将上面三个流程拆分的更细。 enter image description here 除了指令流水线, CPU 还有分支预测,乱序执行等优化速度的手段。好了,我们回到正题,一行 Java 代码是怎么执行的? 一行代码能够执行,必须要有可以执行的上下文环境,包括:指令寄存器、数据寄存器、栈空间等内存资源,然后这行代码必须作为一个执行流能够被操作系统的任务调度器识别,并给他分配 CPU 资源,当然这行代码所代表的指令必须是 CPU 可以解码识别的,所以一行 Java 代码必须被解释成对应的 CPU 指令才能执行。下面我们看下System.out.println("Hello world")这行代码的转译过程。 Java 是一门高级语言,这类语言不能直接运行在硬件上,必须运行在能够识别 Java 语言特性的虚拟机上,而 Java 代码必须通过 Java 编译器将其转换成虚拟机所能识别的指令序列,也称为 Java 字节码,之所以称为字节码是因为 Java 字节码的操作指令(OpCode)被固定为一个字节,以下为 System.out.println("Hello world") 编译后的字节码: 0x00: b2 00 02 getstatic Java .lang.System.out 0x03: 12 03 ldc "Hello, World!" 0x05: b6 00 04 invokevirtual Java .io.PrintStream.println 0x08: b1 return 最左列是偏移;中间列是给虚拟机读的字节码;最右列是高级语言的代码,下面是通过汇编语言转换成的机器指令,中间是机器码,第三列为对应的机器指令,最后一列是对应的汇编代码: 0x00: 55 push rbp 0x01: 48 89 e5 mov rbp,rsp 0x04: 48 83 ec 10 sub rsp,0x10 0x08: 48 8d 3d 3b 00 00 00 lea rdi,[rip+0x3b] ; 加载 "Hello, World!\n" 0x0f: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0 0x16: b0 00 mov al,0x0 0x18: e8 0d 00 00 00 call 0x12 ; 调用 printf 方法 0x1d: 31 c9 xor ecx,ecx 0x1f: 89 45 f8 mov DWORD PTR [rbp-0x8],eax 0x22: 89 c8 mov eax,ecx 0x24: 48 83 c4 10 add rsp,0x10 0x28: 5d pop rbp 0x29: c3 ret JVM 通过类加载器加载 class 文件里的字节码后,会通过解释器解释成汇编指令,最终再转译成 CPU 可以识别的机器指令,解释器是软件来实现的,主要是为了实现同一份 Java 字节码可以在不同的硬件平台上运行,而将汇编指令转换成机器指令由硬件直接实现,这一步速度是很快的,当然 JVM 为了提高运行效率也可以将某些热点代码(一个方法内的代码)一次全部编译成机器指令后然后在执行,也就是和解释执行对应的即时编译(JIT), JVM 启动的时候可以通过 -Xint 和 -Xcomp 来控制执行模式。 从软件层面上, class 文件被加载进虚拟机后,类信息会存放在方法区,在实际运行的时候会执行方法区中的代码,在 JVM 中所有的线程共享堆内存和方法区,而每个线程有自己独立的 Java 方法栈,本地方法栈(面向 native 方法),PC寄存器(存放线程执行位置),当调用一个方法的时候, Java 虚拟机会在当前线程对应的方法栈中压入一个栈帧,用来存放 Java 字节码操作数以及局部变量,这个方法执行完会弹出栈帧,一个线程会连续执行多个方法,对应不同的栈帧的压入和弹出,压入栈帧后就是 JVM 解释执行的过程了。 enter image description here 中断 刚刚说到, CPU 只要一上电就像一个永动机, 不停的取指令,运算,周而复始,而中断便是操作系统的灵魂,故名思议,中断就是打断 CPU 的执行过程,转而去做点别的。 例如系统执行期间发生了致命错误,需要结束执行,例如用户程序调用了一个系统调用的方法,例如mmp等,就会通过中断让 CPU 切换上下文,转到内核空间,例如一个等待用户输入的程序正在阻塞,而当用户通过键盘完成输入,内核数据已经准备好后,就会发一个中断信号,唤醒用户程序把数据从内核取走,不然内核可能会数据溢出,当磁盘报了一个致命异常,也会通过中断通知 CPU ,定时器完成时钟滴答也会发时钟中断通知 CPU 。 中断的种类,我们这里就不做细分了,中断有点类似于我们经常说的事件驱动编程,而这个事件通知机制是怎么实现的呢,硬件中断的实现通过一个导线和 CPU 相连来传输中断信号,软件上会有特定的指令,例如执行系统调用创建线程的指令,而 CPU 每执行完一个指令,就会检查中断寄存器中是否有中断,如果有就取出然后执行该中断对应的处理程序。 陷入内核 : 我们在设计软件的时候,会考虑程序上下文切换的频率,频率太高肯定会影响程序执行性能,而陷入内核是针对 CPU 而言的, CPU 的执行从用户态转向内核态,以前是用户程序在使用 CPU ,现在是内核程序在使用 CPU ,这种切换是通过系统调用产生的。 系统调用是执行操作系统底层的程序,Linux的设计者,为了保护操作系统,将进程的执行状态用内核态和用户态分开,同一个进程中,内核和用户共享同一个地址空间,一般 4G 的虚拟地址,其中 1G 给内核态, 3G 给用户态。在程序设计的时候我们要尽量减少用户态到内核态的切换,例如创建线程是一个系统调用,所以我们有了线程池的实现。 从 Linux 内存管理角度理解 JVM 内存模型 进程上下文 我们可以将程序理解为一段可执行的指令集合,而这个程序启动后,操作系统就会为他分配 CPU ,内存等资源,而这个正在运行的程序就是我们说的进程,进程是操作系统对处理器中运行的程序的一种抽象。 而为进程分配的内存以及 CPU 资源就是这个进程的上下文,保存了当前执行的指令,以及变量值,而 JVM 启动后也是linux上的一个普通进程,进程的物理实体和支持进程运行的环境合称为上下文,而上下文切换就是将当前正在运行的进程换下,换一个新的进程到处理器运行,以此来让多个进程并发的执行,上下文切换可能来自操作系统调度,也有可能来自程序内部,例如读取IO的时候,会让用户代码和操作系统代码之间进行切换。 enter image description here 虚拟存储 当我们同时启动多个 JVM 执行:System.out.println(new Object()); 将会打印这个对象的 hashcode ,hashcode 默认为内存地址,最后发现他们打印的都是 Java .lang.Object@4fca772d ,也就是多个进程返回的内存地址竟然是一样的。 通过上面的例子我们可以证明,linux中每个进程有单独的地址空间,在此之前,我们先了解下 CPU 是如何访问内存的? 假设我们现在还没有虚拟地址,只有物理地址,编译器在编译程序的时候,需要将高级语言转换成机器指令,那么 CPU 访问内存的时候必须指定一个地址,这个地址如果是一个绝对的物理地址,那么程序就必须放在内存中的一个固定的地方,而且这个地址需要在编译的时候就要确认,大家应该想到这样有多坑了吧。 如果我要同时运行两个 office word 程序,那么他们将操作同一块内存,那就乱套了,伟大的计算机前辈设计出,让 CPU 采用 段基址 + 段内偏移地址 的方式访问内存,其中段基地址在程序启动的时候确认,尽管这个段基地址还是绝对的物理地址,但终究可以同时运行多个程序了, CPU 采用这种方式访问内存,就需要段基址寄存器和段内偏移地址寄存器来存储地址,最终将两个地址相加送上地址总线。 而内存分段,相当于每个进程都会分配一个内存段,而且这个内存段需要是一块连续的空间,主存里维护着多个内存段,当某个进程需要更多内存,并且超出物理内存的时候,就需要将某个不常用的内存段换到硬盘上,等有充足内存的时候在从硬盘加载进来,也就是 swap 。每次交换都需要操作整个段的数据。 首先连续的地址空间是很宝贵的,例如一个 50M 的内存,在内存段之间有空隙的情况下,将无法支持 5 个需要 10M 内存才能运行的程序,如何才能让段内地址不连续呢? 答案是内存分页。 在保护模式下,每一个进程都有自己独立的地址空间,所以段基地址是固定的,只需要给出段内偏移地址就可以了,而这个偏移地址称为线性地址,线性地址是连续的,而内存分页将连续的线性地址和和分页后的物理地址相关联,这样逻辑上的连续线性地址可以对应不连续的物理地址。 物理地址空间可以被多个进程共享,而这个映射关系将通过页表( page table)进行维护。 标准页的尺寸一般为 4KB ,分页后,物理内存被分成若干个 4KB 的数据页,进程申请内存的时候,可以映射为多个 4KB 大小的物理内存,而应用程序读取数据的时候会以页为最小单位,当需要和硬盘发生交换的时候也是以页为单位。 现代计算机多采用虚拟存储技术,虚拟存储让每个进程以为自己独占整个内存空间,其实这个虚拟空间是主存和磁盘的抽象,这样的好处是,每个进程拥有一致的虚拟地址空间,简化了内存管理,进程不需要和其他进程竞争内存空间。 因为他是独占的,也保护了各自进程不被其他进程破坏,另外,他把主存看成磁盘的一个缓存,主存中仅保存活动的程序段和数据段,当主存中不存在数据的时候发生缺页中断,然后从磁盘加载进来,当物理内存不足的时候会发生 swap 到磁盘。页表保存了虚拟地址和物理地址的映射,页表是一个数组,每个元素为一个页的映射关系,这个映射关系可能是和主存地址,也可能和磁盘,页表存储在主存,我们将存储在高速缓冲区 cache 中的页表称为快表 TLAB 。 enter image description here 装入位 表示对于页是否在主存,如果地址页每页表示,数据还在磁盘 存放位置 建立虚拟页和物理页的映射,用于地址转换,如果为null表示是一个未分配页 修改位 用来存储数据是否修改过 权限位 用来控制是否有读写权限 禁止缓存位 主要用来保证 cache 主存 磁盘的数据一致性 内存映射 正常情况下,我们读取文件的流程为,先通过系统调用从磁盘读取数据,存入操作系统的内核缓冲区,然后在从内核缓冲区拷贝到用户空间,而内存映射,是将磁盘文件直接映射到用户的虚拟存储空间中,通过页表维护虚拟地址到磁盘的映射,通过内存映射的方式读取文件的好处有,因为减少了从内核缓冲区到用户空间的拷贝,直接从磁盘读取数据到内存,减少了系统调用的开销,对用户而言,仿佛直接操作的磁盘上的文件,另外由于使用了虚拟存储,所以不需要连续的主存空间来存储数据。 enter image description here 在 Java 中,我们使用 MappedByteBuffer 来实现内存映射,这是一个堆外内存,在映射完之后,并没有立即占有物理内存,而是访问数据页的时候,先查页表,发现还没加载,发起缺页异常,然后在从磁盘将数据加载进内存,所以一些对实时性要求很高的中间件,例如rocketmq,消息存储在一个大小为1G的文件中,为了加快读写速度,会将这个文件映射到内存后,在每个页写一比特数据,这样就可以把整个1G文件都加载进内存,在实际读写的时候就不会发生缺页了,这个在rocketmq内部叫做文件预热。 下面我们贴一段 rocketmq 消息存储模块的代码,位于 MappedFile 类中,这个类是 rocketMq 消息存储的核心类感兴趣的可以自行研究,下面两个方法一个是创建文件映射,一个是预热文件,每预热 1000 个数据页,就让出 CPU 权限。 private void init(final String fileName, final int fileSize) throws IOException { this.fileName = fileName; this.fileSize = fileSize; this.file = new File(fileName); this.fileFromOffset = Long.parseLong(this.file.getName()); boolean ok = false; ensureDirOK(this.file.getParent()); try { this.fileChannel = new RandomAccessFile(this.file, "rw").getChannel(); this.mappedByteBuffer = this.fileChannel.map(MapMode.READ_WRITE, 0, fileSize); TOTAL_MAPPED_VIRTUAL_MEMORY.addAndGet(fileSize); TOTAL_MAPPED_FILES.incrementAndGet(); ok = true; } catch (FileNotFoundException e) { log.error("create file channel " + this.fileName + " Failed. ", e); throw e; } catch (IOException e) { log.error("map file " + this.fileName + " Failed. ", e); throw e; } finally { if (!ok && this.fileChannel != null) { this.fileChannel.close(); } } } //文件预热,OS_PAGE_SIZE = 4kb 相当于每 4kb 就写一个 byte 0 ,将所有的页都加载到内存,真正使用的时候就不会发生缺页异常了 public void warmMappedFile(FlushDiskType type, int pages) { long beginTime = System.currentTimeMillis(); ByteBuffer byteBuffer = this.mappedByteBuffer.slice(); int flush = 0; long time = System.currentTimeMillis(); for (int i = 0, j = 0; i < this.fileSize; i += MappedFile.OS_PAGE_SIZE, j++) { byteBuffer.put(i, (byte) 0); // force flush when flush disk type is sync if (type == FlushDiskType.SYNC_FLUSH) { if ((i / OS_PAGE_SIZE) - (flush / OS_PAGE_SIZE) >= pages) { flush = i; mappedByteBuffer.force(); } } // prevent gc if (j % 1000 == 0) { log.info("j={}, costTime={}", j, System.currentTimeMillis() - time); time = System.currentTimeMillis(); try { // 这里sleep(0),让线程让出 CPU 权限,供其他更高优先级的线程执行,此线程从运行中转换为就绪 Thread.sleep(0); } catch (InterruptedException e) { log.error("Interrupted", e); } } } // force flush when prepare load finished if (type == FlushDiskType.SYNC_FLUSH) { log.info("mapped file warm-up done, force to disk, mappedFile={}, costTime={}", this.getFileName(), System.currentTimeMillis() - beginTime); mappedByteBuffer.force(); } log.info("mapped file warm-up done. mappedFile={}, costTime={}", this.getFileName(), System.currentTimeMillis() - beginTime); this.mlock(); } JVM 中对象的内存布局 在linux中只要知道一个变量的起始地址就可以读出这个变量的值,因为从这个起始地址起前8位记录了变量的大小,也就是可以定位到结束地址,在 Java 中我们可以通过 Field.get(object) 的方式获取变量的值,也就是反射,最终是通过 UnSafe 类来实现的。我们可以分析下具体代码。 Field 对象的 getInt方法 先安全检查 ,然后调用 FieldAccessor @CallerSensitive public int getInt(Object obj) throws IllegalArgumentException, IllegalAccessException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } return getFieldAccessor(obj).getInt(obj); } 获取field在所在对象中的地址的偏移量 fieldoffset UnsafeFieldAccessorImpl(Field var1) { this.field = var1; if(Modifier.isStatic(var1.getModifiers())) { this.fieldOffset = unsafe.staticFieldOffset(var1); } else { this.fieldOffset = unsafe.objectFieldOffset(var1); } this.isFinal = Modifier.isFinal(var1.getModifiers()); } UnsafeStaticIntegerFieldAccessorImpl 调用unsafe中的方法 public int getInt(Object var1) throws IllegalArgumentException { return unsafe.getInt(this.base, this.fieldOffset); } 通过上面的代码我们可以通过属性相对对象起始地址的偏移量,来读取和写入属性的值,这也是 Java 反射的原理,这种模式在jdk中很多场景都有用到,例如LockSupport.park中设置阻塞对象。 那么属性的偏移量具体根据什么规则来确定的呢? 下面我们借此机会分析下 Java 对象的内存布局。 在 Java 虚拟机中,每个 Java 对象都有一个对象头 (object header) ,由标记字段和类型指针构成,标记字段用来存储对象的哈希码, GC 信息, 持有的锁信息,而类型指针指向该对象的类 Class ,在 64 位操作系统中,标记字段占有 64 位,而类型指针也占 64 位,也就是说一个 Java 对象在什么属性都没有的情况下要占有 16 字节的空间,当前 JVM 中默认开启了压缩指针,这样类型指针可以只占 32 位,所以对象头占 12 字节, 压缩指针可以作用于对象头,以及引用类型的字段。 JVM 为了内存对齐,会对字段进行重排序,这里的对齐主要指 Java 虚拟机堆中的对象的起始地址为 8 的倍数,如果一个对象用不到 8N 个字节,那么剩下的就会被填充,另外子类继承的属性的偏移量和父类一致,以 Long 为例,他只有一个非 static 属性 value ,而尽管对象头只占有 12 字节,而属性 value 的偏移量只能是 16, 其中 4 字节只能浪费掉,所以字段重排就是为了避免内存浪费, 所以我们很难在 Java 字节码被加载之前分析出这个 Java 对象占有的实际空间有多大,我们只能通过递归父类的所有属性来预估对象大小,而真实占用的大小可以通过 Java agent 中的 Instrumentation获取。 当然内存对齐另外一个原因是为了让字段只出现在同一个 CPU 的缓存行中,如果字段不对齐,就有可能出现一个字段的一部分在缓存行 1 中,而剩下的一半在 缓存行 2 中,这样该字段的读取需要替换两个缓存行,而字段的写入会导致两个缓存行上缓存的其他数据都无效,这样会影响程序性能。 通过内存对齐可以避免一个字段同时存在两个缓存行里的情况,但还是无法完全规避缓存伪共享的问题,也就是一个缓存行中存了多个变量,而这几个变量在多核 CPU 并行的时候,会导致竞争缓存行的写权限,当其中一个 CPU 写入数据后,这个字段对应的缓存行将失效,导致这个缓存行的其他字段也失效。 enter image description here 在 Disruptor 中,通过填充几个无意义的字段,让对象的大小刚好在 64 字节,一个缓存行的大小为64字节,这样这个缓存行就只会给这一个变量使用,从而避免缓存行伪共享,但是在 jdk7 中,由于无效字段被清除导致该方法失效,只能通过继承父类字段来避免填充字段被优化,而 jdk8 提供了注解@Contended 来标示这个变量或对象将独享一个缓存行,使用这个注解必须在 JVM 启动的时候加上 -XX:-RestrictContended 参数,其实也是用空间换取时间。 jdk6 --- 32 位系统下 public final static class VolatileLong { public volatile long value = 0L; public long p1, p2, p3, p4, p5, p6; // 填充字段 } jdk7 通过继承 public class VolatileLongPadding { public volatile long p1, p2, p3, p4, p5, p6; // 填充字段 } public class VolatileLong extends VolatileLongPadding { public volatile long value = 0L; } jdk8 通过注解 @Contended public class VolatileLong { public volatile long value = 0L; } NPTL和 Java 的线程模型 按照教科书的定义,进程是资源管理的最小单位,而线程是 CPU 调度执行的最小单位,线程的出现是为了减少进程的上下文切换(线程的上下文切换比进程小很多),以及更好适配多核心 CPU 环境,例如一个进程下多个线程可以分别在不同的 CPU 上执行,而多线程的支持,既可以放在Linux内核实现,也可以在核外实现,如果放在核外,只需要完成运行栈的切换,调度开销小,但是这种方式无法适应多 CPU 环境,底层的进程还是运行在一个 CPU 上,另外由于对用户编程要求高,所以目前主流的操作系统都是在内核支持线程,而在Linux中,线程是一个轻量级进程,只是优化了线程调度的开销。 而在 JVM 中的线程和内核线程是一一对应的,线程的调度完全交给了内核,当调用Thread.run 的时候,就会通过系统调用 fork() 创建一个内核线程,这个方法会在用户态和内核态之间进行切换,性能没有在用户态实现线程高,当然由于直接使用内核线程,所以能够创建的最大线程数也受内核控制。目前 Linux上 的线程模型为 NPTL ( Native POSIX Thread Library),他使用一对一模式,兼容 POSIX 标准,没有使用管理线程,可以更好地在多核 CPU 上运行。 线程的状态 对进程而言,就三种状态,就绪,运行,阻塞,而在 JVM 中,阻塞有四种类型,我们可以通过 jstack 生成 dump 文件查看线程的状态。 BLOCKED (on object monitor) 通过 synchronized(obj) 同步块获取锁的时候,等待其他线程释放对象锁,dump 文件会显示 waiting to lock <0x00000000e1c9f108> TIMED WAITING (on object monitor) 和 WAITING (on object monitor) 在获取锁后,调用了 object.wait() 等待其他线程调用 object.notify(),两者区别是是否带超时时间 TIMED WAITING (sleeping) 程序调用了 thread.sleep(),这里如果 sleep(0) 不会进入阻塞状态,会直接从运行转换为就绪 TIMED WAITING (parking) 和 WAITING (parking) 程序调用了 Unsafe.park(),线程被挂起,等待某个条件发生,waiting on condition 而在 POSIX 标准中,thread_block 接受一个参数 stat ,这个参数也有三种类型,TASK_BLOCKED, TASK_WAITING, TASK_HANGING,而调度器只会对线程状态为 READY 的线程执行调度,另外一点是线程的阻塞是线程自己操作的,相当于是线程主动让出 CPU 时间片,所以等线程被唤醒后,他的剩余时间片不会变,该线程只能在剩下的时间片运行,如果该时间片到期后线程还没结束,该线程状态会由 RUNNING 转换为 READY ,等待调度器的下一次调度。 好了,关于线程就分析到这,关于 Java 并发包,核心都在 AQS 里,底层是通过 UnSafe类的 cas 方法,以及 park 方法实现,后面我们在找时间单独分析,现在我们在看看 Linux 的进程同步方案。 POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准。 CAS 操作需要 CPU 支持,将比较 和 交换 作为一条指令来执行, CAS 一般有三个参数,内存位置,预期原值,新值 ,所以UnSafe 类中的 compareAndSwap 用属性相对对象初始地址的偏移量,来定位内存位置。 线程的同步 线程同步出现的根本原因是访问公共资源需要多个操作,而这多个操作的执行过程不具备原子性,被任务调度器分开了,而其他线程会破坏共享资源,所以需要在临界区做线程的同步,这里我们先明确一个概念,就是临界区,他是指多个任务访问共享资源如内存或文件时候的指令,他是指令并不是受访问的资源。 POSIX 定义了五种同步对象,互斥锁,条件变量,自旋锁,读写锁,信号量,这些对象在 JVM 中也都有对应的实现,并没有全部使用 POSIX 定义的 api,通过 Java 实现灵活性更高,也避免了调用native方法的性能开销,当然底层最终都依赖于 pthread 的 互斥锁 mutex 来实现,这是一个系统调用,开销很大,所以 JVM 对锁做了自动升降级,基于AQS的实现以后在分析,这里主要说一下关键字 synchronized 。 当声明 synchronized 的代码块时,编译而成的字节码会包含一个 monitorenter 和 多个 monitorexit (多个退出路径,正常和异常情况),当执行 monitorenter 的时候会检查目标锁对象的计数器是否为0,如果为0则将锁对象的持有线程设置为自己,然后计数器加1,获取到锁,如果不为0则检查锁对象的持有线程是不是自己,如果是自己就将计数器加1获取锁,如果不是则阻塞等待,退出的时候计数器减1,当减为0的时候清楚锁对象的持有线程标记,可以看出 synchronized 是支持可重入的。 刚刚说到线程的阻塞是一个系统调用,开销大,所以 JVM 设计了自适应自旋锁,就是当没有获取到锁的时候, CPU 回进入自旋状态等待其他线程释放锁,自旋的时间主要看上次等待多长时间获取的锁,例如上次自旋5毫秒没有获取锁,这次就6毫秒,自旋会导致 CPU 空跑,另一个副作用就是不公平的锁机制,因为该线程自旋获取到锁,而其他正在阻塞的线程还在等待。除了自旋锁, JVM 还通过 CAS 实现了轻量级锁和偏向锁来分别针对多个线程在不同时间访问锁和锁仅会被一个线程使用的情况。后两种锁相当于并没有调用底层的信号量实现(通过信号量来控制线程A释放了锁例如调用了 wait(),而线程B就可以获取锁,这个只有内核才能实现,后面两种由于场景里没有竞争所以也就不需要通过底层信号量控制),只是自己在用户空间维护了锁的持有关系,所以更高效。 enter image description here 如上图所示,如果线程进入 monitorenter 会将自己放入该 objectmonitor 的 entryset 队列,然后阻塞,如果当前持有线程调用了 wait 方法,将会释放锁,然后将自己封装成 objectwaiter 放入 objectmonitor 的 waitset 队列,这时候 entryset 队列里的某个线程将会竞争到锁,并进入 active 状态,如果这个线程调用了 notify 方法,将会把 waitset 的第一个 objectwaiter 拿出来放入 entryset (这个时候根据策略可能会先自旋),当调用 notify 的那个线程执行 moniterexit 释放锁的时候, entryset 里的线程就开始竞争锁后进入 active 状态。 为了让应用程序免于数据竞争的干扰, Java 内存模型中定义了 happen-before 来描述两个操作的内存可见性,也就是 X 操作 happen-before 操作 Y , 那么 X 操作结果 对 Y 可见。 JVM 中针对 volatile 以及 锁 的实现有 happen-before 规则, JVM 底层通过插入内存屏障来限制编译器的重排序,以 volatile 为例,内存屏障将不允许 在 volatile 字段写操作之前的语句被重排序到写操作后面 , 也不允许读取 volatile 字段之后的语句被重排序带读取语句之前。插入内存屏障的指令,会根据指令类型不同有不同的效果,例如在 monitorexit 释放锁后会强制刷新缓存,而 volatile 对应的内存屏障会在每次写入后强制刷新到主存,并且由于 volatile 字段的特性,编译器无法将其分配到寄存器,所以每次都是从主存读取,所以 volatile 适用于读多写少得场景,最好只有个线程写多个线程读,如果频繁写入导致不停刷新缓存会影响性能。 关于应用程序中设置多少线程数合适的问题,我们一般的做法是设置 CPU 最大核心数 * 2 ,我们编码的时候可能不确定运行在什么样的硬件环境中,可以通过 Runtime.getRuntime().availableProcessors() 获取 CPU 核心。 但是具体设置多少线程数,主要和线程内运行的任务中的阻塞时间有关系,如果任务中全部是计算密集型,那么只需要设置 CPU 核心数的线程就可以达到 CPU 利用率最高,如果设置的太大,反而因为线程上下文切换影响性能,如果任务中有阻塞操作,而在阻塞的时间就可以让 CPU 去执行其他线程里的任务,我们可以通过 线程数量=内核数量 / (1 - 阻塞率)这个公式去计算最合适的线程数,阻塞率我们可以通过计算任务总的执行时间和阻塞的时间获得。 目前微服务架构下有大量的RPC调用,所以利用多线程可以大大提高执行效率,我们可以借助分布式链路监控来统计RPC调用所消耗的时间,而这部分时间就是任务中阻塞的时间,当然为了做到极致的效率最大,我们需要设置不同的值然后进行测试。 Java 中如何实现定时任务 定时器已经是现代软件中不可缺少的一部分,例如每隔5秒去查询一下状态,是否有新邮件,实现一个闹钟等, Java 中已经有现成的 api 供使用,但是如果你想设计更高效,更精准的定时器任务,就需要了解底层的硬件知识,比如实现一个分布式任务调度中间件,你可能要考虑到各个应用间时钟同步的问题。 Java 中我们要实现定时任务,有两种方式,一种通过 timer 类, 另外一种是 JUC 中的 ScheduledExecutorService ,不知道大家有没有好奇 JVM 是如何实现定时任务的,难道一直轮询时间,看是否时间到了,如果到了就调用对应的处理任务,但是这种一直轮询不释放 CPU 肯定是不可取的,要么就是线程阻塞,等到时间到了在来唤醒线程,那么 JVM 怎么知道时间到了,如何唤醒呢? 首先我们翻一下 JDK ,发现和时间相关的 API 大概有3处,而且这 3 处还都对时间的精度做了区分: object.wait(long millisecond) 参数是毫秒,必须大于等于 0 ,如果等于 0 ,就一直阻塞直到其他线程来唤醒 ,timer 类就是通过 wait() 方法来实现,下面我们看一下wait的另外一个方法: public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; } wait(timeout); } 这个方法是想提供一个可以支持纳秒级的超时时间,然而只是粗暴的加 1 毫秒。 Thread.sleep(long millisecond) 目前一般通过这种方式释放 CPU ,如果参数为 0 ,表示释放 CPU 给更高优先级的线程,自己从运行状态转换为可运行态等待 CPU 调度,他也提供了一个可以支持纳秒级的方法实现,跟 wait 额区别是它通过 500000 来分隔是否要加 1 毫秒。 public static void sleep(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } sleep(millis); } LockSupport.park(long nans) Condition.await()调用的该方法, ScheduledExecutorService 用的 condition.await() 来实现阻塞一定的超时时间,其他带超时参数的方法也都通过他来实现,目前大多定时器都是通过这个方法来实现的,该方法也提供了一个布尔值来确定时间的精度。 System.currentTimeMillis() 以及 System.nanoTime() 这两种方式都依赖于底层操作系统,前者是毫秒级,经测试 windows 平台的频率可能超过 10ms ,而后者是纳秒级别,频率在 100ns 左右,所以如果要获取更精准的时间建议用后者好了,api 了解完了,我们来看下定时器的底层是怎么实现的,现代PC机中有三种硬件时钟的实现,他们都是通过晶体振动产生的方波信号输入来完成时钟信号同步的。 实时时钟 RTC ,用于长时间存放系统时间的设备,即使关机也可以依靠主板中的电池继续计时。Linux 启动的时候会从 RTC 中读取时间和日期作为初始值,之后在运行期间通过其他计时器去维护系统时间。 可编程间隔定时器 PIT ,该计数器会有一个初始值,每过一个时钟周期,该初始值会减1,当该初始值被减到0时,就通过导线向 CPU 发送一个时钟中断, CPU 就可以执行对应的中断程序,也就是回调对应的任务 时间戳计数器 TSC , 所有的 Intel8086 CPU 中都包含一个时间戳计数器对应的寄存器,该寄存器的值会在每次 CPU 收到一个时钟周期的中断信号后就会加 1 。他比 PIT 精度高,但是不能编程,只能读取。 时钟周期:硬件计时器在多长时间内产生时钟脉冲,而时钟周期频率为1秒内产生时钟脉冲的个数。目前通常为1193180。 时钟滴答:当PIT中的初始值减到0的时候,就会产生一次时钟中断,这个初始值由编程的时候指定。 Linux启动的时候,先通过 RTC 获取初始时间,之后内核通过 PIT 中的定时器的时钟滴答来维护日期,并且会定时将该日期写入 RTC,而应用程序的定时器主要是通过设置 PIT 的初始值设置的,当初始值减到0的时候,就表示要执行回调函数了,这里大家会不会有疑问,这样同一时刻只能有一个定时器程序了,而我们在应用程序中,以及多个应用程序之间,肯定有好多定时器任务,其实我们可以参考 ScheduledExecutorService 的实现。 只需要将这些定时任务按照时间做一个排序,越靠前待执行的任务放在前面,第一个任务到了在设置第二个任务相对当前时间的值,毕竟 CPU 同一时刻也只能运行一个任务,关于时间的精度问题,我们无法在软件层面做的完全精准,毕竟 CPU 的调度不完全受用户程序控制,当然更大的依赖是硬件的时钟周期频率,目前 TSC 可以提高更高的精度。 现在我们知道了,Java 中的超时时间,是通过可编程间隔定时器设置一个初始值然后等待中断信号实现的,精度上受硬件时钟周期的影响,一般为毫秒级别,毕竟1纳秒光速也只有3米,所以 JDK 中带纳秒参数的实现都是粗暴做法,预留着等待精度更高的定时器出现,而获取当前时间 System.currentTimeMillis() 效率会更高,但他是毫秒级精度,他读取的 Linux 内核维护的日期,而 System.nanoTime() 会优先使用 TSC ,性能稍微低一点,但他是纳秒级,Random 类为了防止冲突就用nanoTime生成种子。 Java 如何和外部设备通信 计算机的外部设备有鼠标、键盘、打印机、网卡等,通常我们将外部设备和和主存之间的信息传递称为 I/O 操作 , 按操作特性可以分为,输出型设备,输入型设备,存储设备。现代设备都采用通道方式和主存进行交互,通道是一个专门用来处理IO任务的设备, CPU 在处理主程序时遇到I/O请求,启动指定通道上选址的设备,一旦启动成功,通道开始控制设备进行操作,而 CPU 可以继续执行其他任务,I/O 操作完成后,通道发出 I/O 操作结束的中断,处理器转而处理 IO 结束后的事件。其他处理 IO 的方式,例如轮询、中断、DMA,在性能上都不见通道,这里就不介绍了。当然 Java 程序和外部设备通信也是通过系统调用完成,这里也不在继续深入了。 原文发布时间为:2019-11-22作者: 董鹏本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:日常工作中,我们多少都会遇到应用的性能问题。在阿里面试中,性能优化也是常被问到的题目,用来考察是否有实际的线上问题处理经验。面对这类问题,阿里工程师齐光给出了详细流程。来阿里面试前,先看看这篇文章哦。 性能问题和Bug不同,后者的分析和解决思路更清晰,很多时候从应用日志(文中的应用指分布式服务下的单个节点)即可直接找到问题根源,而性能问题,其排查思路更为复杂一些。 对应用进行性能优化,是一个系统性的工程,对工程师的技术广度和技术深度都有所要求。一个简单的应用,它不仅包含了应用代码本身,还和容器(虚拟机)、操作系统、存储、网络、文件系统等紧密相关,线上应用一旦出现了性能问题,需要我们从多方面去考虑。 与此同时,除了一些低级的代码逻辑引发的性能问题外,很多性能问题隐藏的较深,排查起来会比较困难,需要我们对应用的各个子模块、应用所使用的框架和组件的原理有所了解,同时掌握一定的性能优化工具和经验。 本文总结了我们在进行性能优化时常用的一些工具及技巧,目的是希望通过一个全面的视角,去感知性能优化的整体脉络。本文主要分为下面三个部分: 第一部分会介绍性能优化的一些背景知识。 第二部分会介绍性能优化的通用流程以及常见的一些误区。 第三部分会从系统层和业务层的角度,介绍高效的性能问题定位工具和高频性能瓶颈点分布。 本文中提到的线程、堆、垃圾回收等名词,如无特别说明,指的是 Java 应用中的相关概念。 1.性能优化的背景 前面提到过,应用出现性能问题和应用存在缺陷是不一样的,后者大多数是由于代码的质量问题导致,会导致应用功能性的缺失或出现风险,一经发现,会被及时修复。而性能问题,可能是由多方面的因素共同作用的结果:代码质量一般、业务发展太快、应用架构设计不合理等,这些问题处理起来一般耗时较长、分析链路复杂,大家都不愿意干,因此可能会被一些临时性的补救手段所掩盖,如:系统水位高或者单机的线程池队列爆炸,那就集群扩容增加机器;内存占用高/高峰时段 OOM,那就重启分分钟解决...... 临时性的补救措施只是在给应用埋雷,同时也只能解决部分问题。譬如,在很多场景下,加机器也并不能解决应用的性能问题,如对时延比较敏感的一些应用必须把单机的性能优化到极致,与此同时,加机器这种方式也造成了资源的浪费,长期来看是得不偿失的。对应用进行合理的性能优化,可在应用稳定性、成本核算获得很大的收益。 上面我们阐述了进行性能优化的必要性。假设现在我们的应用已经有了性能问题(eg. CPU 水位比较高),准备开始进行优化工作了,在这个过程中,潜在的痛点会有哪些呢?下面列出一些较为常见的: 对性能优化的流程不是很清晰。初步定为一个疑似瓶颈点后,就兴高采烈地吭哧吭哧开始干,最终解决的问题其实只是一个浅层次的性能瓶颈,真实的问题的根源并未触达; 对性能瓶颈点的分析思路不是很清晰。CPU、网络、内存......这么多的性能指标,我到底该关注什么,应该从哪一块儿开始入手? 对性能优化的工具不了解。遇到问题后,不清楚该用哪个工具,不知道通过工具得到的指标代表什么。 2.性能优化的流程 在性能优化这个领域,并没有一个严格的流程定义,但是对于绝大多数的优化场景,我们可以将其过程抽象为下面四个步骤。 准备阶段:主要工作是是通过性能测试,了解应用的概况、瓶颈的大概方向,明确优化目标; 分析阶段:通过各种工具或手段,初步定位性能瓶颈点; 调优阶段:根据定位到的瓶颈点,进行应用性能调优; 测试阶段:让调优过的应用进行性能测试,与准备阶段的各项指标进行对比,观测其是否符合预期,如果瓶颈点没有消除或者性能指标不符合预期,则重复步骤2和3。 下图即为上述四个阶段的简要流程。 2.1 通用流程详解 在上述通用流程的四个步骤当中,步骤2和3我们会在接下来两个部分重点进行介绍。首先我们来看一下,在准备阶段和测试阶段,我们需要做一些什么。 | 2.1.1 准备阶段 准备阶段是非常关键的一步,不能省略。 首先,需要对我们进行调优的对象进行详尽的了解,所谓知己知彼,百战不殆。 对性能问题进行粗略评估,过滤一些因为低级的业务逻辑导致的性能问题。譬如,线上应用日志级别不合理,可能会在大流量时导致 CPU 和磁盘的负载飙高,这种情况调整日志级别即可; 了解应用的的总体架构,比如应用的外部依赖和核心接口有哪些,使用了哪些组件和框架,哪些接口、模块的使用率较高,上下游的数据链路是怎么样的等; 了解应用对应的服务器信息,如服务器所在的集群信息、服务器的 CPU/内存信息、安装的 Linux 版本信息、服务器是容器还是虚拟机、所在宿主机混部后是否对当前应用有干扰等; 其次,我们需要获取基准数据,然后结合基准数据和当前的一些业务指标,确定此次性能优化的最终目标。 使用基准测试工具获取系统细粒度指标。可以使用若干 Linux 基准测试工具(eg. jmeter、ab、loadrunnerwrk、wrk等),得到文件系统、磁盘 I/O、网络等的性能报告。除此之外,类似 GC、Web 服务器、网卡流量等信息,如有必要也是需要了解记录的; 通过压测工具或者压测平台(如果有的话),对应用进行压力测试,获取当前应用的宏观业务指标,譬如:响应时间、吞吐量、TPS、QPS、消费速率(对于有 MQ 的应用)等。压力测试也可以省略,可以结合当前的实际业务和过往的监控数据,去统计当前的一些核心业务指标,如午高峰的服务 TPS。 | 2.1.2 测试阶段 进入到这一阶段,说明我们已经初步确定了应用性能瓶颈的所在,而且已经进行初步的调优了。检测我们调优是否有效的方式,就是在仿真的条件下,对应用进行压力测试。注意:由于 Java 有 JIT(just-in-time compilation)过程,因此压力测试时可能需要进行前期预热。 如果压力测试的结果符合了预期的调优目标,或者与基准数据相比,有很大的改善,则我们可以继续通过工具定位下一个瓶颈点,否则,则需要暂时排除这个瓶颈点,继续寻找下一个变量。 2.2 注意事项 在进行性能优化时,了解下面这些注意事项可以让我们少走一些弯路。 性能瓶颈点通常呈现 2/8 分布,即80%的性能问题通常是由20%的性能瓶颈点导致的,2/8 原则也意味着并不是所有的性能问题都值得去优化; 性能优化是一个渐进、迭代的过程,需要逐步、动态地进行。记录基准后,每次改变一个变量,引入多个变量会给我们的观测、优化过程造成干扰; 不要过度追求应用的单机性能,如果单机表现良好,则应该从系统架构的角度去思考; 不要过度追求单一维度上的极致优化,如过度追求 CPU 的性能而忽略了内存方面的瓶颈; 选择合适的性能优化工具,可以使得性能优化取得事半功倍的效果; 整个应用的优化,应该与线上系统隔离,新的代码上线应该有降级方案。 3.瓶颈点分析工具箱 性能优化其实就是找出应用存在性能瓶颈点,然后设法通过一些调优手段去缓解。性能瓶颈点的定位是较困难的,快速、直接地定位到瓶颈点,需要具备下面两个条件: 恰到好处的工具; 一定的性能优化经验。 工欲善其事,必先利其器,我们该如何选择合适的工具呢?不同的优化场景下,又该选择那些工具呢? 首选,我们来看一下大名鼎鼎的「性能工具(Linux Performance Tools-full)图」,想必很多工程师都知道,它出自系统性能专家 Brendan Gregg。该图从 Linux 内核的各个子系统出发,列出了我们在对各个子系统进行性能分析时,可使用的工具,涵盖了监测、分析、调优等性能优化的方方面面。除了这张全景图之外,Brendan Gregg 还单独提供了基准测试工具(Linux Performance Benchmark Tools)图、性能监测工具(Linux Performance Observability Tools)图等,更详细的内容请参考 Brendan Gregg 的网站说明。 图片来源:http://www.brendangregg.com/linuxperf.html?spm=ata.13261165.0.0.34646b44KX9rGc 上面这张图非常经典,是我们做性能优化时非常好的参考资料,但事实上,我们在实际运用的时候,会发现可能它并不是最合适的,原因主要有下面两点: 1)对分析经验要求较高。上面这张图其实是从 Linux 系统资源的角度去观测性能指标的,这要求我们对 Linux 各个子系统的功能、原理要有所了解。举例:遇到性能问题了,我们不会拿每个子系统下的工具都去试一遍,大多数情况是:我们怀疑某个子系统有问题,然后根据这张图上列举的工具,去观测或者验证我们的猜想,这无疑拔高了对性能优化经验的要求; 2)适用性和完整性不是很好。我们在分析性能问题时,从系统底层自底向上地分析是较低效的,大多数时候,从应用层面去分析会更加有效。性能工具(Linux Performance Tools-full)图只是从系统层一个角度给出了工具集,如果从应用层开始分析,我们可以使用哪些工具?哪些点是我们首先需要关注的? 鉴于上面若干痛点,下面给出了一张更为实用的「性能优化工具图谱」,该图分别从系统层、应用层(含组件层)的角度出发,列举了我们在分析性能问题时首先需要关注的各项指标(其中?标注的是最需要关注的),这些点是最有可能出现性能瓶颈的地方。需要注意的是,一些低频的指标或工具,在图中并没有列出来,如 CPU 中断、索引节点使用、I/O事件跟踪等,这些低频点的排查思路较复杂,一般遇到的机会也不多,在这里我们聚焦最常见的一些就可以了。 对比上面的性能工具(Linux Performance Tools-full)图,下图的优势在于:把具体的工具同性能指标结合了起来,同时从不同的层次去描述了性能瓶颈点的分布,实用性和可操作性更强一些。系统层的工具分为CPU、内存、磁盘(含文件系统)、网络四个部分,工具集同性能工具(Linux Performance Tools-full)图中的工具基本一致。组件层和应用层中的工具构成为:JDK 提供的一些工具 + Trace 工具 + dump 分析工具 + Profiling 工具等。 这里就不具体介绍这些工具的具体用法了,我们可以使用 man 命令得到工具详尽的使用说明,除此之外,还有另外一个查询命令手册的方法:info。info 可以理解为 man 的详细版本,如果 man 的输出不太好理解,可以去参考 info 文档,命令太多,记不住也没必要记住。 上面这张图该如何使用? 首先,虽然从系统、组件、应用两个三个角度去描述瓶颈点的分布,但在实际运行时,这三者往往是相辅相成、相互影响的。系统是为应用提供了运行时环境,性能问题的本质就是系统资源达到了使用的上限,反映在应用层,就是应用/组件的各项指标开始下降;而应用/组件的不合理使用和设计,也会加速系统资源的耗尽。因此,分析瓶颈点时,需要我们结合从不同角度分析出的结果,抽出共性,得到最终的结论。 其次,建议先从应用层入手,分析图中标注的高频指标,抓出最重要的、最可疑的、最有可能导致性能的点,得到初步的结论后,再去系统层进行验证。这样做的好处是:很多性能瓶颈点体现在系统层,会是多变量呈现的,譬如,应用层的垃圾回收(GC)指标出现了异常,通过 JDK 自带的工具很容易观测到,但是体现在系统层上,会发现系统当前的 CPU 利用率、内存指标都不太正常,这就给我们的分析思路带来了困扰。 最后,如果瓶颈点在应用层和系统层均呈现出多变量分布,建议此时使用 ZProfiler、JProfiler 等工具对应用进行 Profiling,获取应用的综合性能信息(注:Profiling 指的是在应用运行时,通过事件(Event-based)、统计抽样(Sampling Statistical)或植入附加指令(Byte-Code instrumentation)等方法,收集应用运行时的信息,来研究应用行为的动态分析方法)。譬如,可以对 CPU 进行抽样统计,结合各种符号表信息,得到一段时间内应用内的代码热点。 下面介绍在不同的分析层次,我们需要关注的核心性能指标,同时,也会介绍如何初步根据这些指标,判断系统或应用是否存在性能瓶颈点,至于瓶颈点的确认、瓶颈点的成因、调优手段,将会在下一部分展开。 3.1 CPU&&线程 和 CPU 相关的指标主要有以下几个。常用的工具有 top、 ps、uptime、 vmstat、 pidstat等。 CPU利用率(CPU Utilization) CPU 平均负载(Load Average) 上下文切换次数(Context Switch) top - 12:20:57 up 25 days, 20:49, 2 users, load average: 0.93, 0.97, 0.79 Tasks: 51 total, 1 running, 50 sleeping, 0 stopped, 0 zombie%Cpu(s): 1.6 us, 1.8 sy, 0.0 ni, 89.1 id, 0.1 wa, 0.0 hi, 0.1 si, 7.3 stKiB Mem : 8388608 total, 476436 free, 5903224 used, 2008948 buff/cacheKiB Swap: 0 total, 0 free, 0 used. 0 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 119680 admin 20 0 600908 72332 5768 S 2.3 0.9 52:32.61 obproxy 65877 root 20 0 93528 4936 2328 S 1.3 0.1 449:03.61 alisentry_cli 第一行显示的内容:当前时间、系统运行时间以及正在登录用户数。load average 后的三个数字,依次表示过去 1 分钟、5 分钟、15 分钟的平均负载(Load Average)。平均负载是指单位时间内,系统处于可运行状态(正在使用 CPU 或者正在等待 CPU 的进程,R 状态)和不可中断状态(D 状态)的平均进程数,也就是平均活跃进程数,CPU 平均负载和 CPU 使用率并没有直接关系。 第三行的内容表示 CPU 利用率,每一列的含义可以使用 man 查看。CPU 使用率体现了单位时间内 CPU 使用情况的统计,以百分比的方式展示。计算方式为:CPU 利用率 = 1 - (CPU 空闲时间)/ CPU 总的时间。需要注意的是,通过性能分析工具得到的 CPU 的利用率其实是某个采样时间内的 CPU 平均值。注:top 工具显示的的 CPU 利用率是把所有 CPU 核的数值加起来的,即 8 核 CPU 的利用率最大可以到达800%(可以用 htop 等更新一些的工具代替 top)。 使用 vmstat 命令,可以查看到「上下文切换次数」这个指标,如下表所示,每隔1秒输出1组数据: $ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 0 0 0 504804 0 1967508 0 0 644 33377 0 1 2 2 88 0 9 上表的 cs(context switch) 就是每秒上下文切换的次数,按照不同场景,CPU 上下文切换还可以分为中断上下文切换、线程上下文切换和进程上下文切换三种,但是无论是哪一种,过多的上下文切换,都会把 CPU 时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,从而缩短进程真正运行的时间,导致系统的整体性能大幅下降。vmstat 的输出中 us、sy 分别用户态和内核态的 CPU 利用率,这两个值也非常具有参考意义。 vmstat 的输只给出了系统总体的上下文切换情况,要想查看每个进程的上下文切换详情(如自愿和非自愿切换),需要使用 pidstat,该命令还可以查看某个进程用户态和内核态的 CPU 利用率。 CPU 相关指标异常的分析思路是什么? 1)CPU 利用率:如果我们观察某段时间系统或应用进程的 CPU利用率一直很高(单个 core 超过80%),那么就值得我们警惕了。我们可以多次使用 jstack 命令 dump 应用线程栈查看热点代码,非 Java 应用可以直接使用 perf 进行 CPU 采采样,离线分析采样数据后得到 CPU 执行热点(Java 应用需要符号表进行堆栈信息映射,不能直接使用 perf得到结果)。 2)CPU 平均负载:平均负载高于 CPU 数量 70%,意味着系统存在瓶颈点,造成负载升高的原因有很多,在这里就不展开了。需要注意的是,通过监控系统监测平均负载的变化趋势,更容易定位问题,有时候大文件的加载等,也会导致平均负载瞬时升高。如果 1 分钟/5 分钟/15 分钟的三个值相差不大,那说明系统负载很平稳,则不用关注,如果这三个值逐渐降低,说明负载在渐渐升高,需要关注整体性能; 3)CPU 上下文切换:上下文切换这个指标,并没有经验值可推荐(几十到几万都有可能),这个指标值取决于系统本身的 CPU 性能,以及当前应用工作的情况。但是,如果系统或者应用的上下文切换次数出现数量级的增长,就有很大概率说明存在性能问题,如非自愿上下切换大幅度上升,说明有太多的线程在竞争 CPU。 上面这三个指标是密切相关的,如频繁的 CPU 上下文切换,可能会导致平均负载升高。如何根据这三者之间的关系进行应用调优,将在下一部分介绍。 CPU 上的的一些异动,通常也可以从线程上观测到,但需要注意的是,线程问题并不完全和 CPU 相关。与线程相关的指标,主要有下面几个(均都可以通过 JDK 自带的 jstack 工具直接或间接得到): 应用中的总的线程数; 应用中各个线程状态的分布; 线程锁的使用情况,如死锁、锁分布等; 关于线程,可关注的异常有: 1)线程总数是否过多。过多的线程,体现在 CPU 上就是导致频繁的上下文切换,同时线程过多也会消耗内存,线程总数大小和应用本身和机器配置相关; 2)线程的状态是否异常。观察 WAITING/BLOCKED 线程是否过多(线程数设置过多或锁竞争剧烈),结合应用内部锁使用的情况综合分析; 3)结合 CPU 利用率,观察是否存在大量消耗 CPU 的线程。 3.2 内存&&堆 和内存相关的指标主要有以下几个,常用的分析工具有:top、free、vmstat、pidstat 以及 JDK 自带的一些工具。 系统内存的使用情况,包括剩余内存、已用内存、可用内存、缓存/缓冲区; 进程(含 Java 进程)的虚拟内存、常驻内存、共享内存; 进程的缺页异常数,包含主缺页异常和次缺页异常; Swap 换入和换出的内存大小、Swap 参数配置; JVM 堆的分配,JVM 启动参数; JVM 堆的回收,GC 情况。 使用 free 可以查看系统内存的使用情况和 Swap 分区的使用情况,top 工具可以具体到每个进程,如我们可以用使用 top 工具查看 Java 进程的常驻内存大小(RES),这两个工具结合起来,可用覆盖大多数内存指标。下面是使用 free命令的输出: $free -h total used free shared buff/cache available Mem: 125G 6.8G 54G 2.5M 64G 118G Swap: 2.0G 305M 1.7G 上述输出各列的具体含义在这里不在赘述,也比较容易理解。重点介绍下 swap 和 buff/cache 这两个指标。 Swap 的作用是把一个本地文件或者一块磁盘空间作为内存来使用,包括换出和换入两个过程。Swap 需要读写磁盘,所以性能不是很高,事实上,包括 ElasticSearch 、Hadoop 在内绝大部分 Java 应用都建议关掉 Swap,这是因为内存的成本一直在降低,同时这也和 JVM 的垃圾回收过程有关:JVM在 GC 的时候会遍历所有用到的堆的内存,如果这部分内存被 Swap 出去了,遍历的时候就会有磁盘 I/O 产生。Swap 分区的升高一般和磁盘的使用强相关,具体分析时,需要结合缓存使用情况、swappiness 阈值以及匿名页和文件页的活跃情况综合分析。 buff/cache 是缓存和缓冲区的大小。缓存(cache):是从磁盘读取的文件的或者向磁盘写文件时的临时存储数据,面向文件。使用 cachestat 可以查看整个系统缓存的读写命中情况,使用 cachetop 可以观察每个进程缓存的读写命中情况。缓冲区(buffer)是写入磁盘数据或从磁盘直接读取的数据的临时存储,面向块设备。free 命令的输出中,这两个指标是加在一起的,使用 vmstat 命令可以区分缓存和缓冲区,还可以看到 Swap 分区换入和换出的内存大小。 了解到常见的内存指标后,常见的内存问题又有哪些?总结如下: 系统剩余内存/可用不足(某个进程占用太多、系统本身内存不足),内存溢出; 内存回收异常:内存泄漏(进程在一段时间内内存使用持续走高)、GC 频率异常; 缓存使用过大(大文件读取或写入)、缓存命中率不高; 缺页异常过多(频繁的 I/O 读); Swap 分区使用异常(使用过大); 内存相关指标异常后,分析思路是怎么样的? 使用 free/top 查看内存的全局使用情况,如系统内存的使用、Swap 分区内存使用、缓存/缓冲区占用情况等,初步判断内存问题存在的方向:进程内存、缓存/缓冲区、Swap 分区; 观察一段时间内存的使用趋势。如通过 vmstat 观察内存使用是否一直在增长;通过 jmap 定时统计对象内存分布情况,判断是否存在内存泄漏,通过 cachetop 命令,定位缓冲区升高的根源等; 根据内存问题的类型,结合应用本身,进行详细分析。 举例:使用 free 发现缓存/缓冲区占用不大,排除缓存/缓冲区对内存的影响后 -> 使用 vmstat 或者 sar 观察一下各个进程内存使用变化趋势 -> 发现某个进程的内存时候用持续走高 -> 如果是 Java 应用,可以使用 jmap / VisualVM / heap dump 分析等工具观察对象内存的分配,或者通过 jstat 观察 GC 后的应用内存变化 -> 结合业务场景,定位为内存泄漏/GC参数配置不合理/业务代码异常等。 3.3 磁盘&&文件 在分析和磁盘相关的问题时,通常是将其和文件系统同时考虑的,下面不再区分。和磁盘/文件系统相关的指标主要有以下几个,常用的观测工具为 iostat和 pidstat,前者适用于整个系统,后者可观察具体进程的 I/O。 磁盘 I/O 利用率:是指磁盘处理 I/O 的时间百分比; 磁盘吞吐量:是指每秒的 I/O 请求大小,单位为 KB; I/O 响应时间,是指 I/O 请求从发出到收到响应的间隔,包含在队列中的等待时间和实际处理时间; IOPS(Input/Output Per Second):每秒的 I/O 请求数; I/O 等待队列大小,指的是平均 I/O 队列长度,队列长度越短越好; 使用 iostat 的输出界面如下: $iostat -dx Linux 3.10.0-327.ali2010.alios7.x86_64 (loginhost2.alipay.em14) 10/20/2019 x86_64 (32 CPU) Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %utilsda 0.01 15.49 0.05 8.21 3.10 240.49 58.92 0.04 4.38 2.39 4.39 0.09 0.07 上图中 %util ,即为磁盘 I/O 利用率,同 CPU 利用率一样,这个值也可能超过 100%(存在并行 I/O);rkB/s 和 wkB/s分别表示每秒从磁盘读取和写入的数据量,即吞吐量,单位为 KB;磁盘 I/O处理时间的指标为 r_await 和 w_await 分别表示读/写请求处理完成的响应时间,svctm 表示处理 I/O 所需要的平均时间,该指标已被废弃,无实际意义。r/s + w/s 为 IOPS 指标,分别表示每秒发送给磁盘的读请求数和写请求数;aqu-sz 表示等待队列的长度。 pidstat 的输出大部分和 iostat 类似,区别在于它可以实时查看每个进程的 I/O 情况。 如何判断磁盘的指标出现了异常? 当磁盘 I/O 利用率长时间超过 80%,或者响应时间过大(对于 SSD,从 0.0x 毫秒到 1.x 毫秒不等,机械磁盘一般为5ms~10ms),通常意味着磁盘 I/O 存在性能瓶颈; 如果 %util 很大,而 rkB/s 和 wkB/s 很小,一般是因为存在较多的磁盘随机读写,最好把随机读写优化成顺序读写,(可以通过 strace 或者 blktrace 观察 I/O 是否连续判断是否是顺序的读写行为,随机读写应可关注 IOPS 指标,顺序读写可关注吞吐量指标); 如果 avgqu-sz 比较大,说明有很多 I/O 请求在队列中等待。一般来说,如果单块磁盘的队列长度持续超过2,一般认为该磁盘存在 I/O 性能问题。 3.4 网络 网络这个概念涵盖的范围较广,在应用层、传输层、网络层、网络接口层都有不同的指标去衡量。这里我们讨论的「网络」,特指应用层的网络,通常使用的指标如下: 网络带宽:表示链路的最大传输速率; 网络吞吐:表示单位时间内成功传输的数据量大小; 网络延时:表示从网络请求发出后直到收到远端响应,所需要的时间; 网络连接数和错误数; 一般来说,应用层的网络瓶颈有如下几类: 集群或机器所在的机房的网络带宽饱和,影响应用 QPS/TPS 的提升; 网络吞吐出现异常,如接口存在大量的数据传输,造成带宽占用过高; 网络连接出现异常或错误; 网络出现分区。 带宽和网络吞吐这两个指标,一般我们会关注整个应用的,通过监控系统可直接得到,如果一段时间内出现了明显的指标上升,说明存在网络性能瓶颈。对于单机,可以使用 sar 得到网络接口、进程的网络吞吐。 使用 ping 或者 hping3 可以得到是否出现网络分区、网络具体时延。对于应用,我们更关注整个链路的时延,可以通过中间件埋点后输出的 trace 日志得到链路上各个环节的时延信息。 使用 netstat、ss 和 sar 可以获取网络连接数或网络错误数。过多网络链接造成的开销是很大的,一是会占用文件描述符,二是会占用缓存,因此系统可以支撑的网络链接数是有限的。 3.5 工具总结 可以看到的是,在分析 CPU、内存、磁盘等的性能指标时,有几种工具是高频出现的,如 top、vmstat、pidstat,这里稍微总结一下: CPU:top、vmstat、pidstat、sar、perf、jstack、jstat; 内存:top、free、vmstat、cachetop、cachestat、sar、jmap; 磁盘:top、iostat、vmstat、pidstat、du/df; 网络:netstat、sar、dstat、tcpdump; 应用:profiler、dump分析。 上述的很多工具,大部分是用于查看系统层指标的,在应用层,除了有 JDK 提供的一系列工具,一些商用的产品如 gceasy.io(分析 GC 日志)、fastthread.io(分析线程 dump 日志)也是不错的。 排查 Java 应用的线上异常或者分析应用代码瓶颈,可以使用阿里开源的 Arthas ,这个工具非常强大,下面简单介绍下。 Arthas 主要面向线上应用实时诊断,解决的是类似「线上应用异常了,需要在线进行分析和定位」的问题,当然,Arthas 提供的一些方法调用追踪工具,对我们排查诸如「慢查询」等问题,也是非常有帮助的。Arthas 提供的主要功能有: 获取线程统计,如线程持有的锁统计、CPU 利用率统计等; 类加载信息、动态类加载、方法加载信息; 调用栈追踪,调用耗时统计; 方法调用参数、结果检测; 系统配置、应用配置信息; 反编译加载类; .... 需要注意的是,性能工具只是解决性能问题的手段,我们了解常用工具的一般用法即可,不要在工具学习上投入过多精力。 在通过工具得到异常指标,初步定位瓶颈点后,如何进一步进行确认和调优?这里将给出常见的一些调优分析思路,内容会按照CPU、内存、网络、磁盘等进行组织。详情见:https://developer.aliyun.com/article/727625?spm=5176.8068049.0.0.7f0d6d19WJXuiS 原文发布时间为:2019-11-21作者: 齐光本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:选择有时候比努力重要,真正厉害的人不仅仅是埋头苦干,而是会利用好的思维方式、好的方法,看穿事物的本质,顺势而为,找到事情的最优解,并懂得举一反三。架构师是程序员的目标之一,但大多数程序员无法成为架构师。真正厉害的架构师具备什么样的思维方式,到底强在哪?今天,韩帅为你揭秘。 韩帅的其他文章:《架构整洁之道,看这一篇就够了!》 世界上所有的道理都是相通的,而有一些相似到令人吃惊,模型思考者和架构师就是如此。《模型思考者》是密西根大学教授 斯科特·佩奇 的一本书,这本书的目的是教大家如何理解和应对这个复杂的世界。目前这本书的中文版还没正式出版,得到 万维钢精英日课中有15节课程讲解。 这一篇文章是篇读书笔记,内容来自荣华老师的文章,还有万维钢精英日课中对《模型思考者》的讲解,结合一些自己的理解和思考。学习了这些后,我的学习和成长都有了框架有了方向,如果你觉得很有收获,可以阅读原著,欢迎留言区交流。荣华老师的原文还未正式发布,这里在荣华老师同意下,节选了部分内容,识别下方二维码即可阅读,供大家参考、学习。 一、什么是架构? ISO/IEC 42010:20072 中对架构的定义如下: The fundamental organization of a system, embodied in its components, their relationships to each other and the enviroment, and the principles governing its design and evolution.ISO/IEC 42010 这里定义了架构的三要素: 职责明确的模块或者组件 组件间明确的关联关系 约束和指导原则 越是简单抽象的定义,越是美,越是通用。小到一个玩具,大到一个国家的运作都可以隐含着这样的内容。 举两个简单的例子,我们来一起看他们的三要素分别是什么。 软件架构 模块:模型、域 关系:一对一、一对多(模型);依赖(域) 原则:单一职责、开闭原则、里氏替换原则等等 组织架构 模块:部门 关系:管理 or 上报 原则:各种管理原则、财务原则 从不同的角度来丰富架构的定义: 架构的原则是简单,但不能有遗漏; 架构的目的是解决问题。问题的尺度上,可以大到国家战略问题、经济问题、民生问题,也可以小到一只钢笔如何均匀地吐墨;时间上,可以是当下的问题,也可以是预期以后会发生的问题; 架构不是一成不变的,它只适合于特定的场景。过去的架构不一定适合现在,当下的架构不一定能预测未来。 二、什么是架构师? 架构师是一个角色,定义角色其实是定义职责,架构师的职责是:识别并定义问题,创建、选择或调整架构,从而找到最优的方案,解决问题。 这其实也是架构师做事的一般套路:定义问题->确定架构->提出方案->落地拿结果。这四步中,越是前面的步骤,越是重要,越是抽象,也越是困难,越能体现架构师的功力。 1.什么是问题? 问题的定义很宽泛,生存或是毁灭?这是一个问题。晚上去吃烧烤不?也是一个问题。架构师常说,我的架构解决了什么问题,这里的问题不是一般性的问题,而是特指马克思哲学中的矛盾(矛盾的定义也很宽泛,注意这里是马克思哲学中的矛盾)。 问题就是事物的矛盾。哪里有没有解决的矛盾,哪里就有问题。 ——毛泽东任何事物都是作为矛盾统一体而存在的,矛盾是事物发展的源泉和动力。——马克思 架构师要定义和解决的问题,就是特定领域中的矛盾,解决了矛盾,就得到了发展,取得了收益。 关于问题本质的更多解读,请阅读附录部分荣华老师的原文:如何自顶向下构建架构(进阶之路)。 既然架构师眼中的问题就是马克思哲学中的矛盾,我们就可以从马克思哲学中学习定义问题的系统方法,比如矛盾分主要矛盾、次要矛盾。主要矛盾指:在事物发展过程中处于支配地位,对事物发展起决定作用的矛盾;次要矛盾指:处于从属地位、对事物发展不起决定作用的矛盾;主次矛盾相互依赖、相互影响,并在一定条件下相互转化。 当我们面对复杂的问题时要不断反思,这是不是主要问题?是不是当下最主要的问题? 2.如何区分问题、手段、挑战 我们述职或晋升时常常要说问题、手段、挑战,但这些概念总是混淆在一起,很难区分一件事情是问题还是手段。其实问题、手段、挑战都是一回事,都是矛盾,只是层次不同。比如: 每一个问题可以向下不断展开不断细化,下一级的问题是上一级问题的具体解决手段,当你把“提升性能”当做你Owner的问题时,提升帧率、提高页面秒开率、优化启动耗时就成为了你的具体解决手段;而手段的下一级问题,就是你将面临的挑战,比如你要优化网络耗时,你要面临的挑战就有弱网环境、一些国家区域的带宽问题等等。同理,当你把“提升用户体验”当做你Owner的问题,“提升性能”就变成了你的具体手段,帧率、秒开率、启动耗时就成为了挑战。 3.如何定义问题 The significant problems we face cannot be solved at the same level of thinking we were at when we created them.——爱因斯坦 荣华老师把爱因斯坦这种思想定义为升层思考:我们不能只局限于问题本身,还需要看到问题背后的问题,然后才能更容易找到更多的解决方案。 亨利福特说,如果我问客户需要什么,他们会告诉我,他们需要一匹更快的马。从亨利福特的这句话,我们可以提炼出一个最直接的问题:客户需要一匹更快的马。立足这个问题本身去找解决方案,可能永远交不出满意的答卷:寻找更好的品种,更科学的训马方式。 思考问题背后的问题,为什么客户需要一匹更快的马?可能客户想要更快的日常交通方式,上升了一个层次后,我们立刻找到了更好的解决方案:造车。 阅读了本段内容,相信你不仅仅理解了什么是架构师,也理解了架构师的做事套路和思考方式。 三、什么是模型? 《模型思考者》中对模型的定义是这样的: 模型是对真实世界的抽象,明确定义了各种元素、以及元素之间的关系,可以用来做逻辑推导。 对比架构三要素和模型的定义,相同点是都有元素(组件),以及元素(组件)间的关系。不同的是,架构强调约束和指导原则,用来指导我们如何做事;模型强调逻辑推导能力,指导我们在现有规律下寻找答案或寻求最优解。 举个使用模型解决问题的例子:如果你是一位高中班主任,想要提高班级中考试成绩大于90分的人数,你应该怎么做? 首先要找到正确的模型。关键元素有两个:考试成绩、相应的学生人数,这两个元素之间的关系是正态分布函数,所以应该使用的是正态分布模型。 然后使用模型做推导,找到问题的最优解。正态分布函数有两个关键变量:均值、方差,只要这两个变量确定了,正态分布就唯一确定了。想要提高班级中考试成绩大于90分的人数,我们有两个选择,提高均值,或者提高方差。即使忽略提高方差带来的负面影响(成绩差的同学也更多了),提高方差带来的正面影响也远远逊色于提高均值,特别是当均值接近90分时,均值哪怕提高1分,大于90分的人数都有巨大提升。 最后根据推导结果,确定具体落地方案。放弃提高方差的方法,比如给成绩好的同学特别的照顾,把成绩不好的同学座位调到后排。多采取提升均值的方法,比如培养班级的学习氛围,成立学习小组等等。 四、什么是模型思考者? 参考架构师,用模型思考者的做事方式来给它下定义:面对问题,能看穿客观事物的本质,选取或构建合适的模型,推导出问题的最优解。 就像架构和模型的定义类似,只是突出的重点不同一样,架构师和模型思考者的定义也很类似,重点也不同。架构师的重点是定义问题、解决问题、推动事物发展;模型思考者的重点是看穿事物的本质,遵循规律,找到最优解。 接下来介绍几个惊艳的模型: 1.认知模型(认知金字塔):这个模型揭示的是认知的本质,给出了一条提升认知的最佳实践。 模型解释: 金字塔的最底层是数据。数据代表各种事件和现象。数据本身没有组织和结构,也没有意义。数据只能告诉你发生了什么,并不能让你理解为什么会发生。 数据的上一层是信息。信息是结构化的数据。信息是很有用的,可以用来做分析和解读。 信息再往上一层是知识。知识能把信息组织起来,告诉我们事件之间的逻辑联系。有云导致下雨,因为下雨所以天气变得凉快,这都是知识。成语典故和思维套路都是知识。模型,则可以说是一种高级知识,能解释一些事情,还能做预测。 认知金字塔的最上一层,是智慧。智慧是识别和选择相关知识的能力。你可能掌握很多模型,但是具体到这个问题到底该用哪个模型,敢不敢用这个模型,就是智慧。 来源:得到付费课程《精英日课》 作者:万维钢 模型的应用: 信息焦虑:处在信息时代,很多人都有信息焦虑,生怕自己每天阅读的信息不够多而被时代抛下,每天疲于输入新的信息,没时间思考总结,结果随着信息的遗忘,什么也没留下。其实相比于信息,模型并没有那么多,我们应该花更多的时间总结模型,明确模型的使用范围(智慧)。 数据分析:开发完一个功能,加一些埋点,跑sql能拿到数据,做报表能拿到信息,针对报表所做的分析属于知识。反思一下自己,有多少埋点还没跑数据,有多少数据还没结构化为信息?多少信息还没做分析?多少分析还没做下一步决策? 指导投资:DALIO 的《原则》一书中提到了桥水基金挣钱方法:不断采集市场数据,使用计算机加工数据抽象模型,根据模型自动做投资决策。计算机的这个行为,不就是模型思考者吗? 2.索洛模型(经济增长模型) 这是个拿了诺贝尔经济学奖的模型,揭示的是经济增长的本质。 模型解释(摘抄自得到精英日课): O代表经济产出,A代表技术进步,L代表劳动力,s代表储蓄率,d代表折旧率。 O与L成线性关系:索洛考虑劳动力有两个作用:他们不仅仅是去工厂上班工作,他们还要拿工资,拿了工资会存钱,存的这个钱可以用来投资。经济产出跟劳动力工作是平方根关系,跟劳动力的投资也是平方根关系,所以跟劳动力本身就成了正比关系。 O与A成平方关系:你的技术更先进,你的产出就比别人的产出更值钱 —— A 代表了你的技术附加值。为什么经济产出和 A² 成正比?因为 A 有两个效应。一个是 A 能直接增加产出,一个是因为 A 增加了产出,会导致相对于同样的折旧,投资也会增加。如果你能把技术附加值变成两倍,你的总产出就会变成四倍。 来源:得到付费课程《精英日课》 作者:万维钢 模型的应用: 单纯的投资行为,会被折旧追平,是边际效应递减的过程,不可持续劳动力对经济增长的影响是线性的,但是技术进步的影响是平方的,面对我国人口问题,追求技术进步是唯一出路,技术进步是真正的增长之本。 3.其他的模型: 《模型思考者》中有许多许多模型,每个模型有详细的解释,以及模型的适用场景,比如:适用于市场营销、病毒传播领域的“传播模型”,研究稳态与“宿命”的“马尔可夫模型”,大家感兴趣可以阅读原著。 五、总结 架构师和模型思考者是非常相似的两套做事方法,对于所研究的系统,当我们可以对系统做修改时,可以用架构师思维,定义问题、解决问题,推动系统一步步完善;当我们无法影响系统运行机制时,要用模型思考者思维,洞见其本质,顺势而为,找到最优解。 参考文章:荣华老师的多篇文章<识别下方二维码或文末“阅读原文”即可在线阅读>万维钢精英日课,《模型思考者》系列课程 原文发布时间为:2019-11-20作者: 韩帅本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:视觉想象力是人与生俱来的, AI 能否拥有类似的能力呢?比如:给出一段故事情节,如何让机器展开它的想象力,“脑补”出画面呢?看看阿里AI Labs 感知实验室的同学们如何解决这个问题。 1. 背景 —— 视觉想象力(Visual Imagination) 1.1 什么是视觉想象力? 视觉想象力是人脑拥有的一个重要功能,可以将一些抽象的概念具象化,进而凭借这些视觉想象进行思考。如图1最左列,当我们想到:A yellow bird with brown and white wings and a pointed bill时,脑海里可能已经想象出了一幅黄色鸟的画面。这就是视觉想象力。我们的目标就是让AI逐步具备这种能力。 图1:由第一行中的文本描述,AI“想象”出的画面 [1]。 1.2 AI拥有视觉想象力后的影响? AI如果具备视觉想象力后,将会更懂人的需求,并能够对一些传统行业产生颠覆性影响。下面举两个例子。 图2为一个在语义图像搜索领域中的案例。我们在google中搜索man holding fish and wearing hat on white boat,可能返回的结果质量为(a),引擎只是零星理解了我们的搜索意图。而当机器拥有一定视觉想象力后,它的搜索结果可能是(b),这将极大提升我们的信息检索效率,而这些信息是承载于图像中的。 图2:AI具备视觉想象力后将会对语义图像搜索产生重要影响 [2]。 另一个例子在语义图像生成领域。试想:当我们用语言描述一个场景时,机器利用其庞大的经验数据便自动生成了这个场景。如图3,如果我们描述一个人拥有不同的外貌特征,那机器便自动想象出了这个人的样貌,这将对诸如刑侦等领域(如受害人描述犯罪分子样貌)产生怎样的颠覆。 图3:AI具备视觉想象力后将会对语义图像生成产生重要影响 [3]。 2. 选题 —— 站在巨人的肩膀上 2.1 领域的痛点在哪? 我们将焦点移至文本生成图像(text-to-image synthesis)领域。此领域中,针对简单单一主体的图像生成,例如:鸟、花、人脸等,利用GAN的思想而来的一系列算法已经取得了一些令人欣喜的结果,如图1。然而,当文本中含有多个相互关联的物体时,生成的效果就会大打折扣,如下左图所示。这主要是由过于灵活、非结构化文本所造成的。 图4:当前的生成算法很难对包含多个相互作用的物体进行生成,如左边的StackGan算法 [4]。右边的sg2im算法则一定程度上拥有潜力解决这个问题 [5]。 因此,Stanford大学CV组的Johnson等人在CVPR2018中提出了将文本到图像的生成拆分为若干个更加可控的子问题的想法 [5]。这用到了他们之前在CVPR2015中提出的一种新的场景表达方式 —— 场景图(Scene Graph)和语义构图(Semantic Layout) [2]。 图5:场景图和语义构图示意 [6]。 场景图是一种有向图,含有实体、属性、关系三种要素,可以看做是一种语义模态下的结构化表达。 每个场景图中的实体,在图像中会有一个与之对应的bbox。如果不看图像本身,单看图中所有的bbox,就形成了一幅图像的语义构图,因此可以将语义构图看作是一种具有普遍含义的图像结构化表达。 表1:结构名称及所属模态对照表。 2.2 如何解决?—— 我们眼中的大框架 站在大牛们的肩膀上,我们眼中从文本到图像的生成大致分为下面几个子任务: 表2:由文本生成图像任务拆分而来的子任务列表。 为了达到可控生成,信息逐步升维的目的,整个过程大致可拆分为上述子任务。每个子任务都有相应的一些工作,在此不一一具体展开。 2.3 论文的关注点 论文专注于解决子任务3:如何由场景图生成场景构图? 这个任务之所以重要,是因为由这个任务而始,结构化语义态的信息得以“想象”为图像的结构化表达,是赋予机器以视觉想象力的关键所在。 3. 论文的动机及贡献 3.1 当前的问题 | 3.1.1 最接近的工作与组合爆炸问题 图6:sg2im利用图卷积网络,以场景图整体作为输入,生成语义构图整体 [5]。 最接近的工作来自Stanford Johnson等人在CVPR2018中发表的sg2im算法 [5](如图6)。他们首先利用一个图卷积网络将每个实体进行特征嵌入,继而用这些特征通过一个object layout网络去生成语义构图。他们采用的生成方式是由场景图整体到语义构图整体。场景图整体中会包含若干个实体和关系,这些实体和关系的组合所形成的场景图变化极多,使得模型难以有效表达如此多的变化,最终导致了语义构图学习效果的不理想。我们称之为组合爆炸问题。 | 3.1.2 语义构图评价指标的缺失 另一大挑战是:如何直接自动化评价语义构图生成的好坏? 过去绝大部分工作采用间接自动化评价的方式进行,对由语义构图生成后的图像给予打分,利用Inception score或Image captioning score。这样做根本无法评价语义构图的生成好坏,很大程度上只评估了最终的GAN网络是否有效。很多工作还加入了人工评分,虽给出了评分结果,但其几乎不可能被完全复现,这极大地阻碍了本领域的发展。 3.2 Seq-SG2SL的动机 Seq-SG2SL是我们针对组合爆炸问题提出的一个由场景图生成语义构图的框架。本节不谈框架本身,先讲个故事。 故事背景:老师需要教学生学习如何通过看建筑图纸去建楼。如图7。 图7:上图是建筑图纸示意,下图是建成的房间示意(图片来源于网络)。 A老师教快班。他指着一摞厚厚的图纸对学生们说:“看,这是之前的图纸,上面有按图纸建好的大楼地址,你们拿这些图纸去看看那些大楼,应该就能悟出来大楼是怎么建的了。以后我给你一张新图纸,你们就能建出大楼了。”学生们按照A老师的方法,纷纷去学了。当A老师测验时发现,几乎没有学生可以照图纸盖出大楼,A老师生气地说:“还快班呢,这群学生也太没有悟性了,举一反三都不会。” B老师教慢班。他对学生们说:“我给大家一些图纸,今天我先教大家怎么建客厅,明天教怎么建厨房。我们的目标是先把每个房间的建造套路学到,再教大家怎么串起来建一整间房。最后再教你们怎么建栋楼。看看这些图纸,不必着急,我会告诉你们每一部分都和实际建筑里的哪一部分相对应,虽然整栋建筑看起来都不一样,但这些局部是很有套路的,掌握以后保管你们都会盖大楼。”果然,在B老师的悉心教导下,所有同学都很快通过了测验,连小笨笨源方都学会了怎么看图纸建大楼。 故事中,A老师的学生虽然是快班的,都很聪明,但是大楼千变万化,学生们通过这些图纸很难学到其中的共性。而B老师的学生,虽然整体学习比较慢,记性也不好,但B老师通过教授建大楼所需要的一些基础知识,将这些具有共性的要点教给学生,结果笨鸟得以先飞。 场景图就好比建筑图纸,语义构图就好比大楼。A老师的教学方法其实就遇到了组合爆炸的问题,B老师通过教授最基础的建楼操作避免了组合爆炸的问题。 由此启发,我们提出了一种全新的视角,去看待由场景图生成语义构图的问题。语义构图是一个结果,我们要学习的不应该是直接这个结果,而是产生这个结果的过程。通过对更基础单元的学习,解决组合爆炸问题。 3.3 SLEU的动机 为了解决缺乏直接自动化评估指标的问题,我们提出了一个新指标:semantic layout evaluation understudy,简称SLEU。这个指标是受到著名的机器翻译指标BLEU启发而来。 背后的逻辑是这样的: 1)要想完成自动化评估,必须需要真值。2)SLEU的设计目的就是要度量一个生成的语义构图与真值之间的差异。 因此,遵循上述逻辑,我们类比了机器翻译指标BLEU的设计,将BLEU的基本概念由1D扩展到2D,提出了SLEU。 3.4 论文的贡献 1)提出了一个新的框架Seq-SG2SL,将语义构图看作是一系列过程叠加的结果。和以往方法不同,AI学的是生成过程而不是结果。这种序列到序列的学习方式可以解决组合爆炸问题。 2)提出了一个直接自动化评价语义构图生成好坏的指标SLEU,将会解决本领域存在的结果复现问题,为不同构图生成方法的直接比较提供基础。 4. 方法要点简述 4.1 Seq-SG2SL框架 图8:Seq-SG2SL框架。 什么决定了一张语义构图呢?是关系。因此,一个场景图中的关系三元组(主 - 谓 - 宾),决定了组成一张语义构图中的主语和宾语所对应的两个bbox。其中主语和宾语所对应的bbox,分别称为视觉主语(visual subject)和视觉宾语(visual object)。 由此,产生语义构图的过程可拆解为一系列基础动作片段,每一个基础动作片段称为一个brick-action code segments (BACS)。每一个BACS执行的操作就是将一个视觉主语和一个视觉宾语摆放到语义构图中,分别调整他们的类别,位置以及大小。而每一BACS恰恰由其在场景图中所对应的关系三元组所决定。一个关系三元组主-谓-宾顺序相接,三个词组成了一个基础语义片段,我们叫做一个semantic fragments(SF)。如图8,tree by sidewalk就是一个SF,它对应的图中BACS Sequence所示的那10个code(c0002 … h14)就是一个BACS,而这10个code执行的结果就是最右侧layout图中tree和sidewalk两个bbox。 将一系列SF进行串联,形成了SF序列(SF sequence)。这个SF序列所对应的是一个由每一个对应BACS所串联形成的序列(BACS sequence)。这两个序列,就像两种语言,我们需要做的只是让机器学习从SF语言“翻译”到BACS语言就好啦。当然,为了保有scene graph中的有向图信息,我们额外维护了一个节点序列(Node sequence),主要为了确定sequence中的哪些实体属于同一个实体,并且能够通过节点序列直接将场景图中的实体属性传递到语义构图中的bbox上。这样,整个Seq-SG2SL框架做到了灵活且通用。 回想一下,这个过程是不是像我们之前讲过的那个老师教学生从设计图纸建楼的故事。我们看到了设计图纸(scene graph)中的一个局部(一个SF),然后我们去学习大楼(semantic layout)中的这个对应局部是怎么建的(学习一个BACS),最后再综合,教学生去建整幢建筑。这样做是不是很直观,也符合客观规律,我们不要求我们的学生(模型)都是天才般的存在,但是需要我们这个老师教授方式得法,才能最终达到好的效果。 框架的主要思想就讲完了,细节的话感兴趣的读者可以去看论文。 4.2 SLEU指标 在介绍SLEU之前,我们希望读者已经熟悉什么是机器翻译中的BLEU指标。 BLEU的基础是n-gram。n-gram是指文本中连续出现的n个词语(word),是基于(n-1)阶马尔科夫链的一种概率语言模型。简单地说,其假设当前第n个词出现的概率,仅取决于其前(n-1)个词,而跟更前的词无关。在机器翻译中,BLEU评估的基本单位是word,一个unigram代表一个word,评估翻译的充分性,而较长的n-gram代表一个word序列,评估翻译的流畅性。BLEU的思想是将句子拆分为n-grams,评估局部相似度,进而对整体翻译效果进行打分。 对于机器翻译而言,最小可拆分单元是一个word,那对于语义构图生成问题而言,最小可拆分单元又是什么?是一个关系。因此,对于语义构图生成来说,我们的unigram变为了一个关系。评估充分性就是评估单个关系是否匹配;评估流畅性就是评估n个关系是否会同时匹配。我们同样做了n阶马尔科夫链的假设,即:一个关系的出现,只取决于不超过(n-1)个其他关系,而和更多的关系无关。由于场景图和语义构图中的物体是一一对应的,因此没有precision和recall的概念,我们称对单个关系的评估,叫做unigram accuracy,而对多个关系的评估叫做n-gram accuracy。 具体设计我就不在这里细讲了,将关系看做unigram是我们的核心思想。我们的工作只是将这个概念设计出来,将BLEU的概念由1D推广到2D罢了。感兴趣的读者可以参考论文,指标的实现也将会开源。 5. 实验结果预览 图9:由Seq-SG2SL框架在测试集上的一些结果展示。 上图是一些利用Seq-SG2SL在测试集上的生成的结果,其中第一行为输入,第二行为生成的语义构图,第三行是一个参考的语义构图及其对应图像。可以看出,我们的结果可以对含有多个关系的复杂场景进行构图生成。 这里只做个引子,更多定量的分析在论文中详细阐述,主要包括与baseline算法的比较,以及一些关于具体设计的必要性实验等。由于只是导读,结论及未来工作等在此也概不赘述啦,感兴趣的读者可以直接看论文。 本文是ICCV 2019录用论文《Seq-SG2SL:基于序列到序列学习的由场景图生成语义构图的算法》的导读,并不涉及论文中的全部内容,只重点阐述我们对一些问题的思考。虽以论文为背景,但行文上试图以更科普的形式逐步展示给读者,期望对后续研究及应用场景有所启发。 本文由@源方执笔,成果是几位小伙伴共同的结晶@帆月@坎特@铭杨,我们来自阿里AI Labs感知实验室。如果您对研究感兴趣,或者对业务方向有灵感,欢迎您致信:[boren.lbr@alibaba-inc.com],我们会很高兴和您讨论。 论文下载链接:https://arxiv.org/abs/1908.06592 参考文献:[1] Qiao et al., MirrorGAN: Learning Text-To-Image Generation by Redescription, CVPR 2019.[2] Johnson et al., Image Retrieval Using Scene Graphs, CVPR 2015.[3] https://github.com/SummitKwan/transparent_latent_gan[4] Zhang et al., StackGan: Text to Photo-realistic Image Synthesis with Stacked Generative Adversarial Networks, ICCV 2017.[5] Johnson et al., Image Generation from Scene Graphs, CVPR 2018.[6] Krishna et al., Visual Genome: Connecting Language and Vision Using Crowdsourced Dense Image Annotations, IJCV 2017. 原文发布时间为:2019-11-19作者: 源方本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
2019年双11:1分36秒100亿,5分25秒超过300亿,12分49秒超500亿……如果没有双11,中国的互联网技术要发展到今天的水平,或许要再多花20年。 从双11诞生至今的11年里,有一个场景始终在支付宝技术团队之中循环往复——每一年确定目标时,大家都将信将疑,或惊呼或腹诽:“不可能!太夸张了吧!”但每一年的夸张目标,到最后都能奇迹般地成为现实。 前一年需要拼命跃起才能够到的果实,后一年就会成为再普通不过的日常。不知不觉之间,双11已经从最初启航时的小船,成为了承载数十亿人快乐和梦想的巨舰。在这个举世瞩目的“奇迹工程”背后,是技术和技术人一起,十余年如一日,以难以置信的“中国速度”在不知疲倦地向前奔跑。 今年双11小编邀请到11位经历双11的技术同学口述实录,特别筹备了纪录片《一心一役》,讲述这一路走来的那些隐秘往事。 技术人的初衷往往极致单纯——既然决定要做,那就全力以赴,一往无前,但当他们一步一个脚印风雨兼程地走来,蓦然回首,就发现奇迹已经在那里了。 那时距离宕机只有几十秒 2009年11月11日,对于支付宝工程师陈亮而言,本来是与往常没有任何不同的一天。 那年还没有支付宝大楼,更没有Z空间,他趟过早高峰的车流,坐到华星时代广场的工位上时,一封来自CTO程立的邮件发到了他的电脑里:今天淘宝商城要搞一个促销活动,预估交易量比较大,大家盯着点系统。 陈亮当时所在的团队主要职责是保障整个系统的稳定可靠。在促销活动的场景下,通俗地说来,就是要保障服务器“坚挺”,别被蜂拥而来的用户挤爆了。 淘宝商城在2009年的8月刚刚重组上线,日均交易量对于当时的支付宝而言,要稳稳接住不在话下。就算搞促销临时出现洪峰,不怕,扩容就好。 事实上也就是这么操作的。全团队的同学聚在办公室里,盯着电脑屏幕,只要发现交易量逼近系统承载上限,就马上进行扩容。 交易量的上涨有点不寻常,一片安静的办公室里本来只有键盘声,忽然有人高喊一声:“我秒到了!”紧接着又有人跟着喊:“我也秒到了!”办公室里嗡地一下,热闹了起来。 原来有人很好奇淘宝商城究竟在做什么促销让交易量涨成这样,点过去一看,发现有折扣高达50%以上的“秒杀”,忍不住出手一试。 “已经想不起当时究竟秒到了什么,只记得大家都特别快乐。”陈亮说。 快乐,是陈亮对于这个促销活动最初、也最鲜明的印象。 不过除了一整天都忙于扩容的同学之外,当时支付宝的大多数人都对这场促销并无感知。“事后才知道前一天有促销,同事说流量有点猛。”现在已成为蚂蚁金服研究员的李俊奎说,运维负责人很紧张地在第二天的复盘会议上提出“抗议”:“淘宝商城那边在搞什么?支付量一下子提升了这么多,万一我们提前准备的量不够,就危险了。” 淘宝商城搞了什么?站在今天回头去看,他们只是搞了一件不算很大的事:在“光棍节”当天,联合27个品牌做了一场促销活动,单日GMV 5000万。 当时没有任何人能够预计这个促销活动日后会成长为什么模样,不过支付宝从数据的增长之中嗅到了山雨欲来的气息:这个活动带来的交易峰值超过平日的5倍,虽然这次平稳过关,但已经逼近了当时支付宝的承载极限。 2010年的年中刚过,支付宝就去跟淘宝商城通气:去年那个促销,今年还搞吗?淘宝商城说,搞。 好汉不打无准备之仗,如何筹备“双11”被提上了支付宝每周稳定性会议的议程。首当其冲的是要准备充足的容量。但是按多少准备呢?谁都没经验。 “拍脑袋估个数据,然后按预估数据乘以三去买机器,简单粗暴。”李俊奎直言不讳。 为了检验这样拍脑袋的决策行不行,他还和团队一起搞了个测试:通过手动更改配置,把多台机器上的流量导到一台机器上,测试一台机器的能接住多大的流量。“现在想起来,那就是压测最早的雏形。” 他们甚至准备了一个备用的工作联络群。当时还没有钉钉,工作群都搭在旺旺上,“万一旺旺服务器也出问题了,不能及时联络怎么办?” 筹备的时间虽不长,倒也方方面面都有兼顾,“但是不管事先做了怎样万全的准备,每年总有意外发生。”金融核心技术部工程师赵尊奎说。他当年所在的团队是账务会计组,一举一动都关系到钱,丝毫不容有错。 意外真的来了。 11日凌晨,促销活动刚开始不久,支付宝的账务数据库就容量告急。 病来如山倒。发现问题时,状况已经十分危急,“只能再撑几分钟!”运维心急如焚,如果不能马上找到解决办法,支付宝就面临宕机风险,交易链路一断,谁也买不成。 怎么办?运维把心一横,说,砍了会计系统吧,给核心的账务系统腾空间。 时间已经容不得多加斟酌,一群高管站在背后,支付宝中间件团队的工程师蒋涛感到前所未有地紧张,“操作的时候手都在抖。” 这个当机立断的决策将支付宝从距离宕机只差几十秒的悬崖边挽救了回来。事后的数据显示,2010年的双11,参与用户达到2100万,总GMV达到10亿,是上一年的20倍,这是任何人都很难在事先预估到的涨幅。 “能想到会涨,但谁也想不到涨势会这么猛烈。”赵尊奎说,“也就是从那年起,我们开始隐隐觉得,更猛烈的还在后头。” 代码的力量 85后的肖涵和90后的郑洋飞,都是在读大学时就知道了双11。 肖涵喜欢网购,09年就成了第一批尝鲜双11的剁手族,还在一个技术交流群里认识了参与过双11的支付宝工程师;郑洋飞常买《电脑报》,那上面说2010年双11一天的销售额等于香港一天的零售总额,他一边惊叹,一边心生向往。 “觉得好牛B,想进去看看。” 当年互不相识的两个年轻人,不约而同地产生了这样的想法。 肖涵在2011年加入了支付宝,那一年支付宝已经开启了“上半年搞建设、下半年搞大促”的模式,筹备工作从5、6月起就着手进行,他刚一入职,就被调去开发流量接入和调拨系统spanner。 这个系统相当于支付宝交易链路的第一道门户,“好比餐厅上菜的推车。一般餐厅,一个服务员只能每次上一盘菜,但双11的挑战,就是要让一位服务员同时能上十盘菜,因此我们需要一个推车。不过业界没有现成的推车能满足支付宝的需求,我们得自己造。” 差不多一整年的时间中,肖涵和团队为这个项目废寝忘食,spanner终于在2012年的双11迎来了第一次大考。 谁曾想,意外又发生了。 那一年支付宝的大促监控系统也已经上线,流量曲线能够秒级实时显示,零点将近时,所有人都紧盯着屏幕,翘首以盼。 ——零点一到,流量进来了,曲线开始增长,形成很漂亮的弧度,所有人开始欢呼,但是忽然,它跌了下去,然后开始像心电图那样抖动。 监控系统没有问题,也没有报错,为什么流量会进不来呢? 一石激起千层浪。他所能看到的抖动,同样实时显示在了淘宝的作战指挥室里。支付宝工程师贺岩正作为支付宝的唯一“代表”,在那里和淘宝的技术同学一起备战。这是个极其考验心理承受能力的工作,在支付曲线发生抖动的时刻,“淘宝的技术同学们一下子就把我围在了中间。连问‘支付宝出什么事了’?”贺岩回忆道。 肖涵脑子里一片空白,唯一的念头是,“不能让交易停下。” 0:00到0:20,短短的20分钟里,10分钟用来定位问题,10分钟用来解决问题;同样在这短短的20分钟里,外面已经天翻地覆:“‘支付宝不能付款了’登上了微博热搜,家人、亲戚、朋友都给我打电话问是什么情况,手机都要被打爆了。” 关掉了一个健康监控模块之后,系统终于恢复了稳定。比起紧张,肖涵感到的更是前所未有的震撼:原来自己所做的事已经和千万人息息相关,每一点微小的疏漏,所影响的都是难以估量的庞大群体。 “没有身在其中过,就很难意识到自己敲下的每一行代码有着怎样的分量。”郑洋飞说。他在2013年加入支付宝实习,带他的师兄巩杰说了一句令他印象极深的话:你看看那些客服mm,你敲代码时仔细一点,少出一个错,她们就不知能少接多少个报错电话。 架构革命 跨过了2012年的坎儿,DBA就再三给出警告:扩容已经到头了,顶多再撑几个月,按这个增速,如果不想点别的办法,肯定坚持不到明年双11。 祸不单行。另外的“紧箍咒”也接连落下:Oracle数据库的连接数上限成为扩容的瓶颈,更要命的是,由于机房的一再扩容,杭州的电力已不足以支撑。有时候为了保机房供电,“大夏天的,办公室都会停电,不得不运冰块过来降温。”巩杰苦笑着说,杭州的盛夏,谁过谁知道。 治标的方法快要山穷水尽,必须要从治本的角度出发寻找新的解决方案,比如,从架构层面“搞革命”,做单元化。 革命不是请客吃饭,要从架构层面做根本性的调整,举步维艰:一来没有任何成功经验可以借鉴,只能摸索着走;二来牵涉到众多部门,大家需求不同,意见难免相左;三来,既然要革命,那目光必须放得更加长远,不能只是为了解决今年或明年的问题,至少也要做未来三年的规划。 与此同时,在和淘宝商城——现在叫天猫了——沟通之后,支付宝毫不意外地定下了又一个令人惊呼“不可能”的目标:支付峰值每秒2万笔。 事关重大,人人都很谨慎。“光是架构调整的方案就讨论了很久。”陈亮说,作为项目的架构师,他费了不知多少口舌去说服所有人都认同这个方案。 重担的一头落在他的肩上,另一头则交给了2010年抖着手化解危机的蒋涛,蒋涛更愁稳定性问题:“做技术架构变更的同时还得稳住业务,这件事非常复杂,技术风险也很高。” 留给他们的时间也不多了。LDC架构的立项已是2012年年底,距离2013年的双11不足一年,对于这样浩大的工程来说,就一个字,紧。 陈亮最初构想了一个宏大的体系,要把所有系统都一口气单元化,但这个方案被程立否了:“主要问题在淘宝的交易上,先把淘宝做了。”按他的意思,哪怕先做第一期,2013年也必须上线。 一堆不可能的目标聚集在了一起。但目标既然定了,就只剩向前这唯一的方向。 “立项之后,我们几乎每个月都做发布。”蒋涛说,这个频率是一般项目开发的好几倍,但即便如此,直到双11之前半个月,整套系统才算部署完成,小错仍然不断,不过,随着越来越多的小问题和被发现和修正,他终于感到,“心里总算慢慢有点底气了”。 2013年,支付宝LDC架构首次在双11亮相,支付宝也第一次派“代表”前往双11的总指挥室——阿里巴巴西溪园区的“光明顶”。 这位“幸运”的代表就是李俊奎。“我就是去当‘炮灰’的。”他笑称自己一走进光明顶就感受到了热烈的压力。当年的总指挥李津当着全集团几百位工程师的面,指着大屏幕点名喊他:“向秀(李俊奎的花名)!你看看支付宝!” 这项压力山大的任务,李俊奎连做了好几年,乃至于做出了经验。“首先是不要慌,无论接到什么反馈,先说‘知道了,我看看’。因为你一个人在现场其实什么也做不了,你的职责其实是传达,以最快的速度,把问题传达给后方的伙伴,然后,相信他们。” 他说这是支付宝技术团队的重要制胜秘诀之一:你永远都不是一个人在战斗,你也无法一个人战斗,但你的身后永远有最靠谱的伙伴。 至于这一年的战果如何,按蒋涛的话说,“硬扛过去了”。新架构有惊无险,走出了第一步。 关公、灵隐寺和压测 2013年双11的另一个特殊之处是,支付宝的备战室里多出来一幅关老爷的挂画。 挂画是郑洋飞“请”来的,不过“拜关公”作为支付宝技术团队的一项传统,早在他入职之前就由来已久。源头据说要追溯到支付宝建立之初,每到重要的系统更新时,工程师们就会在旺旺群里转发关公表情包,以求更新顺利,“别出bug”。 隔了一年之后,关公像“升级”了,有同学去西安校招时看到了关公的皮影艺术品,就“请”了一个回来放在备战室。后来,程立买了一尊木质关公像放过来,去年,副CTO胡喜又买了一尊关公铜像。 除了拜关公,去寺庙烧香也是例行项目,视目的地不同,还分为“灵隐寺派”和“法喜寺派”两大派别。至于哪边灵验,说法不一,但据观察,每年双11过后,程立、胡喜就会亲自率队上山还愿,从支付宝大楼一路步行到上天竺法喜寺,回来的途中,还会沿途捡垃圾做公益。 技术是纯粹的科学。技术人难道真的相信求神拜佛能避免系统故障和bug吗? “心理上,我觉得还是挺有用的。”陈亮说,“主要是表达对于不可预知之物的一种敬畏。虽然我们已经做了多年技术,但技术的道路上还是充满了很多不可预知的东西。” 不可预知,构成了工程师们每年面对双11最大的焦虑感来源。 他们用各自的办法缓解双11迫近的压力。有人是运动派,用跑步或打球放空大脑,有人是“强迫症”派,一遍又一遍地check代码才能安心,还有人是“吃货”派,迎战之前必定要先组团去吃海底捞。 全程参加了过去11年全部双11的赵尊奎,在被问到“哪年最不好搞”时,秒答曰:“哪年都不好搞。”同样“全勤”的陈亮则表示:“14年之前,我们对于双11零点的信心,如果非要说一个数字的话,60%吧。” 但他很快补充:“不过2014年之后,这个数字就变成95%了。” 陈亮的信心,来自于当年支付宝压测体系的建立。这一次不再是手动调配置测单机了,而是创建出仿真环境让系统去跑,提前找出系统的问题并及时修复,以免在正式战场被打个措手不及。 “压测让双11开始从一个不确定的事逐渐变成确定的事,它极大地改变了我们对于双11稳定性的保障方式。”有“压测小王子”之称的郑洋飞说。 虽然2014年的压测仅覆盖核心系统,但这个体系已经帮了大忙。在双11之前的一个月里,它至少让一百多个致命的问题提前暴露出来。“如果其中有一个没有修复,我们2014年的双11肯定就挂了。”陈亮说。 1%?或10%? 压测这一“压”,既压出很多隐患,也压出了一个大问题:支付宝所用的Oracle数据库在压测之中“抖”了起来,性能眼见得触到了天花板。 2014正是移动互联网大爆发的年份。指数增长的移动支付比例势必带来比往年更汹涌的流量峰值,Oracle肉眼可见地支撑不住了。 再买服务器?成本吃不消,而且为了应对峰值而增添的机器,平日里没有用武之地,完全是资源的浪费。 还有没有别的办法?有的。阿里自研的分布式数据库OceanBase,从淘宝被划到支付宝后,已经沉寂了两年,正在焦急地寻找一展身手的舞台。 但是一听说是自研的数据库,业务满脸都是狐疑。跟交易和金额直接相关的数据库,只要错一个数据,后果就不堪设想,别说双11这么大的流量,即使平日,要不要用这个没经过验证的产品,也颇要斟酌一番。 先切1%的流量给OceanBase试试吧。这是大家争论了好一阵后得出的方案。 但是Oracle在压测中的表现显示,缺口不止1%,而是10%。 OceanBase说,我们来承接这10%。 10%,听起来不多,但双11的10%,相当于平日里的最高峰值。如果OceanBase能平安无事地接住这10%,就意味着它可以担起支撑支付宝日常运行的重任。 OceanBase必须证明自己有这样的能力。“我们找了淘宝的同学,协调了很多资源做了一个测试,主要校验淘宝订单的金额和支付宝交易金额是否能吻合。”DBA团队的工程师师文汇说,当时的方案很谨慎,如果OceanBase出现了问题,随时都可以切回来。 测试结果,OceanBase没有错漏一个数据。程立当即拍了板:10%都切给你们。 这个决定成就了OceanBase在双11的首秀,“相当于Oracle和鲁肃(程立的花名)都帮了我们一把。”师文汇笑着说。 这时距离2014年的双11,时间已经不足两周。可靠性虽然经受住了考验,但OceanBase毕竟是个诞生才四年的年轻数据库,小问题层出不穷,比如响应时间长达10毫秒,比Oracle差了好几个数量级。最后十来天,师文汇和全团队的同学一起,硬是把它优化到了1毫秒以下。 “做了这么些年,对它的容量和性能还是比较有信心的。”师文汇说。 他说得轻描淡写,但在这个曾经面临团队解散项目取消的产品身上,他和整个团队一起倾注了多少心血,除了他们自己之外,谁也说不清楚。 OceanBase最初并不是为双11而做的,但在双11这个舞台上,它第一次获得了聚光灯下的位置,并且表现卓越,从此,支付宝开启了核心交易系统完全搬迁上OceanBase的进程。 到今年,OceanBase对内100%承载蚂蚁业务的流量。对外,在被誉为“数据库领域世界杯”的TPC-C基准测试中,打破了由美国公司Oracle(甲骨文)保持了9年之久的世界记录,成为首个登顶该榜单的中国数据库产品。 我赢了一只apple watch 2015年,李俊奎去拜访了上海证券交易所,那里的交易系统部署在6台大型计算机上,交易峰值能达到每秒10万笔。 他大为惊叹:10万笔!何等高不可攀的数字!什么时候支付宝也能达到就好了! 回到杭州,他马上与同学们分享了这次见闻,结果同学们告诉他说,今年我们的目标就要超过每秒10万笔了。 李俊奎一想,这种一听就不可能的目标,是支付宝的作风,没毛病。 与此同时,郑洋飞则在为这个目标头痛不已。他刚刚跟他的主管打了一个赌,赌的是他作为2015年双11全链路压测的负责人,能不能保障双11的支付不出任何问题。赌注是一只apple watch。 这一年是90后的郑洋飞第一次挑大梁,从双11的参与者转换角色成为一个项目的主导者。但这一年也是他和团队都“忍辱负重”的一年,上半年,因为频繁不断的可用率问题,他们做稳定性的团队也在频繁遭受打击,士气不振,不少同学选择了离开,内外的质疑声也接连不断,那几个月,空气里都仿佛写满了“难熬”二字。 “当时团队没几个人了,但是人人都憋着一口气,想着一定要把双11这个事情搞好。”郑洋飞说,“就是不想让人觉得支付宝不行。” 局面有如背水一战,如果失败了,想要“翻盘雪耻”就要再等一年。因为双11每年只有一次,不仅是一年一度的大考,更是一年一度的舞台。按照系统部资深技术专家杨海悌的说法,“人人都想把自己一年的努力拿到双11去验证和展示,不让上还不高兴。” 跟2014年的全链路压测比起来,2015年主要要做几个方面大刀阔斧的改进:一是要从核心系统扩展到全部系统,二是要平台化,也就是打造一个全链路压测的平台工具,三是要跟整个集团的压测打通联动。 “老实说,非常忐忑。”郑洋飞心里没底。 当双11零点的洪峰扑面而来时,他已经忘掉了apple watch这回事。有一个压测没验证到的数据库定时任务,让曲线看上去不那么平滑。“稳定压倒一切”的宗旨之下,系统只要一抖,整个团队的心也跟着抖了起来。迅速排查的结果是,系统整体没出问题,但压测遗漏的一些细节,让结果不是那么完美。 “曲线不是特别好看。”他不无遗憾地说。 郑洋飞最终赢得了这只apple watch,但对于他而言,除了奖品之外,这只apple watch更有了别样的意义,时刻提醒他即使做了再充分的准备,也没有万无一失。 对“丝般顺滑”的追求永无止境 其实每一位支付宝工程师的心中,都有一条的“完美曲线”。 理想之中,它应该是这样的:双11零点,洪峰来了,曲线漂亮地攀升,没有骤升骤降,不要用频繁的抖动去折磨大家脆弱的神经。 如果要浓缩成一个词,那就是“丝般顺滑”。 但是,每一次为此而做的技术演进和架构变更进行到一定阶段,“你都会发现一开始可能设想得非常美好,但到了一定的规模之后,挑战就接二连三地来了。”杨海悌感叹道,“量变产生质变,这句话不是虚的。” 双11的“量”,早已一骑绝尘地进入前所未有的领域,2016年双11仅用了6个多小时,交易额就已超过2014年全天。这些年以来,都是自己在不断刷新自己的纪录。 在这样的量之下保障稳定,难度不止提高了一个数量级。 还记得2012年底制定的架构革命之“三年计划”吗?它真的持续了三年,这场最初是为了解决数据库连接数和机房限制而进行的架构革命,在三年的演进过程中,又衍生出很多其他的架构,比如异地多活、容灾,弹性的容量调度等,直到2016年,才算全部落地。这之中每一步的演进,都是为了让系统具备动态扩容能力,能够顺滑地进行弹性的扩展和伸缩。 “大”是考验,“小”也是考验。 师文汇体会最深刻的瞬间,不是2014年OceanBase一举成名的时刻,而是2016年一次小小的测试。在这个测试里,他发现了一个指标有一点异常——非常不起眼,2毫秒的偏差。 “2毫秒而已,如果在别的地方,很可能就会被判断为无关紧要,然后漏过了。”但他的团队中的一位小伙伴,非常认真地去检查了这个问题——万幸如此。后来事实证明如果不解决这个问题,那年的双11就会有大麻烦。 “即使资源不足、时间紧张、软件有各种不完善的地方,但我们的小伙伴不会放过任何一个问题。”师文汇感慨。 早些年,流量曾是实现完美曲线最主要的挑战,但是越到后来,随着业务的不断拓展,工程师们越来越清楚地认识到,稳定压倒一切不假,但技术更要着眼于未来。 2017年,是贺岩加入支付宝的第九年。这一年支付宝实现了离线在线混布,离线任务的大量闲置资源可以被用于在线任务,从而大大提升了资源的利用率。 “在一些小的场景中,这种效率提升能带来的节约可能不那么突出,但在我们这样的体量上,它所带来的就是不可估量的一整个未来。”贺岩说。 有了前人积淀这么多年的基础,面向未来的路,就越走越顺利了起来。 2018年双11,支付宝其实保障了两个大促,天猫的大促,和支付宝自己的“码上双11”等玩法。这一年的故障数比前一年下降了70-80%,首次实现大促全天平稳。 大队长李铮非常淡定:“说白了,就是我们把各种风险通过系统化或工程化的流程,控制得比较好。峰值出现的过程,也都在我们的预期之内。” 2019,则是双11的“云原生”元年。 如果说技术是像叠积木那样一层一层累积起来的,那云原生就是最下面的基础,打好了这层基础,上层的应用就像是站在了巨人的肩膀上,生来就具备了一系列强大的能力。业务无需再过多地担忧技术问题,只需要专注于业务代码即可。 点亮全世界 故事讲到这里,不知大家是否还记得,当年因为每秒两万笔的峰值目标而惊呼“不可能”的同学们。 当年,每秒两万笔是他们举全体之力奋斗半年才能冲上的高峰,而去年,每秒两万笔已经成为支付宝再日常不过的状况,随随便便,一秒钟的事。 这样的巨变真实发生了,只是身处当时当地,谁也没有想那么多。几乎每一位工程师都表示:“每年搞完双11,下一年的目标就出来了,然后我们就为着下一年的目标去进行相应的准备和努力。” 一个目标,又一个目标,在征服一个又一个“不可能”的过程中,曾经以为遥不可及的标高,都一一被甩到身后。当年高不可攀的每秒10万笔,今天看来不过小菜一碟。 只有当回头的时候才有所感觉,在某一天忽然发现,原来已经走出了那么远,原来已经攀登到了那么高的地方。 而当年那些惊呼不可能却又拼命将不可能变成现实的年轻人,已经纷纷长大,他们现在有着更多的从容淡定,上限在哪里没人知道,更大的可能是,没有上限。 流量数据的增长也早已不是双11技术保障的全部。更多复杂的业务和玩法,在技术的成果之中生长起来,反过来又为技术的发展提供动力。走出双11,它们还能走入很多的场景:新年红包、五福集卡…… ——或者走出支付宝和阿里巴巴。 那些由支付宝的工程师们创下的奇迹,正一一变成产品,服务更多的金融机构。至今已有数十家银行和金融机构用上了OceanBase,压测平台、云原生等技术也纷纷走向产品化,支付宝通过历年双11沉淀下的技术和经验,正在拉动整个中国的互联网金融科技一起飞奔。 ——或者走向全世界。 双11早已不仅是中国的双11,而是成为了一场全球的狂欢。与双11一起,技术也在走向全世界。 “不过要是说到我们的理想,那就是将来某一年双11,整个备战室空空荡荡,除了关公像之外,不需要有任何同学留守,智能化的系统能搞定一切问题,而我们只需要捧着茶杯或喝着酒,看着丝般顺滑的曲线。” 对于巩杰所展望的这种未来,现场就有同学笑了。“不可能吧!?”每年双11都如同打仗一般,救兵如救火。 但是谁说不可能呢?毕竟,他们是那样一群已经把太多的不可能变为现实的人。 原文发布时间为:2019-11-18作者: 蚂蚁金服科技本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
这是阿仁在阿里卧底的第11个年头了多少次阿仁试图从内部瓦解阿里巴巴但都因为各种原因失败了: 串通洋人的eBay,被淘宝的免费模式打败了;想制造付款信任危机,却被一个支付宝解决了;想用高价服务器拖垮阿里,没想到被王坚整出一个阿里云。 阿仁很苦恼当年的一头青丝如墨染如今已是人间仙境地中海再这么卧底下去不止账上的美元兑不了现怕是手上股票的价值都要超过王健林了 但阿仁时时刻刻都记着自己是一名卧底这一次,他从内部打探到一个非常爆炸的消息!阿里巴巴要做一件非常冒险的事这件事一旦砸了,可能这几年都抬不起头来 机会来了!阿仁兴奋地在天台上点了楼下的盒马外卖正看着手机钉钉突然弹窗了内线发来两个字:上云 这一定就是那件冒险的事了阿仁的脸逐渐舒展开来原先紧致的表皮还残留着没抹开的大宝SOD蜜 回想起卧底的时光阿仁第一次听说云还是在2009年王坚说服了马云要做云计算当时社会上的IT精英分为两派一派支持阿里的去IOE搞云计算,认为中国需要自主研发核心操作系统,也就是后来的“飞天”另一派则质疑云计算没有前景好好的系统放着不用为什么要自己整一个新的 当时阿仁就觉得这事要黄为什么?在云计算的自研路上,这群人太“孤独”了。在国内,没有任何前人的经验,完全是从0做起没有人知道飞天长什么样子没人敢说MySQL就能完全取代Oracle光是一个淘宝,上面就跑了上万个应用每个应用的底层环境都不一一相同要把这么多的东西都装到云计算的框子里工作量是难以想象的 比这更难的是革自己的命当时,阿里巴巴有80多个国内顶级的Oracle 工程师突然有一天,上头下了死命令:淘宝要放弃 Oracle,转投自研的数据库架构“你再说一句试试?兄弟们的前途在哪里?”阿仁还记得当年自己卧底成数据库工程师的台词。但阿里还是干了 不过,之后3年。阿里云和飞天依然没什么起色有段时间阿仁甚至以为自己成功了当时的飞天还在襁褓里第一个陪练的是阿里金融云“牧羊犬”飞天一边开发,一边为牧羊犬服务不负众望,BUG百出数据传输、计算稳定性、处理速度等等都不达标工程师必须24小时盯着系统有位奶爸工程师为了值夜班盯系统把自己小孩的哭声设成了闹铃 最困难的时候阿里云和金融云都要干不下去了阿仁还偷偷策划了一起“事件”让当时阿里金融创始人带着核心骨干跑到王坚办公室门口“讨个说法”无奈之下王坚派出了所有的技术工程师驻扎在阿里金融的现场加班开发然吐槽的声音依然不断: 人家的是云计算,我们家的是“人肉云计算”;人家的是“分布式计算”,我们家的是“分步试计算”。 让阿仁没想到的是,父爱打败了BUG2009 年春节过后阿里云发布了一次大版本升级。升级完成的一瞬间空气突然安静了飞天系统稳定得不像阿里云的作品……“秒级放贷”、“小额多次放贷”什么的,统统实现了 不过此时内部依然有很多人质疑阿里云阿仁趁机密谋,散发“阿里云要被撤掉”的谣言并让业务部门负责人参加高管会议的时候带上技术负责人只等高管一声令下,就争抢技术人才但马云的一句话,堵上了所有人的嘴:“我每年给阿里云投 10 个亿,投个十年,做不出来再说”阿仁又失败了 转折发生在2013年当时,阿里巴巴面临着一个世界级的难题以开源软件为基础的“云梯1”实现了4000台集群调度而纯自研“云梯2”还在1500台集群数量徘徊但这都无法满足业务的运行需要达到 5000 台服务器的调度才行,简称5K。这几年,阿里云一直在死磕5K虽然进展缓慢,但慢慢开始有化学反应了不仅质疑消失了那些技术大神这个时候一齐杀了进来正明、褚霸、多隆、长仁……最终,5K进入了最后的稳定性测试 测试当天,阿仁正好在机房值班振飞提出了一个真•钢铁直男的测试办法:拔电源“如果这种突然暴力断电都能撑得住,阿里云还有什么不稳定的呢?”阿仁反复问了三遍拉吗?拉吗?拉吗?最后才颤抖着双手拉下了电源 四个小时以后,机器陆续重启完毕系统自检一切正常数据毫发无损阿仁的后背已经被冷汗打湿他开始怀疑自己卧底的意义不知不觉阿仁经历了一场充斥着荷尔蒙的理想主义者的长征 一个钉钉消息再次将阿仁拉回了现实内线撤回了“上云”两字缓缓打出:“双11核心系统100%上云” 阿仁用大脑快速搜索了一遍又用google搜索了一遍这样级别的技术工程全球范围内绝无仅有 核心系统100%上云就是把自己的身价性命都放在云上这事连亚马逊、微软、谷歌都不敢做果然是有够冒险 2019年11月11日零点阿里巴巴总部作战指挥室里所有人紧张地盯着大屏幕路过的扫地阿姨对此习以为常她以为这和往年一样又是轻轻松松喝茶度过开心和愉快的一段时光但阿仁知道平静的背后是暗流涌动淘宝天猫的系统应用极其复杂光是购物车一个场景就关联了十多个后台和系统绝对是牵一发而动全身把这些系统全部搬到云上还要保证业务不中断相当于万米高空上,给时速900公里的飞机更换引擎难度不言而喻 时间一分一秒在度过大屏幕上一直显示着“无紧急任务”00点45分有人喊出了一声“扛住了!”接下来阿仁感受到了人生中最响亮、持续时间最长的一次鼓掌“他们竟然做到了”阿仁泪流满面 接下来的一些数字阿仁看到已经麻木了: 双11全天交易额2684亿订单创建峰值54.4万笔/秒实时计算处理峰值25.5亿次/秒 他只知道没有人能够阻挡这群技术人前进的脚步而自己的卧底之路还将继续 (完) 原文发布时间为:2019-11-14文章转载自 后园
阿里妹导读:今年的双11已经是阿里资深前端技术专家舒文来阿里的第11年,从应届生到双11前端PM,他一路升级打怪,实现了岗位上从P4到P9的晋升。这第11届双11顺利结束之际,他把在阿里这些年的成长经历做一个总结和分享,希望你能在他的故事中得到些许启发。 作者简介:舒文,来自淘系技术部前端团队。目前负责淘系(淘宝+天猫)的营销活动、互动的业务,也在阿里巴巴前端委员会主导搭建体系的技术方向。 P4:懵懂学生入行记 (2008.10-2009.12) 2008年10月,入职阿里巴巴日文站,以前端岗加入UED团队。日文站 ( http://www.alibaba.co.jp ) 的业务源于阿里巴巴国际站 - 提供在线的平台化服务,专注撮合中日贸易。 在那个前端的鸿蒙年代,页面重构工程师和Javascript程序员还是两个细分职位: 在阿里,前端、交互、视觉共同划属用户体验部门。 而在部份公司,有专门的重构工程师。 在日文站亦如是:现实情况是杭州和东京的团队,具备写JS能力的工程师均不多。不少同事专注在HTML/CSS领域,且专研极深。举个例子:在日文站,所有的HTML/CSS代码必须通过两个Lint工具:HTML文書の文法をチェックし、採点します、[The W3C Markup Validation - 这让代码的规范性更好外,也能获得更好的搜索引擎分析&索引权重。 随着业务发展,需求场景多且复杂起来,对Javascript开发能力要求就越高。同时,也激发了我的技术热情。除了看书,我迫切想参与其他事业部的前端交流会。感谢时任主管的支持,我开始主动跨部门沟通,将他山之玉和学习所得有了更多结合。慢慢地,在团队内部提出一些技术方案并逐渐应用到杭州/东京两个团队 (特别感谢最早时代的D2)。 在那个阶段,我几乎参与了所有较为复杂的项目。结合当年的热门大作《高性能网站建设指南》、Yahoo网站性能优化黄金法则,负责了主站的性能优化,取得了一些不错结果。 2009年10月获得了日文站优秀员工,年底顺利晋升到新的层级(P5)。 P5:从热血到成长(2010.01-2011.06) 2010年开始,业务略微调整,参与“日本买家获得”(Buyer Growth)。这个业务本质是通过平台技术优化(SEO、SEM等)获得更高的搜索引擎权重、提高Landing到转化。 在这期间,随着被认可度提升、持续的项目历练,我的自信心也得到了锻炼。除了参与更复杂的项目外,我开始主动对业务和技术体系做出一些提案(Proposal) - 包括参考Google Webmaster Guidelines进行技术优化、参与了各种SEO项目,提出了现在回想起来各种或靠谱或不靠谱的提案。非常感谢历任主管的支持和包容,让一个楞头青能够没有后顾之忧地敢想敢做。 日文站初版克隆自Alibaba.com,加之外包参与、跨东京和杭州多团队开发等原因,全站前端框架混杂,包括不限于YUI/Mootools/PrototypeJS/ExtJS/TBra等前端框架&类库混杂在同一业务甚至页面。新人上手、多人协作、性能优化的成本很高,但历史原因积重难返,大家苦不堪言。 结束一个大项目后,时值“愤青”的我对全站的代码做了个调研和分析,说服了主管们,决定启动一个重构项目——旨在将全站混杂的代码统一到 jQuery。并大体确定了执行方案:随业务迭代上线、项目成员1人,我兼任PM、开发/自测。 在之后,一个如同打了鸡血般的前端程序员,白天黑夜的翻历史PRD、熟悉代码逻辑、阅读各种类库的手册,陆续将各业务线的脚本重写了一遍,边做业务项目边重构,整个项目持续了半年多,直到11年Q1完成。 现在回想起来,有些事情真需要一股冲动,正如买房、结婚。如果主管让我再多考虑半天,我兴许就怂了。当然,人生也可能走入另外一条主线。 2011年日文站业务合入B2B,我也参加并通过新事业部的年中晋升(至P6 ) 。 2011年阿里巴巴日文站誓师大会 P6:内部创业 + 前端的无线爆发 (2011.07-2014.06) 两段非常宝贵又特别的经历。 2011年下半年主动选择加入阿里内部的创业项目:打造一款面向个人消费者的云产品。 团队不大,20多人,各路专家云集,各负责人级别都很高。很享受这段工作旅程,我第一次参与桌面Hybrid端项目,第一次开发SPA Web应用,第一次参与跨桌面软件/PC Web/H5的项目,第一次领略敏捷管理的魅力。在来自美国的技术Leader带领下,我接触到很多新领域,极大地开阔了技术视野。 “创业失败是要承担风险的,没有加薪,没有晋升,而且还是996,直到项目成功”。第一次面试这个项目前,业务负责人,阿里工号16的虚竹告诉我。 不算幸运的是,最终这个项目没有取得如预期的成功,业务有了变化和调整。 但幸运的是:这段经历让我亲身体验了一把“创业”,也感受了技术以外的方方面面艰难。这个过程中形成的产品和业务思维,对我日后带来了深刻影响。当然,也在我之后几百次想离职创业时,这段经历让我能够静下心来思考创业的内核 - “为什么要创业?我有什么资源、能做什么事情、创造什么价值?” 忘了说,这段经历之所有特别,还因为办公地点在湖畔花园——马老师家。 湖畔花园办公场地 2012年7月,加入到天猫的前端团队。 主管给予了充分的信任,让我负责天猫H5首页的研发,开始接触淘宝前端—— 一个更成熟完整的技术体系。 2012年底,@三七 加入天猫带领前端团队,作为国内前端领域最早的拓荒者之一,他在随后的两年时间给天猫的前端技术带来体系化的变革——从模块化到工程化再到生产环境的NodeJS,并引入了Mobile First理念。在他的支持下,我带领了一支前端小组陆续参与多条业务线的开发工作。我自己也加入集团级技术建设,主导跨端Web的项目,推进了前端体系的移动化建设。 除专业技术外,花了比原来更多的功夫在团队工作上,包括不限于团队氛围、风格和文化的打造,学会在内网分享中学习借鉴前辈大拿的的管理方法论。在阿里的文化中,非常强调传承。一个人牛不算什么,让一群人厉害才算厉害,最好是让下属比自己还强。 时间花在哪儿,结果就会在哪儿。团队内一些业务尖兵逐渐冒出头来,在后来几年成为天猫甚至大淘系的中坚力量。 这个过程既痛苦又快乐。痛苦在于要做越来越多的超过自己能力层级、远离舒适区的工作,快乐的则是能明显感知到自己在这个过程中的快速成长。 很幸运,在14年年中,顺利通过晋升(P7)。 P7:从双十一中打将出来 (2014.08 - 2016.01) 2014年注定是难忘的一年。在这一年,我开始负责营销活动的业务,并担任2014年双十一前端技术PM工作。 营销活动是一个顶有趣、富有故事的业务: 从技术上:它可以极致简洁到切一个页面上线就行,也可以复杂到如双11一般 ——它是阿里技术的年度大考。 从业务上:它既可以简单到直接把现实生活的促销活动进行虚拟世界的进行流程投射,也能如互联网史上的春运般整合零售生态和供应链。 我和多个团队在天猫制定了各个维度的技术规范,如内控标准、外包规范,有惊无险地渡过了14年双十一。 在PC时代,营销活动的研发模式,对于前端来说,实在过于“简朴”:“5到6位正式前端常年带着数十个外包,根据运营需求开发成百数千的页面,通过一个叫做TMS的运营系统预留坑位给运营同学填数据,交由后端应用推上线(CDN)。” 这类业务活动频次高、页面量大、协同成本高。某种意义上讲,对前端技术挑战并不高,为了更高效的研发,我们能做的是不断提高组件和模块复用率。 感谢TMS,它帮助前端们快速迭代出一个又一个页面,支持了集团的业务,即使在今天,它的设计思路都能称得上精巧犀利。 发生变化来自2014年双十一前,线上发生了一次令我终生难忘的离奇故障(涉及敏感信息,略过细节):当时因临时未排查出原因,VP现场观摩面,我和CDN运维团队的同学们能做的只是不断重启集群应用缓解问题。 我作为时任前端PM,也因技术方案选择失误受到批评责罚。(时隔多年,大家也偶尔调侃,责罚不冤,幸而那天的问题提前暴露而未发生在11.11号当天,否则后果难料 )。 那年双11最终还是完美谢幕,GMV 571亿,移动端成交42.6% ,很多人敏锐的感觉到:移动化时代到来了。 之后技术复盘,我们针对问题做了多轮讨论和推演,决定启动一个代号“斑马”的产品项目:基于我们对营销的理解,运营的实际需求、过往的技术痛点,设计一个高效的页面系统来支持运营快速发布活动上线,更重要的是它是完全的基于Mobile First设计的系统。 正所谓 “一饮一啄,莫非前定” ,因为过去一年的打磨锤炼、痛过后的反思,我们很快把方案框架敲定,并和多个业务搭档共同推进项目实施。 项目于15年初立项,我代表团队“激进”的将阶段目标定为“支撑2015年3月的春季大促” 。这种既要支撑日常高频的大促业务,又要同时从0到1做平台研发,不啻于“飞行中升级引擎”,困难很大。但我背后的思考是:“这就如同创业,没有一往无前的决心和一鼓作气的气势,等想多了细了,多半就怂了怕了”。 我负责整个系统及PM工作,团队培养出来的中坚力量们承担各个功能模块研发。为实现我们心中所定计划,整个项目组如同创业般激情,为了一个共同的目标奋进。春节假期不少同学还在主动提交代码,节后迅速返回公司继续项目。 很幸运,克服了重重困难,“斑马”项目顺利上线,并成功支持了2015年第一个S级大促。基于新的系统,我们共同把营销活动送上新的轨道:因模块化带来的效率提升让我们完全解除了外包依赖、因具备跨端能力运营们可以单次同时进行PC和无线活动、我们设计并推动了CDN构架,单在15年双11就节省了数千万预算。 随着技术迭代,“斑马”这个技术产品,在后面几年成为阿里的基础运营平台之一。 2015年5月,我参与了P8的晋升提名,结果未通过。Leader和我沟通:“成长很快,能力和规划出色,业务成果需要时间验证”。我很坦然接收了建议,晋升答辩中的技术评委的建议中肯不失公正,让我受益很大。 “但行好事,莫问前程”——在随后的时间,“斑马”的能力更为完备。我再次担任了2015年双11的前端PM工作,业务也顺利上线。那一年,双11当天GMV 912亿,移动端成交68%。 16年1月,新主管告诉我,绿色通道通过,晋升至P8。 双十一令牌/虎符 P8:立足业务,敢想敢造 (2016.02 - 2019.07) 一次偶然的机会,看到一张图:如遵循民意苹果手机会变成什么样?给我带来一些对业务和技术的思考:“页面发布系统本质上是一个通用的工具平台:前端/运营/设计师都是它的用户,每个角色都有对它的功能需求。但每个新增功能在解决一类用户需求的同时又增加了平台复杂度,降低的客户的易用性,丢失掉另一类用户的民心。” “这兴许是一个产品的轮回宿命”,我内心有些敬畏地想。带着这个敬畏,时间到了2017年,随着斑马的功能日趋复杂,我决定将运营操作平台和底层技术能力进行剥离: 将斑马定义为运营系统,专为运营打造极致的页面搭建。 将底层能力抽象成服务平台(代号“天马”),提供开放的研发标准、搭建方案、页面渲染能力。 随着时间推移,“天马”的开放能力不断完善,越来越多的事业部基于它构建面向业务的平台系统,带来的技术回报是:技术复用减少从0到1的研发&硬件成本、标准统一使跨业务协同变为可行。 而在技术的另外一边,业务发生着悄无声息的变化:16年双11GMV 1207亿、17年双11则是1682亿。每年双十一在如此巨大的基数下还能持续业务高增长,个性化算法、推荐技术的大规模应用起到了非常重要的作用。在全局效率上,大数据优于人工干预逐渐得到了共识。在我看来,这些变化意味着时下大家认可的大促会场模式,也将带来新的变化。 2018年2月,我在内部做了个分享,描述了对营销活动未来的预判: 业务化:面向“活动”组织,而非页面。 产品化:构建常态化、心智化的大促产品矩阵,而非单纯是模块级的抽象。 智能化:投放算法化和规则化,而非运营的动态化。 基于以上预判,我和团队的小伙伴,计划联同业务、上下游技术团队共同构建一个面向营销活动的平台产品(项目代号:方舟)。 很感谢历任主管实仙/四虎的信任,愿意帮我顶住重重压力,经过多轮沟通,我们达成了一致共识。 和过去几年稍有不同的是,我逐渐把包括不限于方舟的一些重要产品放手给团队中的骨干/TL,让他们去规划、去驱动、去变革。我更多做了辅导性、资源性、业务判断的全局性工作。 随着时间的推移,越来越多的结果开始凸现: 斑马逐渐成为集团级的通用平台,数万小二同学用它发布管理业务。 方舟作为业务平台交付成为大促体系的核心部分,并成功应用到双11期间,在效率、效果和交付质量上取得瞩目的结果。 天马通过“开放共建、出海上云”,为集团多个BU提供基础的技术搭建服务。 在业务中,团队中沉淀出一个个技术产品:渲染引擎、API聚合网关等,也把曾经仅服务于内部的产品开放到商家、ISV。 在2018年中,提名参加晋升面试,结果未通过,技术委员会的评委们建议更多参与“集团级的技术建设、更高格局的视野”。 我很快走出了沮丧,毕竟在阿里,常胜不常有失败倒常伴。更重要是,主管 @四虎 的开导让我开始思考更全局的未来。 随后的时间,我继续投入了2018的双11前端整体工作,推进了多个端技术方案落地天猫。感谢阿里前端技术委员会主席圆心的举荐,我加入前端委员会,担任搭建技术方向的Sponsor - 旨在站在集团视角,定制标准、融合并打通搭建技术体系,在更大范围内赋能业务。 在集团内各个前端Leader的支持下,我们很快跨多个事业群达成了共识,制定完善了技术标准,基于一体化方案启动了多个层面的项目。 2019年5月,参加了年度晋升,顺利通过P9晋升。感恩。 P9:不止于前端 现在,仍然有太多的事情值得去深入: 以双11为代表的大促前端体系仍然是业内最具技术挑战的业务场景之一,包括不限于客户端容器技术、服务端渲染(Node)、框架与组件体系、跨终端技术等综合应用。 阿里有数以亿计的消费者,如何为我们的消费者构建一个好玩有趣的互动购物体验,是我们这个团队一直需要探索和改进的。 在未来,源自于前端的搭建技术,不仅能支持小二还能服务生态角色,不仅能支持国内还要服务全球。 除以上,我也深度参与阿里前端技术委员会的工作: 在阿里的前端体系,除了搭建方向以外,有相当数量的跨事业群共同建设的技术方向&项目,包括不限于Serverless、IDE、智能化、中后台、数据可视化、工程化、Node技术等。这些技术方向或者始于前端,但又不止于前端,共同为打造行业领先的技术生态服务。 有期待有更多的优秀的同学加入阿里,让我们的智力和努力触碰到数以十亿计的用户。欢迎加入联系我:wenliang.shuwl [at]alibaba-inc.com 最后分享一些过往些年的的心得: 关于工作: 1.想做好一件事情前,获得上级的认可非常非常重要,这是把一件事情做成、做好的催化剂。 2.很多时候,源自“事”的困难都可以用态度解决,来自“人”的困难可以用换位思考解决。 3.对于刚参加工作的同学,如果家中无矿,啥也别说,努力就对了!毕业五年内的表现,可以决定人生很多事情。 4.如果你和领导发生分歧:拿出你的数据/理由/态度尽最大的努力去说服他,如果充分沟通还形不成共识,那就先听他的 。这不是媚上,而是“在必须达成一致的前提下,相信更有概率做出正确决定的人。” 5.在工作中,如果某个时间突然发现和往常不一样,陡然压力变大/身心疲惫,别害怕 ,因为,这有可能是你在成长、突破瓶颈前的黑暗期。 6.专业技能是立身之本。从长期来看,它是性价比最高的投资标的之一。 7.结合资源,尽最大的努力、用最好的态度,做好手上的工作,也是一种创业。 8.不要因为背靠大树久了,就误认为自己是颗大树。对客户&用户的尊重和价值创造是我们很大的护城河。 关于生活: 1.技术人读书工作挣钱养家,一步步成长同时也会一岁岁变老。相比不少行业,互联网行业自身有赛道上的优势,但如果单纯以同比其他行业略高的溢价按年/月出售自己的时间和技能,这不应该是我们做的。坚持不断的思考、通过技术优势带来叠加价值,在过程中不断成长 , 这兴许会更好。 2.学会奖励自己。如果经受了辛劳、痛苦和压力走了过来,再不好好的、肉痛地奖励自己一把,哪对得起过去和未来的自己。 3.除了在工作中,人生处处皆可学习:学理财、学打球、学拍照、学游泳、学健身、学拍短视频、学习怎么把生活过得更好。 4.最后,咱们技术人可能很忙,有可能没法“work-life balance”,这是一种取舍。但健康的身体一定是最宝贵的一笔财富,没有之一,不能舍。谢谢大家。 原文发布时间为:2019-11-15作者:舒文本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:文娱内容很难有完整的量化指标体系,内容的复杂性决定了文娱产品的不确定性。我们如何利用AI+大数据能力,建造文娱的内容认知大脑?串联内容全生命周期,实现对内容、流量、宣推等的“未卜先知”,提升对爆款的预测和生产能力?这本《5G+AI 阿里文娱技术实践》会告诉你。 全新电子书《5G+AI 阿里文娱技术实践》正式上线!通过窄宽高清革新、大麦物联网技术、6DoF视频技术等5大板块,带你全面了解阿里文娱独具魅力的思考。点击文末下载链接,即可下载。 一、文娱产业趋势及技术挑战 文娱内容不像商品有完整的量化指标体系,它是一个复杂的实体,它跟意识形态以及用户体验强相关,对内容进行量化评估和衡量是非常困难的。 比如,选角儿。我们不能通过单一指标去衡量一个演员,我们需要综合考量演员的演技、气质、颜值、潜力等与否与某一个角色匹配,并且能生成数据指标,以实现纵横向的对比。另外,导演、主演组盘是否为最优组合,能否成为爆款?这是更加复杂的选择模式问题。今天面临的技术挑战是如何进行知识的抽取、挖掘以及推理,确定什么样的组合是最优解。 除上述两个问题,影片的拍摄过程更是一个庞大的系统工程和艺术创作过程。以《长安十二时辰》为例,该片非群演有约1000人,群演有300到1500人,历时7个月拍摄217天。我们参考软件工程行业,软件工程发展了70年,主要研究三个层面:方法论、过程以及工具,然后是如何将三者组合。软件行业的敏捷开发对于软件工程的质量和效率都有非常大的提升,如何将这些理论应用到内容制作产业,让内容制作敏捷起来? 内容敏捷即知晓过程对结果造成的影响是什么,并快速地调整内容创作过程,让它更敏捷。但内容行业面临的独有特点“延迟满足”,让用户在内容的某一分钟特别嗨,可能来自于前面的30分钟铺垫在那一分钟爆发了,针对内容的这个特点,我们除了要做基本的知识图谱语义的理解之外,还要考虑如何去做有效的对应分析,如何去做对应的知识抽取等问题。 今天这个问题加剧了,比过去还要复杂。在过去的5到10年里,UPGC加上整个内容的生产量极大的发展,用户的消费分层化、多样化。全民爆款越来越少,用户对内容的需求更加个性化。相应于内容生产端,就需要考虑不同用户群的个性化需求。 二、文娱大脑基本框架:内容认知新动力 针对上面几大困难,我们今天在做文娱大脑——优酷北斗星智库来解决。我们将所有的内容形式和用户消费的数据都采集下来,将人工智能的技术手段、业务领域的细分理论做整合融合,构建内容认知框架。 内容认知框架分为两部分,内容和用户。其思路就是心理学发展的基本的思路。 1)内容侧:对内容进行理解,包括外延和内涵。外延就是内容的各种基本属性,比如主创阵容、题材类型等;内涵主要研究内容的戏剧理论和视听语言,围绕制作内容的支撑要素,我们用传统的机器学习方式对内容进行理解,再基于戏剧理论和视听语言构造内容的衡量要素。 2)用户侧:分析用户的观看行为。用户行为来自于用户的心理偏好、心理情绪。用户心理偏好、心理情绪来自于生理构造,基于心理学的五大人格理论和用户的观看行为,构建模型建立左边和右边的连接,从而知道创造什么样的内容,用户会有什么样的感受。 三、贯穿全生命周期的文娱大脑生产力 基于内容认知框架,我们在内容生命周期的每个阶段都做了具体工作:开播前提供内容评估、艺人挖掘和内容情绪挖掘等能力;在早期为内容评估提供有效的数据支撑;在制作阶段提供现场解决方案,比之前更敏捷的反馈机制;同样在播出后也提供数据支持,实现更好的宣发。 1、IP/剧本分析 上图是《长安十二时辰》的分析示例,我们把已有的剧本作为样本,让机器去学习,识别出剧本的所有角色,把角色直接交互的对白、行为识别出来,再进行社团的划分。《长安》剧本最终划分出来几个群体:反恐防暴小分队以张小敬为中心,唐朝核心管理团队以皇上为中心。通过这种方式快速定位整个剧本的人物和人物关系的展开。 2、用户情绪识别与成片情绪挖掘 围绕角色关系,将整个剧本的角色情绪也识别出来,构造成如上的曲线。基于对海量剧本的分析曲线,抽取出各个指标(出镜率、戏份、情绪值等)并形成benchmark,对于之后的每一个剧本进行衡量,相当于对剧本进行一个“体检”。 同样是“体检”的方法,对于《药神》和《长安十二时辰》,我们做了用户情绪的识别、体检的扫描,参考零线的位置。我们发现《药神》几乎都是正向和负向级的,直到最后出现一个正向区间,基本上后期都是以眼泪为主。而《长安十二时辰》的情绪状态比较稳定。对照情绪高低点的具体情节,我们发现,曲线表达的情绪和具体的故事情节是非常相符的。 3、情绪强度预测与网络收视率 然后我们拿更多的方式去验证它的合理性,上图抽取《长安十二时辰》的剧集,每集有两条曲线,蓝线是刚才预测的情绪曲线,黄线是播放指数(表示每一秒钟有多少用户在看),通过两条曲线对比,我们可以发现,两条曲线的相关性比较高的将近60%,情绪的高峰、低谷和用户的观看行为状态是吻合的,由此我们就提供了一种能力,基于这种能力对剧本或影片做情绪扫描,实现对影片热度的未播先知,再对比benchmark,帮助制作者更高效的完成制作。 4、用户情感曲线在技术上是如何实现的? 首先,我们把用户观影情绪的表述,映射到认知计算中常用的二维空间表示,也就是Valence 和Arousal。Valence表示情绪正负极性,Arousal表示情感激烈程度; 其次,基于情绪极性跟强度提供一个预测,这个是我们今年产出的论文。近两年,心理学研究的核心观点是为什么用户会感同身受?这来自于前两年的一个理论——静向神经元,所以我们选择场景、表情、动作以及声音作为基本的模型的输入,对模型参数进行学习。 如上所讲,内容产业有强延迟满足的问题,我们通过两层分析来解决长短期满足的问题,除用户情绪分析,我们也做内容角色的情绪识别。通过图片表情识别模型,识别不同题材类型的影片,可以获得不同角色刻画的人物性格。如2004年的《反贪风暴》,时隔十多年,主创人物形象的脸谱还是正向的。上图显示的负面角色情绪以开心、害怕为主,正面形象以悲伤、生气为主,与负面反派的开心正好相对,正面的人一直很沮丧,是一个有些压抑角色形象。 同样,我们分析角色的每秒情绪,形成角色的正负情绪曲线,部分影片的分析结果曲线如上图,不同题材类型的节目会有不同的情绪密度。所以,你想放松的时候,要看的不一定是喜剧,喜剧其实不一定会放松,因为角色的正负向情绪不停交替,由于延迟满足,大脑负荷非常大,需要做长短记忆,反而很多爱情片对大脑的占用相对低。 角色情绪检测是一个分类问题,所以利用人脸landmark对初始图像做识别,生成densemap作为附加通道,和原始图片RGB三通道拼接合并后作为模型输入,这样可以使densemap对应的关键区域权重更大,更容易让模型捕捉关键区域特征;合成的输入送入到Reduced Xception 网络进行特征提取;在loss方面,我们引入了基于SVM的marge loss,提升各情绪类别的类间差距,提升情绪识别的效果,具体如上图。 基于前面对内容的各种理解产生的各种纬度的内容的量化纬度,我们构建了预测模型,可以提前预测出节目的流量走势,如内容认知框架中所讲的,首先对内容进行量化,然后对内容相应的量化纬度进行提前的预测,为业务决策提供辅助支撑。 最后,分享我对未来趋势的一些见解。在强人工智能尚遥远的情形下,如何结合机器AI和人工经验将是个永恒主题。一是结合符号学派智能和链接学派智能,建设和完善决策引擎,包括结合人工逻辑规则和可学习数据AI,不确定性分析框架和经久不衰的贝叶斯因果决策,以及神经元化的混合智能计算框架。二是量化的心理学研究也越来越重要,如何结合大数据应用价值非常大。这也是阿里文娱大脑探索的方向。 从优酷窄宽高清革新布局到大麦物联网的实践之路从文娱内容认知的AI大脑到交互式的6DoF视频技术5G时代来临如何用新技术提升用户体验?《5G+AI 阿里文娱技术实践》正式上线!点击立刻下载 原文发布时间为:2019-11-14作者: 牧己本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
11月,阿里巴巴西溪园区夜凉如水,双11作战室内,却有人身着短袖衬衫,只见她屏住呼吸,握紧微微出汗的手心,静静立于数字大屏前,等待今年最重要的一个数据。 距离双11结束,还有1分钟。 午夜的钟声敲响,最终,双11作战室的数字大屏将数据定格在2684亿。 在一片欢呼声中,她激动地回到工位上,打开贴着关二爷神像的电脑,开始奋笔疾稿。这个人就是我——为阿里技术粉丝带来深夜报道的阿里妹。当我敲下发送键,心里依旧不能平息,毕竟我也是参与了千亿项目的人了。 突然,一阵倦意袭来,似有狂风在周身涌动不止。等我睁开眼睛,只见面前有一人,他身长九尺,髯长二尺,面若重枣,唇若涂脂,丹凤眼、卧蚕眉,相貌堂堂,威风凛凛。像极了我电脑上的关二爷神像,再看他时,面前突然多了一盏西湖龙井。他说要与我讲讲阿里双11…… 这到底是一个动物园…还是万国博览会?? 变化就变化吧,修炼成精我也能接受。问题是这些人许的愿望… 和这群人的相知相遇相对无言,是我数千年职业生涯里最懵逼的经历… 事情是这样开始的。 呃,这群人自己愿意骨折,商家们似乎并不愿意。当时只有27个商家愿意参加打骨折活动,然而24小时以后… 经此一役…… 就像有人天天喊着剁手还是买个不停,这群说着再也不想干的人,其实并不死心… 在我老人家的庇佑下,他们的生意自然是一发不可收拾~ 为了平稳扛住这个危机,这群人一天到晚疯狂开会……往死里开会…… 竟然还在开会……显然我当时也搞不懂这些云里雾里的词汇…我怀疑这些人自己也不大搞得明白吧? 但他们继续摸爬滚打了好几年… 等等,还没看见硝烟, 这仗就打完了??「分布式架构」就这么厉害?我也来试试… 自「分布式架构」以来,作战室里的人越来越少了… 不可思议的是,这群人的战斗力反而越来越强。 他们很不像话地在战场上开起了趴体… 很不像话地饕餮重口味的夜宵… 条漫来源:怪物旅行社微信公号 难道您最喜欢的语言是C++?正当我话欲出口之时,他却挥来一片云彩,亮得睁不开眼,恍恍有下坠之感。 当光影消退,睁开双眼,看到的是作战室里工程师火红的战袍,原来是梦一场。至于梦见了什么,却是想不起来了,好像是想谁问是不是喜欢C++吧。 原文发布时间为:2019-11-13本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:Kubernetes 大幅降低了容器化应用部署的门槛,并以其超前的设计理念和优秀的技术架构,在容器编排领域拔得头筹。越来越多的公司开始在生产环境部署实践。本文将分享蚂蚁金服是如何有效可靠地管理大规模 Kubernetes 集群的,并会详细介绍集群管理系统核心组件的设计。 系统概览 Kubernetes 集群管理系统需要具备便捷的集群生命周期管理能力,完成集群的创建、升级和工作节点的管理。在大规模场景下,集群变更的可控性直接关系到集群的稳定性,因此管理系统可监控、可灰度、可回滚的能力是系统设计的重点之一。除此之外,超大规模集群中,节点数量已经达到 10K 量级,节点硬件故障、组件异常等问题会常态出现。面向大规模集群的管理系统在设计之初就需要充分考虑这些异常场景,并能够从这些异常场景中自恢复。 设计模式 基于这些背景,我们设计了一个面向终态的集群管理系统。系统定时检测集群当前状态,判断是否与目标状态一致,出现不一致时,Operators 会发起一系列操作,驱动集群达到目标状态。 这一设计参考控制理论中常见的负反馈闭环控制系统,系统实现闭环,可以有效抵御系统外部的干扰,在我们的场景下,干扰对应于节点软硬件故障。 架构设计 如上图,元集群是一个高可用的 Kubernetes 集群,用于管理 N 个业务集群的 Master 节点。业务集群是一个服务生产业务的 Kubernetes 集群。SigmaBoss 是集群管理入口,为用户提供便捷的交互界面和可控的变更流程。 元集群中部署的 Cluster-Operator 提供了业务集群集群创建、删除和升级能力,Cluster-Operator 面向终态设计,当业务集群 Master 节点或组件异常时,会自动隔离并进行修复,以保证业务集群 Master 节点达到稳定的终态。这种采用 Kubernetes 管理 Kubernetes 的方案,我们称作 Kube on Kube 方案,简称 KOK 方案。 业务集群中部署有 Machine-Operator 和节点故障自愈组件用于管理业务集群的工作节点,提供节点新增、删除、升级和故障处理能力。在 Machine-Operator 提供的单节点终态保持的能力上,SigmaBoss 上构建了集群维度灰度变更和回滚能力。 核心组件 集群终态保持器 基于 K8s CRD,在元集群中定义了 Cluster CRD 来描述业务集群终态,每个业务集群对应一个 Cluster 资源,创建、删除、更新 Cluster 资源对应于实现业务集群创建、删除和升级。Cluster-Operator watch Cluster 资源,驱动业务集群 Master 组件达到 Cluster 资源描述的终态。 业务集群 Master 组件版本集中维护在 ClusterPackageVersion CRD 中,ClusterPackageVersion 资源记录了 Master 组件(如:api-server、controller-manager、scheduler、operators 等)的镜像、默认启动参数等信息。 Cluster 资源唯一关联一个 ClusterPackageVersion,修改 Cluster CRD 中记录的 ClusterPackageVersion 版本即可完成业务集群 Master 组件发布和回滚。 节点终态保持器 Kubernetes 集群工作节点的管理任务主要有: 节点系统配置、内核补丁管理; docker / kubelet 等组件安装、升级、卸载; 节点终态和可调度状态管理(如关键 DaemonSet 部署完成后才允许开启调度); 节点故障自愈。 为实现上述管理任务,在业务集群中定义了 Machine CRD 来描述工作节点终态,每一个工作节点对应一个 Machine 资源,通过修改 Machine 资源来管理工作节点。 Machine CRD 定义如下图所示,spec 中描述了节点需要安装的组件名和版本,status 中记录有当前这个工作节点各组件安装运行状态。除此之外,Machine CRD 还提供了插件式终态管理能力,用于与其它节点管理 Operators 协作,这部分会在后文详细介绍。 工作节点上的组件版本管理由 MachinePackageVersion CRD 完成。MachinePackageVersion 维护了每个组件的 rpm 版本、配置和安装方法等信息。一个 Machine 资源会关联 N 个不同的 MachinePackageVersion,用来实现安装多个组件。 在 Machine、MachinePackageVersion CRD 基础上,设计实现了节点终态控制器 Machine-Operator。Machine-Operator watch Machine 资源,解析 MachinePackageVersion,在节点上执行运维操作来驱动节点达到终态,并持续守护终态。 节点终态管理 随着业务诉求的变化,节点管理已不再局限于安装 docker / kubelet 等组件,我们需要实现如等待日志采集 DaemonSet 部署完成才可以开启调度的需求,而且这类需求变得越来越多。 如果将终态统一交由 Machine-Operator 管理,势必会增加 Machine-Operator 与其它组件的耦合性,而且系统的扩展性会受到影响。因此,我们设计了一套节点终态管理的机制,来协调 Machine-Operator 和其它节点运维 Operators。设计如下图所示: 全量 ReadinessGates: 记录节点可调度需要检查的 Condition 列表; Condition ConfigMap: 各节点运维 Operators 终态状态上报 ConfigMap; 协作关系: 外部节点运维 Operators 检测并上报与自己相关的子终态数据至对应的 Condition ConfigMap; Machine-Operator 根据标签获取节点相关的所有子终态 Condition ConfigMap,并同步至 Machine status 的 conditions 中; Machine-Operator 根据全量 ReadinessGates 中记录的 Condition 列表,检查节点是否达到终态,未达到终态的节点不开启调度。 节点故障自愈 我们都知道物理机硬件存在一定的故障概率,随着集群节点规模的增加,集群中会常态出现故障节点,如果不及时修复上线,这部分物理机的资源将会被闲置。 为解决这一问题,我们设计了一套故障发现、隔离、修复的闭环自愈系统。 如下图所示,故障发现方面,采取 Agent 上报和监控系统主动探测相结合的方式,保证了故障发现的实时性和可靠性(Agent 上报实时性比较好,监控系统主动探测可以覆盖 Agent 异常未上报场景)。故障信息统一存储于事件中心,关注集群故障的组件或系统都可以订阅事件中心事件拿到这些故障信息。 节点故障自愈系统会根据故障类型创建不同的维修流程,例如:硬件维系流程、系统重装流程等。 维修流程中优先会隔离故障节点(暂停节点调度),然后将节点上 Pod 打上待迁移标签来通知 PaaS 或 MigrateController 进行 Pod 迁移,完成这些前置操作后,会尝试恢复节点(硬件维修、重装操作系统等),修复成功的节点会重新开启调度,长期未自动修复的节点由人工介入排查处理。 风险防范 在 Machine-Operator 提供的原子能力基础上,系统中设计实现了集群维度的灰度变更和回滚能力。此外,为了进一步降低变更风险,Operators 在发起真实变更时都会进行风险评估,架构示意图如下。 高风险变更操作(如:删除节点、重装系统)接入统一限流中心,限流中心维护了不同类型操作的限流策略,若触发限流,则熔断变更。 为了评估变更过程是否正常,我们会在变更前后,对各组件进行健康检查,组件的健康检查虽然能够发现大部分异常,但不能覆盖所有异常场景。所以,风险评估过程中,系统会从事件中心、监控系统中获取集群业务指标(如:Pod 创建成功率),如果出现异常指标,则自动熔断变更。 结束语 本文主要和大家分享了现阶段蚂蚁金服 Kubernetes 集群管理系统的核心设计,核心组件大量使用 Operator 面向终态设计模式。这套面向终态的集群管理系统在今年备战双 11 过程中,经受了性能和稳定性考验。 一个完备的集群管理系统除了保证集群稳定性和运维效率外,还应该提升集群整体资源利用率。接下来,我们会从提升节点在线率、降低节点闲置率等方面出发,来提升蚂蚁金服生产集群的资源利用率。 Q & A Q1:目前公司绝大多数应用已部署在 Docker 中 ,如何向 K8s 转型?是否有案例可以借鉴? A1:我在蚂蚁工作了将近 5 年,蚂蚁的业务由最早跑在 xen 虚拟机中,到现在跑在 Docker 里由 K8s 调度,基本上每年都在迭代。K8s 是一个非常开放的 “PaaS” 框架,如果已经部署在 Docker 中,符合“云原生”应用特性,迁移 K8s 理论上会比较平滑。蚂蚁由于历史包袱比较重,在实践过程中,为了兼容业务需求,对 K8s 做了一些增强,保证业务能平滑迁移过来。 Q2:应用部署在 K8s 及 Docker 中会影响性能吗?例如大数据处理相关的任务是否建议部署到 K8s 中? A2:我理解 Docker 是容器,不是虚拟机,对性能的影响是有限的。蚂蚁大数据、AI 等业务都已经在迁移 K8s 与在线应用混部。大数据类对时间不敏感业务,可以很好地利用集群空闲资源,混部后可大幅降低数据中心成本。 Q3:K8s 集群和传统的运维环境怎么更好的结合?现在公司肯定不会全部上 K8s。 A3:基础设施不统一会导致资源没有办法统一进行调度,另外维护两套相对独立的运维系统,代价是非常大的。蚂蚁在迁移过程中实现了一个“Adapter”,将传统创建容器或发布的指令转换成 K8s 资源修改来做“桥接”。 Q4:Node 监控是怎么做的,Node 挂掉会迁移 Pod 吗?业务不允许自动迁移呢? A4:Node 监控分为硬件、系统级、组件级,硬件监控数据来自 IDC,系统级监控使用内部自研监控平台,组件(kubelet/pouch 等)监控我们扩展 NPD,提供 exporter 暴露接口给监控系统采集。Node 出现异常,会自动迁移 Pod。有些带状态的业务,业务方自己定制 operator 来实现 Pod 自动迁移。不具备自动迁移能力的 Pod, 超期后会自动销毁。 Q5:整个 K8s 集群未来是否会对开发透明,使用代码面向集群编程或编写部署文件,不再需要按容器去写应用及部署,是否有这种规划? A5:K8s 提供了非常多构建 PaaS 平台的扩展能力,但现在直接面向 K8s 去部署应用的确非常困难。我觉得采用某种 DSL 去部署应用是未来的趋势,K8s 会成为这些基础设施的核心。 Q6:我们目前采用 kube-to-kube 的方式管理集群,kube-on-kube 相比 kube-to-kube 的优势在哪?在大规模场景下,K8s 集群的节点伸缩过程中,性能瓶颈在哪?是如何解决的? A6:目前已经有非常多的 CI/CD 流程跑在 K8s 之上。采用 kube-on-kube 方案,我们可以像管理普通业务 App 那样管理业务集群的管控。节点上除运行 kubelet pouch 外,还会额外运行很多 daemonset pod,大规模新增节点时,节点组件会对 apiserver 发起大量 list/watch 操作,我们的优化主要集中在 apiserver 性能提升,和配合 apiserver 降低节点全量 list/watch。 Q7:因为我们公司还没有上 K8s,所有我想请教以下几个问题:K8s 对我们有什么好处?能够解决当前的什么问题?优先在哪些业务场景、流程环节使用?现有基础设施能否平滑切换到 Kubernetes? A7:我觉得 K8s 最大的不同在于面向终态的设计理念,不再是一个一个运维动作。这对于复杂的运维场景来说,非常有益。从蚂蚁的升级实践看,平滑是可以做到的。 Q8:cluster operator 是 Pod 运行,用 Pod 启动业务集群 master,然后 machine operator 是物理机运行? A8:operator 都运行在 Pod 里面的,cluster operator 将业务集群的 machine operator 拉起来。 Q9:为应对像双十一这样的高并发场景,多少量级的元集群的规模对应管理多少量级的业务集群合适?就我的理解,cluster operator 应该是对资源的 list watch,面对大规模的并发场景,你们做了哪些方面的优化? A9:一个集群可以管理万级节点,所以元集群理论上可以管理 3K+ 业务集群。 Q10:节点如果遇到系统内核、Docker、K8s 异常,如何从软件层面最大化保证系统正常? A10:具备健康检查能力,主动退出,由 K8s 发现,并重新在其它节点拉起。 原文发布时间为:2019-11-13原文作者:沧漠本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
“不是任何一朵云都能撑住这个流量。中国有两朵云,一朵是阿里云,一朵叫其他云。”11月11日晚,阿里巴巴集团CTO张建锋表示,“阿里云不一样,10年前我们从第一行代码写起,构建了中国唯一自研的云操作系统飞天。” 阿里巴巴集团CTO、阿里云智能总裁张建锋 2019天猫双11再次刷新世界纪录,订单创新峰值达到54.4万笔/秒,单日数据处理量达到970PB。今年阿里巴巴核心系统100%上云,撑住了双11的世界级流量洪峰。 “阿里巴巴是一架高速飞行中的飞机,我们成功在此过程中换上了全新的引擎。”张建锋说,用公共云来承载这样一个万亿规模的核心系统,阿里云是第一个做到的,“很多云厂商自己的业务系统,不在自己的云上,今后阿里全部系统都在阿里云上。” 阿里巴巴是全球首家将核心系统100%运行在公共云上的大型互联网公司。 附:张建锋演讲核心观点 从去年开始,我们决定把整个阿里巴巴核心系统全部上云,上云还要加一个定语,是上“阿里云”,并不是上任何一朵云都能撑得住这个流量。在中国,我们经常说中国有两朵云,一朵叫做“阿里云”,还有一朵叫做“其它云”。 为什么讲阿里云跟其它云不一样?因为在中国只有阿里云是完全从头研发的一朵云,从头是从每一行代码,从十年前开始写,今年是阿里云的第10年,所以我们的云有一个特别的名字,叫做“飞天操作系统”,当初王坚博士创办阿里云的时候,对我们这个云下面的调度系统,整个云的基础平台,取名叫做“飞天操作系统”。 我们的云完全是中国自研的一朵云,其它很多云从开源软件改造过来的,这是非常大的不一样。 第二个,今年双11,阿里仍然是一家在高速飞行中的飞机,我们成功的在这个过程中换上了全新的引擎。以前大家把非核心负载放在云上,现在阿里巴巴最核心的系统放在了云上。现在我们的云把原来专用的技术变成公共云,大家都可以来享受普惠服务。我们有最好的系统,你们也可以在上面跟我们一样使用。 阿里云承载阿里巴巴自己100%的核心系统,这个是我们全球第一个做到的。很多云厂商自己也有业务系统,但是自己的系统不在自己的云上。我们以后所有阿里系统全部都在阿里云的飞天操作系统上面。 大家一定会想,这是一个简单的替换还是技术上非常大的一个进步、非常大的挑战?我简单说一下,我们有几个方面做得非常好,它不是简单的替换,上云之后性能有了一个非常大的提高。 今天大家可能有感觉,我们前十分钟,基本上消费者没有感受到任何抖动,大家购物非常顺畅。 第一、我们在核心虚拟机系统上,自研神龙架构,用自研的服务器来做虚拟化。一般的服务器随着压力增长,最终负载能力会慢慢下降,它不是线性的。我们神龙服务器压力越大,输出也是非常线性的,这是非常难做到的。 第二、我们自研了云原生的数据库。今年有两个数据库,一个是自研的OceanBase,大家知道我们在TPC-C测试里面拿下了全球第一。第二是PolarDB,也广泛用在这次双11上,没有任何问题,我们每秒峰值也是远远超越于原来Oracle这样传统的数据库。 第三、我们的计算与存储做了分离,现在阿里巴巴存储是有一个地方专门存数据。数据都是从远端存取的,所以存储可以很方便的扩容,因为它有一个专门的池。原来做不到。因为原来远端存储,访问网络速度就跟不上。 第四、之所以现在能够做到在远端存储,能够比本地读写磁盘更快,是因为我们做了RDMA网络,这是全球第一个大规模做RDMA网络的公司。 我们这四个方面都做了核心突破。 今天,飞天操作系统能够在10万台级别以上的服务器里面做调度,今天双11有非常多的应用,但是它并不是说每一个时间点,所有应用负载都是一样的。我们在零点把大部分计算机资源分配给交易等应用,过了一点之后,我们把数据分配给数据处理系统。数据处理今年创了一个新高,2017年当天处理了300PB的数据,2018年处理了600PB的数据,今年要处理970P左右的数据。 大家可能没有概念,一个P到底有多少数据,我上次跟央视的人交流,央视拍了这么多年电视新闻节目,几十年下来他存了大概80P的数据。双11一天,我们要处理970P的数据,非常大的量,没有一个先进的系统是撑不住的。 第二个是非常实时,大家看到了,今天在秒杀,在会场全部都是个性化、千人千面的,这里的数据不仅是大,而且是要做到非常实时。到目前为止,我们菜鸟物流系统已经产生了超过10亿笔的物流单,这个数据还在很快的上涨,这些都需要依靠阿里云背后大量的计算能力。 我们今年的数据,除了批处理之外,还有流处理,就是实时处理所有数据,就像你们看到的交易数据,每分钟、每秒钟都在变,它并不是从数据库里面统计出来的,它是每生成一笔订单,系统自动一层层把数据汇集上来。我们这个系统每秒能处理25亿笔记录,这是流式的系统。 我们这么多服务器,要把它全部管理起来,除了飞天系统之外,还要管理所有的消息流转,所以我们自己开发了一个叫做MQ的消息系统,这也是全球目前最大的一个消息系统。 今天,从飞天系统、大数据处理平台到智能化应用,这些技术叠加起来打造了一个新的分布式的基于云的平台,才使得阿里整个经济体所有核心应用都能够跑在上面。 最后,我们今年发布了自研的芯片含光800。明年双11,阿里大量人工智能应用都会跑在自研芯片上面。 今天从飞天云操作系统到神龙服务器、数据库、交换机、交换机操作系统、RDMA网络,全部是阿里自研的。我们今天已经积累了非常丰富、非常强的能力,从硬件、数据库、云计算操作系统,到上面的核心应用平台,四位一体,这是这次双11跟往年最大不一样的地方。 所有不可想象,终将化作寻常;我们相信“相信”,一切都是新的。双11快乐! 原文发布时间为:2019-11-11本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
2019 天猫双1196秒成交额破100亿 全球最大流量洪峰每秒订单峰值54.4万笔 阿里巴巴核心系统 100%上云 我们扛住了! 1心1役把不可能变成可能今晚为阿里工程师疯狂打call 在这一年一度的购物大狂欢里,阿里巴巴全球总部“穿上新衣”,以最美好的姿态迎接它。 天猫、盒马、平头哥、考拉、蚂蚁、飞猪……从最初的淘宝网到如今的数字经济体。今天,我们“1心1役”迎接双11。 专属阿里工程师的火红战袍穿起来,据说,在这只猫面前许愿,愿望都会实现哦。今年,阿里工程师的心愿不是“剁手”,而是在岗位上扛住洪峰,守护好大家的“狂欢”。 稳定压倒一切,双11当然要“稳!稳!稳!”。 “稳”不光靠说,我们还有坚实的维稳储备。战袍、定胜鼓、大麦给工程师们稳稳的信心,健康包(提神液、润喉糖、眼罩……)提供稳稳的精神,双11美食餐当然是稳稳的体力。 定胜鼓敲起来 没精神了,来个健康包 定胜操,给工程师们大大的鼓励 阿里技术人面对第11个双11,因为技术,他们更从容。每一年双11破纪录的交易额背后,是更多世界顶尖技术的诞生。今年,阿里园区是五彩斑斓的,技术却是“黑”的。今年双11有哪些黑科技?分别是哪些技术上的新突破?且听阿里妹细细道来。 01 手机AR试装,“变脸”比京剧演员还快 天猫美妆频道推出AR试妆产品,让你用手机轻轻松享受线下商店的“换脸”体验。通过AI+AR技术让用户看到不同商品在自己脸上的不同的效果,几十种口红色号任你切换,李佳琪再也不用一管接一管地往嘴上试色号了。 02 淘宝直播延时2秒以内,智能识别商品 淘宝直播自研了全新的音视频实时通信架构,实现了大规模低延时直播,从原先5-7秒的延时降低2秒内,几乎察觉不到延时,让主播和用户的互动更高效。同时引入AI技术,可以实时识别主播正在讲解的商品,商品链接会直接出现在直播页面中,点击链接就可以马上下单哦。 03 最会说话的人工智能:阿里小蜜 阿里小蜜是智能对话机器人平台,实现7*24的可自然对话交互的智能助理体验。 2018年双11那天,阿里小蜜日活跃用户突破5000万,那天1分钟内最高服务量达到8.3万起,接了淘宝天猫平台98%的在线服务需求,相当于10万名人工客服小二的工作量。店小蜜当天在线咨询对话量达到创纪录的3.5亿次。 今年的阿里小蜜又出新功能了:直播小蜜。今年双11,淘宝直播已经成为商家标配,有人说:“现在还不开直播的店铺,就跟没有详情页一样。”进出直播间的剁手党成千上万,问什么的都有,但主播一个人根本不可能一一回答这些问题。于是,“直播小蜜”出现了,能给主播打下手,还能回答主播问答、商品问答、优惠券问答、吸粉闲聊哦。 04 一天设计600万张图片的AI设计师“鹿班” 鹿班为机智过人节目嘉宾设计的海报 鹿班是个“设计师”,能在短时间内完成大量banner图、海报图和会场图的设计。你只需任意输入想达成的风格、尺寸,鹿班就能代替人工完成素材分析、抠图、配色等耗时耗力的设计项目,实时生成多套符合要求的设计解决方案。 2018天猫双11期间,已经为20万企业、商家设计近600万张图片,你打开天猫看到的各种海报和活动商品图片几乎都出自鹿班。它还能每秒可以提供几十种方案,每秒设计8000张不同的海报。在双11大促中,鹿班提供稳定高效的一键抠图,主图批量打标和定时投放等大促必备功能,帮助客户提高图片更新换汰效率。 05 顶级AI翻译官:同时翻译64种语言方向 双11前,达摩院升级了机器翻译技术,帮助速卖通平台上的中国及全球中小卖家,将产品详情与评论自动翻译成当地语言,实现了实时AI语言翻译的客户服务与业务咨询,涵盖64种语言方向的高质量机器翻译,让全球超过100万家中小企业沟通无障碍。 06 把服务器“泡在水里”的真·黑科技 2016年,阿里云推出了全球首台“凉得快”服务器,为了给数据中心降温,工程师直接把服务器“泡在水里”——这是一种极其高效的散热方式:浸没式液冷。服务器被浸泡在一种特殊的绝缘冷却液里,运算产生热量可被冷却液直接吸收进入外循环冷却,全程用于散热的能耗几乎为零,整体节能可达70%以上。 2019 双11黑科技助力你们只管尽情狂欢,我们负责保驾护航! 原文发布时间为:2019-11-11本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:近几年人工智能、机器学习等词漫天遍地,似乎有一种无AI,无研发,无AI,无测试的感觉。有人说:不带上“智能”二字,都不好意思说自己是创新。我们先暂且不评论对错,只探讨这背后值得我们思考的问题。 在测试领域,人工智能和测试是什么关系?为什么测试领域会谈及人工智能?如果测试工程师不懂AI,是否有未来,测试人员该如何看待“AI测试”?在软件质量保障中到底应该如何循序渐进的切入这一话题?业界在此领域目前现状是怎样?带着这些问题,阿里高级测试开发专家汪维希望借此和大家做一些交流和探讨。 测试发展变革史 借用一幅图先让我们快速来回溯一下测试变革所经历的几个不同的时期,从最早期的纯手工测试,随着整个IT技术的发展,测试也历经了不少的变革,每一次变革我们不难发现侧重点都有所不同。 从最初的验证软件的可工作状态,到强调释放生产力的自动化诉求,从封闭式的自动化能力到基于社区模式的开放式能力建设,再到从更加全面的研发流程体系来构建的持续集成的自动化能力,我们不难发现每次变革背后似乎都有一个核心词在推动,那就是“效率”。但这个效率又有所不同,就是不同阶段对于效率在逐渐从单点效率往系统性效率迈进。 如果我们认为前边四个阶段都是基于规则为核心的测试,而未来则会打破这种模式,推动这个核心改变的模式可能主要来源两个方面,第一是研发技术的升级,第二是研发模式的更加敏捷和分布式开发,这两者都打破了以规则为核心的测试理念。 因为我们可能面对更多的研发人员,更复杂的研发场景,更复杂多变的应用系统,在此基础上便催生了对于软件测试新的思考,那便是如何让软件测试变得更加的“Smart”,这便是我们正在经历的时代,不过很不幸的是,我们可能大多数情况下测试还不够“Smart”,很有可能我们在某些情况下我们还处于“1980-1990”的时代,我想这也是测试人员之痛。 图片来源:https://becominghuman.ai/ai-in-testing-the-third-wave-of-automation-cfdd43f55d9c 如今测试发展面临的主要挑战 对于软件测试而言,其实互联网的发展和兴起对软件测试的发展带来了巨大的挑战,这不得不从本质问题说起,相对互联网时代之前的传统IT时代,软件通常研发周期较长,软件功能庞大,软件更新频率较低,软件是作为支撑企业业务发展的配套设施,之所以叫配套设施,也就是对于企业而言及时没有这个配套设施,业务发展依然可以进行,无非是管理效率可能会受到一些影响,而互联网时代,其本质上软件本身就是企业的商业模式的核心能力,不再仅仅是一个配套设施,而是核心设施,核心能力,其直接决定了在复杂多变的商业环境中是否具备核心竞争力。 因此对于软件无论是在研发模式、交付模式上都提出了更高、更快的要求,“敏捷”研发思想和模式应运而生,敏捷的本质是为了获得更快的Go To Market的能力,从而让企业能获得更快的商机,在敏捷模式下,本身是一种好事,这种模式下需要软件更快的交付能力,而不是等着专业的软件测试人员慢吞吞的进行功能验证。 如果不是等着专业的软件测试人员进行测试,那还能谁来参与测试?开发人员?但是开发人员测试自己的软件还并没有成为主流,大多数开发人员不会写测试来测试自己的代码,他们选择手工测试或者等待专业的测试人员来测试他们的软件,从而保证软件可正确运行。 这正是测试面临的挑战,如何能让研发能参与测试?很不幸的是,目前AI在此领域还不能帮助太多,但也并非完全不能做什么,在理解这个问题之前,我觉得有一个很好的问题,就是我们不妨来思考一下自动化测试的6个层次与人工智能的关系。 人工智能测试的六个层次 什么是自动化测试的6个层次?这6个层次是我目前看到的对于AI和自动化测试相对清晰的一个抽象,先简单介绍一下这6个层次的来源,这是由Applitools 的高级架构师 Gil Tayar在 Craft Conference 2018上介绍他们如何将 AI 技术应用到自动化测试的内容中提到的6个层次,分别为: 层次一 完全没有自动,你需要自己写测试! 层次二 驾驶辅助——AI 可以查看到页面,帮助你写出断言。你还是要自己写“驱动”应用程序的代码,但是 AI 可以检查页面,并确保页面中的期望值是正确的。在这种模式下,软件测试工程师需要自己用传统技术解决流程驱动的问题,但无需在脚本中做Expectation的校验或者无需用脚本方式写Check Point,而把校验的工作交由AI来完成,AI技术在此过程中核心起到辅助的作用。 层次三 部分自动化——虽然能分辨实际页面和期望值的区别这一点已经很好了,但是第二层次的 AI 需要有更深层的理解。比如说,如果所有页面都有相同的变更,AI 需要认识到这是相同的页面,并向我们展示出这些变更。 进一步来说,AI 需要查看页面的布局和内容,将每个变更分类为内容变更或是布局变更。如果我们要测试响应式 web 网站,这会非常有帮助,即使布局有细微变更,内容也应该是相同的。这是 Applitools Eyes 这样的工具所处的层次。在这种模式下,AI逐渐具备了贯穿上下文的能力,如果相对层次二而言,层次二停留在”点“上,层次三模式下的AI已经具备了”线“的辅助能力。 层次四 条件自动化——在第三层,软件中检测的问题和变更仍然需要人来审查。第三层的 AI 可以帮助我们分析变更,但不能仅仅通过查看页面判断页面是否正确,需要和期望值进行对比才能判断。但是第四层的 AI 可以做到这一方面,甚至更多其他方面,因为它会使用到机器学习的技术。 比如说,第四层的 AI 可以从可视化角度查看页面,根据标准设计规则,例如对齐、空格、颜色和字体使用以及布局规则,判断设计是否过关。AI 也能查看页面的内容,基于相同页面之前的视图,在没有人工干预的情况下,判断内容是否合理。在这种模式下,AI逐渐具备了自我学习的能力,能从”面“上进行辅助自动化,但这实现起来非常的困难,目前相对不够成熟。 层次五 高度自动化——直到现在,所有 AI 都只是在自动化地进行检查。尽管使用自动化软件,还是需要手动启动测试,需要点击链接,而第五层的 AI 可以自动启动测试本身。AI 将通过观察启动应用程序的真实用户的行为,理解如何自己启动测试。这层的 AI 可以编写测试,可以通过检查点来测试页面。 但这不是终点,它还需观察人的行为,偶尔需要听从测试人员的指令。在这种模式下,相对前边的几种层次,这个层次的AI已经摆脱了人工”驱动“的模式,核心改变就是从人工”驱动“发展为”AI“驱动,如果说前边几种模式还需要测试人员编写流程驱动脚本,而在这种模式下,测试人员将摆脱这一束缚。 层次六 完全自动化——我必须承认,这个层次有点恐怖。这个层次的 AI 可以和产品经理“交流”,理解产品的标准,自己写测试,不需要人的帮助。这种模式可能是我们所希望追求的最高境界,或许发展到这个阶段,测试这个岗位需要重新被定义。 运用场景 AI技术在测试领域的运用并非新鲜话题,但业界对此讨论的一些方向也值得我们思考和探索AI和ML(机器学习)技术能如何被运用到测试场景,常见的三种运用场景包括: Unit Tests 单元测试对于确保每一次Build都能构建出稳定和具备可测性的软件非常重要,但单元测试的构建和维护本身也面临很大的挑战,在业界例如像RPA这样的AI-Powered Unit Test工具,试图帮助开发人员来更加有效的维护单元测试用例,利用AI技术对代码进行分析和学习,从而有效的减少那些无用的用例集,从而维护一个更加可靠和稳定的单元测试用例库。 API Testing 在敏捷开发模式下,测试人员会面临常态化多变的UI界面,此时针对系统API(接口)的测试其有效性和效率可能会大于UI自动化测试,在此领域有非常多的一些使用AI技术的工具能帮助测试人员对手工UI测试自动转换为API测试,从而帮助组织更加高效的构建起复杂和完善的API测试策略。 UI Testing 目前对于UI自动化测试主要思想主要还是如何把手工测试用例转换为自动化测试用例,AI技术在此场景下目前大多被运用在结果识别以及多场景的适配测试领域,从而降低对UI自动化的维护和运行成本。 业界在AI测试领域的解决方案 针对上述提到的运用场景和不同的六个层次,目前业界在此领域也有非常多的AI Powered Testing Tools,我们可以快速做一个了解(工具排名不分先后)。 Applitools 这是一个运用了AI技术的Visual Testing解决方案,他运用AI技术智能化识别UI界面上那些有价值性的改动,并主动识别其是否是潜在的BUG或者是有意义的改动而并非BUG,从而让自动化脚本的维护从规则化升级为智能化,例如下图中我们可以看到应用的图标位置发生了改变,该工具能自动识别这种变化,其主要主打方向是软件测试的Look & Feel领域,或者我们可以叫用户体验领域。 用该公司自己的话来说其核心价值如下,从其官方价值不难看出,其主要解决的问题是在软件UI影响用户体验的领域,比如像视窗存在遮挡,界面元素颜色、大小、位置可能存在问题等,这对于一些非常重视用户对软件产品体验方面的领域还是具有一定的价值,而这些领域的测试如果用传统的基于规则的自动化,实现成本和维护成本会非常巨大。 Appvance IQ Appvance公司出品的解决方案,官方宣传口号“The Only True AI-Driven Software Test Automation Technology Create 1000's of regression tests in minutes”,翻译过来大致的意思是这是一个真正的AI驱动的自动化测试解决方案技术,该技术能在1分钟内瞬间产生1000个左右的回归测试用例,从官宣口号中不难可以看出,其主打的是“效率”二字,核心希望解决回归测试的痛点,该公司也提出了一个5层自动化模型,这5层模型和前边提到的6层模型其实有异曲同工之处。 Eggplant 该工具获得2019 SIIA CODiE WINNNER(Best DevOps Tool Digital Automation Intelligence Suite),该工具的Eggplant AI功能号称能自动创建Test Case,并优化测试执行来发现更多的BUG,其提出的测试覆盖率思想提出了一个“User Journeys”的思想相对有些有趣,官方有这么一段介绍“Eggplant AI automatically generates test cases and optimizes test execution to find defects and maximize coverage of user journeys”,其实这里的Customer Journey也即是我们常常说的不同的测试场景,为了达到对于Customer Journey的覆盖,其核心实现逻辑抽取出了Model和Tag的概念,前者是Journey建模,后者实际是数据驱动。 Customer Journey图片来源:http://docs.testplant.com/ Journey建模图片来源:http://docs.testplant.com/ Test.AI 这是业界比较知名的两本书籍(《How Google Tests Software》、《App Quality: Secrets for Agile App Teams》)编写团队所创建的一个AI自动化测试平台,其核心能力是将AI大脑添加到Selenium和Appium的工具来提升其智能化能力。 MABL 一帮前Google工程师创办的企业,主攻领域就是提供End-To-End的端到端测试解决方案,AI也是其中很重要的方向,MABL具备自动检测测试对象的变化并动态更新测试脚本的能力。在传统的自动化测试中,可能UI界面的类型变化可能会阻塞脚本执行,而MABL具备自动识别的机制和能力来缓解这类问题。 Sealights 从官方的宣传口号来看,不难看出,其核心定位是利用AI技术做质量管理和质量分析和其他几个的定位略有不同,主要用户主要针对R&D Manager,所以我们可以理解为其核心解决的不是测试自身的问题,而是偏管理方面的问题,利用智能化技术针对此领域希望能更加智能的给予决策人员更加准确的决策信息,提高决策效率。 Sealights图片来源:https://www.sealights.io/ Sealights质量趋势智能分析图片来源:https://www.sealights.io/ ReportPortal 从名字上不难看出,这款工具主要是聚焦在测试结果分析和管理方面,这一点和Sealights有些类似,主要基于测试执行的数据利用AI和ML技术进行挖掘,来快速评估新的风险。 ReportPortal产品图片来源:https://reportportal.io/ Functionlize 该解决方案主打AI自动化领域,其核心能力是其所为的AEA(Adaptive Event Analysis)技术,该技术能自动发现case执行过程中的Broken问题,并自动修复,从而让你的用例Never Break And NO More Test Maintenance,其利用ML技术的智能识别号称覆盖以下一些UI场景,如果在你的测试中有涉及下边这些的Change,利用AEA技术可以自动识别更自动更新测试脚本,无需人工干预: Size of Element Locaiton on Page Previous sizes and locations Visual configurations Xpaths CSS Selectors Parent and child elements Visibility 除了上述提到的这些目前业界已有的解决方案以外,还有很多厂商也在自己现有的工具能力中注入了AI和ML的能力,不过从上述几个中我们不难发现,目前业界在测试领域使用AI和ML技术大致可以分为几类: 利用Computer Vision(计算机视觉)技术对测试结果进行辅助检测,对于检测的结果要么用于结果判断,要么用于更新脚本。 利用Natural Language Porcessing(自然语言处理)技术对测试对象进行分析,或者对测试数据进行分析,从而进行测试决策辅助和脚本优化。 利用ML(机器学习)技术或者深度学习技术,对采用CV和NLP技术所获得的数据进行深度加工,从而来解决自动化脚本Break,或者快速创建大量自动化脚本的目的。 小结 在我看来AI技术的发展应该是测试人员需要重点关注的领域,我们往往会因为有些技术可能当下并不成熟,或者当下并没有很好的落地场景,从而忽略对未来技术的关注度,在测试领域对于AI的探索也是如此,同时不难发现在业界其实已经有非常多的公司已经在自己的商业化解决方案中注入了AI能力,这种趋势也是值得我们持续关注,最后我个人比较推荐在AI领域的落地和时间可以尝试从本文提到的6个层次模型中去由浅入深的探索,这有利于在AI和测试的道路上有层次的循序渐进。 原文发布时间为:2019-11-8作者:汪维本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:好的系统架构离不开好的接口设计,因此,真正懂接口设计的人往往是软件设计队伍中的稀缺型人才。为什么在接口制定标准中说:一流的企业做标准,二流的企业做品牌,三流的企业做产品?依赖倒置到底是什么意思?什么时候使用接口才算合理?今天,阿里匠人——张建飞将为你详细解读。 接口有什么好处(Why) 在我看来,接口在软件设计中主要有两大好处: 1. 制定标准 标准规范的制定离不开接口,制定标准的目的就是为了让定义和实现分离,而接口作为完全的抽象,是标准制定的不二之选。 这个世界的运转离不开分工协作,而分工协作的前提就是标准化。试想一下,你家的电脑能允许你把显卡从NVIDIA换成七彩虹;你家的灯泡坏了,你可以随便找一个超市买一个新的就可以换上;你把数据从Oracle换成了MySQL,但是你基于JDBC写的代码都不用动。等等这些事情的背后都是因为接口,以及基于接口定制的标准化在起作用。 在Java的世界里,有一个很NB的社区叫JCP( Java Community Process),就是专门通过JSR(Java Specification Request)来制定标准的。正是有了JSR-315(Java Servlet),我们服务端的代码才能在Tomcat和Jetty之间自由切换。 最后,我想用一句话来总结一下标准的重要性,那就是:“一流的企业做标准,二流的企业做品牌,三流的企业做产品。 2. 提供抽象 除了标准之外,接口还有一个特征就是抽象。正是这样的抽象,得以让接口的调用者和实现者可以完全的解耦。 解耦的好处是调用者不需要依赖具体的实现,这样也就不用关心实现的细节。这样,不管是实现细节的改动,还是替换新的实现,对于调用者来说都是透明的。 这种扩展性和灵活性,是软件设计中,最美妙的设计艺术之一。一旦你品尝过这种“依赖接口”的设计来带的美好,就不大会再愿意回到“依赖实现”的简单粗暴。平时我们说的“面向接口编程原则”和“依赖倒置原则”说的都是这种设计。 另外,一旦你融会贯通的掌握了这个强大的技巧——面向抽象、面向接口,你会发现,虽然面向实现和面向接口在代码层面的差异不大,但是其背后所隐含的设计思想和设计理念的差异,不亚于我篮球水平和詹姆斯篮球水平之间的差异! //面向接口 Animal dog = new Dog(); //面向实现 Dog dog = new Dog(); 作为一名资深职场老兵,我墙裂建议各位在做系统设计、模块设计、甚至对象设计的时候。要多考虑考虑更高层次的抽象——也就是接口,而不是一上来就陷入到实现的细节中去。要清楚的意识到接口设计是我们系统设计中的主要工作内容。而这种可以跳出细节内容,站在更高抽象层次上,来看整个系统的模块设计、模块划分、模块交互的人,正是我们软件设计队伍中,非常稀缺的人才。有时候,我们也管这些人叫架构师。 什么时候要用接口(When) 有扩展性需求的时候 可扩展设计,主要是利用了面向对象的多态特性,所以这里的接口是一个广义的概念,如果用编程语言的术语来说,它既可以是Interface,也可能是Abstract Class。 这种扩展性的诉求在软件工作中可以说无处不在,小到一个工具类。例如,我现在系统中需要一个开关的功能,开关的配置目前是用数据库做配置的,但是后续可能会迁移到Diamond配置中心,或者SwitchCenter上去。 简单做法就是,我直接用数据库的配置去实现开关功能,如下图所示: 但是这样做的问题很明显,当需要切换新的配置实现的话,就不得不扒开原来的应用代码做修改了。更恰当的做法应该是提供一个Switch的接口,让不同的实现去实现这个接口,从而在切换配置实现的时候,应用代码不再需要更改了。 如果说,上面的重构只是使用策略模式对代码进行了局部优化,做了当然更好,不做的话,影响也还好,可以将就着过。 那么接下来我要给大家介绍的场景,就不仅仅是“要不要”的问题,而是“不得不”的问题了。 例如,老板给你布置了一个任务,实现一个类似于eclipse可以可插拔(Pluggable)的产品,此时,使用接口就不仅仅是一个选择问题了,而是你不得不使用的架构方法了。因为,可插拔的本质就是,你制定一个标准接口(API),然后有不同的实现者去做插件的实现,最后再由PluginManager把这个插件机制串起来而已。 下图是我当时给ICBU设计的一个企业协同云的Pluggable架构,其本质上,也就是基于接口的一种标准和扩展的设计。 需要解耦的时候 上面介绍的关于Switch的例子,从表面上来看,是扩展性的诉求。但不可扩展的本质原因正是因为耦合性。当我们通过Switch Interface来解开耦合之后,扩展性的诉求也就迎刃而解了。 发现这种耦合性,对系统的可维护性至关重要。有一些耦合比较明显(比如Switch的例子)。但更多的耦合是隐式的,并没有那么明显,而且在很长一段时间,它也不是什么问题,但是,一旦它变成一个问题,将是一个非常头痛的问题。 一个真实的典型案例,就是java的logger,早些年,大家使用commons-logging、log4j并没有什么问题。然而,此处一个隐患正在生长——那就是对logger实现的强耦合。 当logback出来之后,事情开始变得复杂,当我们想替换一个新的logger vendor的时候,为了尽量减少代码改动,不得不上各种Bridge(桥接),到最后日志代码变成了谁也看不懂的代码迷宫。下图就是我费了九头二虎之力,才梳理清楚的一个老业务系统的日志框架依赖情况。 试想一下,假如一开始我们就能遇见到这种紧耦合带来的问题。在应用和日志框架之间加入一层抽象解耦。后续的那么多桥接,那么多的向后兼容都是可以省掉的麻烦。而我们所要做的事情,实际上也很简单——就是加一个接口做解耦而已(如下图所示): 要给外界提供API的时候 上文已经介绍过JCP和JSR了,大家有空可以去阅读一些JSR的文档。不管是做的比较成功的JSR-221(JDBC规范)、JSR-315(Servlet规范),还是比较失败的JSR-94(规则引擎规范)等等。其本质上都是在定义标准、和制定API。其规范的内容都是抽象的,其对外发布的形式都是接口,它不提供实现,最多会指导实现。 还有就是我们通常使用的各种开放平台的SDK,或者分布式服务中RPC的二方库,其包含的主要成分也是接口,其实现不在本地,而是在远程服务提供方。 类似于这种API的情况,都是在倒逼开发者要把接口想清楚。我想,这也算微服务架构一个漂亮的“副作用”吧。当原来单体应用里的各种耦合的业务模块,一旦被服务化之后,就自然而然的变成“面向接口”的了。 通过依赖倒置来实现面向接口(How) 关于依赖倒置,我以前写过不少文章,来阐述它的重要性。实际上,我上面给出的关于扩展需求的Switch案例,关于解耦的logger案例。其背后用来解决问题的方法论都是依赖倒置。 如上图所示,依赖倒置原则主要规定了两件事情: 高层模块不应该依赖底层模块,两者都应该依赖抽象(如上面的图2所示) 抽象不应该依赖细节,细节应该依赖抽象。 我们回头看一下,不管是Switch的设计,还是抽象Logger的设计,是不是都在遵循上面的两条定义内容呢。 实际上,DIP(依赖倒置原则)不光在对象设计,模块设计的时候有用。在架构设计的时候也非常有用,比如,我在做COLA 1.0的时候,和大多数应用架构分层设计一样,默许了Domain层可以依赖Infrastructure层。 这种看起来“无伤大雅”的设计,实际上还是存在不小的隐患,也违背了我当初想把业务复杂度和技术复杂度分开的初心,当业务变得更加复杂的时候,这种“偷懒”行为很可能会导致Domain层堕落成大泥球(Big mud ball)。因此,在COLA 2.0的时候,我决定用DIP来反转Domain层和Infrastructure层的关系,最终形成如下的结构: 这样做的好处是Domain层会变得更加纯粹,其好处体现在以下三点: 1、解耦: Domain层完全摆脱了对技术细节(以及技术细节带来的复杂度)的依赖,只需要安心处理业务逻辑就好了。2、并行开发: 只要在Domain和Infrastructure约定好接口,可以有两个同学并行编写Domain和Infrastructure的代码。3、可测试性: 没有任何依赖的Domain里面都是POJO的类,单元测试将会变得非常方便,也非常适合TDD的开发。 什么时候不需要接口 "劲酒虽好,可不要贪杯哦!" 和许多其它软件原则一样,面向接口很好,但也不应该是不分背景、不分场合胡乱使用的杀手锏和尚方宝剑。因为过多的使用接口,过多的引入间接层也会带来一些不必要的复杂度。 比如,我就看过有些应用的内部模块设计的过于“灵活”,给什么DAO、Convertor都加上一层Interface,但实际情况是,应用中对DAO、Convertor的实现进行替换的可能性极低。类似于这样的,装模作样,装腔作势的Interface就属于可有可无的鸡骨头(比鸡肋还低一个档次)。 就像《Effective Java》的作者Joshua Bloch所说: “同大多数学科一样,学习编程的艺术首先要学会基本的规则,然后才能知道什么时候可以打破这些规则。” 原文发布时间为:2019-11-7作者:从码农到工匠本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:初创公司遇到的每一个问题都可能攸关生死。创业之初更应该总结行业的常见问题,对比方案寻找最优解。阿里巴巴地图技术专家常意在技术圈摸爬滚打数年,接触了各式各样的Java服务端架构。服务端问题见得多了,也就更能分辨出各种方案的优劣。今天,常意总结了5大初创公司存在的Java服务端难题,并尝试性地给出了一些解决方案,供大家交流参考。 1.系统不是分布式 1.1.单机版系统抢单案例 // 抢取订单函数 public synchronized void grabOrder(Long orderId, Long userId) { // 获取订单信息 OrderDO order = orderDAO.get(orderId); if (Objects.isNull(order)) { throw new BizRuntimeException(String.format("订单(%s)不存在", orderId)); } // 检查订单状态 if (!Objects.equals(order.getStatus, OrderStatus.WAITING_TO_GRAB.getValue())) { throw new BizRuntimeException(String.format("订单(%s)已被抢", orderId)); } // 设置订单被抢 orderDAO.setGrabed(orderId, userId); } 以上代码,在一台服务器上运行没有任何问题。进入函数grabOrder(抢取订单)时,利用synchronized关键字把整个函数锁定,要么进入函数前订单未被人抢取,从而抢单成功,要么进入函数前订单已被抢取导致抢单失败,绝对不会出现进入函数前订单未被抢取而进入函数后订单又被抢取的情况。 但是,如果上面的代码在两台服务器上同时运行,由于Java的synchronized关键字只在一个虚拟机内生效,所以就会导致两个人能够同时抢取一个订单,但会以最后一个写入数据库的数据为准。所以,大多数的单机版系统,是无法作为分布式系统运行的。 1.2.分布式系统抢单案例 添加分布式锁,进行代码优化: // 抢取订单函数 public void grabOrder(Long orderId, Long userId) { Long lockId = orderDistributedLock.lock(orderId); try { grabOrderWithoutLock(orderId, userId); } finally { orderDistributedLock.unlock(orderId, lockId); } } // 不带锁的抢取订单函数 private void grabOrderWithoutLock(Long orderId, Long userId) { // 获取订单信息 OrderDO order = orderDAO.get(orderId); if (Objects.isNull(order)) { throw new BizRuntimeException(String.format("订单(%s)不存在", orderId)); } // 检查订单状态 if (!Objects.equals(order.getStatus, OrderStatus.WAITING_TO_GRAB.getValue())) { throw new BizRuntimeException(String.format("订单(%s)已被抢", orderId)); } // 设置订单被抢 orderDAO.setGrabed(orderId, userId); } 优化后的代码,在调用函数grabOrderWithoutLock(不带锁的抢取订单)前后,利用分布式锁orderDistributedLock(订单分布式锁)进行加锁和释放锁,跟单机版的synchronized关键字加锁效果基本一样。 1.3.分布式系统的优缺点 分布式系统(Distributed System)是支持分布式处理的软件系统,是由通信网络互联的多处理机体系结构上执行任务的系统,包括分布式操作系统、分布式程序设计语言及其编译系统、分布式文件系统分布式数据库系统等。 分布式系统的优点: 可靠性、高容错性:一台服务器的崩溃,不会影响其它服务器,其它服务器仍能提供服务。 可扩展性:如果系统服务能力不足,可以水平扩展更多服务器。 灵活性:可以很容易的安装、实施、扩容和升级系统。 性能高:拥有多台服务器的计算能力,比单台服务器处理速度更快。 性价比高:分布式系统对服务器硬件要求很低,可以选用廉价服务器搭建分布式集群,从而得到更好的性价比。 分布式系统的缺点: 排查难度高:由于系统分布在多台服务器上,故障排查和问题诊断难度较高。 软件支持少:分布式系统解决方案的软件支持较少。 建设成本高:需要多台服务器搭建分布式系统。 曾经有不少的朋友咨询我:"找外包做移动应用,需要注意哪些事项?" 首先,确定是否需要用分布式系统。软件预算有多少?预计用户量有多少?预计访问量有多少?是否只是业务前期试水版?单台服务器能否解决?是否接收短时间宕机?……如果综合考虑,单机版系统就可以解决的,那就不要采用分布式系统了。因为单机版系统和分布式系统的差别很大,相应的软件研发成本的差别也很大。 其次,确定是否真正的分布式系统。分布式系统最大的特点,就是当系统服务能力不足时,能够通过水平扩展的方式,通过增加服务器来增加服务能力。然而,单机版系统是不支持水平扩展的,强行扩展就会引起一系列数据问题。由于单机版系统和分布式系统的研发成本差别较大,市面上的外包团队大多用单机版系统代替分布式系统交付。 那么,如何确定你的系统是真正意义上的分布式系统呢?从软件上来说,是否采用了分布式软件解决方案;从硬件上来说,是否采用了分布式硬件部署方案。 1.4.分布式软件解决方案 作为一个合格的分布式系统,需要根据实际需求采用相应的分布式软件解决方案。 1.4.1分布式锁 分布式锁是单机锁的一种扩展,主要是为了锁住分布式系统中的物理块或逻辑块,用以此保证不同服务之间的逻辑和数据的一致性。 目前,主流的分布式锁实现方式有3种: 基于数据库实现的分布式锁; 基于Redis实现的分布式锁; 基于Zookeeper实现的分布式锁。 1.4.2分布式消息 分布式消息中间件是支持在分布式系统中发送和接受消息的软件基础设施。常见的分布式消息中间件有ActiveMQ、RabbitMQ、Kafka、MetaQ等。 MetaQ(全称Metamorphosis)是一个高性能、高可用、可扩展的分布式消息中间件,思路起源于LinkedIn的Kafka,但并不是Kafka的一个拷贝。MetaQ具有消息存储顺序写、吞吐量大和支持本地和XA事务等特性,适用于大吞吐量、顺序消息、广播和日志数据传输等场景。 1.4.3数据库分片分组 针对大数据量的数据库,一般会采用"分片分组"策略: 分片(shard):主要解决扩展性问题,属于水平拆分。引入分片,就引入了数据路由和分区键的概念。其中,分表解决的是数据量过大的问题,分库解决的是数据库性能瓶颈的问题。 分组(group):主要解决可用性问题,通过主从复制的方式实现,并提供读写分离策略用以提高数据库性能。 1.4.4分布式计算 分布式计算( Distributed computing )是一种"把需要进行大量计算的工程数据分割成小块,由多台计算机分别计算;在上传运算结果后,将结果统一合并得出数据结论"的科学。 当前的高性能服务器在处理海量数据时,其计算能力、内存容量等指标都远远无法达到要求。在大数据时代,工程师采用廉价的服务器组成分布式服务集群,以集群协作的方式完成海量数据的处理,从而解决单台服务器在计算与存储上的瓶颈。Hadoop、Storm以及Spark是常用的分布式计算中间件,Hadoop是对非实时数据做批量处理的中间件,Storm和Spark是对实时数据做流式处理的中间件。 除此之外,还有更多的分布式软件解决方案,这里就不再一一介绍了。 1.5分布式硬件部署方案 介绍完服务端的分布式软件解决方案,就不得不介绍一下服务端的分布式硬件部署方案。这里,只画出了服务端常见的接口服务器、MySQL数据库、Redis缓存,而忽略了其它的云存储服务、消息队列服务、日志系统服务…… 1.5.1一般单机版部署方案 架构说明:只有1台接口服务器、1个MySQL数据库、1个可选Redis缓存,可能都部署在同一台服务器上。 适用范围:适用于演示环境、测试环境以及不怕宕机且日PV在5万以内的小型商业应用。 1.5.2中小型分布式硬件部署方案 架构说明:通过SLB/Nginx组成一个负载均衡的接口服务器集群,MySQL数据库和Redis缓存采用了一主一备(或多备)的部署方式。 适用范围:适用于日PV在500万以内的中小型商业应用。 1.5.3大型分布式硬件部署方案 架构说明:通过SLB/Nginx组成一个负载均衡的接口服务器集群,利用分片分组策略组成一个MySQL数据库集群和Redis缓存集群。 适用范围:适用于日PV在500万以上的大型商业应用。 2.多线程使用不正确 多线程最主要目的就是"最大限度地利用CPU资源",可以把串行过程变成并行过程,从而提高了程序的执行效率。 2.1一个慢接口案例 假设在用户登录时,如果是新用户,需要创建用户信息,并发放新用户优惠券。例子代码如下: // 登录函数(示意写法) public UserVO login(String phoneNumber, String verifyCode) { // 检查验证码 if (!checkVerifyCode(phoneNumber, verifyCode)) { throw new ExampleException("验证码错误"); } // 检查用户存在 UserDO user = userDAO.getByPhoneNumber(phoneNumber); if (Objects.nonNull(user)) { return transUser(user); } // 创建新用户 return createNewUser(user); } // 创建新用户函数 private UserVO createNewUser(String phoneNumber) { // 创建新用户 UserDO user = new UserDO(); ... userDAO.insert(user); // 绑定优惠券 couponService.bindCoupon(user.getId(), CouponType.NEW_USER); // 返回新用户 return transUser(user); } 其中,绑定优惠券(bindCoupon)是给用户绑定新用户优惠券,然后再给用户发送推送通知。如果随着优惠券数量越来越多,该函数也会变得越来越慢,执行时间甚至超过1秒,并且没有什么优化空间。现在,登录(login)函数就成了名副其实的慢接口,需要进行接口优化。 2.2采用多线程优化 通过分析发现,绑定优惠券(bindCoupon)函数可以异步执行。首先想到的是采用多线程解决该问题,代码如下: // 创建新用户函数 private UserVO createNewUser(String phoneNumber) { // 创建新用户 UserDO user = new UserDO(); ... userDAO.insert(user); // 绑定优惠券 executorService.execute(()->couponService.bindCoupon(user.getId(), CouponType.NEW_USER)); // 返回新用户 return transUser(user); } 现在,在新线程中执行绑定优惠券(bindCoupon)函数,使用户登录(login)函数性能得到很大的提升。但是,如果在新线程执行绑定优惠券函数过程中,系统发生重启或崩溃导致线程执行失败,用户将永远获取不到新用户优惠券。除非提供用户手动领取优惠券页面,否则就需要程序员后台手工绑定优惠券。所以,用采用多线程优化慢接口,并不是一个完善的解决方案。 2.3采用消息队列优化 如果要保证绑定优惠券函数执行失败后能够重启执行,可以采用数据库表、Redis队列、消息队列的等多种解决方案。由于篇幅优先,这里只介绍采用MetaQ消息队列解决方案,并省略了MetaQ相关配置仅给出了核心代码。 消息生产者代码: // 创建新用户函数 private UserVO createNewUser(String phoneNumber) { // 创建新用户 UserDO user = new UserDO(); ... userDAO.insert(user); // 发送优惠券消息 Long userId = user.getId(); CouponMessageDataVO data = new CouponMessageDataVO(); data.setUserId(userId); data.setCouponType(CouponType.NEW_USER); Message message = new Message(TOPIC, TAG, userId, JSON.toJSONBytes(data)); SendResult result = metaqTemplate.sendMessage(message); if (!Objects.equals(result, SendStatus.SEND_OK)) { log.error("发送用户({})绑定优惠券消息失败:{}", userId, JSON.toJSONString(result)); } // 返回新用户 return transUser(user); } 注意:可能出现发生消息不成功,但是这种概率相对较低。 消息消费者代码: // 优惠券服务类 @Slf4j @Service public class CouponService extends DefaultMessageListener<String> { // 消息处理函数 @Override @Transactional(rollbackFor = Exception.class) public void onReceiveMessages(MetaqMessage<String> message) { // 获取消息体 String body = message.getBody(); if (StringUtils.isBlank(body)) { log.warn("获取消息({})体为空", message.getId()); return; } // 解析消息数据 CouponMessageDataVO data = JSON.parseObject(body, CouponMessageDataVO.class); if (Objects.isNull(data)) { log.warn("解析消息({})体为空", message.getId()); return; } // 绑定优惠券 bindCoupon(data.getUserId(), data.getCouponType()); } } 解决方案优点:采集MetaQ消息队列优化慢接口解决方案的优点: 如果系统发生重启或崩溃,导致消息处理函数执行失败,不会确认消息已消费;由于MetaQ支持多服务订阅同一队列,该消息可以转到别的服务进行消费,亦或等到本服务恢复正常后再进行消费。 消费者可多服务、多线程进行消费消息,即便消息处理时间较长,也不容易引起消息积压;即便引起消息积压,也可以通过扩充服务实例的方式解决。 如果需要重新消费该消息,只需要在MetaQ管理平台上点击"消息验证"即可。 3.流程定义不合理 3.1.原有的采购流程 这是一个简易的采购流程,由库管系统发起采购,采购员开始采购,采购员完成采购,同时回流采集订单到库管系统。 其中,完成采购动作的核心代码如下: /** 完成采购动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */ public void finishPurchase(PurchaseOrder order) { // 完成相关处理 ...... // 回流采购单(调用HTTP接口) backflowPurchaseOrder(order); // 设置完成状态 purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.FINISHED.getValue()); } 由于函数backflowPurchaseOrder(回流采购单)调用了HTTP接口,可能引起以下问题: 该函数可能耗费时间较长,导致完成采购接口成为慢接口; 该函数可能失败抛出异常,导致客户调用完成采购接口失败。 3.2.优化的采购流程 通过需求分析,把"采购员完成采购并回流采集订单"动作拆分为"采购员完成采购"和"回流采集订单"两个独立的动作,把"采购完成"拆分为"采购完成"和"回流完成"两个独立的状态,更方便采购流程的管理和实现。 拆分采购流程的动作和状态后,核心代码如下: /** 完成采购动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */ public void finishPurchase(PurchaseOrder order) { // 完成相关处理 ...... // 设置完成状态 purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.FINISHED.getValue()); } /** 执行回流动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */ public void executeBackflow(PurchaseOrder order) { // 回流采购单(调用HTTP接口) backflowPurchaseOrder(order); // 设置回流状态 purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.BACKFLOWED.getValue()); } 其中,函数executeBackflow(执行回流)由定时作业触发执行。如果回流采购单失败,采购单状态并不会修改为"已回流";等下次定时作业执行时,将会继续执行回流动作;直到回流采购单成功为止。 3.3.有限状态机介绍 3.3.1概念 有限状态机(Finite-state machine,FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的一个数学模型。 3.3.2要素 状态机可归纳为4个要素:现态、条件、动作、次态。 现态:指当前流程所处的状态,包括起始、中间、终结状态。条件:也可称为事件;当一个条件被满足时,将会触发一个动作并执行一次状态的迁移。动作:当条件满足后要执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。次态:当条件满足后要迁往的状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。 3.3.3状态 状态表示流程中的持久状态,流程图上的每一个圈代表一个状态。 初始状态: 流程开始时的某一状态;中间状态: 流程中间过程的某一状态;终结状态: 流程完成时的某一状态。 使用建议: 状态必须是一个持久状态,而不能是一个临时状态; 终结状态不能是中间状态,不能继续进行流程流转; 状态划分合理,不要把多个状态强制合并为一个状态; 状态尽量精简,同一状态的不同情况可以用其它字段表示。 3.3.4动作 动作的三要素:角色、现态、次态,流程图上的每一条线代表一个动作。 角色: 谁发起的这个操作,可以是用户、定时任务等;现态: 触发动作时当前的状态,是执行动作的前提条件;次态: 完成动作后达到的状态,是执行动作的最终目标。 使用建议: 每个动作执行前,必须检查当前状态和触发动作状态的一致性; 状态机的状态更改,只能通过动作进行,其它操作都是不符合规范的; 需要添加分布式锁保证动作的原子性,添加数据库事务保证数据的一致性; 类似的动作(比如操作用户、请求参数、动作含义等)可以合并为一个动作,并根据动作执行结果转向不同的状态。 4.系统间交互不科学 4.1.直接通过数据库交互 在一些项目中,系统间交互不通过接口调用和消息队列,而是通过数据库直接访问。问其原因,回答道:"项目工期太紧张,直接访问数据库,简单又快捷"。 还是以上面的采购流程为例——采购订单由库管系统发起,由采购系统负责采购,采购完成后通知库管系统,库管系统进入入库操作。采购系统采购完成后,通知库管系统数据库的代码如下: /** 执行回流动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */ public void executeBackflow(PurchaseOrder order) { // 完成原始采购单 rawPurchaseOrderDAO.setStatus(order.getRawId(), RawPurchaseOrderStatus.FINISHED.getValue()); // 设置回流状态 purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.BACKFLOWED.getValue()); } 其中,通过rawPurchaseOrderDAO(原始采购单DAO)直接访问库管系统的数据库表,并设置原始采购单状态为已完成。 一般情况下,直接通过数据访问的方式是不会有问题的。但是,一旦发生竞态,就会导致数据不同步。有人会说,可以考虑使用同一分布式锁解决该问题。是的,这种解决方案没有问题,只是又在系统间共享了分布式锁。 直接通过数据库交互的缺点: 直接暴露数据库表,容易产生数据安全问题; 多个系统操作同一数据库表,容易造成数据库表数据混乱; 操作同一个数据库表的代码,分布在不同的系统中,不便于管理和维护; 具有数据库表这样的强关联,无法实现系统间的隔离和解耦。 4.2.通过Dubbo接口交互 由于采购系统和库管系统都是内部系统,可以通过类似Dubbo的RPC接口进行交互。 库管系统代码: /** 采购单服务接口 */ public interface PurchaseOrderService { /** 完成采购单函数 */ public void finishPurchaseOrder(Long orderId); } /** 采购单服务实现 */ @Service("purchaseOrderService") public class PurchaseOrderServiceImpl implements PurchaseOrderService { /** 完成采购单函数 */ @Override @Transactional(rollbackFor = Exception.class) public void finishPurchaseOrder(Long orderId) { // 相关处理 ... // 完成采购单 purchaseOrderService.finishPurchaseOrder(order.getRawId()); } } 其中,库管系统通过Dubbo把PurchaseOrderServiceImpl(采购单服务实现)以PurchaseOrderService(采购单服务接口)定义的接口服务暴露给采购系统。这里,省略了Dubbo开发服务接口相关配置。 采购系统代码: /** 执行回流动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */ public void executeBackflow(PurchaseOrder order) { // 完成采购单 purchaseOrderService.finishPurchaseOrder(order.getRawId()); // 设置回流状态 purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.BACKFLOWED.getValue()); } 其中,purchaseOrderService(采购单服务)为库管系统PurchaseOrderService(采购单服务)在采购系统中的Dubbo服务客户端存根,通过该服务调用库管系统的服务接口函数finishPurchaseOrder(完成采购单函数)。 这样,采购系统和库管系统自己的强关联,通过Dubbo就简单地实现了系统隔离和解耦。当然,除了采用Dubbo接口外,还可以采用HTTPS、HSF、WebService等同步接口调用方式,也可以采用MetaQ等异步消息通知方式。 4.3常见系统间交互协议 4.3.1同步接口调用 同步接口调用是以一种阻塞式的接口调用机制。常见的交互协议有: HTTP/HTTPS接口; WebService接口; Dubbo/HSF接口; CORBA接口。 4.3.2异步消息通知 异步消息通知是一种通知式的信息交互机制。当系统发生某种事件时,会主动通知相应的系统。常见的交互协议有: MetaQ的消息通知; CORBA消息通知。 4.4.常见系统间交互方式 4.4.1请求-应答 适用范围:适合于简单的耗时较短的接口同步调用场景,比如Dubbo接口同步调用。 4.4.2通知-确认 适用范围:适合于简单的异步消息通知场景,比如MetaQ消息通知。 4.4.3请求-应答-查询-返回 适用范围:适合于复杂的耗时较长的接口同步调用场景,比如提交作业任务并定期查询任务结果。 4.4.4请求-应答-回调 适用范围:适合于复杂的耗时较长的接口同步调用和异步回调相结合的场景,比如支付宝的订单支付。 4.4.5请求-应答-通知-确认 适用范围:适合于复杂的耗时较长的接口同步调用和异步消息通知相结合的场景,比如提交作业任务并等待完成消息通知。 4.4.6通知-确认-通知-确认 适用范围:适合于复杂的耗时较长的异步消息通知场景。 5.数据查询不分页 在数据查询时,由于未能对未来数据量做出正确的预估,很多情况下都没有考虑数据的分页查询。 5.1.普通查询案例 以下是查询过期订单的代码: /** 订单DAO接口 */ public interface OrderDAO { /** 查询过期订单函数 */ @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day)") public List<OrderDO> queryTimeout(); } /** 订单服务接口 */ public interface OrderService { /** 查询过期订单函数 */ public List<OrderVO> queryTimeout(); } 当过期订单数量很少时,以上代码不会有任何问题。但是,当过期订单数量达到几十万上千万时,以上代码就会出现以下问题: 数据量太大,导致服务端的内存溢出; 数据量太大,导致查询接口超时、返回数据超时等; 数据量太大,导致客户端的内存溢出。 所以,在数据查询时,特别是不能预估数据量的大小时,需要考虑数据的分页查询。 这里,主要介绍"设置最大数量"和"采用分页查询"两种方式。 5.2设置最大数量 "设置最大数量"是一种最简单的分页查询,相当于只返回第一页数据。例子代码如下: /** 订单DAO接口 */ public interface OrderDAO { /** 查询过期订单函数 */ @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day) limit 0, #{maxCount}") public List<OrderDO> queryTimeout(@Param("maxCount") Integer maxCount); } /** 订单服务接口 */ public interface OrderService { /** 查询过期订单函数 */ public List<OrderVO> queryTimeout(Integer maxCount); } 适用于没有分页需求、但又担心数据过多导致内存溢出、数据量过大的查询。 5.3采用分页查询 "采用分页查询"是指定startIndex(开始序号)和pageSize(页面大小)进行数据查询,或者指定pageIndex(分页序号)和pageSize(页面大小)进行数据查询。例子代码如下: /** 订单DAO接口 */ public interface OrderDAO { /** 统计过期订单函数 */ @Select("select count(*) from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day)") public Long countTimeout(); /** 查询过期订单函数 */ @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day) limit #{startIndex}, #{pageSize}") public List<OrderDO> queryTimeout(@Param("startIndex") Long startIndex, @Param("pageSize") Integer pageSize); } /** 订单服务接口 */ public interface OrderService { /** 查询过期订单函数 */ public PageData<OrderVO> queryTimeout(Long startIndex, Integer pageSize); } 适用于真正的分页查询,查询参数startIndex(开始序号)和pageSize(页面大小)可由调用方指定。 5.4分页查询隐藏问题 假设,我们需要在一个定时作业(每5分钟执行一次)中,针对已经超时的订单(status=5,创建时间超时30天)进行超时关闭(status=10)。实现代码如下: /** 订单DAO接口 */ public interface OrderDAO { /** 查询过期订单函数 */ @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day) limit #{startIndex}, #{pageSize}") public List<OrderDO> queryTimeout(@Param("startIndex") Long startIndex, @Param("pageSize") Integer pageSize); /** 设置订单超时关闭 */ @Update("update t_order set status = 10 where id = #{orderId} and status = 5") public Long setTimeoutClosed(@Param("orderId") Long orderId) } /** 关闭过期订单作业类 */ public class CloseTimeoutOrderJob extends Job { /** 分页数量 */ private static final int PAGE_COUNT = 100; /** 分页大小 */ private static final int PAGE_SIZE = 1000; /** 作业执行函数 */ @Override public void execute() { for (int i = 0; i < PAGE_COUNT; i++) { // 查询处理订单 List<OrderDO> orderList = orderDAO.queryTimeout(i * PAGE_COUNT, PAGE_SIZE); for (OrderDO order : orderList) { // 进行超时关闭 ...... orderDAO.setTimeoutClosed(order.getId()); } // 检查处理完毕 if(orderList.size() < PAGE_SIZE) { break; } } } } 粗看这段代码是没有问题的,尝试循环100次,每次取1000条过期订单,进行订单超时关闭操作,直到没有订单或达到100次为止。但是,如果结合订单状态一起看,就会发现从第二次查询开始,每次会忽略掉前startIndex(开始序号)条应该处理的过期订单。这就是分页查询存在的隐藏问题: 当满足查询条件的数据,在操作中不再满足查询条件时,会导致后续分页查询中前startIndex(开始序号)条满足条件的数据被跳过。 可以采用"设置最大数量"的方式解决,代码如下: /** 订单DAO接口 */ public interface OrderDAO { /** 查询过期订单函数 */ @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day) limit 0, #{maxCount}") public List<OrderDO> queryTimeout(@Param("maxCount") Integer maxCount); /** 设置订单超时关闭 */ @Update("update t_order set status = 10 where id = #{orderId} and status = 5") public Long setTimeoutClosed(@Param("orderId") Long orderId) } /** 关闭过期订单作业(定时作业) */ public class CloseTimeoutOrderJob extends Job { /** 分页数量 */ private static final int PAGE_COUNT = 100; /** 分页大小 */ private static final int PAGE_SIZE = 1000; /** 作业执行函数 */ @Override public void execute() { for (int i = 0; i < PAGE_COUNT; i++) { // 查询处理订单 List<OrderDO> orderList = orderDAO.queryTimeout(PAGE_SIZE); for (OrderDO order : orderList) { // 进行超时关闭 ...... orderDAO.setTimeoutClosed(order.getId()); } // 检查处理完毕 if(orderList.size() < PAGE_SIZE) { break; } } } } 原文发布时间为:2019-11-6作者: 常意本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:闲鱼品牌创立于14年阿里的某个茶水间,从0开始到现在千万DAU,5年时间里闲鱼见证了闲置物品从线下到线上交易的转移。而线上交易的繁荣,则需要业务架构做相应的调整、演进才能支撑业务的快速发展。本文主要通过介绍闲鱼从0发展到千万级DAU应用的不同阶段的业务特点、核心问题以及针对性的架构演进,来阐述业务架构的演进思路与心得。 闲鱼业务背景 技术架构的演进跟业务形态都是强相关的,闲鱼的市场本质以及用户特点如下描述: 闲鱼是一个高性价比的二手交易市场。相比新品市场,二手市场的市场空间就是"用户在付出相同成本条件下有可能获取到更高的物品价值”,典型的比如"游戏卡带、乐高"等这些功能型的产品。同时,闲置市场也有着特殊存在的成本——信任成本,信任成本主要体现在:大部分二手可能没有售后服务;每个人对二手物品残值有着自己的主观评价。 扩大市场空间有两种方式: 降低新人成本 提升匹配效率 闲鱼与手淘差异性: 闲鱼与手淘的卖家差异:非专业的个人卖家,利益驱动弱。 发布产品差异:为保证市场供给,只能坚持轻发布。 商品差异:结构化信息少,没有历史累计行为。 闲鱼与手淘在业务、团队结构的差异性导致架构上不同的关注点,导致不同的演进路线。 架构演进——试错期 架构随着业务阶段不断演进,每个阶段都有核心的问题: 试错期业务核心问题:业务不断探索适合的商业模式; 架构核心关注点:提升响应速度,快速支持业务上线; 架构核心原则:以质量换取速度,可以牺牲一点线上质量(业务可接受范围)来换取更快的响应速度。 App发版速度(尤其是IOS)跟不上业务快速迭代的上线周期,动态性是端面临的主要问题,因此端上采用了Hybrid的架构: URL Router:所有请求路由到一个H5的链接,通过URI Schema重定向到真正页面,如果对应的native没有开发出来,就用H5版本来实现,解决安卓与IOS不同步的问题。 开关中心:通过开关控制页面路由,页面入口是否开启,分版本控制,参数变更等改动。 Poplayer:无需发版的情况下在已有的Native界面上弹出H5的部署容器,来满足运营随时创建活动并需要一个活动入口的需求。 架构演进——发展期 发展期业务与架构核心问题: 业务核心问题:隐约看到商业模式,需要加速验证,扩大规模。 架构关注点:提升效率(为了有机会去做更多事情,非降低整体成本),建设更多能力验证业务方向。 架构演进方向:前后端的协议、工具的自动化。服务端通过Mbaas(服务端提供基础的数据源(商品、用户、搜索、互动),让客户端/前端通过类SQL的描述一次性获取自己想要的数据,后端不需要增加接口)来实现活动、feeds投放的自动化。将更多精力投入到本地化、个性化、数据能力(与算法、推荐、搜索打通)的建设中。 客户端开发关注两个点: 对外整体连接协议的梳理,在容器这端演化成Service Bus(类似服务端的ESB),对具体的实现进行封装,以方便后续基础能力的可替换。 组件库的建立,新做一个页面的时候,能通过现有的UI组件进行简单组装,不需要从0开始搭建。组件与服务端打通,组件组装逻辑与数据直接由服务端完成,客户端负责解析与渲染。 因此这个时期客户端更多的工作是支持交互的基础的UI组件和动态适配性。 架构演进——平台期 随着业务的发展,闲鱼基于商品体系的业务达到十几种,逐渐向平台期发展。平台期业务与架构核心问题: 业务核心问题:需要让更多的二方、三方参与到共享经济平台的建设中,但是平台生态建设又超出了闲鱼自身的能力。 架构核心关注点:扩展性(具备接入业务的能力)、业务隔离(已接入业务平稳运行)、平台基础能力建设(业务更好的发展)。 架构原则:做一些更基础的规划,然后把更多的可能性、动态性留给二方或者三方完成。 业务隔离框架SWAK 核心解决因业务发展带来的代码耦合问题,问题主要体现在整体开发、运维效率低,稳定性差。核心思路是分离系统中不可变和可变的部分;分离出”做什么”与”怎么做”、“谁去做”。 将业务中不变的部分放入主干,定义出做什么;变化的部分以扩展点形式开放出来,让具体的业务放自己来实现,完成怎么做,谁去做。Swak的扩展点实现支持远程调用,可以让业务实现应用级别的隔离,相比传统的分包、分模块隔离方式更加彻底。 当前,闲鱼商品主链路完成基于Swak的升级。下面是一个闲鱼币个性化业务的代码案例: 平台通用能力 平台必须提供一些通用能力更好的支持业务发展: 实时选品投放能力——马赫:解决因闲鱼商品特性(结构化信息少,新品成交占比高)导致传统离线选品转换率差的问题。 实时线上故障定位能力——神探:解决类闲鱼规模系统因依赖多、场景多,导致线上问题频发、问题定位投入成本高的问题。核心思路是对系统每一次错误的请求链路进行实时采集、分析、聚合再可视化展现,将整体故障定位过程变成自动化。 架构演进——云端一体化 背景 随着无线发展,移动研发逐渐向多端化发展(IOT、小程序)。传统的基于Native+Web+服务端的开发方式,逐渐出现瓶颈,我们会发现例如: 端上同学离业务越来越远,服务端同学没时间做底层领域沉淀。 各端研发之间存在大量的协同, 整体研发效率低下。 招人也难了,需要同时招多个技术栈的同学; 在这种背景下, 我们的关注点回到研发效率上,从整体研发架构、研发模式出发, 思考什么样的架构演进、关系重塑才能适合当前的业务形态。我们希望探索出适合“ 闲鱼这样规模的具有独立APP” 的高效研发架构,形成云端一体化的研发能力,支持一云多端的发展。 演进步骤 朝着云端一体化的方向,架构的升级大概分成3个步骤: 端上用Flutter实现了两端(IOS、Android)统一。无线发展了现在,跨平台的需求已经非常强烈,团队招聘需要考虑 Android,IOS配比、一个业务需要在两端都写一次, 考虑双端逻辑一致、测试要测两遍。所以跨平台的方案能非常直接有效的降低研发成本,解决资源均衡的问题。 Flutter+dart实现了三端(IOS、Android、服务端)技术栈统一。端上统一了,再通过云端技术栈的打通来减少云端的协同。参考前端+Node.js的方案,闲鱼服务端用dart(Flutter也是dart语言)替换Java,作为服务端server的语言。 Flutter+ Faas(dart runtime)+Nexus。技术栈统一了,人员还不能互补,最新闲鱼将Dart容器嵌入到Faas容器中,配合跨云端的一体化业务研发框架Nexus,进行了一体化的研发模式的探索,使得一个研发人员能从端到服务端完成整个业务的闭环。 端侧方案选择 架构方案的选择,可能造成巨大并且长远的影响。在架构的演进中,我们要善于定义问题,然后通过不断迭代来解决问题,最后才能形成适合自己业务特性的架构。 闲鱼也是一样,所谓没有银弹的解决方案,在跨平台方案的选型中,充分对比了Flutter与RN的差异性,优缺点。闲鱼认为"跨平台与高性能是我们当前的核心诉求”,再结合团队内native技术栈的同学较多这个因素,我们最终选择了Flutter作为跨端解决方案。 云端协同 Flutter两端统一后,会发现客户端与服务端虽然都在做同一个业务,不仅技术栈没有统一,而且存在着大量协同的工作,同时端、云的同学仍然无法真正互补和一体化打通。 因此,我们开始思考是否能有一体的架构,能让一个同学可以Cover一个云到端的完整业务,形成业务闭环。 这不仅仅是效率的提升,更能为业务开发同学带来更大的成长空间,可以完整的和专注的思考业务。 关键问题及解法 我们梳理了需要解决的关键问题: 如何消除云端技术壁垒?首先要统一技术栈,其次端同学对云的思维模式、知识储备上的差异,需要有办法消除。 如何使工作总量减少 ( 1+1<2 )?一体化下需要使总工作量降低,不是简单的进行工作量转移。 如何促进生产关系重塑?生产力发生变化,需要建立新的生产关系。 面向这些问题,闲鱼的解法思路: 统一技术栈:Dart具备服务端语言特点,强类型,支持异步与并发,甚至更快的启动速度,因此作为服务端的server完全没有问题。Dart落地过程中更多的解决的是生态的问题(阿里的大部分生态都是基于java来建设的,例如中间件、消息、远程调用)。我们主要通过通过C++扩展、SideCar方式做桥接,Service Mesh来解决。 云端差异抹平:通过Faas , Baas等无服务器能力的建设, 抹平除写代码外的其他差异性(运维、故障定位等),使得客户端同学能写服务端;通过UI2Code(根据图片生成UI代码),页面代码模板化(页面容器,数据管理)使得服务端写客户端。 一体化总体效率提升:以往的架构是云、端分开架构的,一体化后下沉跨云端的研发框架Nexus,通过框架、工程体系的支持,消除协议层,重新定义UI与逻辑分层,带来了总工作量1+1<2。 关系重塑:领域下沉能让原来服务端同学更加专注领域建设,使领域层更加稳定,让业务层与领域层的变化比例,从当前的2:1,提高到5:1 甚至更高。让大家的关注点都集中在自己的范围内。 业务落地及收益 目前一体化的研发模式已经在闲鱼多个场景落地,以下单页的改造举例: 改造前: 下单页有着复杂的渲染、交互逻辑,之前大部分逻辑都是在端上,需要两个客户端+一个服务端的同学来维护。 改造后: 资源均衡:将客户端界面从 IOS、Android两端统一成了Flutter,后续只需要一个同学维护即可(原来需要两个开发人员),也不会出现逻辑不一致的情况。 协同效率提升:端上由两端协同提升到无需协同,云端由接口协议约定演化成现阶段的一体化协议,未来可将协议下沉到框架实现云端无接口约定。 业务闭环&人员成长:原来云端分离的业务逻辑全部下沉到了Faas(Dart),将原来分散在端与服务端的逻辑进行归一,有机会做更多的规划建设,同时也是端的同一个同学来维护,给这个端的同学带来更大的成长空间。 领域专注:Faas层调用底层领域服务来完成自己的业务,原来服务端的同学更多投入到交易能力的建设上。 框架下沉:跨云端业务研发框架Nexus:寓意着能将客户端与服务端连接在一起。核心思想就是将UI与逻辑分离,框架限定了端上只负责UI与状态的存储,所有的逻辑都在Faas中完成。非常适合类似下单页的领域稳定的的场景。 如上案例所述,云端一体化能在多个方面带来收益,特别适合类似闲鱼规模具有独立APP的研发团队。 说在最后 本文分别介绍了闲鱼从快速试错期→发展期→平台期→云端一体化的整体架构演进及过程中的思考。对核心问题的定义,以及做的具体演进。 我们会发现,架构的演化总是优于一步到位,没有一个大而全或者特效的方法可以一直提升系统效率。软件工程是一个超级复杂的系统,尤其是业务架构,需要随着业务随时变化。明确当前业务特点和核心问题才是设计的根本,不符合业务的架构再领先也没用。相信所有架构师都有这样的体会。 原文发布时间为:2019-11-5作者: 扬羽本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:打开盒马app,相信你跟阿里妹一样,很难抵抗各种美味的诱惑。颜值即正义,盒马的图片视频技术逼真地还原了食物细节,并在短短数秒内呈现出食物的最佳效果。今天,我们请来阿里高级无线开发工程师莱宁,解密盒马app里那些“美味”视频是如何生产的。 一、前言 图片合成视频并产生类似PPT中每页过渡特效的能力是目前很多短视频软件带有的功能,比如抖音的影集。这个功能主要包括图片合成视频、转场时间线定义和OpenGL特效等三个部分。 其中图片转视频的流程直接决定了后面过渡特效的实现方案。这里主要有两种方案: 图片预先合成视频,中间不做处理,记录每张图片展示的时间戳位置,然后在相邻图片切换的时间段用OpenGL做画面处理。 图片合成视频的过程中,在画面帧写入时同时做特效处理。 方案1每个流程都比较独立,更方便实现,但是要重复处理两次数据,一次合并一次加特效,耗时更长。 方案2的流程是相互穿插的,只需要处理一次数据,所以我们采用这个方案。 下面主要介绍下几个重点流程,并以几个简单的转场特效作为例子,演示具体效果。 二、图片合成 1.方案 图片合成视频有多种手段可以实现。下面谈一下比较常见的几种技术实现。 I.FFMPEG 定义输出编码格式和帧率,然后指定需要处理的图片列表即可合成视频。 ffmpeg -r 1/5 -i img%03d.png -c:v libx264 -vf fps=25 -pix_fmt yuv420p out.mp4 II.MediaCodec 在使用Mediacodec进行视频转码时,需要解码和编码两个codec。解码视频后将原始帧数据按照时间戳顺序写入编码器生成视频。但是图片本身就已经是帧数据,如果将图片转换成YUV数据,然后配合一个自定义的时钟产生时间戳,不断将数据写入编码器即可达到图片转视频的效果。 III.MediaCodec&OpenGL 既然Mediacodec合成过程中已经有了处理图片数据的流程,可以把这个步骤和特效生成结合起来,把图片处理成特效序列帧后再按序写入编码器,就能一并生成转场效果。 2.技术实现 首先需要定义一个时钟,来控制图片帧写入的频率和编码器的时间戳,同时也决定了视频最终的帧率。 这里假设需要24fps的帧率,一秒就是1000ms,因此写入的时间间隔是1000/24=42ms。也就是每隔42ms主动生成一帧数据,然后写入编码器。 时间戳需要是递增的,从0开始,按照前面定义的间隔时间差deltaT,每写入一次数据后就要将这个时间戳加deltaT,用作下一次写入。 然后是设置一个EGL环境来调用OpenGL,在Android中一个OpenGl的执行环境是threadlocal的,所以在合成过程中需要一直保持在同一个线程中。Mediacodec的构造函数中有一个surface参数,在编码器中是用作数据来源。在这个surface中输入数据就能驱动编码器生产视频。通过这个surface用EGL获取一个EGLSurface,就达到了OpenGL环境和视频编码器数据绑定的效果。 这里不需要手动将图片转换为YUV数据,先把图片解码为bitmap,然后通过texImage2D上传图片纹理到GPU中即可。 最后就是根据图片纹理的uv坐标,根据外部时间戳来驱动纹理变化,实现特效。 三、转场时间线 对于一个图片列表,在合成过程中如何衔接前后序列图片的展示和过渡时机,决定了最终的视频效果。 假设有图片合集{1,2,3,4},按序合成,可以有如下的时间线: 每个Stage是合成过程中的一个最小单元,首尾的两个Stage最简单,只是单纯的显示图片。中间阶段的Stage,包括了过渡过程中前后两张图片的展示和过渡动画的时间戳定义。 假设每张图片的展示时间为showT(ms),动画的时间为animT(ms)。 相邻Stage中同一张图的静态显示时间的总和为一张图的总显示时间,则首尾两个Stage的有效时长为showT/2,中间的过渡Stage有效时长为showT+animT。 其中过渡动画的时间段又需要分为: 前序退场起始点enterStartT,前序动画开始时间点。 前序退场结束点enterEndT,前序动画结束时间点。 后序入场起始点exitStartT,后序动画开始时间点。 后序入场结束点exitEndT,后序动画结束时间点。 动画时间线一般只定义为非淡入淡出外的其他特效使用。为了过渡的视觉连续性,前后序图片的淡入和淡出是贯穿整个动画时间的。考虑到序列的衔接性,退场完毕后会立刻入场,因此enterEndT=exitStartT。 四、OpenGL特效 1.基础架构 按照前面时间线定义回调接口,用于处理动画参数: //参数初始化 protected abstract void onPhaseInit(); //前序动画,enterRatio(0-1) protected abstract void onPhaseEnter(float enterRatio); //后序动画,exitRatio(0-1) protected abstract void onPhaseExit(float exitRatio); //动画结束 protected abstract void onPhaseFinish(); //一帧动画执行完毕,步进 protected abstract void onPhaseStep(); 定义几个通用的片段着色器变量,辅助过渡动画的处理: //前序图片的纹理 uniform sampler2D preTexture //后序图片的纹理 uniform sampler2D nextTexture; //过渡动画总体进度,0到1 uniform float progress; //窗口的长宽比例 uniform float canvasRatio; //透明度变化 uniform float canvasAlpha; 前后序列的混合流程,根据动画流程计算出的两个纹理的UV坐标混合颜色值: vec4 fromColor = texture2D(sTexture, fromUv); vec4 nextColor = texture2D(nextTexture, nextUv); vec4 mixColor = mix(fromColor, nextColor, mixIntensity); gl_FragColor = vec4(mixColor.rgb, canvasAlpha); 解析图片,先读取Exif信息获取旋转值,再将旋转矩阵应用到bitmap上,保证上传的纹理图片与用户在相册中看到的旋转角度是一致的: ExifInterface exif = new ExifInterface(imageFile); orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); int rotation = parseRotation(orientation); Matrix matrix = new Matrix(rotation); mImageBitmap = Bitmap.createBitmap(mOriginBitmap, 0, 0, mOriginBitmap.getWidth(), mOriginBitmap.getHeight(), matrix, true); 在使用图片之前,还要根据最终的视频宽高调整OpenGL窗口尺寸。同时纹理的贴图坐标的起始(0,0)是在纹理坐标系的左下角,而Android系统上canvas坐标原点是在左上角,需要将图片做一次y轴的翻转,不然图片上传后是垂直镜像。 //根据窗口尺寸生成一个空的bitmap mCanvasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas bitmapCanvas = new Canvas(mCanvasBitmap); //翻转图片 bitmapCanvas.scale(1, -1, bitmapCanvas.getWidth() / 2f, bitmapCanvas.getHeight() / 2f); 上传图片纹理,并记录纹理的handle: int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); int textureId = textures[0]; GLES20.glBindTexture(textureType, textureId); GLES20.glTexParameterf(textureType, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameterf(textureType, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(textureType, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(textureType, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); 加载第二张图片时要开启非0的其他纹理单元,过渡动画需要同时操作两个图片纹理: GLES20.glActiveTexture(GLES20.GL_TEXTURE1); 最后是实际绘制的部分,因为用到了透明度渐变,要手动开启GL_BLEND功能,并注意切换正在操作的纹理: //清除画布 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); GLES20.glUseProgram(mProgramHandle); //绑定顶点坐标 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferName); GLES20.glVertexAttribPointer(getHandle(ATTRIBUTE_VEC4_POSITION), GLConstants.VERTICES_DATA_POS_SIZE, GLES20.GL_FLOAT, false, GLConstants.VERTICES_DATA_STRIDE_BYTES, GLConstants.VERTICES_DATA_POS_OFFSET); GLES20.glEnableVertexAttribArray(getHandle(ATTRIBUTE_VEC4_POSITION)); GLES20.glVertexAttribPointer(getHandle(ATTRIBUTE_VEC4_TEXTURE_COORD), GLConstants.VERTICES_DATA_UV_SIZE, GLES20.GL_FLOAT, false, GLConstants.VERTICES_DATA_STRIDE_BYTES, GLConstants.VERTICES_DATA_UV_OFFSET); GLES20.glEnableVertexAttribArray(getHandle(ATTRIBUTE_VEC4_TEXTURE_COORD)); //激活有效纹理 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); //绑定图片纹理坐标 GLES20.glBindTexture(targetTexture, texName); GLES20.glUniform1i(getHandle(UNIFORM_SAMPLER2D_TEXTURE), 0); //开启透明度混合 GLES20.glEnable(GLES20.GL_BLEND); GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); //绘制三角形条带 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); //重置环境参数绑定 GLES20.glDisableVertexAttribArray(getHandle(ATTRIBUTE_VEC4_POSITION)); GLES20.glDisableVertexAttribArray(getHandle(ATTRIBUTE_VEC4_TEXTURE_COORD)); GLES20.glBindTexture(targetTexture, 0); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); 2.平移覆盖转场 I.着色器实现 uniform int direction; void main(void) { float intensity; if (direction == 0) { intensity = step(0.0 + coord.x,progress); } else if (direction == 1) { intensity = step(1.0 - coord.x,progress); } else if (direction == 2) { intensity = step(1.0 - coord.y,progress); } else if (direction == 3) { intensity = step(0.0 + coord.y,progress); } vec4 mixColor = mix(fromColor, nextColor, intensity); } GLSL中的step函数定义如下,当x Declaration: genType step(genType edge, genType x); Parameters: edge Specifies the location of the edge of the step function. x Specify the value to be used to generate the step function. 已知我们有前后两张图,将他们覆盖展示。然后从一个方向逐渐修改这一条轴上的所扫过的像素的intensity值,隐藏前图,展示后图。经过时钟动画驱动后就有了覆盖转场的效果。 再定义一个direction参数,控制扫描的方向,即可设置不同的转场方向,有PPT翻页的效果。 II.效果图 3.像素化转场 I.着色器实现 uniform float squareSizeFactor; uniform float imageWidthFactor; uniform float imageHeightFactor; void main(void) { float revProgress = (1.0 - progress); float distFromEdges = min(progress, revProgress); float squareSize = (squareSizeFactor * distFromEdges) + 1.0; float dx = squareSize * imageWidthFactor; float dy = squareSize * imageHeightFactor; vec2 coord = vec2(dx * floor(uv.x / dx), dy * floor(uv.y / dy)); vec4 fromColor = texture2D(preTexture, coord); vec4 nextColor = texture2D(nextTexture, coord); vec4 mixColor = mix(fromColor, nextColor, progress); }; 首先是定义像素块的效果,我们需要像素块逐渐变大,到动画中间值时再逐渐变小到消失。 通过对progress(0到1)取反向值1-progress,得到distFromEdges,可知这个值在progress从0到0.5时会从0到0.5,在0.5到1时会从0.5到0,即达到了我们需要的变大再变小的效果。 像素块就是一整个方格范围内的像素都是同一个颜色,视觉效果看起来就形成了明显的像素间隔。如果我们将一个方格范围内的纹理坐标都映射为同一个颜色,即实现了像素块的效果。 squareSizeFactor是影响像素块大小的一个参数值,设为50,即最大像素块为50像素。 imageWidthFactor和imageHeightFactor是窗口高宽取倒数,即1/width和1/height。 通过dx floor(uv.x / dx)和dy floor(uv.y / dy)的两次坐标转换,就把一个区间范围内的纹理都映射为了同一个颜色。 II.效果图 4.水波纹特效 I.数学原理 水波纹路的周期变化,实际就是三角函数的一个变种。目前业界最流行的简易水波纹实现,Adrian的博客中描述了基本的数学原理: 水波纹实际是Sombero函数的求值,也就是sinc函数的2D版本。 下图的左边是sin函数的图像,右边是sinc函数的图像,可以看到明显的水波纹特征。 博客中同时提供了一个WebGL版本的着色器实现,不过功能较简单,只是做了效果验证。 将其移植到OpenGLES中,并做参数调整,即可整合到图片转场特效中。完整的水波纹片段着色器如下: uniform float mixIntensity; uniform float rippleTime; uniform float rippleAmplitude; uniform float rippleSpeed; uniform float rippleOffset; uniform vec2 rippleCenterShift; void main(void) { //纹理位置坐标归一化 vec2 curPosition = -1.0 + 2.0 * vTextureCoord; //修正相对波纹中心点的位置偏移 curPosition -= rippleCenterShift; //修正画面比例 curPosition.x *= canvasRatio; //计算波纹里中心点的长度 float centerLength = length(curPosition); //计算波纹出现的纹理位置 vec2 uv = vTextureCoord + (curPosition/centerLength)*cos(centerLength*rippleAmplitude-rippleTime*rippleSpeed)*rippleOffset; vec4 fromColor = texture2D(preTexture, uv); vec4 nextColor = texture2D(nextTexture, uv); vec4 mixColor = mix(fromColor, nextColor, mixIntensity); gl_FragColor = vec4(mixColor.rgb, canvasAlpha); } 其中最关键的代码就是水波纹像素坐标的计算: vTextureCoord + (curPosition/centerLength)cos(centerLengthrippleAmplitude-rippleTimerippleSpeed)rippleOffset; 简化一下即:vTextureCoord + Acos(Lx - Ty)rippleOffset,一个标准的余弦函数。 vTextureCoord是当前纹理的归一化坐标(0,0)到(1,1)之间。 curPosition是(-1,-1)到(1,1)之间的当前像素坐标。 centerLength是当前点距离波纹中心的距离。 curPosition/centerLength即是线性代数中的单位矢量,这个参数用来决定波纹推动的方向。 cos(centerLengthrippleAmplitude-rippleTimerippleSpeed)通过一个外部时钟rippleTime来驱动cos函数生成周期性的相位偏移。 rippleAmplitude是相位的扩大因子。 rippleSpeed调节函数的周期,即波纹传递速度。 最后将偏移值乘以一个最大偏移范围rippleOffset(一般为0.03),限定单个像素的偏移范围,不然波纹会很不自然。 II.时间线动画 设定颜色混合,在整个动画过程中,图1逐渐消失(1到0),图2逐渐展现(0到1)。 设定画布透明度,在起始时为1,逐渐变化到0.7,最后再逐渐回到1。 设定波纹的振幅,在起始时最大,过渡到动画中间点到最小,最后逐渐变大到动画结束。 设定波纹的速度,在起始时最大,过渡到动画中间点到最小,最后逐渐变大到动画结束。 设定波纹的像素最大偏移值,在起始时最大,过渡到动画中间点到最小,最后逐渐变大到动画结束。 protected void onPhaseInit() { mMixIntensity = MIX_INTENSITY_START; mCanvasAlpha = CANVAS_ALPHA_DEFAULT; mRippleAmplitude = 0; mRippleSpeed = 0; mRippleOffset = 0; } protected void onPhaseEnter(float enterRatio) { mMixIntensity = enterRatio * 0.5f; mCanvasAlpha = 1f - enterRatio; mRippleAmplitude = enterRatio * RIPPLE_AMPLITUDE_DEFAULT; mRippleSpeed = enterRatio * RIPPLE_SPEED_DEFAULT; mRippleOffset = enterRatio * RIPPLE_OFFSET_DEFAULT; } protected void onPhaseExit(float exitRatio) { mMixIntensity = exitRatio * 0.5f + 0.5f; mCanvasAlpha = exitRatio; mRippleAmplitude = (1f - exitRatio) * RIPPLE_AMPLITUDE_DEFAULT; mRippleSpeed = (1f - exitRatio) * RIPPLE_SPEED_DEFAULT; mRippleOffset = (1f - exitRatio) * RIPPLE_OFFSET_DEFAULT; } protected void onPhaseFinish() { mMixIntensity = MIX_INTENSITY_END; mCanvasAlpha = CANVAS_ALPHA_DEFAULT; mRippleAmplitude = 0; mRippleSpeed = 0; mRippleOffset = 0; } protected void onPhaseStep() { if (mCanvasAlpha < CANVAS_ALPHA_MINIMUN) { mCanvasAlpha = CANVAS_ALPHA_MINIMUN; } } 将本次动画帧的参数更新到着色器: long globalTimeMs = GLClock.get(); GLES20.glUniform1f(getHandle("rippleTime"), globalTimeMs / 1000f); GLES20.glUniform1f(getHandle("rippleAmplitude"), mRippleAmplitude); GLES20.glUniform1f(getHandle("rippleSpeed"), mRippleSpeed); GLES20.glUniform1f(getHandle("rippleOffset"), mRippleOffset); GLES20.glUniform2f(getHandle("rippleCenterShift"), mRippleCenterX, mRippleCenterY); 其中GLClock是一个与mediacodec编码时间戳绑定的外部时钟,用于同步合成时间和动画时间戳位置。 III.最终效果 图片展示时长:3s过渡动画时长:1.5s波纹中心为图片中心点 5.随机方格 I.噪声函数 我们想实现的效果是前一个画面上随机出现很多方块,每个方块中展示下一张图的画面,当图片上每一块位置都形成方块后就完成了画面的转换。 首先就需要解决随机函数的问题。虽然Java上有很多现成的随机函数,但是GLSL是个很底层的语言,基本上除了加减乘除其他的都需要自己想办法。这个着色器里用的rand函数是流传已久几乎找不到来源的一个实现,很有上古时期游戏编程代码的风格,有魔法数,代码只要一行,证明要写两页。 网上一个比较靠谱且简洁的说明是StackOverflow上的,这个随机函数实际是一个hash函数,对每一个相同的(x,y)输入都会有相同的输出。 II.着色器实现 uniform vec2 squares; uniform float smoothness; float rand(vec2 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); }; void main(void) { vec2 uv = vTextureCoord.xy; float randomSquare = rand(floor(squares * uv)); float intensity = smoothstep(0.0, -smoothness, randomSquare - (progress * (1.0 + smoothness))); vec4 fromColor = texture2D(preTexture, uv); vec4 nextColor = texture2D(nextTexture, uv); vec4 mixColor = mix(fromColor, nextColor, intensity); gl_FragColor = vec4(mixColor.rgb, canvasAlpha); } 首先将当前纹理坐标乘以方格大小,用随机函数转换后获取这个方格区域的随机渐变值。 然后用smoothstep做一个厄米特插值,将渐变的intensity平滑化。 最后用这个intensity值mix前后图像序列。 III.效果图 原文发布时间为:2019-11-4作者: 莱宁本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:什么是经验?就是遇到问题,解决问题,总结方法。遇到的问题多了,解决的办法多了,经验自然就积累出来了。今天的文章是阿里技术专家蛰剑在工作中遇到的一个问题引发的对TCP性能和发送接收Buffer关系的系列思考(问题:应用通过专线从公司访问阿里云上的服务,专线100M,时延20ms,一个SQL查询了22M数据出现10倍+的信息延迟,不正常。)希望,你也能从中得到启发。 前言 本文希望解析清楚,当我们在代码中写下 socket.setSendBufferSize 和 sysctl 看到的rmem/wmem系统参数以及最终我们在TCP常常谈到的接收发送窗口的关系,以及他们怎样影响TCP传输的性能。 先明确一下:文章标题中所说的Buffer指的是sysctl中的 rmem或者wmem,如果是代码中指定的话对应着SO_SNDBUF或者SO_RCVBUF,从TCP的概念来看对应着发送窗口或者接收窗口。 TCP性能和发送接收Buffer的关系 相关参数: $sudo sysctl -a | egrep "rmem|wmem|adv_win|moderate" net.core.rmem_default = 212992 net.core.rmem_max = 212992 net.core.wmem_default = 212992 net.core.wmem_max = 212992 net.ipv4.tcp_adv_win_scale = 1 net.ipv4.tcp_moderate_rcvbuf = 1 net.ipv4.tcp_rmem = 4096 87380 6291456 net.ipv4.tcp_wmem = 4096 16384 4194304 net.ipv4.udp_rmem_min = 4096 net.ipv4.udp_wmem_min = 4096 vm.lowmem_reserve_ratio = 256 256 32 先从碰到的一个问题看起: 应用通过专线从公司访问阿里云上的服务,专线100M,时延20ms,一个SQL查询了22M数据,结果花了大概25秒,这太慢了,不正常。如果通过云上client访问云上服务那么1-2秒就返回了(说明不跨网络服务是正常的)。如果通过http或者scp从公司向云上传输这22M的数据大概两秒钟也传送完毕了(说明网络带宽不是瓶颈),所以这里问题的原因基本上是我们的服务在这种网络条件下有性能问题,需要找出为什么。 抓包 tcpdump+wireshark 这个查询结果22M的需要25秒,如下图(wireshark 时序图),横轴是时间,纵轴是sequence number: 粗一看没啥问题,因为时间太长掩盖了问题。把这个图形放大,就看中间50ms内的传输情况(横轴是时间,纵轴是sequence number,一个点代表一个包)。 换个角度,看看窗口尺寸图形: 从bytes in flight也大致能算出来总的传输时间 16K*1000/20=800Kb/秒我们的应用会默认设置 socketSendBuffer 为16K: socket.setSendBufferSize(16*1024) //16K send buffer 来看一下tcp包发送流程: 图片来源:陶辉 如果sendbuffer不够就会卡在上图中的第一步 sk_stream_wait_memory,通过systemtap脚本可以验证: #!/usr/bin/stap # Simple probe to detect when a process is waiting for more socket send # buffer memory. Usually means the process is doing writes larger than the # socket send buffer size or there is a slow receiver at the other side. # Increasing the socket's send buffer size might help decrease application # latencies, but it might also make it worse, so buyer beware. # Typical output: timestamp in microseconds: procname(pid) event # # 1218230114875167: python(17631) blocked on full send buffer # 1218230114876196: python(17631) recovered from full send buffer # 1218230114876271: python(17631) blocked on full send buffer # 1218230114876479: python(17631) recovered from full send buffer probe kernel.function("sk_stream_wait_memory") { printf("%u: %s(%d) blocked on full send buffern", gettimeofday_us(), execname(), pid()) } probe kernel.function("sk_stream_wait_memory").return { printf("%u: %s(%d) recovered from full send buffern", gettimeofday_us(), execname(), pid()) } 原理解析 如果tcp发送buffer也就是SO_SNDBUF只有16K的话,这些包很快都发出去了,但是这16K不能立即释放出来填新的内容进去,因为tcp要保证可靠,万一中间丢包了呢。只有等到这16K中的某些包ack了,才会填充一些新包进来然后继续发出去。由于这里rt基本是20ms,也就是16K发送完毕后,等了20ms才收到一些ack,这20ms应用、内核什么都不能做,所以就是如第二个图中的大概20ms的等待平台。 sendbuffer相当于发送仓库的大小,仓库的货物都发走后,不能立即腾出来发新的货物,而是要等对方确认收到了(ack)才能腾出来发新的货物。 传输速度取决于发送仓库(sendbuffer)、接收仓库(recvbuffer)、路宽(带宽)的大小,如果发送仓库(sendbuffer)足够大了之后接下来的瓶颈就是高速公路了(带宽、拥塞窗口)。 如果是UDP,就没有可靠的概念,有数据统统发出去,根本不关心对方是否收到,也就不需要ack和这个发送buffer了。 几个发送buffer相关的内核参数 vm.lowmem_reserve_ratio = 256 256 32 net.core.wmem_max = 1048576 net.core.wmem_default = 124928 net.ipv4.tcp_wmem = 4096 16384 4194304 net.ipv4.udp_wmem_min = 4096 net.ipv4.tcp_wmem 默认就是16K,而且是能够动态调整的,只不过我们代码中这块的参数是很多年前从Cobra中继承过来的,初始指定了sendbuffer的大小。代码中设置了这个参数后就关闭了内核的动态调整功能,但是能看到http或者scp都很快,因为他们的send buffer是动态调整的,所以很快。 接收buffer是有开关可以动态控制的,发送buffer没有开关默认就是开启,关闭只能在代码层面来控制: net.ipv4.tcp_moderate_rcvbuf 优化 调整 socketSendBuffer 到256K,查询时间从25秒下降到了4秒多,但是比理论带宽所需要的时间略高。 继续查看系统 net.core.wmem_max 参数默认最大是130K,所以即使我们代码中设置256K实际使用的也是130K,调大这个系统参数后整个网络传输时间大概2秒(跟100M带宽匹配了,scp传输22M数据也要2秒),整体查询时间2.8秒。测试用的mysql client短连接,如果代码中的是长连接的话会块300-400ms(消掉了慢启动阶段),这基本上是理论上最快速度了。 如果指定了tcp_wmem,则net.core.wmem_default被tcp_wmem的覆盖。send Buffer在tcp_wmem的最小值和最大值之间自动调整。如果调用setsockopt()设置了socket选项SO_SNDBUF,将关闭发送端缓冲的自动调节机制,tcp_wmem将被忽略,SO_SNDBUF的最大值由net.core.wmem_max限制。 BDP 带宽时延积 BDP=rtt*(带宽/8) 这个 buffer 调到1M测试没有帮助,从理论计算BDP(带宽时延积) 0.02秒*(100MB/8)=250Kb 所以SO_SNDBUF为256Kb的时候基本能跑满带宽了,再大实际意义也不大了。也就是前面所说的仓库足够后瓶颈在带宽上了。 因为BDP是250K,也就是拥塞窗口(带宽、接收窗口和rt决定的)即将成为新的瓶颈,所以调大buffer没意义了。 用tc构造延时和带宽限制的模拟重现环境 sudo tc qdisc del dev eth0 root netem delay 20ms sudo tc qdisc add dev eth0 root tbf rate 500kbit latency 50ms burst 15kb 这个案例关于wmem的结论 默认情况下Linux系统会自动调整这个buffer(net.ipv4.tcp_wmem), 也就是不推荐程序中主动去设置SO_SNDBUF,除非明确知道设置的值是最优的。 从这里我们可以看到,有些理论知识点虽然我们知道,但是在实践中很难联系起来,也就是常说的无法学以致用,最开始看到抓包结果的时候比较怀疑发送、接收窗口之类的,没有直接想到send buffer上,理论跟实践的鸿沟。 说完发送Buffer(wmem)接下来我们接着一看看接收buffer(rmem)和接收窗口的情况 用这样一个案例下来验证接收窗口的作用: 有一个batch insert语句,整个一次要插入5532条记录,所有记录大小总共是376K。SO_RCVBUF很小的时候并且rtt很大对性能的影响 如果rtt是40ms,总共需要5-6秒钟: 基本可以看到server一旦空出来点窗口,client马上就发送数据,由于这点窗口太小,rtt是40ms,也就是一个rtt才能传3456字节的数据,整个带宽才80-90K,完全没跑满。 比较明显间隔 40ms 一个等待台阶,台阶之间两个包大概3K数据,总的传输效率如下: 斜线越陡表示速度越快,从上图看整体SQL上传花了5.5秒,执行0.5秒。 此时对应的窗口尺寸: 窗口由最开始28K(20个1448)很快降到了不到4K的样子,然后基本游走在即将满的边缘,虽然读取慢,幸好rtt也大,导致最终也没有满。(这个是3.1的Linux,应用SO_RCVBUF设置的是8K,用一半来做接收窗口)。 SO_RCVBUF很小的时候并且rtt很小时对性能的影响 如果同样的语句在 rtt 是0.1ms的话: 虽然明显看到接收窗口经常跑满,但是因为rtt很小,一旦窗口空出来很快就通知到对方了,所以整个过小的接收窗口也没怎么影响到整体性能。 如上图11.4秒整个SQL开始,到11.41秒SQL上传完毕,11.89秒执行完毕(执行花了0.5秒),上传只花了0.01秒,接收窗口情况: 如图,接收窗口由最开始的28K降下来,然后一直在5880和满了之间跳动: 从这里可以得出结论,接收窗口的大小对性能的影响,rtt越大影响越明显,当然这里还需要应用程序配合,如果应用程序一直不读走数据即使接收窗口再大也会堆满的。 SO_RCVBUF和tcp window full的坏case 上图中红色平台部分,停顿了大概6秒钟没有发任何有内容的数据包,这6秒钟具体在做什么如下图所示,可以看到这个时候接收方的TCP Window Full,同时也能看到接收方(3306端口)的TCP Window Size是8192(8K),发送方(27545端口)是20480。 这个状况跟前面描述的recv buffer太小不一样,8K是很小,但是因为rtt也很小,所以server总是能很快就ack收到了,接收窗口也一直不容易达到full状态,但是一旦接收窗口达到了full状态,居然需要惊人的6秒钟才能恢复,这等待的时间有点太长了。这里应该是应用读取数据太慢导致了耗时6秒才恢复,所以最终这个请求执行会非常非常慢(时间主要耗在了上传SQL而不是执行SQL)。 实际原因不知道,从读取TCP数据的逻辑来看这里没有明显的block,可能的原因: request的SQL太大,Server(3306端口上的服务)从TCP读取SQL需要放到一块分配好的内存,内存不够的时候需要扩容,扩容有可能触发fgc,从图形来看,第一次满就卡顿了,而且每次满都卡顿,不像是这个原因 request请求一次发过来的是多个SQL,应用读取SQL后,将SQL分成多个,然后先执行第一个,第一个执行完后返回response,再读取第二个。图形中卡顿前没有response返回,所以也不是这个原因。 ……其它未知原因 接收方不读取数据导致的接收窗口满同时有丢包发生 服务端返回数据到client端,TCP协议栈ack这些包,但是应用层没读走包,这个时候 SO_RCVBUF 堆积满,client的TCP协议栈发送 ZeroWindow 标志给服务端。也就是接收端的 buffer 堆满了(但是服务端这个时候看到的bytes in fly是0,因为都ack了),这时服务端不能继续发数据,要等 ZeroWindow 恢复。 那么接收端上层应用不读走包可能的原因: 应用代码卡顿、GC等等 应用代码逻辑上在做其它事情(比如Server将SQL分片到多个DB上,Server先读取第一个分片,如果第一个分片数据很大很大,处理也慢,那么第二个分片数据都返回到了TCP buffer,也没去读取其它分片的结果集,直到第一个分片读取完毕。如果SQL带排序,那么Server。 会轮询读取多个分片,造成这种卡顿的概率小了很多 上图这个流因为应用层不读取TCP数据,导致TCP接收Buffer满,进而接收窗口为0,server端不能再发送数据而卡住,但是ZeroWindow的探测包,client都有正常回复,所以1903秒之后接收方窗口不为0后(window update)传输恢复。 这个截图和前一个类似,是在Server上(3003端口)抓到的包,不同的是接收窗口为0后,server端多次探测(Server上抓包能看到),但是client端没有回复 ZeroWindow(也有可能是回复了,但是中间环节把ack包丢了,或者这个探测包client没收到),造成server端认为client死了、不可达之类,进而反复重传,重传超过15次之后,server端认为这个连接死了,粗暴单方面断开(没有reset和fin,因为没必要,server认为网络连通性出了问题)。 等到1800秒后,client的接收窗口恢复了,发个window update给server,这个时候server认为这个连接已经断开了,只能回复reset。 网络不通,重传超过一定的时间(tcp_retries2)然后断开这个连接是正常的,这里的问题是: 为什么这种场景下丢包了,而且是针对某个stream一直丢包? 可能是因为这种场景下触发了中间环节的流量管控,故意丢包了(比如proxy、slb、交换机都有可能做这种选择性的丢包) 这里server认为连接断开,没有发reset和fin,因为没必要,server认为网络连通性出了问题。client还不知道server上这个连接清理掉了,等client回复了一个window update,server早就认为这个连接早断了,突然收到一个update,莫名其妙,只能reset。 接收窗口和SO_RCVBUF的关系 初始接收窗口一般是 mss乘以初始cwnd(为了和慢启动逻辑兼容,不想一下子冲击到网络),如果没有设置SO_RCVBUF,那么会根据 net.ipv4.tcp_rmem 动态变化,如果设置了SO_RCVBUF,那么接收窗口要向下面描述的值靠拢。 初始cwnd可以大致通过查看到: ss -itmpn dst "10.81.212.8" State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 0 10.xx.xx.xxx:22 10.yy.yy.yyy:12345 users:(("sshd",pid=1442,fd=3)) skmem:(r0,rb369280,t0,tb87040,f4096,w0,o0,bl0,d92) Here we can see this socket has Receive Buffer 369280 bytes, and Transmit Buffer 87040 bytes. Keep in mind the kernel will double any socket buffer allocation for overhead. So a process asks for 256 KiB buffer with setsockopt(SO_RCVBUF) then it will get 512 KiB buffer space. This is described on man 7 tcp. 初始窗口计算的代码逻辑,重点在18行: /* TCP initial congestion window as per rfc6928 */ #define TCP_INIT_CWND 10 /* 3. Try to fixup all. It is made immediately after connection enters * established state. */ void tcp_init_buffer_space(struct sock *sk) { int tcp_app_win = sock_net(sk)->ipv4.sysctl_tcp_app_win; struct tcp_sock *tp = tcp_sk(sk); int maxwin; if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK)) tcp_sndbuf_expand(sk); //初始最大接收窗口计算过程 tp->rcvq_space.space = min_t(u32, tp->rcv_wnd, TCP_INIT_CWND * tp->advmss); tcp_mstamp_refresh(tp); tp->rcvq_space.time = tp->tcp_mstamp; tp->rcvq_space.seq = tp->copied_seq; maxwin = tcp_full_space(sk); if (tp->window_clamp >= maxwin) { tp->window_clamp = maxwin; if (tcp_app_win && maxwin > 4 * tp->advmss) tp->window_clamp = max(maxwin - (maxwin >> tcp_app_win), 4 * tp->advmss); } /* Force reservation of one segment. */ if (tcp_app_win && tp->window_clamp > 2 * tp->advmss && tp->window_clamp + tp->advmss > maxwin) tp->window_clamp = max(2 * tp->advmss, maxwin - tp->advmss); tp->rcv_ssthresh = min(tp->rcv_ssthresh, tp->window_clamp); tp->snd_cwnd_stamp = tcp_jiffies32; } 传输过程中,最大接收窗口会动态调整,当指定了SO_RCVBUF后,实际buffer是两倍SO_RCVBUF,但是要分出一部分(2^net.ipv4.tcp_adv_win_scale)来作为乱序报文缓存。 1.net.ipv4.tcp_adv_win_scale = 2 //2.6内核,3.1中这个值默认是1。 如果SO_RCVBUF是8K,总共就是16K,然后分出2^2分之一,也就是4分之一,还剩12K当做接收窗口;如果设置的32K,那么接收窗口是48K。 static inline int tcp_win_from_space(const struct sock *sk, int space) {//space 传入的时候就已经是 2*SO_RCVBUF了 int tcp_adv_win_scale = sock_net(sk)->ipv4.sysctl_tcp_adv_win_scale; return tcp_adv_win_scale <= 0 ? (space>>(-tcp_adv_win_scale)) : space - (space>>tcp_adv_win_scale); //sysctl参数tcp_adv_win_scale } 接收窗口有最大接收窗口和当前可用接收窗口。 一般来说一次中断基本都会将 buffer 中的包都取走。 绿线是最大接收窗口动态调整的过程,最开始是146010,握手完毕后略微调整到147210(可利用body增加了12),随着数据的传输开始跳涨。 上图是四个batch insert语句,可以看到绿色接收窗口随着数据的传输越来越大,图中蓝色竖直部分基本表示SQL上传,两个蓝色竖直条的间隔代表这个insert在服务器上真正的执行时间。这图非常陡峭,表示上传没有任何瓶颈。 设置 SO_RCVBUF 后通过wireshark观察到的接收窗口基本 下图是设置了 SO_RCVBUF 为8192的实际情况: 从最开始的14720,执行第一个create table语句后降到14330,到真正执行batch insert就降到了8192*1.5. 然后一直保持在这个值。 If you set a "receive buffer size" on a TCP socket, what does it actually mean? The naive answer would go something along the lines of: the TCP receive buffer setting indicates the maximum number of bytes a read() syscall could retrieve without blocking. Note that if the buffer size is set with setsockopt(), the value returned with getsockopt() is always double the size requested to allow for overhead. This is described in man 7 socket. 总结 一般来说绝对不要在程序中手工设置SO_SNDBUF和SO_RCVBUF,内核自动调整比你做的要好; SO_SNDBUF一般会比发送滑动窗口要大,因为发送出去并且ack了的才能从SO_SNDBUF中释放; TCP接收窗口跟SO_RCVBUF关系很复杂; SO_RCVBUF太小并且rtt很大的时候会严重影响性能; 接收窗口比发送窗口复杂多了; 发送窗口/SO_SNDBUF--发送仓库,带宽/拥塞窗口--马路通畅程度,接收窗口/SO_RCVBUF--接收仓库; 发送仓库、马路通畅程度、接收仓库一起决定了传输速度--类比一下快递过程。总之记住一句话:不要设置socket的SO_SNDBUF和SO_RCVBUF。 相关和参考文章经典的 nagle 和 dalay ack对性能的影响 就是要你懂 TCP-- 最经典的TCP性能问题关于TCP 半连接队列和全连接队列MSS和MTU导致的悲剧双11通过网络优化提升10倍性能就是要你懂TCP的握手和挥手高性能网络编程7--tcp连接的内存使用The story of one latency spikeWhat is rcv_space in the 'ss --info' output, and why it's value is larger than net.core.rmem_max 原文发布时间为:2019-11-1作者:蛰剑本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:随着大量新生的异步框架和支持协程的语言(如Go)的出现,在很多场景下操作系统的线程调度成为了性能的瓶颈,Java也因此被质疑是否不再适应最新的云场景了。4年前,阿里JVM团队开始自研Wisp2,将Go语言的协程能力带入到Java世界。既享受Java的丰富生态,又获得异步程序的性能,Wisp2让Java平台历久弥新。 Java平台一直以生态的繁荣著称,大量的类库、框架帮助开发者们快速搭建应用。而其中大部分Java框架类库都是基于线程池以及阻塞机制来服务并发的,主要原因包括: Java语言在核心类库中提供了强大的并发能力,多线程应用可以获得不俗的性能; Java EE的一些标准都是线程级阻塞的(比如JDBC); 基于阻塞模式可以快速地开发应用。 但如今,大量新生的异步框架和支持协程的语言(如Go)的出现,在很多场景下操作系统的线程调度成为了性能的瓶颈。Java也因此被质疑是否不再适应最新的云场景了。 4年前,阿里开始自研Wisp2。它主要是用在IO密集的服务器场景,大部分公司的在线服务都是这样的场景 (离线应用都是偏向于计算,则不适用)。它在功能属性上对标Goroutine的Java协程,在产品形态、性能、稳定性上都达到了一个比较理想的情况。到现在,已经有上百个应用,数万个容器上线了Wisp1/2。Wisp协程完全兼容多线程阻塞的代码写法,仅需增加JVM参数来开启协程,阿里巴巴的核心电商应用已经在协程模型上经过两个双十一的考验,既享受到了Java的丰富生态,又获得了异步程序的性能。 Wisp2主打的是性能和对现有代码的兼容性,简而言之,现有的基于多线程的IO密集的Java应用只需要加上Wisp2的JVM参数就可以获得异步的性能提升。 作为例子,以下是消息中间件代理(简称mq)和drds只添加参数不改代码的压测比较: 可以看到上下文切换以及sys CPU显著降低,RT减少、QPS分别提升11.45%,18.13%。 Quick Start 由于Wisp2完全兼容现有的Java代码,因此使用起来十分简单,有多简单? 如果你的应用是“标准”的在线应用(使用/home/admin/$APP_NAME/setenv.sh配置参数),那么在admin用户下输入如下命令就可以开启Wisp2了: curl https://gosling.alibaba-inc.com/sh/enable-wisp2.sh | sh 否则需要手动升级JDK和Java参数: ajdk 8.7.12_fp2 rpm sudo yum install ajdk -b current # 也可以通过yum安装最新jdkjava -XX:+UseWisp2 .... # 使用Wisp参数启动Java应用 然后就可以通过jstack验证协程确实被开启了。 Carrier线程是调度协程的线程,下方的- Coroutine [...]表示一个协程,active表示协程被调度的次数,steal表示被work stealing的次数,preempt表示时间片抢占次数。 下图是DRDS在ecs上压测时的top -H,可以看出来应用的数百个线程被8个Carrier线程托管,均匀地跑在CPU核数个线程上面。下方一些名为java的线程是gc线程。 过多线程的开销 误区1: 进内核引发上下文切换 我们看一段测试程序: pipe(a); while (1) { write(a[1], a, 1); read(a[0], a, 1); n += 2; } 执行这段程序时上下文切换非常低,实际上上面的IO系统调用都是不会阻塞的,因此内核不需要挂起线程,也不需要切换上下文,实际发生的是用户/内核态的模式切换。 上面的程序在神龙服务器测得每个pipe操作耗时约334ns,速度很快。 误区2: 上下文切换的开销很大 本质上来说无论是用户态还是内核态的上下文切换都是很轻量的,甚至有一些硬件指令来支持,比如pusha可以帮助我们保存通用寄存器。同一个进程的线程共享页表,因此上下文切换的开销一般只有: 保存各种寄存器 切换sp(call指令会自动将pc压栈) 可以在数十条指令内完成。 开销 既然近内核以及上下文切换都不慢,那么多线程的开销究竟在哪? 我们不妨看一个阻塞的系统调用futex的热点分布: 可以看到上面的热点中有大量涉及调度的开销。我们来看过程: 调用系统调用(可能需要阻塞); 系统调用确实需要阻塞,kernel需要决定下一个被执行的线程(调度); 执行上下切换。 因此,上面2个误区与多线程的开销都有一定因果关系,但是真正的开销来源于线程阻塞唤醒调度。 综上,希望通过线程模型来提升web server性能的原则是: 活跃线程数约等于CPU个数 每个线程不太需要阻塞 文章后续将紧紧围绕这两个主题。 为了满足上述两个条件,使用eventloop+异步callback的方式是一个极佳的选择。 异步与协程的关系 为了保持简洁,我们以一个异步服务器上的Netty写操作为例子(写操作也存在阻塞的可能): private void writeQuery(Channel ch) { ch.write(Unpooled.wrappedBuffer("query".getBytes())).sync(); logger.info("write finish"); } 这里的sync()会阻塞线程。不满足期望。由于netty本身是一个异步框架,我们引入回调: private void writeQuery(Channel ch) { ch.write(Unpooled.wrappedBuffer("query".getBytes())) .addListener(f -> { logger.info("write finish"); }); } 注意这里异步的write调用后,writeQuery会返回。因此假如逻辑上要求在write后执行的代码,必须出现在回调里,write是函数的最后一行。这里是最简单的情形,如果函数有其他调用者,那么就需要用CPS变换。 需要不断的提取程序的"下半部分",即continuation,似乎对我们造成一些心智负担了。这里我们引入kotlin协程帮助我们简化程序: suspend fun Channel.aWrite(msg: Any): Int = suspendCoroutine { cont -> write(msg).addListener { cont.resume(0) } } suspend fun writeQuery(ch: Channel) { ch.aWrite(Unpooled.wrappedBuffer("query".toByteArray())) logger.info("write finish") } 这里引入了一个魔法suspendCoroutine,我们可以获得当前Continuation的引用,并执行一段代码,最后挂起当前协程。Continuation代表了当前计算的延续,通过Continuation.resume()我们可以恢复执行上下文。因此只需在写操作完成时回调cont.resume(0),我们又回到了suspendCoroutine处的执行状态(包括caller writeQuery),程序继续执行,代码返回,执行log。从writeQuery看我们用同步的写法完成了异步操作。当协程被suspendCoroutine切换走后,线程可以继续调度其他可以执行的协程来执行,因此不会真正阻塞,我们因此获得了性能提升。 从这里看,只需要我们有一个机制来保存/恢复执行上下文,并且在阻塞库函数里采用非阻塞+回调的方式让出/恢复协程,就可以使得以同步形式编写的程序达到和异步同样的效果了。 理论上只要有一个库包装了所有JDK阻塞方法,我们就可以畅快地编写异步程序了。改写的阻塞库函数本身需要足够地通用流行,才能被大部分程序使用起来。据我所知,vert.x的kotlin支持已经做了这样的封装。 虽然vert.x很流行,但是无法兼顾遗留代码以及代码中的锁阻塞等逻辑。因此不能算是最通用的选择。实际上Java程序有一个绕不过的库——JDK。Wisp就是在JDK里所有的阻塞调用出进行了非阻塞+事件恢复协程的方式支持了协程调度,在为用户带来最大便利的同时,兼顾了现有代码的兼容性。 上述方式支持了,每个线程不太需要阻塞,Wisp在Thread.start()处,将线程转成成了协程,来达到了另一目的: 活跃线程数约等于CPU个数。因此只需要使用Wisp协程,所有现有的Java多线程代码都可以获得异步的性能。 手工异步/Wisp性能比较 对于基于传统的编程模型的应用,考虑到逻辑清晰性、异常处理的便利性、现有库的兼容性,改造成异步成本巨大。使用Wisp相较于异步编程优势明显。 下面我们在只考虑性能的新应用的前提下分析技术的选择。 基于现有组件写新应用 如果要新写一个应用我们通常会依赖JDBC、Dubbo、Jedis这样的常用协议/组件,假如库的内部使用了阻塞形式,并且没有暴露回调接口,那么我们就没法基于这些库来写异步应用了(除非包装线程池,但是本末倒置了)。下面假设我们依赖的所有库都有回调支持,比如dubbo。 1)假设我们使用Netty接受请求,我们称之为入口eventLoop,收到请求可以在Netty的handler里处理,也可以为了io的实时性使用业务线程池。 2)假设请求处理期间需要调用dubbo,因为dubbo不是我们写的,因此内部有自己的Netty Eventloop,于是我们向dubbo内部的Netty eventLoop处理IO,等待后端响应后回调。 3)dubbo eventLoop收到响应后在eventloop或者callback线程池调用callback。 4)后续逻辑可以在callback线程池或者原业务线程池继续处理。 5)为了完成对客户端的响应最终总是要由入口的eventloop来写回响应。 我们可以看到由于这种封装导致的eventLoop的割裂,即便完全使用回调的形式,我们处理请求时多多少少要在多个eventLoop/线程池之间传递,而每个线程又都没法跑到一个较满的程度,导致频繁地进入os调度。与上述的每个线程不太需要阻塞原则相违背。因此虽然减少了线程数,节约了内存,但是我们得到的性能收益变得很有限。 完全从零开始开发 对于一个功能有限的新应用(比如nginx只支持http和mail协议)来说我们可以不依赖现有的组件来重新写应用。比如我们可以基于Netty写一个数据库代理服务器,与客户端的连接以及与真正后端数据库的连接共享同一个eventloop。 这样精确控制线程模型的应用通常可以获得很好的性能,通常性能是可以高于通过非异步程序转协程的,原因如下: 线程控制更加精确:举个例子,比如我们可以控制代理的客户端和后端连接都绑定在同一个netty线程,所有的操作都可以threadLocal化 没有协程的runtime和调度开销(1%左右) 但是使用协程依旧有一个优势:对于jdk中无处不在的synchronized块,wisp可以正确地切换调度。 适应的Workload 基于上述的背景,我们已经知道Wisp或者其他各种协程是适用于IO密集Java程序设计的。否则线程没有任何切换,只需要尽情地在CPU上跑,OS也不需要过多的干预,这是比较偏向于离线或者科学计算的场景。 在线应用通常需要访问RPC、DB、cache、消息,并且是阻塞的,十分适合使用Wisp来提升性能。 最早的Wisp1也是对这些场景进行了深度定制,比如hsf接受的请求处理是会自动用协程取代线程池,将IO线程数量设置成1个后使用epoll_wait(1ms)来代替selector.wakeup(),等等。因此我们经常受到的一个挑战是Wisp是否只适合阿里内部的workload? 对于Wisp1是这样的,接入的应用的参数以及Wisp的实现做了深度的适配。 对于Wisp2,会将所有线程转换成协程,已经无需任何适配了。 为了证明这一点,我们使用了web领域最权威的techempower benchmak集来验证,我们选择了com.sun.net.httpserver、Servlet等常见的阻塞型的测试(性能不是最好,但是最贴近普通用户,同时具备一定的提升空间)来验证Wisp2在常见开源组件下的性能,可以看到在高压力下qps/RT会有10%~20%的优化。 Project Loom Project Loom作为OpenJDK上的标准协程实现很值得关注,作为java开发者我们是否应该拥抱Loom呢? 我们首先对Wisp和Loom这里进行一些比较: 1)Loom使用序列化的方式保存上下文,更省内存,但是切换效率低。 2)Wisp采用独立栈的方式,这点和go类似。协程切换只需切换寄存器,效率高但是耗内存。 3)Loom不支持ObectMonitor,Wisp支持。 synchronized/Object.wait()将占用线程,无法充分利用CPU。 还可能产生死锁,以Wisp的经验来说是一定会产生死锁(Wisp也是后来陆续支持ObectMonitor的)。 4)Wisp支持在栈上有native函数时切换(反射等等),Loom不支持。 对dubbo这样的框架不友好,栈底下几乎都带有反射。 总根据我们的判断,Loom至少还要2年时间才能到达一个稳定并且功能完善的状态。Wisp的性能优秀,功能要完整很多,产品本身也要成熟很多。Loom作为Oracle项目很有机会进入Java标准,我们也在积极地参与社区,希望能将Wisp的一些功能实现贡献进社区。 同时Wisp目前完全兼容Loom的Fiber API,假如我们的用户基于Fiber API来编程,我们可以保证代码的行为在Loom和Wisp上表现完全一致。 FAQ 协程也有调度,为什么开销小? 我们一直强调了协程适用于IO密集的场景,这就意味了通常任务执行一小段时间就会阻塞等待IO,随后进行调度。这种情况下只要系统的CPU没有完全打满,使用简单的先进先出调度策略基本都能保证一个比较公平的调度。同时,我们使用了完全无锁的调度实现,使得调度开销相对内核大大减少。 Wisp2为什么不使用ForkJoinPool来调度协程? ForkJoinPool本身十分优秀,但是不太适合Wisp2的场景。 为了便于理解,我们可以将一次协程唤醒看到做一个Executor.execute()操作,ForkJoinPool虽然支持任务窃取,但是execute()操作是随机或者本线程队列操作(取决于是否异步模式)的,这将导致协程在哪个线程被唤醒的行为也很随机。 在Wisp底层,一次steal的代价是有点大的,因此我们需要一个affinity,让协程尽量保持绑定在固定线程,只有线程忙的情况下才发生workstealing。我们实现了自己的workStealingPool来支持这个特性。从调度开销/延迟等各项指标来看,基本能和ForkJoinPool打平。 还有一个方面是为了支持类似go的M和P机制,我们需要将被协程阻塞的线程踢出调度器,这些功能都不适宜改在ForkJoinPool里。 如何看待Reactive编程? Reactive编程模型已经被业界广泛接受,是一种重要的技术方向;同时Java代码里的阻塞也很难完全避免。我们认为协程可以作为一种底层worker机制来支持Reactive编程,即保留了Reactive编程模型,也不用太担心用户代码的阻塞导致了整个系统阻塞。 这里是Ron Pressler最近的一次演讲,作为Quasar和Loom的作者,他的观点鲜明地指出了回调模型会给目前的编程带来很多挑战 。 Wisp经历了4年的研发,我将其分为几个阶段: 1)Wisp1,不支持objectMonitor、并行类加载,可以跑一些简单应用; 2)Wisp1,支持了objectMonitor,上线电商核心,不支持workStealing,导致只能将一些短任务转为协程(否则workload不均匀),netty线程依旧是线程,需要一些复杂且trick的配置; 3)Wisp2,支持了workStealing,因此可以将所有线程转成协程,上述netty问题也不再存在了。 目前主要的限制是什么? 目前主要的限制是不能有阻塞的JNI调用,wisp是通过在JDK中插入hook来实现阻塞前调度的,如果是用户自定义的JNI则没有机会hook。 最常见的场景就是使用了Netty的EpollEventLoop: 1)蚂蚁的bolt组件默认开启了这个特点,可以通过-Dbolt.netty.epoll.switch=false 来关闭,对性能的影响不大。 2)也可以使用-Dio.netty.noUnsafe=true , 其他unsafe功能可能会受影响。 3)(推荐) 对于netty 4.1.25以上,支持了通过-Dio.netty.transport.noNative=true 来仅关闭jni epoll,参见358249e5 原文发布时间为:2019-10-31作者: 梁希本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:除了KPI,考核员工还有什么方式?OKR是一种简单的管理方法,但是对于其误解和各种疑惑却很多。推崇者把OKR说得是无所不能,反对者认为不过是新瓶装旧酒,和KPI差不多。到底OKR和KPI,哪种方式更靠谱?技术leader该怎么选?今天,阿里资深技术专家燕标谈谈,他对OKR的思考。 引子 每过一段时间,就会出现一波新的管理概念方法和理念。最近OKR逐渐走到聚光灯下,引起越来越多人的注视。OKR简单后面隐藏着相对复杂的一面。简单的事物运行起来往往可以非常复杂,例如围棋。 关于OKR的书对于如何实施OKR有详细的说明和讨论。还包括成功的实例,劝人去使用体验OKR。但是看了这些书,总是有一点隔靴搔痒的感觉。例如,几乎所有的书上都反复强调不要把OKR和KPI混淆而和绩效挂钩,但都没有给出让我信服的解释。这种情况下依样画葫芦当然也可能做一个七八分象,但是终究不能让人坦然。我的个性让我去刨根问底,希望找出OKR设计者背后的一以贯之的逻辑。本文是自认为可以自圆其说的解释。不能说是终极的答案,只是希望抛砖引玉,写出来一起讨论。 什么是OKR? 对于OKR,百度百科有如下介绍: OKR(Objectives and Key Results)即目标与关键成果法,是一套明确和跟踪目标及其完成情况的管理工具和方法。OKR的主要目标是明确公司和团队的“目标”以及明确每个目标达成的可衡量的“关键结果”。OKR可以在整个组织中共享,这样团队就可以在整个组织中明确目标,帮助协调和集中精力。——百度百科 简单说,OKR是一种管理工具,一个思考框架,一种方法,让团队的协作更加具有成效。 OKR有两个基本的组成部分: 目标:设定一个定性的时间内目标(通常是一个季度)。目标描述应该说明: 为什么目标很重要? 它是如何同公司目标关联的,有哪些具体依赖? 它所支撑或依赖的内部客户有哪些? 关键结果:是由量化指标形式呈现的,用来衡量在这段时间结束时是否达到了目标。作为关键结果,必须具体: 有挑战性; 只写关键项,而非全部罗列; 说明结果,而非任务; 用积极正向的语言表述。 目标为主,关键结果为从。在实施过程中,OKR可以根据情况调整或者重新定义。 OKR可以在不同的层级设施:公司层面、不同团队层面、个人层面。个人和团队都要支持其上级组织的目标。OKR在公司内部实现一定程度的透明,相互对齐,保证相互之间的目标的关联性。所有的OKR因此形成了一张非常复杂的网络。 数字化管理 目标是定性的,关键结果则要包含可测量的(measurable)指标。 目标和指标之间是密切关联的,越接近指标,应该也离目标越近。但是两者之间并不是强因果关系,而只能说有关联性。各项指标都达标,并不意味着达到了预期的目标。或者达到了预期目标,但是有些指标没有达标。正如我们觉得身体不舒服,但是各项体检指标都达标。或者,我们觉得身体很正常,但是指标可能有些不正常。下面举一些目标和指标对应的例子,体会其间微妙的区别: 以滴滴和快的为例。如果关注目标,快的的目标应该是超越滴滴;如果关注指标,快的的指标应该是司机数量、订单数、乘客数等; 以足球运动员为例。如果关注目标,我们会想到夺冠、四强、保级;如果关注指标,那我们就会想到进球数、助攻数、跑动距离、比赛场次等; 以程序员为例。如果我们关注目标,我们会想接下来我应该做什么事情,是要解决产品的卡顿问题,还是可以引入大数据来做精准推荐;而如果关注指标,因为我们的工作是编程,那我们就会想哪些指标可以衡量编程工作呢?我们想到的是代码行数、bug 数、单元测试覆盖率这些; 数字化代表了先进、精确、可控、智能等。数字给控制和决策提供了非常好的抓手,加强了管理的效率。但是如果深入数字化,立刻就体会到数字化的困境: 数据太多,如何进行分析和聚合作为关键指标,需要一个更高级的指导原则。 数据具有的片面性和误导性。如果坚持优化某一指标,往往会危及整体利益。正如有练习健美的人为了追求肌肉量和形态美,最后往往走极端,而危及生命。所以数字化管理也存在一个过犹不及的问题。数字可以用于决策和控制的手段,但是如果作为终极追求目标则需要谨慎。 指标的优点在于精确,但是不能全面反应意图。目标反应了真实的意图,但是无法精确表达,只能定性说明。模糊性是它的特点,也是它的优点。这个类似于测不准原理。OKR就是考虑到这种矛盾性,有机结合一阴一阳两个方面,以期达到一个平衡。 OKR框架 前面说过OKR的目标通常是一个季度的。所以OKR是专注短周期的执行的,通常认为OKR是一个战术性执行框架,而不是战略执行的手段。它通过聚焦目标,不断审查关键指标,来提高执行力。不过这里有一个问题,就是如何保证OKR的目标和总体战略不脱节。为此OKR的实施还需要一个OKR框架,让OKR放在公司的使命、愿景框架下去考虑。 下面就是OKR框架: 上面三层使命、愿景、战略是中长期的目标的管理,相对是规划阶段。这是管理艺术化的领域,没有规定的程序,也无法实施工具化的支持。关于这些方面的书数不胜数。这里就不再介绍了。使命、愿景和战略为OKR的执行设置了上下文,要清晰传递业务重点,所以其质量具有关键性的作用。如果没有明确的战略就制订OKR,则只是一种肤浅的形式上的模仿。 下面三层目标、关键结果、任务是中短期管理,是执行阶段,是管理科学化的领域。团队的执行力是公司的核心竞争力。这个层次的关键,一是要做正确的事,一是要正确的做事。大规模团队协作是非常具有挑战的事情。协作就必须要有共同的规则,有流程和工具的支持,管理方法的创新往往集中在下面三层。 OKR所对应的是目标和关键结果两个层次。在同一个层次的还有比较熟悉的KPI。在任务层的管理则是类似精益开发、Scrum等管理方法所关注的领域。这一层次关注的任务更具体。 在这个框架中,对于下层的工具支持比较完善。随着时间发展,管理工具自然会向上层延伸。OKR管理现在已经成为协作平台的下一个竞争点。 OKR分析 在所有的OKR介绍中,都反复强调:OKR不是考核工具,最好不要和绩效挂钩。这是最让人疑惑的地方,主要是因为OKR和KPI都包含可衡量的指标。设定了指标而又不用于考核,那是为什么呢? 要讨论OKR,就绕不开和KPI的对比。也只有把两者对比起来讨论,才能加深对OKR的认识。我们先有必要简单介绍一个KPI。 KPI简介 KPI系统是一个纵向的指标体系:先确定公司层面关注的KPI,再确定部门乃至个人要承担的KPI,由于KPI体系是经过层层分解,这样,就在指标体系上把战略落到“人”了。而要把战略具体落实,需要“显性化”,要对每个层面的KPI进行赋值,形成一个相对应的纵向的目标体系。所以,在落实战略时有“两条线”:一条是指标体系,是工具;另一条是目标体系,利用指标工具得到。 KPI制订需要遵循的SMART原则: S代表具体(Specific),指绩效考核要切中特定的工作指标,不能笼统; M代表可度量(Measurable),指绩效指标是数量化或者行为化的,验证这些绩效指标的数据或者信息是可以获得的; A代表可实现(Attainable),指绩效指标在付出努力的情况下可以实现,避免设立过高或过低的目标; R代表有关联性(Relevant),指绩效指标是与上级目标具明确的关联性,最终与公司目标相结合; T代表有时限(Time-bound),注重完成绩效指标的特定期限。 总体来说,KPI是对重点经营活动的衡量,而不是对操作过程的反映。它要求精确,注重目标,而不对过程有太多关注。 KPI由上级与员工共同参与完成,双方达成一致意见后,作为最后员工考核的要素和依据。从表面看,KPI的优点明显:目标明确,员工和公司目标一致,有利于公司的战略实现。在一定程度上可以激励员工。但是在实施过程中,KPI的弊病也是明显的: 因为KPI直接影响到最后的考核结果,所以员工一般都偏于保守,争取对于自己有利的指标,这样会从根本上抑制团队的创造力的积极性,让团队追求稳定和平庸。 如果机械的按照KPI考核,个人和团队都可以为了某一个时段的指标达标,而损害长期的目标,从而妨碍战略的优化执行。局部的优化和全局优化间的矛盾在这种意义上是无法避免的。 KPI重结果,轻过程,做得极端的企业基本上是只看结果的。从而KPI对于赋能团队和个人是有限的。 让过程精彩,为结果买单 阿里有句土话:为过程喝彩,为结果买单。这作为一个公司的考核原则是可以的。但是从团队管理的角度讲,则不能仅仅是站在旁边为团队的同学喝彩,更重要的是要保证团队的过程精彩。OKR针对上面KPI的一些缺点,不和考核挂钩,就是为了让过程精彩。这便是OKR和KPI本质定位的不同。 如果我们把OKR和Scrum来对比,可能可以更加容易理解为何OKR不要和绩效挂钩。Scrum在实施过程中,也会要定义一些关键指标来分析团队实施的效率。但是我们清楚,一个团队Scrum实施得流畅,并不能保证最终产品就一定好,一定会给最终客户带来价值。 从这种意义上讲,OKR实际上和Scrum更类似,而不是和KPI更类似。只是OKR管理的是季度目标,而Scrum管理的是月度目标。两者实际可以相互补充。 归纳起来,OKR的KPI之间有如下区别:KPI主要是一个考核工具,OKR是一个对团队和个人的赋能工具。OKR的关注点是结果和过程并重的,特别是探索性的工作可能会更偏重于过程。 70分万岁 OKR的另一个核心理念是70分万岁,就是鼓励团队定一个足够高的目标,团队在非常努力之后也只能达到70%。不过由于创新等不确定性,团队不会因为只拿了30分而被惩罚,这个只是表明指标太高了,或者目标不正确,需要调整。同时OKR也不鼓励设置能拿到100分的指标,因为那只是表明指标设置得缺乏挑战性。70分是一个满意的分数,较好平衡了挑战和现实。 取法于上,仅得为中,取法于中,故为其下。设置70分的目标,就要求团队能够打破常规思维,要求行动有一定的创新,有一定的“杠杆率”,要求“行必果”,就是所用的行动都一定要有结果的。所以OKR的实施重点在于激励创新,发现业务的杠杆点,最大可能撬动业务发展。 70分原则是为了让团队放下一时的得失之心,更好发挥团队的潜力。这个原则集中实际上体现了OKR的理念:激发每个人的自我驱动力。 对比OKR的冒险精神,Scrum比较保守,要求百分百完成目标。这也是因为两者的目的不一样。基本来说,越是低层的管理工作,越需要可预测性,需要考虑各种条件限制。越往上,则越需要跳出现实的限制,通过战略来拉动执行力。 管理之道 管理在汉语中我们现在都当做一个词语,但是实际上包括两个方面:管和理。 管原意为细长而中空之物,其四周被堵塞,中央可通达。使之闭塞为堵;使之通行为疏。管,就表示有堵有疏、疏堵结合。所以,管既包含疏通、引导之意,又包含限制、约束之意。总之是通过制度规章等约束条件引导事物向期望的方向发展。 理者,治玉也,就是把一块璞玉粗糙的表层清理掉,露出下面美玉细腻的花纹来。引申的意义,理就是要发现、开发人物中善和美的一面。 管是止恶,理是扬善。管理,一阴一阳结合,才是管理的比较全面的意义。如果说KPI偏向管。那么OKR更倾向于理。两者是可以有机结合的。 任何一种新的理论和方法的提出,都具有它的背景。工业时代主要靠提高机器的效率来提高劳动生产率,人和机器是同等对待的。现代企业,人才是最重要的资源。员工提高执行力和创造力是企业的核心竞争力。效能的关键的是要提高组织的活力和创造力。一个具有自驱力的员工和一个被动的员工间的效率可以有一个数量级的差别。但是这种组织的能力不是天生的,而是需要培养和建设的。OKR就是组织能力建设工具,通过一个OKR团队来帮助其他个人和团队成长,这也是OKR在各大高科技公司广受欢迎的根本原因。 总体来说,实施OKR是希望得到如下收益: 聚焦在最重要的事情上。 提升敏捷性和快速应对的能力。 通过公开透明促进跨部门的一致性。 促进前瞻性思考,鼓励创新。 OKR的实施 实施OKR,需要转换心态,把OKR不是当做考核工具,而是来帮助团队更好成长的助手。人都是怕繁琐的,对于新的管理流程都会不自觉有抗拒的心理。让每个人对于OKR的心态变化,认识到其是帮助个人成长的工具,对于其成功落地是非常关键的。当然这对实施过程中的推动的团队要求非常之高。OKR和KPI在具体流程上类似,而其根本的不同在于这一点用心处。这一点不同在实施过程中容易被忽略掉,但却至关重要。 OKR只是一个工具,实施其过程是容易的,真正的难点在于:制订高质量的目标和准确衡量目标效果的关键成果指标,以及持之以恒的执行。这需要结合业务进行深入的分析和思考。 OKR和KPI解决不同的问题,所以两者可以并存。另外,OKR的实施可以从局部实施起,例如从某个团队、项目,或者管理的中高层开始,在积累经验后确定如何推广。 [1]中总结的OKR十大关键注意事项: 制订OKR前 帮助大家理解为什么要实施OKR。 得到高管的赞助。 提供OKR培训。 确保存在一个清晰的战略。 制订OKR时 目标应定性而非定量。 避免所有OKR都是自上而下制定的。 解决KR上出现的一系列问题,例如条数过多、质量差等。 使用一致的评分系统。 制定OKR后 避免制定后就束之高阁。要有周例会和中期审核计划。 链接OKR确保同上层组织对其一致。 总结 OKR当然不是一种包治百病的银弹。但是其鼓励创新和冒险,支持自下到上的挑战,激发团队和个人主动性的精神,在当前的行业快速发展的条件下,还是很具有意义的。 《晏子春秋》说:橘生淮南则为橘,生于淮北则为枳,叶徒相似,其实味不同。所以然者何?水土异也。实施OKR,需要团队文化的支持,需要一个良好的文化氛围才可能发挥其威力,否则终将蜕化为类似KPI的考核手段。这需要中坚的团队,能够深刻理解OKR的设计理念,才不会在实施过程中走样。OKR学其形易,而学其神难。在实施过程中一定要因地制宜,变通实施,坚持之后才能取得预期的效果。 参考资料:1.OKR:源于因特尔和谷歌的目标管理利器 机械工业出版社2.https://baike.baidu.com/item/关键绩效指标法 3.http://www.woshipm.com/zhichang/765124.html4.https://worktile.com/blog/okr/1b53bcdb66cba75.https://www.zhihu.com/question/22478049/answer/23833548 6.https://www.atiim.com/blog/how-okrs-complement-scrum/7.https://baijiahao.baidu.com/s?id=1623530400696261877&wfr=spider&for=pc OKR与KPI的三个本质不同 原文发布时间为:2019-10-30作者:燕标本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:从深蓝战胜象棋冠军到AlphaGo战胜围棋冠军,每一次机器智能在特定领域战胜人类,都会引发整个社会的广泛关注。洞察了棋类博弈真相的机器智能,接下来能洞察网络安全的真相并且在黑客博弈中战胜人类吗?在机器智能炙手可热的今天,或许我们该静下心来,去理解机器智能的本质、网络安全的困境以及未来二者结合的挑战。 安全的真相是什么?安全的过去是人与人的对抗,安全的现状是攻击者加攻击机器对抗防御者加防御机器。而未来安全的终局,一定是机器与机器的自主对抗。从这个角度来看,安全的本质其实是智能体之间的知识对抗,智能可以是碳基的智能,也可以是硅基的智能。到终局那一天我们有没有「搞清楚安全」显得一点都不重要,重要的是到那一天我们亲手锻造的机器智能,作为人类智能的延伸,注定是会早于我们一步,提前触达安全的真相。 通用技术与人类发展 技术是人类自身能力的延伸,发明技术是人类最大的天赋。早在现代智人出现之前,早期的原始人就发明了各种技术,使得他们在与其他动物的生物竞争中更占优势。而在人类历史进程中,生产力和经济水平的一次次跃迁,背后的核心推动力是一代又一代通用技术(GPTs General Purpose Technologies)的发明。通用技术通过对已有经济结构和社会结构的影响,彻底影响着人类的发展进程。 通用技术是单一可识别的基础性共性技术,目前为止人类历史上只有二十来种技术可被归类为通用技术,这些技术具有如下几个特点: 「无处不在」通用技术有着各种各样的使用用途以及大量广泛的使用场景; 「持续改进」随着时间的推移,通用技术不断在改进使用成本也不断降低; 「驱动创新」通用技术使技术创新和技术发明更加容易,催生更多新产品。 从新石器时代农业革命,对动植物的驯化技术、文字书写技术,到18-19世纪第一次工业革命,蒸汽引擎技术、工厂系统、铁路系统……,到第二次工业革命,内燃机技术、电力技术、汽车技术、飞机技术……,再到20世纪信息革命,计算机、互联网、生物科技等。通用技术的发明间隔时间越来越短、密集程度越来越高、影响范围也越来越大、生产力的提升也越来越快。 同一时代各种通用技术之间的技术衔接产生的协同效应,更是对生产力提升、经济发展、促进创新起到了叠加推动的作用。蒸汽时代,蒸汽引擎提供动力能源,铁路网连接各个物理空间传输钢铁等物资,应用到各种机器系统。电气时代,中央电站提供电力能源,电力网连接各个物理空间传输电流,应用到各类电气系统。 信息时代,个人电脑(或服务器)提供计算能力,互联网连接传输数据,连接各个数字空间的信息系统。而在智能时代,通用计算(云边端等各类计算形态)提供计算能力,物理空间和数字空间的边界会越来越模糊形成融合空间,万物互联网连接融合空间中的各个智能系统。不同的时代,通用技术之间有着相似的协同方式。蒸汽时代给机器以动能,电力时代给机器以电能,信息时代给机器以数据,智能时代给机器以知识。 机器智能的历史发展 在所有通用技术中,机器智能又是最为特殊的一种通用技术,这是人类第一次发明让机器能自主获取知识的技术,也是人类第一次有能力打造非碳基体系的智能体。1882年2月一个寒冷的下午,年轻的尼古拉·特斯拉完成了困扰其5年的交流电发电机设想,欣喜若狂地感叹道「从此之后人类不再是重体力劳动的奴役,我的机器将解放他们,全世界都将如此」。 1936年,为证明数学中存在不可判定命题,24岁的艾伦·图灵提出「图灵机」的设想,1948年在论文《 INTELLIGENT MACHINERY》中描绘了联结主义的大部分内容,紧接着在1950年发表《COMPUTING MACHINERY AND INTELLIGENCE》,提出了著名的「图灵测试」。同年,马文·明斯基与其同学邓恩·埃德蒙建造了世界上第一台神经网络计算机。 1955年冯·诺伊曼接受了耶鲁大学西里曼讲座的邀请,讲稿内容后来汇总成书《THECOMPUTER AND THE BRAIN》。1956年,约翰·麦卡锡在达茅斯学院夏季学术研讨会上首次提出了「Aritificial Intelligene」的概念。至此,机器智能的历史序幕正式拉开,符号主义(Symbolism)、联结主义(Connectionism)、行为主义(Actionism)三大流派相继形成。 机器智能发展至今,经历了几次浪潮和寒冬,三大主义也各自起起落落。50年代起,以专家系统、经典机器学习为代表的符号主义长期占据统治地位。相比之下联结主义则历经了一波三折,从感知机的提出到80年代反向传播的发表,再到深度学习借助算力与数据大获成功,到2018年三巨头Geoffrey Hinton、Yann LeCun、Yoshua Bengio获得图灵奖,最后才变得炙手可热。而以强化学习为代表的行为主义在2016年AlphaGo、AlphaZero的横空出世之后大获关注,更是被誉为通向通用机器智能的必经之路。 人类智能的演化经历了上百万年,机器智能演化迄今为止也才六十余年。尽管通用机器智能依旧还很遥远,但今天机器智能在很多领域已经逐渐超过了人类智能。过去这六十年,数据计算能力、数据存储能力、数据传输能力都至少提升了1000万倍。同时数据资源的增长速度,更是远远超过摩尔定律增长的速度,预计到2020年全球数据总量能达到40ZB。机器智能今天已经到达通用技术爆炸的一个关键节点,同时在其他通用技术的协同作用下,这一次通用技术引发的变革会比以往任何一次都来得更剧烈。 数据驱动到智能驱动 「商业智能与智能商业」、「安全智能与智能安全」……类似这样的词还有很多,二者之间核心的区别前者是单点的智能,后者是全局的智能,前者是基于数据驱动,而后者是基于智能驱动。「数据驱动」与「智能驱动」看似相似但却有着根本性的区别,最本质的不同是背后决策主体的不同。「数据驱动」最终还是依赖人类来做决策,数据只是提供了能够做出更好决策的辅助判断信息,而「智能驱动」则是机器取代人类直接做在线决策。 人类大脑受认知偏见的影响一直是生命进化的结果。受限于人脑信息传输带宽和信息处理速度的限制,从早期狩猎者阶段开始,人类就逐步形成了基于简单启发式的推理决策系统,规避了处理大量信息的高额成本。这使得人类在处于各种危险的环境时能够快速、几乎无意识地做出决策,文明才得以延续至今。然而,快速和几乎无意识的决策并非意味着总是最佳甚至是准确的决策。 启发式的方法通过遗传,成为刻入我们大脑中预先加载的认知偏差,这些「偏见」以偏离理性客观的方式影响人类的决策。直到「数据驱动」时代的来临,丰富海量的在线数据为更好的决策提供了辅助判断的依据。我们用通用计算、海量数据处理技术,将数据量减少到人脑可消化的摘要范围之内,用于各种应用场景下的辅助决策。 「数据驱动」相比以往基于「直觉驱动」或「经验驱动」有着无法比拟的优势,但人类在这其中仍然扮演着「中央处理器」的决策主体,这依然存在着局限性。人脑处理器的吞吐量限制,无法处理全量原始数据,只能将全量数据资源变为「汇总数据」或「摘要数据」,进而再从其中提取知识。这个过程注定是伴随着信息量的损失,从而会丢掉全量数据中的部分隐含关系、数据模式以及数据背后的洞察。 「智能驱动」是让机器智能直接做线上决策,无论是决策效率、规模程度、客观程度还是进化成长速度,都是「数据驱动」所无法比拟的。「智能驱动」是直接从全量数据资源中提取全量知识,然后运用全量知识直接进行全局决策。「数据驱动」本质上是汇总数据加人类智能,「智能驱动」的本质则是全量数据加机器智能。 然而现实的现状是在业务场景中我们大量的决策连「数据驱动」都还没做到,更谈不上「智能驱动」。机器智能实现「感知」只是第一步,实现「决策」则是更为关键的一步,现阶段的机器智能正如丘吉尔的一句话「Now this is not the end, it is not even the beginning of the end.But it is perhaps the end of beginning」。那么,到底什么才是真正的机器智能系统? 智能系统的核心范式 真正意义上的智能系统,实例的核心范式一定有如下几个组成部分:感知体系、认知体系、决策体系、行动体系。同时,一个智能系统的实例,一定离不开与环境的交互,过去我们总是过多的强调和关注系统内在本身,却容易忽视与环境交互的作用。 感知体系的作用是对环境进行观测和沉淀,产出的是数据。一切数据的产生都源于对环境的观测和沉淀,观测和沉淀背后的动机是我们测量、记录、分析世界的渴望。信息时时刻刻存在于环境中(数字空间或物理空间),在不同的场景下,我们用硬件、软件、算法的方式,将其「数据化」。硬件有如传感器、摄像头等,软件如日志记录器、数据采集器等,算法如各类智能视觉算法、智能语音算法等。终有一天,我们能够将一切物理空间都数据化,将物理空间完完全全映射到数据空间。 认知体系的作用是对数据进行归纳和总结,提炼出知识。人类理解的知识一定是要用自然语言表达,而对机器而言,用能够代表问题空间的数据集进行训练,再用训练好的「模型」来在新的数据空间中进行推理。只要是能解决特定目标任务,无论其表现形态是向量、图谱还是自然语言,其实都是知识,特征空间的表达本身就是一种知识。 决策体系的作用是对目标任务进行规划和决策,生成对目标任务的策略。行动体系根据策略执行具体动作,和环境进行交互、对环境产生影响。动作作用于环境后形成反馈,反馈又促进感知体系感知更多的数据,进而持续获取更多的知识,对目标任务作出更好的决策,形成闭环持续迭代进化。 从这个角度来看,机器智能的本质,实质是一种观测环境沉淀数据、归纳数据提炼知识、规划目标在线决策、作出行动影响环境的自主机器。机器智能是一种自主机器,而自主机器与过去自动化机器的最大区别在于其能否自主获取解决目标任务的知识。 单体智能到群体智能 今天大多数的智能系统,都是一个个孤立分布的单体智能实例,解决的相应也是一个个孤立分布的单体问题。云计算的本质是「计算在线」,大数据的本质是「数据在线」,而机器智能最终也需要实现让智能在线,让智能实例之间进行自主在线交互。 单个智能实例都是由「感知-认知-决策-行动」的体系构成的自主系统,有着自己的世界表征形式,能自主完成自身的目标任务。在同一个动态复杂的博弈环境之中,实例与实例之间通过互联实现在线,彼此存在相互作用,可以合作、竞争,可以竞合并存,也可以既不合作也不竞争。一个实例的策略变化不光会影响自身的环境,也会影响其他实例的策略变化。 对于合作的多个智能实例之间,可以选择共享数据、知识、策略或动作,协调协作以完成更为复杂的目标任务,共同形成更为高阶的智能实例。当单位空间内智能实例的覆盖密度足够大的时候,单体智能开始向群体智能演进。 智能与安全的四象限 安全是所有技术中最为特殊的一种,严格意义上甚至或许都不能称「安全」为一门技术。早在人类还未发明任何技术之前,安全就已经伴随着人类的各种活动。迄今为止,没有任何一种技术是安全领域独有或者说从安全领域长出来的,但安全从来都是与其他技术相伴相生、相辅相成。 任何一门通用技术,与安全的结合都有如下四种方式。机器智能技术也不例外,纵向是「给智能以安全」和「给安全以智能」,横向是「攻击视角」和「防御视角」。给智能以安全,是指机器智能技术本身会带来新的安全问题,一种是机器智能自身脆弱性导致的安全问题,一种是机器智能引发周边场景衍生出的安全问题。给安全以智能,是指将机器智能应用于安全场景,攻击者利用机器智能赋能攻击,防御者利用机器智能赋能防御。 而在这四个象限中,新技术与安全发生交集的时间和发展的成熟程度又有所不同。攻击者相比防御者而言,有更强的动机和利益,所以攻击相关的象限通常都会更容易去探索新技术去接纳新技术。防御者总是滞后,也总是容易沉迷于旧技术和人工经验营造出的安全假象中,导致第四象限总是发展最滞后最缓慢的一个象限。当然,这与防御视角自身的属性与困境也有直接关系。 机器智能的安全之困 围棋是简单的复杂游戏,而安全是复杂的简单游戏。1994年,认知科学家Steven Pinker在《The Language Instinct》中写道「对机器智能而言,困难的问题是易解的,简单的问题是难解的」。「简单的复杂问题」指的是问题空间是闭合的,但是问题本身却又有较高的复杂度,「复杂的简单问题」指的是问题空间是是无限开放式的,但问题本身却并没有很高的复杂度。今天机器智能技术在「简单的复杂问题」的领域,往往都比人类会更强,但对于「复杂的简单问题」,泛化界限引起的维数灾难,机器智能往往都会失效。 安全是一个典型的「复杂的简单问题」,莫拉维克悖论在安全领域更为明显。高度不确定性是安全最大的特点,安全自身最大的困境就是如何去应对「未知的未知」。很多时候我们问题都没定义清楚问题就冲上去说要用机器智能解决问题,这是绝大多数机器智能在安全领域失效的主要原因。今天在安全领域,不太需要去突破智能技术的天花板,亟待解决的反而是「定义清楚问题」,即如何闭合掉问题空间。 安全的问题空间通常都是无界的,同时问题空间对应的正负样本的样本空间却又严重的不对称。「未知的未知」引起的负向数据(如攻击数据、风险数据等)的严重缺乏导致特征空间的不对称,进而导致特征空间无法真正表征问题空间。「模型」是已有数据空间下关于世界的假设,并且用于在新的数据空间下进行推理。今天机器智能技术已经能很好的解决表示输入和输出之间的非线性复杂关系,但对于样本空间与问题空间存在的巨大鸿沟却依然比较乏力。 20世纪六十年代,贝尔-拉帕杜拉安全模型(Bell-La Padula )指出「当仅当系统开始于安全的状态,且一直不会落入非安全状态,它才是安全的」。由于安全的本质是对抗,对抗的存在导致安全领域的机器智能模型多数都逃不过的「上线即衰减」的命运。在训练集上表现良好的模型,对于大规模的现实环境,从上线那一刻起就在引起对抗升级,进而不断跌入失效的状态。模型衰减和封闭系统中的熵增一样,是一个必然。 同时,安全场景中对检测结果的准确性、结果可解释性都高度敏感。机器智能相比于传统安全中经常使用的基于规则、基于策略的检测技术,优势在于其强大的表征能力,但同时其不可解释性、模糊性导致推理结果在决策场景下无法直接使用,这也是今天很多智能安全系统大都只在做「感知」,至多也只是做辅助决策的原因。 然而这些都还不是最大的「困」,机器智能在安全领域最大的「困」是思维模式上的困局。安全的思维模式是「守正出奇」,而机器智能的思维模式是「Model The World」。这两种思维模式之间不仅存在巨大的差异,也异常难调和。一方面极少有人能同时驾驭这两种思维方式,另一方面把两种思维的人放到一起也极难协作起来,本质原因是缺少桥梁来衔接安全问题到算法问题之间的相互转换和定义。 问题空间之困、样本空间之困、推理结果之困、对抗衰减之困、思维模式之困,这些问题导致了今天绝大多数现实中的智能安全系统的表现都差强人意。或者也可以说得更悲观一点,今天在安全领域,迄今为止还没有真正意义的智能安全系统。 真正的智能安全系统 先来说说通用安全场景下的通用数据范式。柏拉图学派认为「我们感知的世界是洞穴里面墙壁上的投影」,现象世界都是理性世界的倒影,理性世界才是世界的本质或本原。「洞穴比喻」意味着存在一个外在的客观的知识体系,不依赖人类的认知而存在,人类探索知识的过程就是不断从现实世界的现象观察中,摸索、推测这个客观知识体系的过程。亚里士多德进一步奠定了本体论最初的思想,定义其为研究「存在」的科学,是形而上学的基本分支。再到17世纪,哲学家郭克兰纽 (R. Goclenius) 首次提出「Ontology」一词,再到20世纪60年代,机器智能领域开始引入Ontology的思想,之后又进一步演化出语义网、知识图谱等。 安全中的对抗本质是知识的对抗,获取知识更多的一方就能拥有更多的不对称优势。无论是威胁分析、情报研判、攻击检测、事件溯源……本质都是在探索知识的一个过程,这就是为什么Palantir的Gotham、IBM的I2、UEBA、各种威胁情报产品等等背后都不约而同或多或少借鉴了Ontology思想的根本原因。 而安全场景下的通用数据范式,也离不开Ontology。实体、属性、行为、事件、关系,通过这五大元数据类型,可以构建出所有安全场景中的数据架构(无论是基础安全、业务安全、数据安全、公共安全、城市安全……注:公共安全领域也单独关注「轨迹」这一类元数据类型,因「轨迹」是一种特殊的「行为」数据,故这里统一都合并成行为)。 实体:实体是客观存在并可以与其他对象区分开来的对象; 属性:属性即为标签,是描述实体的表述,对实体抽象方面的刻画; 行为:行为是实体在特定时间、空间下发出的动作; 事件:事件是一定时空或条件下所认识到的可识别的事情; 关系:关系是实体与其他实体之间的关联程度与表述。 安全领域绝大多数沉淀的源数据都是行为类数据,无论是网络流量日志、主机命令日志、业务日志、摄像头数据流、感知设备数据流……,这些都是行为数据。而实体、属性、关系、事件的产生都是从行为数据中进行萃取,通过对不同的行为数据运行不同的 Function 来产生。 当 Function 是生成事件的时候,即为安全检测问题,包括攻击检测、威胁检测、风险检测、异常检测等等。绝大多数安全检测问题的原子范式都可以抽象为Y=F(X),其中X是实体的行为数据,Y是检测结果,F是检测模型。F可以是基于规则、基于策略、词法语义、统计检测、机器学习、深度神经网络等等,Y可以是正常、异常、攻击或者未知。 更为复杂的检测场景也都可以通过一个个基本F与各类算子组装编排而成。每一种类型的F都有其优势和劣势,有不同的最优使用场景,并不存在一种绝对先进绝对领先的检测技术。事实上算法在安全检测中最应该关注的不是去做检测模型本身,而是能否自主化的根据各种场景生成最优的检测模型,并能自主化持续迭代检测模型。 真正意义上的智能安全系统一定也是具备感知体系、认知体系、决策体系和行动体系,同时和环境形成反馈闭环。感知体系至少包括异常感知器、攻击感知器、漏报感知器和误报感知器。「异常感知器」的作用一方面是保持感知「未知的未知」的能力,另一方面是利用「通过定义正常来寻找异常」的思想来解「样本空间之困」的问题。「攻击感知器」的作用是在异常数据的基础上去检测攻击,为了解「推理结果之困」的同时,也大大缩减推理结果误报漏报范围。「漏报感知器」和「误报感知器」是为了去解「对抗衰减之困」。由此可以看出,整个行业内大家最常关注的「用算法做攻击检测」,其实只是做了智能系统当中感知体系里很小的一小步。 认知体系沉淀的是跟安全相关的各种知识,至少包括正常知识、攻击知识、漏报知识、误报知识。安全知识可以是基于专家规则、向量、模型、图谱、自然语言等等,但无论是哪种形态,一定都是精细化个性化的「千人千面」的知识。即对每一个受保护对象(如用户、系统、资产、域名、数据等),沉淀形成适用于该受保护对象的一套感知异常、攻击、漏报、误报的知识。决策体系当中至少包含对目标任务的拦截策略、各类模型的上线下线等策略等,能自主决策哪些行为该拦截,哪些模型已经衰减该重训练该替换等。 行动体系当中是各类作用于环境的动作,如放行、阻断、重训练、发布等等。一个真正的智能安全 instance 里面包了含成千上万的 agent ,每一个 agent 只作用于其对应的受保护对象。最后,「问题空间之困」的解法是将开放的问题空间收敛为一个个小的闭合的风险场景,一方面靠的是四个感知器的级连形成的纵深检测,另一方面靠的就是「千人千面」的 agent 。 机器智能重塑新安全 安全领域发展至今,一直处于问题消灭得少概念却造得不少的阶段,亟待利用新技术去真正解决旧问题。机器智能在各个行业的炙手可热,同样也引起了安全行业的追捧。但今天安全领域的智能能力参差不一的同时,又难以分辨其真假。以至于但凡用了一丁点算法的,都会宣称「基于人工智能的XX安全系统」。同早年的智能驾驶领域一样,今天的智能安全也亟需统一的分级标准,用以明确不同级别智能安全技术之间的差异性。「安全的本质是智能体的对抗」,故根据自主对抗的程度,我们将智能安全划分为L0~5共如下6个级别: L0级别为「人工对抗」,即完全没有任何机器智能的能力,完全由防御者人工与攻击者进行对抗,对抗操作、感知判断、任务支援全都由人工进行。 L1级别为「辅助对抗」,由机器完成已知攻击的攻击检测和攻击防御,其余的操作(如感知未知威胁、感知漏报、感知误报等)由人类进行。 L2级别为「低度自主对抗」,由机器完成已知攻击攻击检测和攻击防御,并具备能感知未知威胁或误报漏报,其余由人类操作。 L3级别为「中度自主对抗」,由机器完成所有的对抗操作(攻击检测、攻击防御、主动感知未知威胁、误报漏报主动感知、对抗升级自动学习),根据系统要求,人类在适当的时候进行应答(中间过程必须需要人类参与)。 L4级别为「高度自主对抗」,由机器完成所有的对抗操作,根据系统要求,人类不一定提供所有的应答(中间过程非必须有人类参与),但只能作用于限定的特定的安全场景(如网络域、主机域等)。 L5级别为「完全自主对抗」,由机器完成所有的对抗操作,根据系统要求,人类不一定提供所有的应答,不限定特定的场景,作用于全域范围。 不同于智能驾驶技术,不同的 Level 采用的是完全不同的技术栈,智能安全中的 L0~5 是需要逐步往上构建往上发展。按照这个划分,今天行业内绝大部分的安全系统都是L1 的系统,极少一部分能达到 L2,但还没有真正意义上的L3及以上的智能安全系统。随着级别往上走,能够将防御者从低水平对抗中逐步释放出来,能更加关注高级对抗,L3是个分水岭,有望在5年内实现。「始于围棋终于安全」,机器智能在安全领域的终局是什么?网络层、主机层、应用层、业务层、数据层都分别有各自的智能实例,不同层的实例在线互联,实现真正意义的协同防御与情报共享。当智能「Intelligence」和情报「Intelligence」融合的那一天,才是真正的「Intelligence Remodels New Security」。 目前阿里云智能安全实验室正在多个领域打造L3级别的智能安全系统,致力于智能技术在云安全中的应用,现招聘安全算法专家和安全数据专家,寻志同道合者一起探索打造「智能重塑新安全」。目前在不到一年的时间,已经取得了一定的阶段性成果: LTD 攻击检测算法入选人工智能顶会 IJCAI 2019「Locate Then Detect:Web Attack Detection viaAttention-Based Deep Neural Networks」; WAF AI 内核助力阿里云 WAF 入选 2019 Gartner Web 应用防火墙魔力象限,算法能力被评为强势; Anti-Bot AI 内核助力阿里云爬虫风险管理入选 2018Forrester Bot Management 竞争者象限; 内容安全算法助力阿里云在国家级重保活动中平稳度过,无任何风险外漏; 上线[XDATA]安全数据内核、[XID]核心数据资产、[XService]智能安全服务、[弦+]安全知识引擎等一系列安全数据平台服务产品,上线百亿级节点千亿级边的复杂网络和图计算应用;上线QPS千万级的复杂流计算应用。 原文发布时间为:2019-10-29作者:楚安本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:在刚刚结束的乌镇世界互联网大会上,阿里云自主研发的POLARDB云原生数据库当选世界互联网领先科技成果,凭实力站上C位。这个”包管“了北京市每天800万人次的公交出行的下一代分布式数据库到底有多强大?我们请阿里云智能数据库事业部总经理鸣嵩跟大家一起聊聊。 POLARDB 是阿里云自主研发的下一代云原生分布式数据库。POLARDB 100%兼容MySQL、PostgreSQL等开源数据库,高度兼容Oracle语法,使用RDS服务的客户不需要修改应用代码,可以一键迁移到POLARDB,体验更大的容量,更高的性能,更低的成本,和更灵活的弹性。目前POLARDB是阿里云增速最快的数据库产品,广泛应用于互联网金融、政府便民工程、新零售、教育、游戏、社交直播等行业。 作为基于计算与存储分离架构的新一代云原生数据库,POLARDB的计算节点里主要实现了 SQL 解析和优化、以及查询并行执行与无锁高性能事务处理,计算节点之间通过高吞吐的物理复制协议同步内存状态。而存储层基于分布式文件系统PolarFS,通过ParallelRaft共识算法实现多数据副本间的强一致性,在存储层进行存储引擎的多版本页管理来支持全集群跨计算节点的Snapshot Isolation隔离级别。 基于计算与存储分离的先进架构 计算节点与存储节点之间通过理解数据库语义的智能互联协议将filter和projection等算子从计算层下推到存储层执行。为了保证事务和查询语句的低延迟,同时降低计算节点之间状态同步的延迟,计算节点和存储节点之间使用25Gb高速RDMA网络互联,采用Bypasskernel的用户态网络协议层进行通讯。基于计算与存储分离的先进架构,POLARDB可以从1个计算节点(2个CPU核)弹性伸缩到16个计算节点(最高达到1000核)的事务扩展能力,单实例存储容量从10GB按使用量弹性扩展到100TB。 计算节点与存储节点分离的架构设计给POLARDB带来了实时的水平扩展能力。由于单个数据库实例的计算能力有限,传统的做法是通过搭建多个数据库副本来分担压力,从而提供数据库Scale out 的扩展能力。然而,这种做法需要存储多份全量数据,并且频繁同步日志数据造成了过高的网络开销。此外,在传统数据库集群上,增加副本需要同步所有增量数据,这带来了同步延迟上涨的问题。 POLARDB 将数据库文件以及Redo log 等日志文件存放在共享存储设备上,确保主实例和所有副本共享同一份全量数据和增量日志数据。节点间只需要同步内存里的元数据信息,通过MVCC机制的保证,就能支持跨节点读取数据的一致性,非常巧妙地解决了主实例和副本之间的数据同步问题,大大节约了跨节点的网络开销,降低副本间的同步延迟。 提升事务性能 POLARDB内核层面优化揭秘 为了提高事务性能,POLARDB 在内核层面进行了大量优化。把一系列性能瓶颈用无锁(lockless)算法以及各种并行优化算法进行改造,减少甚至消除各种锁之间的相互冲突,大大增加了系统的scalability 能力。同时,我们依托处理双十一这种大规模高并发场景下的经验, 在 POLARDB 上实现了对库存等热点数据进行优化的功能。对于简单重复的查询,POLARDB支持直接从存储引擎获取结果,从而减少了优化器及执行器的开销。 此外,进一步优化已经高效的物理复制。比如,我们在重做日志加了一些元数据,以减少日志解析CPU开销. 这个简单优化减少了了60%日志解析时间。我们也重用 一些数据结构,以减少内存分配器的开销。 POLARDB运动了一系列算法来优化日志应用,比如只有在buffer pool中的数据页面 才需要日志应用。同时我们也优化了了page cleaner and double write buffer,大大减少这些工作的成本. 这一系列优化使得在性能上 POLARDB 远超 MySQL ,在sysbencholtp_insert等大量并发写入的基准评测中达到最高6倍于MySQL 的性能。 支持并行查询(Parallel Query) 为了提高子查询和join等复杂查询(例如TPC-H基准评测)的能力,POLARDB的查询处理器支持并行查询(parallel query),可以将一个查询同时在多个或所有可用CPU核上进行执行。并行查询能够将一个查询任务(当前只支持SELECT语句)划分为多个子任务,多个子任务可以并行进行处理,整体采用Leader-Worker的并发模型。Leader线程负责生成并行查询计划,协调并行执行过程的其他组件,并行执行计划会包括并行扫描、多表并行连接、并行排序、并行分组、并行聚集等子动作。 Message queue是leader线程和worker线程的通讯层,worker线程通过message queue向leader线程发送数据,而leader线程也会通过message queue向worker线程发送控制信息。Worker线程负责真正的执行任务。Leader线程解析查询语句生成并行计划,然后同时启动多个worker线程进行并行任务处理,为了高效的执行查询,Worker上的执行不需要进行再次优化,而是直接从Leader上来拷贝生成好的计划分片。这需要实现执行计划树上所有节点的拷贝。worker线程在进行扫描,聚集,排序等操作后将中间结果集返回给leader,leader负责收集来自worker的所有数据集,然后进行适当的二次处理(比如merge sort,二次group by 等操作),最后将最终结果返回给客户端。 Parallel Scan层会结合存储引擎的数据结构特征来实现工作负载的均衡。如何将扫描数据划分成多个分区,使得所有的工作线程尽可能的均匀的工作是数据分区划分的目标。在以B+树作为存储结构的存储引擎里,划分分区的时候,是先从根上来划分,如果根上不能划分出足够多的分区(>= 并行度),将会继续从下一层进行划分。而如果我们需要6个分区的话,根节点最多分出4个分区,所以就需要继续搜索下一层来进行分区。以此类推。在实际实现并行查询的过程中,为了能让多个工作线程更加均匀的分配扫描段,会在B+树里尽可能的多划分分区,这样如果某个工作线程由于过滤性比较高会优先完成当前分区,那么它会自动attach下一个分区继续执行,通过自动attach的方式来实现所有线程的负载均衡。 新一代基于代价的优化器 云上客户的业务是多样化的,如果执行计划选错会导致慢查询。为了系统性地解决这些问题,POLARDB推出了新一代的基于代价的优化器。POLARDB里实现新的直方图Compressed Histogram对高频率数据进行自动探测并构建精确描述,在选择率计算时考虑数据频率和取值空间,解决实际应用中普遍存在的数据倾斜场景。 POLARDB大量基于改良的直方图进行代价估算,比如估算表和表join的结果大小,是join代价和join order优化的决定性因素,MySQL只能根据经验公式粗略的估算,无论是有索引时的rows_per_key,还是无索引时的默认参数值,估算的误差都较大,这些误差会在多表连接的过程中不断放大,导致生成效率低下的执行计划。 在POLARDB中使用直方图对重合部分进行合并计算,并根据不同的直方图类型适配不同的estimation算法,大大提高了估算精度,帮助优化器做出更优的join order选择。在随机生成的正态分布数据测试中,多表联合查询优化后可提速2.4-12倍,TPC-H测试中多个查询的join order发生变化,性能提升77%-332%。POLARDB也使用直方图优化了record_in_range的逻辑,MySQL对于有索引的过滤条件采用index dive来估算区间的记录数,这个操作在OLTP短查询中CPU占比较高。在使用基于直方图估算替换index dive后,在淘宝电商核心业务中,绝大多数的查询查询响应时间减少一半。 自研分布式文件系统PolarFS:高可靠、高可用、与数据库协同设计 POLARDB的存储层采用的是阿里云自主研制的分布式文件系统PolarFS。PolarFS是国内首款面向DB应用设计的采用了全用户空间I/O栈的低延迟高性能分布式存储系统(参见VLDB 2018 上的文章 PolarFS: An Ultra-low Latency and Failure Resilient Distributed FileSystem for Shared Storage Cloud Database),其具备与本地SSD硬盘架构相当的低延迟高性能I/O能力,同时也以分布式集群的方式提供了优异的存储容量与存储性能的扩展能力。 而PolarFS作为一款与POLARDB深度协同的存储基础设施,其最核心的竞争力不仅体现在性能和扩展性方面,更深层次的则是在面临有许多挑战性的POLARDB客户业务需求和规模化的公有云研发运维过程中而长期积累形成的一系列高可靠、高可用、与数据库协同设计的存储技术。 为了支持POLARDB在多个计算节点之间分发查询且保持全局的Snapshot Isolation语义,PolarFS支持存储POLARDB存储引擎B+树动态生成的多版本(Multi-version page)。为了减少读写冲突,现代数据库一般都通过以MVCC并发控制为框架来提供RC、SI、SSI等不同的事务隔离级别,在MVCC机制下,B+树的每个页面会动态维护一系列的版本,并发执行中的多个事务允许各自访问一个页面的不同版本。 在POLARDB集群里,由于跨节点复制同步延迟的存在,每个计算节点B+树的页面可能是不同版本的,这时多版本存储可以为各节点提供其所对应版本。在POLARDB中,计算节点向PolarFS写入一个页面的同时要提供该数据页的版本信息(LSN),PolarFS不仅存储数据页的同时还要存储数据版本元信息;计算节点读取数据页时,也会提供版本信息从存储获取相应的数据页(历史)版本。POLARDB数据库层定期会将集群所有计算节点版本号的低水位线发送给PolarFS,PolarFS会基于此版本号清理不再使用的历史版本。 保证数据可靠性是POLARDB所有设计的底线。在实际的分布式系统中,硬盘、网络与内存等硬件、固件或软件的bug等问题可能会造成数据错误,从而给数据可靠性保障带来各种挑战。存储端的可靠性问题来自静默错误(lost write、misdirected write,block corruption等),网络和内存主要来自于比特反转和软件bug。为了保证在各种异常情况(包括:硬件故障,软件故障,人工操作故障)发生时的数据可靠性,POLARDB和PolarFS 提供了端到端全链路数据校验保障。 在数据写入时,POLARDB 从计算节点的存储引擎开始,一直到PolarFS存储节点的数据落盘,经过的中间链路,都会对数据的正确性做校验,防止异常数据写入。在数据读取时,PolarFS和POLARDB存储引擎都会对读取到的数据做checksum校验,准确地识别磁盘静默错误的发生,防止静默错误扩散。在业务流量低峰时,还会在后台持续性的做数据一致性扫描,用于检查单副本数据的checksum是否正确以及各个副本间的数据是否一致。数据迁移过程中的正确校验性也非常重要:POLARDB在执行任何形式的数据迁移动作时,除了副本自身数据的 checksum 校验,还会对多个副本数据的一致性做校验;当这两个校验都通过,才会将数据迁移到目标端;最大限度的防止由于迁移动作,导致单副本上的数据错误扩散,避免数据损坏问题。 PolarFS还支持对POLARDB做快速的物理快照备份与还原。快照是一种流行的基于存储系统的备份方案。其本质是采用Redirect-On-Write 的机制,通过记录块设备的元数据变化,对于发生写操作的存储卷进行写时复制,将写操作内容改动到新复制出的存储卷上,来实现恢复到快照时间点的数据的目的。快照是一个典型的基于时间以及写负载模型的后置处理机制。也就是说创建快照时,并没有备份数据,而是把备份数据的负载均分到创建 快照之后的实际数据写发生的时间窗口,以此实现备份、恢复的快速响应。POLARDB通过底层存储系统的快照机制以及Redo log增量备份,在按时间点恢复用户数据的功能上,比传统的全量数据结合逻辑日志增量数据的恢复方式更加高效。 高度兼容Oracle语法 成本是商业数据库的1/10 除了100%兼容MySQL和PostgreSQL这两个最流行的开源数据库生态, POLARDB还高度兼容Oracle语法,为传统企业上云提供成本是商业数据库1/10的方案。通过用DMS替换Oracle的GUI管理工具OEM,以及用POLARDBPlus替换命令行工具SQL Plus,沿袭了OracleDBA的使用习惯;客户端SDK可以从OCI和O-JDBC Driver替换成libpq和JDBC Driver,只需要做so和jar包的替换,程序主体代码不需要修改;对Oracle的SQL普通DML语法都能支持,对几乎所有高级语法如connect by、pivot、listagg等也都全面支持;对PL/SQL存储过程、以及存储过程用到的内置函数库也能做到全面覆盖支持;对一些高级功能(如安全管理、AWR等)提供完全相同的格式布局和操作语法,所以综合看来,POLARDB对Oracle的操作方法、使用习惯、生态工具、SQL语法、格式布局等都做到了全面的兼容和替换,结合迁移评估工具ADAM,应用可以做到少量改动甚至无改动。 提前看:更多新技术和企业级特性即将上线 除了上面介绍的技术,POLARDB还有大量新技术和企业级特性在2019下半年陆续发布,这些技术会全面提升POLARDB的可用性、性能,降低POLARDB的使用成本: 1)从弹性存储到弹性内存,热缓冲池(warm buffer pool)技术 POLARDB即将支持和计算节点进程解构的“热”缓冲池,这将大大减少用户业务在计算节点重启时受到的影响。在进行机型替换规格升降级的时候(serverless),对业务的影响更小。同时,一个独立的内存也使得其动态按需扩展或收缩成为可能。 2) 性能数倍增长,更好的DDL支持(FAST DDL) POLARDB即将支持并行DDL,这将大大缩短表级别的DDL延迟。这个功能把并行化做到极致,可以把建索引等DDL的时间减少近10倍。同时,POLARDB还进行了大量的DDL复制层面的优化,这使得DDL可以进行跨区域的大批量复制,速度更加迅速,资源的消耗更少。 3) 支持跨地域的全球数据库(Global Database) POLARDB 支持跨地域、长距离的物理复制,帮助用户建立其全球数据库的部署。通过物理复制,数据可以实时复制到全球各个机房,使得全球用户的查询在当地机房就得到响应,反应更迅速。 4)分区表的支持 POLARDB支持100T的存储容量。但是随着表的大小的增长,单表索引的层次也增加,导致数据的查找定位也变得更慢,一些单表上的物理锁也导致并行DML碰到天花板。所以进行合理的分区变得更加紧迫。之前不少用户依赖数据库外部中间件的分库分表的来减少单表的压力。但是,随着POLARDB在各方面比如并行查询的发展,我们可以把这些分库分表的功能通过分区表的形式在数据库内更有效的实现。有效的分区不但使我们能够支持更大的表,而且它减少了一些数据库索引的全局物理锁的冲突,从而提高整体DML的性能。同时,这种形态之后可以更好的支持冷热数据分离,把不同“温度“的数据存放在不同的存储介质中,在保证数据access的性能的同时,减少数据存放的成本。POLARDB在增强分区表的一系列功能,包括全局索引(Global Index),分区表的外键(Foreign Key Constraint),自增分区表(Interval Partition)等,使得POLARDB更好的应对特大表 5) 行级压缩 POLARDB即将推出行级压缩功能。业界通常的做法是在数据页级别通过通用压缩算法(比如LZ77、Snappy)进行压缩,但页级压缩会带来CPU开销过大的问题,因为改动一行数据,也要把整个数据页解压,改动,再压缩。此外有些场景下数据页压缩后反而变大(bloat),还会导致多重索引页分裂 (multiple splits)。POLARDB采用细粒度(fine-grain)行级压缩技术,对不同的数据类型采用特定的压缩方式。数据以压缩的方式同时存在于外存及内存中,只有在要查询的时候才进行行级数据的解压,而不用解压整个数据页。由于数据除查询外都是以压缩方式存储,所以日志也记录了压缩的数据,这个进一步减少了日志的大小,以及在网络传输的数据/日志的压力。同时其相对应的索引也只存储压缩的数据。整体数据量的减少足以抵消解压所引起的额外开销,使得这种压缩在大大减少数据存储的同时并不会引起性能衰退。 6)In-Memory的列存(HTAP) 在传统的数据库领域,分析数据库和在线事务处理是分隔开来的。因此通常需要在一天的经营结束后将在线事务处理的数据与往期分析处理的数据一起导入至数据仓库后运行分析,以生成相应的报表。在HTAP数据库中,则省去了大规模数据搬移的时间与运营成本,一站式解决大部分企业级应用的需求,并在交易结束当天同步出具T+0的分析报告。在这种需求下,POLARDB在实现in-memory的列存数据表。通过物理逻辑日志直接和POLARDB行存数据同步。这样通过特定适合分析的算子可以对这些列存数据进行实时的大数据分析。使得用户可以一站式的得到分析结果。 7)冷热分离存储引擎X-Engine 存储数据的规模越来越庞大,但不是所有的数据访问频率都相同,实际上数据访问总是呈现比较明显的冷热分布特征,基于这一特征,X-Engine设计了冷热分层的存储架构,根据数据访问频度(冷热)的不同将数据划分为多个层次,针对每个层次数据的访问特点,设计对应的存储结构,写入合适的存储设备。不同于传统的B+树技术,X-Engine使用了LSM-Tree作为分层存储的架构基础,使用多事务处理队列和流水线处理技术,减少线程上下文切换代价,并计算每个阶段任务量配比,使整个流水线充分流转,极大提升事务处理性能。数据复用技术减少数据合并代价,并且因为数据复用减少缓存淘汰带来的性能抖动。进一步利用FPGA硬件加速compaction过程,使得系统上限进一步提升。相对于其他类似架构的存储引擎比如RocksDB,X-Engine的事务处理性能有10倍以上提升。X-Engine的详细技术参考SIGMOD 2019的论文X-Engine: An Optimized StorageEngine for Large-scale E-Commerce Transaction Processing。 目前,POLARDB不仅支撑阿里巴巴集团淘宝、天猫、菜鸟等业务场景,还广泛应用于政务、零售、金融、电信、制造等领域,目前已经有40万个数据库迁上阿里云。基于POLARDB分布式数据库,北京的公交系统快捷、流畅地安排着全市2万多辆公交车,方便每天800万人次出行;众安保险则使用该数据库处理保单数据,效率提升25%。 原文发布时间为:2019-10-28作者: 鸣嵩本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
十年前(2009年)的9月,我奉命组建当时的淘宝技术保障部;随即启动了2010年的技术预算工作,记得第一次给时任集团首席架构师的王坚博士汇报预算的时候,我得意地说到:“(淘宝)2010年不再购买小型机”,被王博士狠狠批评了一顿:“既然2010年可以不再购买小型机,为何还要给自己留下活口,2011年以后还可以买呢?加一个字:2010年起不再购买小型机!” 由此,启动了后面几年备受争议的“去IOE”工程。 2010年5月,邀请正祥加盟淘宝,由此拉开了 OceanBase的自主研发之路。 中间历尽磨难,各种反对、讽刺挖苦……回想起来当时都是折磨、痛苦和眼泪,今天说起来可以算是传奇和笑话了。 2019年10月,OceanBase TPC-C 世界第一!热烈祝贺正祥和OB团队。 今天我们可以自豪地说,阿里巴巴数字经济体的小二们,十年磨一剑,终于解决了困扰国家已久的信息技术三大软肋(“CPU芯片、操作系统、数据库”)之一的数据库难题,OceanBase 100% 完全自主研发、安全可控! 从2009年启动“去IOE”到2019年OceanBase拿下TPC-C世界第一,这十年漫长的时光,有无数次可能让OceanBase夭折,坚持到今天真是一个特别了不起的奇迹。 性能大幅超越 Oracle 是第一步,期待后面10年时间的努力,OceanBase 也从市场占有率上超越 Oracle,那才是彻底完胜的姿势! “去IOE”工程的战略价值何在?我们总结有三点: 架构灵活支撑业务飞速发展 基础工程技术和人才的积累 大幅降低成本 当然,今天我们可以自豪地讲“去IOE”工程结出一个硕果:OceanBase ===“去IOE”工程的关键时点=== 2009.11 王坚博士决策启动阿里“去IOE”工程2010.01 大淘宝核心系统“去IOE”工作启动2010.05 正祥加盟淘宝,OceanBase立项2010.07 完成商品库“去I”2011.07 完成商品库“去OE”2011.09 完成交易库”去IOE”2011.11 OceanBase第一次支持双十一,服务淘宝收藏夹2012.06 B2B/阿里金融启动“去IOE”2012.11 OceanBase转战支付宝2012.12 完成大淘宝“去IOE”2013.04 CBU/ICBU 完成去I2013.05 支付宝完成“去IE”2013.06 阿里妈妈完成“去O”2013.06 支付宝官微宣布:【再见!亲爱的小机】2014.11 OceanBase承担支付宝交易10%流量2015.11 OceanBase承担支付宝交易100%、支付50%流量2016.11 OceanBase承担支付宝交易100%、支付100%、花呗账务30%流量2017.11 OceanBase承担支付宝交易100%、支付100%、账务100%流量,“去O”大功告成!并第一次走出阿里,应用到南京银行互联网核心系统2018.09 云栖大会发布了OceanBase 2.0,正式宣布兼容Oracle2019.10 OceanBase发布Oracle兼容版本2.2,并公布TPC-C结果 阿里经济体的程序员同学们,节日快乐!振飞 @2019-10-24 北京 【资料一览】(一)“去IOE”《再见!亲爱的小机》王坚:阿里巴巴为什么会“去IOE”(一)王坚:阿里巴巴为什么“去IOE”(二)王坚:阿里巴巴为什么“去IOE”(三)王坚:阿里巴巴为什么“去IOE”(四)以阿里云去IOE【深度】解密阿里巴巴的技术发展路径 (二)OceanBaseOceanBase TPC-C 世界第一中国自研数据库拿下世界第一!性能超老牌数据库Oracle 100%蚂蚁金服OceanBase战胜9年前的Oracle是关公战秦琼吗如何看待蚂蚁金服OceanBase拿下世界第一,性能超老牌数据库Oracle 100%?特稿 | 蚂蚁“备战”TPC-C这1年OceanBase数据库创始人阳振坤分享征战6088万tpmC的艰辛之路 (三)其他Oracle创始人Larry Ellison扬言要遏制中国,去IOE迫在眉睫甲骨文创始人去年10月接受FOX采访Amazon消费者业务宣布永久关闭Oracle数据库 原文发布时间为:2019-10-25作者: 高德总裁 刘振飞本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹请来AI大神贾扬清,作为一位开发者,聊一聊他自己的开发者经历,希望对你有所启发。 贾扬清阿里巴巴集团副总裁、高级研究员阿里巴巴计算平台事业部总裁 深度学习框架Caffe 作者,TensorFlow 作者之一,曾任 Facebook AI 架构部门总监,负责前沿 AI 平台的开发,Facebook 各产品部门 AI 平台的支持以及前沿机器学习系统研究。加州大学伯克利分校计算机科学博士学位、清华大学硕士学位和学士学位。 01 人生经历 当我觉得正在做的事情有意思,我就觉得投时间在里头是对的。 阿里妹:大家对你的个人经历特别感兴趣,这么年轻来到阿里,想了解你是什么时候开始编程的?大学、研究生、博士分别学了什么? 贾扬清:其实我学编程还挺晚的。初三的时候,有一台计算机,我觉得挺有意思的,这时候开始接触电脑。大学的时候我想报相关的专业,清华计算机的分数线非常高,我就去了自动化线。我在本科学的是正常的课程,到了研究生阶段才开始学习机器学习相关的内容。当时有一句话是说:机器学习在80%的时间里解决80%的问题,但我们不知道哪80%的时间解决了哪些80%的问题。当时大家都很沮丧,想着毕业可能就失业了,还不如学系统或是数据库,至少有工作的机会。后来是因为兴趣,就一直坚持下来了,再后来去伯克利读PHD。博士期间已经在做计算机视觉的研究。要说正式开始非常认真的编程的话,Caffe 应该算是我第一个C++的项目。 阿里妹:你认为你是一个智力超群的人还是学习刻苦的人? 贾扬清:我觉得我智商没太高,可能还是因为兴趣,我也是相对比较懒的人,但是干活还是干的。我基本上7点起来,半夜还在做。当我觉得我的事儿特别有意思,那我就觉得投时间在里头是对的。 02 深度学习技术 这是一个值得投入的方向。 阿里妹:Caffe 是在什么场景下开发的这个框架,并且选择开源的,开发这么一个复杂的计算框架中遇到最大的坑是什么? 贾扬清:当时我们是想解决一个现实的问题,AelxBnet 出来之后,一直没有一个通用的让我们做科研的像 cuda-covnet 这样的一个平台,大家如果去看 cuda-covnet的话,是很难做扩展的。所以,当时我和伯克利的其他研究生是想解决自己的痛点:怎样来训练和设计深度学习的网络? 开源的原因是我们觉得我们有这样的一个问题,那么斯坦福肯定也有,清华也有,其他人肯定也是有这样的一个痛点。后来,我们基于共享的考虑因素就把它放出来了,可能也是因为这个缘故,正好踩到了点。 如果要说坑的话,搞开发的时候,最痛苦的事情是怎样来Debug。最开始的时候没有测试,后来我们跟以前在谷歌和其他的科技公司的朋友谈了一些测试框架。我们把测试的东西弄起来之后,写完Code跑一遍测试就可以了,这是很大的收获。 阿里妹:AI 对于解决常识或者推理的问题上面,会不会有重大的突破?包括知识图谱跟深度学习学习如何结合?知识图谱+图计算是不是有希望的方向? 贾扬清:凭心而论,我不是图计算的专家,只在读Paper了解的程度。我觉得类似于知识图谱,或者说用一个很老的词叫专家系统,这方面的研究可能是人工智能接下去的方向。以前做专家系统的时候,大家发现一个问题:我知道这个逻辑是怎么样的,但是输入一个图,怎么样从图到逻辑。所以,这几年人工智能在感知方面,像图像识别、语音识别、自然语言理解,都是从各种各样的输入到所谓深度学习的阶段,这方面深度学习的问题没有解决。图计算让我想起我在做PHD的时候一个特别热的领域叫概率图模型,我觉得这是一个很值得投入的方向。 阿里妹:目前出现了无需大规模训练集的深度学习算法模型,只是在精准度方面有一定的损失,请问这个方向是否有生命力?如果有,可以应用在哪些场景中? 贾扬清:这还是跟场景有关系,在一些影像识别等场景中的确有这样的问题,当我要找一个特殊癌症的人,我不能满世界找十个得癌症的人。所以说怎样在小数据上做学习是一个问题。我也经常在反思,不一定需要拘泥于数据太少的问题,可以通过一些方法来看能不能找到更多的数据。目前,小数据的收集还处于一个偏科研的方向,很多场景在真正落地的时候,还是需要数据,但是这个方向还是非常值得关注的。 阿里妹:你会推荐其他技术上的同学投入深度学习吗? 贾扬清:我夫人是做 finance 和 accountant 的,有一天她说要学 SQL,在finance 中,很多人需要数据处理。后来,我们就用一个周末的时间在一块儿学习。我觉得这件事和这个问题类似,大家可以把深度学习当做一个工具。如果你在周末的时候把它当做练练手的项目来做,那还可以,否则就不要搞了。有很多专业的,像做编译器的同学都可以帮忙搞定。怎么投入学习呢?看一看数据科学家都在做什么事情,如果自己有一堆数据的话,是不是可以用深度学习的模型跑一跑,前提是作为一个用户来用,而不是作为底层的开发者来用。我觉得深度学习是一个好东西,看大家怎么来用,到底要不要造这个工具,有兴趣的话当然可以。 03 职业成长 人应该是要想着怎样做更好的工具,而不是跟工具抢活。 阿里妹:AutoML是一个兴起的领域,预期会有越来越多的日常工作被自动化,算法工程师应该如何选择发展提升方向? 贾扬清:我认为这是一个机会。当年C语言出来的时候,做汇编的同学会觉得自己没饭吃了。python、java出来后, C语言的人是不是没饭吃了?其实都不是,而是说效率提升了。所以,我觉得 AutoML 更多的是让我们能够放手来考虑更多更上层的东西。所以人应该是要想着怎样做更好的工具,而不是跟工具抢活。 阿里妹:对算法工程师来说有没有更具体的,可以发力的方向? 贾扬清:AutoML 应该是从2016年开始的, AutoML 最开始做搜索的时候,需要大量的资源,一开始大家开玩笑说AutoML 属于谷歌秀肌肉的文章,一上来就说800个GPU跑了几个月的时间,这事儿不现实的。我们用一个相对批评的态度来看 AutoML 文章,他基本上就是跑一堆程序,最后跑出一个最好的。但是我们随机搜了一堆,它的效果能差多少?这个问题在研究领域也在讨论。所以,AutoML 一个比较现实的方向是,怎么样在资源受限的情况下,最快地寻找到一个最好的模型,这是一个传统的像搜索等方面的问题。 阿里妹:在你的感受中,国内工程师跟硅谷工程师有什么差异点和共同点,对国内的一些工程师有什么样的建议? 贾扬清:我觉得咱们国内的工程师最好的一点是特别认真,真的是特别认真。国外的工程师,有的时候到周末就找不到人,甚至星期五下午四点钟就下班了。我来阿里之后,加入了骑自行车锻炼的群,虽然还没有机会去,我觉得还是需要平衡一下。硅谷工程师的想法更加天马行空一点,愿意投资到看似没什么用的地方去。这体现在公司的文化上面,硅谷的工程师把很多的工具做的挺好的,这无形当中就提升了我们的效率,比如说像代码的审查,怎样做测试等。也不是批评咱们自己,国外的公司可能在这方面的投资的范围更大,这个反过头来帮助了日常的工作效率,这可能是咱们应该向硅谷学习的一个地方。 04 未来 人不是一台机器,但也正因此,我们才比机器更加温暖。 阿里妹:AI 在落地改变人类发展的道路上,面临的主要挑战是什么? 贾扬清:我觉得面临的挑战是我们做 AI 的人不落地,这算是一个自我批评。大家做科研,很多时候像在象牙塔里,需要解决的问题很简单,就像 imagenet,最开始我们说人工智能爆发的这一点,imagenet 图像解压缩之后250G左右的样子,我拿个硬盘就解决了,即使硬盘不够快,我拿个 SSD 也差不多也解决了。然后,我们再看看应用到产品里,有的时候是30亿的照片,拿 SSD 就放不下。你再拿一个机器跑一跑试试,半年后说不定也没戏。所以这个时候,算法需要真正的在不同的产品环境里头摸爬滚打。产品的数据,在一定程度上来说挺脏的,但是这事得做,做完了才能把算法推下去,有的时候做事不能太干净,这个是最主要的一个挑战。AI 领域发展得很快,科研人员很快地冲到了产品里,对软件工程这方面的敬畏在一定程度上是不够的,包括我自己,这是最大的挑战。 阿里妹:知识爆炸的时代,未来应该如何选择可以长期投入的方向去研究? 贾扬清:这个有点难说,我们当时搞人工智能的时候,开玩笑说,毕业就是失业。我觉得还是要找一个自己感兴趣的方向,现在大家都在做的,并不一定是最有意思的一个事儿。 有一次,我们一群博士生在讨论日子要怎么过。我们的想法是找一个自己喜欢做的事情。如果是我不喜欢做的事情,即使挣钱了,也还是在干不喜欢干的事。我们当时的说法是:即使我做得不开心,即使我赚不了钱,但我觉得这事儿有意思就行。我有一位同学是做天文的,虽然不赚钱,但是他做得很开心,这事儿就是我想做的事情。 2002年我刚入学的时候,我们班主任跟我们说了这样一句话:“你们已经读大学了,都已经在这儿了,你们要考虑到将来你毕业之后,年薪10万是没有问题的,养小孩是没有问题的。你们要想想,当温饱解决了之后,你做事儿是不是开心?”我觉得这句话对我的触动挺大,我一直都记得这句话。 阿里妹:如果推荐一本书给现在的开发者,你会推荐哪一本书? 贾扬清:作为软件工程师我经常有这样一份沮丧:为什么别人总是不讲逻辑?这种沮丧在我走上技术管理岗位以后越来越多,直到今天。戴尔卡内基的《人性的弱点》,从畅销书的角度可能都已经被说烂了,不过我觉得它对像我这样的技术人员来说,是一本特别值得读的书:它用逻辑来讲述逻辑之外的事情,同时向我展示了一系列为人处世和共创成功的方法论。我2014年的时候第一次读到这本书,感觉它解决了我心底里对于和人打交道的恐惧。乍一看,书中讲到的事情似乎都是一些技巧:如何使你更受欢迎,如何交朋友,如何增加赚钱的能力,等等;但一条主线贯穿始终:人作为一种社会动物,有着喜怒哀乐,有着不同的个性,但是最终都希望获得相互的真诚,合作和理解。书中讲到的无数种技巧,归结起来,就是真心理解他人,尊重他人。人不是一台机器,但也正因此,我们才比机器更加温暖,共勉。 原文发布时间为:2019-10-24作者: 贾扬清本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:对事物的归类分组是我们人类的天性,我们的大脑会自动将发现的所有事物以某种持续组织起来。但如何组织才能帮助我们解决工作和生活中出现的各种复杂问题?今天,我们请阿里高级技术专家张建飞分享他的黄金三步法。 我们经常说软件开发中,没有银弹。的确,单看软件领域,很少有什么方法论是普适的。然而再拔高一些,跳出软件的范畴,有一些东西是普适的。 最近,我有一个新发现:我发现“归类分组”在我们解决问题中起着巨大的作用。而这里的“问题”不仅仅限于软件设计,从日常生活、产品设计到公司战略、生物分类。都有它的身影。 这是为什么呢?究其原因可能有两个: 1.归类分组是抽象的重要方法,解决复杂问题我们离不开抽象。2.归类分组是结构化的重要步骤,结构化的表达离不开归类分组。 当然,归类分组要先有素材可以“归类”才行。因此,完整的方法论是这样的,我给他起了一个好听的名字——解决问题黄金三步:定义问题——分解问题——归类分组。 这三步的详细操作如下: 第一步,定义问题:也就是要清楚我们要解决的问题是什么? 第二步,分解问题:对问题进行分析拆解,形成平铺的多个子问题,此步可以尽量发散。 第三步,归类分组:对子问题进行归纳、剪枝,将趋同的子问题,合并成一类问题。 如上图所示,通过黄金三步以后,我们就可以得到一个形同“金字塔”的结构,也就是我们经常说的金字塔结构。根据问题的复杂程度,这个金字塔结构可能是两层,也可能是三层和多层。 看似简单的三步操作会有那么大的作用吗?废话不多说,直接上案例。 在日常生活中的运用 你出门买报纸,你老婆说家里冰箱空了,顺便带点东西回来吧。她给你列了一个清单,里面有葡萄、橘子、咸鸭蛋、土豆、鸡蛋。你说就这么多了吗?她说苹果和胡萝卜也可以买一点。当你准备出门的时候,她说家里的牛奶和酸奶也没有了,最好也买一点回来。 你觉得你能把老婆交代的东西都买齐吗?我看很难,因为我们的大脑短期记忆无法一次容纳7个以上的记忆项目,超过5个时,我们就会开始将不同的项目归类到不同的逻辑范畴,以便于记忆。 如果我们将葡萄、橘子、牛奶、咸鸭蛋、土豆、鸡蛋、胡萝、苹果、酸奶。按照逻辑关系,进行下归类分组,比如把葡萄、橘子、苹果归为水果类,把土豆、胡萝卜归为蔬菜类,便可以大大帮助我们提高记忆效率。 注意这里分类的作用不只是将一组9个概念,分成每组各有4个、3个和2个概念的3组概念,因为这样还是9个概念,你所要做的是提高一个抽象层次,将大脑需要处理的9个项目变成3个项目。 如果你已经这么做了,恭喜你,你已经在实践黄金三步了: 定义问题:冰箱空了,需要购买补给。 分解问题:你要分析购买哪些东西呢? 归类分组:为了方便记忆,你将要买的9个东西按性质分成了三组,形成如下的金字塔结构。 这就是结构化思维,下次你再接到老婆这样的“需求”,记得把这个方法论用上,她一定会对你刮目相看。 在工作汇报中的运用 定义问题 “小张,客户对销售报告和库存报告不满意,你去看一下什么原因。” 面对这种典型的工作问题,你打算怎么处理呢? 分解问题 首先,你肯定要去调研客户不满意的原因,经过调查你发现,客户不满意主要有以下原因造成的: 提交报告的周期不恰当; 库存数据不可靠; 获得库存数据的时间太迟; 库存数据与销售数据不吻合; 客户希望能改进报告的格式; 客户希望除去无意义的数据; 客户希望突出说明特殊情况; 客户希望减少手工计算。 虽然你做了大量的工作,调查也很充分。但是,如果你要是把这8个原因直接给老板汇报,估计效果不会太好。你可以自己做个试验,仔细阅读上面的列表3分钟,你能从中获得什么?同样的,你老板看到这样的罗列也会是一脸懵逼,不知道重点在哪。 归类分组 所以我们有必要加上第三步,对问题进行进一步的归类分组,我们可以将8个问题概括为3组: 报告中含有不可靠的数据; 报告的格式混乱; 产生报告的时间太晚,无法采取有效措施。 进行分组之后,我们就可以得到如下的金字塔结构: 这种结构化的表达,很明显让问题的表述更加清晰。领导也可以很快抓住问题的要点,并作出相应的决策。当然,这里是为了着重介绍归类分组,真正的汇报你还要提供对应的解决方案,然后让老板做“选择题”。 在写代码时的运用 你遇到一个相当复杂的业务场景,在这个业务操作中,涉及到大量的校验和执行操作。这种代码,如果没有一定的策略,很容易写成大泥球。 定义问题 例如,在我们的业务中有一个商品上架的操作,是一个非常复杂的业务操作。 分解问题 对这种业务问题的分解,通常是产品经理的职责,但是作为工程师也不能完全依赖PRD。因为产品视角和工程视角还是有差别的。 针对“商品上架”,我们做了如下的功能分解: 归类分组 同样,在分解之后,我们需要有一个归类分组的过程。否则,这些步骤的平铺会让代码显得凌乱,不方便记忆和维护。通过分析,我们可以把分解后的步骤分成三个阶段: 初始化阶段 校验阶段 执行阶段 通过黄金三步,我们可以得到如下的金字塔结构: 最后,我们按照这个结构去组织我们的代码,整个代码结构会更加清晰,代码的可维护性也会好很多。 在应用架构中的运用 应用架构主要解决的是模块、组件定义和模块、组件关系的问题。 从宏观层面来说,架构设计也是遵循这三个步骤的。比如,我们的架构要如何分层,分模块、分组件就是在做问题分解。然后,模块和组件要归属在哪个层次,要如何命名,就是在做抽象,在做分类归组。 在综合考虑功能属性+质量属性,然后通过黄金三步,就能得到我们想要的架构设计。例如,我们的COLA 2.0架构也是在这个方法论的指导下完成的。 在产品架构中的运用 定义问题 “小张,为了做新零售,我们打算做一款智能互联网POS机,你先做一下产品设计。” 分解问题 通过调研你发现,作为POS机,其核心功能是收银和经营管理。所以至少需要包含收银的功能、服务核销的功能、商品管理的功能、库存管理的功能等等。 但如果仅仅是满足这些功能,和传统的POS并没有多大的区别,为了满足“智能”和“互联网”的要求,你去深入百度了一下智能POS应该具备的功能,大致包含以下功能: 刷脸支付; 支持品牌商营销; 支持自主营销; 智能定价; 外卖对接; 彩票对接; 虚拟充值等等。 在问题分解阶段,我们应该尽量多的收集信息,多发散,多头脑风暴。 归类分组 发散完,我们还是要收回来。在收敛之前,我们先看一下产品框架应该包含哪些东西,通常,一个产品架构至少要包含三个层次: 用户感知层(在何种场景下通过何种方式触达用户); 功能模块层(通过哪些功能模块实现产品的核心功能,和哪些外部平台功能有信息交互); 数据层(产品的数据从哪里来、产品的数据沉淀到何处去)。 在这三个层次的基础上,我们再对每个层次内的模块进行分组。例如在功能模块层,我们要对功能进行分类,让分散的功能点内聚成更大的产品模块(体现在用户界面上,往往是一级菜单和子菜单的关系)。 比如对于POS的收银产品模块,我们可以提供以下的产品功能: 支付宝收银 现金收银 微信收银 刷脸支付 记账等 通过层次划分,模块划分我们就可以得到一个相对清晰的产品架构,以智能POS为例,我们可以画出如下的产品架构: 分类是科学也是艺术 通过上面的案例,我想你已经领会到黄金三步:定义问题——分解问题——归类分组的要义了。其中前两步相对比较直观,而第三步往往是不容易做好,也容易被忽略的关键步骤。 实际上,对事物的归类分组是我们人类的天性。人类大脑会自动将发现的所有事物以某种持续组织起来。基本上,大脑会认为同时发生的任何事物之间都存在某种关联,并且会将这些事物按某种逻辑模式组织起来。 比如,下面这张图片: 无论是谁,乍一看到上面的六个黑点,都会认为共有两组墨点,每组三个。造成这种印象的原因主要是有些黑点之间的距离比另一些黑点之间的距离大。 空间是一个相对比较直观的逻辑关系,然而,并不是所有的逻辑关系都是如此的显性化。实际上,很多的概念会在多个维度进行交叉耦合,这就给我们的归类分组带来了很大的挑战。 生物分类学 生物分类学通常直接称分类学(Taxonomy),是一门研究生物类群间的异同以及异同程度,阐明生物间的亲缘关系、基因遗传、物种进化过程和发展规律的基础科学。 最流行的分类是五界系统。通常包括七个主要级别:界(Kingdom)、门(Phylum)、纲(Class)、目(Order)、科(Family)、属(Genus)、种(Species)。种(物种)是基本单元,近缘的种归合为属,近缘的属归合为科,科隶于目,目隶于纲,纲隶于门,门隶于界。 不过分类学到不是一门很严谨的“科学”。就像比尔.布莱森在《万物简史》里说的: 分类学有时候被描述成一门科学,有时候被描述成一种艺术,但实际上那是一个战场。即使到了今天,那个体系比许多人认为的还要混乱。以描述生物基本结构的门的划分为例。许多生物学家坚持认为总数30个门,但有的认为20来个门,而爱德华在《生命的多样性》一书里提出的数字高达令人吃惊的89门。 由此可见,分类并不像我们想的那么简单。我们观察事物的视角不同,对问题的认知程度不同,得出来的分类很可能也完全不同。 特别是当概念之间有交叉情况,分类就会变得更加棘手。比如,在你的笔记本中,有“读书笔记”和“哲学笔记”两个平级的分类,此时你阅读了一本哲学书籍,那么你会把这本书的读书笔记放在哪个分类里呢? 分类的原则 分类的基本原则是MECE法则。透过结构看世界,说的就是MECE法则。 MECE法则即mutually exclusive collectively exhaustive的缩写,是麦肯锡咨询顾问芭芭拉·明托在《金字塔原理》中提出的一个思考工具,意思是“相互独立,完全穷尽”,也常被称为“不重叠,不遗漏”。 MECE原则的思想精髓,就是全维度的去分析一件事情,不要有遗漏和重复的部分。我们可以借助已有的结构化思维模型(分类的框架)来分析问题,确保每一层要素之间“不重复、不遗漏”。 分类的思维模型 实际上,在上文中我们已经提到了一些分类的思维模型。比如,在应用架构中,我们通常有展现层、控制层、应用层、领域层和基础实施层;在产品架构中,有用户感知层、功能模块层、数据层。这些框架可以有效的指导我们在各自领域中开展工作。 类似于这样的分类思考模型还有很多,比如: 1.制定市场营销策略的“4P”模型,即产品策略(Product Strategy)、价格策略(Price Strategy)、渠道策略(Place Strategy)、促销策略(Promotion Strategy)。 2.分析问题的“5W2H"模型,即Why、What、Who、When、Where、How和How much。 3.思考组织战略的“7S”模型,即经营策略(Srategy)、组织结构(Structure)、运营系统(System)、经营风格(Style)、职员(Staff)、组织技能(Skill)和共享价值观(Shared value)。 4.分析竞争力的SWOT模型,SWOT分析代表分析企业优势(Strengths)、劣势(Weakness)、机会(Opportunity)和威胁(Threats)。 5.制定目标的SMART模型,即制定目标要满足确定性(Specific) 、可度量性(Measurable)、可实现性(Attainable)、相关性(Relevant)和时效性(Time-based)。 这些思维模型都是宝贵的经验总结,相当于已经帮我们做好了第三步“归类分组”的工作,我们只需要按照模型制定的框架往里面填充要素即可。 因此擅用模型,活用框架。可以极大的提升我们解决问题的效率,同时帮助我们做更加全面的、更加结构化的思考,做了“无遗漏,不重复”。 原文发布时间为:2019-10-23作者:从码农到工匠本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:1年前OceanBase团队开了个会,定了个小目标,接下来的一年中这群工程师闭关攻坚。从掉头发到睡几个小时就跳起来看邮件,更甚至有人天天焦虑地捧着手机等邮件。 1年后,蚂蚁金服自研数据库OceanBase登上TPC-C排行榜榜首,这一成绩开创了多项世界先河:中国数据库第一次登上TPC-C榜单;第1次基于公有云通用机型进行测试;分布式无共享关系数据库第1次通过审计并取得创纪录成绩。今天,我们走到幕后看成绩背后的光荣与梦想,艰辛与泪水。 2010年,在全球OLTP数据库权威测试TPC-C中,老牌数据库巨头Oracle取得了tpmC 3000多万的成绩,接近第二名IBM的3倍,堪称“独步武林”。此后的9年中,这一纪录无人能破。 事情回到2011年,阿里巴巴内部一个数据库团队在备战双11时遇到了点麻烦,他们所支持的淘宝收藏夹业务只要流量一大就反复宕机。大战在即,团队全员立刻汇合,紧急修改代码。 今天看来这不过是个小问题,但在当时却生死攸关。“要是解决不了这个问题,我们的项目就挂了。” 蚂蚁金服研究员杨传辉当时就站在负责修改代码的工程师背后,一行一行给他review。他的身旁站着阳振坤——他们的Leader,也是这个数据库项目的创始人。 他们的项目名叫OceanBase,第一次正式承接双11流量时,这个数据库还没满2岁,淘宝收藏夹是他们唯一支持的业务。 那一年谁也没有想到,九年之后,OceanBase一举超越Oracle,登上了TPC-C benchmark的榜首。 测试只是手段,做出更好的产品才是目的 TPC-C是全球目前最具公信力的联机交易处理(OLTP)数据库的功能与性能结合的测试标准。简而言之,一款商业数据库想要向业界证明自身实力,TPC-C测试是一项硬指标。TPC-C的排行榜长期被Oracle、IBM等外国厂商占据,从未出现过中国自研品牌的身影。 很高的测试门槛,把众多厂商挡在门外。首先,TPC-C测试需要模拟真实的交易环境去运行,整个测试的工具,包括所有链路在内,都需要自己搭建,而且要严格按照TPC委员会发布的标准来做——一份100多页的PDF,事无巨细,全是英文。其次,交易数据库性能的提升本身就是一件极为困难的事,Oracle已经站在巅峰,想超越它谈何容易。 “我们不是去年才想起要跑TPC-C测试的,我们从2010年立项第一天就研究过这个指标。”杨传辉说。但是他们也清楚地知道,拿着尚未成熟的产品去跑测试是白费功夫:“三四十名可能都拿不到。” 厚积薄发,这一“积”就是九年多。 2018年8月,蚂蚁金服技术专家邹银超和OceanBase团队一起开了个会。在这场为筹备TPC-C测试而开的项目会上,杨传辉定下了打榜“小目标”:超越Oracle的纪录。 邹银超感到震惊。Oracle 3000多万的纪录已经保持了九年,无人可以撼动,OceanBase起步至今也才九年,对于数据库来说还非常年轻,第一次冲击TPC-C纪录,就直接剑指数据库王者Oracle。 “这个项目大概要做两三年吧?”他当时如此揣测。 按照既有的经验,两三年时间都很紧张。蚂蚁金服资深技术专家蒋志勇作为OceanBase团队的核心成员,在筹备的初期阶段跟随阳振坤先后拜访了浪潮集团乔鑫总经理和清华大学陈文光教授,学习和了解TPC-C测试的相关流程。而为了做出符合TPC-C规范的测试工具,需要将大量的人力和资源投入到产品研发和改进方面。“不管怎么说,测试只是手段,做出更好的产品才是目的。”蒋志勇说。 专攻测试工具的团队很快组建起来,蒋志勇牵头,成员人数一双手就数得过来。 TPC-C项目北京团队闭关攻坚 技术攻坚组也组建到位,北京一个团队,杭州一个团队,各占一个项目室,开始闭关攻坚。 其他各个团队都伸出了援手:硬件团队、中间件团队、阿里云……DBA团队和业务团队也以极大的包容,在人手紧张资源不足的情况下,支持着OceanBase技术攻坚。 困难虽多,但开弓没有回头箭。“蚂蚁的技术人是使命必达的。”正如DBA团队的资深运维专家师文汇所言,定下了世界第一的目标之后,所有人都会团结一致,就会倾尽全力去战斗。“追求极致,没有上限。” “审计员都没见过的新玩意儿” TPC-C测试的流程中,非常重要的一环是审计员来现场审计,以确认测试的所有细节符合规范。审计员全世界只有三位,满世界飞来飞去,日程紧张。 邹银超来自数据技术部,在项目中负责硬件选型、系统性能优化和测试方案评估,几乎每天都要和审计员进行邮件沟通。因为十多个小时的时差,他最常做的事就是凌晨把邮件发出去,睡几个小时,然后跳起来看审计员有没有回复。 一开始的测试方案是基于物理机做的传统方案。但到了2019年新年前后,策略发生了变化:准备使用阿里云ECS虚拟机进行测试。 比起使用昂贵的专用设备,上云意味着大幅度降低实际测试成本,以及优秀的可扩展性。OceanBase的这个方案,硬件成本仅占18%,远低于Oracle的硬件成本占比,但是在公有云上做TPC-C测试,前所未有。 审计员对此表示出了强烈的兴趣。分布式数据库来测TPC-C,OceanBase已经是第一家了,以如此大规模的集群上云测试,更是闻所未闻。 但是面对这样一种全新的形式,审计员也很谨慎,每一个细节都“抠字眼”式地确认。“有时候我们所有测试都跑完了,审计员忽然指出‘这里不合规’,就又得重头来过。”回忆起艰辛的沟通过程,蒋志勇苦笑。 “来回沟通了三个月左右,因为拿不准会测什么和怎么测,每一种测试方案都要准备和沟通。”邹银超说,“天天焦虑地捧着手机等邮件。” 更让他们焦虑的事来了:到了2019年5月,整整一个星期,审计员没有任何反馈。 阳振坤保持淡定:“再等两天。” 两天过去了,仍然杳无音信。团队坐不住了,写邮件过去询问。这一次审计员回了一封极长的邮件,原来是因为OceanBase的测试方案从未有人做过,而且规模特别庞大,审计员自己也没把握,专程去了一趟TPC组织总部,把方案提交给TPC-C技术委员会开会讨论。 讨论的结果反馈到了阳振坤手中,他第一时间把消息发到了整个项目的每一个相关群里。“通过了!”测试方案最终获得了TPC-C委员会的认可。“心头的一块大石头总算落了地。”蒋志勇松了口气。各个群里一片欢腾,但这还只是路程中的第一块里程碑。 “大家像战友一样,把后背交给对方。” “聚到一个项目里,大家从肉体到心灵,都靠得更近了。” TPC-C项目杭州攻坚团队 蚂蚁金服技术专家曹晖是早期就加入TPC-C测试项目的成员之一,这一年多来,他最常感到的就是“时间不够用”。物理机换成虚拟机,单机50%的性能差距很快显现出来,如何在短时间内找到优化点提升性能,成为团队攻坚的重点任务。 杭州闭关室的墙上画着一张性能表,每周更新,这条性能曲线也成为团队成员的“心情曲线”:“看着它像阶梯一样一步一步地走高,大家就非常高兴,要是连续一段时间爬不上去,就要开始掉头发了。”曹晖笑着说。 掉头发的不仅仅是OceanBase的攻坚团队。师文汇和DBA团队也每天都在“发愁”稳定性和研发效能。蚂蚁最核心的业务全部都运行在OceanBase上,稳定性丝毫不容有失,但大量人力和资源投入到TPC-C项目后,支持和维护日常业务的重担都落到DBA团队和业务团队肩上,工作量激增。 “最紧张的是研发环境,如果出问题,很多业务开发都会受影响。”整整一个半月,DBA、交付、系统等团队都在携手应对这个问题,终于,风险排除了。 TPC-C测试的结果公布后,阳振坤在群里向所有兄弟团队表示感谢:“感谢大家对OceanBase的理解和宽容。” 想要取得任何重大成就,一个团队的力量始终有限,但百川汇海,聚沙成塔。“一场仗,一颗心。”杨传辉用这六个字总结这场举全员之力的联合作战,师文汇的说法更热血也更感性一些:“胜则举杯同庆,败则拼死相救。大家像战友一样,把后背交给对方。” “从来没见过这么平稳的曲线” 7月底,性能攻坚告一段落,真正用于测试的大规模集群由阿里云准备到位,交到了项目团队手中。曹晖被任命为“操作员”,掌控两百多台ECS云服务器。 意想不到的事又发生了。之前在小规模集群上解决掉的问题,一上到大规模集群,又一一暴露出来。“压力不均匀,抖动很厉害,还出现了不少完全没预料到的问题。”蒋志勇说,“而且这还只是一些基础问题,把它们解决掉之后,还得把曲线跑得很平稳才行。” 按照TPC-C的要求,在测试取值的2小时之内,系统抖动不能超过2%。 而此时距离审计员前来杭州现场审计,时间仅剩两周。 从拿到正式测试的大规模集群开始,大家就开始连轴转。曹晖天天在群里“钉”人,他盯着200多台云服务器的运行,只要发现问题,马上“钉”到对应的同学,被“钉”的同学无论在杭州还是北京,第一时间抄起电脑就冲到闭关室。 阿里云团队也倾力配合,IaaS层的配置和性能的优化,问题一个一个地在他们的支持下被攻克。 “想法很简单,就是往前冲。没有退路,很多岗位连backup都没有。” 没有两三年时间,只用了一年,OceanBase就迎来了TPC-C的最终大考。 8月6日,TPC-C审计员抵达杭州蚂蚁金服总部。 “一边很有信心,一边又很紧张。”杨传辉这样形容当时的心情。信心来自于九年以来的积淀,他们自信已经具备了冲击Oracle纪录的实力;紧张来自于时间紧迫,最后针对大规模集群的调试只有两周,会不会有不可预知的问题发生? 测试工具启动,OceanBase开始运行,预热的时间中,审计员出去喝了杯咖啡。等他再回来时,性能曲线已经展示在了屏幕上——极其平稳,没有丝毫抖动。 经验丰富的审计员大为惊叹,因为从来没有见过这么平稳的曲线:“Very impressive!” TPC-C的性能测试要求取值时段是2小时,OceanBase是8小时;TPC-C要求的抖动幅度是不超过2%,OceanBase是低于0.5%。 现场审计结果获得审计员认可,接下来又经过了一个多月的调优,9月底,OceanBase跑出了Finalrun结果。凌晨4点,曹晖和邹银超把最终报告发给审计员。 10月2日一早,审计通过的结果出现在了TPC-C官网上——tpmC 6088万,是Oracle纪录的两倍多,新的纪录诞生了。 “那一刻真没有感觉到特别兴奋,反而觉得,这就是理所当然的。”蒋志勇说。 “技术男不会特别情绪化。而且,我们在2014年就已经兴奋过了。”杨传辉说。这位从头看着OceanBase诞生和成长的工程师经历过更多风浪,最情绪化的反应留在了当年OceanBase第一次承接核心支付业务成功的时刻,“写代码这件事,只要坚持得足够长,肯定能做出来,但是就是需要坚持,这种坚持可能以十年二十年为单位。到了真正做出来的时刻,就会觉得是理所当然的了。” “不管是在做TPC-C还是日常工作中,我觉得蚂蚁的技术人是使命必达的,在做一件事情或者确定一个目标以后,倾尽全力去战斗。就像TPC-C项目启动的时候,阳老师说一定要做到世界第一,然后大家团结一致为了这个目标去努力。”师文汇说,“蚂蚁技术人一直在追求极致,不断的用创新去突破当前技术的限制,这个在双十一、TPC-C测试以及日常的工作中都有所体现,比如想尽办法去优化CPU的消耗、用各种方法降低RT时间。” “家人终于知道我是做什么的了” “比打破纪录更重要的,是OceanBase为这个行业打开了全新的可能性。”蒋志勇说,“假如按Oracle的路子走,Oracle就是顶峰了,但OceanBase用分布式的方式去做这件事,取得了一个新的纪录,让市场和客户有了新的选择。” 以这个世界第一为契机,平时不太接触技术领域的人,也开始了解中国自研的数据库。“我的家人终于知道我是做什么的了。”曹晖感慨说,“更让我高兴的是,他们已经开始给别人讲什么是数据库了。” 杨传辉把OceanBase登顶成功的报道转到了朋友圈,他很快接到了母亲的电话:“你朋友圈里发的那个‘世界第一’是什么?”七岁的女儿也兴致盎然地来问他,他对孩子解释说,这是一个跑得很快很快的东西。 但每一位工程师都清楚地知道,“跑得很快”只是一个优秀数据库的必要条件,而非充分条件。 数据库像个跑车。极限速度取决于发动机,但跑车不仅仅只是一个发动机。OceanBase在TPC-C测试的成绩,证明我们在“发动机”性能方面达到了全球领先水平。但是数据库的综合能力方面,我们还有很大的进步空间。 下一步,OceanBase还要开发更丰富的功能,提升复杂查询的能力,在同一套引擎里既支持OLTP又支持OLAP。此外还要基于上下游产业来营造一整套生态系统。这些就像是跑车的外观、车内设施、配套服务等等,只有全都做好,才能打造出一款顶级跑车。 此外,蚂蚁金服还决定将自己开发用于TPC-C测试的工具开源。这意味着TPC-C将变成一种“普惠”测试。“任何一家公司只要在阿里云上租用资源,用这个开源工具跑测试,然后请审计人员来审计就可以了。”杨传辉说,“关键只在于,你的产品够不够好。” 2018双十一中的OceanBase团队 OceanBase用了九年将自己的产品打磨至此,但创造一个新纪录并不是它最终的目的。更重要的是,它为中国自研的数据库铺平了一条道路。 原文发布时间为:2019-10-22作者: 数据库OB团队本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:#技术双11系列#全链路压测是阿里的首创,我们将从工作内容、操作过程、运行总结等多个方向来介绍下阿里内部典型电商活动(如双11准备),以给大家展示一个完整的压测流程,帮助更多的企业和用户更好的完成性能测试。 前言 关于性能测试的重要性及必要性已经是个老生常谈的问题了,现分别从技术角度和业务战略角度总结如下: 而性能测试的目的也就是为了解决大型营销活动中洪峰流量引起的系统表现不确定性,一个理想的营销活动周期应该是有如下闭环流程: PS:1和2之间再加一个步骤。环境改造和基础数据准备。强调必须在生产环境。 压测环境准备:需要复用真实的线上环境,压测结果和问题暴露才都是最真实情况。可通过压测流量全局识别、透传(数据进影子区域)。 基础数据准备:以电商场景为例,构造满足大促场景的核心基础相关数据(如买家、卖家、商品信息),以线上数据为数据源,进行采样、过滤和脱敏,并保持同等量级。 可以看出,性能测试通过真实、高效的压测方式进行容量评估/瓶颈定位&解决,最终来保障活动稳定进行;每一个环节的内容都非常重要,以阿里双11活动为例,我们除了技术上的准备、执行、保障之外,还会有一些流程及分工细节。以下将逐一介绍。 关于流程及管理 阿里巴巴全链路压测从2013年到现在也已经是第7个年头了,在这7年中间我们不断的积累、总结、优化进步,从开始的200多人参与、通宵压测的大规模全员项目活动到后来仅仅几个人白天压测、更智能化的压测方式,这样一种大规模的项目活动,离不开有效的流程把控及分工管理。 阿里巴巴在多年双十一大促保障——全链路压测项目中,有着严格的流程把控及分工管理模式与经验,总结如下: 说明:该图中时间点为模拟时间点,仅做先后顺序的参考。 好的流程规划与管理,可以大大提升团队协作效率。叠加上工具平台的智能化功能,可以将参与的200人力通宵压测缩减至10人以内白天压测,有效的方案 + 充足的准备 + 靠谱的平台技术产品 = 成功的压测。 下面将结合主题系列前几次的文章,介绍下在数据准备、架构改造、流量安全策略(环境及流量隔离)、压测实施、问题定位分析这几方面,阿里巴巴在双十一压测这个项目上具体是怎么做的。 压测环境改造 数据准备的同时,需要考虑压测环境(即压测对象的部署环境)是哪里,不同环境就需要做不同的准备。 整个阿里经济体的压测环境,包括双十一压测,全部选择的是线上环境,此时需要评估如果要进行全链路压测,是否直接可以使用现有环境、同一个API多次压测是否会被拦截、是否会有脏数据影响、如果有影响应该如何改造避免等。以上这些问题总结下来即为两类问题:业务问题和数据传递问题。问题比较明确,我们就根据这两类问题来做逐一的改造。 改造分为2方面:业务改造和中间件改造,这些在内部全链路压测1.0 时代就已经完成了,对于外部客户来说,可以作为一个技术改造上的参考点。同时我们已经有成熟的产品化方案提供一站式的能力,免去复杂的改造和维护成本。 业务改造 业务改造即为了解决压测过程中的业务异常问题,或者压测请求无法正常被执行的问题。举例如下: 流量区分与识别:压测流量和业务流量的区分,并可在全链路系统中识别出来; 流量单一性问题:比如下单,同一个人执行一次下单后再重复执行就会失败; 流量的限流拦截:如果日常有限制,需要改造为接入流量降级能实时生效调整配置; 剔除压测数据对报表的影响 动态校验 ...... 业务改造涉及的内容无法一一穷举,需要根据不同的业务模型、业务架构及配置,一一梳理。一般梳理改造之后,后续所有新应用都按照规范去开发,每年的压测前进行基础的查漏补缺即可。 中间件改造 中间件作为衔接业务应用之间的组件,在压测中有个至关重要的功能就是将流量标识传递下去,一直到最终的数据库层面。虽然我们在13年开始,从核心应用使用到的中间件已经升级改造完成,中间我们踩过不少坑,诸如改造全面性、改造带来的业务代码修改成本、版本兼容问题等。 改造完成之后,压测流量的模型图可以参考如下: 环境的改造,需要结合业务场景具体分析、设计。云上高可用解决方案,提供了全链路压测解决方案的服务。 数据准备 大促活动确定之后,会对业务模型进行一次评审,即确定该业务模式对应的技术架构应用有哪些,需要做压测的业务范围有哪些、以及数据量级、数据形式是什么样的。所以数据准备包括准备业务模型数据和压测流量数据两部分。数据的准备,主要分为两部分:业务模型的建立和基础数据的构造。 业务模型数据 业务模型数据,即压测的业务模型相关的数据,包括涉及到哪些API,这些API之间的压测量级是什么样的或者有什么样的比例关系等。业务模型的构造准确度,直接影响了压测结果的可参考性。 模型设计的目的主要是将业务进行采集并抽象成可执行的压测模型,并对各个子模型中的元素进行预测和设计,最终产生可以执行的压测模型。在双十一大促前,我们会确定好相关的业务,进行场景分类。 已有业务场景:采集以往数据并做处理,作为预测数据,形成一个模型雏形,结合新的业务玩法,形成已有业务的模型; 新业务场景:直接按照新的业务,模型配比,形成一个新业务模型。 最终会将两种业务场景类型进行组合,形成最终的终态业务模型。以下图作为示例: 在组装业务模型数据的时候,需要注意一些关键因素,比如修改具体的电商业务模型关键因素: 1对N :上游业务一个请求对应下游业务接口是否会存在调用多次的情况; 业务属性的比例:根据历史数据计算不同类型业务的比例关系; 业务模型组装之后,单一事务中的业务模型,应该是一个漏斗状的。而每层之间的漏斗比例,是根据不同的层级、不同的玩法、不同的规则会有不一样的比例关系。在一次大促活动中,这个比例关系理论上是不会变化的。漏斗模型参考如下: 业务模型在压测时对应的就是压测量级,淘宝大促用的全部都是RPS模式压测,即从服务端角度出发每个API之间是漏斗比例关系、能够很好地应用于容量规划上。商业化产品PTS(性能测试服务,Performance Testing Service)中也很好的支持了RPS模式。 压测基础数据 如果说业务模型对应的是确定要压测的接口/API的话,那压测流量数据,就是确定这些压测API到底压测的是什么内容,比如:登录哪些用户、查看哪些商品和店铺、购买哪些商品,甚至是付款价格是什么。 流量数据中,有一部分为上述业务模型对应具体RPS值,模型体现的是比例关系,而流量数据即有每次压测具体的RPS值。 流量数据中最重要的部分,即为真实的压测数据,我们可以称之为基础数据,比如交易的买家、卖家、商品数据等。全链路压测的目的是为了模拟双11,所以模拟的真实性非常重要,基础数据的真实性就是至关重要的一环。全链路压测会以线上数据作为数据源,经过采样、过滤、脱敏等操作,形成可作为压测使用的数据。 线上数据拿出来使用的时候,特别涉及到写数据的时候,避免造成脏数据,我们落地或者读取的时候,采用影子表的形式。当识别到压测流量,则读写影子表,否则就读写线上正式表。影子表的产生为的是压测流量安全。 淘宝内部系统使用的压测体系,数据平台和压测平台是两套平台。数据平台管理/提供压测数据(包括模型数据和流量数据),压测平台提供施压能力,即保证压测请求能够以指定的“协议”、指定的量级速率、从全国各地发送出来。商业化产品PTS(性能测试服务,Performance Testing Service)中提供的数据工厂能力,很好的将内部的数据平台和压测平台结合起来,产出为统一的一个压测系统,只需用户构造好压测数据以文件/自定义的形式定义好参数,在使用处配置即可。 流量安全策略 流量安全策略主要是为了保证能够正常的施压流量且数据不错乱,安全地、符合预期地进行。这里面就包括了两层考虑: 测试数据和正常数据的严格隔离,即非法流量的监控和保护机制; 手段:影子表数据。影子表为和线上结构一致,但是处于隔离位置的可写压测数据表。 效果:数据隔离,避免了数据错乱。 压测流量的安全过滤,即不被识别为攻击流量; 手段:将安全相关策略接入流控降级功能;针对压测适当放松安全策略,或根据特殊标记识别; 效果:压测流量不被判定为攻击流量,成功压测的同时保障线上业务的安全性。 此处,涉及到第三方的系统,诸如支付宝、短信等服务,因业务特殊性需要做压测系统的打通。13年淘宝实现了第一次全链路压测,但是未能打通下游业务链路。在14年双十一压测前,和支付宝、物流环节等打通了全面的压测系统。对于外部客户来说,支付宝、短信等都有对应的挡板服务可提供,用来供用户做全链路压测时使用。 压测实施 根据最开始介绍到的流程管控,一切准备就绪之后,即可开始进行全链路压测。除常规理解的正式压测之外,我们还有额外的两个预操作:系统预热、登录准备。 说明:此处未介绍首次改造之后的单链路压测调试,这部分基本由开发同学自行操作验证,故不在此特殊阐述。 关于系统预热 这里说的预热,未包含我们内部提到的预跑。预热是为了该缓存的数据提前缓存好,达到大促缓存态的状态,也更好地实现我们缓存的目的。大促缓存的使用应该利用到极致,故需要通过预热来进行。 对外部客户来说,可以通过先一轮、低量级的全链路压测,来提前预热系统,包括在真正大促活动之前也可这样操作,即提前缓存住需要缓存的数据。 登录准备:登录准备主要是用于需要长连接保持、秒杀等场景,即用户都是逐步登录上来,然后再进行业务操作的场景。故如果量级特别大的时候,可以提前做登录的准备,一则来模拟真实用户登录场景,二则是对登录系统的保护。 正式压测:一般正式压测会按照压测计划,执行多种压测策略。淘宝的双11大促压测,一般包含这样几步: 1)峰值脉冲:即完全模拟0点大促目标峰值流量,进行大促态压测,观察系统表现。 2)系统摸高:取消限流降级保护功能,抬高当前压测值(前提是当前的目标压测值已经达到,则可以进行摸高测试),观察系统的极限值是多少。可进行多轮提升压力值压测,直到系统出现异常为止。 3)限流降级验证:即验证限流降级保护功能是否正常。 (AHAS引入)商业化产品AHAS(应用高可用服务,Application High Availability Service)提供了全面的限流降级能力,可进行全链路的降级保护。 4)破坏性测试:这个主要是为了验证预案的有效性,类似于容灾演练时的预案执行演练。即为持续保持大促态压测,并验证预案的有效性,观察执行预案之后对系统的影响。 对外部客户来说,可以配置不同的压测量级数据,来进行多轮压测,并观察其系统表现。压测不应该是一次性的操作,而应该是反复的、多轮验证的操作。 问题定位分析 压测结束之后,会将压测过程中的系统表现、监控数据等整理,进行压测复盘,分析当前系统瓶颈、后续改进修复计划及下一轮压测时间等。在分析定位问题时,因涉及的系统较多、子业务系统的形态不一,需要具体问题具体分析,其中不免需要一线研发的介入。 商业化产品PTS(性能测试服务,Performance Testing Service)的压测报告,有详细统计数据及趋势图数据,采样日志以及添加了的监控数据。后续PTS还会提供架构监控,帮助性能测试执行同学,更好地从系统架构角度判定压测过程中系统是否正常,大致瓶颈点。 智能化压测 阿里巴巴全链路压测已经进入第7个年头,从开始的摸着石头过河,发展到现在更智能化形态。其中部分功能也会体现在商业化产品中,大家敬请期待。 更多协议的支持 容量评估 问题自动发现 全链路功能测试&压测预演 压测常态化 弹性大促,边压边弹 ...... 未来 阿里巴巴将全链路压测进行到第7个年头,中间经历了太多的磨练与积累,随着新技术的出现,我们也将不断的完善自己,做到更好。同时,更希望能将这么多年的经验,能赋能到外部客户,比我们少踩坑、完美的度过每一轮大促活动,并将全链路压测应用到更多的日常场景中。 原文发布时间为:2019-10-22作者: 牛兔本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:阿里20周年年会上,全球6万阿里人 "回家",用一种特别的方式为阿里庆生。年会现场,每位阿里员工人手一只白色IoT手环,这个由102颗LED小灯组成的手环,随着现场音乐、节目节奏变幻不同色彩。别小看这只手环,它会找到你的专属座位,为马老师送礼物(组成生日快乐字样),会告诉你绿色请等待,红色请离场。总之,这项阿里巴巴#办公黑科技#今天被阿里妹首次揭秘了,这个“小玩意”有大作用哦。 作者 | 韩康 背景 大型晚会舞台灯光效果虽然够炫够亮,但身处其中的观众,往往被动参与,无法通过自己的动作、语言和状态对晚会进程产生影响,更无法和身边的人、场馆建立联系,更不要提观众和舞台、节目之间的互动了。 基于此考虑,早在2017年阿里巴巴18周年年会上,阿里巴巴企业智能事业部的工程师就研发出IoT发光手环。让每个手环通过无线电单独实时控制,随着晚会节目进程,发出不同的颜色和强度的光,并按照一定的帧率实时刷新,从而让佩戴手环的人以一种全新的方式参与到晚会之中。 时隔两年后,工程师们在原IoT发光手环的基础上,全线升级成为云手环产品。每一个手环中不仅由102颗全彩LED灯组成,并且每一颗LED灯都由中控系统控制,通过无线通讯进行调光,达到业界领先的、精确到点的实时控制技术。 除了光效变化外,增加了个性化的设置,让其可以实现诸如入离场引导、公益“摇臂种树”等实时互动体验。下面将重点介绍升级后的云手环产品的系统设计点。 20周年年会手环 设计难点 1、人、节目、场的数字化重构 阿里巴巴18周年IoT发光手环虽然完成了每个观众作为单个“像素”共同一起拼大图,播视频的效果,但也仅限于舞台灯光效果的互动,人、场、节目的数字化实时互动仍需探索。 2、大规模高密终端无线上下行数据通信 相比起18周年年会,今年年会在多个方面都提出了更高要求:首先,受控终端更多。人数再创新高,达到6万+,增加50%以上。 其次,部署场地更大。作为2022年亚运会主场馆,杭州滨江奥体的物理尺寸较黄龙体育馆大不少,整个上下行无线覆盖系统需要应对此种变化。 最后,终端实时通信。需要在高密场景,直径300米的大场地大小,完成6万+终端的实时上下行通信,进而完成实时互动。 3、手环像素级控制 在受控终端数暴增的情况下,进一步强化发光效果。在整场体育馆这个“大画板”的基础上,如何完成实时内容生成-压缩-下发-执行展现的全过程。如何在由云手环组成的“超级无线大屏”上演绎更酷炫的视频内容。 系统关键点设计 1、立体发光手环 为了达到完美的发光效果,手环采用全环发光的设计,102颗高亮LED遍布表盘、表带,消除发光死角,即除了覆盖整个表盘的LED,在两侧的表带上也嵌入了LED灯珠。 云手环外观效果和LED布局示意图 每个LED灯珠可以单独控制,独立显示颜色和亮度,继而可以在表盘及表带的平面上做显示各式形状及动画。 2、感知&互动手环 为了完成实时交互,手环硬件上配备了如下感知器件: 3轴陀螺仪+3轴加速度:支持通过算法实时感知佩戴者姿态、摇晃次数、摇晃节奏等动作变化模式。 心率传感器:可精准识别手环是否处于佩戴状态。 配备麦克风声音检测:支持手环周边声音强度、频谱、音乐节奏的监测。 根据传感器实时计算的需要用户体验到的反馈,可以通过本地LED阵列的动画效果和马达振动实时反馈给用户。 3、大规模超密无线传感网系统 在大型体育场馆的场景,完成无线接入覆盖历来是一个世界级的技术难题,大型赛事活动鲜有WiFi或者运营商无线网络覆盖特别成功的案例。而云手环实现全场6万+终端的实时互动,第一个要跨越的技术难点就是单个终端的实时上下行通信。 云手环通过上下行分离、冗余链路、小包重传等技术手段,实现了6万终端的实时在线,实现了高密场景下的超大规模的无线传感网节点的无缝覆盖。 通过全面实时感知并上传手环/上下行基站等设备的休眠、在离线、电量、异常信息、温度、负载、流量、业务量等关键状态,实现关键设备关键指标监控的全程全场全覆盖。 | 上下行分离 在无线传输侧,采用上下行分离的方法实现实时上下行通信。系统架构如下图所示: 手环技术架构 无线上行信道,实时信息通过非链接式的无线小包多频点快传,降低链接保持开销和高密端侧相互干扰的同时,降低部分实时性要求搞的时延。 无线下行信道,使用独立信道,长包传输媒体控制信息,提高协议效率,增加下行吞吐;通过多基站热备形式提高系统稳定性;为了降低基站要求,采用非链接、多次重传的形式完成下行QoS保障。 | 冗余链路 面对实时性最高的无线上行链路,由于终端数量庞大,为了应对终端间的同频干扰,上行实时消息采用类UDP的非链接状态协议,通过冗余部署上行接入AP,单个手环发送,多个AP同时冗余接收转发,最终聚合于云端的形式,保障上行数据成功率。 4、服务端大流量应对:6万人实时大互动支持 实时互动环节,所有人同时开始摇手,每个人每3秒上报最新消息,考虑10倍冗余链路的情形下,需要服务端TPS>20万。服务端和基站侧分别通过中间件调优和多任务聚合的方式从容应对,最终实现全场6万人实时互动。 5、大场景科技感 在IoT发光手环的基础上,通过基于GPU的多重加速压缩技术,对媒体素材进行实时压缩。使得视频流可以在带宽容量有限的情况下,依然可以顺畅传输。每个参会者作为一个发光像素,共同构建全球最大的无线大屏,演绎各种视频和复杂动效。 功能设计 基于上述系统设计点,在20周年年会上,云手环也带来了更丰富的互动与多样功能:在产品设计上,产品经理朱江认为:云手环不是一个单纯的手环硬件设备,它同时承载了人与人连接,人与场的互动,以及作为大型活动硬件的一个组成部分。结合AI大数据及云端能力,带给用户实时的沉浸式互动体验的IOT智能硬件。以下是产品功能的详细介绍。 互动公益挑战 本场晚会专门设计了公益挑战环节,通过现场观众在主持人的带动下挥舞手臂,将摆臂次数转化为能量填充公益挑战能量条,完成最后20%的公益挑战。 手环通过内置的6轴运动传感器识别用户的手势动作,可以对用户的各种摆臂动作进行精准识别,手环会自动跟随用户动作节奏,通过变换颜色给用户即时的反馈。摇臂次数被实时上报到云端。通过阿里云dataV数据大屏在现场完美呈现。 公益摇臂环节示意图 设备个性化,活动期间专属设备 每个手环背后都印有一个和设备唯一ID号对应的二维码,用户通过使用钉钉,淘宝、支付宝扫描手环背后的二维码,将个人身份与硬件设备进行授权绑定。扫码系统专门针对晚会观众身份的复杂性进行设计开发,观众在30秒时间内即可完成绑定操作。 授权绑定后,赋予了硬件设备佩戴者的信息,不再是一个冰冷冷的设备,而是成为了一个温度的智能硬件,正如手环的slogan“一直坚持,一直发光”。每位小二在晚会过程中的活动情况,情绪指数被精准感知,在BG(事业群)秀,阿里年陈(阿里年陈文化,有1、3、5、10年陈)环节被精准点亮,沸腾全场。 马老师的生日礼物 在给马老师特别礼物的环节,当主持人一句对马老师说:“全体阿里技术人给您的一个特别的礼物”,看台上所有手环立即呈现出“生日快乐”四个巨型大字,可以说是阿里全体技术人送给马老师的礼物。 入离场引导 大型活动人数众多,撤场环节安全和用户体验至关重要,云手环作为现场唯一一个能实时感知用户信息,并能够实时触达到每个用户智能硬件设备,完美的完成了现场超过6万的观众的撤场引导工作。 “红灯停,绿灯行”,依据现场指挥的指令,精准控制不同区块手环的颜色,亮起红色的代表需要暂时等待,不能离场。绿色的表示可以开始离场,并通过佩戴检测功能,对摘下放回座位的手环进行灭灯,节能环保。 总结 本次云手环通过102颗高亮LED遍布表盘、表带消除了发光死角,实现全环立体发光;通过自建上行分离式的无线覆盖网,完成了6万多手环终端高密场景下超大规模的物联网终端的实时上下行通信,构建了滨江奥体主场馆的大场景科技感,覆盖受控手环数比18周年4万终端增加50%以上;结合动作、声音、心率等多维度的实时感知和振动、LED造型闪烁能力,构建了全场沉浸式的互动体验,完成人、节目、场的数字化的一步重要探索。 作为盛大的20周年年会的一部分,在奥体现场浩如烟海的各式子系统当中,在“IoT发光手环”正式蜕变升级成为云手环,全面提升了感知能力、模式识别能力和互动能力,完成了晚会氛围营造、大场景科技感打造、现场实时互动、辅助撤场等功能,是现场大规模实时互动不可缺少重要组成部分。面对五彩缤纷的各式大型公司年会、演唱会、体育盛会,有深远的商业前景。 看到这里如果你也跃跃欲试,想跟我们一起探索云手环的奥秘,欢迎将简历发送给我们。联系邮箱:poli.yzj@alibaba-inc.com,期待你的加入。 原文发布时间为:2019-10-21作者:企业智能事业部本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:全球首个云原生应用标准定义与架构模型 Open Application Model (OAM)开源项目,由阿里云与微软联合推出。今天,阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟(花名:小邪)在上海重磅宣布。OAM 的愿景是以标准化的方式沟通和连接应用开发者、运维人员、应用基础设施,让云原生应用管理与交付变得更加简洁,高效。 2019 年 10 月 17 日上午 9 点 15 分,阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟在上海《基于云架构的研发模式演进》主题演讲中,正式宣布: “今天,我们同微软联合发布了一个全新的项目,叫做开放应用模型 Open Application Model(OAM)。” 项目主页:https://openappmodel.io 蒋江伟在发布中讲道:“OAM 这个项目是业界第一个云原生应用标准定义与架构模型。我们希望通过这样的架构模型,以高效、标准的方式连接应用开发者、运维人员和应用的最终用户,让这些云端应用的参与者能够享受到像智能手机上一样简单、轻松、畅快的应用管理体验。” 与此同一时间,微软杰出工程师(Distinguished Engineer) 、Kubernetes 项目创始人 Brendan Burns 也在微软的官方网站上,宣布了同阿里云的这次重量级联合发布。 图片来源:https://cloudblogs.microsoft.com/opensource/2019/10/16/announcing-open-application-model/ 你可能会好奇,到底什么是 OAM 呢?又是什么原因,让两家全球顶级技术公司走在一起,希望联合整个云原生技术社区共同推进这样一个应用定义与架构模型项目呢? 这个事情本身,可能要从世界上最早的一次云端应用交付的故事讲起。 一个云端应用交付的故事 2006 年 3 月 14 日,美国某在线电商公司发布了一个叫做 Simple Storage Service(简称 S3)的存储服务。在这个服务发布不久,一名来自微软的技术人员带着尝鲜的想法,编写了一个使用该服务作为底层存储的应用。据这位开发者讲述,从决定编写应用到将该应用交付运行“总共花掉了几天的时间”。对此,他感到非常兴奋,后来他在自己的博客里这么写到: “这个服务的发布,彻底改变了技术行业的游戏规则”。 这位开发者的名字,叫做 James Hamilton 。他是 IBM DB2 早期的架构师,也是当时 Microsoft SQLServer 的负责人之一。就在编写完这个神奇的应用两年后,他选择加入了这家电商公司。如今,James Hamilton 已经成为了 AWS 的副总裁(VP)和标志性人物。 从 2006 年到现在,云计算经历了一波又一波变革和浪潮。如今的云,早已不再是 13 年前那个大众眼里需要“尝鲜”的陌生事物。如今的云服务,也早已在一次次的变革之中脱胎换骨,在成本和资源效率的极致优化中,将“摩尔定律”的失效推上了顶峰。2019 年 8 月,阿里云骄傲的向全世界宣布,云计算“拐点已至”。上云,正迅速成为企业进行应用架构和基础设施规划的默认选项。 在 2019 年,如果一位开发者要像 James 当年一样,把一个 Java Web 网站在云平台上线,到底体验如何呢? 云端应用管理的两大难题 在回答上面的问题之前,我们不妨先思考一下:现如今在智能手机上安装一个应用,是什么样的体验?升级应用呢? 在以 iPhone 为代表的智能手机上,应用管理的体验实际上是一个“如丝般顺滑”的感受。 现在,我们再回过头来看云。 一个云上的应用,肯定不只是简单的可执行文件。就拿 Java Web 网站为例,这个应用要想在云平台上被最终用户使用到,就需要有大量的外部依赖需要处理。这就是云端应用开发者实际上关心的事情,其实一点也不简单: 这种情况下,在云上交付一个应用的过程,实际上非常坎坷。 举几个例子:作为云上的开发者,我们首先需要花费大量的时间来进行应用整体部署架构的设计,才能大致搞清楚这个应用到底需要开通哪些云服务。但这依然避免不了一些让人头疼情况的发生,例如: 因为一个操作流程失误,一些需要预先申请的云资源不到位,就得返工重来; 一旦某个云服务的配置不合理,就得重新配置,甚至删掉重来; 整个上线应用的过程,我们需要不停地在各种云产品控制台之间来回跳转; 还有很多时候,我们不得不一个一个手工处理应用删除遗留下来的各种云资源。 上述情况的出现,总的来说是因为云上应用管理的过程,实际上是一个离散的状态,导致整个流程杂乱无序,也很难把控,出错返工就在所难免了。 再举个例子。除了过程上的繁杂之外,云端应用管理的另一个现状就是:开发者总是需要不停的去开通和配置各种各样的云服务,同时也要花大量的时间去开发各种云产品的开通和接入代码。尤其让人头疼的是,这些所有对云服务的开通、配置和接入工作,几乎都是“一次性工作”。我给一个应用组件做的事情,再上线另一个应用组件的时候,又得全部重新做一遍。甚至有时候为了接入一个新的云服务,必须重新开发整个应用。上述情况的出现,归根到底是因为对于应用所依赖的云服务来说,它们的开通与配置工作,并不是一个可复用的能力,这就导致了大量的重复劳动和对接工作需要一而再、再而三的在应用管理的过程中不断重复进行。 这些情况,都是现今制约和困扰着云端应用开发和运维人员的几个典型“症状”,也点明了当前云应用管理过程“耗时”、“耗力”背后,两个显著的症结所在: 应用本身:不能以统一、自描述的方式定义应用与云资源的关系; 云基础设施本身:没有一种统一、标准、高效的方式交付给应用使用。 智能手机 VS 云 在体验到了手机上 App 管理的流畅和简单之后,现在再来看云端应用管理的卡顿和繁杂,我们不禁会想到这样一个问题:云计算技术日新月异的今天,我们是否有机会在云上也感受到像智能手机一样的应用管理体验呢? 事实上,如果我们深入分析一下智能手机上的应用管理体系和如今的云端应用管理架构,就会发现,这两者本质上是完全一致的。 首先,在标准的硬件,或者说手机的“标准化基础设施”之上,智能手机已经为使用者铺上了一个“标准化的接入层”。在 iPhone 上,这个标准化接入层,就是 iOS 操作系统,它对上暴露出 UNIX 风格的操作系统接口来屏蔽掉底层资源的细节。在云上,这一层实际上就是 Kubernetes 和云服务本身的 OpenAPI。 但,这显然还不是全部。 无论是应用开发者还是 APP 最终用户,我们还是不会直接跟 UNIX 操作系统接口打交道。这是因为,在“标准化的接入层”之上,iPhone 还为使用者提供了一整套的“标准化应用架构与管理体系”。这套体系,既包括了对操作系统接口的 Library 化封装(即:模块化的 SDK),也包括应用如何组织和打包的交付规范,还以此为基础,提供了 IDE 等一系列开发工具甚至编程语言。这才使得基于 iOS 之上的应用管理,呈现出了“如丝般顺滑”的用户体验。 这时候,我们再回过头来看云计算。就会发现:在对应“标准化的应用架构与管理体系”这一层,云的能力目前是缺失的。云上的应用,并没有一种统一和标准的方式来描述自己与云资源的关系,也没有一种统一和标准的方式来对接云计算的基础设施服务。 这也是为什么在 2006 年,一位开发者上线世界上最早的 S3 应用,只需要花费几天的时间,然而 13 年后,当云计算提供的能力已经强大到天壤之别的今天,我们在云上交付和升级一个 Java Web 网站,却恐怕还是要花费数小时之久,甚至更长。 什么是 OAM? 开放云应用模型 Open Application Model(OAM),它正是一个云原生时代的应用标准定义与架构模型。Open Application Model 的主要内容,就是为云端应用管理者提供了一套描述应用的规范。应用管理者只要遵守这个规范,就可以编写出一个自包含、自描述的“应用定义文件”。具体的说,这个应用定义文件实际上由两部分组成: 应用描述 = 应用组件 + 应用特征 应用组件:应用开发者通过一个声明式的描述,来定义他要部署和管理的是什么样的应用。比如,是 Java Web 网站?是容器?还是 Function?这个应用怎么运行,是通过 Kubernetes ?还是 ECS?需要注意的是,这个应用描述,是对应用本身开发和运行方式的、一个开发人员视角的叙述,它不会包括任何应用运维和基础设施相关的细节。 应用特征:应用运维人员则通过另一个声明式的描述,来定义这个应用的“运维特征”。比如,安全组策略怎么设置?路由访问策略是什么规则?水平扩展策略如何?可以看到,这些应用特征,实际上是对应用运维细节和基础设施能力的叙述。 所以说,在 OAM 规范下,在云上管理一个应用,实际上是通过这样两个声明式的描述配合来完成的。在实际操作中,应用开发人员只需要提交他所编写的应用描述,运维人员则负责定义和管理各种各样的应用特征。云平台或者应用架构师,则负责按照应用描述中的需求,为它绑定合适的应用特征。 而 OAM 这套规范的特殊性在于,它并不是一套“应用配置 + 外部依赖 ”的简单组合,而是在应用定义的规范中,“植入”了对云端应用管理至关重要的两个“解耦”: 1)应用管理过程中的角色解耦。通过这个解耦,OAM 应用管理流程中开发和运维角色就可以各司其职,只关注自己关心的部分,这是 OAM 提升开发者生产力的重要途径。而与此同时,云平台或应用架构师,则可以灵活的组合应用特征和应用描述,从而在完全不影响开发人员体验的情况下,为云上的应用搭配合适的应用特征。这种关注点分离(Seperate Conerns)的设计,使得 OAM 模型下的应用的定义,不仅职责明确,同时也能清晰的自描述出一个应用所依赖的所有云基础设施能力(即:特征),并为对它们的关系进行系统化管理提供了基础。 2)应用定义与实现层解耦。通过这个解耦,用户的应用定义和云的基础设施能力是完全分开的。所以一个应用,不会再被局限于只能运行某种具体的平台或者云服务上:对于这些应用运行时的选择,只是应用描述中的一个可配置参数而已。而应用的运维特征,在实现层实际上就是每一个云基础设施能力的模块化实现。所以一旦一个应用描述与某个应用特征绑定,云平台就可以自动将对应的云服务接入到应用运行时当中,从而避免了用户陷入到“永无止境”的云服务开通和配置工作中。 不过,OAM 对于云端应用管理的价值,还远远不止更好的用户体验这么简单。 应用特征系统:平衡可移植性和差异性 在 Open Application Model 的模型中,应用管理人员可以灵活搭配应用描述与应用特征,从而对接不同的云基础设施能力到应用中。这种基础设施能力通过 OAM 对用户透出之后,实际上就能够差异化的表达不同运行环境能够为应用提供的不同基础设施能力。 举个例子,一位开发者定义了一个叫做“车”的应用描述,并做出了如下叙述: 要有安全性 要能有操控能力 要有行使动力 然后,他把这样的应用描述交给了云平台,云平台根据这个描述,为它绑定了一组比较基础的“应用特征”: 安全:安全带、气囊、普通刹车 操控:手动 5 档、普通方向盘 动力:普通发动机 这样,这个应用在它的最终用户看来,实际上就是一个“家用汽车”。 但是,过了一阵子,开发者决定对这个“车”进行一次升级。这时候,他该怎么做呢? 实际上,他什么也不用做。他只要告诉应用运维,为之前的“车”应用描述,绑定一组更加“强大”的应用特征即可,比如: 安全:高强度车架、悬挂减震、全身防护、赛车式刹车 操控:手动 8 档、赛车方向盘 动力:赛车引擎 所以,一旦上述“更强大”的应用特征,同“车”应用描述绑定,我们就可以将一个“家用车”立刻升级成一部强大的“赛车”。而在这个过程中,应用开发和应用运维唯一要做的事情,就是像“乐高积木”那样,灵活搭配和组装不同的应用特征而已。 而更重要的是,OAM 的设计初衷之一,是要提供标准化云端应用管理体验,这就需要“抹平”不同运行环境之间的不同,以便让应用“ 一次定义,多处运行”。但与此同时,OAM 提供的应用特征系统,则使得云平台标准化的暴露出自己的能力之后,用户依然可以通过对比不同运行环境的应用特征的差异,从而更准确的选择自己的应用要部署到哪个运行环境当中,真正意义上实现“Define Once, Run Where I Choose” 的交付体验。所以说,应用特征系统,正是 OAM 在设计中平衡可移植性和差异性的一个重要的创新之举。 OAM 项目的现状与未来 OAM 开源项目,主要包括了两部分内容: 1、Open Application Model Specification (OAM Spec):这个项目是 OAM 模型的官方规范与标准库。在这个代码库中,详细的阐述和定义了 OAM 模型中的所有核心理念和详细到具体字段的 API 规范。与此同时,这些所有的 Spec 也都原生提供了进一步的扩展规范,使得这个模型本身具备了接入任意运行时、模块化任意云基础设施能力、标准化描述任意应用运维特征的、高灵活度的模型约束力。可以看到,这个库堪称 OAM 的使用者和实现者的官方“圣经”。 OAM Spec 项目 GitHub 地址:https://github.com/oam-dev/spec 2、Rudr 项目: 这个项目是 Open Application Model 在 Kubernetes 上的标准实现。Rudr 项目本身是 Kubernetes 的一个标准插件,只要安装上去即可为用户提供标准的 OAM 风格的的应用管理能力,通过模块化应用特征同 SMI,Knative,Istio 等应用基础设施能力无缝对接。值得一提的是,Rudr 项目是由 Rust 编写完成的,它的实现简洁、高效,性能优异,也是全世界第一个 Rust 实现的 Kubernetes 生态开源组件。 Rudr 项目 GitHub 地址:https://github.com/oam-dev/rudr 虽然 OAM Spec 和 Rudr 项目目前是由阿里云和微软云的工程师共同发起和维护的,但这两个项目本身,均从一开始就采用中立基金会的方式进行运作,使用完全中立和开放的开源贡献者协议,同任何一家公司或者组织都没有绑定关系。这么做的原因也非常明朗:作为未来云计算应用管理生态的基础性模型,Open Application Model 从一开始就采用完全中立和开放的方式同整个社区协作,并计划在项目稳定后便移交给中立基金会进行托管。 目前,OAM 已经在阿里云多个项目中进行了数月的内部落地的尝试。通过一套统一、标准的应用定义体系,承载了多个云应用管理项目和产品的应用与外部资源关系的高效管理,并将云原生应用管理体验统一的带给了基于 Function、ECS、Kubernetes 等不同运行时的应用管理流程;通过应用特征系统,将多个阿里云独有的能力进行了模块化,大大提高了阿里云基础设施能力的交付效率。 与此同时,作为 OAM 的初创参与方,微软 Service Fabric 团队已经开始通过 OAM 模型将 Service Fabric 强大的应用基础设施能力进行了“乐高积木化”,从而无缝接入到云原生技术体系当中,并进一步在此基础上开始了“Mesh 化编程模型”的构建。 在未来的发展过程中,OAM 将会始终致力为云应用管理的参与者带来智能手机一般的使用体验,在保证可移植性和可扩展性的前提下,以标准化的方式帮助“应用”高效和高质量地交付到世界上任何一个位置。我们期待全世界每一位开发者和每一个云计算生态的参与者,都能加入到这个全新的应用架构模型的发展过程中来,共同打造一个繁荣的云端应用生态背后的标准模型和基础依赖。 原文发布时间为:2019-10-18作者:张磊本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:Flutter从诞生到现在,已经成为了跨端开发的领跑者。闲鱼应用在flutter能够以模块形式存在前,进行了很长时间的混合app架构的探索,对原生工程进行较多改动,在官方推出flutter模块模式后,我们进行了大量调研,最终推出了一套开箱即用的混合工程脚手架flutter-boot,帮助大家快速搭建混合工程。Google Flutter 产品经理推荐,精解Flutter企业级实践,揭秘亿级流量背后的技术秘籍,工程师不容错过的学习宝典的《Flutter in action》,今天正式发布,文末下载。 作者 | 兴往、向志明、马引 背景 国内外越来越多的公司走上了flutter探索之路。Flutter的主要开发模式分成两种,一种是独立app的模式,以flutter为主,原生工程会被包含在flutter工程下;另一种是让flutter以模块(flutter module)的形式存在,分别集成在已有的iOS和android原生应用下,此时原生工程可以在任何的目录结构下,和flutter工程地址不产生关联,但需要在原生工程结构中声明flutter工程的本地地址。 简介 flutter-boot核心解决了混合开发模式下的两个问题:flutter混合开发的工程化设计和混合栈。那flutter-boot是如何解决的呢? 首先在工程化设计的问题上,flutter-boot建立了一套标准的工程创建流程和友好的交互命令,当流程执行完成后,即拥有了混合开发的标准工程结构,这一套工程结构能够帮助我们同时拥有flutter和native(原生)两种开发视角,本地flutter开发和云端flutter构建两种flutter集成模式,其效果如图: 另外在混合栈的问题上,flutter-boot能自动注入混合栈依赖,同时将核心的混合栈接入代码封装后注入到原生工程内,在用户按提示插入简单几行模版代码后,即可看到混合栈的效果。 使用flutter-boot搭建的混合工程,开箱即可使用,接下来让我们了解下flutter-boot解决这些问题的详细过程。 开源地址https://github.com/alibaba-flutter/flutter-boot 工程化设计 了解官方的Add Flutter to existing apps项目 在了解flutter-boot的工程化设计细节前,我们需要对Google官方提供的Add Flutter to existing apps方案有一个初步的了解。Add Flutter to existing apps项目会引导我们以module的形式创建flutter,module形态的flutter的工程结构如下: some/path/ my_flutter/ lib/main.dart .ios/ .android/ 在官方的工程结构下,.ios和.android是运行flutter时的模版工程,在flutter工程目录下运行时即通过这两个工程来启动应用。那我们如何让原生工程和产生关联呢?这里的关联会分成三个部分,分别是flutter的framework,flutter的业务代码,和flutter的插件库。其中flutter插件库分成 flutter plugin native(即插件原生代码)和flutter plugin dart(即插件的dart代码)两个部分。这四部分的差异在于: 因此flutter framework只需要在依赖管理中声明即可,flutter plugin native可以直接以源码的方式集成,flutter plugin dart只有在被业务代码引用时才有效,因此和业务代码一样,需要支持dart代码的调试模式和发布模式,因此dart代码的关联会侵入到app的构建环节,根据app构建的模式来决定dart代码的构建模式。 具体的实现,拿iOS来举例,我们会在podfile文件中增加一个自定义的ruby脚本podfilehelper的调用,podfilehelper会声明flutter framework的依赖,声明flutter plugin native的源码引用,同时声明业务代码的路径。接下来会介入构建流程,在xcode的build phase内加入shell脚本xcode_backend的调用,xcode_backend会根据当前构建模式,来产出dart构建产物。 flutter-boot的补充 对于官方的混合工程项目,我们在体验后发现有如下的问题: 文件或配置的添加为手动添加,流程较长。 不支持在flutter仓库下运行原生工程。 不支持flutter以独立代码仓库部署时的远端机器构建。 因此在flutter-boot脚手架中,为了解决这些问题,我们把混合工程的部署分为create,link,remotelink,update四个过程。 | create create过程目的在于帮助我们搭建一个flutter module,包括flutter module的创建和git仓库的部署,flutter module创建命令调用前,我们会做基础的检查来让工程位置和命名的规范满足官方的条件。在git仓库部署时,我们会在gitignore中忽略部分文件,同时我们会对仓库的状态进行检查,在仓库为空时,直接添加文件,在仓库非空时,会优先清理仓库。 | link link过程目的在于关联本地的原生工程和flutter工程。关联的过程中,我们会先请求获取flutter工程的地址和原生工程的地址,然后我们将上面提到的需要手动集成的部分通过脚本的方式自动集成;为了获得flutter开发视角(即flutter工程下运行原生工程),我们将原生工程进行了软链接,链接到flutter工程的ios目录和android目录,flutter在运行前会找到工程下的ios或android目录然后运行,在flutter工程下运行iOS工程会存在一个限制,即iOS工程的target需要指定为runner,为了解决这个问题,我们将原生工程的主target进行了复制,复制了一份名为runner的target。 同时,为了支持远程构建的模式,我们flutter仓库本地路径的声明根据构建模式进行了区分,封装在自定义的依赖脚本中,例如在iOS工程内,我们会添加fbpodhelper.rb脚本文件。然后将flutter仓库本地路径添加到了配置文件fbConfig.local.json中。 | remotelink update remotelink过程目的在于远端构建模式下,能够获取flutter仓库的代码,并在远端机器上进行构建。在远端构建模式下,我们会侵入依赖管理的过程,在依赖获取时,拉取flutter仓库的代码,将代码放置在原生工程的.fbflutter目录下,并将该目录声明为flutter仓库本地路径,拉取flutter代码并进行本地部署的过程,我们称之为update过程。 这样在远端构建时就能和本地构建如出一辙。 那远端模式和本地模式如何区分呢?为了区分远端模式与本地模式,我们将远端的flutter仓库信息记录在fbConfig.json,同时在gitignore中忽略fbConfig.local.json文件,这样只需要初始化混合工程的工程师运行一次remotelink,其他的开发协同者将不用关注远端构建的配置流程。 | init 为了方便快速搭建,我们提供了一个命令集合,命名为init,我们将必备的环节以命令行交互的模式集成在了init命令中。 混合栈 混合栈是闲鱼开源的一套用于flutter混合工程下协调原生页面与flutter页面交互的框架,目前是混合开发模式下的主流框架。在混合栈开源后,我们关注到大量开发者在集成混合栈时会产生各种环境配置或代码添加导致的集成问题。因此我们决定提供一套快速集成的方案。要做到快速集成我们面临两个问题: flutter和混合栈的版本兼容; 混合栈demo代码封装及插入。 版本兼容问题 目前混合栈发布版本为0.1.52,支持flutter 1.5.4。当flutter升级时混合栈势必要进行适配,即我们集成的混合栈版本也需要变更。因此我们将混合栈的版本配置通过文件进行维护,记录当前flutter所需要的混合栈版本。在初版的flutter-boot中,我们限定了混合栈的版本号,在新版本混合栈发布时,我们将开放版本选择的功能。 代码封装及插入问题 在调研了混合栈的使用过程后,我们将混合栈需要的demo代码分成了四个部分: flutter引擎的托管; 页面路由的配置 ; demo形式的dart页面 ; 原生的测试跳转入口。 | flutter引擎的托管 引擎的托管我们依赖于应用的初始化,由于初始化过程随着应用的复杂程度提升而提升,因此目前我们提供了一行代码作为接口,使用者在应用初始化时加入这一行代码即可完成托管。 | 页面路由的配置 demo形式的dart页面,路由配置即路由到某个标识符时,flutter或原生页面需要识别并跳转相应页面。路由的配置需要在原生和flutter两侧进行部署。在原生侧,我们将混合栈的demo路由代码进行了精简,然后添加在了原生工程的固定目录下。由于iOS仅添加代码文件是不会被纳入构建范围的,因此我们封装了一套iOS侧的代码添加工具来实现文件的插入。在flutter侧我们对main.dart文件进行了覆盖,将带有路由逻辑的main.dart集成进来,同时提供了demo dart页面的创建逻辑。 | 原生的测试跳转入口 为了方便使用者快速看到混合工程的跳转模式,我们在iOS和android双端封装了一个入口按钮和按钮的添加过程,使用者在测试的页面手动加入一行代码,即可看到跳转flutter的入口。 效果 在使用flutter-boot前,开发者可能要花费数天来进行混合工程搭建,现在,使用者只需要调用一个命令,加入两行代码即可完成混合工程的搭建,大大降低了开发者的开发成本。但flutter-boot的使命还未达成,我们期望使用者能更加流畅的进行flutter开发,未来我们会优化多人协同的开发流程,完善持续集成环境的搭建,让使用者拥有更佳的开发体验。 最后,福利来了,现在承载亿级流量的闲鱼将多年最佳实践经验整理成册,《Flutter in action》 面世啦,本篇内容仅为书中的精华之一! 4大精彩看点,快速了解《Flutter in action》 Flutter开源工具大集合 闲鱼Flutter应用框架Fish Redux、开发利器AspectD、FlutterBoost等一众开发工具正式开源,现在《Flutter in action》一次帮你找全了。开源地址+技术解析,手把手教你Flutter应用框架和混合开发原理! 闲鱼Flutter企业级应用实践 随着无线,IoT的发展,5G的到来,移动研发越发向多端化发展。传统的基于Native+Web+服务端的开发方式,研发效率低下,显然已经无法适应发展需要。 而Flutter是Google开源的跨端便携UI工具包,除了具有非常优秀的跨端渲染一致性,还具备非常高效的研发体验,丰富的开箱即用的UI组件,以及跟Native媲美的性能体验。由于它的众多优势,也使得Flutter成为了近些年来热门的新技术。 在这本书中将详细讲解闲鱼Flutter&FaaS云端一体化架构,和闲鱼基于Flutter的架构演进与创新,学习一套全面的Flutter架构应用方案。 混合开发实践指南 你将在这里看到闲鱼技术团队利用Flutter技术改造和上线复杂业务的混合工程改造实践、抽取Flutter依赖到远程的实现细节、以及使用Plugin桥接获取设备信息、使用基础网络库等混合开发实践指南。 这些实践遍布闲鱼各大业务线和应用场景,为你使用Flutter打造自己的研发体系探索一条实践之路。 Flutter的深入进阶教程 在获得开源工具与开发实践指南后,你还将在本书中学到Flutter的更多应用场景。 它们包括了如何低成本实现Flutter富文本、设计一个高准确率的Flutter埋点框架、Flutter外接纹理、可定制化的Flutter相册组件等等深入进阶内容。 如何下载 Google Flutter 产品经理推荐,揭秘亿级流量背后的技术秘籍,工程师不容错过的学习宝典,包括开源工具、峰会直达、混合开发、深入进阶等,点击下载即可获得完整资源。 原文发布时间为:2019-10-17本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:本文主要介绍阿里巴巴和蚂蚁金服在大规模生产环境中落地 Kubernetes 的过程中,在集群规模上遇到的典型问题以及对应的解决方案,内容包含对 etcd、kube-apiserver、kube-controller 的若干性能及稳定性增强,这些关键的增强是阿里巴巴和蚂蚁金服内部上万节点的 Kubernetes 集群能够平稳支撑 2019 年天猫 618 大促的关键所在。文内藏福利,向下滑滑滑,免费课程立刻领取~ 背景 从阿里巴巴最早期的 AI 系统(2013)开始,集群管理系统经历了多轮的架构演进,到 2018 年全面的应用 Kubernetes ,这期间的故事是非常精彩的。这里忽略系统演进的过程,不去讨论为什么 Kubernetes 能够在社区和公司内部全面的胜出,而是将焦点关注到应用 Kubernetes 中会遇到什么样的问题,以及我们做了哪些关键的优化。 在阿里巴巴和蚂蚁金服的生产环境中,容器化的应用超过了 10k 个,全网的容器在百万的级别,运行在十几万台宿主机上。支撑阿里巴巴核心电商业务的集群有十几个,最大的集群有几万的节点。在落地 Kubernetes 的过程中,在规模上面临了很大的挑战,比如如何将 Kubernetes 应用到超大规模的生产级别。 罗马不是一天就建成的,为了了解 Kubernetes 的性能瓶颈,我们结合阿里和蚂蚁的生产集群现状,估算了在 10k 个节点的集群中,预计会达到的规模: 20w pods 100w 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 在大规模场景下的性能。 首先是 etcd 层面,作为 Kubernetes 存储对象的数据库,其对 Kubernetes 集群的性能影响至关重要。 第一版本的改进,我们通过将 etcd 的数据转存到 tair 集群中,提高了 etcd 存储的数据总量。但这个方式有一个显著的弊端是额外增加的 tair 集群,增加的运维复杂性对集群中的数据安全性带来了很大的挑战,同时其数据一致性模型也并非基于 raft 复制组,牺牲了数据的安全性。 第二版本的改进,我们通过将 API Server 中不同类型的对象存储到不同的 etcd 集群中。从 etcd 内部看,也就对应了不同的数据目录,通过将不同目录的数据路由到不同的后端 etcd 中,从而降低了单个 etcd 集群中存储的数据总量,提高了扩展性。 第三版本的改进,我们深入研究了 etcd 内部的实现原理,并发现了影响 etcd 扩展性的一个关键问题在底层 bbolt db 的 page 页面分配算法上:随着 etcd 中存储的数据量的增长,bbolt db 中线性查找“连续长度为 n 的 page 存储页面”的性能显著下降。 为了解决该问题,我们设计了基于 segregrated hashmap 的空闲页面管理算法,hashmap 以连续 page 大小为 key, 连续页面起始 page id 为 value。通过查这个 segregrated hashmap 实现 O(1) 的空闲 page 查找,极大地提高了性能。在释放块时,新算法尝试和地址相邻的 page 合并,并更新 segregrated hashmap。 通过这个算法改进,我们可以将 etcd 的存储空间从推荐的 2GB 扩展到 100GB,极大的提高了 etcd 存储数据的规模,并且读写无显著延迟增长。除此之外,我们也和谷歌工程师协作开发了 etcd raft learner(类 zookeeper observer)/fully concurrent read 等特性,在数据的安全性和读写性能上进行增强。这些改进已贡献开源,将在社区 etcd 3.4 版本中发布。 API Server improvements Efficient node heartbeats 在 Kubernetes 集群中,影响其扩展到更大规模的一个核心问题是如何有效的处理节点的心跳。在一个典型的生产环境中 (non-trival),kubelet 每 10s 汇报一次心跳,每次心跳请求的内容达到 15kb(包含节点上数十计的镜像,和若干的卷信息),这会带来两大问题: 心跳请求触发 etcd 中 node 对象的更新,在 10k nodes 的集群中,这些更新将产生近 1GB/min 的 transaction logs(etcd 会记录变更历史); API Server 很高的 CPU 消耗,node 节点非常庞大,序列化/反序列化开销很大,处理心跳请求的 CPU 开销超过 API Server CPU 时间占用的 80%。 为了解决这个问题,Kubernetes 引入了一个新的 build-in Lease API ,将与心跳密切相关的信息从 node 对象中剥离出来,也就是上图中的 Lease 。原本 kubelet 每 10s 更新一次 node 对象升级为: 每 10s 更新一次 Lease 对象,表明该节点的存活状态,Node Controller 根据该 Lease 对象的状态来判断节点是否存活;处于兼容性的考虑,降低为每 60s 更新一次 node 对象,使得 EvictionManager 等可以继续按照原有的逻辑工作。 因为 Lease 对象非常小,因此其更新的代价远小于更新 node 对象。kubernetes 通过这个机制,显著的降低了 API Server 的 CPU 开销,同时也大幅减小了 etcd 中大量的 transaction logs,成功将其规模从 1000 扩展到了几千个节点的规模,该功能在社区 Kubernetes-1.14 中已经默认启用,更多细节详见 KEP-0009。 API Server load balancing 在生产集群中,出于性能和可用性的考虑,通常会部署多个节点组成高可用 Kubernetes 集群。但在高可用集群实际的运行中,可能会出现多个 API Server 之间的负载不均衡,尤其是在集群升级或部分节点发生故障重启的时候。这给集群的稳定性带来了很大的压力,原本计划通过高可用的方式分摊 API Server 面临的压力,但在极端情况下所有压力又回到了一个节点,导致系统响应时间变长,甚至击垮该节点继而导致雪崩。 下图为压测集群中模拟的一个 case,在三个节点的集群,API Server 升级后所有的压力均打到了其中一个 API Server 上,其 CPU 开销远高于其他两个节点。 解决负载均衡问题,一个自然的思路就是增加 load balancer。前文的描述中提到,集群中主要的负载是处理节点的心跳,那我们就在 API Server 与 kubelet 中间增加 lb,有两个典型的思路: API Server 测增加 lb,所有的 kubelets 连接 lb,典型的云厂商交付的 Kubernetes 集群,就是这一模式; kubelet 测增加 lb,由 lb 来选择 API Server。 通过压测环境验证发现,增加 lb 并不能很好的解决上面提到的问题,我们必须要深入理解 Kubernetes 内部的通信机制。深入到 Kubernetes 中研究发现,为了解决 tls 连接认证的开销,Kubernetes 客户端做了很多的努力确保“尽量复用同样的 tls 连接”,大多数情况下客户端 watcher 均工作在下层的同一个 tls 连接上,仅当这个连接发生异常时,才可能会触发重连继而发生 API Server 的切换。其结果就是我们看到的,当 kubelet 连接到其中一个 API Server 后,基本上是不会发生负载切换。为了解决这个问题,我们进行了三个方面的优化: API Server:认为客户端是不可信的,需要保护自己不被过载的请求击溃。当自身负载超过一个阈值时,发送 429 - too many requests 提醒客户端退避;当自身负载超过一个更高的阈值时,通过关闭客户端连接拒绝请求; Client:在一个时间段内频繁的收到 429 时,尝试重建连接切换 API Server;定期地重建连接切换 API Server 完成洗牌; 运维层面,我们通过设置 maxSurge=3 的方式升级 API Server,避免升级过程带来的性能抖动。 如上图左下角监控图所示,增强后的版本可以做到 API Server 负载基本均衡,同时在显示重启两个节点(图中抖动)时,能够快速的自动恢复到均衡状态。 List-Watch & Cacher List-Watch 是 Kubernetes 中 Server 与 Client 通信最核心一个机制,etcd 中所有对象及其更新的信息,API Server 内部通过 Reflector 去 watch etcd 的数据变化并存储到内存中,controller/kubelets 中的客户端也通过类似的机制去订阅数据的变化。 在 List-Watch 机制中面临的一个核心问题是,当 Client 与 Server 之间的通信断开时,如何确保重连期间的数据不丢,这在 Kubernetes 中通过了一个全局递增的版本号 resourceVersion 来实现。如下图所示 Reflector 中保存这当前已经同步到的数据版本,重连时 Reflector 告知 Server 自己当前的版本(5),Server 根据内存中记录的最近变更历史计算客户端需要的数据起始位置(7)。 这一切看起来十分简单可靠,但是…… 在 API Server 内部,每个类型的对象会存储在一个叫做 storage 的对象中,比如会有: Pod Storage Node Storage Configmap Storage …… 每个类型的 storage 会有一个有限的队列,存储对象最近的变更,用于支持 watcher 一定的滞后(重试等场景)。一般来说,所有类型的类型共享一个递增版本号空间(1, 2, 3, ..., n),也就是如上图所示,pod 对象的版本号仅保证递增不保证连续。Client 使用 List-Watch 机制同步数据时,可能仅关注 pods 中的一部分,最典型的 kubelet 仅关注和自己节点相关的 pods,如上图所示,某个 kubelet 仅关注绿色的 pods (2, 5)。 因为 storage 队列是有限的(FIFO),当 pods 的更新时队列,旧的变更就会从队列中淘汰。如上图所示,当队列中的更新与某个 Client 无关时,Client 进度仍然保持在 rv=5,如果 Client 在 5 被淘汰后重连,这时候 API Server 无法判断 5 与当前队列最小值(7)之间是否存在客户端需要感知的变更,因此返回 Client too old version err 触发 Client 重新 list 所有的数据。为了解决这个问题,Kubernetes 引入 watch bookmark 机制: bookmark 的核心思想概括起来就是在 Client 与 Server 之间保持一个“心跳”,即使队列中无 Client 需要感知的更新,Reflector 内部的版本号也需要及时的更新。如上图所示,Server 会在合适的适合推送当前最新的 rv=12 版本号给 Client,使得 Client 版本号跟上 Server 的进展。bookmark 可以将 API Server 重启时需要重新同步的事件降低为原来的 3%(性能提高了几十倍),该功能有阿里云容器平台开发,已经发布到社区 Kubernetes-1.15 版本中。 Cacher & Indexing 除 List-Watch 之外,另外一种客户端的访问模式是直接查询 API Server,如下图所示。为了保证客户端在多个 API Server 节点间读到一致的数据,API Server 会通过获取 etcd 中的数据来支持 Client 的查询请求。从性能角度看,这带来了几个问题: 无法支持索引,查询节点的 pod 需要先获取集群中所有的 pod,这个开销是巨大的; 因为 etcd 的 request-response 模型,单次请求查询过大的数据会消耗大量的内存,通常情况下 API Server 与 etcd 之间的查询会限制请求的数据量,并通过分页的方式来完成大量的数据查询,分页带来的多次的 round trip 显著降低了性能; 为了确保一致性,API Server 查询 etcd 均采用了 Quorum read ,这个查询开销是集群级别,无法扩展的。 为了解决这个问题,我们设计了 API Server 与 etcd 的数据协同机制,确保 Client 能够通过 API Server 的 cache 获取到一致的数据,其原理如下图所示,整体工作流程如下: t0 时刻 Client 查询 API Server; API Server 请求 etcd 获取当前的数据版本 rv@t0; API Server 请求进度的更新,并等待 Reflector 数据版本达到 rv@t0; 通过 cache 响应用户的请求。 这个方式并未打破 Client 的一致性模型(感兴趣的可以自己论证一下),同时通过 cache 响应用户请求时我们可以灵活的增强查询能力,比如支持 namespace nodename/labels 索引。该增强大幅提高了 API Server 的读请求处理能力,在万台规模集群中典型的 describe node 的时间从原来的 5s 降低到 0.3s(触发了 node name 索引),其他如 get nodes 等查询操作的效率也获得了成倍的增长。 Context-Aware API Server 接收请求并完成请求需要访问外部服务,如访问 etcd 将数据持久化、访问 Webhook Server 完成扩展性的 Admission 或者 Auth,甚至是 API Server 自己访问自己(loopback client) 去完成 ServiceAccount 的鉴权工作。 在这种 API Server 处理请求模型的框架下,就有如下这样的问题:当一个客户端的请求已经被客户端主动结束、或者超时结束时,如果 API Server 还依然还在为这个请求去请求外部的服务的数据、并没有也在第一时间及时停止请求,那么就会导致 Goruntine 和资源的“积压”。而客户端在主动结束、或者超时结束它的请求之后,因为 Kubernetes 面向终态的架构,客户端势必会立刻又发起新的请求,从而使得“积压”甚至是泄露的 Goruntine 和资源越来越多,最终导致 API Server OOM 和 crash。 我们都知道 golang 中使用 context 来表示“上下文”的含义。API Server 请求外部服务的“上下文”就是客户端发起请求,那么当客户端的请求结束之后,API Server 也应该立刻回收 API Server 请求外部服务的资源,即这类请求也应该立刻停止并退出,只有这样,API Server 才能提高吞吐并不会被积压的 Goruntine 和资源所拖累。 阿里巴巴和蚂蚁金服的工程师发现并参与了 API Server 全链路的 context-aware 的优化工作,Kubernetes v1.16 版本已经将 Admission、Webhook 等优化为 context-aware,从而进一步提升 API Server 的性能和吞吐。 Requests Flood Prevention API Server 对于接收处理请求的自我保护能力太过薄弱,目前可以说除了 max-inflight filter 做了限制最大读、写并发外,没有其它能够限制请求数量和并发的功能。这带来一个非常大的问题:API Server 可能因为接收并处理太多的请求从而导致 API Server OOM 或者崩溃。 虽然 API Server 是一个内部的系统,几乎没有外来请求的攻击,所有的请求都来自 Kubernetes 内部的组件和模块,API Server 也可能因为内部的请求量过大而导致自己身崩溃。根据我们的观察和经验,API Server 接收过多请求处理而导致崩溃的主要场景有如下两部分: API Server 自身重启或者升级 我们知道 Kubernetes 是以 API Server 为中心的系统。当 API Server 重启或者升级之后,所有的组件 client 都断开了连接并开始重新请求 API Server,特别是重新建立 List/Watch 需要比较大的资源开销。而 API Server 与 etcd 有自己的 cache 层,当客户端的 Informer List 请求到来之时,如果 cache 还未 ready 就会去请求 etcd,而大量的从 etcd List 资源可能会将 API Server 与 etcd 网络链路打满,甚至出现 API Server 和 etcd 的 OOM。而刚启动的 API Server 就陷入 crash,势必会导致客户端更大量的请求,从而陷入雪崩状态。 对于这种场景,我们采用“主动拒绝”请求的方式。在 API Server 刚启动之时,如果 API Server 和 etcd 之间的 cache 还未 Ready,API Server 就会拒绝耗资源较大的请求,如 List 资源的请求:只有在 cache Ready 之后,API Server 才向客户端提供 List 资源的服务,否则返回 429 让客户端等待一短时间后重试。只有这样,API Server 才能将接受大规模请求的主要瓶颈优化到 API Server 和客户端网络上 IO 的瓶颈。 客户端组件出现 Bug,疯狂的请求 API Server 特别是 DaemonSet 组件出现 Bug,那么请求量将乘以节点数目。我们在线上发生过 Daemonset 出现 Bug,上万个节点一直疯狂 List Pod 从而导致 API Server crash 的案例。 对于这种的场景,我们采用应急限流的方案。我们实现了可以动态配置,根据请求来源的 User-Agent 去做限流。当再次出现此类问题时,我们从监控图表里发现有问题的 User-Agent 并将它限流。只有在 API Server 健康的前提下,我们才能对 DaemonSet 做出修复并升级。在 API Server crash 时,DaemonSet 的升级也失效了,从而陷入集群无法挽救的局面。 采用 User-Agent 而非根据 Identity 信息(请求的用户信息) 做限流原因是因为 API Server 做请求的身份识别也需要耗费资源,很可能因为在为大量请求做身份识别过程中就出现 API Server 资源耗尽的情况。其次,我们可以从监控中快速的发现有问题的请求的 User-Agent,从而做到更快速的响应。 阿里和蚂蚁金服的工程师已经将该限流方案的 User Story 和优化方式已经提交到社区。 Controller failover 在 10k node 的生产集群中,Controller 中存储着近百万的对象,从 API Server 获取这些对象并反序列化的开销是无法忽略的,重启 Controller 恢复时可能需要花费几分钟才能完成这项工作,这对于阿里巴巴规模的企业来说是不可接受的。为了减小组件升级对系统可用性的影响,我们需要尽量的减小 controller 单次升级对系统的中断时间,这里通过如下图所示的方案来解决这个问题: 预启动备 controller informer ,提前加载 controller 需要的数据; 主 controller 升级时,会主动释放 Leader Lease,触发备立即接管工作。 通过这个方案,我们将 controller 中断时间降低到秒级别(升级时 < 2s),即使在异常宕机时,备仅需等待 leader lease 的过期(默认 15s),无需要花费几分钟重新同步数据。通过这个增强,显著的降低了 controller MTTR,同时降低了 controller 恢复时对 API Server 的性能冲击。该方案同样适用于 scheduler。 Customized scheduler 由于历史原因,阿里巴巴的调度器采用了自研的架构,因时间的关系本次分享并未展开调度器部分的增强。这里仅分享两个基本的思路,如下图所示: Equivalence classes:典型的用户扩容请求为一次扩容多个容器,因此我们通过将 pending 队列中的请求划分等价类的方式,实现批处理,显著的降低 Predicates/Priorities 的次数;Relaxed randomization:对于单次的调度请求,当集群中的候选节点非常多时,我们并不需要评估集群中全部节点,在挑选到足够的节点后即可进入调度的后续处理(通过牺牲求解的精确性来提高调度性能)。 总结 阿里巴巴和蚂蚁金服通过一系列的增强与优化,成功将 Kubernetes 应用到生产环境并达到了单集群 10000 节点的超大规模,具体包括: 通过将索引和数据分离、数据 shard 等方式提高 etcd 存储容量,并最终通过改进 etcd 底层 bbolt db 存储引擎的块分配算法,大幅提高了 etcd 在存储大数据量场景下的性能,通过单 etcd 集群支持大规模 Kubernetes 集群,大幅简化了整个系统架构的复杂性; 通过落地 Kubernetes 轻量级心跳、改进 HA 集群下多个 API Server 节点的负载均衡、ListWatch 机制中增加 bookmark、通过索引与 Cache 的方式改进了 Kubernetes 大规模集群中最头疼的 List 性能瓶颈,使得稳定的运行万节点集群成为可能; 通过热备的方式大幅缩短了 controller/scheduler 在主备切换时的服务中断时间,提高了整个集群的可用性; 阿里巴巴自研调度器在性能优化上最有效的两个思路:等价类处理以及随机松弛算法。 通过这一系列功能增强,阿里巴巴和蚂蚁金服成功将内部最核心的业务运行在上万节点的 Kubernetes 集群之上,并经历了 2019 年 618 大促的考验。 Kubernetes 是为生产环境而设计的容器调度管理系统,一经推出便迅速蹿红,它的很多设计思想都契合了微服务和云原生应用的设计法则。随着对 K8s 系统使用的加深和加广,会有越来越多有关云原生应用的设计模式产生出来,使得基于 K8s 系统设计和开发生产级的复杂云原生应用变得像启动一个单机版容器服务那样简单易用。 那么什么是“云原生”?作为云计算时代的开发者和从业者,我们该如何在“云原生”的技术浪潮中站稳脚跟,在将云原生落地的同时实现自我价值的有效提升呢? 现在,我们邀请来自全球“云原生”技术社区的亲历者和领军人物,为每一位中国开发者讲解和剖析关于“云原生”的方方面面,用视频的方式揭示这次云计算变革背后的技术思想和本质。 学习地址 金牌讲师带来重磅课程,点击“CNCF×Alibaba云原生技术公开课”,登录后,即可在线学习。 课程全程免费,我们将带来: 完善的知识体系,打造属于自己的云原生技术树; 理解云原生技术背后的思想与本质; 与知识体系相辅相成的动手实践; 一线技术团队云原生技术最佳实践。 适合人群: 计算机科学、软件工程等领域的软件工程师和大学生; 使用/尝试使用容器和Kubernetes技术的应用程序开发者; 具有基本服务器端知识、正在探索容器技术的软件开发者和技术管理者; 希望理解云原生技术栈基本原理的技术管理者和开发者。 部分讲师介绍 原文发布时间为:2019-10-16作者:曾凡松(逐灵)本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:API 是模块或者子系统之间交互的接口定义。好的系统架构离不开好的 API 设计,而一个设计不够完善的 API 则注定会导致系统的后续发展和维护非常困难。比较好的API设计样板可以参考 github 和 k8s ,它们都是典型的RESTful接口。云服务对外开放的窗口就是OpenAPI,今天要讨论的话题是“云服务场景下OpenAPI设计的挑战”。 为什么要有API规范 之所以强调“云服务”的原因在于,小规模独立API的设计与大规模批量生产API面临的问题是不一样的。同样,只专注于自身产品API的可用性与从更高的层次去看云服务整体API体系的健壮性,要建设的体系也是不一样的。 例如,做一个WEB页面使用的API,只需要考虑性能、稳定性、鉴权就好,因为页面与API是一体的,可以一起发布和回滚,只要功能正常,即便API设计有缺陷,用户也可以接受。而云服务要开放API考虑问题就多了: 首先,云服务开放的是基础设施和服务接口,一般是系统级的对接,API一旦开放想要变更就非常困难; 其次,云服务并非单独运行,不同的产品实际场景中是互相组合的,需考虑产品间的一致性和互通便利性; 云服务API数量庞大,为了更方便使用,配套的API查找、编排、自动化生成SDK等需求也比普通服务强烈; 云服务的稳定性非常重要,核心产品的稳定性就是客户的生命线,要求非常高。 所以云服务由于产品线众多,需要统一的API规范来保证云产品间API体验一致,在底层平台层面互相兼容,便于上层应用平台化,同时要兼顾到围绕公有云服务的第三方生态、开源生态、企业生态。 在具体规范层面,业界也在不断尝试,比较知名的是OpenAPI Specfication和 Google API Design Guide。前者针对RESTful API设计在细节层面给出了非常具体的规定,已经成为RESTful API设计领域的事实标准,而后者则主要从云厂商的角度提出许多最佳实践性质的规范与建议,这些原则不仅仅适用于RESTful API,也适合其他类型API设计。对API设计感兴趣的开发者,可以详细研究一下资料。 2018年Openapi Specification发布了3.0版本图片来源:https://swagger.io/ 因此,对于云服务商来说,在关键环节制定明确的API规范有助于云服务对内提高产品间互通的效率,对外提供一致的使用体验,有助于云服务更好地被集成。那么要做好云服务的横向规范会碰到哪些困难呢?本文就从实践中总结了7大挑战。 挑战1:选择API设计模式 当你在考虑单个产品的API表现形式时,首先会选择一种具体的API风格,常见的有RPC(Remote Procedure Call)和ROA(the Rest-Oriented Architecture)两种模式,然后针对复杂的数据结构你会考虑使用什么样的序列化协议,常见的包括Json/Xml/WSDL/Hessian等,用于封装传输数据。但涉及到不同的产品时,在具体选型时考虑的问题可能就不太一样了。 gRPC示意图图片来源:https://www.grpc.io/docs/guides/ 虽然RESTful设计风格曝光率很高,但并不是所有云服务商都选择了完全遵循RESTful,例如AWS和阿里云RPC风格反而占了大多数,Google和Azure则RESTful居多。 Restful API的优势是HTTP具备更好的易用性,让异构系统更容易集成,且开发执行效率比较高,面向资源要求也比较高。而RPC API可以使用更广泛的框架和方案,技术层面更底层也更为灵活,设计起来相对简单,掌握起来有一定门槛,架构上更加复杂。 RESTful与RPC模式对比 不同的团队根据实际情况和业务形态可能选择不同的方案,那云服务作为一个整体应该强制统一好还是随意选择好?如果强制统一风格,有些适合RESTful风格的服务非要使用RPC的话,看起来就会比较丑陋,如果只是一个过程化的服务调用,往RESTful资源化设计方向去靠会比较困难。但如果不强制使用统一风格,会造成针对API的体系化支持会更加复杂,例如为兼容两种风格SDK的自动化支持需要两套代码。 通常RESTful风格对API设计者的要求是比较高的,主要的难点在于面向资源设计要求开发者事先做好规划,将后端数据模型与API服务模型相匹配。所以RESTful API的开发者应该是熟知系统整体实体关系模型的,很难想象一个不懂后台业务的新手能设计出合理的API来。那么是不是RPC风格API就不需要面向资源设计了呢?从实践中来看,并不是!RPC风格的API也需要资源模型来支持,在下一节中会重点分析。 所以,对于云服务商来说,选择API风格时要考虑几个问题: 选择支持哪种风格,才能更好地体现业务特性,让客户操作起来更加方便; 设计API时能否面向资源设计,相应的工程人员是否具备做这种设计的能力; 针对这种风格工具链的支持是否到位,投入产出比如何; 业界流行的趋势如何,是否需要考虑与其他系统体系的互操作。 选定了具体设计模式后,就要努力做到统一,避免多种模式混杂带来管理成本的上升。如果确实有必要两种方式都支持(例如阿里云就同时支持RPC和ROA),就需要在技术上做好充分的准备。 挑战2:面向资源设计API 用户使用API来访问云服务,本质上是想通过对某种云资源执行特定的操作来完成一个业务动作。Restful API需要面向资源设计众所周知,那为什么RPC接口在云服务场景下也需要有资源模型呢? RPC形式的API组织形态是领域和行为(对象和方法),随着时间的推移,API越来越多最终形成一个庞大的集合。以阿里云为例,对外开放的OpenAPI数量已经达到10000多个,涵盖了接近200个不同的产品。因为开发者必须单独学习每个API,耗时又容易出错,如果没有一个脉络的理解起来比较困难。如果有一套标准的资源模型,API就可以按照资源模型的维度分类组织,用户使用起来也会有迹可循,体验上会更好,否则面对如此多的API一点点学习无疑是个痛苦的过程。 另外,云服务并非单个服务的简单排列,它是多个体系的横向整合,总体对外呈现出有机连接。随着云计算的发展,企业客户对对云服务的要求不断提高。最典型的就是当企业客户大规模开始上云后,对虚拟化的云产品提出了各种管理需求,例如鉴权、编排、弹性伸缩等。 企业客户对云服务的管理需求 以最常用的鉴权功能为例,客户创建了一组云服务器来跑业务,运维人员需要有重启服务器的权限,其他角色人员则不需要此类权限,针对RestartServer这么一个API就需要进行权限控制了。权限在云平台中一般是中心式管理的,单独的云产品不需要分别管理。如果统一处理鉴权,就需要知道当前API正在操作什么资源,与此相关的资源有哪些(例如与服务器有关的资源包括磁盘、网卡、网络等关联资源),然后针对所有相关资源逐一鉴权才能确认API操作是否可行。 上面提到的资源有两个关键点,一是要有统一的资源模型,便于云产品对特定的API进行鉴权,二是要明确资源关系,当出现资源依赖的时候,需要关联鉴权。在Google的API Guide中就明确提到需要资源关系,可以看出资源关系不仅仅是用来做API设计建模的,它对于平台功能的实现也有至关重要的作用。不了解资源关系,有可能把多个资源封装到一个API中,使用和变更都很痛苦,即便后期发现了问题再补救拆开,由于很多用户已经在使用了,要付出的开发成本和沟通成本也是极大的,甚至成为不可能。所以清晰的资源模型有利于梳理清楚API体系。 定义清晰的实体关系图有助于设计出更为完整的API 而且,明确的资源模型对于构建云上运维管理基础设施至关重要,例如可以通过对资源打Tag来对资源进行分类管理(参考阿里云资源Tag),分组授权(参考阿里云资源组),资源审计(参考阿里云CloudConfig),类似开源软件Terraform这样的编排工具,由于有资源模型会更容易接入和使用。 所以,统一的资源模型对云服务的帮助是巨大的: 它可以使API具有更清晰的结构,帮助用户理解; 它可以帮助对比API与后台实体关系模型,更容易提供更完整的API服务; 它可以使产品协作更加顺畅,对资源的操作也更加规范化; 它可以使云服务底层平台实现起来更统一、更方便; 它可以使围绕API的生态集成起来更加简单、高效。 既然有这么多好处,那么众多的云产品在实际设计API的时候能否坚持以资源为中心,充分考虑到上下游的需求就变成一个很大的挑战了。 挑战3:API设计风格 确定了设计模式和资源模型后,是时候进入到API设计细节了。诸如API名称、参数名、属性名称、数据格式、错误码之类的信息,看起来根本不是问题。单个产品问题还不大,只要保证内部风格一致即可,如果考虑到云服务多产品对外的整体体验,就有必要考虑以下问题: 在API命名的时候,遵循什么样的范式来确保大体风格相似?动词、名词、介词如何组合才能保持API风格看起来比较统一,降低理解成本? 对于类似的操作,有没有使用规范?有哪些公共的标准词汇使得同类型的操作可以比较容易理解,避免使用晦涩奇怪的词汇(例如读操作,Read/Query/Describe/List/Get中都在什么场合使用什么动词)? 被广泛使用的参数如何尽可能保持一致,避免不同产品的表达混乱的情况(例如分页参数用PageNumber还是PageNum)? 对于常用的场景,例如幂等、分页、异步API的设计有没有统一的规范,避免使用体验不一致? 错误码应该怎么设计?公共错误码怎么统一,业务错误码怎么表达? 上述问题都是实际研发过程中要注意的,要全部罗列的话远不止这些。API的用词描述是云服务展现给外部用户的第一印象,绝非随意写就。对人员有一定规模,内部有多条产品线的组织来说,如何协调组织的各个部分对外具有统一的体验是个很大挑战。 回顾下HTTP协议,最广为认知的是对HTTP Mehod的约定,使用9个单词就完成了对基本动作的规范,为开发者提供了清晰的思维模型: Http Method 类型图片来源:https://tools.ietf.org/html/rfc7231 同样,在HTTP Header里面也对浏览器信息、语言、网络连接属性等做了详细的规定,这样开发者在使用HTTP服务的时候都有一个大致的约定,在关键信息上面不会有偏差,保障了异构系统的接口一致性。 因此云服务在管理API时应该考虑一些具体的规范,对命名规则、标准词汇、最佳实践模式、错误码等信息都有明确的规定,同时用系统化、平台化的手段来管理API,确保不走偏。设计风格不是云服务API设计中致命的问题,但是它关乎云服务外表形象,不可不察。 挑战4:服务端容错处理 API是后端服务的外部表达,是服务就有可能出现问题,无论这个问题是可预期的还是不可预期的。如果只考虑功能本身功能特性,而忽视对异常情况的设计,当问题出现的时候业务本身可能无法感知造成服务异常,更重要的是站在客户角度去看,不能有效获取错误原因是非常痛苦的,很多时候只能束手无策,降低云服务提供商的整体口碑,甚至损害营收。 假设有个创建资源的API,每调用一次都会创建新的资源,考虑以下情况: 同样的请求多次提交,是否会重复创建资源? 请求处理时间过长,客户端是长时间等待,还是先异步返回一个任务ID? 如果需要等待,Timeout最大值是多少? 如果Timeout最大值达到,客户端的策略是重试还是放弃 如果最终处理还是失败了,具体是哪个环节的问题?如何给出准确的错误信息? 如果异步方式,异步处理完成后是主动查询还是另有通知? 第三方工具和集成商到哪里去获取这些信息?能不能有标准化的处理? 实践中,如果不做好问题a的处理,可能会造成系统异常情况下大批资源被重复创建,有可能造成用户或云服务商资损;问题b-e没有处理好,可能会让用户陷入盲目等待或者盲目重试,使用体验和效率极差;而f-g关系到自动化处理工具如何做到效率最大化,也关系到被集成的效率。 一个异步重试的状态机 当出现异常的时候,API一般是要靠错误码来告知用户有什么问题。HTTP协议本身对错误码做出了详尽的规定,Restful的API要尽可能地符合标准。除此之外,云服务有必要在此基础上进一步提供业务错误码和错误信息,来描述错误具体的细节。 当前云服务的错误码很多,看起来非常专业,但问题主要集中在以下几个方面: 1.错误类型过多:错误码越多客户端处理起来越方便吗?看一下HTTP的错误码,只有5大类加几十个子类的错误码就涵盖了所有场景,而通常大家耳熟能详的无非是200、400、404、500、502等屈指可数的状态码。有的人考虑错误码越多,客户端可以switch/case一下针对每个错误码做不同的操作,这个就见仁见智了,一般的程序员可能不会写那么多的分支流程,必要性也不大。 2.错误信息表达不够充分:相比于提供大量的错误码,错误信息的明确表达更为重要。如果错误信息不够详细,作为用户不了解细节无法掌控的云服务,几乎是无法明确发生了什么,要么提工单,要么只能被动等待,客户体验很差。 3.业务错误码与HTTP错误码含义不匹配:例如参数错误应该返回4xx系列Code,返回5xx系列就不够专业。即便是RPC风格的API,也要大致符合HTTP规则,否则可能会给一些依赖HTTP Code的系统造成误导。网上有种思路觉得无论后台响应如何,HTTP Code统统返回200,在Body里面的错误信息体现异常信息,这种不利于对接口的监控,因为监控系统很难通过识别消息体来鉴别功能是否正常响应。 4.相同错误不同云产品表达不一致:这会给客户端开发造成困扰,增加开发工作量,不利于自动化集成,用户体验比较差。 5.错误码应该是可枚举集合:一个API能够产生的错误码类型应该是可预期的,即便是业务升级,也应该给客户提供明确的错误码列表,不能随心所欲。因为用户端需要明确知道可能会发生什么,而不是随时可能出现不可预知的错误类型。如果错误类型不确定,就意味着针对错误码分支处理基本是无效的。 要做好服务端容错上述问题,需要从云服务整体层面加强API的容错设计,做好错误码规范,加强对错误信息的管理,来提升用户体验。 挑战5:版本管理 API都是不断迭代的,通常都需要版本管理。云服务API的版本管理尤其重要,主要是以下原因: 云服务API直接面向用户,由于调用量大,变更影响的用户范围也更广,版本变更要非常谨慎; 云服务有多种形态,主要是公有云、私有云、细分的行业云等,不同的云对同样功能API的要求可能不一样,API更加多样化。既要保障不同版本功能正常,又要能快速部署维护不同版本,挑战很大; 不兼容变更的推广极其困难,很难让用户在短时间内快速升级,维护多版本API成本很高; 必须变更的情况,要确保调用方能够及时感知并且平滑切换的技术难度很大。 针对API各种场景的管理,需要一套成熟的API管理平台,照顾到各种场景的需求,也是一项不小的挑战。 挑战6:API该如何开放 API如何开放看起来是奇怪的问题,难道API做出来不就是开放给别人用的吗?做好就开放就开放啊?但在云服务场景下,情况会更复杂一点! 产品技术都是在不断迭代的,功能始终在增加,新的API层出不穷。从客户视角来看,他们对已开发完毕的API是否开放的需求是什么?假设一个云产品有100个功能特性,20个只能保留在内部,官网上总共提供了80个特性,而公开API只开放了50个,这是用户期望的状态吗?理想状态应该是什么? 围绕云服务有一个庞大的生态,除了普通的开发者,还有许多第三方服务商、企业客户、开源工具。当我们考虑所有这些生态成员的需求时会发现:云服务自身拥有的功能集合与客户能使用的功能集合之间的差异比较能体现产品的开放程度。这里的差异强调的是云服务已经拥有的,不包括云服务自身没有的服务。客户能使用的功能与云服务能提供的功能之间的差距越大,说明云产品的开放程度越低,因为很多功能都作为保留节目被雪藏了,导致客户和第三方服务商无法享受与云厂商接近的服务,最终生态无法拓展。理想状况是,云服务商自身能通过API使用的功能,客户都能通过OpenAPI使用,内外看到的是一致的。 但具体到某个API应不应该开放,实践中会做如下考虑: API刚刚上线尚未打磨充分,贸然开放可能会留下隐患,再想调整为时已晚,所以选择先不开放。 API本身并非原子化,封装了若干业务场景,主要目的是优化性能或者服务特定的客户,并不需要开放给所有用户;而且当业务场景发生变化的时候,调整起来也比较困难,不适合开放出去。 某些API不适合开放给全部用户,只能部分开放,例如特定行业专业的API。 API仅供特定场景或私有场景使用,需要外部能够访问,但是不能开放给用户。 针对这些问题,需要在API的管理机制上面下功夫,能够区分不同的场景,并做好元数据的管理。针对API是否开放要有明确的规范和度量机制,确保该开放的API都可以开放,确保内外客户看到的功能集合基本一致,才有利于云服务更好地被集成,在客户的业务中发挥更大的作用。 挑战7:API体系如何打通 在云服务场景中,API并非孤立存在: 首先,API发布以后,用户要想顺利地使用API,配套设施必不可少,SDK、文档、工具链的集成都需要考虑到,这里的重点是如何保障准确性、及时性和一致性。开发工程师一般都不太喜欢写文档,专业写文档的又可能不太懂技术,再考虑到国际化的问题,就十分有挑战了。SDK方面,一个API要有多种语言的实现,每种语言还要保障其专业性、可用性,非常考验对开发人员编程语言掌握的深度和对API的理解,业界经常采用的自动化生成SDK的方式也会考验对多语言的兼容能力。工具链比如阿里云的 API Explorer、CloudShell等产品也需要及时与API的最新状态保持同步。 其次,云服务由于产品线众多,如何让用户能够快速学习使用API和相关工具,需要在教程、案例、运行时环境等诸多方面加强建设。围绕云服务,已经发展出许多上层生态工具,例如terraform/ansible/spinnaker等开源软件,它们能够帮助云服务更好地使用起来,必须对它们提供支持,如何能够快速覆盖也对平台开发能力是个考验。 另外,API本身的质量保障也是非常重要的。一般都要考虑性能、稳定性、安全等方面的保障体系,通过压测、监控、部署防护软件等方式来确保API在服务的时候不会掉链子。传统的套路在解决系统问题时非常有效,但具体到业务问题的时候就无能为力了。例如,一个创建服务器的API一般来说都是要求幂等的,怎么检测该API实际上有没有做到幂等呢?推而广之,其他业务流程的正确性又如何保障呢?等API开放了发现问题再修复就为时已晚,显然应该在上线前通过测试来发现这类问题。但是随着业务的发展,如何能保障这类问题可以有统一的解决方案,能够长期跟进及时发现风险避免损失呢? 阿里云API体系简易图 所谓量变引起质变,上述问题针对单个API的时候都好解决,但是当API规模达到成千上万的时候,就必须通过平台化、系统化的手段来解决了。例如,API服务可靠性SLA指标如果要达到4个9,需要制定明确的标准,并且有手段监控到所有API的运行结果,通过分析成功率来判断其是否达到预期水平,这对云服务本身的底层系统建设提出了较高的要求。 所以,以API为中心完善相关体系,保障用户体验的一致性、及时性、稳定性、易用性是非常有挑战的。 参考文献:https://developer.mozilla.org/en-US/docs/Web/HTTP/Messageshttps://tools.ietf.org/html/rfc7231#section-4https://www.cnblogs.com/sparkdev/p/10052310.htmlhttps://www.grpc.io/docs/guides/https://www.terraform.io/docs/index.htmlhttp://www.grabsun.com/article/2015/1135807.html 原文发布时间为:2019-10-15作者:虚明本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:好的开始是成功的一半!工作中,目标的设置是最不能马虎的事情。今天,我们请来孙阳(阿里巴巴测试开发专家),他从11年入职至今已有8年。在测试技术目标的KPI设置上,他有一些想法要与你分享。 常见误区 误区一:我能做什么,就做什么? 我们在讲测试能力建设的时候,往往会说我们有什么样的问题,所以要建什么样的测试能力,要做到什么样之类。这里经常能看到大家在设置目标的时候,思考路径往往是“我能做什么,我要怎么做”。 比如,海外没有真机环境,所以我们需要建设海外真机环境。接下去就会想到我们在海外有办公室,所以上半年的目标就是在海外办公室部署真机环境,技术方案上可以采用某个方案。 看上去没有什么问题,但是如果你仔细想一想,就会发现这个目标其实禁不起推敲。上半年建了海外环境,那下半年要建什么?这当中对国家选择的标准是什么?对真机环境建设的成本、稳定性、速度、可移植性等要怎么衡量?如果只是觉得业务上需要海外环境,我也能做海外环境,就去做了,那在方案选择上可能就会存在局限性,比如当前的方案就很依赖办公网的建设,其实不利于在世界范围内复制。 这种定目标的方式,很容易变成:今年没有能力,我建了某个能力,明年我发现这个能力不完善,然后我又做了优化一二三。这样做规划, 没有体系,缺乏前瞻性,也看不到终局。 误区二:拿手段当目标 有的同学说,我要做自动化调度中心,整合各种自动化平台,解决自动化调度问题,统一所有自动化测试件的执行。上半年的目标就是把平台重构,接入测试件类型一二三四。 这就是典型的拿手段当目标了,我们做自动化调度中心,这是一个手段,而不是目标。 目标是要结合业务测试的痛点问题来的,比如,目前自动化能力分散,在持续集成能力落地时,自动化能力集成成本高。那这时候我建立调动中心的目的就变成了降低持续集成自动化集成成本,然后就可以对这个成本进行度量,以描述我达到的阶段。同时,对平台集成能力也可以进行度量,建立你的子目标:比如可扩展性、稳定性等等。 技术目标设置的思考路径 在说思考路径之前,我想先分析一下,一般的技术目标有哪几种类型。 在质量团队来说,一般我们的技术目标会有两种,一种是做原子能力,解决某一类问题,比如UI自动化、接口自动化、doom等等;二是建一个整合平台,做原子能力的调度,打整体效果。 那么接下来我就以活动质量中心的目标设置来谈一下我的思考路径。 平台整合型 我们在说到平台整合的时候,往往给自己找的价值点都是:降低工具使用成本、沉淀保障策略之类,要降低工具使用成本,其实一个门户网站做个导航可能就能解决问题;而沉淀保障策略,一个文档也能解决问题。所以如果你把价值定位在这两个方面,最多就是量变,很难做出质变。其核心问题是: 没有对业务的测试工作产生改变 ,也就是目标和价值没有想清楚。 | 定义目标 我认为,做平台能力整合,想要做出质变, 一定是要对工作模式发生一些变化的 。你把一堆能力整合到一起,肯定是希望这些能力能够在某一件事情上产生合力,而这个合力所产生的效果,我认为就是能够对这件事情的模式,产生一些变化。 比如,aone(研发工具平台)整合了一堆能力,改变了研发的工作模式。 那么要怎么去思考这样的变化呢?我觉得有以下几个步骤: 1)先从问题出发,聚焦到一个域,定义出当前工作模式的一个状态,即:当下的阶段。 2)把目光放长远,从三年后往回看,你期望的工作模式是什么?即:愿景的阶段。 3)一年内我期望给工作带来的变化是什么?即:短期目标。 按照这个三个步骤去拆解问题,把终局和阶段想清楚,就能把平台的工作的价值整明白了。 | 能力建设 在定义清楚当前阶段、愿景和目标之后,我们就需要来看Action是什么了,也即是说,为了实现我的目标和愿景,我需要做的事情有哪些。 这时候我就可以根据目标进行拆解,明确哪些是平台架构需要完成的,哪些是原子能力需要提升的,然后看当前的短板在哪里,重点投入兵力建设。 | 举个例子 以活动质量中心为例,我们先来灵魂拷问: 当下我们对会场的质量保障模式是:S级(重大项目)大促半自动化、日常活动全裸奔; 如果往后做三年,我希望给这件带来的变化是:以场景化的方式、针对不同活动,提供个性化的全自动化保障策略; 一年内我能达到的台阶:所有营销类活动全自动化保障。 在定义清楚目标之后,就要来看达成目标我需要建设的能力了。 比如,我要在今年实现所有营销类活动全自动化保障,我就需要做这几件事情: 1)建立活动质量中心,和研发活动一体化系统对接,实现关键节点(选品、搭建)环境的自动化检测和流程卡口。 2)针对会场特色,把核心原子能力做厚。 而往三年目标走的话,我还需要:沉淀保障能力模板,针对不同类型活动提供场景化解决方案(比如3C和大工业);活动覆盖面拓展,系统对接导购系统等。那这些的优先级在本财年就会被降低。 平台能力整合型的,我认为大概就是这个思路了,下面再来看看原子能力型的。 原子能力型 所谓原子能力,一定是解决某一类型问题的,比如会场UI自动化,解决的就是会场UI层质量问题。 针对原子能力的KPI,我认为其思考路径应该是这样的:先定义问题,再定义目标,然后思考需要能力项,再根据指标思考方案和实施路径。 | 定义问题 明确当前的问题是什么,这里对问题的分析需要更加全面,要有抽象能力。比如当你遇到了一个单点的问题,可以思考这个问题是否具备通用性,可能一个个例问题背后,是一个普遍的现象。而越是抽象的问题,解决的难度越大,而价值也越大。 以活动会场UI自动化为例,开始我想解决的是S级(重大项目)大促会场质量保障的问题,后来想想这东西一年用两次,不划算,而日常几百次的营销活动会场都是裸奔,为什么我不能把S级(重大项目)大促保障的思路复用到日常会场呢?然后再往外延展,导购活动的页面质量,能不能用同样的思路解决呢?最终,我定义的会场UI自动化能力要解决的问题就是:所有活动类型的会场UI质量保障。 | 定义目标和能力项 所谓的价值,就是要把问题解决到什么程度。问题解决得越彻底,价值越大,所以我们在解决问题的时候,姿势一定帅,不能是一个临时方案,而要有长效机制。 在明确了目标之后,就可以分析要实现这个目标,我们所需要的能力项有哪些了。 还是以活动会场UI自动化为例,作为测试能力,我认为必须能够替代手工测试。比如破图、死链、样式错乱、空楼层、空白品、重复品、无价格品等等影响用户体验的问题。所以,对于问题的覆盖面肯定有要求。 然后,运行速度要快,要给运营及时的反馈,否则做为发布卡点,体验很差。再者,对稳定性和误报率肯定有要求,假摔会导致排查成本增加,也会给运营发布的体验造成负面影响。 这样分析下来,我相信,能力项也就出来了:覆盖率、执行速度、稳定性。 | 定义指标 定义了目标和能力项,之后就需要对每一个能力项做到什么程度进行定义,这就是我们说的指标了。 比如覆盖率上,我希望脚本能发现所有问题,而所有问题又很难定义,所以,我至少希望脚本能替代手工,那我的KPI就可以定义为:脚本覆盖所有手工测试的case,实现会场内容全自动化检查。 在执行速度上,我们希望做到2分钟内执行完成。 在稳定性上,我们希望成功率达到三个9,误报率小于1‰。 当然,有时候由于缺乏历史数据积累,很多数字只能靠拍,但是拍也不是乱拍。我们可以从业务上的效果来思考,比如执行速度,我们如果希望一次S级大促所有会场在15分钟之内完成测试,那么根据会场数量、执行机的数量,就可以反推出单个case的运行时长,而这个就是我们能力项的指标了。 | 方案和实施路径选择 好了,根据以上的分析,我们已经得到了能力建设的目标和指标,那在方案的选择和实施路径上也就会相对清晰了,这里本文就不再赘述了。 小结 在很多时候,能力整合和原子能力往往是相辅相成的,能力整合打应用场景,原子能力打解决问题的深度,只有结合起来很才能最大化价值。但是在定义KPI的时候,两者其实可以分开考虑,这样在团队资源投入上也更容易分工和形成合力。 另外,思考价值、目标的过程是很折磨人的,但是我认为这是一个锻炼“思考力”的方式,一旦习惯了以后,就会按照这个思考路径来想问题了。 原文发布时间为:2019-10-14作者:孙阳本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:位于杭州阿里巴巴西溪园区旁边的大型商场“亲橙里”2018年正式开业。和传统的线下综合型商场不同的是,亲橙里从规划之初就定位为数字化商场,通过植入自研的IBOS平台完成建筑内的所有子系统的接入,而让建筑和建筑内的设备、空间、人的“在线”是我们数字化的第一个目标。为了实现这个目标,阿里工程师做了哪些动作,一起往下看。 建筑数字化基础——建筑IoT和边缘计算 设备数据字典 建筑楼宇内的设备纷繁复杂,包括设备种类、功能、性能、安装方式、通讯方式、通讯协议等等各个方面都不一而同,而这些设备正是建筑楼宇正常运营和管理的前提,构成了建筑楼宇自动化的基础。因此,建筑数字化的前提是建筑设备数字化,而设备数字化的前提是首先需要定义统一的数据字典。我们将建筑楼宇领域的空调、给排水、消防、安防、强电、弱电六大系统、100多种设备类型统一进行编码、分类、定义完整的数据模型。 Niagara——建筑IoT神器 上面提到建筑楼宇内的设备类型繁多,虽然有一些行业标准,但标准本身也比较复杂、且没有一个标准能接入所有或绝大多数子系统;因此、建筑IoT系统中,设备统一接入一直是个挑战。Niagara较完美地解决了这个问题。Niagara是基于Java的开放物联网解决方案,其边缘终端产品JACE支持多种通讯I/O端口、内置了大量常见的楼控系统和设备驱动,同时支持驱动插件式管理、允许用户二次开发;支持分布式部署架构、兼容包括Haystack、LonWorks、BACnet在内的多种行业标准。 Niagara的引入解决了两个痛点问题: 1)异构子系统的接入、屏蔽了大量设备协议的开发和适配;2)按照标准数据类型、统一单位、精度等,将原始设备数据标准化为Haystack格式的数据报文,便于上层系统(IBOS)处理。 IBOS边缘计算 人、设备、空间是构成建筑的三个基本要素(类比商场的人、货、场),因此人、设备、空间也是建筑领域的基础领域模型,其他模型都可以在此基础上进行构建。IB平台的边缘计算也正是围绕这三种基础领域模型来设计:IB-Connector接入设备数据的同时,会根据数据字典的定义,动态转换为人、设备、或空间模型;IB规则引擎,本质是围绕人、设备、空间模型实例之间互操作、基于事件驱动的实时计算引擎。另外,它还提供丰富的组件支持,可以按需灵活自定义模型并把数据发送到云端、供云端服务消费和使用。下图简单示例了亲橙里的水电表设备如何通过边缘计算平台把数据实时上送到云端并接入数据中心。 数据中心大图 IB数据中心,以建筑业务数字化和数字业务化为目标,对下采集建筑线上线下的各种数据,进行建筑全域统一建模和加工;向上提供PaaS化的数据服务和算法服务,并最终为业务层提供辅助决策。 下面是数据中心的架构大图,依然是分层架构。总体上自下而上可分为四层。 1)数据源,包括采集的各类线上的和线下的数据,来自IBOS、IB应用、集团DT(OneData、A+等)以及其他合作BU的数据,另外还包括外部数据等; 2)数据建模和计算,包括各种异构数据源的数据接入和清洗、针对建筑领域的全域数据的数据模型设计、实时数据/离线ETL开发; 3)数据服务中台,面向业务领域的应用层数据OLAP分析,提供高性能、通用、开放的数据服务; 4)应用,包括纯数据类的应用例如招商、选址(办公/商业场景)、运营分析、消费者洞察、客流动线、财务模型分析等,以及基础的商业、办公/产业运营、资产管理类应用中的数据统计/分析类场景。 数据模型建设 数据模型建设是数据仓库开发中的关键环节。我们在对亲橙里的所有业务系统(客流、停车、POS、CRM、多屏和导购、招商租赁、物业和能效管理等)及其关键场景进行梳理、识别出公共的业务领域、并抽象出核心的领域模型;在此基础上进行汇总分析,进行逻辑建模、包括模型之间的关系和模型内部的关键属性。下图简单示例了模型建设的整个过程。 在建模方法上,我们采用了目前业内主流的Kimball维度建模(这也是集团推荐的方式)。维度建模的特点总结了如下几点: 数据冗余小(很多具体信息都存在相应的维度表中了)结构清晰(表结构一目了然)便于做OLAP分析(这个很重要、我们有很多业务数据分析的场景和需求)有一定的扩展性:当增加新的数据分析需求时,可以较容易地扩展模型一定程度上增加使用成本,比如查询时要join多张表有时会产生数据不一致(如维度表和事实表) 当然、维度建模也不是十全十美,但确实是最适合我们场景的建模方法。 数据服务中台 亲橙里的应用场景丰富,不同的应用场景对数据有不同的需求;为了灵活高效地提供不同应用的数据接口,我们设计开发了数据接口服务;基于实时计算和离线计算加工好的汇总数据,通过提供图形化的方式、及SQL的方式来完成接口的自定义、发布以及监控。带来的好处也非常明显: 上层应用可灵活自定义所需的数据接口、不再强依赖数据开发团队; 统一数据口径、便于数据质量监控和管理; 降低了数据集市/应用层表的规模,将数据开发团队从上层应用的需求中解放出来,专心底层和中间层数据和算法开发; 完善的API管理、运维、监控、流控、自动化测试等机制,保障服务效率和质量。 对于上线的数据接口,提供接口的调用qps、rt、调用异常等监控,支持报警规则配置和推送。 核心商业场景 客流、交易、会员是线下零售场的最核心场景,也是亲橙里数字化的重点场景。 客流 客流系统是传统线下商场最基础的系统之一。类比线上电商和A+,线下商场就是站点,商铺就是页面,客流系统采集到的人次、人数就是商场/商的"PV"、"UV";通过定义活跃度模型,我们也可以统计线下场的月活、日活,并针对活跃用户进行挖掘分析。 | 业务价值 1)客流监测及预测,帮助商场和商家更合理地安排资源,更客观地评估业态吸客能力及优化决策。 2)客群分析(性别/年龄/活跃度)帮助大小B决策提供针对性的服务,提升顾客体验,提高顾客黏性及忠诚度。 3)客流数据聚合销售数据,帮助大小B更客观更精确评估人员/活动/业态的绩效。 4)顾客识别(身份识别/行为轨迹分析),帮助商场和商家更直接触达会员群体,加强互动,提高会员黏性及忠诚度。 5)客流动线及热点分析,帮助商场更准确捕捉业态冷热分布,更合理优化布局;帮助商家更大程度协同发展、更合理优化店内陈列、商品类别及人员安排,持续增强吸客能力。 | 技术方案 传统的客流系统一般只支持人次统计、并不支持去重、更不支持身份识别,同时设备本身的识别精度、安装位置和角度、光照条件、现场调校、系统维护等都会影响最后的统计精度,因此获取较高质量的客流数据是传统线下场的痛点之一。经过充分的调研、测试和验证后,我们采用了头肩识别+人脸识别的混合方案,每个商场/商铺的出入口通过两组摄像头分别抓拍头肩和人脸,除了可以统计路过、进店、离开的人次,还支持去重以及用户特征识别(年龄、性别等)和身份识别。 | 活跃度模型 根据不同周期下的访问频次,可以定义出访客活跃度和会员活跃度等级。 通过这个定义,系统可以自动给访客/会员打标,进而统计出日活、周活、月活访客/会员数,以及各活跃度访客/会员的占比。 交易 亲橙里的交易非常复杂,商家众多、交易系统不统一;同时由于亲橙里的招商初期并未约定采用统一收银方式,后期商家入驻后再推进统一POS方案也比较困难: 1)对阿里系商家如盒马、心选、小厨,以及天猫智慧门店,这类商家的交易直接走TP; 2)对大部分餐饮类、零售类、服务类企业,我们部署了口碑的POS系统; 3)剩余的商家,我们上线了销售管理系统。由商家小二后台手动录入数据,系统采集后流入数据仓库; | GMV 基于交易数据的三类指标即GMV、坪效、租售比均是我们重点关注的指标,它们可以衡量店铺的运营效率以及健康度。下图是我们对亲橙里76个商家的GMV、客流、租金指标进行汇总后生成的气泡图,业务可根据此图表,了解商家所处位置象限,以进一步进行运营及招商的调整。 基于GMV、客流、租金指标的商家气泡图 会员 会员系统是亲橙里重点打造的服务: 1)会员交易自动实时积分; 2)多方权益打通;会员免费停车,交易积分兑停车权益,趣抓、ROM、黄小鹿权益,等等; 3)自然植入的人脸交互场景、完成会员身份识别闭环;通过停车、客流、轨迹、交易、场内互动等多个场景,尝试多维度认识会员; 4)基于OneID的能力,我们将亲橙里会员和淘系会员打通;同时结合集团的线上大数据和场内的线下数据,使用户画像更完整和丰满;另外基于集团LBS数据的能力,我们可以挖掘距离商场周边3公里/5公里范围内的潜客,并结合他们在场内的到访、活动、下单等数据进行跟踪分析。 数据可视化 我们的日常数据报表,在可视化上目前选型的都是集团内的成熟产品,如有数、dataV。 同时,针对建筑本身的空间特点,我们正在规划基于2D/3D地图、CAD/BIM模型等做一些建筑数据可视化的尝试。 双11 去年双11是亲橙里第一个双11,我们也首次和集团数据技术及产品部合作,把亲橙里的实时数据对接到集团媒体大屏。 运维监控可视化 目前,我们也在和云智能基础产品事业部研发效能合作的「须弥山-态势平台」共建出了亲橙里的监控模型。 本图为mock数据本图为mock数据 原文发布时间为:2019-10-12作者:永诺本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:代码中的"坏味道",如"私欲"如"灰尘",每天都在增加,一日不去清除,便会越累越多。如果用功去清除这些"坏味道",不仅能提高自己的编码水平,也能使代码变得"精白无一毫不彻"。这里,一直从事Java研发相关工作的阿里高级地图技术工程师王超,整理了日常工作中的一些"坏味道",及清理方法,供大家参考。 让代码性能更高 需要 Map 的主键和取值时,应该迭代 entrySet() 当循环中只需要 Map 的主键时,迭代 keySet() 是正确的。但是,当需要主键和取值时,迭代 entrySet() 才是更高效的做法,比先迭代 keySet() 后再去 get 取值性能更佳。 反例: Map<String, String> map = ...; for (String key : map.keySet()) { String value = map.get(key); ... } 正例: Map<String, String> map = ...; for (Map.Entry<String, String> entry : map.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); ... } 应该使用Collection.isEmpty()检测空 使用 Collection.size() 来检测空逻辑上没有问题,但是使用 Collection.isEmpty()使得代码更易读,并且可以获得更好的性能。任何 Collection.isEmpty() 实现的时间复杂度都是 O(1) ,但是某些 Collection.size() 实现的时间复杂度可能是 O(n) 。 反例: if (collection.size() == 0) { ... } 正例: if (collection.isEmpty()) { ... } 如果需要还需要检测 null ,可采用CollectionUtils.isEmpty(collection)和CollectionUtils.isNotEmpty(collection)。 不要把集合对象传给自己 此外,由于某些方法要求参数在执行期间保持不变,因此将集合传递给自身可能会导致异常行为。 反例: List<String> list = new ArrayList<>(); list.add("Hello"); list.add("World"); if (list.containsAll(list)) { // 无意义,总是返回true ... } list.removeAll(list); // 性能差, 直接使用clear() 集合初始化尽量指定大小 java 的集合类用起来十分方便,但是看源码可知,集合也是有大小限制的。每次扩容的时间复杂度很有可能是 O(n) ,所以尽量指定可预知的集合大小,能减少集合的扩容次数。 反例: int[] arr = new int[]{1, 2, 3}; List<Integer> list = new ArrayList<>(); for (int i : arr) { list.add(i); } 正例: int[] arr = new int[]{1, 2, 3}; List<Integer> list = new ArrayList<>(arr.length); for (int i : arr) { list.add(i); } 字符串拼接使用 StringBuilder 一般的字符串拼接在编译期 java 会进行优化,但是在循环中字符串拼接, java 编译期无法做到优化,所以需要使用 StringBuilder 进行替换。 反例: String s = ""; for (int i = 0; i < 10; i++) { s += i; } 正例: String a = "a"; String b = "b"; String c = "c"; String s = a + b + c; // 没问题,java编译器会进行优化 StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10; i++) { sb.append(i); // 循环中,java编译器无法进行优化,所以要手动使用StringBuilder } List 的随机访问 大家都知道数组和链表的区别:数组的随机访问效率更高。当调用方法获取到 List 后,如果想随机访问其中的数据,并不知道该数组内部实现是链表还是数组,怎么办呢?可以判断它是否实现 RandomAccess 接口。 正例: // 调用别人的服务获取到list List<Integer> list = otherService.getList(); if (list instanceof RandomAccess) { // 内部数组实现,可以随机访问 System.out.println(list.get(list.size() - 1)); } else { // 内部可能是链表实现,随机访问效率低 } 频繁调用 Collection.contains 方法请使用 Set 在 java 集合类库中,List 的 contains 方法普遍时间复杂度是 O(n) ,如果在代码中需要频繁调用 contains 方法查找数据,可以先将 list 转换成 HashSet 实现,将 O(n) 的时间复杂度降为 O(1) 。 反例: ArrayList<Integer> list = otherService.getList(); for (int i = 0; i <= Integer.MAX_VALUE; i++) { // 时间复杂度O(n) list.contains(i); } 正例: ArrayList<Integer> list = otherService.getList(); Set<Integer> set = new HashSet(list); for (int i = 0; i <= Integer.MAX_VALUE; i++) { // 时间复杂度O(1) set.contains(i); } 让代码更优雅 长整型常量后添加大写 L 在使用长整型常量值时,后面需要添加 L ,必须是大写的 L ,不能是小写的 l ,小写 l 容易跟数字 1 混淆而造成误解。 反例: long value = 1l; long max = Math.max(1L, 5); 正例: long value = 1L; long max = Math.max(1L, 5L); 不要使用魔法值 当你编写一段代码时,使用魔法值可能看起来很明确,但在调试时它们却不显得那么明确了。这就是为什么需要把魔法值定义为可读取常量的原因。但是,-1、0 和 1不被视为魔法值。 反例: for (int i = 0; i < 100; i++){ ... } if (a == 100) { ... } 正例: private static final int MAX_COUNT = 100; for (int i = 0; i < MAX_COUNT; i++){ ... } if (count == MAX_COUNT) { ... } 不要使用集合实现来赋值静态成员变量 对于集合类型的静态成员变量,不要使用集合实现来赋值,应该使用静态代码块赋值。 反例: private static Map<String, Integer> map = new HashMap<String, Integer>() { { put("a", 1); put("b", 2); } }; private static List<String> list = new ArrayList<String>() { { add("a"); add("b"); } }; 正例: private static Map<String, Integer> map = new HashMap<>(); static { map.put("a", 1); map.put("b", 2); }; private static List<String> list = new ArrayList<>(); static { list.add("a"); list.add("b"); }; 建议使用 try-with-resources 语句 Java 7 中引入了 try-with-resources 语句,该语句能保证将相关资源关闭,优于原来的 try-catch-finally 语句,并且使程序代码更安全更简洁。 反例: private void handle(String fileName) { BufferedReader reader = null; try { String line; reader = new BufferedReader(new FileReader(fileName)); while ((line = reader.readLine()) != null) { ... } } catch (Exception e) { ... } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { ... } } } } 正例: private void handle(String fileName) { try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) { String line; while ((line = reader.readLine()) != null) { ... } } catch (Exception e) { ... } } 删除未使用的私有方法和字段 删除未使用的私有方法和字段,使代码更简洁更易维护。若有需要再使用,可以从历史提交中找回。 反例: public class DoubleDemo1 { private int unusedField = 100; private void unusedMethod() { ... } public int sum(int a, int b) { return a + b; } } 正例: public class DoubleDemo1 { public int sum(int a, int b) { return a + b; } } 删除未使用的局部变量 删除未使用的局部变量,使代码更简洁更易维护。 反例: public int sum(int a, int b) { int c = 100; return a + b; } 正例: public int sum(int a, int b) { return a + b; } 删除未使用的方法参数 未使用的方法参数具有误导性,删除未使用的方法参数,使代码更简洁更易维护。但是,由于重写方法是基于父类或接口的方法定义,即便有未使用的方法参数,也是不能删除的。 反例: public int sum(int a, int b, int c) { return a + b; } 正例: public int sum(int a, int b) { return a + b; } 删除表达式的多余括号 对应表达式中的多余括号,有人认为有助于代码阅读,也有人认为完全没有必要。对于一个熟悉 Java 语法的人来说,表达式中的多余括号反而会让代码显得更繁琐。 反例: return (x); return (x + 2); int x = (y * 3) + 1; int m = (n * 4 + 2); 正例: return x; return x + 2; int x = y * 3 + 1; int m = n * 4 + 2; 工具类应该屏蔽构造函数 工具类是一堆静态字段和函数的集合,不应该被实例化。但是,Java 为每个没有明确定义构造函数的类添加了一个隐式公有构造函数。所以,为了避免 java "小白"使用有误,应该显式定义私有构造函数来屏蔽这个隐式公有构造函数。 反例: public class MathUtils { public static final double PI = 3.1415926D; public static int sum(int a, int b) { return a + b; } } 正例: public class MathUtils { public static final double PI = 3.1415926D; private MathUtils() {} public static int sum(int a, int b) { return a + b; } } 删除多余的异常捕获并抛出 用 catch 语句捕获异常后,什么也不进行处理,就让异常重新抛出,这跟不捕获异常的效果一样,可以删除这块代码或添加别的处理。 反例: private static String readFile(String fileName) throws IOException { try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) { String line; StringBuilder builder = new StringBuilder(); while ((line = reader.readLine()) != null) { builder.append(line); } return builder.toString(); } catch (Exception e) { throw e; } } 正例: private static String readFile(String fileName) throws IOException { try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) { String line; StringBuilder builder = new StringBuilder(); while ((line = reader.readLine()) != null) { builder.append(line); } return builder.toString(); } } 公有静态常量应该通过类访问 虽然通过类的实例访问公有静态常量是允许的,但是容易让人它误认为每个类的实例都有一个公有静态常量。所以,公有静态常量应该直接通过类访问。 反例: public class User { public static final String CONST_NAME = "name"; ... } User user = new User(); String nameKey = user.CONST_NAME; 正例: public class User { public static final String CONST_NAME = "name"; ... } String nameKey = User.CONST_NAME; 不要用NullPointerException判断空 空指针异常应该用代码规避(比如检测不为空),而不是用捕获异常的方式处理。 反例: public String getUserName(User user) { try { return user.getName(); } catch (NullPointerException e) { return null; } } 正例: public String getUserName(User user) { if (Objects.isNull(user)) { return null; } return user.getName(); } 使用String.valueOf(value)代替""+value 当要把其它对象或类型转化为字符串时,使用 String.valueOf(value) 比""+value 的效率更高。 反例: int i = 1; String s = "" + i; 正例: int i = 1; String s = String.valueOf(i); 过时代码添加 @Deprecated 注解 当一段代码过时,但为了兼容又无法直接删除,不希望以后有人再使用它时,可以添加 @Deprecated 注解进行标记。在文档注释中添加 @deprecated 来进行解释,并提供可替代方案。 正例: /** * 保存 * * @deprecated 此方法效率较低,请使用{@link newSave()}方法替换它 */ @Deprecated public void save(){ // do something } 让代码远离 bug 禁止使用构造方法 BigDecimal(double) BigDecimal(double) 存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。 反例: BigDecimal value = new BigDecimal(0.1D); // 0.100000000000000005551115... 正例: BigDecimal value = BigDecimal.valueOf(0.1D);; // 0.1 返回空数组和空集合而不是 null 返回 null ,需要调用方强制检测 null ,否则就会抛出空指针异常。返回空数组或空集合,有效地避免了调用方因为未检测 null 而抛出空指针异常,还可以删除调用方检测 null 的语句使代码更简洁。 反例: public static Result[] getResults() { return null; } public static List<Result> getResultList() { return null; } public static Map<String, Result> getResultMap() { return null; } public static void main(String[] args) { Result[] results = getResults(); if (results != null) { for (Result result : results) { ... } } List<Result> resultList = getResultList(); if (resultList != null) { for (Result result : resultList) { ... } } Map<String, Result> resultMap = getResultMap(); if (resultMap != null) { for (Map.Entry<String, Result> resultEntry : resultMap) { ... } } } 正例: public static Result[] getResults() { return new Result[0]; } public static List<Result> getResultList() { return Collections.emptyList(); } public static Map<String, Result> getResultMap() { return Collections.emptyMap(); } public static void main(String[] args) { Result[] results = getResults(); for (Result result : results) { ... } List<Result> resultList = getResultList(); for (Result result : resultList) { ... } Map<String, Result> resultMap = getResultMap(); for (Map.Entry<String, Result> resultEntry : resultMap) { ... } } 优先使用常量或确定值来调用 equals 方法 对象的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals 方法。当然,使用 java.util.Objects.equals() 方法是最佳实践。 反例: public void isFinished(OrderStatus status) { return status.equals(OrderStatus.FINISHED); // 可能抛空指针异常 } 正例: public void isFinished(OrderStatus status) { return OrderStatus.FINISHED.equals(status); } public void isFinished(OrderStatus status) { return Objects.equals(status, OrderStatus.FINISHED); } 枚举的属性字段必须是私有不可变 枚举通常被当做常量使用,如果枚举中存在公共属性字段或设置字段方法,那么这些枚举常量的属性很容易被修改。理想情况下,枚举中的属性字段是私有的,并在私有构造函数中赋值,没有对应的 Setter 方法,最好加上 final 修饰符。 反例: public enum UserStatus { DISABLED(0, "禁用"), ENABLED(1, "启用"); public int value; private String description; private UserStatus(int value, String description) { this.value = value; this.description = description; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } 正例: public enum UserStatus { DISABLED(0, "禁用"), ENABLED(1, "启用"); private final int value; private final String description; private UserStatus(int value, String description) { this.value = value; this.description = description; } public int getValue() { return value; } public String getDescription() { return description; } } 小心String.split(String regex) 字符串 String 的 split 方法,传入的分隔字符串是正则表达式!部分关键字(比如.[]()| 等)需要转义。 反例: "a.ab.abc".split("."); // 结果为[] "a|ab|abc".split("|"); // 结果为["a", "|", "a", "b", "|", "a", "b", "c"] 正例: "a.ab.abc".split("\\."); // 结果为["a", "ab", "abc"] "a|ab|abc".split("\\|"); // 结果为["a", "ab", "abc"] 总结 这篇文章,可以说是从事 Java 开发的经验总结,分享出来以供大家参考。希望能帮大家避免踩坑,让代码更加高效优雅。 原文发布时间为:2019-10-11作者:王超本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
9月25日-27日,2019杭州云栖大会圆满成功。全球6.7万开发者现场参会,超1250万人在线观看,3万平方米科技展区,200位+世界级科学家,1000个+技术Topic。今天,超全大会PPT一次送出,供大家下载学习! 数智商业专场 数据+算法定义新世界数智化运营中的数据中台构建解析卓诗尼破局”数智“转型大数据咨询方法论白皮书 & 360°数据管家数智化时代下购物中心运营模式新探索圆桌对话-Mall的数字化运营落地路径 基于图神经网络认知的智能计算专场 超大规模图神经网络实践AliGraph- 大规模图神经网络系统架构剖析图模型在阿里安全中的实践Graph Neural Networks- Combing Deep Learning & Symbolic Reasoning关于网络嵌入和图卷积神经网络的一些思考基于视觉推理的视频理解 阿里云智能弹性计算技术专场 阿里巴巴神龙(X-Dragon)架构演进之路万物互联时代的虚拟化技术云上服务:超越硬件的稳定性 计算机视觉新探索专场 计算机视觉新探索低成本自动三维室内重建系统视频内容理解的研究与实践深度学习在线下场景的探索与实践-云栖大会MPEG和AVS视频编码标准最新动态圆桌会人工智能赋能升级线下零售图像搜索与识别在大规模场景中的研究与实践OCR:图文结构理解探索和实践 elasticsearch开发者生态专场 阿里云Elasticsearch2.0发布 全面上云-企业敏捷创新之道专场 容器上云的守与攻 互联网中间件专场 互联网中间件的演进阿里巴巴在线应用的Serverless架构演进运营商如何实现大型企业大型企业应用上用云中间件助力云途时代应用上云千万级用户-亿级请求的平台架构演变阿里云链路追踪服务全面支持SkyWalking 开放智能化网络专场 混合云网络2.0:云网一体 助力企业平滑上云云上应用生态全栈IPv6演进云网络开放生态集成智能接入网关APP:优化企业级移动办公网络云网络发展历程和未来展望 下一代云数据分析 下一代云数据分析展望和产品重磅发布AnalyticDB-快数据时代的实时数据仓库技术内幕分析型数据库标准发展与行业观察TSDB-云边一体化时序时空数据库技术揭秘全新的Greenplum 6.0内核优化解读和7.0展望利用ADB打造游戏行业新一代实时数据运营平台Tableau带来实时数仓产品全新的洞察能力基于数据湖的精准广告投放系统技术解密 容器专场 拐点已至,云原生引领数字化转型升级Future of CI_CD with Tekton and kubernetes谐云科技阿里云联合新品解读容器混合云趋势与展望微博在线机器学习平台的K8S实践安全容器的发展与思考 互联网企业数据智能云服务专场 数据增长时代下的数据融合实践智能网络 智能运营 迅游高速增长的数据化运营实践数据智能引爆用户增长回归互联网数据科学的本质:客户&数据 企业级数据库最佳实践专场 企业级数据库敏捷研发模式云时代,数据库容灾解决方案 企业协作与研发效能专场 业务引领的DevOps一天发布 10 次-基于元数据的持续交付方法阿里巴巴研发效能提升及业务创新实践 开发者进阶专场 在这里,找到开发者技术进阶的N种方案程序员的成长攻略技术影响力:从技术深度到产品广度的裂变中断学业为创新从技术人到CEO 高可用架构专场 面向失败设计企业应用迁云的全链路评估方案智能化压测-应用稳定性基石云上应用高可用防护体系 数字金融技术专场 SQLFlow.连接数据和AI引擎的鹊桥 开源数据库专场 开源数据库在平安的应用实践传统数据库到开源数据库迁移工具化流程传统数据库DBA到开源的技能和心理切换 SaaS加速器专场 API网关加速能力聚合与生态集成SaaS加速器——让天下没有难做的SaaS能力中心开启携手伙伴共享共赢云粒费控携手阿里云构建区域软件生态宜搭PLUS发布:SaaS加速器面向ISV的技术红利SaaS上云工具包为企业应用构筑上云之梯云栖大会_RPA机器人流程自动化云栖大会_全域集成解决方案 更多精彩,点击大赛精彩回顾即可回看云栖大会演讲视频。
阿里妹导读:搜索,推荐和广告是互联网内容提供商进行价值创造的核心业务,在阿里巴巴的电子商务交易平台上,搜索,推荐和广告业务同样具有举足轻重的意义和价值。现在,阿里推荐技术又双叒优化了,新的推荐技术,新的体验,一起来看。 一. 背景 搜索、推荐和广告看似业务形态不同,其实技术组成却是非常相通的。从推荐的视角看,搜索可以认为是一种带query相关性约束的推荐,而广告则是叠加了广告主营销意愿(价格)约束的推荐,所以推荐技术的创新对推动搜索、推荐和广告业务技术的整体发展具有基础性的作用。 从技术演进的角度,推荐算法近年来也在不断地更新换代。从限定在一个有限的历史兴趣范畴内推荐的第一代基于统计的启发式规则方法(代表算法Item-based Collaborative Filtering, Item-CF)到第二代基于内积模型的向量检索方法,推荐技术打开了候选子集检索范围的天花板。然而,向量检索方法限定了内积模型这种用户-商品偏好度量方式,无法容纳更加先进的打分模型(例如带有Attention结构的深度网络)。 为了在全库检索和效率约束的基础上进一步打开推荐技术中模型能力的天花板,此前阿里妈妈精准定向广告业务团队自主提出了新一代任意深度学习+树型全库检索推荐算法(Tree-based Deep Model,TDM),在大规模推荐问题上取得了显著的效果提升。最近,该团队针对大规模推荐问题的研究取得了最新的成果,介绍了如何通过数据驱动的方式,实现模型、索引、检索算法的联合优化。基于这一最新研究成果整理的论文,已经被NeurIPS 2019会议接收。 二. 现有体系存在的问题 如下图所示,在大规模任务中,搜索,推荐和广告的系统通常由模型,索引和检索算法三大组件组成。模型计算单个用户-商品的偏好概率,索引将所有商品有序地组织在一起,检索算法根据模型的输出在索引中召回最终的推荐结果。三者共同决定了召回质量且存在内在联系。 然而,以推荐为例,现有的推荐体系对模型索引和检索的相互联系往往没有做充分的考量。从联合调优这一视角出发,对现有的几代推荐体系的代表算法存在问题分析如下: 1.在Item-CF中,倒排索引根据Item之间某种自定义的相似度量建立,检索过程则是根据用户历史行为在倒排索引中查询候选集后排序截断,模型在排序过程中对候选集中的Item根据某种自定义的规则进行打分。在系统中,模型和检索被规则固化,没有学习调优。 2.在向量检索的模式中,系统会分别为用户和商品学习一个向量表达,其内积作为用户对商品的偏好程度的预测。检索等价于用户向量在商品向量集合中的kNN最近邻检索,在大规模问题中,可以采用近似的最近邻索引结构来加速检索。在建立向量检索推荐系统的过程中,模型训练的目标是准确的预测单个用户-商品的偏好概率,而kNN检索索引建立的目标则最小化近似误差,二者的优化方向并不一致。同时,内积形式的偏好预测表达能力有限,无法容纳更加先进的打分模型。 3.在TDM中,我们通过交替迭代优化模型和树结构再加之无参数的逐层beam search检索过程进行了模型、索引和检索联合优化上的实践和创新。然而在TDM中,模型的优化和树结构索引的学习二者的优化目标也不完全一致,这可能导致二者的优化相互牵制而导致最终整体效果次优。特别是对于树结构索引,模型训练样本的构造和检索路径的选择与树结构具有更加紧密的联系,因此其质量好坏尤为重要。 综上分析,本文针对当前大规模推荐方法中存在的问题,提出了一种统一目标下的模型、索引、检索联合优化的算法JTM(Joint Optimization of Tree-based Index and Deep Model),打破系统各模块独立优化带来的相互掣肘,使得整体推荐效能达到最优。 三. 端到端联合学习的深度树匹配推荐技术 JTM继承了TDM树结构索引+任意深度用户-商品偏好打分模型的系统框架,通过联合优化和层次化特征建模取得了大幅超越TDM的推荐精度。为了更好地理解JTM,我们首先简单了解一下TDM的原理。 3.1 深度树推荐模型TDM 推荐系统的任务是从候选集(例如,商品库)中选出用户当前偏好的一个子集。当候选集的规模很大时,如何快速有效地从全库中做推荐是一个具有挑战性的问题。TDM创造性地采用树结构作为索引结构并进一步地令用户对树上节点的偏好满足下面的近似最大堆性质: 其中 p(l)(n|u) 是用户 u 对节点 n 偏好概率的真值,α(l)是第 l 层内偏好概率分布的归一化项。这样的建模保证了第 l 层节点中偏好概率最大的 k个节点一定包含在第 l−1 层的top-k节点的子节点中。基于这一建模,TDM将推荐问题转换为树结构中自上而下的层次化的检索问题。下图给出了TDM候选子集的生成过程。 首先,候选集中的每个商品都被分配到树的一个不同叶子节点上,如图(b)所示。树上的非叶子节点可以看做是它的子节点集合的一个抽象。图(a)给出了用户对节点的偏好概率的计算过程,用户信息和待打分的节点首先被向量化为深度打分网络(例如,全连接网络,attention网络等等)的输入,网络的输出作为用户对该节点的偏好概率。在检索top-k的候选子集即top-k叶子节点的过程中,我们采用自顶向下的逐层beam search方法。在第l层中,我们只对第l−1层被选中的k个节点的子节点进行打分和排序来选择第l层的k个候选。图(b)给出了检索过程的示意。 通过采用树结构作为索引,对一个用户的偏好子集进行top1检索的时间复杂度为O(log(N)),其中 N 为全部候选集的大小。这一复杂度也与用户偏好打分模型的结构无关。同时,近似最大堆的假设将模型学习的目标转化为用户-节点偏好分布的学习,这使得TDM能够打破最近邻检索模式带来的内积形式的用户偏好打分的限制,赋能任意复杂打分模型,从而极大的提升了推荐的准确率。 3.2 JTM中的联合优化框架 从检索过程中可以看到,TDM的推荐准确率由用户偏好打分模型 M 和树索引结构 T 共同决定,且二者是相互耦合的关系。具体地,给定 n 对正样本 ,即用户 u(i)对商品c(i)感兴趣,树结构 T 决定了模型 M 需要选择哪些非叶子节点来对用户 u(i)返回商品c(i) 。在统一目标下联合优化 M 和 T 能够避免二者优化方向的冲突造成的整体结果次优。为此,在JTM中,我们在一个共同的损失函数下联合优化 M 和 T 。我们首先构造联合优化的目标函数。 记 p(π(c)|u;π)为用户u对叶子节点 π(c) 的偏好概率,其中 π(⋅) 是把候选集中的商品投射到树的叶子节点上的投影函数。π(c) 决定了候选集中的商品在树结构上的索引顺序。如果 (u,c) 是一个正样本,我们有 p(π(c)|u;π)=1。同时在近似最大堆的假设下,对 π(c) 的所有祖先节点,其偏好概率也为1,即。其中 bj(⋅) 是把一个节点投影到其在第 j 层的祖先节点的投影函数,lmax 为树 T 的层数。记 为模型 M 返回的用户 u 对节点 π(c) 的偏好概率的估计值,其中 θ 为模型的参数。给定 n 对正样本,我们希望联合优化 π 和 θ 来拟合上述的用户对树上节点的偏好分布。为此,我们希望 π 和 θ 最小化如下的全局经验损失函数: 在求解中,由于优化 π 是一个组合优化问题,很难和 θ 用基于梯度的优化算法同时优化。因此,我们提出交替优化 θ 和 π 的联合优化框架,如下图所示。优化 θ 和 π 的目标一致性促进了整个算法的收敛。事实上,如果模型学习和树学习能够同时使得损失函数下降,那么整个算法一定会收敛,因为 {L(θt,πt)} 是下界为0的单调下降序列。 在模型训练中,minθL(θ,π) 是为了求解得到每层的用户-节点偏好打分模型。得益于树结构和近似最大堆的性质,我们只需要在模型训练中拟合训练集中的用户-节点偏好分布,这使得我们可以使用任意复杂的神经网络模型,同时 minθL(θ,π) 可以用流行的算法例如SGD、Adam求解。采样策略如Noise-contrastive estimation (NCE)可以用来加速计算中的归一化项。 树结构学习是在给定模型参数的情况下求解 maxπ−L(θ,π) ,这是一个组合优化问题。事实上,给定树的形状时(为了便于表达,我们假定树的形状是完整二叉树。我们提出的优化算法可以方便地推广到多叉树的情况),maxπ−L(θ,π) 等价于寻找候选集和所有叶子节点之间的一个最优匹配,这进一步等价于一个带权二部图的最大匹配。分析过程如下: 若第k个商品ck被分配到第m个叶子节点 nm 上,即 π(ck)=nm,我们可以计算如下的权重: 其中为目标商品为 ck 的训练样本集合。把树的叶子节点和候选集作为顶点,叶子节点和候选集之间的全连接作为边,把 Lck,nm 作为 ck 和 nm 之间边的权重,我们可以构造一个带权二部图V ,如2.1节的流程图(b)所示。在这种情况下,每个可能的 π(⋅) 都是 V 的一个匹配,同时我们有 C 为所有ck的集合。因此,maxπ−L(θ,π) 等价于求解 V 的最大权匹配。 对于大规模的候选集,传统的求解最大权匹配的算法例如匈牙利算法由于复杂度太高而难以使用。即使是最简单的贪婪算法,计算和存储所有的权重的成本也是无法接受的。为解决这一问题,我们利用树结构提出了一种分段式树学习算法。相比于直接将所有商品分配到叶子节点中,我们在树中自上而下地一步步实现商品到节点的分配。记: 其中为目标商品为 ck 的训练样本集合,s 和 e 分别为一个起始和终止层。我们的分段算法,首先通过找到一个最优映射 π∗ 来最大化 ,等价于将候选集分配到第d层的所有节点上。对于一个完整二叉树来说,每个第d层的节点应该包含不超过 2lmax−d个商品。这也是一个最大权匹配问题,但由于每个商品可能的分配位置大大减少了,所以可以通过贪婪算法有效求解。然后,在保持每个商品在第 d 层的祖先节点不变的前提下,即保证∀c∈C,bd(π(c))=bd(π∗(c)) 的前提下,继续将候选集在接下来的d层中进行分配,即优化。不断重复这一过程,直至每个商品被分配到叶子节点上为止。整个算法的流程如下图所示: 3.3 层次化用户兴趣表达 本质上,JTM(和TDM)是对推荐系统中索引结构和检索方式的一种深度改造。树结构的每一层可以看做是商品不同粒度上的聚合表示,JTM通过树上自顶向下的逐层相关性检索,从粗到细地找到用户信息匹配的最佳候选子集,这也与人类视角选择偏好商品的过程相契合。通过联合考虑模型和索引结构,JTM和TDM将一个复杂的大规模推荐任务分解为若干个级联的子检索任务,在上层的检索任务中,只需要在一个更粗的粒度上做圈选,且每层圈选的候选集远小于候选集全集,其训练难度将大大缩小。可以预见,当子检索任务的解足够理想时,其级联后的检索结果将超越直接在候选集中圈选候选集的效果。 事实上,树结构本身提供了候选集的一个层次结构,因为每个非叶子节点都是其所有子节点的一个学习到的抽象,这启发我们在训练模型 M 做每层的子检索任务时对用户行为特征做最利于模型学习的精准层次建模。 具体而言,用户历史行为的每个商品都是一个ID类离散特征,在模型训练中,每个商品和树上的节点都被嵌入到一个连续特征空间并与模型同时优化。从每个非叶子节点是其子节点的抽象这一点出发,给定用户行为序列c={c1,c2,⋯,cm} ,我们提出用 cl={bl(π(c1)),bl(π(c2)),⋯,bl(π(cm))}联合目标节点以及用户的其他特征来生成模型 M 在第 l 层检索的输入。通过这种方式,用户行为过的商品在第 l 层的祖先节点被用作了用户抽象化的行为序列。这主要带来了两方面的好处: 1.层间的独立性。 传统的在每层检索中共用用户行为序列的embedding会在训练 M 作为每层的用户偏好打分模型时引入噪声,这是因为 M 在每层的训练目标是不同的。一个直接的解决办法是在每层赋予每个商品一个单独的embedding来联合训练。但是这会极大的增加参数量。我们提出的层次化用户行为特征使用对应层节点的embedding生成 M 的输入,从而在没有增加参数总量的同时实现了层间embedding学习的独立。 2.用户的精准建模。 M 在检索过程中逐层选择最终候选子集的从粗到细的抽象。我们提出的层次化用户行为特征表达抓住了这一检索过程的本质,用当前层的节点对用户行为进行了适当的抽象,从而增加了用户偏好的可预测性,减少了过粗或者过细的特征表达带来的混淆。 四. 实验效果 4.1 实验设置 我们使用了Amazon Books和UserBehavior两个大型公开数据集来进行方法的效果评估。Amazon Books是用户在Amazon上的行为记录,我们选择了其中最大的Books这一子类目。UserBehavior为阿里开源的淘宝用户行为数据集。数据集的规模如下: 在实验中,我们比较了下面几种方法: Item-CF: 基本的协同滤波算法,被广泛应用于个性化推荐任务中。 YouTube product-DNN: 应用于Youtube视频推荐的向量内积检索模型。 HSM: 层次Softmax模型,被广泛应用于NLP领域,作为归一化概率计算的一种替代. TDM: 我们此前的工作深度树匹配推荐技术。 DNN: TDM模型去掉树结构后的版本。该方法不使用索引结构,在预测时会对全量候选集进行打分,再挑选topk。由于对全量候选集打分的计算复杂度非常高,因此无法实际应用,但可以作为强baseline来进行比较。 JTM: 本文中提出的联合优化方法。同时,我们对比了JTM的两个变种版本,分别为JTM-J和JTM-H。其中,JTM-J为使用了树结构联合优化但没有采用层次化用户兴趣表达的版本;JTM-H相反,其使用了层次化用户兴趣表达,但会使用固定的初始树结构而不进行联合学习。 在所有神经网络模型中,均使用相同的三层全连接网络作为打分模型。评测方面,我们使用Precision, Recall和F-measure作为性能评测指标,定义如下: Pu 是对用户 u召回的商品集合,Gu 是用户 u感兴趣集合的真集。 4.2 比较结果 下表给出了各个方法在两个数据集上的比较结果。相比于效果最佳的baseline方法DNN(计算量太高无法应用于实际),JTM在Amazon Books和UserBehavior上的recall分别取得了45.3%和8.1%的相对提升。 DNN的性能要优于YouTube product-DNN,这反应了内积模型的局限性,只通过内积的形式构造用户对商品的偏好概率无法充分拟合用户-商品偏好分布。此外,TDM的性能不如DNN,这说明了树结构优化的必要性。 欠佳的树结构可能导致模型学习收敛到次优的结果。特别是对于Amazon Books这种稀疏的数据,树上节点的embedding无法充分学习而不具有显著的区分性导致TDM效果不显著。与之对应的是,通过应用本文提出的层次化用户兴趣表征方法,JTM-J方案在一定程度上解决了粗粒度上的数据稀疏性问题,所以对比TDM在Amazon Books数据集上取得了十分显著的提升,通过联合优化,JTM在所有数据集和评测指标上显著优于DNN全量打分的结果,且检索复杂度也要低得多。 这些结果说明,通过统一目标下联合优化的方式,JTM能够学习到更优的树结构和兴趣模型。从JTM、JTM-J、JTM-H三者的相互对比来看,可以发现不管是同一目标下的联合学习,还是层次化的用户兴趣表示,都能提升最终的推荐精准度。另外,在JTM的联合框架下,树结构学习和层次化兴趣表示叠加,起到了1+1>2的效果。 4.3 树结构学习收敛性 在基于树的推荐方法中,树结构直接影响了训练时的样本生成和预测时的检索路径。一个好的树结构,不管是对模型训练还是兴趣检索,都能发挥重要的正面作用。下图中,我们对比了JTM提出的基于统一目标的树联合学习方案,和TDM工作中使用到的基于商品embedding聚类的方案。其中,前三个图为Amazon Books数据集上的效果,后三个图为UserBehavior数据集上的效果 从实验结果中可以发现,本文提出的JTM方案,在树结构学习的逐步迭代过程中,能够稳定地收敛到一个更优的树结构。与之对比的是,基于聚类的方案,在迭代最后都会出现类似于过拟合的情况。 五. 总结 JTM给出了一种统一目标下联合优化深度树匹配模型的打分模型和树索引结构的算法框架。在树结构优化中,我们基于树型结构的特点提出了一种可用于大规模任务的分层重建算法。在模型优化和打分中,基于树上检索逐层细化候选集合的本质,我们相应的提出了对用户行为特征层次化的建模方法。 JTM继承了TDM打破内积模型的约束,可容纳任意深度打分模型的优势。此外,通过联合调优,JTM带来了显著的效果提升。JTM彻底解决了历史推荐系统架构的非最优联合问题,建立了完全数据驱动下端到端索引,模型和检索联合最优化的系统组成。进一步的,JTM的提出,是对以user-tag-doc两段式检索为基础的搜索,推荐和广告现有架构的一次重大技术革新。 之前的TDM解决方案已经基于阿里巴巴自研的深度学习平台X-DeepLearning在 Github 开源,点击获得Github下载链接,了解更多详情。 阿里妈妈信息流广告算法团队常年诚招大数据处理和机器学习算法方向的能人志士!有意者可发简历联系:zhuhan.zh@alibaba-inc.com。 原文发布时间为:2019-10-10作者:阿里妈妈技术团队本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:TPC-C是TPC组织(国际事务性能委员会)制定的关于商品销售的订单创建和订单支付等的基准测试标准,是数据库联机交易处理系统的权威基准测试标准。 蚂蚁金服自研的分布式关系数据库OceanBase获得TPC-C测试第一名后,引起了大量关注,今天,我们邀请了OceanBase的核心研发人员对本次测试做专业的技术解读。 一、OceanBase如何做TPC-C测试 有机会挑战TPC-C测试相信是所有数据库内核开发人员的梦想,但TPC-C测试标准非常复杂。由于这是国产数据库同时也是分布式数据库第一次冲击这个榜单,为了完成这次挑战,OceanBase团队前后准备时间超过一年。 前期准备 TPC-C测试首先需要找到官方唯一认证的审计员来对测试进行审计监察,他们对这次OceanBase的审计也相当重视,全世界仅有的三个审计员这次就有两个参与到测试审计工作中。 测试系统 目前市面上基本找不到一个能够开箱即用的符合TPC-C标准的测试工具。以目前各个厂商PoC环境最常遇到的benchmarksql为例,可以说只是模拟TPC-C压力模型的压测工具,连最基本的数据导入都不合规,大量的字符串生成未保证全局随机,缺乏压测阶段最基本的think time、keying time这些基本配置导致极小的数据量就能跑出很高的tpmC,最关键的是它将测试模型大大简化为工具直连DB测试,完全没有遵守TPC-C测试标准规范。 在标准定义中,测试系统可以分为RTE(Remote Terminal Emulator)和SUT两部分,但实际上从角色上看SUT可以进一步拆分为两部分:WAS(web application server)和DB Server。 这其中DB Server是每个测试厂商的数据库服务;RTE扮演的角色是测试模型中的客户终端,事务的触发、RT的统计等都在这里完成;标准明确要求每一个用户terminal都必须保持一个长连接,显然在海量Warehouse时DB是无法承受这么多连接的,WAS就是RTE和DB之间桥梁,标准定义可以使用连接池,在保证对应用透明的情况下它可以做所有请求的管理。 这三个角色中,WAS和DB是必须商业化可购买且提供支付服务的,OceanBase这次是使用了OpenResty作为WAS供应商。而RTE则一般由各个参测厂商自行根据标准实现,但所有实现代码必须经过审计的严格审计,OceanBase这次完整的实现了一整套完全合规的RTE,并且支持在大规模测试系统中部署。要知道在实际的TPC-C测试流程中,不只是对DB端能力的考验,RTE端同样存在极大的资源消耗和压力。以这次6088w tpmC测试结果看,我们一共在64台64c128G的云服务器上运行了960个RTE客户端,来模拟总计47942400个用户terminal,最后还需要基于这么多RTE统计结果进行一致性和持久化审计验证。 虽然只是测试客户端,但RTE的中同样有大量的可能导致最后测试失败的小细节,比如大家可能注意不到的所有事务都因为用了web端模拟终端后需要增加的100毫秒rt,又比如为了模拟用户终端输出显示的100毫秒延时。 测试规划 TPC-C从来都不是一个简单的测试,它很科学并没有给出强制的软硬件配置,只是给出测试规范和各种审计检查限制标准,所有数据库厂商可以根据自己的特性充分调优来拿到一个最好的性能或性价比。但这同时也对所有参测厂商提出了一个巨大的难题,如何对已有的资源进行合理规划来保证顺利完成一次对TPC-C榜单的冲击。 硬件选型,这里不仅要对数据库服务器选型,还需要对RTE以及WAS服务器选型。这之前需要先期进行大量的测试和调优,来摸出在保证性价比的前提下每个角色服务器的资源配置是多少刚好。这次OceanBase测试最大的优势就是全部使用了云化资源,我们不需要再关注最底层机房、机柜、布线这些细节,只需要通过快速的规格调整来拿到我们需要的机型。 参数选择,如何选择合适的配置参数是一个非常令人头疼的问题。举个例子,一个最典型的问题就是我们最终要跑多少个Warehouse,每个数据库服务器上承载多少Warehouse。TPC-C标准为了尽可能模拟真实业务场景,通过每个事务限定不同的think time和keying time保证了一个warehouse的数据最多能够提供12.86tpmC值,因此数据库厂商想要拿到更高的成绩就必须装载更多的warehouse,但是另一方面单机的存储空间又有预计80%(经验值)需要预留给60天增量存储。在分布式数据库架构下,为了能让每个数据库服务器跑满又能充分利用本地存储空间,让每个服务器的CPU、内存、IO能力、存储空间的资源最大化利用,我们前后调整优化了近一个月时间。 性能压测 最受关注的性能压测部分在TPC-C标准中规定了以下三个状态: Ramp-up,标准允许每个数据库进行一定时间的预热来达到稳定状态,但是ramp-up阶段的所有配置必须和最终报告配置保持一致。 Steady state,保证ACID及可串行化隔离级别前提下,数据库需要能够以最终报告tpmC值在稳定状态无任何人工干预前提下保持运行8小时以上,每隔半小时需要完成一次checkpoint。 Measurement Interval,标准规定了需要能够支持8小时稳定运行,但性能采集阶段只需要保设置2小时以上即可。这个阶段还需要保证累计tpmC波动不能超过2%,并且必须完成至少4个以上的checkpoint。 所以之前一般数据库进行性能压测一般是以下的流程,先进行一段时间的预热到达稳态,等待稳定运行一段时间并完成一个checkpoint后开始进入2小时的性能采集阶段。 而OceanBase这次是使用了TPC-C测试迄今以来最严苛的一个流程来完成这个性能测试的,我们首先使用了10分钟进行预热,然后在6088w tpmC稳态保持运行25分钟并完成一个检查点,再继续跑了完整的8小时性能压测采集阶段,总耗时超过8个半小时,中间性能最大波动不到0.5%,最终结果也让审计员异常兴奋。 整个性能测试前后,审计员还需要进行数据及事务随机分布检查,简单说来就是大量全表扫描和统计sql,最大的一条sql需要访问超过万亿行的order_line表结果,可以算是TPC-C里的“TPC-H测试”。现场审计第一次遇到这些sql时我们也大量的出现sql执行超时情况,但后续基于OceanBase2.2版本最新的并行执行框架我们还是很快搞定了这些大sql,所以要顺利完成TPC-C测试并不能只是一个偏科生,保持自身没有短板才是真正意义上的通用关系数据库,从这点上说Oracle仍是OceanBase学习的榜样。 ACID A测试,通过提交和回滚payment事务来确认数据库对原子性支持,和I测试一样,OceanBase的A测试跑了两遍,分别应对分布式事务和本地事务。 C测试,标准里的C测试一共包含12个case,前四个是必须要完成验证的,每个case其实都可以认为是一个复杂的大sql,而对于分布式数据库来说重点是需要始终保证全局一致。 I测试,标准要求TPC-C模型里5个事务除了StockLevel事务都需要满足最高的可串行化隔离级别,并构造了9个case来验证隔离性。对于分布式数据库而言,这个要求并没有那么容易实现,所幸OceanBase在2.x版本中提供了全局时间戳的支持,所以的I测试都在审计员的特别要求下跑完了全本地和全分布式两种模式的两轮测试,这也应该是TPC-C测试中首次有数据库厂商需要做两轮I测试跑18个case的,也许在不久后TPC-C标准定义也会因为OceanBase的这次测试而带来针对分布式数据库的相关更新。 D测试,OceanBase在这个场景其实相对传统数据库是有较大天生优势的,OceanBase每个Warehouse数据有两份数据三份日志,通过paxos强同步,保证RPO=0的前提下只有秒级RTO。 面对D测试标准中最严格的一项-部分存储介质永久故障,OceanBase还使用了最严苛的测试场景,在使用超出标准要求的全量6000W tpmC压力下,我们强行销毁了一个云服务器节点,整体tpmC在两分钟内恢复到6000w并持续跑到测试时间结束,这些表现都是远远超过TPC-C规范要求的,相比较而言其它传统数据库基本面对有日志的存储介质故障D测试场景都是依赖磁盘RAID来恢复,OceanBase应该算是首个没有raid完全依赖数据库自身恢复机制完成全部D测试的数据库厂商了。 同时我们在D测试中是连续杀掉了两台服务器节点,首先杀掉一个数据节点,等待tpmC恢复且稳定5分钟后,再次杀掉了rootserver leader节点,tpmC仍然快速恢复。 二、TPC-C基准测试之SQL优化 对TPC-C有所了解人都知道,TPC-C是一个典型的OLTP (On-Line Transaction Processing) 场景测试,考察的是数据库在高并发压力场景下的事务处理能力,最终的性能指标以tpmC(transaction per minute,也即每分钟系统处理TPC-C模型中的new order事务的数量)和平均到每tpmC的系统成本作为衡量标准。在OLTP场景中,每条请求的响应时间都是极短的。因此,各个数据库厂商在进行TPC-C测试时,都会尽一切可能将每一个操作时间压缩到最短,不夸张的说,在TPC-C的测试中,一些关键操作的优化往往需要细化到CPU指令级。 在进入我们的主题前,我们先来谈谈TPC-C中的事务模型,主要分为五种事务,订单创建、订单支付、订单查询、订单发货以及库存查询,这五种事务按照一定的比例发生,测试最终衡量的是每分钟订单创建事务的执行个数。大家知道,每一个数据库的事务,其实就是由一定逻辑关系关联的若干条SQL语句组成,他们在一个事务中,要么全部成功,要么全部失败,这个在数据库中称为“原子性”,也就是ACID中的“A”。那么TPC-C中的一个事务的耗时大约是多久呢?看一下报告就很清楚了——只有十几个毫秒。考虑到一个事务由多条SQL构成,那么每一条SQL的平均耗时都不到1毫秒! 在C/S(client-server)模型中,一条SQL语句从发起到执行完成需要经历从客户端输入、网络传输、SQL优化、执行、结果返回到客户端这样一个流程。而具体每一条SQL的执行可能只是做一个字段的更新,所需要的执行时间是非常短暂的,从整个链路的角度来看,大量的时间会花费在与客户端的交互过程中,造成资源的浪费和耗时的增加。那么如何解决这个问题的呢?答案就是使用存储过程。 存储过程 所谓“存储过程”就是数据库为用户提供的一种面向过程的编程语言。基于这种语言,用户可以将应用程序的逻辑封装为一个可调用的过程(procedure)存放在数据库中并随时进行调用。通过这种方式,用户可以将本来需要与数据库进行多次交互才能完成的工作通过一次交互完成,省去了中间网络的传输和等待时间(参见图1)。假如一条事务的网络开销平均是30%,也就是说30%的CPU都花在了网络的收发和解析上。那么在6千万规模tpmC测试中节省下来30%的CPU资源换算成系统处理能力是惊人的。使用存储过程还可以带来事务响应时间的下降,导致数据库内核中事务锁的临界区缩短,间接的提升了系统CPU利用率,整个吞吐量也随之提高。存储过程在缩短应用端的等待耗时上同样有很大作用。 图1 传统的C/S模型与使用存储过程的执行方式对比 在TPC-C中,存储过程对于整个系统的执行效率提升是至关重要的。OceanBase 的2.2版本不仅全面支持了存储过程,而且对存储过程的执行效率做了大量极致的优化。 编译执行 存储过程作为一种面向过程的高级语言,需要转换成机器码才能够执行。这个过程一般可以分为“编译执行”和“解释执行”两种,一般来说,编译执行相比解释执行有代码优化充分、执行效率高等特点。OceanBase利用近两年逐渐成熟的LLVM编译器框架实现了一个支持存储过程的编译器,通过动态编译(Just-in-Time Compilation)的方式将存储过程翻译成高效的二进制可执行代码,在执行效率上获得了数量级的提升。同时,过程中LLVM框架将存储过程转换为与机器无关的中间代码,使得存储过程也自然而然地获得了跨平台的编译执行能力,LLVM内置的优化过程确保我们在各种不同的硬件平台上都可以获得正确、高效的可执行代码。 Array Binding 另外一个在TPC-C测试中发挥了重要作用的功能就是对DML语句进行批量处理的能力,在Oracle中该功能也称为“Array Binding”。一条SQL在数据库中的执行过程大致上可以分为“计划生成”和“执行”两个阶段。尽管我们对SQL的执行计划做了高速缓存,但找到一个合适的执行计划在整个执行过程中仍然是比较耗时的一个部分。那有没有办法省去这个时间呢?当一组SQL的执行计划完全一样而只有执行参数不同时,在存储过程中我们可以通过特定的语法将他们的执行做成一个批量处理的过程,此时“计划生成”只需要做一次即可,这就是所谓的“Array Binding”。 在Array Binding中,数据库会首先找到需要使用的计划,然后执行该计划,并在每次执行完毕后,重新执行参数绑定(binding)的过程。打个比方,这就像是在一个C语言的for循环中,反复赋值而不是重新定义一个数据结构。Array Binding的使用受用户控制,需要在存储过程中使用FORALL关键字来触发这一功能,在TPC-C的测试过程中,我们多次使用了Array Binding来提升系统的处理能力,效果非常明显。 Prepared Statement与执行计划缓存 Prepared Statement是一种二进制的请求交互协议,可以大大降低系统的交互成本。OceanBase不仅支持用户程序与数据库间使用Prepared Statement, 也支持在存储过程引擎调用SQL引擎执行时使用这种交互方式。存储过程在对SQL进行一次Prepare操作并获取唯一id后, 后续的每次执行仅需要传入该id和对应的参数,系统可以通过高速缓存找到对应的存储过程或SQL计划开始执行。该过程相比使用SQL文本的交互方式,省去了大量请求文本解析的CPU开销。 OceanBase内部实现了高速缓存来缓存存储过程的可执行代码及SQL执行计划,不同参数的存储过程和SQL可以通过这一高速缓存快速获取需要的执行对象, 耗时一般在几十微秒以内, 有效避免了重新编译带来的毫秒级的延迟和CPU消耗。 可更新视图 在OLTP场景中,通过减少应用与数据库的交互次数来实现性能提升的例子很多,可更新视图就是其中之一。我们常见的数据库视图通常是只读的,通过定义视图,用户可以定义自己感兴趣的数据以及其获取接口,但视图同时也可以作为更新操作的入口,比如在TPC-C的new order创建场景中,应用需要得到商品信息,更新库存并得到更新后的值。一般可以通过两条SQL实现这一过程: select i_price,i_name, i_data from item where i_id = ?; UPDATE stock SET s_order_cnt = s_order_cnt + 1, s_ytd = s_ytd + ?, s_remote_cnt = s_remote_cnt + ?, s_quantity = (CASE WHEN s_quantity< ? + 10 THEN s_quantity + 91 ELSE s_quantity END) - ? WHERE s_i_id = ? AND s_w_id = ? RETURNING s_quantity, s_dist_01, CASE WHEN i_data NOT LIKE'%ORIGINAL%' THEN 'G' ELSE (CASE WHEN s_data NOT LIKE '%ORIGINAL%' THEN 'G'ELSE 'B' END) END BULK COLLECT INTO ...; 但通过建立一个可更新视图: CREATE VIEW stock_item AS SELECT i_price, i_name, i_data, s_i_id,s_w_id, s_order_cnt, s_ytd, s_remote_cnt, s_quantity, s_data, s_dist_01 FROM stock s, item i WHERE s.s_i_id =i.i_id; 我们就可以通过一条语句更新库存并得到商品和库存信息: UPDATE stock_item SET s_order_cnt = s_order_cnt + 1, s_ytd = s_ytd + ?, s_remote_cnt = s_remote_cnt + ?, s_quantity = (CASE WHEN s_quantity< ? + 10 THEN s_quantity + 91 ELSE s_quantity END) - ? WHERE s_i_id = ? AND s_w_id = ? RETURNING i_price, i_name, s_quantity,s_dist_01, CASE WHEN i_data NOT LIKE'%ORIGINAL%' THEN 'G' ELSE (CASE WHEN s_data NOT LIKE '%ORIGINAL%' THEN 'G'ELSE 'B' END) END BULK COLLECT INTO ...; 这样就省去了一条语句的交互,并且更新逻辑更加直观。可更新视图允许用户可以像普通表一样操作视图,但不是所有视图都可以定义为可更新视图。比如带distinct, group by的视图,具体更新哪些行语义是不明确的,因此不能允许更新。具体到上面的stock_item两表join的视图,需要满足所更新表的unique key在join之后保持unique(key-preserved table),即item.i_id必须是唯一的这个前提。 需要强调,TPC-C规范禁止使用物化视图,而可更新视图并没有改变底层数据表格的存储形式,是符合规范的。 因为TPC-C的设计原则是尽可能的“真实”反应一个OLTP系统的运行场景,我们所做的很多优化都具有广泛的适用性。例如,对于一个高并发的OLTP系统来说,大部分的SQL请求的耗时是非常短的,采用纯粹的C/S交互模型的后果必然使系统的时间浪费在应用与数据库的频繁交互中,而使用存储过程可以大大缓解这种交互的耗时,并且增强系统对于网络抖动的免疫力,这种核心能力对于一个分布式OLTP数据库是不可或缺的。 在这次的TPC-C测试中,我们采用了OceanBase 2.0版本开始支持的Oracle兼容模式,存储过程和SQL全部使用了兼容Oracle的数据类型和语法,这样做也是为了在追求极致优化的同时,确保产品迭代可以沿着通用和正规的方向发展。 三、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的特性的解决方案。 Paxos日志同步保证持久性(Durability) OceanBase数据库的事务持久性(Durability)保证是依赖事务重做日志(Redo Log)的持久性来达成的。所有的 Redo Log 会实时强同步到另外两台数据库服务机器上,包含产生 Redo Log 的机器在内,总共会有三台机器在硬盘中持久化 Redo Log。 OceanBase 采用了 Paxos 一致性同步协议来协调这三台机器上 Redo Log 的持久化,Paxos协议采用超过半数(也叫“多数派”)成功即算成功的算法(三个副本时,两个成功即超过半数),当其中两台机器完成持久化后,事务即可完成提交,剩下的一台机器的 Redo Log 在通常情况下,也是立即就持久化完成了。但如果这台机器碰巧出现异常,也不会影响事务的提交,系统会在其恢复后自动补齐所缺失的 Redo Log。如果机器永久故障,系统会将故障机器所应负责同步的数据分散给集群内的其他机器,这些机器会自动补齐所缺失内容,并跟上最新的 Redo Log 写入。 使用Paxos一致性协议的最大优势是数据持久化和数据库服务可用性的完美平衡。当使用三个副本时,任何时候坏掉一个副本时至少还有另一个副本有数据,并且写入还可以持续,因为还剩下两个副本,后续的写入也不受影响。 所以,OceanBase 在保证了事务持久性的同时,也大大提升了数据库的连续服务能力。TPC组织的审计员在现场审计OceanBase持久性能力时,在客户端持续产生压力的情况下,从OceanBase集群中随意挑选了一台机器做了强制断电操作,发现数据库的数据不仅没丢,数据库不需要任何人工干预还能持续的提供服务,审计员们都很吃惊,并且对OceanBase大为赞赏。 依靠自动两阶段提交解决原子性(Atomicity) TPC-C测试模型的五种事务中的“订单创建”和“订单支付”两个事务分别会对很多数据做修改,是其中相对复杂的两个事务。TPC-C标准对事务的原子性(Atomicity)是强制性的要求,要求一个事务内部对仓库、订单、用户等表格的修改一定要原子的生效,不允许出现只有一半成功的情况。 OceanBase的数据是按照仓库ID(Warehouse_ID)拆分到多台机器上的,如果所有的事务都是发生在同一个仓库内部,那么无论数据量有多大,事务的修改都只会涉及一台机器的数据,也就是在一台机器上完成事务提交,这是一种完美的线形扩展的场景。但是这不符合实际的业务场景,大多数的实际业务都会有很多不同维度之间的数据交互。TPC-C测试标准也是对此认真考虑,所以对于事务操作数据的随机性规则提出了要求,最终要保证产生10%的“订单创建”事务和15%的“订单支付”事务要操作两个及以上的仓库。在OceanBase数据库内,这样就产生了跨机器的事务操作,而这必须使用两阶段提交协议来保证原子性。 OceanBase会自动跟踪一个事务内所有SQL语句操作的数据,根据实际数据修改的位置自动确定两阶段提交的参与者,事务开始提交时,OceanBase自动选择第一个参与者作为协调者,协调者会给所有参与者发送Prepare消息,每个参与者都需要写各自的Redo Log和Prepare Log(也意味着每个参与者各自做自己的Paxos同步),等协调者确认所有参与者的Redo Log和Prepare Log完成后,然后再给所有参与者发送Commit消息,再等所有参与者的Commit工作完成。整个协议是在事务提交过程中自动完成,对用户完全透明。OceanBase为每一个两阶段提交事务自动选择一个协调者,整个系统任何机器都可以分担协调者工作,所以OceanBase可以将事务处理能力进行线形扩展。 多版本并发控制保证事务的隔离性(Isolation) TPC-C标准里要求“订单创建”、“订单支付”、“订单配送”、“订单支付”事务之间都是串行化隔离级别(Serializable)。OceanBase采用的方法是基于多版本的并发控制机制。事务提交时会申请一个事务的提交时间戳,事务内的修改以新的版本写入存储引擎,并且保证之前版本的数据不受影响。事务开始时会获取一个读取时间戳,整个事务内数据的读取操作只会看到基于读取时间戳的已提交数据。所以,事务的读取不会遇到脏数据、不可重复读数据以及幻读数据。同时,事务的修改会在修改的数据行上持有行锁,保证两个并发的修改相同行的事务会互斥。 OceanBase的全局时间戳生成器也是由多副本组成,可以独立部署在三台机器上,也可以像这次TPC-C评测中一样部署在root node机器上,与root node共享资源。全局时间戳的三副本是一种极高可用的架构,任何一次时间戳的获取操作都至少在三台机器上的两台获得了确认,所以任意一台机器出现故障,获取时间戳的操作不会有一点影响。 按照TPC-C标准,OceanBase准备了9种不同的场景测试有读-读、读-写冲突时事务的隔离性,最终都完美通过了审计员的审计。 一致性保证(Consistency) 在有了上述的事务能力后,OceanBase可以完美的保证各种数据的一致性的约束。TPC-C标准里提出了12种不同的一致性测试场景在各种测试运行前后对数据库内的数据进行一致性校验。因为OceanBase此次测试数据规模庞大,一致性校验的SQL需要核对大量的数据,所以一致性校验的挑战在于校验的SQL本身运行的效率。基于OceanBase的并行查询能力,发挥整个集群所有的计算资源,校验SQL的运行时间均缩短了几个数量级,很好的完成一致性功能的审计工作。 复制表 TPC-C测试模型中有一张商品(ITEM)表,这张表的内容是测试所模拟的销售公司所有售卖的商品信息,包含了商品的名字、价格等信息。“订单创建”事务执行中需要请求这张表内的数据来确定订单的价格信息,如果商品表的数据只存放在一台机器上,那么所有机器上发生的“订单创建”事务都会请求包含商品表的机器,这台机器就会成为瓶颈。OceanBase支持复制表功能,将商品表设置为复制表后,商品表的数据会自动复制到集群中的每一台机器上。 TPC-C标准不限制数据的副本数,但是不管数据的组织形式,标准里要求事务的ACID一定要保证。OceanBase使用特殊的广播协议保证复制表的所有副本的ACID特性,当复制表发生修改时,所有的副本会同时修改。并且,当有机器出现故障时,复制表的逻辑会自动剔除无效的副本,保证数据修改过程中不会因为机器故障出现无谓的等待。复制表在很多业务场景中都有使用,例如很多业务中存储关键信息的字典表,还有金融业务中存储汇率信息的表。 四、TPC-C基准测试之存储优化 TPC-C规范要求被测数据库的性能(tpmC)与数据量成正比。TPC-C的基本数据单元是仓库(warehouse),每个仓库的数据量通常在70MB左右(与具体实现有关)。TPC-C规定每个仓库所获得的tpmC上限是12.86(假设数据库响应时间为0)。 假设某系统获得150万tpmC,大约对应12万个仓库,按照70MB/仓库计算,数据量约为8.4TB。某些厂商采用修改过的不符合审计要求的TPC-C测试,不限制单个warehouse的tpmC上限,测试几百到几千个warehouse全部装载到内存的性能,这是没有意义的,也不可能通过审计。在真实的TPC-C测试中,存储的消耗占了很大一部分。OceanBase作为第一款基于shared nothing架构登上TPC-C榜首的数据库,同时也作为第一款使用LSM Tree存储引擎架构登上TPC-C榜首的数据库,在存储架构上有如下关键点: 为了保证可靠性,OceanBase存储了两个数据副本和三个日志副本,而传统的集中式数据库测试TPC-C只存储一份数据; 由于OceanBase存储两个数据副本,再加上OceanBase TPC-C测试采用了和生产系统完全一样的阿里云服务器i2机型,SSD硬盘的存储容量成为瓶颈。OceanBase采用在线压缩的方式缓解这个问题,进一步增加了CPU使用;相应地,集中式数据库测试存储一份数据,不需要打开压缩; OceanBase LSM引擎定期需要在后台做compaction操作,而TPC-C要求测试至少运行8小时且2小时之内抖动小于2%,因此,OceanBase存储需要解决LSM引擎后台操作导致的抖动问题; 两份数据 为了保证可靠性和不丢数据(RPO=0),有两种不同的方案:一种方案是在硬件层面容错,另一种方案是在软件层面容错。OceanBase选择在软件层面容错,优势是硬件成本更低,带来的问题是需要冗余存储多个副本的数据。OceanBase使用Paxos协议保证在单机故障下数据的强一致。在Paxos协议中,一份数据需要被同步到多数派(超过一半),才被认为是写入成功,所以一般来说副本个数总是奇数,出于成本考虑最常见的部署规格是三副本。 三副本带来的首要问题就是存储成本的上升,之前商业数据库的TPC-C测试大多基于磁盘阵列,而TPC-C规范中明确对磁盘阵列不做容灾要求,使用相对于传统数据库三倍的存储空间进行TPC-C测试显然难以接受。 我们注意到这样一个事实,通过Paxos协议同步的只是日志,日志需要写三份,但数据不是,数据只需要有两份就可以完成单机故障的容灾了,当一份数据由于服务器宕机不可用时,另一份数据只要通过日志把数据补齐,就可以继续对外提供访问。 和数据存储相比,日志的存储量比较小。我们将数据与日志分开,定义了三种不同的副本类型:F副本既包含数据又同步日志,并对外提供读写服务;D副本既包含数据又同步日志,但对外不提供读写服务;L副本只同步日志,不存储数据。当F副本出现故障时,D副本可以转换为F副本,补齐数据后对外提供服务。在TPC-C测试中我们使用FDL模式进行部署(一个F副本,一个D副本,一个L副本),使用了两倍数据副本的存储空间。无论是D副本还是L副本,都需要回放日志,D副本还需要同步数据,这些都是都会消耗网络和CPU。 在线压缩 在sharednothing架构下,OceanBase至少需要存储两份数据才可以满足容灾的要求,这意味着OceanBase需要比传统数据库多耗费一倍的存储空间。 为了缓解这个问题,OceanBaseTPC-C测试选择对数据进行在线压缩,Oracle数据库中一个warehouse的存储容量接近70MB,而OceanBase压缩后存储容量只有50MB左右,大幅降低了存储空间。TPC-C规范要求磁盘空间能够满足60天数据量的存储,对于OceanBase,由于需要保存两份数据,虽然可靠性更好,但需要保存相当于120天的数据量,这些存储成本都要计入总体价格。 OceanBase使用了204台ECS i2云服务器存储数据,服务器规格和线上真实业务应用保持一致。每台服务器的日志盘1TB,数据盘接近13TB。计算两份压缩后的数据60天的存储空间之后,服务器的数据盘基本没有太多余量,从服务器的资源成本消耗来看,已经达到了比较好的平衡。如果OceanBase的单机性能tpmC进一步提升,磁盘容量将成为瓶颈。OceanBase LSM引擎是append-only的,它的优势是没有随机修改,能够在线压缩。无论是TPC-C测试,还是最核心的OLTP生产系统(例如支付宝交易支付),OceanBase都会打开在线压缩,通过CPU换存储空间。 存储性能平滑 TPC-C测试很大的挑战在于在整个压测过程中性能曲线要求是绝对平滑的,曲线上的波动幅度不能超过2%,这对于传统数据库来说都是一件困难的事情,因为这要求对于所有后台任务的精细控制,不能由于某个后台任务的资源过度使用导致前台请求的阻塞积压。而对于OceanBase而言,事情变得更为困难,因为OceanBase的存储引擎是基于LSM Tree的,在LSM Tree要定期执行compaction操作。Compaction是个非常重的后台操作,会占用大量CPU和磁盘IO资源,这对前台的用户查询和写入天然就会造成影响。我们做了一些优化,来平滑后台任务对性能的影响,从最终的测试结果来看,性能曲线在整个8小时压测过程中的抖动小于0.5%。 | 分层转储 在LSMTree中,数据首先被写入内存中的MemTable,在一定时候为了释放内存,MemTable中的数据需要与磁盘中的SSTable进行合并,这个过程被称为compaction。在很多基于LSM Tree的存储系统中,为了解决写入的性能问题,通常会将SSTable分为多层,当一层的SSTable个数或者大小达到某个阈值时,合并入下一层SSTable。多层SSTable解决了写入的问题,但是SSTable的个数过多,会极大拖慢查询的性能。OceanBase同样借鉴了分层的思路,但同时使用了更加灵活的compaction策略,确保SSTable总数不会太多,从而在读取和写入性能之间做了更好的平衡。 | 资源隔离 Compaction等后台任务需要消耗大量的服务器资源,为了减少后台任务对用户查询和写入的影响,我们在CPU、内存、磁盘IO和网络IO四个方面对前后台任务做了资源隔离。在CPU方面,我们将后台任务和用户请求分为不同的线程池,并按照CPU亲和性做了隔离。在内存方面,对前后台请求做了不同的内存管理。在磁盘IO方面,我们控制后台任务IO请求的IOPS,使用deadline算法进行流控。在网络IO方面,我们将后台任务RPC和用户请求RPC分为不同队列,并对后台任务RPC的带宽使用进行流控。存储CPU占用 TPC-C基准测试主要考察整体性能tpmC,很多人也会关注单核的tpmC。然而,这个指标只有在相同架构下才有意义。对于存储模块的CPU占用,有如下三点: 对于集中式架构,除了数据库使用CPU之外,专用存储设备也需要使用CPU。例如,第二名Oracle 3000多万tpmC的测试中,数据库使用了108颗T3SPARC处理器,共有1728个物理核心和13824个执行线程,同时存储设备使用的是Intel服务器作为机头,总共使用了97台服务器,194颗Intel X5670 CPU,2328个物理核心。 集中式数据库使用高可靠硬件,只需要存储一个副本,而OceanBase通过软件层面容错,虽然硬件成本更低但需要两个数据副本和三个日志副本,维护多个副本需要耗费大量CPU; OceanBase在TPC-C测试和生产系统中都打开了在线压缩,进一步增加了CPU使用; 因此,简单地对比OceanBase和Oracle的CPU核是不科学的,还需要算上共享存储设备的CPU核,以及OceanBase存储多副本和在线压缩带来的CPU开销。TPC-C推荐的方案是不关注具体的软件架构和硬件架构,关注硬件总体成本。在OceanBase的测试中,硬件成本只占整体成本的18%左右,只考虑硬件的性价比大幅优于集中式数据库。 后续发展 OceanBase的优势在于采用分布式架构,硬件成本更低,可用性更好且能够做到线性扩展,但是,OceanBase单机的性能离Oracle、DB2还有不小的差距,后续需要重点优化单机存储性能。另外,OceanBase的定位是在同一套引擎同时支持OLTP业务和OLAP业务,而目前OceanBase的OLAP处理能力还不如Oracle,后续需要加强存储模块对大查询的处理能力,支持将OLAP算子下压到存储层甚至在压缩后的数据上直接做OLAP计算。 作者:阳振坤(OceanBase创始人)曹晖(OceanBase技术专家)陈萌萌(OceanBase资深技术专家)潘毅(OceanBase资深技术专家)韩富晟(OceanBase资深技术专家)赵裕众(OceanBase高级技术专家) 原文发布时间为:2019-10-9本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:从十余年前的各种分布式系统研发到现在的容器云,从支撑原有业务到孵化各个新业务,企业的发展离不开统一的、与时俱进的技术架构。本篇文章从企业分布式应用架构层面介绍了云原生计算架构带来的变化,希望能够帮助更多企业的 IT 转型,利用云计算技术推动其成为市场竞争中的敏捷力量。 进入 21 世纪以来,我们见证了企业分布式应用架构从 SOA (Service-oriented Architecture),到微服务架构,再到云原生应用架构的演化。 为了说明企业架构演化背后的思考,我们先谈一些玄学。 第一,企业 IT 系统的复杂性(熵)符合热力学第二定律。随着时间的推演,业务的变化,企业 IT 系统的复杂度会越来越高; 第二,在计算机交互设计中有一个著名的复杂性守恒定律[1]。应用交互的复杂性不会消失,只会换一种方式存在。这个原理也同样适用于软件架构。引入新的软件架构,不会降低IT系统的整体复杂性。 听到这里,是否让生命不息、折腾不止的我们感到一丝凉凉? 现代软件架构的核心任务之一就是定义基础设施与应用的边界,合理切分复杂性,减少应用开发者需要面对的复杂性。换句话说,就是让开发者专注在核心价值创新上,而把一些问题交给更合适的人和系统来解决。 我们就从下面这张图开始,探究企业分布式应用架构演进背后的逻辑。 本图来自 Bilgin Ibryam 的 twitter[2] 蜕变之痛:SOA 2004 年,IBM 建立 SOA 全球设计中心,我作为研发 TL 和架构师参与了一系列全球客户的 pilot 项目,帮助 Pepboys, Office Depot 等国际企业利用 SOA 优化企业内部和企业间的业务流程,提升业务敏捷性。 当时的大背景是:随着经济全球化逐渐深入,企业面对的竞争加剧,商业变革也开始提速。在大型企业内部的 IT 系统已经经过了数十年的演化,整个的技术体系变得异常复杂,并存着诸如主机系统上的 CISC/COBOL 交易应用,小型机 AS400 中的 RPG 业务系统,和 X86/Power 等分布式系统的 C/JEE/.Net 应用。 大量应用系统由三方供应商提供,一些系统甚至已经无人维护。而且随着业务迭代,一些新的业务系统被持续构建出来,由于缺乏合理的方法论指导,系统之间缺乏有机的链接,形成了若干的孤岛,持续加剧了 IT 架构的复杂性,无法支撑业务的发展诉求。这就仿佛各派高手为了帮助受伤的令狐冲,把异种真气输入体中,虽然短时间可以缓解伤势。可是多道真气无法融合,互相激荡,长时间下来会伤上加伤。 因此,企业 IT 所面临的首要挑战就是整合企业中大量竖桶型(silo-ed)的 IT 系统,支撑日益复杂的业务流程,进行高效的业务决策和支撑业务快速变化。 在这种背景下,IBM 等公司提出了 SOA(面向服务的架构)理念,将应用系统抽象成一个个粗粒度的服务,构建松耦合服务架构,可以通过业务流程对服务进行灵活组合,提升企业 IT 资产复用,提高了系统的适应性、灵活性和扩展性,解决“信息孤岛”问题。 SOA 提出了一系列构建分布式系统的原则,这些思考直到今天也依然适用: 服务具备明确定义的标准化的接口。通过服务定义描述,将服务消费者(Service Consumer)和服务提供者 (Service Provider) 的实现进行解耦,并且服务应该采用 contract-first 而非 code-first 方式进行开发。服务间通信采用面向文档的消息而非特定语言 RPC 协议,一方面可以解决服务与实现语言的解耦,另一方面可以灵活选择同步或者异步的通信实现,提升系统可用性和可伸缩性; 服务应该是松耦合的,服务之间不应存在时间、空间、技术、团队上的依赖; 服务应该是无状态的,使得服务调用与会话上下文状态实现解耦; 服务应该是自治和自包含的,服务的实现是可以独立进行部署、版本控制、自我管理和恢复; 服务是可发现、可组合的。比如可以通过 Service Registry 进行服务发现,实现了服务消费者和服务提供者的动态绑定。业务流程中可以对来自不同系统的的业务服务进行编排组装。 在初始构建 SOA 系统的时候,大多采用点对点的通信连接,服务调用和集成逻辑被内嵌在应用实现中。这种方式在服务数量比较少的时候,确实是一种简单和高效的开发方式。但其最大的问题是,随着服务规模的增长,服务之间通信愈发复杂,连接路径和复杂性会剧增,给服务治理带来巨大的挑战。 为了解决上述挑战,企业服务总线 (Enterprise Service Bus,ESB) 开始被引入。企业服务总线提供了服务之间的连接(connection),转换(transformantion), 以及中介处理(mediation)的能力。可以将企业内部和各种服务连接到服务总线上,实现信息系统之间的松耦合架构,屏蔽了系统集成的复杂性,提高了 IT 系统架构的灵活性,降低企业内部信息共享的成本。 SOA 方法论的目标就像易筋经可以帮助梳理、归聚不同的真气,融会贯通,为我所用。然而修炼过程却绝非易事。大量雄心勃勃的 SOA 项目并未取得预期的效果,其背后的原因是什么? 任何 IT 架构的成功,都离不开与业务目标、技术基础和组织能力的相互配合。 在业务上,当时 SOA 重点解决的是企业 IT 的存量市场的问题。这使得 SOA 方法论很大程度被窄化为 Enterprise Application Integration (EAI 企业应用集成)。 在 SOA 理念中,打通信息系统间的经络只是第一步,还需要勤修内功,持续重构迭代企业 IT 架构,这样才能保持企业 IT 架构的敏捷、柔性,持续支撑业务的发展和变化。 在组织结构上,由于当时在大部分企业的 IT 部门仍然是成本中心,是业务的附属支撑部门,大多数企业缺乏长远的 IT 战略规划,IT 团队也缺乏成长认同,SOA 沦为项目制运作而没有组织化保障和持续投入。 即使当时成功的项目也会在复杂性日积月累的侵蚀下,逐渐失去活力。去年在美国生活的朋友发过来照片,15 年前我们为客户构建的业务系统还在支撑其现有全国门店的业务。这是技术项目的成功,却反映了企业技术战略的缺失。 在技术上,ESB 架构虽然实现了业务逻辑与服务集成的解耦,可以更好地进行中央化的服务治理,也暴露出一些严肃问题: 由于过度强调业务系统的可复用性,而不是对企业 IT 架构的治理和重构。大量服务集成的实现逻辑被下沉到 ESB 内部(如上图最右侧所示),这些逻辑非常难以维护,难以移植和扩展,成为 ESB 不可承受之重。我们必须在合适的地点合理地处理复杂性,而非将其简单转移; ESB 基于一个中心化的消息处理系统,但随着互联网的高速发展,ESB 已经无法应对企业IT规模化成长的挑战; ESB 这样的 Smart Pipes, Dumb endpoints 的系统架构是一个无法适应快速变化和大众创新的一个架构。 类比一下,电信运营商曾经希望将视频通信,电话会议等复杂功能纳入电信基础设施,只需一个 Dummy 电话终端就可以享受丰富的通信服务。然而随着智能电话的普及,微信和钉钉这样的分布式协同工具创新彻底颠覆了人们沟通交流的方式,而电信网络重回管道的宿命。 羽化之美:微服务 随着互联网的发展,尤其是移动互联时代的到来,整个世界的经济形态发生了巨大的变化改变。企业 IT 的重点从传统的 System of Record(交易系统,如 ERP、SCM 等)演化到 System of Engagement(互动系统,如全渠道营销)。 这些系统需要能够应对互联网规模的快速增长,并且能够快速迭代,低成本试错。企业 IT 已经成为创新驱动的引擎之一,技术拓展商业边界的理想也帮助 IT 团队更有使命感,进一步加速推动了企业 IT 的进化。 以 Netflix、阿里为首的一系列互联网公司主导了企业架构新的变革 - 微服务架构。Apache Dubbo, Spring Cloud 等微服务框架得到了广泛应用。 微服务的核心思想便是应用功能拆分与解耦,降低业务系统实现复杂性。微服务强调将应用功能拆解为一组松耦合服务,每个服务遵守单一责任原则(Single Responsibility Principle)。微服务架构解决了传统单体式架构存在的几个固有问题:每个服务可以独立部署和交付,大大提升了业务敏捷性;每个服务可以独立横向扩展/收缩,应对互联网规模的挑战。 原图来自于 Martin Fowler 对微服务架构的定义[3] 当然,将大型的单体应用拆解为多个微服务,也一定会增加 IT 系统研发协同、交付、运维的复杂性。这时候微服务架构与 DevOps 和容器自然走到了一起,构成了云原生应用架构的雏形。 微服务架构继承了 SOA 的架构原则,但是在实现层面,它倾向于通过构造智能端点和哑管道的去中心化分布式架构风格来替代 ESB。 微服务架构首先要面对分布式架构的内生复杂性,请参考分布式计算的误区[4]。微服务框架需要能够解决服务通信和服务治理的复杂性,比如服务发现、熔断、限流、全链路追踪等挑战。 微服务框架,如 HSF/Dubbo 或 Spring Cloud 以代码库的方式来封装这些能力。这些代码库被构建在应用程序本身中,随着应用一起发布和维护。 原图来源:[5] 服务通信和治理本质是横向的系统级关注,是与业务逻辑正交的。但在微服务架构中,其实现方式和生命周期与业务逻辑耦合在一起的。 微服务框架的升级会导致整个服务应用的重新构建和部署。此外由于代码库通常与特定语言所绑定,难以支持企业应用的多语言(polyglot)实现。 进化之光:云原生 SOA 采用中心化的服务总线架构,解耦了业务逻辑和服务治理逻辑;微服务架构回归了去中心化的点对点调用方式,在提升敏捷性和可伸缩性的同时,也牺牲了业务逻辑和服务治理逻辑解耦所带来的灵活性。 为了解决上述挑战,社区提出了 Service Mesh(服务网格)架构。它重新将服务治理能力下沉到基础设施,在服务的消费者和提供者两侧以独立进程的方式部署。 这样既达到了去中心化的目的,保障了系统的可伸缩性;也实现了服务治理和业务逻辑的解耦,二者可以独立演进不相互干扰,提升了整体架构演进的灵活性。同时服务网格架构减少了对业务逻辑的侵入性,降低了多语言支持的复杂性。 原图来源:[5] Google, IBM,Lyft 主导发起的 Istio 项目就是服务网格架构的一个典型的实现,也成为了新的现象级“网红”项目。 上图是 Istio 的架构,逻辑上分为数据平面和控制平面: 数据平面由一组以 sidecar 方式部署的智能代理组成,负责截获应用网络流量,收集遥测数据并且执行服务治理策略; 控制平面中,Galley 负责配置管理,Pilot 负责下发配置,Mixer 负责策略检查和遥测数据聚合,Citadel 负责通信中安全证书管理。 Istio 提供了一系列高阶的服务治理能力,比如:服务发现和负载均衡,渐进式交付(灰度发布),混沌注入与分析,全链路追踪,零信任网络安全等,可以供上层业务系统将其编排到自己的 IT 架构和发布系统之中。 但是 Service Mesh 不是银弹,其架构选择是通过增加部署复杂性(sidecar)和损失性能(增加两跳),来换取架构的灵活性和系统的可演化性。 为了解决部署复杂性的挑战,社区和云服务商都在共同进行努力: 一方面简化服务网格自动化运维水平(比如阿里云通过 operator 大大简化了 Istio的升级运维和跨 K8s 集群部署的复杂度); 另一方面提供托管的服务网格服务,帮助用户关注在业务层面的服务治理而非基础架构实现。 关于性能问题: 一方面 Service Mesh 需要降低自身控制平面和服务平面的性能开销,比如尽可能 offload mixer 负载,将治理策略执行下沉到数据平面完成; 另一方面还需要重新思考整个通信栈中应用与网络基础设施的边界。 为了实现容器应用之间的互联互通,Kubernetes 社区提出 CNI 网络模型,将容器网络连通性与底层网络实现的进行解耦,同时 K8s 提供了 Service, Ingress, Network policy 等基本元语来支持应用层的服务通信和访问控制。但是这些能力远不能满足应用对服务治理的需求。 服务网格在 L4/L7 增加了流量管理、全链路可观测性、安全互联等新功能,这些是通过引入运行在用户空间的 Envoy 代理实现的,在提升灵活性的同时也不可避免地增加了性能开销。 为了系统化解决这个问题,社区在进行有趣的探索。比如在 Cillium 容器网络中,可以利用 eBPF/XDP 等操作系统和底层网络能力,将应用层的服务控制能力(如 Kube-Proxy 提供的 service, network policy)下沉到操作系统内核和网络层解决,并优化了 Service Mesh 数据链路,减少上下文切换和数据拷贝,有效地减少了性能开销。 目前 Service Mesh 技术还处在技术成熟度曲线的初期,除了在 L4/L7 层提供灵活的服务通信功能,社区也在探索通过网络 Service Mesh[6] 实现灵活的 L2/L3 组网能力。我们相信其会成为未来企业分布式应用通信基础设施。 在这个过程中会有一些新的理念和项目被持续创造出来,我们需要能够理性地分析其业务价值和技术局限性。我们要避免将 Service Mesh 作为万灵药,不要将应用集成、应用侧安全等业务逻辑下沉到服务网格中,避免我们重蹈复杂性覆辙。可以参考 Application Safety and Correctness Cannot Be Offloaded to Istio or Any Service Mesh[7]。 回望历史 天下大势,分久必合,合久必分。企业分布式应用架构也走过一条分分合合的进化道路。在新技术迭起的今天,我们既要拥抱新技术带来的架构变化,更加要关注其背后的演进逻辑和核心价值,系统化地控制复杂性。 相关链接: [1] https://en.wikipedia.org/wiki/Law_of_conservation_of_complexity[2] https://twitter.com/bibryam/status/1026429379587567616[3] https://martinfowler.com/articles/microservices.html[4] https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing[5] https://philcalcado.com/2017/08/03/pattern_service_mesh.html [6] https://networkservicemesh.io/[7] https://blog.christianposta.com/microservices/application-safety-and-correctness-cannot-be-offloaded-to-istio-or-any-service-mesh/ 原文发布时间为:2019-10-8作者:易立 本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
Aliyun Linux 2(注1) CIS benchmark在2019年8月16日正式通过了CIS组织的全部认证流程对外发布,详情参见:https://workbench.cisecurity.org/benchmarks/2228。 关于Aliyun Linux 2的介绍可以参见:https://www.aliyun.com/product/alinux。所以在这里给大家介绍一下整个认证的一些详细信息。 首先介绍一下CIS认证 CIS全名Center for Internet Security,是一个美国的第三方安全组织,他们致力于采用线上社区的模式与大公司、政府机构、学术机构一起打造优秀的安全实践解决方案(各种benchmarks),详情可以参见维奇百科(注2)。当前各个公司发布的Linux操作系统大多都已经提供CIS benchmark和CIS Hardened image,包括CentOS、Ubuntu、Windows等,详情可以参见CIS网站(注3)。当前发布CIS benchmark已经成为很多阿里云客户对于OS安全的重要评判依据之一。 Aliyun Linux 2 CIS benchmark发布流程 从2019年3月收到需求到2019年8月完成最终的认证,总共耗时6个月,详细流程如下: Aliyun Linux 2 CIS benchmark详细信息 关于Aliyun Linux 2 CIS benchmark的详细内容可以通过CIS官网下载(注4)。Aliyun Linux 2 CIS Benchmark中主要分为了八大类:Profile Applicability,Description,Rationale,Audit,Remediation,Impact,References和CIS Controls。 Profile Applicability:其分为了Level 1和Level 2。Level 1是说明此加固条目是基础项加固且基本不会带来较大的性能影响,Level 2是说明此条目是安全性较重的、且如果管理员设置有稍偏差可能会带来很大的性能开销。 Decription:加固条目的简单介绍。 Rationale:用于描述条目的细节和背景,告诉读者这么加固的意义和原因。 Audit:关键项,用于判断检测系统是否达标的脚本。根据此脚本的运行返回值来判断是否需要加固。 Remediation:关键项,如果Audit环节判断系统需要进行加固,那此环节就是执行脚本进行安全处理。 Impact:影响,主要来描述如果不进行正确配置可能会导致的影响。 References:参考文献。 CIS Controls:此条目对应的CIS control文档的讲解,需要注册后才能下载。 每一个条目分为了Scored和Not Scored两类: Scored:意味着此条目会纳入计分项,如果验证系统是安全的,则加分,如果不安全,则减分。 Not Scored:意味着无论是否安全,都不纳入计分项,也就是说不增减分。 简单以1.1.2章节为例列举一下: 由于内容较多就不一一列举,详情可以参见附件或者CIS网站 Aliyun Linux 2 CIS benchmark使用 那么用户肯定会问如何使用Aliyun Linux 2的CIS benchmark呢? 首先用户在准备使用Aliyun Linux 2 CIS benchmark的时候需要问自己两个问题: 我使用Aliyun Linux 2 CIS benchmark是为了满足什么需求? 我的专业能力能否支持我完成我的镜像的Aliyun Linux 2 CIS benchmark改造? 第一个问题主要是因为该benchmark里面包含的内容非常多,如果所有项都进行改造来满足了可能会反而适得其反,所以需要用户分析清楚自己的真实安全需求再参考该benchmark进行改造。 第二个问题主要是因为该benchmark涉及到Linux操作系统的方方面面,如果没有非常专业的操作系统能力,可能在实施过程中出现各种各样的问题,比如性能恶化、功能丢失等。 所以如果有使用该benchmark诉求的业务,可以按照如下建议实施: 如果有充足的操作系统专业能力,那么可以直接参考附件中的benchmark按照自己的需求进行配置; 如果没有充足的操作系统运维能力,那么可以寻求操作系统团队(工单或者钉钉(注5)联系)的支持,后续操作系统团队也会发布自动化修复工具(可能无法覆盖所有修复项)来简化实施难度; 如果有客户只是需要一个配置了CIS benchmark的OS来使用,不需要考虑其他,那么可以直接使用CIS发布的Aliyun Linux 2的加固镜像(需要额外收费,当前还在制作中)。 备注: 注1:Aliyun Linux 2即现在的Alibaba Cloud Linux 2,只是名称发生了改变,内容没有任何变更,部分介绍可以参见:https://yq.aliyun.com/articles/719814注2:CIS维奇百科,网址:https://en.wikipedia.org/wiki/Center_for_Internet_Security注3:CIS网站,网址:https://workbench.cisecurity.org/files?q=linux&tags=注4:CIS官网下载,网址:https://workbench.cisecurity.org/files/2449注5:操作系统团队钉钉,群号:Alibaba Cloud Linux OS 开发者&用户群;群二维码:
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
阿里妹导读:什么是设计?什么是架构?从零开始建立一个新的系统,新写的每行代码都可能成为明天的历史包袱?如何能有效的在遗留代码上工作?今天,阿里资深技术专家辉子为我们带来NBF框架下软件工程架构设计通用方法论,值得细细品读。 Note:本文讨论的是基于服务化前提下的通用软件工程架构方法论,并未涉及到微观设计或架构的具体细节。 前言 即使代码多年的人都会对这两个问题有点蒙圈:什么是设计?什么是架构? 从单词上看:设计是Software Design,架构是Software Architecture;分别对应的作者是:Designer和Architect: Architect都是Designer,但Designer未必是Architect。正如所有的架构设计都是设计,但设计未必是架构设计; Design关注微观代码(inside component),Architecture关注宏观软件结构(between components); Architect应该都是从Designer成长起来的。毕业了用code编写软件;成长了用ppt设计软件; 只会用ppt设计,但代码写得不好的Architect都是假的Architect; Architecture里听到比较多的词语:Serverless、FAAS、Microservice、multi-layer、Event driven、OSGI、NBF...... Design里听到比较多的词语:SOLID、 DDD、正交设计、Design Pattern; 搞不清SOLID,也不可能把软件的层次分好,也无法理解什么是OSGI的价值; 好的Designer是通往好的Architect的必经之路。 服务化架构的基本原则 New System 从零开始建立一个新的系统,有几个特征: 历史包袱小 上下文简单 设计的约束小 新写的每行代码都可能成为明天的历史包袱 由于调用方还没有,新系统可以比较完美的执行我们预想的架构设计,但是切记,最后那行才是最重要的那行:不要让今天的代码成为明天的历史包袱,新的每行代码都在书写历史。 上图的1,2,3,4代表新建系统的顺序: 由“相”抽象出“心”:先思考,那么多的业务场景下“相”,共同的特征“心”是什么。并反向用更多的相去验证心。 将“心”具象成领域模型:关注领域模型(Domain Model),解耦数据模型(Persistence Model):将TUNNEL SPI化。 将领域模型中的依赖SPI化:解耦对外部系统的依赖,反转依赖控制权。 Mock所有spi实现,确保“心”领域模型包裹的单元测试完全通过 实现TUNNEL BUNDLE:设计数据模型(Persistence Model),关注“存”,“取”不关注领域模型。 实现依赖SPI适配BUNDLE:连接真实依赖服务。 包装domain service:模型相关,业务无关。 根据业务需求组合/编排domain service成为scenario bundle或者业务SOP。 Working on legacy 对于一个软件工程师来讲,写代码最痛苦的事情莫过于coding on legacy,但同时又给了我们各种说辞: 这些代码太烂了,改起来太费劲【需要更多人】 这事做不到,因为以前系统架构问题导致的【责任不在我】 经过我的修改,现在已经好很多了,工单数量大批下降【我功劳显著】 知不知道:接手你代码的人其实也在重复说上述3件事情 如何能有效的在遗留代码上工作,业内有本非常不错的书,叫"Working Effectively with Legacy Code",值得精读: 图片来源:书籍《Working Effectively with Legacy Code》 所以我这里的标题可能不准确,我要讨论的更多是"遗留代码的重构",什么时候我们开始讨论需要把现有系统重构: 代码确实腐化到无法正常维护,或者新加一个需求代价很大; 目前代码的技术架构满足不了下一步业务的发展; 很多特性已经下线作废,却跟有用的代码藕断丝连; 业务逻辑随着发展分散到不同的应用里,界限不清; 专家级的未雨绸缪,着眼未来的规划和新技术的应用; 换老大了,需要立新的flag。 架构的基本原则依然是上面那幅图。但上下文的不同,我们的发力点和优先级有明显的区别。阿里整个体系里的依赖关系错综复杂,要对阿里环境下的系统做重构是件绝对谨小慎微的事情。为了完成在这么复杂体系下的架构及代码重构,我们必须有条不紊的分离关注点以及一如既往的坚持软件卓越。 聚焦与收敛上游调用 解耦下游依赖 以服务为单位切换 老系统下线 经过一步一步的分解,legacy系统已经完全被重构,并且具备随时切换的准备。这里我给几个建议: 先把老实现作为API的默认实现,新的实现作为老的实现的降级实现,并使用策略分流一部分流量(具体比例跟团队信心相关); 对于有业务需求变更的部分应尽快实现在新的实现里,并将新实现作为API的默认实现,老实现作为新实现的降级实现,策略应该是即时降级,也就是新实现出现问题立刻降级到老实现; 运行一段时间没有问题后,讲所有默认实现切换为新实现,并将老实现作为新实现的降级实现; 其实这时就算所有切换完毕:老实现可以永远作为新实现的降级实现,也就是只要我升级一次服务,上一次成功版本就可以作为这次的降级实现,这样,线上问题回滚就是秒级的。 总结 本文基于借助NBF提供的远程多态,服务编排等能力下基础资料,商品,组网等系统新建,重构的经验及方法论总结。仅供遇到架构重构,解耦等问题困扰的技术团队参考。 Note:本文所有图形出自玉简 原文发布时间为:2019-09-30作者:辉子本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
9月27日,2019杭州云栖大会落下帷幕。全球超过6.7万开发者现场参会、超1250万人在线观看。科学家、技术人、开发者在为期三天的大会上,呈现了时代的最强科技,更展示了未来十年数字经济发展的核心驱动力量。 云栖大会到底为什么能吸引这么多技术人参与呢?在大会的最后一天,阿里妹跑遍全场,为你们搜来云栖大会3天要闻。用一张图,一句话,一段文字,揭秘云栖大会,告诉你技术人在谈论云栖大会时,究竟在聊些什么!了解云栖大会,一点都不难嘛,不信,咱们往下看~ <阿里云三任总裁同台对话 十年传承,步入技术无人区> 阿里云三任总裁王坚、胡晓明和张建锋在2019杭州云栖大会呈现了一场精彩的对话。从10年前坚定投入云计算核心技术研发,到数字经济时代的全面到来,十年传承的坚定投入,让阿里云能够持续探索科技未来,步入技术的无人区。 <达摩院公布2周年成绩单 获40余项世界第一> 成立两年后,达摩院今日对外公布了研究成果及进展。截至2019年9月,达摩院在国际顶级学术会议上累计发表近450多篇论文,在自然语言处理、智能语音、视觉计算等领域算法夺得40多项世界第一,已成为阿里聚集科技人才和基础技术研发的“技术基石”。 <第二届达摩院青橙奖名单揭晓 每人奖励100万> 阿里巴巴达摩院揭晓了第二届青橙奖获奖名单。10位青年科研学者经过层层筛选获得了这一奖项,每人将获得达摩院提供的100万元人民币奖金和全方位的研发资源支持。 阿里和SAP公司共同宣布,SAP S/4HANA® Cloud (ERP云) 和SAP® Cloud Platform (云平台) 正式落地阿里云。中国企业可以便捷地享受到SAP世界领先的企业级解决方案,以及阿里云平台强大的计算资源。这是世界级SaaS和PaaS平台落地在阿里云上的典型案例,也是双方战略合作的又一个重要里程碑。 <阿里巴巴AI每天调用超1万亿次 成中国最大的人工智能公司> 阿里在杭州云栖大会上首次公布人工智能调用规模:每天调用超1万亿次,服务全球10亿人,日处理图像10亿张、视频120万小时、语音55万小时及自然语言5千亿句,已经成为中国最大的人工智能公司。 <阿里巴巴发布技术公益基金 聚焦信息无障碍等三大领域> 云栖大会现场,阿里巴巴发布技术公益基金,并建立集团合伙人负责制。该项技术公益基金未来将重点支持普惠教育、信息无障碍建设、赋能公益行业数字化转型等领域,每个领域分别由阿里巴巴集团合伙人阿玺、范禹、小邪牵头负责。 <数字浙江的“时间减史” 引入阿里中台让群众没有难办的事> 云栖大会上,浙江政府首度集中公布了多项便民成果以及中台技术在数字浙江建设中的应用。以前需要多种证明材料,如今只需要一张身份证,就能办理385项民生事项。 浙江,越来越多的服务流程在精简,越来越多的服务事项在上线。这得益于浙江政府不断推进的数字化转型以及阿里巴巴提供的数字技术。 <阿里云智能总裁张建锋: 钉钉和淘宝一样有划时代意义> 9月25日,2019年云栖大会在杭州开幕,阿里云智能总裁张建锋发表演讲,称阿里云智能是未来的数字经济核心基础,在帮助中国企业数字化转型实现智能移动协同方面,钉钉是杰出的代表。在生产领域和管理领域,钉钉帮助1000万家企业组织数字化转型,这个意义堪比淘宝在消费领域的影响。 <阿里巴巴与联合国发布“饥饿地图” 中国技术力量帮助实现全球零饥饿> 中国技术为世界可持续发展提供新助力。美国纽约当地时间9月25日,阿里巴巴集团合伙人、蚂蚁金服董事长兼CEO井贤栋在出席联合国大会相关活动期间,与联合国世界粮食计划署(WFP)执行干事大卫·比斯利一起,共同宣布有“世界饥饿地图”之称的全球饥饿信息系统发布。 <阿里云发布新一代AIoT智能设备操作系统 可实现秒级故障定位> 阿里云正式发布了面向AIoT时代的新一代智能设备操作系统AliOS Things 3.0。该操作系统具备全新的开发模式、在线裁剪工具、应用与内核分离、脚本语言支持、本地AI框架等一系列特性,尤其是实现了智能设备秒级故障定位,让开发者能高效完成智能设备的开发和调试,实现快速上线。 <阿里云推出高速自由流解决方案 解决高速省界“撤站”收费难题> 9月26日,在2019杭州云栖大会期间,阿里云联合千方科技、北京特微智能科技、中远海运等多家生态伙伴发布了智慧高速自由流解决方案。 该方案具备达摩院视觉智能能力、云计算大数据平台能力、城市大脑多源数据融合能力、互联网服务能力等四大核心能力,能极大提升自由流收费稽核准确性。 此前,国务院印发《取消高速公路省界收费站实施方案》。按照计划,将力争2019年底前基本取消全国高速公路省界收费站。 <阿里云与Facebook达成合作 机器学习平台支持PyTorch> 阿里云与FaceBook宣布达成关于深度学习框架PyTorch的合作,开发者可以在阿里云机器学习平台上方便快捷获取PyTorch框架,使用模型训练、预测部署等全流程功能,享受云带来的便捷体验。 <把云数据库带回家 阿里云发布POLARDBBOX 高性能一体机> 阿里云宣布正式推出高性能数据库一体机——POLARDB BOX,用户部署在自有数据中心即可享受云数据库的便捷体验,同时还为Oracle等传统数据库用户提供一键迁移功能,最多节省95%迁移成本,更适合政企、交通、航运、金融等行业。 <全球23个城市引入阿里云城市大脑> 阿里云公布了城市大脑三年来取得的一系列进展:全球23个城市引入城市大脑,覆盖了交通、城管、文旅、卫健等11个领域,48个场景。同时,城市大脑成为“数字经济第一城”杭州发展的关键动力。 <阿里云发布第三代神龙架构 激发100%计算潜能> 阿里云正式发布第三代自研神龙架构,全面支持ECS虚拟机、裸金属、云原生容器等,贯穿整个IaaS计算平台,并在IOPS、PPS等方面提升5倍性能,用户能在云上获得物理机100%的计算能力。 **<银泰商业CEO陈晓东: 未来五年线上再造一个银泰百货> **新零售提出三周年,银泰百货交出答卷。在9月26日的云栖大会新零售生态峰会上,银泰商业CEO陈晓东宣布:截至今年9月,银泰百货数字化会员已突破1000万。未来五年线上将再造一个银泰百货。这意味着,银泰百货线上线下销售占比将达到1:1。 <贾扬清担任阿里巴巴开源技术委员会负责人> 9月27日杭州云栖大会,阿里巴巴副总裁、阿里云智能计算平台事业部总经理贾扬清正式担任阿里巴巴开源技术委员会负责人。 据悉,阿里巴巴开源技术委员会的目标是持续赋能开源,在基于阿里巴巴自身优势、发展社区协同机制的基础上为开发者深挖开源价值:提供强大计算能力、丰富应用场景帮助开源项目的成熟与完善;鼓励、发扬开源文化,输出更多阿里优秀的开源项目;提供机制、合规、安全、工具等服务,做好开源的“后勤保障”,提升开发者体验。 原文发布时间为:2019-09-27作者:阿里技术本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
阿里妹导读:这么多的CASE,花了大量时间和资源去运行,真能发现BUG吗?CI做到90%的行覆盖率,能发现问题吗?测试用例越来越多,删一些,会不会就发现不了问题了?今天,我们谈谈如何评估测试用例的有效性? 我们的测试用例有两个比较关键的部分: 1)调用被测代码:例如下面的RuleService.getLastRuleByClientId(ClientId)。2)进行结果Check:例如下面的AssertEqual(OrderId,"ABCD1234")。 TestCaseA ... RuleService.createRuleByClientId(ClientId,RuleDO); StringOrderId=RuleService.getLastRuleByClientId(ClientId); ... TestCaseB ... RuleService.createRuleByClientId(ClientId,RuleDO); StringOrderId=OrderService.getLastOrderByClientId(ClientId); AssertEqual(OrderId,"ABCD1234"); ... 我们希望一组测试用例不仅能够“触发被测代码的各种分支”,还能够做好结果校验。 当业务代码出现问题的时候,测试用例可以发现这个问题,我们就认为这一组测试用例是有效的。 当业务代码出现问题的时候,测试用例没能发现这个问题,我们就认为这一组测试用例是无效的。 我们对测试用例有效性的理论建模是: 测试有效性 = 被发现的问题数 / 出现问题的总数 为什么要评估测试用例的有效性? 测试用例有效性评估的方法? 基于故障复盘的模式成本太高,我们希望能够主动创造问题来评估测试用例的有效性。 我们找到了一种衡量“测试有效性”的方法,变异测试(mutation testing): 变异测试的例子 我们用了一组测试用例(3个),去测试一个判断分支。而为了证明这一组测试用例的有效性,我们向业务代码中注入变异。我们把b<100的条件改成了b<=100。 我们认为: 一组Success的测试用例,在其被测对象发生变化后(注入变异后),应该至少有一个失败。 如果这组测试用例仍然全部Success,则这组测试用例的有效性不足。 通过变异测试的方式:让注入变异后的业务代码作为“测试用例”,来测试“测试代码”。 我们实现了多种规则,可以主动的注入下面这些变异: 如何优雅的评估测试有效性? 为了全自动的进行测试有效性评估,我们做了一个变异机器人,其主要运作是: 往被测代码中写入一个BUG(即:变异); 执行测试; 把测试结果和无变异时的测试结果做比对,判断是否有新的用例失败; 重复1-3若干次,每次注入一个不同的Bug; 统计该系统的“测试有效性” 。 变异机器人的优点: 防错上线:变异是单独拉代码分支,且该代码分支永远不会上线,不影响生产。 全自动:只需要给出系统代码的git地址,即可进行评估,得到改进报告。 高效:数小时即可完成一个系统的测试有效性评估。 扩展性:该模式可以支持JAVA以及JAVA以外的多种语系。 适用性:该方法不仅适用于单元测试,还适用于其他自动化测试,例如接口测试、功能测试、集成测试。 变异机器人的使用门槛: 测试成功率:只会选择通过率100%的测试用例,所对应的业务代码做变异注入。 测试覆盖率:只会注入被测试代码覆盖的业务代码,测试覆盖率越高,评估越准确。 高配版变异机器人 我们正在打造的高配版变异机器人拥有三大核心竞争力: 分钟级的系统评估效率 为了保证评估的准确性,100个变异将会执行全量用例100遍,每次执行时间长是一大痛点。 高配版变异机器人给出的解法: 并行注入:基于代码覆盖率,识别UT之间的代码覆盖依赖关系,将独立的变异合并到一次自动化测试中。 热部署:基于字节码做更新,减少变异和部署的过程。 精准测试:基于UT代码覆盖信息,只运行和本次变异相关的UT(该方法不仅适用于UT,还适用于其他自动化测试,例如接口测试、功能测试、集成测试)。 学习型注入经验库 为了避免“杀虫剂”效应,注入规则需要不断的完善。 高配版变异机器人给出的解法:故障学习,基于故障学习算法,不断学习历史的代码BUG,并转化为注入经验。可学习型经验库目前覆盖蚂蚁金服的代码库,明年会覆盖开源社区。 兼容不稳定环境 集成测试环境会存在一定的不稳定,难以判断用例失败是因为“发现了变异”还是“环境出了问题”,导致测试有效性评估存在误差。 高配版变异机器人给出的解法: 高频跑:同样的变异跑10次,对多次结果进行统计分析,减少环境问题引起的偶发性问题。 环境问题自动定位:接入附属的日志服务,它会基于用例日志/系统错误日志构建的异常场景,自动学习“因环境问题导致的用例失败”,准确区分出用例是否发现变异。 落地效果如何? 我们在蚂蚁金服的一个部门进行了实验,得出了这样的数据: 换言之,几个系统的测试有效性为:系统A 72%,系统B 56%,系统C 70%。 测试有效性(%) = 1 - 未发现注入数 / 注入数 更多的测试有效性度量手段 基于代码注入的测试有效性度量,只是其中的一种方法,我们日常会用到的方法有这么几种: 代码注入:向代码注入变异,看测试用例是否能发现该问题 内存注入:修改API接口的返回内容,看测试用例是否能发现该问题 静态扫描:扫描测试代码里是否做了Assert等判断,看Assert场景与被测代码分支的关系 ... 还有更多其他的度量手段 Meet the testcase again 测试有效性可以作为基石,驱动很多事情向好发展: 让测试用例变得更能发现问题。 让无效用例可被识别、清理。 创造一个让技术人员真正思考如何写好TestCase的质量文化。 测试左移与敏捷的前置条件。 ...... 写到最后,想起了同事给我讲的一个有趣的人生经历: “大二期间在一家出版社编辑部实习,工作内容就是校对文稿中的各种类型的错误。编辑部考核校对质量的办法是,人为的事先在文稿中加入各种类型的错误,然后根据你的错误发现率来衡量,并计算实习工资。” “你干得咋样?” “我学习了他们的规则,写了个程序来查错,拿到了第一个满分” “厉害了...” “第二个月就不行了,他们不搞错别字了,搞了一堆语法、语义、中心思想的错误... 我就专心干活儿了” “...” 殊途同归,其致一也。 原文发布时间为:2019-09-27作者:义理本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
云栖大会精彩进行中,今天,是 Day-2。一早,阿里技术明星团闪亮登场!贾扬清:阿里巴巴副总裁、阿里云智能计算平台事业部总经理、高级研究员李飞飞:阿里巴巴副总裁、阿里云智能数据库产品事业部总经理、高级研究员蒋江伟(小邪):阿里巴巴合伙人、阿里云智能基础产品事业部总经理、研究员…… 有了重磅嘉宾,自然会有重磅消息。接下来就听阿里妹为你一一道来: 01 AI每天调用超1万亿次 阿里成中国最大的人工智能公司 上午,阿里首次公布人工智能调用规模:每天调用超1万亿次,服务全球10亿人,日处理图像10亿张、视频120万小时、语音55万小时及自然语言5千亿句,已经成为中国最大的人工智能公司。 同时,首次披露人工智能的完整布局,在AI芯片、AI云服务、AI算法、AI平台、产业AI实现全线领先。 在AI芯片层,平头哥发布全球最强AI芯片含光800,打破业界纪录,性能及能效比全球第一,1块含光800算力就相当于10块GPU。目前已应用于集团多个场景,如支持拍立淘索引时间从1小时缩短至5分钟。 在AI云服务层,全球前三、亚太第一的阿里云已构建起亚洲种类最全、规模最大的人工智能集群,包括GPU、FPGA、NPU、CPU、超算集群、第三代神龙架构等在内的公共云服务共同形成面向人工智能产业的最强力支持。日前为某现象级APP调集近万片GPU,创下世界记录。 在AI平台层,飞天AI平台、飞天大数据平台、AIoT平台等大大降低AI开发门槛。其中,飞天AI平台是国内首个云端商业化机器学习平台,支持上百亿特征、千亿训练样本的超大规模经典算法,降低35%训练成本、多个场景下提升400%训练速度,还首创公共云上可视化建模,为开发者提供了接近本地开发的极致体验。 在AI算法层,达摩院成立两年来在自然语言处理、智能语音、视觉计算等领域夺得40多项世界第一。其中自然语言处理在SQuAD机器阅读评比中精确阅读率首次超过人类,智能语音入选MIT Technology Review 2019年“全球十大突破性技术”,电话语音客服机器人被认为是“比谷歌更好的语音技术”,视觉计算可识别超过100万种物理实体。 达摩院机器智能实验室主任金榕说,阿里巴巴人工智能在技术布局、产业落地上已经形成了独一无二的良性循环,真正把商业机会与科技优势紧密联系。 与此同时,阿里已拥有10多位IEEE FELLOW、30多位知名高校教授、30位国家/省级千人称号专家,先后在国际顶级学术会议上发表450多篇论文。 阿里还在持续加大研发投入。在全球知名科技公司中,阿里巴巴是唯一一家研发人员占一半以上的公司,据普华永道调研数据显示,阿里巴巴的研发投入已经连续三年位列中国上市企业第一。 02 5倍性能提升、统一IaaS平台 阿里云推出第三代神龙架构 上世纪50年代,集装箱的发明缩短了远洋货运时间85%。60年后,2019杭州云栖大会上,阿里云神龙架构,正以“集装箱”式的创新推动云计算的算力变革。 阿里云发布了第三代自研神龙架构,全面支持ECS虚拟机、裸金属、云原生容器等,贯穿整个IaaS计算平台,并在IOPS、PPS等方面提升5倍性能,用户能在云上获得物理机100%的计算能力。 虚拟化带来的是云计算上的一次革命,它最大的价值是给计算带来了弹性,但同时又带来了性能损耗的负担——如果不解决虚拟化损耗,云计算产业将很快碰到技术的天花板。 “十年自研积淀让阿里云有机会突破自我,找到进化的方向,”阿里云智能基础产品事业部总经理蒋江伟说到,从2017年发布第一代神龙架构开始,阿里云就独自研发解决方案,最终突破了虚拟化的性能束缚,激发服务器释放100%潜能,重构云计算的基础设施。 在历经软件虚拟化、通用硬件虚拟化、专用硬件芯片虚拟化三个阶段后,第三代神龙架构实现了裸金属服务器、ECS虚拟机等计算平台的架构统一,用户可获得高质量的弹性资源。 此外,阿里云还发布了基于神龙架构的第六代ECS实例,计算性能提升20%,内存延迟降低30%,ESSD云盘延时降低70%。更高的性能背后是更普惠的价格,基于新架构的各产品最高降价幅度达58%以上。 值得一提的是,神龙架构对云原生浪潮下容器等产品适配程度极高。通过I/O offload芯片加速,高效调度和自动化弹性伸缩的容器化产品可帮助用户降低50%的计算成本,性能比物理机更优越。 正如集装箱改变了全球化的格局,以“神龙”为代表的创新技术也正在重塑云计算技术格局。阿里巴巴丰富的应用场景让阿里云成为极少数同时具备研发以及应用场景的闭环,一边研发一边应用,一边创造一边吃狗粮,这种双冲程式的方式构成了独特的能力。 “接下来,阿里云遍布全球的百万服务器将全面升级至第三代神龙架构,”蒋江伟说到,在阿里巴巴内部,神龙架构已大规模应用于淘宝、天猫、菜鸟等业务,解决高峰值的性能瓶颈问题。 03 把云数据库带回家 阿里云发布POLARDBBOX 高性能一体机 9月26日,阿里云宣布正式推出高性能数据库一体机——POLARDB BOX,用户部署在自有数据中心即可享受云数据库的便捷体验,同时还为Oracle等传统数据库用户提供一键迁移功能,最多节省95%迁移成本,更适合政企、交通、航运、金融等行业。 “POLARDB BOX是一款跨时代的产品,打破了云数据库的服务边界,”阿里云智能数据库产品事业部总经理李飞飞表示,阿里云希望将云原生数据库的管理能力下沉到本地IDC,在自有机房里就能享受公共云的体验。 发布会现场,POLARDB BOX实现了对航空App “飞常准” 25亿全球航班4D数据的毫秒级数据查询,如果这些数据按每条1厘米的宽度,加起来能绕地球4周。在过去,这些3D空间+时间的数据需要不同系统协同调度才能完成实时呈现,而通过POLARDB BOX特有的Ganos 时空 SQL 引擎即可一步到位,避免跨多个系统带来的复杂操作。 此外,POLARDB BOX一体机还具备三大亮点,满足企业在数字时代海量的数据存储需求: 极致性能,最大支持1152 vCPU、9TB内存、118TB SSD有效存储空间; 开放OpenAPI接口,可与私有云平台无缝对接; 无锁备份能力,10TB本地备份及恢复仅需10秒。 POLARDB是阿里云在2018年推出的商用云原生数据库,价格仅为传统数据库的1/6,具备快速弹性能力、超大规格、超高可靠性。目前,已有约40万个数据库迁移到阿里云上,涵盖金融、电信、制造、物流等领域的龙头企业。 与传统数据库不断下降的营收相比,云数据库正在积极地快速增长。到2022年,预计有3/4的数据库天然部署或迁移到云上。POLARDB BOX的发布,或将推动用户提前感受云原生数据库带来的技术变革,促进行业加速上云。 04 全球23个城市引入阿里云城市大脑 9月26日,2019杭州·云栖大会上,阿里云公布了城市大脑三年来取得的一系列进展:全球23个城市引入城市大脑,覆盖了交通、城管、文旅、卫健等11个领域,48个场景。同时,城市大脑成为“数字经济第一城”杭州发展的关键动力。 当天,阿里云还宣布同萧山机场的一项新合作。使用城市大脑的调度能力帮助机场有效管控航班起降、上下客、行李搬运、加油、餐配、检修、保洁等各个环节。 未来,城市数据将同机场数据进行融合,小到一件快递寄送,大到与高铁站的协同运行,都可以被精确计算和调度,减少因运筹不到位造成时间损失和资源浪费。让高铁、城市道路也能读懂飞机。 三年前,城市大脑在云栖大会上正式发布。从萧山区市心路的一条街道开始,现在城市大脑已经开始辅助管理整个杭州。一系列微小而美好的变化正在发生。 4.6分钟:22公里的中河-上塘高架出行时间节省4.6分钟。2.6秒:无杆停车试点推广,通过收费口只需2.6秒。12次:首个外地车“弹性限行”城市,每辆车可申请12次。30秒:酒店自助入住和退房,全程仅需30秒20秒:数字公园卡免排队,入园只要20秒1小时:先看病后付费,看病快了1小时。7分钟:救护车一键护航,平均快了7分钟。 新的变化还在不断发生…… 原文发布时间为:2019-09-26作者:阿里技术本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
9月25日,一年一度的云栖大会,聚集了来自世界上几十个国家的上千名科学家、CTO、企业家,以及对技术充满热情的超过6万名从业者和爱好者。 “十年前,我们讨论的是云计算和大数据的萌芽,五年前进入移动互联网的大时代,站在十年展望未来,整个社会都在全面进入数字经济时代。”阿里巴巴董事局主席兼首席执行官张勇在主论坛演讲中说。 01 阿里第一颗芯片问世 平头哥发布AI芯片含光800 阿里巴巴第一颗自研芯片正式问世,达摩院院长张建锋现场展示了这款全球最强的AI芯片——含光800。 含光为上古三大神剑之一,该剑含而不露,光而不耀,正如含光800带来的无形却强劲的算力。在杭州城市大脑的业务测试中,1颗含光800的算力相当于10颗GPU。 在业界标准的ResNet-50测试中,含光800推理性能达到78563IPS,比目前业界最好的AI芯片性能高4倍;能效比500IPS/W,是第二名的3.3倍。 张建锋说:“在全球芯片领域,阿里巴巴是一个新人,玄铁和含光800是平头哥的万里长征第一步,我们还有很长的路要走。” 含光800性能的突破得益于软硬件的协同创新:硬件层面采用自研芯片架构,通过推理加速等技术有效解决芯片性能瓶颈问题;软件层面集成了达摩院先进算法,针对CNN及视觉类算法深度优化计算、存储密度,可实现大网络模型在一颗NPU上完成计算。 含光800已开始应用在阿里巴巴内部核心业务中。根据云栖大会的现场演示,在城市大脑中实时处理杭州主城区交通视频,需要40颗传统GPU,延时为300ms,使用含光800仅需4颗,延时降至150ms。拍立淘商品库每天新增10亿商品图片,使用传统GPU算力识别需要1小时,使用含光800后可缩减至5分钟。 含光800将通过阿里云对外输出AI算力。基于含光800的AI云服务当天正式上线,相比传统GPU算力,性价比提升100%。 过去半年,平头哥先后发布玄铁910、无剑SoC平台。随着含光800的发布,平头哥端云一体全栈产品系列初步成型,涵盖处理器IP、一站式芯片设计平台和AI芯片,实现了芯片设计链路的全覆盖。 02 阿里云三任总裁同台对话 十年传承,步入技术无人区 阿里云三任总裁,王坚、胡晓明和张建锋在2019杭州云栖大会呈现了一场精彩的对话。 从10年前坚定投入云计算核心技术研发,到数字经济时代的全面到来,十年传承的坚定投入,让阿里云能够持续探索科技未来,步入技术的无人区。 阿里巴巴技术委员会主席王坚回忆道,“10年前,我们为云计算加上了一个定语,定义为以数据为中心的云计算,为今天以数据为关键要素的数字经济搭了一个桥,开了一条更远的路。” 这是阿里云的起点,通过大规模计算操作系统飞天的建设,开创了中国云时代,为数字经济时代提供计算引擎。到现在,全球200多个国家地区的数百万客户在使用阿里云的云计算、大数据和人工智能服务,其中包括一半以上的中国上市公司和80%以上的科技公司。 过去10年间,云计算已经在关键技术和应用规模上实现对传统IT的全面超越,云、大数据、AIoT和移动协同正在引领时代的发展。 “阿里巴巴是一家技术公司,科技是未来,”阿里巴巴集团CTO兼阿里云智能总裁张建锋表示,“今天所有的技术都是为了满足一种需求。并不是说CEO一定要理解技术,但是他需要理解技术的趋势和业务的变化。” 蚂蚁金服集团总裁胡晓明举了一个生动的例子:在西安的一个羊肉泡馍路边摊,也能够通过信用体系,获得贷款。这背后就是数据计算在重构这个世界的信用体系。他认为,互联网已经成为一个通用技术,必须要去思考、去推动技术和数据的创新。 面向未来,云计算、大数据、智联网、移动协同将成为数字经济的四大关键技术,阿里云的使命也从“飞天梦”走向更广泛的数字经济领域,支撑数字经济的伟大进程成为阿里云的新使命和要解决的核心问题。 03 达摩院公布2周年成绩单 40余项世界第一 2年前,阿里巴巴宣布成立达摩院,马云要求达摩院“活得要比阿里巴巴长”、“服务全世界至少20亿人口”、“必须面向未来、用科技解决未来的问题”,不少世界级科学家纷纷加入达摩院,潜心扎根基础科学研究。 截至2019年9月,达摩院在国际顶级学术会议上累计发表近450多篇论文,在自然语言处理、智能语音、视觉计算等领域算法夺得40多项世界第一,已成为阿里聚集科技人才和基础技术研发的“技术基石”。 目前,达摩院内“高手如林”,拥有10多位IEEE FELLOW、30多位知名高校教授、超过一半的科学家拥有名校博士学历。 达摩院今日对外公布了研究成果及进展。 作为一家面向未来的研究,达摩院同时还致力于推动整个社会加强对于基础科学研究发展的关注,先后举办了全球数学竞赛和青橙奖,为青年一代科学家提供开放的数据资源和应用场景等全方位支持。 今天,第二届达摩院青橙奖名单揭晓。10位青年科研学者经过层层筛选获得了这一奖项,每人将获得达摩院提供的100万元人民币奖金和全方位的研发资源支持,此次获奖者最年轻的科学家只有28岁。 “在青年博士走上科研道路的初期给予支持,能激发他们的研发热情与坚持扎根基础科研的决心。阿里巴巴达摩院院长张建锋说,“借助青橙奖、我们希望更多人关注到青年科学家,他们是中国创新技术的未来”。 与其他科学奖项不同,除去现金奖励外,达摩院会为青橙奖得主提供自由出入阿里全球各地研发机构的权限,提供数据、场景、计算力在内的研发资源,配备专门的技术与工程团队,帮助青年学者将科学想法转化为现实。2018年首届青橙奖的得主,中国科技大学计算机科技学院研究员张兰在达摩院的提供的应用场景的帮助下,推动自己的科研成果——基于指纹的数据追溯大数据原型平台走出实验室。 最后,一段视频介绍“AI芯片含光800”,感受现场魅力~ 原文发布时间为:2019-09-25本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。
2019年11月
2019年10月
2019年09月
内置filter函数视为None始终返回的“函数” True。
from functors import partial
from operators import lt, gt
def filter_dates(x, lower_bound=None, upper_bound=None):
lb = None if lower_bound is None else partial(lt, lower_bound)
ub = None if upper_bound is None else partial(gt, upper_bound)
return filter(lb, filter(ub, x))
(请注意,这将适用于字符串或date对象;只需传递相应类型的下限和上限。)