关于兼容性:Cocos-Creator-最佳实践JavaScript兼容性问题规避

本文从Cocos Creator 开发的角度登程,认真探讨了关注 JavaScript API 兼容性的必要性,以及如何借助工具和 Polyfill 来躲避 Cocos Creator 我的项目的兼容性问题。 一、引言:JavaScript虚拟机的差异性不同的浏览器和挪动设施所应用的 JavaScript 虚拟机(VM)千差万别,所反对的 API 也天壤之别。 咱们来理解一下 Cocos Creator 在各个端所应用的 JavaScript VM : 对于 iOS 客户端和 Mac 客户端:在 Cocos Creator 1.6 及以前,Cocos Creator 始终是应用非零碎原生的 SpiderMonkey 作为 JS VM ;从 1.7 开始,Cocos Creator 引入了 JSB 2.0 ,反对了 V8、JavaScriptCore 等多种 JS VM 。于是 Cocos Creator 便将 iOS 端和 Mac 端的 JS VM 都改为了零碎自带的 JavaScriptCore ,以达到节俭包体的目标;到了 2.1.3 ,Cocos Creator 又将 Mac 端的 JS VM 切换到了 V8,以晋升利用性能。对于 Android 客户端和 Windows 客户端:在 Cocos Creator 1.6 及以前,Cocos Creator 同样是应用 SpiderMonkey 作为 JS VM ;从 1.7 开始,得益于 JSB 2.0 ,V8 成了 Android 和 Windows 客户端的 JS VM 。对于 Web 端:应用浏览器自身的JavaScript VM 来解析 JavaScript 代码。 ...

August 18, 2021 · 6 min · jiezi

填坑手册小程序PC版来了如何做PC端的兼容

微信宣布小程序将可以在PC端微信打开后,智库君就接到要求,需要兼容PC端小程序,一开始以为官方已经做了完美适配,不需要改什么,但当本人下载内测版开始测试的时候,才发现或许坑还挺多的~~~ 下面分享下本人“搬砖填坑”的全过程:(以下都是PC端小程序特有的问题,手机端正常) 先说下使用流程 微信开发者工具菜单栏点击 设置->通用设置,在自动预览部分勾选“启动 PC 端自动预览”。使用自动预览功能,点击 预览->自动预览->编译并预览,成功的话将在微信 PC 版上自动拉起小程序。 PC版打开后就横屏问题 { "pages": [], "resizable":false, //在这里设置false,使得小程序默认手机尺寸 "pageOrientation":"portrait", //这里默认设置即可 ...}PC版微信默认打开小程序是ipad版,这样就会出现各种形变,布局错乱,这个可以在app.json进行配置,静止自动旋转,默认手机竖屏样子打开。 页面找不到问题 这个问题本人也找了很久,一直很纳闷IDE工具和手机打开看都没什么问题,用PC打开小程序就出现页面找不到的情况,大致报错是: page[pages/XXX/XXX] not found.May be caused by :1. Forgot to add page route in app.json.2. Invoking Page() in async task.一般这种情况以往是 app.json没配,或者页面里面缺少page(),但这次诡异的地方是只有“PC版小程序”报这个错!后来分析问题发现是:目前PC版小程序不能直接支持ES6,必须转换成ES5,同时由于一些语法转化不够完善,特别是ES7中的await 和 async 导致转化二次报错,这里就需要打开 “增强编译” 配置。 打开有CSS报错 因为目前PC版小程序估计内核的机制问题,还只支持低版本的选择器,如果你直接写小程序的标签,它无法识别,比如 .popCont navigator{ //navigator 标签是小程序里的,PC端无法支持 width: 560rpx; height: 300rpx;}.popCont image{ //image 标签是小程序里的,PC端无法支持 width: 560rpx; height: 300rpx;}但这些写法,其实在手机小程序和IDE工具里是完全正常的,PC版需要做兼容,改成class选择器。 布局结构混乱如果遇到这种情况,会检查一下是否使用屏幕尺寸(rpx)来计算布局,PC 上屏幕尺寸比窗口尺寸大,应该使用窗口尺寸来计算。 ...

October 16, 2019 · 1 min · jiezi

蚂蚁金服终端实验室演进之路

摘要: 本文将从支付宝业务特性出发,深度解析无线实验集群在支付宝的演进与发展,并探讨 IoT 与人机如何交互并提供真正落地的时间方案。作者:周力(问瑾),蚂蚁金服技术专家。本文将从支付宝业务特性出发,深度解析无线实验集群在支付宝的演进与发展,并探讨 IoT 与人机如何交互并提供真正落地的时间方案。 现场视频(复制地址到浏览器中打开):http://t.cn/AiKDZg5G 0. 背景作为国民级 App,支付宝客户端需要为亿级用户提供多元化的服务,因此应用的稳定性与可靠性面临巨大的挑战,需要不断地完善和优化。 今天,让我们站在服务质量的全方位监控与优化的角度,从蚂蚁终端实验室的演进之路展开探讨,从借助使用开源的自动化方案,到自研并逐步完善无线实验集群技术体系,支付宝内部经历了怎样的业务场景演练,以及相应的技术架构如何借助移动开发平台 mPaaS 对外输出。 1. 发展历程 总的来说, 蚂蚁终端实验室从诞生到现在,一共经历过三个阶段(工具化、服务化以及中台化),其每个阶段都有特点和意义: 工具化阶段:该阶段主要以使用市面上主流开源软件为主,如客户端开源软件 Appium, 其覆盖的端为 Android 和 iOS;通过这种开源工具和 App 测试流程结合的方式,快速满足业务方的提测需求,从而帮助业务方完成一般意义上的自动化测试工作(如基本的功能测试、兼容性测试等)。 服务化阶段:服务化阶段存在一个重要的背景:支付宝着手前后端研发流程分离,并逐步沉淀出独立的 App 端研发流程系统(研发协作流程与 App 构建流程)。在独立的 App 研发流程和系统的基础上,终端实验室以一种服务化的形式支撑 App 的研发和协作, 处理满足日常用户自动化工作外,同时还担当着持续集成、日常发布前自动验包工作等; 另外在日常发布发布提供质量数据支持,如客户端代码覆盖率统计等。 中台化阶段:伴随着终端实验室的能力不断提升优化以及测试规模的逐步扩大,服务上不仅需要满足蚂蚁金服体系 App(支付宝、口碑、网商银行等)日常测试需求,而且还需要将能力扩散覆盖到整个阿里巴巴集团的业务。 随之而来的是实验室需要面临多样化的业务方需求和定制化功能,如何在多元复杂的业务环境中,与业务方或者说上游系统完成能力共建?带着这个问题,终端实验室逐步沉淀并着手建设中台化平台:一方面让通用服务不断下沉,另一方面抽象出标准 SDK 的方式,让业务方根据自身业务特点建设特定的能力。 此外,在建设平台化的同时,终端实验室贴合支付宝业务场景的发展,构建如网络实验室、扫码实验室等一系列真实实验室的能力。 经历了几年的不断发展,终端实验室逐步完成了中台化的转变,其端上覆盖了 Android、iOS 以及 IoT 设备,服务上覆盖了通用能力、小程序准入、研发流程建设、真机租用以及用例管控等。 2. 技术生态在了解完终端实验室的历程之后,我们能够对其提供的服务有一个全面的认识。当我们去总结和分析这些服务时,可以把这些具体能力分为三大块:平台服务能力、客户端SDK 以及 实验室能力。 平台服务能力平台服务能力的目标是聚焦“如何把蚂蚁实验室构建成一个更为开放的平台”,因此我们需要考虑到如何让更多的业务方和上游系统一起参与能力共建,从而将平台的建设思路分为 2 大部分:设备实验集群和开放SDK。 1. 设备集群 蚂蚁实验室不仅包含数以千计的公用终端设备,覆盖市面绝大多数手机终端,帮助业务同学完成日常自动化测试工作,而且提供了用户自建实验室的方式:用户只需要根据自身业务场景特性进行设备采购、实验室部署,便具备在自有平台上运行自有设备的能力。 从平台的开放性与部署动态化角度看,目前设备集群能保证设备归属和业务场景做到充分隔离,保证各业务在平台使用上能相互独立。另外,面对阿里巴巴集团众多研发中心,设备集群在部署上也支持多地部署、相互隔离。 2. 开放SDK 为了给上游系统和用户提供更为开放的能力,帮助业务方根据自身需求完成能力建设。终端实验室提供开放的 SDK 能力:上游系统只需在自己服务上接入 SDK,就能够完成任务构建链路,从用例管理、设备选择、任务执行,到执行结果回调,在此基础上用户就能够根据自身业务特点将业务数据进行多维度组合,形成自己的能力输出。 ...

June 5, 2019 · 1 min · jiezi

判断浏览器是否支持-webp-的几种解决方法

我们都知道,WebP 是 Google 推出的 WebP 图片格式,它是一种支持有损压缩和无损压缩的图片文件格式,根据Google测试,相同的图片,WebP 格式的图片均能比 PNG,JPG 格式的图片节约不少体积,但是其兼容性不是很好,如下:因此我们需要做一些兼容处理,那么如何判断浏览器支持 webp 呢?下面有几种方法可供参考。方法一使用 canvas 的 toDataURL 进行判断 toDataURL方法在MDN解释如下: HTMLCanvasElement.toDataURL() 方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。如果画布的高度或宽度是0,那么会返回字符串“data:,”。如果传入的类型非“image/png”,但是返回的值以“data:image/png”开头,那么该传入的类型是不支持的。Chrome支持“image/webp”类型。toDataURL方法将图片转化为包含dataURI的DOMString,通过 base64 编码前面的图片类型值是image/webp进行判断。 比如在谷歌浏览器使用toDataURL方法转成image/webp: 在 Safari 浏览器使用toDataURL方法转成image/webp: 可以发现在不支持 webp 的浏览器进行toDataURL,得到的图片类型并不是 webp,因此我们可以通过这个进行判断。 实现方法: var isSupportWebp = function () { try { return document.createElement('canvas').toDataURL('image/webp', 0.5).indexOf('data:image/webp') === 0; } catch(err) { return false; }}isSupportWebp()方法二在服务端根据请求header信息判断浏览器是否支持webp 谷歌浏览器上请求图片 header是这样的: IE 浏览器请求图片 header是这样的: 在图片请求发出的时候,Request Headers 里有 Accept,服务端可以根据Accept 里面是否有 image/webp 进行判断。 ...

June 1, 2019 · 2 min · jiezi

ios12中遇到的带input弹窗的错位问题

问题描述:使用fixed定位的弹窗,在ios12的系统里,软键盘调起后,页面整体上移,当软键盘消失时,视觉上页面已经回到原始位置,但其实弹窗的焦点位置仍在软键盘调起时的位置。 解决办法:这也是参考某位大佬的解决办法 document.body.addEventListener('focusin', () => { // 软键盘弹出的事件处理 this.isReset = false})document.body.addEventListener('focusout', () => { // 软键盘收起的事件处理 this.isReset = true setTimeout(() => { // 当焦点在弹出层的输入框之间切换时先不归位 if (this.isReset) { window.scroll(0, 0) // 失焦后强制让页面归位 } }, 300)})尝试解决的其他方法尝试不使用fix定位,选择的absolute,判断input失焦时,使用window.scroll(),但是需要解决的问题很多 不同手机的input框在软键盘收起时情况不一样。苹果手机软键盘收起时,input框就失焦,但是小米手机键盘收起时,input框不失焦使用absolute定位后,软键盘出现页面会上移,软键盘消失时,页面不能恢复原来的位置

May 18, 2019 · 1 min · jiezi

函数运行环境系统动态链接库版本太低?函数计算 fun 神助力分忧解难

背景最近在处理线上工单的时候,遇到一个用户使用 nodejs runtime 时因为函数计算运行环境的 gcc 版本过低导致无法运行的问题,觉得非常有意思,所以深入的帮用户寻找了解决方案。觉得这个场景应该具有一定的通用性,所以在这篇文章里面重点的介绍一下如何使用函数计算的周边工具 fun 解决因为 runtime 中系统版本导致的各种兼容性问题。场景介绍用户问题简要描述一下用户当时遇到的问题:用户使用函数计算的 nodejs8 runtime,在本地自己的开发环境使用 npm install couchbase 安装了 couchbase 这个第三方库。couchbase 封装了 C 库,依赖系统底层动态链接库 libstdc++.so.6。因为用户自己的开发环境的操作系统内核比较新,所以本地安装、编译和调试都比较顺利。所以,最后按照函数计算的打包方式成功创建了 Function,但是执行 InvokeFunction 时,遇到了这样的错误:“errorMessage”: “/usr/lib/x86_64-linux-gnu/libstdc++.so.6: version CXXABI_1.3.9' not found (required by /code/node_modules/couchbase/build/Release/couchbase_impl.node)", "errorType": "Error", "stackTrace": [ "Error: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version CXXABI_1.3.9’ not found (required by /code/node_modules/couchbase/build/Release/couchbase_impl.node)”,…错误发生的原因如堆栈描述,即没有 CXXABI_1.3.9 这个版本,可以看到函数计算 nodejs 环境中的支持情况:root@1fe79eb58dbd:/code# strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 |grep CXXABI_ CXXABI_1.3CXXABI_1.3.1CXXABI_1.3.2CXXABI_1.3.3CXXABI_1.3.4CXXABI_1.3.5CXXABI_1.3.6CXXABI_1.3.7CXXABI_1.3.8CXXABI_TM_1升级底层系统版本的代价比较大,需要长时间的稳定性、兼容性测试和观察,所以,为了支持这类使用场景,我们希望能够有比较简单的方式绕行。场景复现和问题解决前提:先按照 fun 的安装步骤安装 fun工具,并进行 fun config 配置。在本地很快搭建了一个项目目录:- test_code/ - index.js - template.yml其中 index.js 和 template.yml 的 内容分别为# index.jsconst couchbase = require(‘couchbase’).Mock;module.exports.handler = function(event, context, callback) { var cluster = new couchbase.Cluster(); var bucket = cluster.openBucket(); bucket.upsert(’testdoc’, {name:‘Frank’}, function(err, result) { if (err) throw err; bucket.get(’testdoc’, function(err, result) { if (err) throw err; console.log(result.value); // {name: Frank} }); }); callback(null, { hello: ‘world’ })}# template.yml ROSTemplateFormatVersion: ‘2015-09-01’Transform: ‘Aliyun::Serverless-2018-04-03’Resources: fc: # service name Type: ‘Aliyun::Serverless::Service’ Properties: Description: ‘fc test’ helloworld: # function name Type: ‘Aliyun::Serverless::Function’ Properties: Handler: index.handler Runtime: nodejs8 CodeUri: ‘./’ Timeout: 60为了能够在本地模拟函数计算的真实环境进行依赖包安装和调试,这里生成一个 fun.yml 文件用于 fun install 安装使用,内容如下:runtime: nodejs8tasks: - shell: |- if [ ! -f /code/.fun/root/usr/lib/x86_64-linux-gnu/libstdc++.so.6 ]; then mkdir -p /code/.fun/tmp/archives/ curl http://mirrors.ustc.edu.cn/debian/pool/main/g/gcc-6/libstdc++6_6.3.0-18+deb9u1_amd64.deb -o /code/.fun/tmp/archives/libstdc++6_6.3.0-18+deb9u1_amd64.deb bash -c ‘for f in $(ls /code/.fun/tmp/archives/*.deb); do dpkg -x $f /code/.fun/root; done;’ rm -rf /code/.fun/tmp/archives fi - name: install couchbase shell: npm install couchbasefun.yml中参数说明:前面的分析已经了解到函数计算 nodejs8 runtime 的 libstdc++.so.6 的版本偏低,所以,我们找到一个更新的版本来支持,见新版本的 libstdc++.so.6 的 CXXABI_ 参数:$strings .fun/root/usr/lib/x86_64-linux-gnu/libstdc++.so.6|grep CXXABI_CXXABI_1.3CXXABI_1.3.1CXXABI_1.3.2CXXABI_1.3.3CXXABI_1.3.4CXXABI_1.3.5CXXABI_1.3.6CXXABI_1.3.7CXXABI_1.3.8CXXABI_1.3.9CXXABI_1.3.10CXXABI_TM_1CXXABI_FLOAT128执行 fun install 命令安装各种第三方依赖,显示如下:本地执行情况执行 fun local invoke helloworld,可以看到执行成功的效果:$fun local invoke helloworld begin pullling image aliyunfc/runtime-nodejs8:1.4.0………………………………………………………pull image finishedpull image finishedFC Invoke Start RequestId: 78e20963-b314-4d69-843a-35a3f465796cload code for handler:index.handlerFC Invoke End RequestId: 78e20963-b314-4d69-843a-35a3f465796c{“hello”:“world”}2019-02-19T08:16:45.073Z 78e20963-b314-4d69-843a-35a3f465796c [verbose] { name: ‘Frank’ }发布上线使用 fun deploy 发布上线,然后到控制台执行一下线上实际的运行效果:总结fun install 功能能够将代码和依赖文件分离开,独立安装系统依赖文件,而且 fun local 和 fun deply 都能够自动帮你设置第三方库的依赖引用路径,让您无需关心环境变量问题。本文的解法只是提供了一个对于系统版本偏低无法满足用户一些高级库使用需求时的简单绕行方案,仅供参考,对于一些复杂的环境依赖问题,可能还需要具体情况具体分析。更多参考:函数计算 nodejs runtimefun local99dXnVk4I)fun install本文作者:清宵阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 21, 2019 · 2 min · jiezi

知识整理之CSS篇

CSS篇主要从CSS兼容、CSS3新特性、CSS选择器、高频属性、高频布局、高频知识点、性能优化等方面进行归纳。如对HTML知识点感兴趣,可移步至:知识整理之HTML篇CSS HackCSS Hack就是针对不同的浏览器或不同版本浏览器写特定的CSS样式达到让浏览器兼容的过程。CSS Hack常见的有三种形式:CSS属性Hack、CSS选择符Hack以及IE条件注释Hack。CSS属性Hack(在标准模式下)color: red; /* 所有浏览器识别 /_color: red; / 仅IE6 浏览器识别 /-color: red; / 仅IE6 浏览器识别 /color: red; / 仅IE6、IE7 浏览器识别 /+color: red; / 仅IE6、IE7 浏览器识别 /+color: red; / 仅IE6、IE7 浏览器识别 /#color: red; / 仅IE6、IE7 浏览器识别 /color: red\0; / 仅IE8-IE10 浏览器识别 / color: red\9\0; / 仅IE9、IE10 浏览器识别 / color: red!important; / 仅IE6 浏览器不支持 / CSS选择符Hackhtml #demo { color: red; } /* 仅IE6 浏览器识别 /+html #demo { color: red; } /* 仅IE6、IE7 浏览器识别 /body:nth-of-type(1) #demo { color: red; } / IE9+、FF3.5+、Chrome、Safari、Opera 可以识别 /head:first-child+body #demo { color: red; } / IE7+、FF、Chrome、Safari、Opera 可以识别 /:root #demo { color: red9; } / 仅IE9 识别 /IE条件注释Hack<!–[if IE]>此处内容只有IE可见<![endif]–><!–[if IE6]>此处内容只有IE6.0可见<![endif]–><!–[if !IE 7]>此处内容只有IE7不能识别,其他版本都能识别,当然要在IE5以上。<![endif]–><!–[if gt IE 6]> IE6以上版本可识别,IE6无法识别 <![endif]–><!–[if gte IE 7]> IE7以及IE7以上版本可识别 <![endif]–><!–[if lt IE 7]> 低于IE7的版本才能识别,IE7无法识别。 <![endif]–><!–[if lte IE 7]> IE7以及IE7以下版本可识别<![endif]–><!–[if !IE]>此处内容只有非IE可见<![endif]–>常见浏览器兼容性问题与解决方案?不同浏览器的标签默认的padding和margin不同问题症状:常用标签,不加样式控制的情况下,各自的margin、padding差异较大。解决方案: { margin: 0; padding: 0;} 备注:这个是最常见的也是最易解决的一个浏览器兼容性问题,几乎所有的CSS文件开头都会用通配符来设置各个标签的margin、padding是0。块属性标签float后,又有横行的margin情况下,在IE6显示margin比设置的大问题症状:常见症状是IE6中后面的一块被顶到下一行。解决方案:在float的标签样式中设置#demo { display: inline } 当标签的高度设置小于10px,在IE6、IE7中会超出自己设置的高度问题症状:IE6、7和遨游里这个标签的高度不受控制,超出自己设置的高度。解决方案:给超出高度的标签设置#demo { overflow: hidden; }/ 或者 /#demo { line-height: 8px; } / 假设标签高度为9px / 行内属性标签,设置display:block后采用float布局,又有横行的margin的情况,IE6间距bug问题症状:IE6里的间距比超过设置的间距解决方案:#demo { display: block; display: inline; display: table;}备注:行内属性标签,为了设置宽高,我们需要设置display:block;(除了input标签比较特殊)。在用float布局并有横向的margin后,在IE6下,他就具有了块属性float后的横向margin的bug。不过因为它本身就是行内属性标签,所以我们再加上display:inline的话,它的高宽就不可设了。这时候我们还需要在display:inline后面加入display:talbe。图片默认有间距问题症状:几个img标签放在一起的时候,有些浏览器会有默认的间距,加了问题一中提到的通配符也不起作用。解决方案:img { float: left; }备注:因为img标签是行内属性标签,所以只要不超出容器宽度,img标签都会排在一行里,但是部分浏览器的img标签之间会有个间距。去掉这个间距使用float是正道。IE9一下浏览器不能使用opacity解决方案:#demo { opacity: 0.5; filter: alpha(opacity=50); filter: progid:DXImageTransform.Microsoft.Alpha(style = 0, opacity = 50); -moz-opacity: 0.5; -khtml-opacity: 0.5;}介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的?CSS盒子模型:由四个属性组成的外边距(margin)、内边距(padding)、边界(border)、内容区(width和height)盒子模型有两种, IE 盒子模型、W3C 盒子模型IE盒子模型宽高 = 内边距﹢边界﹢内容区标准盒子模型宽高 = 内容区宽高css设置方法/ 标准盒模型 /box-sizing: content-box;/ IE盒模型 /box-sizing: border-box;/ 继承父元素 */box-sizing: inherit;对BFC规范的理解?什么是BFCBFC(Block Formatting Context)即“块级格式化上下文”。常规流(也称标准流、普通流)是一个文档在被显示时最常见的布局形态。一个框在常规流中必须属于一个格式化上下文,你可以把BFC想象成一个大箱子,箱子外边的元素将不与箱子内的元素产生作用。BFC是W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。当涉及到可视化布局的时候,Block Formatting Context提供了一个环境,HTML元素在这个环境中按照一定规则进行布局。一个环境中的元素不会影响到其它环境中的布局。比如浮动元素会形成BFC,浮动元素内部子元素的主要受该浮动元素影响,两个浮动元素之间是互不影响的。也可以说BFC就是一个作用范围。形成BFC的条件浮动元素,float 除 none 以外的值定位元素,position(absolute,fixed)display 为以下其中之一的值 inline-block,table-cell,table-captionoverflow 除了 visible 以外的值(hidden,auto,scroll)BFC的特性内部的Box会在垂直方向上一个接一个的放置垂直方向上的距离由margin决定bfc的区域不会与float的元素区域重叠计算bfc的高度时,浮动元素也参与计算bfc就是页面上的一个独立容器,容器里面的子元素不会影响外面元素具体特性解释,可移步至CSS中的BFC详解什么是 FOUC?如何来避免 FOUC?什么是外边距重叠? 重叠的结果是什么?解释下什么是浮动和它的工作原理?什么是浮动?非IE浏览器下,容器不设高度且子元素浮动时,容器高度不能被内容撑开。 此时,内容会溢出到容器外面而影响布局。这种现象被称为浮动(溢出)。工作原理浮动元素脱离文档流,不占据空间(引起“高度塌陷”现象)浮动元素碰到包含它的边框或者其他浮动元素的边框停留如何清除浮动1. 给浮动元素的父元素添加高度(扩展性不好)如果一个元素要浮动,那么它的父元素一定要有高度。高度的盒子,才能关住浮动。可以通过直接给父元素设置height,实际应用中我们不大可能给所有的盒子加高度,不仅麻烦,并且不能适应页面的快速变化;另外一种,父容器的高度可以通过内容撑开(比如img图片),实际当中此方法用的比较多。2. clear:both在最后一个子元素新添加最后一个冗余元素,然后将其设置clear:both,这样就可以清除浮动。这里强调一点,即在父级元素末尾添加的元素必须是一个块级元素,否则无法撑起父级元素高度。<style>#wrap{ border: 1px solid;}#inner{ float: left; width: 200px; height: 200px; background: pink;}</style><div id=“wrap”> <div id=“inner”></div> <div style=“clear: both;"></div></div>3. 伪元素清除浮动上面那种办法固然可以清除浮动,但是我们不想在页面中添加这些没有意义的冗余元素,此时如何清除浮动吗?结合 :after 伪元素和 IEhack ,可以完美兼容当前主流的各大浏览器,这里的 IEhack 指的是触发 hasLayout。<style>#wrap { border: 1px solid;}#inner { float: left; width: 200px; height: 200px; background: pink;}.clearfix { *zoom: 1; }/ie6 7 不支持伪元素/.clearfix:after { content: ‘’; display: block; clear: both; height: 0; line-height: 0; visibility: hidden; /允许浏览器渲染它,但是不显示出来/}</style><div id=“wrap” class=“clearfix”> <div id=“inner”></div></div>4. 给父元素使用overflow:hidden这种方案让父容器形成了BFC(块级格式上下文),而BFC可以包含浮动,通常用来解决浮动父元素高度坍塌的问题。设置zoom:1清除浮动原理?触发hasLayout,清除浮动。zoom属性是IE浏览器的专有属性,它可以设置或检索对象的缩放比例。解决ie下比较奇葩的bug。譬如外边距(margin)的重叠,浮动清除,触发ie的haslayout属性等。原理:当设置了zoom的值之后,所设置的元素就会就会扩大或者缩小,高度宽度就会重新计算了,这里一旦改变zoom值时其实也会发生重新渲染,运用这个原理,也就解决了ie下子元素浮动时候父元素不随着自动扩大的问题。zoom属是IE浏览器的专有属性,火狐和老版本的webkit核心的浏览器都不支持这个属性。然而,zoom现在已经被逐步标准化,出现在CSS 3.0 规范草案中。目前非ie由于不支持这个属性,它们又是通过什么属性来实现元素的缩放呢? 可以通过css3里面的动画属性scale进行缩放。 ...

February 14, 2019 · 2 min · jiezi

跨越适配&性能那道坎,企鹅电竞Android weex优化

作者:龙泉,腾讯企鹅电竞工程师商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。原文链接:https://wetest.qq.com/lab/view/441.htmlWeTest 导读企鹅电竞从17年6月接入weex,到现在已经有一年半的时间,这段时间里面,针对遇到的问题,企鹅电竞终端主要做了下面的优化:image组件预加载预渲染_Image组件weex的list组件和image组件非常容易出问题,企鹅电竞本身又存在很多无限列表的weex页面,list和image的组合爆发的内存问题,导致接入weex后app的内存问题导致的crash一直居高不下。list组件问题首先来说一下list,list对应的实现是WXListComponent,对应的view是BounceRecyclerView。RecyclerView应该大家都很熟悉,android support库里面提供的高性能的替代ListView的控件,它的存在就是为了列表中元素复用。本来weex使用了RecyclerView作为list的实现,是一件皆大欢喜的事情,但是RecyclerView中有一种使用不当的情况,会导致view不可复用。下图描述了RecyclerView的复用流程:[ RecyclerView复用 ]weex中的RecyclerView并没有设置stableId,所以RecyclerView的所有复用都依赖于ViewHolder的ViewType,Weex的ViewType生成见下图: private int generateViewType(WXComponent component) { long id; try { id = Integer.parseInt(component.getRef()); String type = component.getAttrs().getScope(); if (!TextUtils.isEmpty(type)) { if (mRefToViewType == null) { mRefToViewType = new ArrayMap<>(); } if (!mRefToViewType.containsKey(type)) { mRefToViewType.put(type, id); } id = mRefToViewType.get(type); } } catch (RuntimeException e) { WXLogUtils.eTag(TAG, e); id = RecyclerView.NO_ID; WXLogUtils.e(TAG, “getItemViewType: NO ID, this will crash the whole render system of WXListRecyclerView”); } return (int) id; }在没有设置scope的情况下,viewHolder的component的ref就是viewType,即所有的ViewHolder都是不同且不可复用的,此时的RecyclerView也就退化成了一个稍微复杂一点的ScrollView。如果设置了scope属性,但你绝对想不到,scope本身也是一个坑。下面直接上代码:// BasicListComponent.onBindViewHolder() public void onBindViewHolder(final ListBaseViewHolder holder, int position) { … if (holder.getComponent() != null && holder.getComponent() instanceof WXCell) { if(holder.isRecycled()) { holder.bindData(component); component.onRenderFinish(STATE_UI_FINISH); } … } } // ListBaseViewHolder.bindData() public void bindData(WXComponent component) { if (mComponent != null && mComponent.get() != null) { mComponent.get().bindData(component); isRecycled = false;`` } }上面代码中,可以看到,使用了scope,当复用Holder时,会把需要展示的component的数据绑定到复用的component中。那么问题来了,如果我不是只是想修改部分属性,而是需要改变component的层级关系呢?例如从a->b->c修改成a->c->b,那么是不是只能用不同的viewType或者是说变成下面的结构:a->b a->c b->b1 b->c1 c->c2 c->b2这样的结构,但是view的实例多了,必然又会导致内存等各种问题。最为致命的问题是,createViewHolder的时候,传给ViewHolder的component实例就是原件,而非拷贝,当bindData执行了以后,就等用于你复用的那个component的数据被修改了,当你再滑回去的时候,GG。所以scope属性基本不可用,留给我们的只有相当于scrollView的list。还好,为了解决list这么戳的性能,有了recyclerList,从vue的语法层,支持了模板的复用。但是坑爹的是,0.17 、 0.18 版本recyclerList都有这样那样的问题,重构同学觉得使用起来效率较低。0.19版本weex团队fix了这些问题后,企鹅电竞的前端同学也正在尝试往recyclerList去切换。image组件问题相信android开发们都清楚,图片的问题永远是大问题。OOM、GC等性能问题,经常就是伴随着图片操作。在0.17版本以前,WXImageView中bitmap的释放都是在component的recycle中执行,0.17版本之后,在detach时也会执行recycle,但是WXImageView的recycle只是把ImageView的drawable设置为null,并没有实际调用bitmap的recycle。而企鹅电竞在版本运行过程中发现,仅仅把bitmapDrawable设置为null,不去调用bitmap的recycle,部分机型上面的oom问题非常突出(这里一直没想明白,为啥这部分机型会出现这个问题,后面替换成fresco去管理就没这个问题了)。当然,如果直接recycle bitmap,不设置bitmapDrawable,会直接导致crash。回到企鹅电竞本身,企鹅电竞中的图片管理使用了fresco,在接入weex以前,我们已经针对fresco加载图片做了一系列优化,而且fresco本身已经包含了三级缓存等功能。接入weex后,首先想到的就是使用fresco的管线加载出bitmap后给WXImage使用。在这个过程中,先是遇到了对CloseableReference管理不恰当导致bitmap 还在使用却被recycle 掉了,然后又遇到了没有执行recycle导致bitmap无法释放的坑。在长列表中,图片无法释放的问题被无限放大,经常出现快速滑动几屏就oom的问题。而且随着业务发展使用WXImage无法播放gif和webp图片也成为瓶颈。后续版本中,企鹅电竞直接重写了image和img标签,使用Fresco的SimpleDraweeView替换了ImageView。该方案带来的收益是bitmap不在需要自己管理,即oom问题和bitmap recycle之后导致的crash问题会大大减少,且fresco默认就支持gif和webp图片。但是,这个方案也有个致命的问题:圆角。圆角问题得先从fresco和weex各自的圆角方案说起。weex圆角(盒模型-border):https://weex.apache.org/cn/wi…fresco圆角:https://www.fresco-cn.org/doc…fresco圆角方案具体可见RoundedBitmapDrawable,RoundedColorDrawable,RoundedCornersDrawable这3个类,fresco圆角属性的改变最终都只是修改这3个类的属性,圆角也是基于draw时候修改canvas画布内容实现,BtimapDrawable的裁减以及边框的绘制都是在draw的时候绘制上去。weex圆角方案具体可见ImageDrawable,实现方案为借助android的PaintDrawable,通过设置shader实现bitmapDrawable的裁减,但是边框的绘制则依赖于backgroundDrawable。而且在fresco中,封装了多层的drawable,较难修改drawabl的 draw的逻辑,而且边框参数的设置也不如weex众多样化。针对两者的差异性,企鹅电竞的解决方案是放弃fresco的圆角方案,通过fresco的后处理器裁减bitmap达到圆角的效果,边框复用weex的background的方案。这个方案唯一的问题后处理器中必须创建一份新的bitmap,但是通过复用fresco的bitmapPool,并不会导致内存有过多的问题。下面贴一下后处理器处理圆角的关键代码:public CloseableReference<Bitmap> process(Bitmap sourceBitmap, PlatformBitmapFactory bitmapFactory) { CloseableReference<Bitmap> bitmapRef = null; try { if (mInnerImageView instanceof FrescoImageView && sourceBitmap != null && !sourceBitmap.isRecycled() && sourceBitmap.getWidth() > 0 && sourceBitmap.getHeight() > 0) { … // 解决Bitmap绘制尺寸上限问题,比如:Bitmap too large to be uploaded into a texture (1302x9325, max=8192x8192) int maxSize = EGLUtil.getGLESTextureLimit(); int resizeWidth = mWidth; int resizeHeight = mHeight; float ratio = 0; if (maxSize > 0 && (mWidth > maxSize || mHeight > maxSize)) { ratio = Math.max((float) mWidth / maxSize, (float) mHeight / maxSize); resizeWidth = (int) (mWidth / ratio); resizeHeight = (int) (mHeight / ratio); } float[] borderRadius = ((FrescoImageView) mInnerImageView).getBorderRadius(); if (checkBorderRadiusValid(borderRadius)) { Drawable imageDrawable = ImageDrawable.createImageDrawable(sourceBitmap, mInnerImageView.getScaleType(), borderRadius, resizeWidth, resizeHeight, false); imageDrawable.setBounds(0, 0, resizeWidth, resizeHeight); CloseableReference<Bitmap> tmpBitmapRef = bitmapFactory.createBitmap(resizeWidth, resizeHeight, sourceBitmap.getConfig()); Canvas canvas = new Canvas(tmpBitmapRef.get()); imageDrawable.draw(canvas); bitmapRef = tmpBitmapRef; } else if (ratio != 0) { bitmapRef = bitmapFactory.createBitmap(sourceBitmap, 0, 0, resizeWidth, resizeHeight, sourceBitmap.getConfig()); } } if (bitmapRef == null) { bitmapRef = bitmapFactory.createBitmap(sourceBitmap); } } catch (Throwable e) { WeexLog.e(TAG, “process image error:” + e.toString()); } return bitmapRef; }当list和image组合在一起的时候,由于weex的image并没有recycle掉bitmap,而且没有bitmapPool的使用,会导致长列表weex页面占用内存特别高。而替换为fresco的bitmap内存管理模式后,由于weex导致的内存crash问题占比明显从最开始版本的2%下降到了0.1%-0.2%。预加载当踩完大大小小的坑,缓解了内存和crash问题之后,企鹅电竞在weex使用上又遇到了2大难题:调试困难页面加载慢调试困难weex的页面并不能给前端的开发同学丝滑的调试体验。最开始前端同学是采用终端日志或者弹框的方式调试(心疼前端同学就这么学会了看android日志),后面通过再三跟weex团队的沟通,终于确定了weex和weex_debuger对应的版本,前端同学可以在chrome上面调试weex页面。然而weex_deubgger并不是完美的解决方案,weex本身是jscore内核,而weex_debugger只是通过chrome调试协议开了个服务,等同于使用的是chrome的内核,内核的不一致性无法保证调试的准确性。连weex的开发同学自己都说了会遇到debug环境和正式环境结果不一致的情况。解决方案也很简单,那就是可以在mac的xcode和safari上面调试。当时由于替换mac的成功过高,就将就使用了weex_debugger的方案,后面怎么解决了相信大家心里有数。页面加载速度慢随着企鹅电竞业务的发展,很快前端同学就反馈过来,怎么weex页面打开的速度这么慢,这个菊花转了这么久。当时的内心是崩溃的,明明接入的时候好好的,一个页面轻轻松松500-600ms就加载回来了,哪里会有问题?业务的发展速度永远是你想象不到的,2个版本不到的时间,企鹅电竞中的weex页面轻轻松松从个位数突破到两位数,bundle大小也轻轻松松从几十kb突破到了上百kb,由此带来的问题是打开weex页面后能明显看到菊花转动了,甚至打开速度上还不如直出的web页面。首先从数据报表中发现,页面打开速度中,1s中有300-400ms是bundle从网络下载的时间,那是不是把这段时间省了,页面有轻轻松松回到毫秒级别打开速度了。下图展示了预加载的整体流程。[ 预加载流程 ]预加载方案上线后,页面成功节省了将近200ms的耗时。20M的LRUCache大小也是参考了http cache的默认大小值,页面打开的预加载率在75%-80%。预渲染做了预加载之后,很快又发现,就算没有网络请求,页面打开耗时还是超过了1s。这种情况下,现有的方案已经无法继续优化页面。这个时候突然有了个想法,weex本身是把前端的虚拟dom转化为终端的各种view控件,那么为什么weex页面的打开会慢终端页面打开这么多呢?定义问题解决问题之前,先来定义一下问题具体是什么。针对渲染速度慢,企鹅电竞对weex渲染的耗时定义如下:· renderStart = 调用WXSdkInstance.render()的时间点· httpFinish = httpAdapter请求回来之后调用WXSdkInstance.onHttpFinish()的时间点· renderFinish = 回调 IWXRenderListener.onRenderSuccess()的时间点· 页面打开耗时 = renderFinish - renderStart· 网络耗时 = httpFinish - renderStart· 渲染耗时 = renderFinish - httpFinish所以之前的预加载,已经优化了网络耗时,但是渲染耗时在页面大了之后,依旧会有很大的性能问题。为了揭开这个问题的本质,先来看一下weex整体的框架:[ weex框架图: ]JSFrameWork提供给前端的sdk,对vue的dom操作做了各种封装,JSFrameWork单独打包到apk包中。JavaScriptCore使用与safari的JavaScript引擎,专门处理JavaScript的虚拟机,对应chrome的v8,功能可以大体联想成java的jvm。JSSweex core的server端,封装了对JavaScripteCore的调用,封装了instance的沙盒,多进程实现中,JSS和JavaScriptCore的执行在另外的进程,防止JS执行异常导致主进程崩溃。JSCweex core的client端,作为WeexFrameWork和JSS桥接层,另外从0.18版本开始,cssLayout也下沉到了这一层。WeexFrameWork提供各种sdk接口的java调用,虚拟dom和Android控件树的转换,控件管理等。了解完了weex框架,再把关注点转移到js build之后生成的jsBundle,细心的同学肯定能够发现,生成的jsBundle本质上就是一个js方法,所以weex页面render的过程本质上是执行一个js方法。针对企鹅电竞关注的游戏首页,对整个weex框架加了完整的打点,看到在nexus 6上面,对应的耗时以及整体流程如下图:[ weex执行流程以及耗时 ]可以看到性能的热点主要在执行js方法以及虚拟dom的执行这两个关键步骤上,根据打点来看,单个js方法和单个虚拟dom的执行,耗时都很低。企鹅电竞抓了多次打点,看到启动时候执行js最慢的也仅仅是3ms,大多数执行都在0.1ms - 0 ms这个区间。但是,再快的执行耗时,也架不住量多,同样以企鹅电竞游戏首页为例,启动的时候该页面执行的js方法多大2000+个,这2000+个方法执行再加上方法调度的耗时,能成为性能热点一点也不意外。而虚拟dom的执行也同理,单次执行经过weex团队的优化,执行耗时基本在1ms-3ms之间,但是同样的架不住量多以及线程调度的时间问题。预渲染方案了解RN的同学应该也知道,js方法的执行和虚拟dom的执行是这种框架的核心所在,想要撬动整个核心,基本上难度等同于重写一个了。那么剩下的方案也就只有一个:提前渲染。[ 预渲染 ]预渲染的方案修改了WeexFrameWork虚拟dom和Android控件树转换的部分,在预渲染时,不生成真正的component和view结构,用抽象出来的ComponentNode存储虚拟dom的操作,并在RealRender的时候将node转换成一个个component以及View。这个方案的基本原理就是典型的以提前消费的空间换取时间,不去转换真正的component和View原因是view在不同context中的不可复用性以及view本身会占用大部分内存。预渲染优化数据内存消耗提前渲染必然导致类内存的提前消耗,在huawei nove3上测试得到,预渲染游戏首页时的峰值内存会去到10M,但是在最后预渲染完成后GC会释放这部分内存,最终常驻内存为0.3M。 真正渲染游戏首页的内存峰值会去到20M,最后的常驻内存为5.6M。可以看到预渲染对常驻内存的消耗极少,但是由于虚拟dom执行,导致峰值内存偏高,在某些内存敏感场景下,还是会有一定风险。页面打开耗时实验室中游戏首页的正常加载数据为900ms(已经预加载,无网络耗时),经过预渲染,页面打开仅需要150ms。现网数据:[ 预渲染页面打开上报 ]最后,来两张优化前后的对比图:[ 预渲染: ][ 非预渲染: ]_“深度兼容测试”现已对外,腾讯专家为您定制自动化测试脚本,覆盖应用核心场景,对上百款主流机型进行适配兼容测试,提供详细测试报告。另有客户端性能测试,一网打尽FPS、CPU等基础性能数据,详细展示各类渲染数据,极速定位性能问题。点击:https://wetest.qq.com/cloud/deepcompatibilitytesting 即可体验。如果使用当中有任何疑问,欢迎联系腾讯WeTest企业QQ:2852350015 ...

January 26, 2019 · 2 min · jiezi

真香!iOS云真机全新上线!

作者:WeTest小编商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。原文链接:https://wetest.qq.com/lab/view/434.htmlWeTest 导读众多开发者已经渐渐适应通过调用线上的安卓真机进行远程调试,但是针对iOS设备,则依然存在“iOS设备昂贵”“无法及时采购iOS最新设备”“无法复现iOS历史系统版本”等问题。为了帮助开发者解决这一困扰, WeTest的iOS云真机正式上线了!!!iOS机型及系统列表一、全新iOS版本,覆盖不同系统WeTest上线的iOS设备中,除了市场主流的iOS机型,考虑到像邮箱类的应用对低系统版本设备也有测试需求,所以WeTest也增加了不同iOS机型的不同系统版本。使用者们可以在导航栏的筛选功能里选出自己想要的操作系统。二、还原真机操作,定位“刘海屏”适配问题设备支持多点触控,保留iOS辅助触控功能,贴近真实手机的操作。针对今年突出的 “刘海屏”适配问题,在左侧设备显示界面上,还原刘海屏显示,让开发者们能检验“刘海屏”问题做适配。并在测试过程中可以切换横竖屏显示。三、实时日志,精准读取数据在使用iOS云真机时,右侧会同步显示实时日志情况,方便开发者查看App运行时的日志,准确定位问题。四、截图查看,完整反馈在测试过程中,提供截图功能,可以保留关键图片附在报告里,便于不同人员之间的流转。五、安装说明在上传安装ipa包的时候,请注意以下几点:1.企业证书签名的ipa包,需要在描述文件中信任。2.个人证书签名的ipa包,可以参考【iOS云真机调试】一栏,添加测试设备的UDID。签名方式:打开苹果官方开发者网站将设备的UDID加入到对应的“Devices”列表中打开本地Xcode使用 同一账号密码登录工程下General的Signing中,配置签名“iOS云真机”现已对外,和真实手机一样的好用。点击:https://wetest.qq.com/product/cloudphone?from=content_lab 即可体验。如果使用当中有任何疑问,欢迎联系腾讯WeTest企业QQ:2852350015

January 3, 2019 · 1 min · jiezi

阿里云推出全栈IPv6解决方案,加速推进下一代互联网应用

IPv4地址已接近枯竭,被誉为下一代互联网技术的IPv6成为新的“全球互联网门牌号”,它可以让地球上的每一粒沙子都拥有地址。12月6日,阿里云宣布为企业提供全栈IPv6解决方案,加速推进中国下一代互联网应用。作为国内首个全面支持IPv6的云厂商,过去5个月,阿里云DNS的IPv6日查询量增长了600倍,目前,核心产品已全面支持。当下,各国都在加速推进下一代互联网的部署,今年5月,工信部发布了推进互联网协议第六版(IPv6)规模部署行动计划,IPv6改造已迫在眉睫。阿里巴巴2017年开始投入IPv6技术的研发与应用,并率先实现了大规模应用。其中,优酷、淘宝、天猫以及高德地图等多个用户量过亿的应用已开花结果,实现了IPv6的落地。现在,所有企业都可以通过阿里云获得这一能力。“我们希望通过阿里云向客户、合作伙伴分享向IPv6迁移的技术与经验,共同构建中国的IPv6生态,推动中国互联网加速迈向下一代互联网。” 阿里巴巴集团CTO、阿里云智能总裁张建锋表示。IPv6的改造是一项庞大的系统工程,如果用传统方式,需要对服务器、网络以及应用进行全方位的升级,不仅技术挑战大,而且周期长。阿里云提供了国内最完整的IPv6解决方案,可帮助企业最短几分钟完成业务系统的升级,同时保障业务的连续性。阿里云国内数十个IDC已支持IPv6。阿里云核心产品如ECS、VPC、OSS等均已支持IPv6自研技术Netframe拥有400G级IPv6转发能力。AliBGP实现跨厂商路由协议对接,解决了多厂商兼容性问题。AliGuard可以提供T级抗攻击能力。据了解,阿里云IPv6全栈解决方案已服务超过200个行业场景,涵盖零售、金融、制造、广电传媒等行业。新浪微博通过阿里云完成了IPv6公网访问业务的升级改造,未来将通过无NAT的IPv6源地址更精准地描绘用户画像,为用户提供千人千面的定制化服务;长江电力基于IPv6网站解决方案,实现了互联网网站和应用系统的双栈访问,为终端用户提供了更流畅的网络体验。阿里云智能研发总经理蒋江伟(花名:小邪)表示,“阿里云将与运营商、设备商、应用软件厂商深入合作,共同构建IPv6生态,加速推进中国IPv6的规模部署。”本文作者:阿里云头条阅读原文本文为云栖社区原创内容,未经允许不得转载。

December 6, 2018 · 1 min · jiezi

全图化引擎(AI·OS)中的编译技术

全图化引擎又称算子执行引擎,它的介绍可以参考从HA3到AI OS - 全图化引擎破茧之路。本文从算子化的视角介绍了编译技术在全图化引擎中的运用主要内容有:1.通过脚本语言扩展通用算子上的用户订制能力,目前这些通用算子包括得分算子,过滤算子等。这一方面侧重于编译前端,我们开发了一种嵌入引擎的脚本语言cava ,解决了用户扩展引擎功能的一些痛点,包括插件的开发测试效率,兼容性,引擎版本升级效率等。2.通过代码技术优化全图化引擎性能,由于全图化引擎是基于tensorflow开发,它天生具备tensorflow xla编译能力,利用内核的熔丝提升性能,这部分内容可以参考XLA概述 .xla主要面向tensorflow内置的内核,能发挥的场景是在线预测模型算分。但是对于用户自己开发的算子,XLA很难发挥作用。本文第二部分主要介绍对于自定义算子我们是如何做的代码生成优化的。通用算子上的脚本语言静脉由于算子开发和组图逻辑对普通用户来说成本较高,全图化引擎内置了一些通用算子,比如说射手算子,过滤器算子。这些通用算子能加载C ++插件,也支持用静脉脚本写的插件。关于静脉参考可以这篇文章了解一下。和C ++插件相比,静脉插件有如下特点:1.类java的语法。扩大了插件开发的受众,让熟悉java的同学能快速上手使用引擎。2.性能高.cava是强类型,编译型语言,它能和c ++无损交互。这保证了cava插件的执行性能,在单值场景使用cava写的插件和c ++的插件性能相当。3.使用池管理内存.cava的内存管理可定制,服务端应用每个请求一个池是最高效的内存使用策略。4.安全。对数组越界,对象访问,除零异常做了保护。5.支持jit,编译快。支持upc时编译代码,插件的上线就和上线普通配置一样,极大的提升迭代效率6.兼容性:由于cava的编译过程和引擎版本是强绑定的,只要引擎提供的cava类库接口不变,cava的插件的兼容性很容易得到保证。而c ++插件兼容性很难保证,任何引擎内部对象内存布局的变动就可能带来兼容性问题。射手算子中的静脉插件cava scorer目前有如下场景在使用1.主搜海选场景,算法逻辑可以快速上线验证2.赛马引擎2.0的算分逻辑,赛马引擎重构后引入cava算分替代原先的战马算分样例如下:package test;import ha3.;/ * 将多值字段值累加,并乘以query里面传递的ratio,作为最后的分数 * /class DefaultScorer { MInt32Ref mref; double ratio; boolean init(IApiProvider provider) { IRefManager refManger = provider.getRefManager(); mref = refManger.requireMInt32(“ids”); KVMapApi kv = provider.getKVMapApi(); ratio = kv.getDoubleValue(“ratio”);//获取kvpair内参数 return true; } double process(MatchDoc doc) { int score = 0; MInt32 mint = mref.get(doc); for (int i = 0; i < mint.size(); i++) { score = score + mint.get(i); } return score * ratio; }}其中cava scorer的算分逻辑(process函数)调用次数是doc级别的,它的执行性能和c ++相比唯一的差距是多了安全保护(数组越界,对象访问,除零异常)。可以说cava是目前能嵌入C ++系统执行的性能最好的脚本语言。过滤算子中静脉插件filter算子中主要是表达式逻辑,例如filter =(0.5 * a + b)> 10.以前表达式的能力较弱,只能使用算术,逻辑和关系运算符。使用cava插件可进一步扩展表达式的能力,它支持类的Java语法,可以定义变量,使用分支循环等。计算 filter = (0.5 * a + b) > 10,用cava可定义如下:class MyFunc { public boolean init(FunctionProvider provider) { return true; } public boolean process(MatchDoc doc, double a, double b) { return (0.5 * a + b) > 10; }}filter = MyFunc(a, b)另外由于静脉是编译执行的,和原生的解释执行的表达式相比有天然的性能优势。关于静脉前端的展望静脉是全图化引擎上面向用户需求的语言,有用户定制扩展逻辑的需求都可以考虑用通用算子+静脉插件配合的模式来支持,例如全图化SQL上的UDF,规则引擎的匹配需求等等。后续静脉会进一步完善语言前端功能,完善类库,尽可能兼容的Java。依托苏伊士和全图化引擎支持更多的业务需求。自定义算子的代码生成优化过去几年,在OLAP领域codegen一直是一个比较热门的话题。原因在于大多数数据库系统采用的是Volcano Model模式。其中的下一个()通常为虚函数调用,开销较大。全图化引擎中也有类似的代码生成场景,例如统计算子,过滤算子等。此外,和XLA类似,全图化引擎中也有一些场景可以通过算子融合优化性能。目前我们的代码生成工作主要集中在CPU上对局部算子做优化,未来期望能在SQL场景做全图编译,并且在异构计算的编译器领域有所发展。单算子的代码生成优化统计算子例如统计语句:group_key:键,agg_fun:总和(VAL)#COUNT(),按键分组统计键出现的次数和缬氨酸的和在统计算子的实现中,键的取值有一次虚函数调用, sum和count的计算是两次虚函数调用,sum count计算出来的值和需要通过matchdoc存取,而matchdoc的访问有额外的开销:一次是定位到matchdoc storage,一次是通过偏移定位到存取位置。那么统计代码生成是怎么去除虚函数调用和matchdoc访问的呢?在运行时,我们可以根据用户的查询获取字段的类型,需要统计的功能等信息,根据这些信息我们可以把通用的统计实现特化成专用的统计实现。例如统计sum和count只需定义包含sum count字段的AggItem结构体,而不需要matchdoc;统计函数sum和count变成了结构体成员的+ =操作。假设键和VAL字段的类型都是整型,那么上面的统计语句最终的代码生成成的静脉代码如下:class AggItem { long sum0; long count1; int groupKey;}class JitAggregator { public AttributeExpression groupKeyExpr; public IntAggItemMap itemMap; public AggItemAllocator allocator; public AttributeExpression sumExpr0; … static public JitAggregator create(Aggregator aggregator) { …. } public void batch(MatchDocs docs, uint size) { for (uint i = 0; i < size; ++i) { MatchDoc doc = docs.get(i); //由c++实现,可被inline int key = groupKeyExpr.getInt32(doc); AggItem item = (AggItem)itemMap.get(key); if (item == null) { item = (AggItem)allocator.alloc(); item.sum0 = 0; item.count1 = 0; item.groupKey = key; itemMap.add(key, (Any)item); } int sum0 = sumExpr0.getInt32(doc); item.sum0 += sum0; item.count1 += 1; } }}这里总计数的虚函数被替换成和+ +和计数+ =,matchdoc的存取变成结构体成员的读写item.sum0和item.count0。经过llvm jit编译优化之后生成的ir如下:define void @_ZN3ha313JitAggregator5batchEP7CavaCtxPN6unsafe9MatchDocsEj(%“class.ha3::JitAggregator”* %this, %class.CavaCtx* %"@cavaCtx@", %“class.unsafe::MatchDocs”* %docs, i32 %size){entry: %lt39 = icmp eq i32 %size, 0 br i1 %lt39, label %for.end, label %for.body.lr.phfor.body.lr.ph: ; preds = %entry %wide.trip.count = zext i32 %size to i64 br label %for.bodyfor.body: ; preds = %for.inc, %for.body.lr.ph %lsr.iv42 = phi i64 [ %lsr.iv.next, %for.inc ], [ %wide.trip.count, %for.body.lr.ph ] %lsr.iv = phi %“class.unsafe::MatchDocs”* [ %scevgep, %for.inc ], [ %docs, %for.body.lr.ph ] %lsr.iv41 = bitcast %“class.unsafe::MatchDocs”* %lsr.iv to i64* // … prepare call for groupKeyExpr.getInt32 %7 = tail call i32 %5(%“class.suez::turing::AttributeExpressionTyped.64”* %1, i64 %6) // … prepare call for itemMap.get %9 = tail call i8* @_ZN6unsafe13IntAggItemMap3getEP7CavaCtxi(%“class.unsafe::IntAggItemMap”* %8, %class.CavaCtx* %"@cavaCtx@", i32 %7) %eq = icmp eq i8* %9, null br i1 %eq, label %if.then, label %if.end10// if (item == null) {if.then: ; preds = %for.body // … prepare call for allocator.alloc %15 = tail call i8* @_ZN6unsafe16AggItemAllocator5allocEP7CavaCtx(%“class.unsafe::AggItemAllocator”* %14, %class.CavaCtx* %"@cavaCtx@") // item.groupKey = key; %groupKey = getelementptr inbounds i8, i8* %15, i64 16 %16 = bitcast i8* %groupKey to i32* store i32 %7, i32* %16, align 4 // item.sum0 = 0; item.count1 = 0; call void @llvm.memset.p0i8.i64(i8* %15, i8 0, i64 16, i32 8, i1 false) // … prepare call for itemMap.add tail call void @_ZN6unsafe13IntAggItemMap3addEP7CavaCtxiPNS_3AnyE(%“class.unsafe::IntAggItemMap”* %17, %class.CavaCtx* %"@cavaCtx@", i32 %7, i8* %15) br label %if.end10if.end10: ; preds = %if.end, %for.body %item.0.in = phi i8* [ %15, %if.end ], [ %9, %for.body ] %18 = bitcast %“class.unsafe::MatchDocs”* %lsr.iv to i64* // … prepare call for sumExpr0.getInt32 %26 = tail call i32 %24(%“class.suez::turing::AttributeExpressionTyped.64”* %20, i64 %25) // item.sum0 += sum0; item.count1 += 1; %27 = sext i32 %26 to i64 %28 = bitcast i8* %item.0.in to <2 x i64>* %29 = load <2 x i64>, <2 x i64>* %28, align 8 %30 = insertelement <2 x i64> undef, i64 %27, i32 0 %31 = insertelement <2 x i64> %30, i64 1, i32 1 %32 = add <2 x i64> %29, %31 %33 = bitcast i8* %item.0.in to <2 x i64>* store <2 x i64> %32, <2 x i64>* %33, align 8 br label %for.incfor.inc: ; preds = %if.then, %if.end10 %scevgep = getelementptr %“class.unsafe::MatchDocs”, %“class.unsafe::MatchDocs”* %lsr.iv, i64 8 %lsr.iv.next = add nsw i64 %lsr.iv42, -1 %exitcond = icmp eq i64 %lsr.iv.next, 0 br i1 %exitcond, label %for.end, label %for.bodyfor.end: ; preds = %for.inc, %entry ret void}代码生成的代码中有不少函数是通过C ++实现的,如docs.get(i)中,itemMap.get(键)等。但是优化后的IR中并没有docs.get(I)的函数调用,这是由于经常调用的c ++中实现的函数会被提前编译成bc,由cava编译器加载,经过llvm inline优化pass后被消除。可以认为cava代码和llvm ir基本能做到无损映射(cava中不容易实现逻辑可由c ++实现,预编译成bc加载后被内联),有了cava这一层我们可以用常规面向对象的编码习惯来做codegen,不用关心llvm api细节,让codegen门槛进一步降低。这个例子中,统计规模是100瓦特文档1瓦特个键时,线下测试初步结论是延迟大约能降1倍左右(54ms-> 27ms),有待表达式计算进一步优化。2.过滤算子在通用过滤算子中,表达式计算是典型的可被codegen优化的场景。例如ha3的过滤语句:filter =(a + 2 * b - c)> 0:表达式计算是通过AttributeExpression实现的,AttributeExpression的评价是虚函数。对于单文档接口我们可以用和统计类似的方式,使用静脉对表达式计算做代码生成。对于批量接口,和统计不同的是,表达式的批量计算更容易运用向量化优化,利用CPU的SIMD指令,使计算效率有成倍的提升。但是并不是所有的表达式都能使用一致的向量化优化方法,比如filter = a> 0 AND b <0这类表达式,有短路逻辑,向量化会带来不必要的计算。因此表达式的编译优化需要有更好的codegen 抽象,我们发现Halide能比较好的满足我们的需求.Halide的核心思想:算法描述(做什么ir)和性能优化(怎么做schedule)解耦。种解耦能让我们更灵活的定制优化策略,比如某些场景走向量化,某些场景走普通的代码生成;更进一步,不同计算平台上使用不同的优化策略也成为可能。3.倒排召回算子在寻求算子中,倒排召回是通过QueryExecutor实现的,QueryExecutor的seek是虚函数。例如query = a AND b OR c。QueryExecutor的和或ANDNOT有比较复杂的逻辑,虚函数的开销相对占比没有表达式计算那么大,之前用VTUNE做过预估,求虚函数调用开销占比约10%(数字不一定准确,内联效果没法评估)和精确统计,表达式计算相比,查询的组合空间巨大,寻求的代码生成得更多的考虑对高性价比的查询做编译优化。海选与排序算子在HA3引擎中海选和精排逻辑中有大量比较操作例如排序= + RANK; ID字句,对应的比较函数是秩Compartor和标识Compartor的联合比较.compare的函数调用可被代码生成掉,并且还可和STL算法联合inline.std ::排序使用非在线的补偿函数带来的开销可以参考如下例子:bool myfunction (int i,int j) { return (i<j); }int docCount = 200000;std::random_device rd;std::mt19937_64 mt(rd());std::uniform_int_distribution<int> keyDist(0, 200000);std::vector<int> myvector1;for (int i = 0 ; i < docCount; i++) { myvector1.push_back(keyDist(mt));}std::vector<int> myvector2 = myvector1;std::sort (myvector1.begin(), myvector1.end()); // cost 15.475msstd::sort (myvector2.begin(), myvector2.end(), myfunction); // cost 19.757ms对20瓦特随机数排序,简单的比较直列带来30%的提升。当然在引擎场景,由于比较逻辑复杂,这部分收益可能不会太多。算子的保险丝和代码生成算子的fuse是tensorflow xla编译的核心思想,在全图化场景我们有一些自定义算子也可以运用这个思想,例如特征生成器。FG生成特征的英文模型训练中很重要的一个环节。在线FG是以子图+配置形式描述计算,这部分的代码生成能使数据从索引直接计算到张量上,省去了很多环节中间数据的拷贝。目前这部分的代码生成可以工作参考这篇文章关于编译优化的展望SQL场景全图的编译执行数据库领域全阶段代码生成早被提出并应用,例如Apache的火花作为编译器 ;还有现在比较火的GPU数据库MAPD,把整个执行计划编译成架构无关的中间表示(LLVM IR),借助LLVM编译到不同的目标执行。从实现上看,SQL场景的全图编译执行对全图化引擎还有更多意义,比如可以省去tensorflow算子执行带来的线程切换的开销,可以去除算子间matchdoc传递(matchdoc作为通用的数据布局性能较差)带来的性能损耗。面向异构计算的编译器随着摩尔定律触及天花板,未来异构计算一定是一个热门的领域.SQL大规模数据分析和在线预测就是异构计算可以发挥作用的典型场景,比如分析场景大数据量统计,在线预测场景深度模型大规模并行计算.cpu驱动其他计算平台如gpu fpga,相互配合各自做自己擅长的事情,在未来有可能是常态。这需要为开发人员提供更好的编程接口。全图化引擎已经领先了一步,集成了tensorflow计算框架,天生具备了异构计算的能力。但在编译领域,通用的异构计算编程接口还远未到成熟的地步。工业界和学术界有不少尝试,比如tensorflow的xla编译框架,TVM,Weld等等。借用焊接的概念图表达一下异构计算编译器设计的愿景:让数据分析,深度学习,图像算法等能用统一易用的编程接口充分发挥异构计算平台的算力。总结编译技术已经开始在引擎的用户体验,迭代效率,性能优化中发挥作用,后续会跟着全图化引擎的演进不断发展。能做的事情很多,挑战很大,感兴趣有同学的可以联系我们探讨交流。参考使用带宽优化存储平衡向量化查询执行,第3章有效地编译现代硬件的高效查询计划TensorFlow编译优化策略 - XLAWeld:重新思考数据密集型库之间的接口TVM:用于深度学习的自动化端到端优化编译器本文作者:sance阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

December 3, 2018 · 4 min · jiezi

onorientationchange 事件的兼容性处理

PrefaceWhen I was using orientationchange event, I met a few bugs. So, I take it down.Maincompatibility problemWhen I was testing my code on my android, it was ok. However, it doesn’t work on my boss’s iPhone6. So, i have to change the code.The origin code was like: <div class=“box” id=“box”>html,body { width: 100%; height: 100%;}.box { width: 100%; height: 100%; background: pink;}.box.landscape { background: lightblue;}let box = document.getElementById(‘box’)window.addEventListener(‘orientationchange’, orientationChangeCb)function orientationChangeCb(event) { let isLand = isLandscape() if (isLand) { box.classList.add(’landscape’) } else { box.classList.remove(’landscape’) }}function isLandscape() { if (‘orientation’ in window) { return Math.abs(window.orientation) === 90 } else { return window.innerWidth > window.innerHeight }}To be compatible with iPhone6, I use resize event instead.However, the better way I think is :let eventForOrientationChange = ‘onorientationchange’ in window ? ‘orientationchange’ : ‘resize’if (isMobile()) { window.addEventListener(eventForOrientationChange, orientationChangeCb)}isMobile ?Because onorientationchange is a mobile event, so if you try to run code below on your computer with chrome, you will get:window.onorientationchange //undefined’onorientationchange’ in window //falseIt seems a little weird but it’s true until chrome 69. That’s why I add isMobile() before I use window.addEventListener. In that case, we don’t have to listen for the resize event on desktop.window.orientation or screen.orientationAccording to mdn, window.orientation has been Deprecated. However, similar API screen.orientation has a big problem for compatibility. Safari and QQ doesn’t support. As of 2018.10.4, global support in caniuse is only 72.5%.css onlyIf you just want to update style, you don’t have to use code above. CSS media queriessupport code like:@media (min-height: 680px), screen and (orientation: portrait) { /* …; /}@media (min-height: 680px), screen and (orientation: landscape) { / …; */}Original PostReferencedetect viewport orientation, if orientation is portrait display alert message advising user of instructions ...

November 25, 2018 · 2 min · jiezi

这一年多来,阿里Blink测试体系如何从0走向成熟?

引言Apache Flink是面向数据流处理和批处理的分布式开源计算框架,2016年阿里巴巴引入Flink框架,改造为Blink。2017年,阿里整合了所有流计算产品,决定以Blink引擎为基础,打造一款全球领先的实时计算引擎。当年双11,Blink支持了二十多个事业部/群,同时运行了上千个实时计算job,每秒处理的日志数峰值达到惊人的4.7亿。因此Blink的可靠性和稳定性保障变得极其重要,搜索事业部的质量团队为此专门成立了Blink测试小组,通过一年多的努力,建立了从代码质量到持续集成再到预发测试的全面的测试体系,帮助Blink的质量取得大幅提高。Blink测试平台介绍Blink测试团队为Blink质量量身打造Blink测试平台,内容如下图所示:Blink测试平台包含了三个测试阶段: 代码质量校验阶段,主要进行静态代码扫描、单元测试和基于minicluster的测试;集成测试阶段,主要是进行功能测试、性能测试和带有破坏性的稳定性测试;而预发测试阶段,主要是利用用户的job进行仿真测试,并在版本发布之前做最后的版本兼容性测试。平台选取部分测试集合纳入到precommit的验证中,可尽早发现代码中问题,而大规模的功能、性能、稳定性测试,通常作为dailybuild的集合。另外,Blink测试平台建立了较为完善的质量度量体系,除去对代码覆盖率的统计及变化的分析,还可一键生成测试报告,并对不同版本的质量进行横向对比。代码质量校验阶段代码质量校验阶段是整个Blink质量保障的基础。主要包含单元测试,利用aone提供的"集团代码规约扫描"工具对代码进行规范扫描,单机运行的基于minicluster的集成测试,只有这三个阶段都测试通过后才允许Blink代码提交到项目git。功能测试Blink功能测试框架使用defender,该框架是由pytest[1]改造而来,很好地支持了BlinkSql测试的特性,并支持第三方插件的引入。在测试集群中可以端到端的对某一场景进行精准测试。具体流程如下图所示,支持IDE和Jenkins两种触发模式,yarn_job、yarn_session和local三种case运行调度模式。执行结束后通过web页面或邮件的形式对结果进行展示,并对运行结果进行持久化。具有如下优势:1、case的统一调度与精细化管理:现在Blink在defender上有12个场景4000多个case,可以每天定时进行dailyrun,如果某一类别的case出现问题可单独执行,并可在页面上显示详情。2、case的三种运行模式满足了不同场景的测试需求:其中yarn_session模式对一个模块中存在sqlCase的场景较为适用,可大大减少与Yarn交互的时间。3、case灵活配置:不仅可以支持系统配置,对每个case集所需资源(slot,memory等)或集群其他配置的不同进行单独配置。4、一个case可同时支持批和流两种运行类型。5、client类型灵活扩展:可对现有数据存储和服务进行集成和扩展。现已支持多类型data store读写服务,yarn_session的启动,Blink job交互等。性能测试Blink作为实时大数据处理引擎,其对单位时间内的数据处理能力和数据处理的实时性提出了非常严苛的要求。因此,性能测试是整个Blink测试中非常重要的一环,是衡量Blink新版本能否发布的核心标准之一。Blink的性能测试主要包含Operator性能测试、SQL性能测试和runtime性能测试:Operator指构成SQL语义的一个原子操作,例如Sum,Aggregate等,是一个不能再分割的算子。Operator的性能测试主要用于监控单个算子在整个开发过程中的性能变化,以保证局部处理的优化和提高。目前,Operator的测试分成两个部分:单个算子的性能测试和算子组合的性能测试。Operator测试以Daily Run的方式反馈性能的变化。SQL性能测试主要用于监控版本开发过程中单个SQL的性能变化。TPCH和TPCDS是业界SQL标准性能测试集,分别有22和103个测试用例。测试平台将其引入到Blink性能测试中,以更全面地衡量Blink的性能变化。Runtime性能测试主要为了保障runtime层面性能不回退,主要包含端到端性能测试和模块性能测试。端到端性能测试首先根据梳理出测试场景,关注各场景job在指定数据量下的job运行时间,模块性能测试主要包含网络层性能测试,调度层性能测试,failover性能测试等,更关注在特定场景下job的处理时间。性能测试未来规划是将E2E性能测试、模块级别性能测试和参数调整整体联动起来,使其能够更好协助开发定位性能问题root cause和查看参数调优效果。稳定性测试对于支持高并发、多节点,集群物理环境复杂的分布式系统来说,类似磁盘打满、网络延迟等物理节点的异常很难避免。Blink作为一个高可用的分布式系统,必然要做到在异常情况下也能保证系统的稳定运行及数据的正常产出。“避免失败的最好方法就是不断地失败”,因此,在Blink任务运行期间将可能发生的异常模拟出来,就能够验证Blink的稳定性。我们把异常场景分为两类:一类是"黑猴子",该类场景与运行环境相关,包括机器重启、网络异常、磁盘异常、cpu异常等,这部分异常主要用shell命令来模拟;另一类异常是"白猴子",此类场景与Blink job相关,包括rpc消息超时,task异常,heart beat消息超时等,主要通过byteman[2]软件注入的方式来实现。在稳定性测试中,monkey作为调度会随机选取上述异常场景进行组合,以模拟线上可能出现的所有异常场景。考虑到Blink支持任务failover的特性和稳定性测试的自动运行,我们把稳定性测试设定为一轮轮的迭代循环,每一轮迭代都包含释放出monkey,提交任务,等待job恢复,校验四个阶段,校验主要包含checkpoint,container及slot资源等是否符合预期,校验失败就报警,校验成功后通过后进入下一轮迭代,以验证任务在长时间运行下的任务稳定性。稳定性测试架构分为四层:组件层主要包含测试Blink job,monkeys和dumper;action层包含job启动,状态校验,输出校验等;执行层包含service,monkey操作等,monkey操作时会根据ssh到具体机器,执行monkey操作;最上层是WebUI。详情如下图所示:预发测试Blink预发测试阶段主要通过克隆线上的真实任务和数据来进行复杂业务逻辑和大数据量的测试。因此,Blink 预发测试是对代码质量校验和集成测试的补充以及整个测试流程的完善,是Blink版本发布的最后一道关卡。Blink预发测试主要分为两个部分:仿真测试和兼容性测试。仿真测试仿真测试对Blink的功能、性能和稳定性等基础测试指标进行进一步地衡量,并将开发中的版本与当前的线上版本进行横向比较。因此,仿真测试能够尽早发现各种功能、性能退化和稳定性问题,从而提高上线版本的质量。仿真测试主要分为环境克隆,环境适配和测试运行三个阶段:环境克隆环境克隆是实现整个仿真测试的基础,包括线上任务的挑选、克隆和测试数据的采样。Blink的线上任务分散在多个不同的工程中,数量较多。虽然,每一个线上任务都有其内在的业务逻辑,但是,不同的任务可以根据其主要的处理逻辑进行归类,例如,以Agg操作为主的任务集合,以Sum操作为主的任务集合等,因此,Blink仿真测试需要对线上任务进行甄别,挑选出其中最具有代表性的任务。仿真测试的测试数据集是当前线上任务输入数据的采样,仅在数据规模上有差异,并且,可以根据测试需求的不同进行动态地调节,从而实现对测试目标的精确衡量。环境适配环境适配是仿真测试过程中的初始化阶段,主要进行测试用例的修改,使其能够正常运行。该过程主要包括两个步骤:更改测试数据输入源和测试结果输出地址和更新任务的资源配置。测试运行测试运行是仿真测试流程中的实际执行模块,包括测试用例的运行和结果反馈两个部分。Blink仿真测试包括功能测试、性能测试和稳定性测试等模块,不同的测试模块具有不同的衡量标准和反馈方式。这些测试模块的测试结果与代码质量校验和集成测试的结果一起构成Blink测试的结果集。性能测试和功能测试以仿真任务和采样数据作为输入,对比和分析任务在不同执行引擎上的执行过程和产出。其中,性能测试重点考察执行过程中不同执行引擎对资源的利用率、吞吐量等性能指标。功能测试则将执行的最终结果进行对比。需要特别指出的是,在功能测试中,线上版本的运行结果被假定为真,即当线上版本的执行结果与开发版本的执行结果不同时,认为开发版本的执行存在错误,需要修复开发中引入的错误。稳定性测试重点关注仿真测试任务在线上克隆环境、大数据量和长时间运行条件下的稳定性。其以Blink开发版本作为唯一的执行引擎,通过收集执行过程中的资源利用情况、吞吐量、failover等指标来进行度量。兼容性测试Blink兼容性测试主要用于发现Blink新、旧版本之间的兼容性问题,从而为线上任务升级Blink执行引擎的版本提供依据。目前,兼容性测试主要分为静态检查和动态运行两个阶段,其中,静态检查是整个兼容性测试的基础。静态检查静态检查主要用于分析线上任务在不同执行引擎下生成执行计划的不同,包括两个方面的内容:新的执行引擎生成执行计划的正确性及生成执行计划的时间长短。新、旧版本的执行引擎生成的执行计划是否兼容。在静态检查中,若新的执行引擎不能正确地生成执行计划,或者生成执行计划的时间超出预期,都可以认为静态检查失败,Blink新版本中存在异常或者缺陷,需要查找原因。当新版本能够正确地生成执行计划时,若新、旧版本的执行引擎生成的执行计划不兼容,那么,需要将对比结果反馈给开发人员以判断该执行计划的更改是否符合预期;若执行计划兼容或者执行计划的更改符合预期,则可以直接进行运行时测试。动态运行测试Blink动态运行测试利用仿真测试中的功能测试模块来进行任务的运行,是升级Blink新版本之前的最后一轮测试。若任务能够正常启动且测试结果符合预期,则认为该任务可以自动升级,反之,则需要人工介入进行手动升级。展望通过一年多的努力,Blink整体质量已经有很大幅度的提高,Blink的测试方法和工具也越来越成熟,Blink回馈社区之际,我们会逐步将测试工具一起输出,回馈更多的社区开发测试者,与此同时,随着Blink用户群的壮大,Blink业务开发者对于业务任务的质量保证需要日渐高涨,Blink测试团队未来会提供更多质量保证和开发效率工具,进一步提升Blink开发者工程效率。本文作者:溶月阅读原文本文来自云栖社区合作伙伴“阿里技术”,如需转载请联系原作者。

November 23, 2018 · 1 min · jiezi