阿里云服务-米姆科技官方网站 http://www.51mimu.com 广州米姆信息科技有限公司,深度服务阿里云用户,为用户提供产品及技术支持:云服务器ECS、数据库RDS、网站加速CDN、阿里云邮箱、对象存储OSS、阿里云企业邮箱、WAF防火墙、高防IP、企业短信等产品服务,为用户给予阿里云优惠折扣解决方案、阿里云沙龙及阿里云峰会、 免费云主机试用、阿里云促销优惠活动、代金券优惠等活动,让用户享受米姆服务中心阿里云全栈服务。,在线咨询电话:020-22822863 Mon, 27 Jan 2020 20:58:33 +0800 zh-CN hourly 1 https://www.s-cms.cn/?v=4.7.5 公司文化 Mon, 27 Jan 2020 20:58:33 +0800

目前米姆采用人性化管理及制度综合管理相结合的方式让企业管理效率最大化,目前我公司的管理制度,涉及到安全、流程、认证等各方面,各种制度健全合理,包含部分岗位流程SOP、销售服务准则、职能认证规范及要求等十数种程序文件。从各种制度的执行情况来看,各种制度制定的有效合理,能够有效地给予生产进行指导和员工行为规范的约束


   

米姆核心目标:

米姆立志并已致力成为全球领先的企业数字化转型一站式解决方案和营销一体化的服务提供商。价值观:技术创新、诚信合作、以人为本、服务企业员工:员工相互信任、尊重、积极向上,为员工提供更多的培训机会,鼓励员工技术与管理创新、分享知识与经验,为员工提供一个公平公正的可持续发展平台,为客户提供一个搞效率服务平台。
诚信:铸诚魂 弘商誉品质:品质第一 客户至上创新:持续创新 不断超越分享:整合资源 分享世界
]]>
米姆(MEME)科技简介 Mon, 27 Jan 2020 20:58:33 +0800            

我们初创 却并非新生


    广州米姆信息科技有限公司简称米姆或MEME,伴随着企业上云和产业互联网化的趋势快速发展,米姆立志并已致力成为企业数字化转型一站式解决方案和营销一体化的服务提供商。
我们的核心团队汇聚了来自于阿里、华为和中软等业内领先企业的高端人才,完全具备了理解用户,提供物超所值的解决方案和服务的能力。
    目前,我们已经成为阿里云和华为的生态合作伙伴,为更好地提供数字化解决方案和服务,我们也与相关领域的优秀平台达成战略合作。
    米姆的核心理念在于创新,在于进步,更在于分享。选择我们,就是选择未来!







]]>
【升级】1月14日、16日DDoS高防(新BGP)系统升级通知 Mon, 27 Jan 2020 20:58:33 +0800

【阿里云】【DDos高防(新BGP)】【升级通知】

升级窗口:北京时间 2020年1月14日 00:00 - 06:00、1月16日 00:00 - 06:00

升级内容:DDoS高防(新BGP)机房进行系统升级操作
升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断5次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

给您带来的不便敬请谅解,有任何问题,可随时通过工单联系反馈。

]]>
【漏洞预警】ThinkPHP 6.0 任意文件操作漏洞 Mon, 27 Jan 2020 20:58:33 +0800

2020年1月4日,阿里云应急响应中心监测到ThinkPHP6.0 任意文件操作漏洞。利用该漏洞可以创建或覆盖文件。


漏洞描述

ThinkPHP是一个免费开源的,快速、简单的面向对象的轻量级PHP开发框架。ThinkPHP 6.0中存在逻辑漏洞,在启用session的条件下,攻击者利用该漏洞可以创建或覆盖文件,从而造成 Web 应用拒绝服务,在特殊场景下可能造成getshell。阿里云应急响应中心提醒 ThinkPHP6.0 用户尽快采取安全措施阻止漏洞攻击。


影响版本

ThinkPHP6.0.X < ThinkPHP6.0.2


安全版本

ThinkPHP6.0.X >= ThinkPHP6.0.2


安全建议

升级至安全版本。


相关链接

https://github.com/top-think/framework/commit/1bbe75019ce6c8e0101a6ef73706217e406439f2



我们会关注后续进展,请随时关注官方公告。

如有任何问题,可随时通过工单或服务电话95187联系反馈。

阿里云应急响应中心

2020.01.15

]]>
【漏洞预警】WebLogic高危漏洞预警(CVE-2020-2551、CVE-2020-2546) Mon, 27 Jan 2020 20:58:33 +0800

2020年1月15日,阿里云应急响应中心监测到Oracle官方发布安全公告,披露WebLogic服务器存在多个高危漏洞,包括反序列化等。黑客利用漏洞可能可以远程获取WebLogic服务器权限,风险较大。


漏洞描述

CVE-2020-2551中,未经授权的攻击者可以利用Internet Inter-ORB Protocol(互联网内部对象请求代理协议)从而接管WebLogic服务器。

CVE-2020-2546中,未经授权的攻击者则通过构造T3协议请求,绕过WebLoig的反序列化黑名单,从而接管WebLogic服务器。


漏洞评级

CVE-2020-2551:严重

CVE-2020-2546:严重


影响版本

WebLogic 10.3.6.0.0

WebLogic 12.1.3.0.0

WebLogic 12.2.1.3.0

WebLogic 12.2.1.4.0


安全建议


一、禁用T3协议

如果您不依赖T3协议进行JVM通信,可通过暂时阻断T3协议缓解此漏洞带来的影响。

1. 进入Weblogic控制台,在base_domain配置页面中,进入“安全”选项卡页面,点击“筛选器”,配置筛选器。

2. 在连接筛选器中输入:weblogic.security.net.ConnectionFilterImpl,在连接筛选器规则框中输入:* * 7001 deny t3 t3s。

3. 保存生效(无需重启)。




二、禁止启用IIOP

登陆Weblogic控制台,找到启用IIOP选项,取消勾选,重启生效



三、升级官方补丁


相关链接

https://www.oracle.com/security-alerts/cpujan2020.html



我们会关注后续进展,请随时关注官方公告。

如有任何问题,可随时通过工单或服务电话95187联系反馈。

阿里云应急响应中心

2020.01.15

]]>
考拉技术支持的前世今生 Mon, 27 Jan 2020 20:58:33 +0800 本文来自考拉海购技术支持中心负责人--书渊的分享,想和大家聊一聊考拉技术支持的前世今生,在这个发展历程的介绍当中,大家也可以此对考拉窥一斑而知全豹。当然,既然是聊我们的家常(“黑历史”),我会从这几年在考拉供应链产品事业部的视角去讲述(请轻拍~~),并且,也不会就很多过往事项留恋于细致的介绍,只讲下大面上的东西。

技术支持的由来和定位

技术支持由来

其实电商公司或者说考拉这个 BG ,刚开始成立时是绝不可能就有技术支持这个岗位的。技术支持岗位诞生的前提往往有这么几个条件(满足其中1-2个即可):
1、业务发展迅速,产品对应的业务规模需要得到迅速扩张;
2、产品涉及客诉、咨询相对频繁,虽需要技术解决、解答,但是重复性高;
3、产品研发的人力资源紧张;
4、发展初期的业务、技术职责不清(自营电商躺枪的重灾区);
5、工作内容可复制性高,可沉淀性少,日常太多技术工作是与业务或者各种第三方重复沟通,模式单一,但是每次问题不一(狭义来说不可穷举,广义来说可穷举)。

以上这些原因,可能并不全,但是我想一定基本符合 80% 以上的电商环境下招聘技术支持来解决这些问题的初衷。因为,在电商环境下,产品和研发既要懂业务,又要不断沉淀自己的能力,如果频繁都在做技术咨询解决方案的工作,还有茫茫多的重复性对接工作,没有时间成长,并且,精力分散的情况下,这些技术咨询的应答时效,也是相当没有保证的。

因此,技术支持团队的出现,刚开始其实是为了解决产品研发的效能问题和提高技术咨询的响应效率问题的。而且,产品研发专门负责让大团队高速运转,解决业务的核心痛点、做好核心需求,而技术支持则负责复杂而重复的业务、技术任务,并不断从中挖掘可以提高处理效能的点,可能只提思路,也可能想好了思路设计 PRD,交给研发去实现,当然甚至还自己研发一些功能。

在产品研发和技术支持各司其职的情况下,产品和研发只需要专注于让团队快速支撑业务发展,不再分心于一些细枝末节的设计,即使某些环节的业务SOP并不十分清晰,也可以通过技术支持提供的技术咨询得到较快速的解决,即使业务流程阻断的,或者有新玩法未在原先需求中体现而需要快速支撑的,技术支持可以提供一些简单的解决方案完美过渡,即使部分功能需要小优化和改动的,技术支持会汇总整理成需求给到产品研发。

总的来看,如下图所示,技术支持团队为产品研发团队支持电商业务“野蛮”增长提供了较为充足的柔韧性,因为很多 SOP 及对应的系统设计如果要达到面面俱到的地步,不是说完全不可能,但是会非常费功夫,而这些实际是不影响业务需求的主要达成目标的。

1

技术支持的“黑历史”

前面说的可能相对枯燥了点,那就回归正题——回顾下“黑历史”!

依稀还记得 16 年 1 月刚入职考拉的时候,整个团队内就十几个研发,当时就 2 个研发同学负责订单履行和跨境通关申报,每天既要做系统功能的迭代开发,还要不断开新仓,监控订单履行健康情况,最关键的每天要花半天时间解答和处理客服问题群里的订单长期未发货问题等等。我的到来,让他们特别喜出望外:终于可以安下心来做研发了!当然,那时的我们(技术支持),就两个人,一个主要负责订单履行(主要是申报和问题咨询、处理),一个主要负责仓库 WMS 系统对接、后台理论库存和 WMS 库存的核对以及一些各种和服务商接口、业务相关的问题(当时的第三方服务商的问题排查支持能力相对很弱,经常很多服务商产品设计问题或者使用问题也来找我们,后来慢慢的,我们新开的仓库越来越多,新开的口岸也越来越多,订单的咨询量也越来越多,库存的核对等工作开始变的多起来,刚开始纯靠人工解答、人工 Excel 核对的方式,已经没法满足了,而且手头的事情总是在滚雪球似的增加(因为前面大家也看到了,我们的支持内容,非常和业务运营相关,属于奋战在一线运营的角色,所以没人比我们更清楚细节流程,没人比我们更清楚产品产生的痛点,我们在解答问题的时候慢慢已经能够提供各种解决方案了),并且在极其碎片化的时间里,在客诉咨询解决方面,我们自行设计和搭建了一个订单异常问题自助查询工具,不但自动告知订单当前所处状态,还显示在履行平台上各个节点的时间信息,以及对应当前异常的建议处理方式;而在库存比对方面,团队内自行研发了库存比对功能,极大的提高了每月、半年、全年的库存盘点效率;在订单履行健康度方面,我们自行根据不同需要,也编写了各种自动生成监控报表的IM消息、邮件,甚至也有异常情况自动短信通知服务商处理的功能。

不过,那时的我们,遇到的问题和挑战,远比上面列的多,可能每天会有非常多的和通关相关的税费不一致问题、撤单退税问题、溯源问题、申报合规问题等等,以及和仓库作业衔接相关的可能是订单发货的、订单打标的、包材的、称重重量的、取消异常的、发票的……等等,提货、采购、盘亏盘盈单据伴随的库存不一致问题、判责问题、价格问题、超卖问题、数量问题,还有一系列各种服务商系统、自营系统上的误操作问题……还有仓储物流系统的仓费运费对账、包材费核对、支付对账等等等等,还有追求创新的供应链管理业务同学、类目BU同学的各种新玩法支持。

2017 年以后,考拉开始计划投产 WMS (泰坦)的研发,由于各种组织上的需要,让原先的供应链产品团队中的负责供应链上游及订单履行相关的子团队和当时负责仓配研发的子团队分开形成了两个不同的事业部——供应链产品部和仓配客产品部,为的是让各自更精益于各自擅长的领域,当然,依旧需要保持协同作战的 CP 关系。不过当时跟跨境关务相关的国际头程和港到仓入区、出区申报、账册等功能,就因为这个历史原因,被划分到了 2 个事业部,而当时对跨境产品较为资深的产品经理去到了仓配客产品部,供应链产品部之后再无对跨境模式非常资深的产品专家。历史也非常造化弄人, 17 年之后的几年时间里,跨境行业的变化及其迅速,几乎每小半年就有一个新政策,在此期间,我们的技术支持小团队(大概三四人)一次又一次承担了考拉跨境产品(主要是出区整个流程相关)的产品策划或者核心解决方案策划,成功的项目很多,比较重要的主要是拓展新的海关关区(每个关区流程很多是不一样的)、海关总署三单加签项目、 pop 商家申报合规项目、分销及多平台合规申报、关税对账平台,到后来的全链路监控平台、考拉跨境先知平台、商品备案中心、跨境额度库建设(代付、超限提前拦截等功能的底层载体)等等,还有很多为了解决、优化各种流程中的问题的小项目这里不再一一列举,而且,这只是在跨境这块的产品线上所取得的成绩,在其他领域我们也有不小建树。

对我们来说,一直以来的团队成长和建设思路是——主动补位,不给自己设限。在业务眼中,我们需要成为一名全栈技术工程师,可以解决当前实际问题,可以聊需求和实现需求,可以提供数据分析,也可以当天就给他们实现一些小工具,还能帮他们去跟海关聊业务聊对接,也能帮他们去跟服务商撕逼优化他们产品,还能在操作失误时做好善后措施,当然,我们还有很多功能……

特别值得“哔哔”的是,对自营电商体系来说,太多东西得自己兜着,而量变导致质变,比如下面的一个实际场景来对比菜鸟关务平台和考拉针对某个问题的处理方式:

2

类似上面这种情况的场景有很多,考拉依旧负重前行。

之前我们和业务方沟通的过程中聊的,经常和产品出现职责不清晰的情况,不过这个我觉得本身不属于一个通用的放之四海皆准的一个案例,原则上我们只是做适当补位,不过确实当时真的太难招到一个对跨境十分资深的产品经理了,这是特殊场景之下产生的,并且我们拥有的资源也十分有限——能调动的资源少,还得干成事。但是,实际我们对自己的中心定位从来没有改变过。

技术支持的定位

其实前面说了那么多,大家应该有对我们有基本的了解了——什么样的技术团队,需要技术支持,那技术支持就需要深耕这一块产品技术所涉及的业务。而不管是在考拉也好还是阿里众多 BG、BU 也好,基本上大多是为对应的产品研发部门服务的,我们的核心目标就是帮助好产品研发一起,给业务部门提供最好的服务,帮助业务获得快速增长,不必畏首畏尾考虑太多细枝末节的东西,或者说投入产出比极不科学的东西,这些硬骨头,我们事后慢慢啃,逐步再改善,而这当中缺什么,我们就补什么。

技术支持的发展方向

之前没加入阿里之前,我们对团队内部的人员主要就是分三块:
1、一线主要做重复性更高的客服咨询处理,简单系统运营,他们的主要指标是重复工作量和质量
2、经一线过滤后的综合性较高的问题,由一线小组长 Cover ,并且小组长需要做一些业务沉淀和输出,整理知识库
3、二线主要为我们的解决方案技术支持(虽然我们在考拉的组织架构里没有这样的区分,但是我们在团队内部确实有这样的定位和安排),主要接收业务的一些个性化需求(你懂得,啥都有),可能涉及产品、解决方案或者一些简单的开发支持等等。

每条线的核心考察点或者任职要求在于:

3

后来跟新零售和蚂蚁金服这边的各位较资深的技术支持同学经过了不少沟通,发现其实咱们的定位几乎完全匹配!可以参考下蚂蚁这边对技术支持与保障和解决方案工程师这 2 个技术支持方向的定位。

4
5

我很庆幸当时给团队中的同学定位,以及我们当时的老板给我们的成长空间,与阿里系的技术支持定位十分的一致和准确,即便是现如今,随着考拉技术支持的合并,也并不影响我们的这一初心,合并只是组织架构上和集团更容易步调一致的对标,避免各自为政的现象,但是我们的核心 KPI 依旧需要和各个所服务的业务产技团队保持一致,工作重心没有发生根本性变化,只不过融入大阿里后,技术支持需要和产品研发、测试一起,对产品的质量严要求、高标准,作为价值观一样的存在,留在脑海里,但这不代表我们的主要工作是抓安全生产:

6

目标具体解释:
1、两个核心:奥大制定的以用户体验作为新形势下考拉事业部的唯一KPI,以及范禹强调的保证考拉“安全生产”这两点核心,将是每一个技术支持工程师需要贯彻到脑海里的理念,并作为在日常工作中单独需要升级的一条主线。
2、两手抓:一手抓业务硬核:技术支持既需要在日常的工作中不断去主动理解工作相关的业务(这其中包括业务现状、业务痛点、业务改善方向以及当前的业务变更里程碑),成为业务的好伙伴。

一手抓技术硬核:技术支持也需要在日常的工作中不断提升自己的产品思路和技术解决能力,成为产技的好伙伴。

团队发展及人员成长

在跟考拉前台技术支持组合并前,我们在为考拉供应链产品、跨境关务平台、订单履行中心、商品及库存中心等几个供应链相对核心的领域服务期间,学到了很多交易、跨境以及物流供应链相关的内容,我们团队的业务能力、产品设计能力以及研发能力在这期间也得到了很多锻炼,每一个成员都在忙碌的工作中乐此不疲,至少没有彷徨过,因为我们把路子走广了,没有脱离实际。从我们的团队里也曾走出去过开发、测试、产品,只不过在工作的过程中,发现他们有了新的目标,但是技能都是在这期间得到锻炼的。

过往的缺陷与不足

其实作为一线技术支持来说,一套好的工单系统和知识库平台非常重要,以往我们在网易的时候,太多的沟通方式为拉群沟通和私下沟通,虽然也有简单的工单系统,但是问题点很多:
1、问题分类不科学且无对标
2、问题对应的处理(服务)时效(SLA)没有对焦
3、没有明确的工作流概念,全靠人工升级和转派, backup 对象也靠人工联系

客服的工单系统和技术支持的工单系统,各自为政,客服按自己的理解对各自异常进行了归类,然后遇到这些类别的问题会线下咨询技术支持,技术支持没有很好的问题统计沉淀,也没有很好的工作流,并且处理时效存在很多 GAP ,客服对客户的承诺处理时效和技术支持实际心里理解的问题处理时效完全不对标,导致无谓的问题重复反馈催处理——当然,这个问题主要困扰前台技术支持多一些,原先的我们在供应链时所遇到的问题往往都要棘手的多,大家对时效有较为”宽泛”的预期,在后台供应链里,我们对工单不敏感,但是对每个问题所涉及的业务方,其实是缺少很好的 SOP 以及这些 SOP 对应的小二处理平台。

除了工单系统和小二处理平台以外,我们还经常遇到缺少数据抓手,太多的情形下,业务开发在设计的时候基本只考虑业务实现,但是对技术支持在其中所产生的有益作用(比如异常处理的介入等等)并没有进行合理的埋点,这在我们的每一次季度总结中比较受伤,虽然自己也通过很多手段近似的用脚本去做了这样的统计工作,但是总体来说,效果并不是很理想,至少还难成体系。

不过,加入阿里怀抱后,看到了很多的工具,虽然很多工具我们还没法完全接入,但是至少让我们看到了很多的曙光,我对未来还是期待的。

翻开新篇章

合并后的首要事项,当然是需要做人员的调整和支持内容的融合,因为原先做自营供应链和平台供应链就有存在着不少共性,做供应商直发和做 pop 商家开放平台也存在不少共性,商家商品、库存与自营商品、库存也存在诸多共性,自营履约和 pop 商家履约也是。这是需要做一番最初的整合的(当然,需要假设考拉未来的产品规划依旧保持现状的前提下,未来还是会有诸多调整的,等着拥抱变化,这样带来的好处,一个是让有工作有业务共性的技术支持能够互相帮助互通有无,也让这些同学能在新环境下让自己视野更广阔,了解更多的行业业务背景。

还有就是,可以让我们的团队在考拉更多部门得到更多发声的机会,更多的解决当前的核心痛点,从而让团队得到更良性的发展同时,更好的服务于产技业务团队。

接下来在我们的规划里,一个就是工具的升级引入、流程的制定、服务时效的确认,以及推进小二工具整合(规划中,这个短期来看需要等融合完成后进行了),大致如下所示(这里省略了非客服的业务方提出常规问题和业务问题解决的需求给一二线的场景,红色虚线为我们未来规划里想提的内容):

7

我们一起期待下融合后的小二工作台搭建吧。

作者信息:书渊,考拉海购技术支持中心负责人,多年供应链产品及跨境关务业务解决方案经验,现主要参与考拉交易、库存、商品、订单履约、跨境关务等多个平台的技术咨询以及“考拉融阿”的技术支持体系改革等工作。

]]>
阿里巴巴高级算法专家威视:组建技术团队的一些思考 Mon, 27 Jan 2020 20:58:33 +0800 本文是我从2019年1月底接手CRO线NLP算法团队以来,在团队组建、能力建设、以及管理上的一些思考,全部是没有科学论证的主观判断,不过都进行了实践。我没有任何管理学背景知识,把拙见没羞没臊地写出来,是为了抛砖引玉,和同学们交流讨论。

团队的定位是什么?——做正确的事

定位

团队的定位是重要的事情之一,有了偏差,后续做得越多错得越多。确定团队的定位花了我很长时间,中间还发生了一次组织变化,和两任主管有多次讨论。
首先,这个团队配置在CRO线,肯定要为风险管理业务服务;同时,这又是一支能力团队,还要考虑和业务团队的协同关系。最终,我确定了3点:
1)能力建设为主,同时也需要有业务抓手
2)不做业务团队已经做得好的事情
3)立志高远,勇攀高峰,要做就做到最好

壁垒与价值

不夸张地说,现在是NLP领域的大航海时代,新算法层出不穷,日新月异,后浪各种把前浪碾死在沙滩上。尤其是BERT横空出世之后,整个NLP的研究范式都发生了变化,从原来的 task-specific的模型结构设计 转变到 语言模型pretrain+下游任务finetuning的模式,预训练模型是含金量高的工作。研究一下预训练模型,你会发现这是个需要海量金钱+数据+技术才能玩的游戏。
这就陷入两难:如果搞预训练模型,没那么多资源;如果不搞,用开源模型做下游任务,实际上很难有什么技术壁垒。其实就算你真的搞出什么新算法可以充当技术壁垒,可能两三个月之后就又有人做出了更强的结果。
困境的根源在于,目前NLP算法这个领域发展速度太快了,在高速变化的领域是很难形成壁垒的。所以,我们需要结合自身所处的环境,寻找变化不那么快的东西。
我经过好多天的考虑之后,认为沉淀风险管控知识可以作为壁垒。原因:1)风险知识随时间有变化,但速度明显比算法慢很多;2)CRO线在这方面有一定积累,也需要用于实际业务管控,并沉淀到产品。
所以,团队的宗旨我定义为:基于知识驱动的NLP算法团队。为CRO线乃至集团沉淀风险知识,并提供不同层次的服务:
1
图-4层服务体系

最近,CRO线在清华举办了AI与安全研讨会,会上张钹院士谈到了第三代人工智能,尤其强调了其中知识的核心作用;我们走访中科院信工所,对方的宗旨也是建设基于知识驱动的算法,和业界发生的共鸣,更坚定了我们走这条道路的决心和信心。

团队需要什么能力?

先要搞清楚团队所处的环境。

阿里是一个什么结构的组织?

有人说是矩阵式的,有人说是树+网状的,我不知道确切的答案。不过,如果把每个小团队看作节点的话,有2点是确定的:
1)体量巨大,各种节点(业务、产品、工程、算法)种类繁多数量大。
2)单元节点 之间比较容易发生跨大团队甚至跨BU的联系,条件合适可以发生协同关系。
继续观察,你会发现:
1)你所需要的一切资源几乎都能找到提供者,而且往往不止一个。
2)你也可以给各种需求节点 提供服务,只要还在你的能力范围内。
3)由于规模巨大,需求节点 和 资源节点 相互之间往往不知道对方在哪。
基于以上,我认为一个身处中台的算法团队,需要具备4项能力:连接-生产-传播-服务。
2
图-能力中台的算法团队需要具备的能力

四项能力

1,连接
就是寻找到自己所需要的资源,筛选出其中最优的,建立长期稳定的合作关系。比如算法团队需要的爬虫、标注工具、分布式模型训练工具、模型的评测工具等,都能在公司范围内获取,就没必要刀耕火种从头开始自己建设了。
2,生产
这是传统意义上算法工程师的工作,指获取数据后产出效果和效率达标的算法模型,并上线。
对算法的要求,主管的主管早有论述:算法要全!算法要强!算法要快!算法要便宜!精炼简洁,振聋发聩,细化一下就有:
3
图-对算法的要求
3,传播
针对中台的算法团队提的要求,因为你需要让目标业务节点知道你的存在,知道你的能力项,以及细节。ATA是个不错的对内传播途径,之前在CV团队时,很多业务方是通过ATA找到我的。
4,服务
如果是专属某业务的算法团队,只需要考虑该业务下的SLA即可;如果是中台的算法团队,还需要考虑如何满足不同业务节点的需求,同时又不至于做开发和维护成本很高的个性化定制,避免随着接入业务的增长各种资源的开销也随着线性增长。

能力雷达图

团队的能力雷达图是由成员的个人能力长板组成的。(话越短意思越长)
4
图-团队能力雷达图

组织与个人的关系

为什么需要一个组织呢?

先从一个现象出发:在小区业主和物业公司的纠纷斗争中,业主获胜的概率很低。从人数、个体的教育背景和素质来看,业主都占据绝对优势,可是为什么会输?
粗略分析,大体有三个原因:
1)共同利益,目标明确。物业的目标非常清晰,就是为了从业主那里赚钱,这也是物业人员的共同利益。业主人多,情况各有不同,各自的利益诉求差异大,容易被分化。
2)组织严密,相互协同。物业内部有明确的分工,平时也长期一起工作,相互间有信任感,能够进行配合。业主彼此之间往往是陌生人,缺乏信任感,难以统一行动,是原子化的散点。
3)局部相对优势。相对单个业主,物业具有明显力量优势。比如,物业有资金,而业主因为缺乏信任很难筹措资金。
好,看出来了,小规模组织的力量可以超过大规模原子化散点存在的个体集合的力量。

组织为个人提供什么?

1)组织能使资源增效
组织能把各种资源组合成有机的整体,使各种分散的力量形成合力,从而产生大于这些资源和力量机械总和的效能。这个原理2000多年前亚里斯多德就论述过了,后来马克思又更严密地论述过一次。
2)组织是实现目标的依托
个人可以依托组织的能力和资源去做事,而组织的能力和资源远大于个人,所以依托组织的时候,个人能够实现比单打独斗模式大得多的目标。
举个例子,我们团队做了UGC场景效果超越开源模型的预训练模型,每个同学都可以在这个预训练模型的基础上去做下游的有监督学习任务,起点就比别人高。

个人为组织提供什么?

是否有短板不是那么重要,关键是要有长板,能够对组织的能力雷达图做贡献。
这一节留一个思考题:公司设置主管这个职位的目的是什么?是为了像幼儿园阿姨那样,保障每一个小朋友都有自己喜欢的玩具,高高兴兴上学来,平平安安回家去?

招聘团队需要的人才

招聘为什么特别重要?

世界有个普遍规律:在前序阶段做严格的控制会大大降低后序阶段的实现难度,比如数据标注、写代码、模型中的预处理等等。人招进来之后是要用要管的,招聘的时候高标准严要求,后续管理会轻松很多;如果降低标准甚至放水,后续管理付出的代价远远高于招聘时偷懒省的功夫。
所以,我花了至少1/3的时间在招聘上。对的,至少1/3,你没有看错。从2019年2月到现在,社招弄了300+份简历;校招100+份简历。在这里特别要感谢团队里球夫、天逸、开阳3位同学,牺牲了大量业余时间做简历评估和初面。从统计数据看,每100份简历产生2-3个offer,入职1-2个人。从100份简历中招来的人绝对比10份简历中招来的省心很多。
我给算法团队找来过不少人,both 社招 and 校招,具体的展开讨论见彩蛋部分的【招聘】

招聘要考察哪些能力?

招聘,首先要确定job model。限于篇幅,这里只讨论“生产”环节所需要的能力。
所处的时代背景:快速变化,新算法层出不穷。
不变的是什么:数学基础、计算机基础、动手能力。
我们很难预测新技术的具体实现,但是当新技术出现的时候,需要能够快速地分析、学习、掌握。而且,我们经常要解决从来没有遇到过的新问题,这就要求候选人在面对没见过的问题时具备分析判断,在具体约束条件下找完整解决方案的能力。另外,在复杂的业务场景里,问题经常没有确定性的答案,我们往往通过对过程的合理性来判断整个方案是否合乎要求。寻找答案的过程很少有一帆风顺的,大概率会遭遇挫折,非常需要候选人不断尝试不断修正去抵达终点。
至于教育背景、之前从业经历,反而不怎么重要。我不赞成对于毕业好几年的社招候选人还要参考毕业学校、最高学位,甚至本科学校是否985——如果一个人能力强,是不需要靠学校学位来旁证的,直接用行动证明就行了。
对于候选人的考察,我往往从基础的硬技能、创新性/开放性思维、精神素质三方面考察。

硬技能

数学:概率论与数理统计、矩阵论、随机过程
计算机基础:操作系统、组成原理、数据结构
算法能力:领域内主流模型的演进,优缺点对比;在具体设定的场景下选择合适的方案
动手:C++/python/Java (什么?你说matlab?工业界里这个不算编程语言)
有人说,面试过程中要求做代码测试,就像相亲时要求看存款证明一样残暴。我赞同这个说法,因为不少候选人听到要写代码就高傲地拒绝了。我给大家推荐一个在线代码测试工具:http://collabedit.com
从我长期的观察情况看,发展得好的算法同学,动手能力都比较强。毕竟,算法工程师,首先是一个工程师。

创新性/开放性思维

其实我还经常干比代码测试更令人发指的事情——做智力题。这个不是我的创新,是跟google等公司学来的,而且是直接找网上流传的面试题换个马甲来用。
前面的硬技能,看的往往是结果;这里对思考能力的考察,看的是过程:是否有方法论,思路是否清晰,是否言之有据。所以,这种问题的面试方式往往是讨论式。如果候选人能够完成,最后再请TA做个总结,观察归纳要点的能力,视线的高度。有些候选人结束面试后仍然会继续思考,给出更好的回答。

精神素质

公司对人才的要求是:乐观、皮实、聪明、自省。
你看,4个词里面有2个都在强调坚韧不拔。在面试过程中,我会看候选人在解题不顺时的表现,有时甚至故意小刺激一下观察候选人的反应,偶尔还会故意中途改变限制条件。阿里内部竞争激烈,经常需要拥抱变化,如果心理承受力脆弱,是不适合当同路人的。
还有一点很重要:自我驱动力。这是从降低对内管理成本来要求的,后面会具体说。
在我看来,硬技能、创新性/开放性思维和精神素质 缺一不可。即使这三方面我都满意了,如果主管,主管的主管,HR对候选人明确提出疑虑,我一般不申辩直接放弃掉。因为,他们比我level高,阅人无数,往往不会错。
有的同学会问:这样子做,会不会错失优秀人才?是的,我的方式几乎可以确保招进来的同学肯定是好的,但会漏掉一些优秀的候选人,不过这不会造成严重的后果。相比之下,招进来不合格的人才会有大麻烦。

用人

主管的角色是什么?

以前有句话,叫做“火车跑得快,全靠车头带”,这说的是前动车时代。动车和高铁为什么比传统的火车速度更快?根本原因是:大多数车厢都能提供动力。
同样的,如果一个团队完全靠主管来驱动,来提供动力,主管很容易成为团队的瓶颈。我的团队成员,很多都是自己领域的高手,专业能力在我之上,我就应该顺应实际情况,不要拿自己的愚见去束缚同学们的发挥。因此,我的角色更多的是眺望远方,掌握方向盘,有时踩一下刹车;团队大多数同学一起构成动力引擎。
5
图-动车/高铁跑得快,是因为大多数车厢都提供动力

对内管理模式

一个不恰当的比喻:放羊。
这么做,堂而皇之的理由是“因为信任,所以简单”。技术层面的原因,对算法类同学做过程管理性价比太低。
算法类工作,创造性在其中占据重要地位,而创造性很难在过程中量化度量,也很难从外部观测现象来判断。比如:身边的同学坐在工位直视屏幕目不转睛,我不知道他到底是在思考论文中的公式 还是在回味昨天晚上看的电影。再比如,我base在杭州,没办法知道团队内base北京的同学是不是在工作时间打游戏。
所以,我选择信任我的同学,只在一些必须监管的事项上把关,比如数据安全、安全生产等,其他事项一般不做过程管理,只做结果管理。得益于招聘时把关严格,绝大多数同学的自我驱动力都比较强,我并不用操心偷懒的事情;相反,偶尔需要操心一下少部分同学拚过了头的问题。关于这一点,更多的内容见彩蛋中的【认真工作,快乐生活】。
肯定有同学问:上面说的是不担心出工不出力,那么,怎么出力的问题呢?你难道不指导同学做项目吗?
我一般只给出项目的目标,有时给一个粗略的方案设想,有时不给。公司对于P6同学已经有“独当一面拿结果” 的要求,大家都应该具备独立作战的能力。而且,按照前面说的,团队内大部分同学都应该是提供动力的车厢,没必要依赖我。人是否有自我意志?这个问题我不知道答案。但我知道,如果一个人认为主意是自己想出来的,决定是自己做的,会更有动力去实现。尝试做决策,尝试完成不确定的任务,都有利于自己的成长。
思考题:管理有很多种style。有的主管喜欢自己做需求分析,然后拆解细化到原子级的技术问题,让下属做执行。这种模式,和“放羊”模式相比,从主管视角,以及下属视角看,各有什么优缺点?
综合以上两点,我觉得放羊是可行的。而且,放羊这件事,羊倌也是要做很多工作的:选择合适的天气,找到草地,把羊群带到草地,放哨保护羊群,是不是?如果还要把青草割好喂给羊吃,那成什么了?
羊倌应该把更多的精力花在 寻找丰美的草地,购买强壮的羊,与其他羊倌交流 这些事情上,要是成天忙于喂羊,督促偷懒的羊快点吃草,拉开打架的羊 这些内部事务,羊群怎么发展壮大?更多的展开见彩蛋中的【因为信任,所以简单】。
鼓舞团队信心,最好的方式是什么?
痛痛快快地赢一次。如果不够,就两次。
接手团队的时候,在商业化方向上局势是很差的:去年三次PK竞品都输了,稳定性问题频发以至于新版本都无法发布……团队好几个人都扑在这一个阵地上干得很苦但就是拿不到结果。
这个时候我要是去发表个类似《至暗时刻》里丘吉尔那样让人热血沸腾的演讲是否可以解决问题?可能有短暂的强心剂作用,但是不长久,因为实际困难没解决。何况,我也肯定不具备丘吉尔的演说能力。最有用的办法,还是分析失败的原因,制定正确的打法,指导同学们获得一次成功。鼓舞信心最好的方式还是靠实实在在的成功。
结果大家都看到了,今年我们PK竞品的战绩是N:0,付费调用量上涨25倍以上。大家肯定好奇:正确的打法是什么样的呢?我放在后面《正确地做事》那一节讲。

做有吸引力的事情

目标要定得高一些,有挑战性,达成的时候内心的成就感会更高一些。这个很容易理解,就好比你打游戏,虐了个菜,没多少快感;如果能赢下之前屡战屡败的对手,一定会兴奋很久。我跟部分同学说过,大家的眼光不要局限在三号楼,也不要局限在聚橙路,而是要放眼世界。
目标定得太低,不仅不能逼出自己的潜力,还容易让自己关注于一些鸡毛蒜皮的小问题。
前几天,我的主管在一个项目kick off会上说,当你回首往事时,要有一件做过的事情能够拿出来吹牛逼,人生才有意义。深以为然。

过程即享受

在阿里的工作肯定是辛苦的,我没看到过谁能随随便便就成功。如果只是冲着收入来做工作,难免在过程中会感觉到很多痛苦。物质的刺激是短暂的,不管是加薪、年终奖,或者option,兴奋高兴个几天就过去了。如果喜欢自己做的事情,专注于工作本身,从中源源不断地获得成就感,就能做到虽然辛苦但是不痛苦。
我家做饭的阿姨是拆迁户,坐拥N套房,每天仍然跑几家做饭,我问她为什么?她说,以前是开苍蝇馆子的,拆迁后没得开了,但是自己就是喜欢做饭。理想状况下就是要招聘这种人。
采取什么工作模式?——正确地做事

四个在线化

互联网的本质是连接,最大价值也是连接。
这句话不知道是谁说的,第一次听说是在《计算机网络》课程上。互联网连接的可以是人和人,人和文档,人和数据,人和代码,人和……和一切你工作中需要的东西。
接手团队之后,我发现同学们的工作模式真的是自耕农一般:各做各的模型,各用各的数据,各读各的paper,完全是原子化的散点存在。说得不客气一点,除了聚餐的时候,平时感觉不到这是一个组织。也就是说,身处中国顶级互联网公司,大家却像农业社会时期一样在进行生产,当着不折不扣的“码农”。
团队里一个同学说得很好:相互间建立信任关系的最好办法是发生工作上的协同。我觉得,要发生工作上的协同,前提就是把工作相关的资源都在线化,与组织成员发生连接,于是,我设想做4个在线化。

6
图-文档、数据、代码、评测在线化
1,文档在线化
春节期间我建了个团队语雀,自己做顶层设计,写好框架,然后让同学们把业务、技术、资源、技术影响力等等和工作相关的内容都填写其中。这样子,每个同学都可以看到团队的各种信息和资源,以及其他人的工作。目前团队的语雀还对部分关联紧密的兄弟团队完全开放。
2,数据在线化
如果同学们各自管理自己的数据,形成数据孤岛不说,发生机器重装,或者转岗、离职,往往数据就丢了。接手的时候,能清理出来的有标签数据远远低于应有的数量,就是因为一直没有做数据的在线化管理。团队里的言奇同学做了样本大表项目,已经完成了将整个智能认知团队的全面标签数据在线化。这一点非常重要,后续在开发各种新模型,以及做预训练模型时,就拥有不同业务不同场景不同风险的大量数据,在短时间内取得了良好的效果。
3,代码在线化
这个正在进行中,预期S2结束时完成,出发点是:
1)代码是团队重要的技术资产,应该统一管理,提高安全性。
2)在线化后方便团队协作,共享优秀代码
3)基础性模块代码统一,降低维护成本
4,评测在线化
也在进行中,设想是在一些特定任务上做几种经典模型和确认无误的主流模型,能够一键实现自己的模型和前者的自动化比对,提升工作效率。除此之外还有个作用:经典模型的结果可以作为baseline,帮助验证深度模型的正确性。因为,你做了一个深度模型,效果好也就罢了,效果不好的时候都搞不清楚是模型不适用,还是自己的代码写错了。

找对前进的方向

主管最重要的职责之一是当同学们迷茫的时候明确前进的方向。
接着前面商业化的例子,详细情况是这样的:我们通过阿里云对外输出文本风险识别的算法能力做商业化,比如涉政、色情低俗、广告、辱骂等。我接手的时候,有3-4个同学全职投入这项工作,他们工作非常努力,干得也很辛苦,但是效果并不好,PK竞品的时候并无胜算。出了什么问题呢?
分析之后,我发现以下问题:
1)确实是一个内容维度的问题,但只使用了分类模型一种方式。
分类模型适合解决静态标准的问题,并不适合及时响应业务上的快速变化。模型迭代更新的速度做到极限也只能是T+1或者T+2天,且人力消耗高。之前的主管为了解决这个问题,在分类模型中塞了一个风险词包,由算法同学维护更新,接到运营反馈的badcase之后手动添加到风险词包,然后定时推送到分类模型应用中。这个复杂的机制带来了词典的频繁构建,结果导致应用的稳定性问题频发,甚至已经无法更新。
2)缺乏顶层设计,同学们各自为战。
几个风险各自单独做模型,技术选型高度自由,百花齐放,starspace、SVM、CRF、kenlm、textCNN都有,难以统一提升能力,维护的难度大。
3)做了过多的个性化定制,导致后续维护和升级的成本非常高。
几乎为每一个稍微大一点的用户都单独做了模型,付费调用量不大,模型倒是有了好几十个。同学们频繁地做模型的迭代更新(每周都至少有1-2次),占用大量人力。
怎么办呢?
建设技术体系去解决某一类问题,而不是某个技术点去解决某一个问题;
结合安全业务的特点,设计可以强化通用算法效果的基础能力或处理框架。
——上面两句话不是我说的,来源于前主管。(插一句:本文还有一些内容来源于前主管和主管。向主管学习是提升自己的一个重要途径)
具体来说,解法有几点:
1)明确 风险词包、相似性检索、分类模型、风险知识图谱 4种手段 适合完成的任务,且相互配合。
2)把风险词包从分类模型中拆出来,降低应用的复杂度,以及模型迭代的频率,解决稳定性问题。
3)分类模型的结构尽可能统一,标准尽可能不变,持续把效果做强。
4)自从 BERT 提出以来,NLP问题的基本范式 从原来的 task-specific的模型结构设计 转变到 语言模型pretrain+下游任务finetuning的模式。工作重点应该转向预训练模型与知识蒸馏。
由于目前对内的内容交互风险管控业务也在我的团队内,我就贴一张全局视角的问题分析与解决方案。
7
图-全局视角的UGC风险管控思路

明确解法之后,同学们快速做了实践,到4月份就基本扭转了被动的局面,随后打了翻身仗,付费调用量增长25倍。现在模型的更新周期降低到以月为周期,稳定性大幅度提升,同学们也不再疲于奔命;而且,投入的人力也明显下降了。

绩效的考核

绩效考核决定了收益的分配,也是团队最重要的事情之一。
如果把团队比作一个模型,考核的标准就是loss function。loss function一旦确定,模型的优化方向也就定了,团队成员会按照利益最大化原则沿着这个方向调整自己的action。
所以,考核标准的设计需要体现团队的定位、价值和需求;在执行的过程中需要满足平等性。
8

图-绩效考核的3个维度

业务结果

阿里有个文雅的说法是:为过程鼓掌,为结果付酬。
还有个话糙理不糙的说法是:没有过程的结果是垃圾,没有结果的过程是放屁。
你觉得哪一句对你的胃口就看哪一句。
配置在业务BU的算法团队,帮助业务目标达成肯定是首要任务。今年以来,AI行业也都渐渐挤出泡沫,回归本质,开始强调创造业务价值了。

能力进步

从价值观上讲,今天的最好表现是明天的最低要求。
从业务需求讲,量级越来越大,业务形态越来越复杂,老算法是解决不了新问题的。
从团队利益讲,成员的能力进步可以扩展团队的能力雷达图。
参加百阿的时候,一位讲师的发言我到现在都记得:在座的各位最终都是要离开阿里的,离开的时候无非两种情况:1,公司不要你了;2,你不要公司了。怎么离开,取决于是你的能力提高快,还是公司对能力的要求提高快。
技术影响力
什么是技术影响力?
有形物:Paper、竞赛成绩、著作、专利、ATA文章等。
无形物:对内对外合作、对外PR、对内分享、组织机构任职、参会做报告等。

为什么要建设技术影响力?

1)团队的四项基本能力:连接、生产、传播、服务,其中“传播”就需要技术影响力。
2)CRO线的使命“四心”中,有一条叫“让监管单位放心”。技术影响力是让监管放心的有效方式之一。
3)商业化需要资质:搞过投标的同学都知道。
4)招聘需要名气:对候选人讲解我们的技术水平时,如果用内部业务举例,不容易产生共鸣;但是如果直接亮出顶会论文、刷榜名次之类的,对方马上就懂了。
5)个人的市场价值需要证明:这些东西都可以作为个人技术品牌,到哪都能带着。
假定一个场景,有人问:“你说这个业务做得好,说明你的算法水平高。会不会换一个人能够做得更好?”你打算怎么回答这个问题?
再假定一个场景,你打算给自己团队的算法能力定性为“xx领先”或者“xx第一梯队”,如果没有硬核的技术影响力做支撑,是否还能理直气壮?

平等性

团队协作的基础是团结,团结的基础是平等。
平等性最重要的体现,就是在考核过程中尽可能只衡量以上三项,不去考虑地域、教育背景、从业经历、之前表现、颜值、性别、个人动向等等其他因素。我认为:结果体现的就是能力,直截了当,最能服众。
当然,这个世界上是否有完全客观的判断?或者,完全客观的判断如果存在,是不是就是最合适的?我不知道答案。不过,我觉得不能因为做不到完全的平等而放弃追求平等。

广告

按照惯例,it's the time for employment ad。我们需要研究这些领域:
1)安全场景特有的问题
1.1)无限制条件下的攻击与防御
现实世界里,违规者的变形变异方法是不受约束的,思路非常广,让人防不胜防。我们需要研究无限制条件下的攻击与防御以提升我们对于业务(尤其是信息治理 与 商品)中层出不穷的变形变异,这属于核心能力。
1.2)模型的可解释性
是AI的一大发展趋势。我们作为安全AI,这张牌更需要打出来,体现安全领域的特色;也需要给业务同学提供人可以理解的原因,放心地做决策。
1.3)小样本学习
安全场景经常因为不可抗力不能搜集到足够的样本,或者长尾风险因为成本的原因无法去做样本收集,必须要发展只凭借少量样本快速获取“够用”的模型的手段。
2)预训练与知识蒸馏
自从 BERT 提出以来,NLP问题的基本研究范式 从原来的 task-specific的模型结构设计 转变到 语言模型预训练+下游任务微调的模式。这是历史必然的趋势,不可扭转。
2.1)预训练
预训练模型自身的提高,可以带来 分类、检索、NER等NLP基本任务(这些都是我们需要具备的核心能力)的水平提高,且有利于NLP技术体系的统一化。可以把我们的各种应用算法能力比喻为船,预训练模型是水,水涨就能船高。
2.2)知识蒸馏
由于目前还存在模型复杂度和计算资源的尖锐矛盾,对于我们这种业务量动辄十几亿的情况,模型的计算效率具有非常重要的财务意义。如何在尽可能保障效果的前提下降低计算资源的消耗具有非常现实的意义。
3)更基础的数据科学
3.1)对数据规律的发现与分析
3.2)弱监督/半监督学习
3.3)噪声数据的处理
现在有一种倾向是把重心放在模型的优化上,尝试变换各种模型来提升效果,但对数据本身的分析往往是缺少的,没有耐心去分析badcase,挖掘数据规律。我们实际业务中遇到的数据虽然量很大,但质量并不高,如果能有比较高的数据敏感性,分析数据的规律,可能并不需要太复杂的模型,只需要根据数据规律进行有针对性的调整采样,就能够提升算法表现以及训练效率。
3.4)迁移学习
包括同种语言内 不同domain语料之间的迁移;以及多语言之间的迁移。
__
我们的工作很有挑战,也很辛苦,确定自己想来再钉钉我,北京杭州两地可选。

彩蛋

感谢你耐心地坚持看到这里,作为奖励,我故意把正文里一些重要内容放在了这里,尤其是像招聘这种主管的核心技能。

招聘

招聘是主管的基本功;招聘做好了,团队已经成功了一半。
之前在CV团队,不光给自己招,也给几乎所有兄弟团队招,很高兴地看到这些同学已经在建功立业了。先摆一下这几年来招聘任务的量:
9
图-几年来累积的招聘任务数量
大家肯定会问:这么多简历,哪来的?
我所在的BU在外知名度还不算高,很少能收到主动投递的简历,所谓的合作猎头从来就没起到过作用,只能自己想办法。来源包括:1)采蜜;2)社交网站;3)内推;4)去各种会上收。希望以后团队的对外影响力越来越大,我能够躺着被简历淹死。
再掰开了说,以采蜜为例,就有很多苦操作,比如之前社招简历是每天夜里12点超期释放,就天天蹲守;校招公海池是每天晚上8点开闸,就要天天拚手速;还有候选人被其他BU持有不放,就各种协调乃至扯皮…… 举个例子,大团队内有位同学,当初我是在出差路上,出租车上挂上VPN抢到的简历。
在招聘中,我发现一个现象:当你只接触过百级别的候选人时,不容易招到人;但是如果你接触过千级别的候选人,招聘的难度明显下降了。原因大约是,在广撒网的过程中积累了一个高可能性的候选人池子,池子的规模大到一定程度,产生合适的候选人就不那么难了。

关于“嫡系”

从CV团队换到NLP团队接手的时候,HR询问是否需要从原团队带一个人到新团队。
我考虑之后答复不带。因为,我需要快速地和新团队的同学们建立信任感,并且全心全意地依靠他们。如果我带一个同学过去,新团队的同学怎么看?他们会在心里说:看,那个人是主管带过来的,是亲信。这样很容易在我和同学之间产生距离与疑虑。同样的,原来团队的同学会想:看,主管带走的那个人是他最信任的,我不是。
团队的持续发展需要海纳百川,搞出类似“嫡系”和“非嫡系”这种分群,长久来看是会制约团队能达到的的高度,也会制约主管的高度。

因为信任,所以简单

除了出勤之类,在工作成果的真实性上主管也只能选择信任下属,原因很简单:如果10个下属每周报告的结果,主管都去亲手验证一遍,可能一个星期还不够。
但是,信任同时也是很容易被打破的——一旦主管发现下属报告的结果有故意虚报浮夸的部分,后续就很难再默认信任TA。类似的,某些人的周报总是花团锦簇高歌猛进,但是一年下来模型的效果效率并没有提升,大家只能选择默认不相信。
前面说的都是主管要信任下属,大家思考一个问题:下属是否需要默认信任主管呢?为什么?

认真生活,快乐工作

阿里有句老话:加班是对的,不加班也是对的,只有不完成工作是不对的。
我从内心里不喜欢长时间高强度加班。为了赶一下进度,阶段性地温和加班一下,是可以接受的。如果长期需要高强度加班,那一定是主管的计划、评估或者安排出了问题。最近刚和2个同学聊过,因为他们回家太晚或者周末来公司加班,我担心是自己有什么做错了。
当然了,这一点我自己做得也不好,尤其是接手初期百废待兴的时候,经常工作到很晚,周末做几个面试。前些天做新六脉神剑培训,传橙官说得特别好:“认真生活,快乐工作”这一条,主管要以身作则,带头做好——如果你自己都苦哈哈的,下面的同学怎么快乐得起来?深以为然,于是我打算坚决落实领导指示,定个小目标:每周三天按时下班,目前已经坚持了三个星期。

参考文献

我是吴军的粉,在这里推荐几本他的著作:
《见识》《态度》《格局》《浪潮之巅》《全球科技通识》
本文中一些内容的源头来自以上书籍。
有些书我给团队内的同学送过,不过大多数人都没有看,这件事情我违背了自发性原则。

作者信息:
张荣,花名威视,现任职阿里巴巴 CRO 线 NLP 算法团队 leader ,长期聚焦于 NLP 、图像识别、视频分析算法领域,面向整个经济体提供信息治理、数据安全、商品、人机等多个方向的算法能力,并通过商业化服务于社会。目前致力于基于风险知识的 NLP 算法建设。

]]>
“国货之光” 完美日记的微服务实践和优化思路 Mon, 27 Jan 2020 20:58:33 +0800 lADPDgQ9rVfPg37NArPNBMs_1227_691_jpg_620x10000q90g

如果你是一位程序媛,你一定知道完美日记。
如果你是一位程序员,你的那个她一定知道完美日记。

今年双11,完美日记仅用28分钟就超过了2018年双11全天的销售额,成为第一个登上天猫双11彩妆榜首的国货品牌。在这个遍地都是漂亮小姐姐、号称男人(特指程序员)天堂的公司里,拥有着一支什么样的基础架构技术团队,他们是如何在 4 个月内筹建、上线电商平台的呢?本文将为您分享他们在实践微服务过程遇到的难点和优化思路。

完美日记基础架构技术团队欢迎您的加入,移步文末,了解详情。

起步

自建商城在设计之初,业务部门就提出了两个要求:不崩 & 快速上线。

在立项之后,团队还没有完全配备好,一边从其他团队里调取人手,一边大力招聘,与此同时,我们的架构师也在搭建一套分布式商城开发框架,编写 Demo,让新加入的同学能快速上手。

暴露问题

问题一:分布式事务
为什么会使用分布式事务?

这个暂且可以归因于快速上线,因为生成订单会调用到商品服务扣减库存,使用了分布式事务解决了因为跨服务调用引起库存超卖的问题,带来的问题就是性能上的消耗。

问题二:数据库压力
在大促活动期间,有个实时统计是直接从业务库上直接查询统计的,运营部门的小姐姐在不断地刷新,导致该接口上的压力山大,而且没有使用缓存,连 SQL 查询条件的时间都是动态的,导致 DB 层的缓存也使用不上,每次请求都打到 DB 上。

开发和测试环境是使用自建的 MySQL,生产环境使用的是 PolarDB,从阿里云官网上看到:

  • 集群架构,计算与存储分离
  • 读写分离

我们主观地认为,只要我们使用了集群连接地址就会自动进行读写分离,但是实际上并没有,后来发现在方法上显式的指定只读事务就有请求走到只读节点上了。
@Transactional(readOnly = true)

# 优化思路:
1)从 SQL 洞察和慢 SQL 里找调用响应时间最长和频度最高的 SQL;
2)结合代码,能用缓存代替的直接处理掉,不用能缓存的优化查询,结合阿里云提供的优化分析工具,调整索引;
3)活动高峰时段,禁止分析统计类的查询执行,临时改代码已经来不及了,幸亏 AHAS(阿里云的一款限流降级产品) 的接口限流和 SQL 限流功能;
4)TP 和 AP 分离,避免分析类直接查询到业务库(这是一个比较漫长的过程)。

问题三:缓存压力
除了前面所提到的分布式事务之后,发现还有同事写了使用 Keys 模糊查询 Redis,直接导致 Redis 的 CPU 飙升严重,通过阿里云提供的 Redis 管理工具可以很方便地查看到有哪些慢查询。

另外一个低级错误,我们相信应该不是第一个,也不会是最后一个,本来要设置一个 Key 的过期时间,结果少写了个 Unit 参数,第三个就变更偏移量了。

redisTemplate.opsForValue().set(key, value, offset)

# 为什么我们花了10分钟左右才解决?
1)惯性思维,review 代码没发现出来;
2)在错误日志里发现 Redisson 锁失败时,怀疑是 Redis 写满了;
3)使用阿里云的工具去查大 Key 时发现了 Key 很大,但是直接在网页查看值的时候只看到保存了一个字符,问题就出在这里,因为 RDS 管控台里获取到的值看起来是正确的,大概又过了2分钟左右,我觉得不太对劲,然后登录上去用 redis-cli 查看,傻眼了,里面塞满了 0x00。

lALPDgQ9rVfPg33NAiPNBDg_1080_547_png_620x10000q90g

问题四:
商城上线当月有一个促销活动,因为瞬间进来的流量过大,小程序前端埋点事件上报的接口连接数爆了,商城实时数据统计调用了流量统计服务的接口,然而服务调用超时时间设置的是60s,导致过多请求积压,CPU 突然飙升得很厉害。

# 优化思路:
1)充分利用 Nginx 的并发处理能力,Lua 脚本提供了强大的处理能力,将 Java 处理请求改为使用 OpenResty 接收;
2)接收到请求之后做好基本的校验之后,使用 lua-resty-kafka 模块异步发送到 Kafka;
3)Kafka 落盘到 HDFS 后,由 Spark 离线计算日志数据;
4)后端接口独立部署,实时数据统计调用接口设置更短的超时时间;

经过以上改造之后,前端日志上报服务单机处理能力由原来的 1K 提升 40K,那种如丝般顺滑的体验实在是太好了。

迭代

从当时的情形来看,针对双11的活动做大动作调整代码优化基本上是来不及了,离活动还有不到两个星期的时间,即便改了,风险也很高。

1、压测
作为一个新上线的项目,数据量还比较小,使用云服务来搭建一套1比1的压测环境还是比较容易的,在这个时间节点上,我们需要模拟真实的场景摸清楚目前的系统能承受多大的压力,需要多少机器。

阿里云上有个 PTS 的压测工具,可以直接导入 Jmeter 脚本,使用起来很方便,接下来说说我们的使用步骤:

1)先是按过往一个月的用户行为日志里,找出用户的路径和每个行为的思考时间,做了一个大概的模型;
2)按照双十一活动的运营节奏,定义了两到三个场景;
3)使用 ECS 搭建 Jmeter 集群,内网对接口进行施压,目的是减少网络开销,让请求都能打到后端服务器上;
4)观察服务器的压力,调节应用内存分配,再通过 PolarDB 性能分析,找出有性能瓶颈的 SQL 尽可能地优化掉;
5)将 Jmeter 脚本导入到 PTS,关联上数据库和 ECS 机器的云监控,设置好思考时间等相关的参数后施压,可以动态秒级调整压力,生成的压测报告就是我们想要的结果,需要拿这个结果来进行下一步的限流控制。

2、限流
1)在接入 AHAS 过程中,由于微商城项目当前版本接入的是spring-cloud-alibaba-dependencies-0.9.0.RELEASE版本来使用阿里云的 OSS 与 SMS,在接入 AHAS 后,需要对依赖 Alibaba 版本的升级,涉及包括 Nacos 配置中心与服务发现的升级和包路径的命名变更修改;
2)在接入 AHAS 的 gateway 网关路由限流,采用的是 SDK 接入方式,AHAS 采用了符合 springboot-starter 特性的 SDK 开发,这样在我们微商城接入 gateway 时只需要在项目 POM 中加入 spring-cloud-gateway-starter-ahas-sentinel,在接入 gateway 的时候发现,网关路由限流采集上传的 API 出现了没有兼容 Restfull 风格 API 的问题,导致 URL 上出现参数时多个url没有合并一起的情况,阿里云 AHAS 支持团队立即发布 Fix 版本,提供新的 SentinelWebInterceptor 拦截器进行清洗 Restful 风格 API 处理;
3)在接入 AHAS 的应用模块限流,采用的也是 SDK 接入方式,在按官网文档进行接入的时候,发现我们微商城采用的是最新版本的 Mybatis Plus 版本,在接入 SQL 限流分析功能时发现出现ahas报错,在将此反馈到ahas钉钉团队支援群后,当时已经差不多凌晨一点了,ahas团队的及时响应以及第二天早上就发布了兼容 Mybatis Plus 版本的SQL 限流分析版本给到我们微商城,在我们接入新版本后,SQL 分析和限流功能也能正常使用了;
4)在使用 AHAS 接入的时候,发现 AHAS 除了接口的 API 限流功能外,还提供了CPU/Load 的限流,对服务器性能情况的监控和保护做了很好的护航,在微商城服务器压力过高时能够很好的保护服务器不被高并发压垮,保证了服务的高可用,同时在服务器压力大的时候,做到了实时 QPS 日志上传的隔离,避免上传抢占服务器资源,保证了服务器在接入 AHAS 后也能保持良好的性能。

未来

未来计划要做的事情:
1)按服务拆分 Redis;
2)数据库读写分离、分库分表、TP/AP 分离;
3)业务中台化:建立业务中台,打通商品中心、库存中心、用户中心和交易中心;

为了更好的应对源源不断的挑战,以下岗位持续招聘中:

  • Java开发工程师
  • 前端开发工程师
  • 测试工程师

**有意请发送简历至邮箱:
Lynn.Guo@yatsenglobal.com**

作者信息:
庄工:逸仙电商架构师&技术委员会负责人,负责完美日记商城基础架构和微服务体系建设。
关工:逸仙电商后端技术专家,现主要参与微商城后端框架集成方案、以及性能调优和微商城技术规范管理。
唐工:逸仙电商技术经理,曾先后就职于中国航信和唯品会,现主要负责前后端技术统筹等打杂工作。

]]>
重塑云上的 Java Mon, 27 Jan 2020 20:58:33 +0800 音乐无国界,但是音乐人有国界。

云原生亦如此。虽没有限定的编程语言,但应用所使用的编程语言已经决定了应用部署运行的行为。

Java 诞生于20年前,拥有大量优秀的企业级框架,践行 OOP 理念,更多体现的是严谨以及在长时间运行条件下的稳定性和高性能。反观如今,在要求快速迭代交付的云场景下,语言的简单性似乎成了首要的要求,而传统的 Java 语言显得有一些过于重量了。

本文由阿里巴巴 JVM 团队技术专家郁磊(花名:梁希)分享 JVM 团队是如何面对和处理集团巨大的业务规模和复杂的业务场景的。

ElasticHeap

Java 常因为耗资源而受诟病,其中最显著一点就是 Heap 对内存的占用,即便没有请求在处理也没有对象分配,进程仍然会保留完整的堆内存空间,保障 GC 进行分配内存和操作内存的快速敏捷。

AJDK ZenGC/ElasticHeap 双十一全面支持核心链路上百应用和数十万实例。
1

JDK12 开始支持固定时间的触发 concurrent mark 并在 remark 中收缩 Java 堆归还内存的功能,然而并未解决在 stw 中增加暂停时间的问题,因此无法在每次 young GC 时做内存归还。 ElasticHeap 在并发异步线程中完成内存处理反复 map/unmap 以及 page fault 的开销,因此任意一次 young GC 都可以敏捷的及时归还内存,或重新恢复内存使用。

ElasticHeap 阿里巴巴实战

ElasticHeap场景1:可预测的流量高峰

2
3

ElasticHeap 场景 2 :单机运行多个 Java 实例
4

多个 Java 实例接受的流量任务较为随机,峰值不会重叠,在闲时可以有效降低多个实例整体的内存占用,提高部署密度。

双11验证核心交易系统使用 ElasticHeap 进行低功耗模式运行,大幅降低 WSS(Working Set Size) 规模的实例。

lADPDgQ9rTs_h4TNAfXNAvU_757_501_jpg_620x10000q90g

静态编译

很多云上的新应用不约而同地选择了 Go 语言,很大的原因是 Go 应用对运行时没有依赖,静态编译的程序启动速度快,也不需要通过 JIT 来预热。在阿里有大量 Java 代码的前提下,我们是如何为 Java 注入这方面的能力的呢?

Java 静态编译技术是一种激进的 AOT 技术,通过单独的编译阶段将 Java 程序编译为本地代码,在运行时无需传统 Java 虚拟机和运行时环境,只需操作系统类库支持即可。其工作基本原理如下图所示。静态编译技术实现了 Java 语言与原生 native 程序的“合体”,将原本的 Java 程序编译成为了一个自举的具有 Java 行为的原生 native 程序,由此兼有 Java 程序和原生 native 程序的优点。

JVM 团队与 SOFAStack 团队密切合作,在中间件应用上率先实现静态编译的落地。将一个应用的启动速度从 60 秒优化到 3.8 秒,双十一期间静态编译的应用运行稳定,没有故障, GC 停顿时间在 100 毫秒,在业务允许范围之内,内存占用和 RT 与传统 Java 应用持平。

6

综上所述,静态编译的应用在稳定性、资源占用、RT 响应等各方面指标与传统 Java 应用基本持平的状况下,将启动时间降低了 2000% 。

Wisp2

当你用时下最酷炫的 Vert.X 开发一个简单的 Web 服务,准备体验一下最强的性能, QA 同学拿来一台 1C 2G 的容器让你压一下,你却发现你怎么也拼不过别人 Go 应用。研究之后发现,原来协程模型在这样的少核心的情况下性能要好很多。是时代变了, Java 落伍了?

AJDK Wisp2 回答了这个问题: Java 同样可以拥有高性能的协程。今年是 Wisp2 大规模上线的第一年, Wisp2 具有如下特点:
在整个Java runtime中支持了协程调度,线程(比如 Socket.getInputStream().read() )阻塞会变成更轻量的协程切换。
完全兼容 Thread API ,在开启 Wisp2 的 JDK 中,Thread.start() 实际创建的是一个协程(轻量级线程),可以类比 Go 只提供协程关键字 go 而没有暴露线程接口;我们同样只提供创建协程的方式,应用可以透明切换到协程。
支持 work stealing ,调度策略特别适合 web 场景,在高压力下调度开销极小。

在今年双十一, Wisp 支持了上百应用,十万级容器,其中 90% 的容器已经升级到 Wisp2 。

7

我们可以看到峰值附近, Wisp2 机器的 CPU 要低 7%( Wisp1 更低,Wisp2的取向是 RT ,因此 CPU 会高一些)左右,这主要是轻量级调度所节省的 sys CPU 。 0 点的 CPU 是相等的,这也说明一点: Wisp2 解决的是调度开销,当 CPU 低,调度没有压力时是看不出差距的。

8

从 RT 角度看, Wisp2 机器的 RT 要低 20% 左右, RT 减少明显的一个原因是这批机器的 CPU 压力很大,协程的调度优势更容易体现出来。这样的优势可以帮助系统摸高到更高的水位,整体地提高利用率而担心 RT 过高导致系统雪崩。

FDO

双十一正零点相对后面几分钟会有一个明显的 CPU 峰值,根据数据分析,主要原因是双十一零点触发了 JIT 编译。 举个例子,程序里有逻辑:
if (is1111(LocalDate.now())) {
branch1
} else {
branch2
}
假设预热时一直在走 branch2 ,那么 JIT 有理由相信后续基本也都会走 branch2 ,而不会对 branch1编译。在零点时,我们进入 branch1 ,此时就需要触发退优化重新编译方法。我们来看 AJDK 如何通过 profiling 解决这个问题。

退优化原理及其危害

JDK 运行代码的时候,采用分层编译的方式对 Java 方法进行动态编译。在最高等级(峰值性能最好)的编译中,出于性能的考虑,编译的时候会根据收集的信息做一些比较乐观的假设,一旦这些假设条件不满足了,就会出现退优化的现象。比如某个热点方法中某段代码仅会在双十一中执行,那么在预热过程中这段代码不会被编译,双十一到来时这段代码一旦被执行,就会触发整个方法的退优化。

发生退优化有两个方面的负面影响,一是需要运行的方法由高效率的编译执行变成了解释执行,运行速度降低百倍以上;二是流量高峰期退优化的方法会很快被重新编译,编译线程会消耗 CPU 。因此在双十一这种流量短时间剧增且与预热流量不太一样的场景下,退优化的危害会特别明显。

通过 FDO 减少退优化

FDO 是 feedback directed optimization 的缩写,即参考以往 JVM 运行时的编译信息,指导本次运行时进行更好的编译。具体的,我们采用了两个层面的方法来减少退优化。
将每次运行时的退优化信息记录到文件中,下次运行时读取这个文件,在决定是否做乐观假设的时候参考文件中的信息做判断,从而减少退优化的概率。

信息显示出现最多的退优化与 if-else 相关,占总数量的一半以上。我们提供了一个方法根据以往出现 if-else 退优化的信息,关闭某个路径上所有相关的乐观假设。

双十一中 FDO 的效果

FDO 今年双十一上线,目标解决两个问题:
1、双十一 0 点流量高峰和退优化/编译高峰叠加造成的 CPU 使用率脉冲过高。

2、预热效率低,压测经过前长时间预热后,增大流量时仍然伴随着大量的编译及退优化。

针对第一个问题,我们收集了双十一高峰第一分钟的退优化/C2 编译次数以及 CPU 数据。

可见开启 FDO 后高峰期 C2 编译数目减少约 45% ,退优化数目减少约 70% 。

CPU 数据上,高峰期第一分钟内开启 FDO 后 CPU 由约 67.5 降低到 63.1 ,降低约 7.0% 。

9

第二个目标可以通过压测第一分钟的 CPU 数据验证。

开启 FDO ,压测第一分钟 CPU 使用率由 66.19 降低到 60.33% ,降低约 10% 。

Grace

ZProfiler 一直是全集团排查 Java 应用各类问题的利器,而 Grace 作为其平台化的版本,对其实施了一系列的优化,从原来的单机版本到现在的 Master/Worker 架构,同时引入了任务排队机制,在高压力情况下对用户的任务进行排队从而解决 Worker 不堪重负的问题。在可维护性、拓展性、以及用户体验上得到了质的提升,为后续工具平台的上云、开源事项打下了夯实的基础。

目前已经集成了 Heap Dump 功能,在继承 ZProfiler 功能的基础上做了一定的优化,提升了解析引擎的版本,支持更全面的 OQL 语法等等。

10

JDK11

JDK8 作为一个经典版本,正被大规模使用,虽然从 JDK6 和 7 迁移上来有一定的阵痛,但是升级后普遍的反馈是:“真香”。

OpenJDK 8的下一个稳定版本是 OpenJDK 11 。JVM 团队自然会在这个方向上积极跟进,目前 AJDK11 支持了 AJDK8 的 Wisp2 、多租户特性。本次双十一的部分集群已经上线到 JDK11 ,表现稳定。

升级 JDK11 是否会和升级 JDK8 一样给我们带来同样的的惊喜呢?在 JDK11 上我们可以体验到最新的 ZGC 。

ZGC

JDK11 引入了一个重要特性: ZGC 内存垃圾回收器。这个垃圾回收器号称能够在几十 GB 至若干 TB 的堆上把暂停时间保持在 10ms 以内。许多 Java 开发者苦于过去的垃圾回收器的暂停时间带来延迟, ZGC 短暂停的特性未来无疑会成为 Java 开发者的新宠。

目前 ZGC 在 OpenJDK 中仍然处于实验特性,而且 JDK11 尚未在产业界完全普及, JDK11 只支持 Linux 上的 ZGC( MacOS 和 Windows 的 ZGC 预计在 2020 年 3 月发布的 JDK14 版本才会支持),许多 Java 开发者仍然只能垂涎欲滴,处于观望状态。

向来敢于吃螃蟹的我们岂能望而却步?阿里 JVM 团队和数据库团队已经开始让数据库应用运行在 ZGC 上,并根据运行的效果对 ZGC 进行了相应的改进工作,包括 ZGC 的页缓存机制优化、ZGC的触发时机优化等等。

从 9 月开始,两个团队推动线上数据库应用在 ZGC 上运行,目前已经稳定运行两个月,并顺利通过双十一大考。线上反馈的效果可喜可贺:
1、 JVM 暂停时间保持在官方的 10ms 以内;
2、 ZGC 大大改善了线上运行集群的平均 RT 与毛刺指标。

小结

从上述的功能特性可以看到 AJDK 已经从一个传统的 Managed Runtime 脱胎换骨。今后 AJDK 将继续致力于提高云上的应用的开发体验,通过底层的创新为上层应用提供更多的可能。

在 Dragonwell 上使用 AJDK 功能

上述的这些经过双十一考验的功能都将随着 Dragonwell 陆续开源和交付到广大用户手中,敬请关注。

Alibaba Dragonwell 8 是一款免费的 OpenJDK 发行版。它提供长期支持,包括性能增强和安全修复。Alibaba Dragonwell 8 目前支持 X86-64/Linux 平台,在数据中心大规模 Java 应用部署情况下, 可以大幅度提高稳定性、效率以及性能。Alibaba Dragonwell 8 是 OpenJDK 的下游( friendly fork ),使用了和 OpenJDK 一样的 license。Alibaba Dragonwell 8 与 Java SE 标准兼容,用户可以使用 Alibaba Dragonwell 8 开发和运行 Java 应用程序。此次开源的 Alibaba Dragonwell 8 是阿里巴巴内部 OpenJDK 定制版 AJDK 的开源版本, AJDK 为在线电商,金融,物流做了结合业务场景的优化,运行在超大规模的,1,000,000+ 服务器的阿里巴巴数据中心。

近期我们正在紧密筹备 Alibaba Dragonwell 11 的 release 。 Dragonwell 11 是基于 OpenJDK 11 的 Dragonwell 发行版本,拥有更多特性,可以更多地为云上场景赋能,模块化更加清晰,并将获得长期的支持,因此推荐大家关注和适时升级。

]]>
免费试用!容器集群监控利器 阿里云 Prometheus 服务正式商业化 Mon, 27 Jan 2020 20:58:33 +0800 阿里云Prometheus服务将在2020年1月6日正式开启商业化,商业化之后,阿里云Prometheus服务致力于提供更加稳定、高效、优惠的Prometheus托管一站式服务,现在接入更有15天免费试用。
15天免费试用地址,点击这里

产品文档,点击这里

Prometheus介绍

Prometheus 是云原生领域最受认可的监控解决方案,在 2016 年加入 CNCF 基金会后,成为继 K8S 后第二个毕
业项目,Prometheus 已经成为云原生监控领域的事实标准。
开源Prometheus痛点

  • Prometheus高可用问题:Prometheus服务器是单机设计,在生产环境极易出现自身宕机不可用情况。
  • Prometheus服务器资源消耗大:Prometheus服务器对于集群的资源消耗巨大,特别是对于内存的占用,极易出现内存OOM问题导致服务不可用。
  • Prometheus本地存储无可扩展性:Prometheus默认提供的本地存储无法水平扩展,一旦出现磁盘问题则会造成数据丢失,无法满足生产环境下对于监控数据存储的扩展性和高可用性。
  • Prometheus自身运维挑战巨大:Prometheus server,alert manager,grafana等组件繁多,配置管理复杂,自身可运维差,虽然有Prometheus Operator这种方式,但还是无法从根本上解决需自运维这个症结。
  • Prometheus监控配置复杂,无法开箱即用:各种第三方exporter配置复杂,运行起来成本高,缺少监控大盘,需要大量时间精力进行监控配置。

开源Prometheus优势

  • 开源社区活跃,迭代迅速,功能不断增强
  • 各种第三方监控插件和exporter不断贡献监控能力

阿里云Prometheus服务优势

针对开源Prometheus优缺点, 阿里云推出了Prometheus服务,在完全兼容开源Prometheus的同时,提供了一站式开箱即用的云服务,其特点包括

  • 精简稳定的Prometheus采集器:通过helm chart方式在用户集群部署,prometheus采集器基于prometheus server进行了功能的精简和优化,专注于服务发现和数据采集,资源消耗是相同的prometheus server的二十分之一,且支持水平扩展。
  • 稳定大容量的云存储:云存储基于阿里云的日志服务,提供接近无上限的存储空间和高效稳定的分布式存储。
  • 高效且强大的云端数据查询能力:通过部署在云端的Prometheus服务器提供完全兼容PromQL的指标查询能力,增加下推采样和长时间查询优化。
  • 默认告警服务:支持原生alerting rule, 默认集成钉钉、邮件等告警通道,打通云上告警联系人。
  • 开箱即用的Grafana云服务监控面板:默认提供K8S集群的监控大盘,包含主机,Pod,deployment,API Server,Ingress,coreDNS等各组件监控面板,并提供各种三方组件的监控面板。
    (各种组件监控大盘)

1

(默认提供的k8s集群监控)
2

(默认提供的APIServer监控面板)
3

阿里云Prometheus服务专业版

在15天免费试用期结束后,用户可以自主选择是否开通Prometheus专业版,专业版针对默认抓取的Kubernetes集群相关的指标免费,包括集群概览,Deployment,Pod,Node指标以及ApiServer,Etcd,CoreDNS,Ingress等Kubernetes组件指标;而针对用户自定义上报的指标,按照上报次数收费。
基础指标(免费)说明,点击这里

收费标准,点击这里

总结

阿里云Prometheus服务经过近半年的公测试用,终于在新年伊始开始提供商业化服务,感谢广大用户在这半年里和我们一起成才和蜕变。我们坚信阿里云Prometheus服务会在云原生时代提供给大家最佳的监控体验。欢迎开通免费试用,入群更能享受专属优惠。
15天免费试用地址,点击这里

产品免费试用群(钉钉群):23155358
产品文档,点击这里

]]>
【升级】1月消息队列AMQP升级计划通知 Mon, 27 Jan 2020 20:58:33 +0800

【阿里云】【消息队列AMQP】【升级通知】

升级窗口:

北京时间2020年1月14日 00:00 - 06:00

北京时间2020年1月16日 00:00 - 06:00

北京时间2020年1月21日 00:00 - 06:00
北京时间2020年1月23日 00:00 - 06:00

北京时间2020年1月28日 00:00 - 06:00

北京时间2020年1月30日 00:00 - 06:00

升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

]]>
【升级】1月14日阿里云支付系统​停机升级通知 Mon, 27 Jan 2020 20:58:33 +0800

【阿里云】【支付系统】【升级通知】

升级窗口:北京时间 2020年1月14日 00:00 - 03:00

升级内容:为增强阿里云交易和支付系统的用户体验,我们将对相关系统进行升级,升级期间给您带来的不便敬请谅解,感谢您的支持与理解。

升级影响:

1、预付费(包年包月)商品无法正常支付,无法进行新购、续费、升级、退款等操作,如您有相关诉求,可提前操作;

2、后付费(按量付费)产品可正常开通、变配,极端情况下可能会不可用,可在几分钟后重试相关操作;

3、现金账户无法进行充值、提现等操作;

4、后付费(按量付费)产品账单结算将延迟到升级完成后执行。

给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

]]>
【升级】1月14日MMX注册局系统维护通知 Mon, 27 Jan 2020 20:58:33 +0800

【阿里云】【域名】【注册局维护通知】

维护时间:北京时间2020年1月14日 14:00 - 16:00

维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

维护影响:届时 .Vip/.Work/.Beer/.Luxe/.Fit/.Yoga 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

由此给您带来的不便,我们表示歉意,敬请谅解。

]]>
【升级】1月15日DataHub升级通知 Mon, 27 Jan 2020 20:58:33 +0800

【阿里云】【Datahub】升级通知
升级窗口:北京时间2020年1月15日 10:00 - 17:00
升级内容:产品稳定性升级
升级区域:华北,华东,华南,新加坡,吉隆坡,孟买,德国
升级影响:正常情况下对客户无影响,升级过程中如果遇到任何问题,可随时联系阿里云反馈。

给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

]]>
解决微服务缓存常见问题——缓存击穿 Mon, 27 Jan 2020 20:58:33 +0800 “缓存”和“击穿”

什么是缓存?

缓存就是数据交换的缓冲区。
我们通常的理解缓存的主要作用是提高查询效率。其实它还有着另一个非常重要的作用,就是上面提到的“缓冲”也就是对下层资源的保护作用。

如何理解击穿

很简单,我们上面提到的缓存的另外一个主要作用是“缓冲”对下层资源的防护,那么“击穿“就是让你的缓冲失效,从而对被保护的资源进行”冲击“。
回到我们所开发的系统中,我们常常用缓存中间件如redis等,作为我们的缓存数据存储。当请求到达服务端时,我们优先查询缓存,缓存如果不存在,再查询数据库,如果查询到将数据写回缓存,使得下一次同样的请求能够命中缓存。如下图所示:
image.png
当大多数正常请求到达服务,大多会命中缓存数据库,也就是左半边红色部分(表示资源忙碌)。当一些攻击性请求,特意查询缓存中不存在的数据时,这时候按照我们上述处理逻辑,会直接透过缓存,到达数据库,进而对数据库进行流量冲击,体现为图中右半部分。

一种有效的解决缓存击穿利器——布隆过滤器

之上部分,我们提到了特意查询缓存数据库中不存在的数据会对数据库造成冲击,而数据库相对缓存,查询效率低很多,当大批量请求到达数据库,数据库因性能问题导致不再能及时响应请求,甚至出现宕机。那么我们如何进行防护?
我们换种思路出发,如果能够判断出,哪些请求一定是系统中不存在的数据时,是不是就可以将请求进行拦截,不让请求到达数据层?答案:YES
在这里,我们介绍一种有效的思路——布隆过滤器

布隆过滤器概念

什么是布隆过滤器?
本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),
特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。

布隆过滤器数据结构

布隆过滤器是一个bit向量或者说bit数组,如下图:
image.png

布隆过滤器原理分析

如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向bit位置1,例如针对值”baidu“和三个不同的哈希函数分别生成了哈希值1、4、7,如下图所示:image.png
OK,我们再存一个值”tencent“,如果哈希函数返回3、4、8的话,图则继续变为:
image.png

值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被覆盖了。现在我们如果想查询 “dianping” 这个值是否存在,哈希函数返回了 1、5、8三个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,因此我们可以很确定地说 “dianping” 这个值不存在。而当我们需要查询 “baidu” 这个值是否存在的话,那么哈希函数必然会返回 1、4、7,然后我们检查发现这三个 bit 位上的值均为 1,那么我们可以说 “baidu” 存在了么?答案是不可以,只能是 “baidu” 这个值可能存在。


]]>
手动搭建Gateway连接阿里云E-MapReduce Mon, 27 Jan 2020 20:58:34 +0800 网络环境

首先要保证 Gateway 节点在 E-MapReduce 对应集群的安全组中,Gateway 节点可以顺利的访问 E-MapReduce 集群。设置节点的安全组请参考创建安全组

软件环境

系统环境:推荐使用 CentOS 7.2 及以上版本。
Java环境:安装 JDK 1.7 及以上版本,推荐使用 OpenJDK version 1.8.0 版本。

搭建步骤

E-MapReduce 2.7 及以上版本,3.2 及以上版本

这些版本推荐您直接使用 E-MapReduce 控制台来创建 Gateway。如果您选择手动搭建,请先创建一个脚本,脚本内容如下所示,然后在 Gataway 节点上执行。执行命令为:

sh deploy.sh <masteri_ip> master_password_file
  • deploy.sh:脚本名称,内容见下面代码

  • masteri_ip:集群的master节点的IP,请确保可以访问

  • master_password_file:保存 master 节点的密码文件,将 master 节点的密码直接写在文件内即可

#!/usr/bin/bashif [ $# != 2 ]then
   echo "Usage: $0 master_ip master_password_file"
   exit 1;
fi
masterip=$1masterpwdfile=$2if ! type sshpass >/dev/null 2>&1; then
   yum install -y sshpass
fiif ! type java >/dev/null 2>&1; then
   yum install -y java-1.8.0-openjdk
fi
mkdir -p /opt/apps
mkdir -p /etc/ecm
echo "Start to copy package from $masterip to local gateway(/opt/apps)"echo " -copying hadoop-2.7.2"sshpass -f $masterpwdfile scp -r -o 'StrictHostKeyChecking no' root@$masterip:/usr/lib/hadoop-current /opt/apps/echo " -copying hive-2.0.1"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/hive-current /opt/apps/echo " -copying spark-2.1.1"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/spark-current /opt/apps/echo " -copying tez"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/tez-current /opt/apps/echo " -extra-jars"sshpass -f $masterpwdfile scp -r root@$masterip:/opt/apps/extra-jars /opt/apps/

echo "Start to link /usr/lib/\${app}-current to /opt/apps/\${app}"if [ -L /usr/lib/hadoop-current ]then
   unlink /usr/lib/hadoop-currentfi
ln -s /opt/apps/hadoop-current  /usr/lib/hadoop-currentif [ -L /usr/lib/hive-current ]then
   unlink /usr/lib/hive-currentfi
ln -s /opt/apps/hive-current  /usr/lib/hive-currentif [ -L /usr/lib/spark-current ]then
   unlink /usr/lib/spark-currentfi
ln -s /opt/apps/spark-current /usr/lib/spark-currentif [ -L /usr/lib/tez-current ]then
   unlink /usr/lib/tez-currentfi
ln -s /opt/apps/tez-current /usr/lib/tez-currentecho "Start to copy conf from $masterip to local gateway(/etc/ecm)"sshpass -f $masterpwdfile scp -r root@$masterip:/etc/ecm/hadoop-conf  /etc/ecm/hadoop-conf
sshpass -f $masterpwdfile scp -r root@$masterip:/etc/ecm/hive-conf /etc/ecm/hive-conf
sshpass -f $masterpwdfile scp -r root@$masterip:/etc/ecm/spark-conf /etc/ecm/spark-conf
sshpass -f $masterpwdfile scp -r root@$masterip:/etc/ecm/tez-conf /etc/ecm/tez-conf
echo "Start to copy environment from $masterip to local gateway(/etc/profile.d)"sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/hdfs.sh /etc/profile.d/
sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/yarn.sh /etc/profile.d/
sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/hive.sh /etc/profile.d/
sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/spark.sh /etc/profile.d/
sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/tez.sh /etc/profile.d/if [ -L /usr/lib/jvm/java ]then
   unlink /usr/lib/jvm/javafi
echo "" >>/etc/profile.d/hdfs.sh
echo export JAVA_HOME=/usr/lib/jvm/jre-1.8.0 >>/etc/profile.d/hdfs.sh
echo "Start to copy host info from $masterip to local gateway(/etc/hosts)"sshpass -f $masterpwdfile scp root@$masterip:/etc/hosts /etc/hosts_bak
cat /etc/hosts_bak | grep emr | grep cluster >>/etc/hostsif ! id hadoop >& /dev/nullthen
   useradd hadoop
fi

E-MapReduce 2.7 以下版本,3.2以下版本

需要创建一个脚本,脚本内容如下所示,然后在 Gataway 节点上执行。执行命令为:

sh deploy.sh <masteri_ip> master_password_file
  • deploy.sh:脚本名称,内容见下面代码

  • masteri_ip:集群的 master 节点的 IP,请确保可以访问

  • master_password_file:保存 master 节点的密码文件,将 master 节点的密码直接写在文件内即可

!/usr/bin/bash
if [ $# != 2 ]
then
   echo "Usage: $0 master_ip master_password_file"
   exit 1;
fi
masterip=$1
masterpwdfile=$2
if ! type sshpass >/dev/null 2>&1; then
   yum install -y sshpass
fi
if ! type java >/dev/null 2>&1; then
   yum install -y java-1.8.0-openjdk
fi
mkdir -p /opt/apps
mkdir -p /etc/emr
echo "Start to copy package from $masterip to local gateway(/opt/apps)"
echo " -copying hadoop-2.7.2"
sshpass -f $masterpwdfile scp -r -o 'StrictHostKeyChecking no' root@$masterip:/usr/lib/hadoop-current /opt/apps/
echo " -copying hive-2.0.1"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/hive-current /opt/apps/echo " -copying spark-2.1.1"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/spark-current /opt/apps/echo "Start to link /usr/lib/\${app}-current to /opt/apps/\${app}"if [ -L /usr/lib/hadoop-current ]then
   unlink /usr/lib/hadoop-currentfi
ln -s /opt/apps/hadoop-current  /usr/lib/hadoop-currentif [ -L /usr/lib/hive-current ]then
   unlink /usr/lib/hive-currentfi
ln -s /opt/apps/hive-current  /usr/lib/hive-currentif [ -L /usr/lib/spark-current ]then
   unlink /usr/lib/spark-currentfi
ln -s /opt/apps/spark-current /usr/lib/spark-currentecho "Start to copy conf from $masterip to local gateway(/etc/emr)"sshpass -f $masterpwdfile scp -r root@$masterip:/etc/emr/hadoop-conf  /etc/emr/hadoop-conf
sshpass -f $masterpwdfile scp -r root@$masterip:/etc/emr/hive-conf /etc/emr/hive-conf
sshpass -f $masterpwdfile scp -r root@$masterip:/etc/emr/spark-conf /etc/emr/spark-conf
echo "Start to copy environment from $masterip to local gateway(/etc/profile.d)"sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/hadoop.sh /etc/profile.d/if [ -L /usr/lib/jvm/java ]then
   unlink /usr/lib/jvm/javafi
ln -s /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-3.b12.el7_3.x86_64/jre /usr/lib/jvm/javaecho "Start to copy host info from $masterip to local gateway(/etc/hosts)"sshpass -f $masterpwdfile scp root@$masterip:/etc/hosts /etc/hosts_bak
cat /etc/hosts_bak | grep emr | grep cluster >>/etc/hostsif ! id hadoop >& /dev/nullthen
   useradd hadoop
fi

测试

Hive

[hadoop@iZ23bc05hrvZ ~]$ hive
hive> show databases;
OKdefaultTime taken: 1.124 seconds, Fetched: 1 row(s)
hive> create database school;
OK
Time taken: 0.362 seconds
hive>

运行 Hadoop 作业

[hadoop@iZ23bc05hrvZ ~]$ hadoop  jar /usr/lib/hadoop-current/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.6.0.jar pi 10 10Number of Maps  = 10Samples per Map = 10Wrote input for Map #0Wrote input for Map #1Wrote input for Map #2Wrote input for Map #3Wrote input for Map #4Wrote input for Map #5Wrote input for Map #6Wrote input for Map #7Wrote input for Map #8Wrote input for Map #9
  File Input Format Counters 
      Bytes Read=1180
  File Output Format Counters 
      Bytes Written=97Job Finished in 29.798 seconds
Estimated value of Pi is 3.20000000000000000000


]]>
卸载centos自带的openjdk和MySQL Mon, 27 Jan 2020 20:58:34 +0800 卸载openjdk
1.查看已经安装的jdk
rpm –qa|grep jdk
java-1.7.0-openjdk-1.7.0.141-2.6.10.5.el7.x86_64
java-1.8.0-openjdk-1.8.0.131-11.b12.el7.x86_64
java-1.8.0-openjdk-headless-1.8.0.131-11.b12.el7.x86_64
java-1.7.0-openjdk-headless-1.7.0.141-2.6.10.5.el7.x86_64
ldap-4.19-1.el7.noarch
copy-jdk-configs-2.2-3.el7.noarch
2.卸载命令
yum -y remove java-1.7.0-openjdk-1.7.0.141-2.6.10.5.el7.x86_64
yum -y remove java-1.8.0-openjdk-1.8.0.131-11.b12.el7.x86_64
yum -y remove java-1.8.0-openjdk-headless-1.8.0.131-11.b12.el7.x86_64
yum -y remove java-1.7.0-openjdk-headless-1.7.0.141-2.6.10.5.el7.x86_64
.noarch文件不用
3.用Java –version查看

 提示如下: No such file or directoy

卸载完成
卸载mysql
执行命令yum remove mariadb-libs即可。


]]>
搭建一个完整的Kubernetes集群--自签APIServer SSL证书+master1配置 Mon, 27 Jan 2020 20:58:34 +0800 生成apiserver证书!

vim ca-csr.json

{    "CN": "kubernetes",    "key": {        "algo": "rsa",        "size": 2048
    },    "names": [
        {            "C": "CN",            "L": "BeiJing",            "ST": "BeiJing",            "O": "k8s",            "OU": "System"
        }
    ]
}vim ca-config.json
{  "signing": {    "default": {      "expiry": "876000h"
    },    "profiles": {      "kubernetes": {         "expiry": "876000h",         "usages": [            "signing",            "key encipherment",            "server auth",            "client auth"
        ]
      }
    }
  }
}

创建CA配置json文件

创建apiserver证书的所需配置文件

vim kube-proxy-csr.json
{  "CN": "system:kube-proxy",  "hosts": [],  "key": {    "algo": "rsa",    "size": 2048
  },  "names": [
    {      "C": "CN",      "L": "BeiJing",      "ST": "BeiJing",      "O": "k8s",      "OU": "System"
    }
  ]
}


vim server-csr.json
{    "CN": "kubernetes",    "hosts": [      "10.0.0.1",      "127.0.0.1",      "kubernetes",      "kubernetes.default",      "kubernetes.default.svc",      "kubernetes.default.svc.cluster",      "kubernetes.default.svc.cluster.local",      "10.100.97.55",      "10.100.97.78",      "10.100.97.79",      "10.100.97.80",      "10.100.97.81",      "10.100.97.82",      "10.100.97.83"
    ],    "key": {        "algo": "rsa",        "size": 2048
    },    "names": [
        {            "C": "CN",            "L": "BeiJing",            "ST": "BeiJing",            "O": "k8s",            "OU": "System"
        }
    ]
}

.
├── ca-config.json
├── ca-csr.json
├── kube-proxy-csr.json
└── server-csr.json

生产证书

cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes server-csr.json | cfssljson -bare servercfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy

image.png

部署master

二进制包下载地址:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.16.md#v1161

image.png

mkdir -p /opt/kubernetes/{bin,cfg,k8s,ssl}
wget "https://dl.k8s.io/v1.16.1/kubernetes-server-linux-amd64.tar.gz"tar xvf kubernetes-server-linux-amd64.tar.gz
cp  *.pem /opt/kubernetes/ssl/cp /usr/local/src/kubernetes/server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler,kubectl} /opt/kubernetes/bin/

二进制文件位置:kubernetes/server/bin

创建配置文件 /opt/kubernetes/cfg/
#####
vim kube-apiserver.conf 

KUBE_APISERVER_OPTS="--logtostderr=false \
--v=2 \--log-dir=/opt/kubernetes/logs \--etcd-servers=https://10.100.97.78:2379,https://10.100.97.79:2379,https://10.100.97.55:2379 \--bind-address=10.100.97.78 \--secure-port=6443 \--advertise-address=10.100.97.78 \--allow-privileged=true \--service-cluster-ip-range=10.0.0.0/24 \--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \--authorization-mode=RBAC,Node \--enable-bootstrap-token-auth=true \--token-auth-file=/opt/kubernetes/cfg/token.csv \--service-node-port-range=30000-32767 \--kubelet-client-certificate=/opt/kubernetes/ssl/server.pem \--kubelet-client-key=/opt/kubernetes/ssl/server-key.pem \--tls-cert-file=/opt/kubernetes/ssl/server.pem \--tls-private-key-file=/opt/kubernetes/ssl/server-key.pem \--client-ca-file=/opt/kubernetes/ssl/ca.pem \--service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \--etcd-cafile=/opt/etcd/ssl/ca.pem \--etcd-certfile=/opt/etcd/ssl/server.pem \--etcd-keyfile=/opt/etcd/ssl/server-key.pem \--audit-log-maxage=30 \--audit-log-maxbackup=3 \--audit-log-maxsize=100 \--audit-log-path=/opt/kubernetes/logs/k8s-audit.log"#####
vim kube-controller-manager.conf 

KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \
--v=2 \--log-dir=/opt/kubernetes/logs \--leader-elect=true \--master=127.0.0.1:8080 \--address=127.0.0.1 \--allocate-node-cidrs=true \--cluster-cidr=192.244.0.0/16 \--service-cluster-ip-range=192.0.0.0/24 \--cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \--cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem  \--root-ca-file=/opt/kubernetes/ssl/ca.pem \--service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \--experimental-cluster-signing-duration=87600h0m0s"#####
vim kube-scheduler.conf

KUBE_SCHEDULER_OPTS="--logtostderr=false \
--v=2 \--log-dir=/opt/kubernetes/logs \--leader-elect \--master=127.0.0.1:8080 \--address=127.0.0.1"

创建启动文件到/usr/lib/systemd/system

##########cat kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes[Service]
EnvironmentFile=/opt/kubernetes/cfg/kube-apiserver.conf
ExecStart=/opt/kubernetes/bin/kube-apiserver $KUBE_APISERVER_OPTS
Restart=on-failure

[Install]
WantedBy=multi-user.target



######################
cat kube-controller-manager.service 
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes[Service]
EnvironmentFile=/opt/kubernetes/cfg/kube-controller-manager.conf
ExecStart=/opt/kubernetes/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_OPTS
Restart=on-failure

[Install]
WantedBy=multi-user.target

########################
cat kube-scheduler.service 
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes[Service]
EnvironmentFile=/opt/kubernetes/cfg/kube-scheduler.conf
ExecStart=/opt/kubernetes/bin/kube-scheduler $KUBE_SCHEDULER_OPTS
Restart=on-failure

[Install]
WantedBy=multi-user.target
###############

启用TLS Bootstrapping
vim /opt/kubernetes/cfg/token.csv

[root@master1 cfg]# tree /opt/kubernetes/

├── bin
│   ├── kube-apiserver
│   ├── kube-controller-manager
│   ├── kubectl
│   └── kube-scheduler
├── cfg
│   ├── kube-apiserver.conf
│   ├── kube-controller-manager.conf
│   ├── kube-scheduler.conf
│   └── token.csv
├── k8s
└── ssl
    ├── ca-key.pem
    ├── ca.pem
    ├── kube-proxy-key.pem
    ├── kube-proxy.pem
    ├── server-key.pem
    └── server.pem##################手动启动可以检测参数是否有错误
/opt/kubernetes/bin/kube-apiserver  --logtostderr=true --v=2 --log-dir=/opt/kubernetes/logs --etcd-servers=https://10.100.97.78:2379,https://10.100.97.79:2379,https://10.100.97.55:2379 --bind-address=10.100.97.78 --secure-port=6443 --advertise-address=10.100.97.78 --allow-privileged=true --service-cluster-ip-range=10.0.0.0/24 --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction --authorization-mode=RBAC,Node --enable-bootstrap-token-auth=true --token-auth-file=/opt/kubernetes/cfg/token.csv --service-node-port-range=30000-32767 --kubelet-client-certificate=/opt/kubernetes/ssl/server.pem --kubelet-client-key=/opt/kubernetes/ssl/server-key.pem --tls-cert-file=/opt/kubernetes/ssl/server.pem --tls-private-key-file=/opt/kubernetes/ssl/server-key.pem --client-ca-file=/opt/kubernetes/ssl/ca.pem --service-account-key-file=/opt/kubernetes/ssl/ca-key.pem --etcd-cafile=/opt/etcd/ssl/ca.pem --etcd-certfile=/opt/etcd/ssl/server.pem  --etcd-keyfile=/opt/etcd/ssl/server-key.pem --audit-log-maxage=30  --audit-log-maxbackup=3 --audit-log-maxsize=100 --audit-log-path=/opt/kubernetes/logs/k8s-audit.log

systemctl start kube-apiserver
systemctl start kube-controller-manager
systemctl start kube-scheduler
systemctl enable kube-apiserver
systemctl enable kube-controller-manager
systemctl enable kube-scheduler

![image.png](https://ucc.alicdn.com/pic/developer-ecology/68c415e0915e4dd3951af488b5b93003.png)


]]>
Dart 语言基础入门篇 Mon, 27 Jan 2020 20:58:34 +0800 本文是【从零开始学习,开发个Flutter App】路上的第 1 篇文章。

这篇文章介绍了 Dart 的基础特性,目的在于让大家建立对 Dart 语言的总体认知,初步掌握 Dart 的语法。

我们假定读者已经有一定的编程基础,如果你了解 JavaScript 或者 Java 等面向对象语言,那 Dart 学习起来应该很有亲切感。

Dart 是一门采取众家之长的编程语言。尽管 Dart 很多语法和 JavaScript 很相似,但 Dart 语言同时是一门强类型的语言,它同时结合了像 Java 这样强类型面向对象语言的特性,这使得它能胜任大型应用开发,同时它没有 Java 的臃肿,Dart 语言在设计上非常简洁、灵活和高效。

JavaScript 从简单的浏览器脚本到服务端(nodejs),慢慢延伸到PC客户端(electron)、App (React Native)甚至小程序开发,它已然成为一门真正意义上的全栈开发语言。

如果说 JavaScript 是在漫长的时光里野蛮生长,那 Dart 从一开始就是精心设计出来的。如果说有一门语言取代JavaScript的位置,那很可能就是Dart。

Talk is cheep,下面就让我们来亲自感受一下这门语言的吧。

变量

你可以像 JavaScript 那样声明一个变量:

var name = 'Bob';

编译器会推导出 name 的类型是String 类型,等价于:

String name = 'Bob';

我们可以从下面代码窥见 Dart 是强类型语言的特性:

var name = 'Bob';  
// 调用 String 的方法print(name.toLowerCase());// 编译错误// name = 1;

前面我们说过,Dart 除了具备简洁的特点,而且也可以是非常灵活的,如果你想变换一个变量的类型,你也可以使用dynamic 来声明变量,这就跟 JavaScript 一样了:

dynamic name = 'Bob'; //String 类型name = 1;// int 类型print(name);

上面的代码可以正常编译和运行,但除非你有足够的理由,请不要轻易使用。

final 的语义和 Java 的一样,表示该变量是不可变的:

// String 可以省略final String name = 'Bob'; 

// 编译错误// name = 'Mary';

其中 String 可以省略,Dart 编译器足够聪明地知道变量name 的类型。

如果要声明常量,可以使用const 关键词:

const PI = '3.14';class Person{  static const name = 'KK';
}

如果类变量,则需要声明为static const 。

内置类型

不像Java把类型分的特别细,比如整数类型,就有byteshortint 、long 。Dart 的类型设计相当简洁,这也是 Dart 容易上手的原因之一,可以理解为通过牺牲空间来换取效率吧。

数值类型

Dart 内置支持两种数值类型,分别是int 和double ,它们的大小都是64位。

var x = 1;// 0x开头为16进制整数var hex = 0xDEADBEEF;var y = 1.1;// 指数形式var exponents = 1.42e5;

需要注意的是,在Dart中,所有变量值都是一个对象,intdouble类型也不例外,它们都是num类型的子类,这点和JavaJavaScript都不太一样:

// String -> intvar one = int.parse('1');assert(one == 1);// String -> doublevar onePointOne = double.parse('1.1');assert(onePointOne == 1.1);// int -> StringString oneAsString = 1.toString();assert(oneAsString == '1');// double -> StringString piAsString = 3.14159.toStringAsFixed(2);assert(piAsString == '3.14');

字符串

Dart 字符串使用的是UTF-16编码。

var s = '中';
s.codeUnits.forEach((ch) => print(ch));// 输出为UNICODE值20013

Dart 采用了 JavaScript 中类似模板字符串的概念,可以在字符串通过${expression}语法插入变量:

var s = "hello";  
print('${s}, world!');  
//可以简化成:print('$s, world!');//调用方法print('${s.toUpperCase()}, world!');

Dart 可以直接通过==来比较字符串:

var s1 = "hello";var s2 = "HELLO";assert(s1.toUpperCase() == s2);

布尔类型

Dart 布尔类型对应为bool关键词,它有truefalse两个值,这点和其他语言区别不大。值得一提的是,在Dart的条件语句ifassert表达式里面,它们的值必须是bool类型,这点和 JavaScript 不同。

var s = '';assert(s.isEmpty);if(s.isNotEmpty){// do something}  
//编译错误,在JavaScript常用来判断undefinedif(s){
}

Lists

你可以把Dart中的List对应到 JavaScript 的数组或者 Java 中的ArrayList,但 Dart 的设计更为精巧。

你可以通过类似 JavaScript 一样声明一个数组对象:

var list = [];
list.add('Hello');
list.add(1);

这里List容器接受的类型是dynamic,你可以往里面添加任何类型的对象,但如果像这样声明:

var iList = [1,2,3];
iList.add(4);//编译错误 The argument type 'String' can't be assigned to the parameter type 'int'//iList.add('Hello');

那么Dart就会推导出这个List是个List<int>,从此这个List就只能接受int类型数据了,你也可以显式声明List的类型:

var sList = List<String>();//在Flutter类库中,有许多这样的变量声明:List<Widget> children = const <Widget>[];

上面右边那个 const 的意思表示常量数组,在这里你可以理解为一个给children赋值了一个编译期常量空数组,这样的做法可以很好的节省内存,下面的例子可以让大家更好的理解常量数组的概念:

var constList = const <int>[1,2];
constList[0] = 2; //编译通过, 运行错误constList.add(3); //编译通过, 运行错误

Dart2.3 增加了扩展运算符 (spread operator) ... 和...?,通过下面的例子你很容易就明白它们的用法:

var list = [1, 2, 3];var list2 = [0, ...list];assert(list2.length == 4);

如果扩展对象可能是null,可以使用...?

 var list; var list2 = [0, ...?list]; assert(list2.length == 1);

你可以直接在元素内进行判断,决定是否需要某个元素:

var promoActive = true;var nav = [    'Home',    'Furniture',    'Plants',
    promoActive? 'About':'Outlet'];

甚至使用for来动态添加多个元素:

var listOfInts = [1, 2, 3];var listOfStrings = [  '#0',  for (var i in listOfInts) '#$i'];assert(listOfStrings[1] == '#1');

这种动态的能力使得 Flutter 在构建 Widget 树的时候非常方便。

Sets

Set的语意和其他语言的是一样的,都是表示在容器中对象唯一。在Dart中,Set默认是LinkedHashSet实现,表示元素按添加先后顺序排序。

声明Set对象:

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

遍历Set,遍历除了上面提到的for...in,你还可以使用类似 Java 的 lambada 中的 forEach 形式:

halogens.add('bromine');
halogens.add('astatine');
halogens.forEach((el) => print(el));

输出结果:

fluorine
chlorine
bromine
iodine
astatine

除了容器的对象唯一特性之外,其他基本和List是差不多的。

// 添加类型声明:var elements = <String>{};var promoActive = true;// 动态添加元素final navSet = {'Home', 'Furniture', promoActive? 'About':'Outlet'};

Maps

Map对象的声明方式保持了 JavaScript 的习惯,Dart 中Map的默认实现是LinkedHashMap,表示元素按添加先后顺序排序。

var gifts = {  // Key:    Value
  'first': 'partridge',  'second': 'turtledoves',  'fifth': 'golden rings'};assert(gifts['first'] == 'partridge');

添加一个键值对:

gifts['fourth'] = 'calling birds';

遍历Map

gifts.forEach((key,value) => print('key: $key, value: $value'));

函数

在 Dart 中,函数本身也是个对象,它对应的类型是 Function,这意味着函数可以当做变量的值或者作为一个方法入传参数值。

void sayHello(var name){  print('hello, $name');
}void callHello(Function func, var name){
  func(name);
}void main(){  // 函数变量
  var helloFuc = sayHello;  // 调用函数
  helloFuc('Girl');  // 函数参数
  callHello(helloFuc,'Boy');
}

输出:

hello, Girlhello, Boy

对于只有一个表达式的简单函数,你还可以通过=> 让函数变得更加简洁,=> expr在这里相当于{ return expr; } ,我们来看一下下面的语句:

String hello(var name ) => 'hello, $name';

相当于:

String hello(var name ){  return 'hello, $name';
}

参数

在Flutter UI库里面,命名参数随处可见,下面是一个使用了命名参数(Named parameters)的例子:

void enableFlags({bool bold, bool hidden}) {...}

调用这个函数:

enableFlags(bold: false);
enableFlags(hidden: false);
enableFlags(bold: true, hidden: false);

命名参数默认是可选的,如果你需要表达该参数必传,可以使用@required

void enableFlags({bool bold, @required bool hidden}) {}

当然,Dart 对于一般的函数形式也是支持的:

void enableFlags(bool bold, bool hidden) {}

和命名参数不一样,这种形式的函数的参数默认是都是要传的:

enableFlags(false, true);

你可以使用[]来增加非必填参数:

void enableFlags(bool bold, bool hidden, [bool option]) {}

另外,Dart 的函数还支持设置参数默认值:

void enableFlags({bool bold = false, bool hidden = false}) {...}String say(String from, [String device = 'carrier pigeon', String mood]) {}

匿名函数

顾名思意,匿名函数的意思就是指没有定义函数名的函数。你应该对此不陌生了,我们在遍历ListMap的时候已经使用过了,通过匿名函数可以进一步精简代码:

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {  print('${list.indexOf(item)}: $item');
});

闭包

Dart支持闭包。没有接触过JavaScript的同学可能对闭包(closure)比较陌生,这里给大家简单解释一下闭包。

闭包的定义比较拗口,我们不去纠结它的具体定义,而是打算通过一个具体的例子去理解它:

Function closureFunc() {  var name = "Flutter"; // name 是一个被 init 创建的局部变量
  void displayName() { // displayName() 是内部函数,一个闭包
    print(name); // 使用了父函数中声明的变量
  }  return displayName;
}void main(){  //myFunc是一个displayName函数
  var myFunc = closureFunc(); //(1)
  
  // 执行displayName函数
  myFunc(); // (2)}

结果如我们所料的那样打印了Flutter

在(1)执行完之后,name作为一个函数的局部变量,引用的对象不是应该被回收掉了吗?但是当我们在内函数调用外部的name时,它依然可以神奇地被调用,这是为什么呢?

这是因为Dart在运行内部函数时会形成闭包,闭包是由函数以及创建该函数的词法环境组合而成,这个环境包含了这个闭包创建时所能访问的所有局部变量 。

我们简单变一下代码:

Function closureFunc() {  var name = "Flutter"; // name 是一个被 init 创建的局部变量
  void displayName() { // displayName() 是内部函数,一个闭包
    print(name); // 使用了父函数中声明的变量
  }
  name = 'Dart'; //重新赋值
  return displayName;
}

结果输出是Dart,可以看到内部函数访问外部函数的变量时,是在同一个词法环境中的。

返回值

在Dart中,所有的函数都必须有返回值,如果没有的话,那将自动返回null

foo() {}assert(foo() == null);

流程控制

这部分和大部分语言都一样,在这里简单过一下就行。

if-else

if(hasHause && hasCar){
    marry();
}else if(isHandsome){
    date();
}else{
    pass();
}

循环

各种for:

var list = [1,2,3];for(var i = 0; i != list.length; i++){}for(var i in list){}

while和循环中断(中断也是在for中适用的):

  var i = 0;  while(i != list.length){    if(i % 2 == 0){       continue;
    }     print(list[i]);
  }

  i = 0;  do{    print(list[i]);    if(i == 5){      break;
    }
  }while(i != list.length);

如果对象是Iterable类型,你还可以像Java的 lambada 表达式一样:

list.forEach((i) => print(i));
  
list.where((i) =>i % 2 == 0).forEach((i) => print(i));

switch

switch可以用于intdoubleString 和enum等类型,switch 只能在同类型对象中进行比较,进行比较的类不要覆盖==运算符。

var color = '';  switch(color){    case "RED":      break;    case "BLUE":      break;    default:
      
  }

assert

在Dart中,assert语句经常用来检查参数,它的完整表示是:assert(condition, optionalMessage),如果conditionfalse,那么将会抛出 [AssertionError]异常,停止执行程序。

assert(text != null);assert(urlString.startsWith('https'), 'URL ($urlString) should start with "https".');

assert 通常只用于开发阶段,它在产品运行环境中通常会被忽略。在下面的场景中会打开assert

  1. Flutter 的 debug mode

  2. 一些开发工具比如dartdevc默认会开启。

  3. 一些工具,像dart 和dart2js ,可以通过参数 --enable-asserts 开启。

异常处理

Dart 的异常处理和Java很像,但是Dart中所有的异常都是非检查型异常(unchecked exception),也就是说,你不必像 Java 一样,被强制需要处理异常。

Dart 提供了Exception 和 Error 两种类型的异常。 一般情况下,你不应该对Error类型错误进行捕获处理,而是尽量避免出现这类错误。

比如OutOfMemoryErrorStackOverflowErrorNoSuchMethodError等都属于Error类型错误。

前面提到,因为 Dart 不像 Java 那样可以声明编译期异常,这种做法可以让代码变得更简洁,但是容易忽略掉异常的处理,所以我们在编码的时候,在可能会有异常的地方要注意阅读API文档,另外自己写的方法,如果有异常抛出,要在注释处进行声明。比如类库中的File类其中一个方法注释:

  /**   * Synchronously read the entire file contents as a list of bytes.   *   * Throws a [FileSystemException] if the operation fails.
   */
  Uint8List readAsBytesSync();

抛出异常

throw FormatException('Expected at least 1 section');

throw除了可以抛出异常对象,它还可以抛出任意类型对象,但建议还是使用标准的异常类作为最佳实践。

throw 'Out of llamas!';

捕获异常

可以通过on 关键词来指定异常类型:

 var file = File("1.txt");  try{
    file.readAsStringSync();
  } on FileSystemException {     //do something
  }

使用catch关键词获取异常对象,catch有两个参数,第一个是异常对象,第二个是错误堆栈。

try{
    file.readAsStringSync();
} on FileSystemException catch (e){    print('exception: $e');
} catch(e, s){ //其余类型
   print('Exception details:\n $e');   print('Stack trace:\n $s');
}

使用rethrow 抛给上一级处理:

 try{
    file.readAsStringSync();
  } on FileSystemException catch (e){    print('exception: $e');
  } catch(e){     rethrow;
  }

finally

finally一般用于释放资源等一些操作,它表示最后一定会执行的意思,即便try...catch 中有return,它里面的代码也会承诺执行。

try{     print('hello');     return;
  } catch(e){     rethrow;
  } finally{     print('finally');
}

输出:

hellofinally

Dart 是一门面向对象的编程语言,所有对象都是某个类的实例,所有类继承了Object类。

一个简单的类:

class Point {  num x, y;  // 构造器
  Point(this.x, this.y);  
  // 实例方法
  num distanceTo(Point other) {    var dx = x - other.x;    var dy = y - other.y;    return sqrt(dx * dx + dy * dy);
  }
}

类成员

Dart 通过. 来调用类成员变量和方法的。

//创建对象,new 关键字可以省略var p = Point(2, 2);// Set the value of the instance variable y.p.y = 3;// Get the value of y.assert(p.y == 3);// Invoke distanceTo() on p.num distance = p.distanceTo(Point(4, 4));

你还可以通过.?来避免null对象。在Java 里面,经常需要大量的空判断来避免NullPonterException,这是让人诟病Java的其中一个地方。而在Dart中,可以很方便地避免这个问题:

// If p is non-null, set its y value to 4.p?.y = 4;

在 Dart 中,没有privateprotectedpublic这些关键词,如果要声明一个变量是私有的,则在变量名前添加下划线_,声明了私有的变量,只在本类库中可见。

class Point{  num _x;  num _y;
}

构造器(Constructor)

如果没有声明构造器,Dart 会给类生成一个默认的无参构造器,声明一个带参数的构造器,你可以像 Java这样:

class Person{  String name;  int sex;
  
  Person(String name, int sex){    this.name = name;    this.sex = sex;
  }
}

也可以使用简化版:

Person(this.name, this.sex);

或者命名式构造器:

Person.badGirl(){    this.name = 'Bad Girl';    this.sex = 1;
}

你还可以通过factory关键词来创建实例:

Person.goodGirl(){    this.name = 'good Girl';    this.sex = 1;
}factory Person(int type){    return type == 1 ? Person.badGirl(): Person.goodGirl();
}

factory对应到设计模式中工厂模式的语言级实现,在 Flutter 的类库中有大量的应用,比如Map

// 部分代码abstract class Map<K, V> {  
    factory Map.from(Map other) = LinkedHashMap<K, V>.from;
}

如果一个对象的创建过程比较复杂,比如需要选择不同的子类实现或则需要缓存实例等,你就可以考虑通过这种方法。在上面Map例子中,通过声明 factory来选择了创建子类LinkedHashMapLinkedHashMap.from也是一个factory,里面是具体的创建过程)。

如果你想在对象创建之前的时候还想做点什么,比如参数校验,你可以通过下面的方法:

 Person(this.name, this.sex): assert(sex == 1)

在构造器后面添加的一些简单操作叫做initializer list

在Dart中,初始化的顺序如下:

  1. 执行initializer list;

  2. 执行父类的构造器;

  3. 执行子类的构造器。

class Person{  String name;  int sex;

  Person(this.sex): name = 'a', assert(sex == 1){    this.name = 'b';    print('Person');
  }

}class Man extends Person{
   Man(): super(1){     this.name = 'c';     print('Man');
   }
}void main(){
  Person person = Man();  print('name : ${person.name}');
}

上面的代码输出为:

PersonManname : c

如果子类构造器没有显式调用父类构造器,那么默认会调用父类的默认无参构造器。显式调用父类的构造器:

Man(height): this.height = height, super(1);

重定向构造器:

Man(this.height, this.age): assert(height > 0), assert(age > 0);

Man.old(): this(12, 60); //调用上面的构造器

Getter 和 Setter

在 Dart 中,对 Getter 和 Setter 方法有专门的优化。即便没有声明,每个类变量也会默认有一个get方法,在隐含接口章节会有体现。

class Rectangle {  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);  num get right => left + width;  set right(num value) => left = value - width;  num get bottom => top + height;  set bottom(num value) => top = value - height;
}void main() {  var rect = Rectangle(3, 4, 20, 15);  assert(rect.left == 3);
  rect.right = 12;  assert(rect.left == -8);
}

抽象类

Dart 的抽象类和Java差不多,除了不可以实例化,可以声明抽象方法之外,和一般类没有区别。

abstract class AbstractContainer {  num _width;  void updateChildren(); // 抽象方法,强制继承子类实现该方法。

  get width => this._width;  
  int sqrt(){    return _width * _width;
  }
}

隐含接口

Dart 中的每个类都隐含了定义了一个接口,这个接口包含了这个类的所有成员变量和方法,你可以通过implements关键词来重新实现相关的接口方法:

class Person {  //隐含了 get 方法
  final _name;

  Person(this._name);  String greet(String who) => 'Hello, $who. I am $_name.';
}class Impostor implements Person {  // 需要重新实现
  get _name => '';  // 需要重新实现
  String greet(String who) => 'Hi $who. Do you know who I am?';
}

实现多个接口:

class Point implements Comparable, Location {...}

继承

和Java基本一致,继承使用extends关键词:

class Television {  void turnOn() {
    doSomthing();
  }
}class SmartTelevision extends Television {  @override
  void turnOn() {    super.turnOn(); //调用父类方法
    doMore();
  }
}

重载操作符

比较特别的是,Dart 还允许重载操作符,比如List类支持的下标访问元素,就定义了相关的接口:

 E operator [](int index);

我们通过下面的实例来进一步说明重载操作符:

class MyList{  var list = [1,2,3];  operator [](int index){    return list[index];
  }
}void main() {  var list = MyList();  print(list[1]); //输出 2}

扩展方法

这个特性也是Dart让人眼前一亮的地方(Dart2.7之后才支持),可以对标到 JavaScript 中的 prototype。通过这个特性,你甚至可以给类库添加新的方法:

//通过关键词 extension 给 String 类添加新方法extension NumberParsing on String {  int parseInt() {    return int.parse(this);
  }
}

后面String对象就可以调用该方法了:

print('42'.parseInt());

枚举类型

枚举类型和保持和Java的关键词一致:

enum Color { red, green, blue }

switch中使用:

// color 是 enmu Color 类型switch(color){    case Color.red:      break;    case Color.blue:      break;    case Color.green:      break;    default:      break;
}

枚举类型还有一个index的getter,它是个连续的数字序列,从0开始:

assert(Color.red.index == 0);assert(Color.green.index == 1);assert(Color.blue.index == 2);

新特性:Mixins

这个特性进一步增强了代码复用的能力,如果你有写过Android的布局XML代码或者Freemaker模板的话,那这个特性就可以理解为其中inlclude 的功能。

声明一个mixin类:

mixin Musical {  bool canPlayPiano = false;  bool canCompose = false;  bool canConduct = false;  void entertainMe() {    if (canPlayPiano) {      print('Playing piano');
    } else if (canConduct) {      print('Waving hands');
    } else {      print('Humming to self');
    }
  }
}

通过with关键词进行复用:

class Musician extends Performer with Musical {  // ···}class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

mixin类甚至可以通过on关键词实现继承的功能:

mixin MusicalPerformer on Musician {  // ···}

类变量和类方法

class Queue {  //类变量
  static int maxLength = 1000;  // 类常量
  static const initialCapacity = 16;  // 类方法
  static void modifyMax(int max){
    _maxLength = max;
  }
}void main() {  print(Queue.initialCapacity);
  Queue.modifyMax(2);  print(Queue._maxLength);
}

泛型

在面向对象的语言中,泛型主要的作用有两点:

1、类型安全检查,把错误扼杀在编译期:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);//编译错误names.add(42);

2、增强代码复用,比如下面的代码:

abstract class ObjectCache {  Object getByKey(String key);  void setByKey(String key, Object value);
}abstract class StringCache {  String getByKey(String key);  void setByKey(String key, String value);
}

你可以通过泛型把它们合并成一个类:

abstract class Cache<T> {
  T getByKey(String key);  void setByKey(String key, T value);
}

在Java中,泛型是通过类型擦除来实现的,但在Dart中实打实的泛型:

 var names = <String>[];
 names.addAll(['Tom',"Cat"]); // is 可以用于类型判断
 print(names is List<String>); // true
 print(names is List); // true
 print(names is List<int>); //false

你可以通过extends关键词来限制泛型类型,这点和Java一样:

abstract class Animal{}class Cat extends Animal{}class Ext<T extends Animal>{
  T data;
}void main() {  var e = Ext(); // ok
  var e1 = Ext<Animal>(); // ok
  var e2 = Ext<Cat>(); // ok
  var e3 = Ext<int>(); // compile error}

使用类库

有生命力的编程语言,它背后都有一个强大的类库,它们可以让我们站在巨人的肩膀上,又免于重新造轮子。

导入类库

在Dart里面,通过import关键词来导入类库。

内置的类库使用dart:开头引入:

import 'dart:io';

了解更多内置的类库可以查看这里。

第三方类库或者本地的dart文件用package:开头:

比如导入用于网络请求的dio库:

import 'package:dio/dio.dart';

Dart 应用本身就是一个库,比如我的应用名是ccsys,导入其他文件夹的类:

import 'package:ccsys/common/net_utils.dart';import 'package:ccsys/model/user.dart';

如果你使用IDE来开发,一般这个事情不用你来操心,它会自动帮你导入的。

Dart 通过pub.dev来管理类库,类似Java世界的Maven 或者Node.js的npm一样,你可以在里面找到非常多实用的库。

解决类名冲突

如果导入的类库有类名冲突,可以通过as使用别名来避免这个问题:

import 'package:lib1/lib1.dart';import 'package:lib2/lib2.dart' as lib2;// 使用来自 lib1 的 ElementElement element1 = Element();// 使用来自 lib2 的 Elementlib2.Element element2 = lib2.Element();

导入部分类

在一个dart文件中,可能会存在很多个类,如果你只想引用其中几个,你可以增加show或者hide来处理:

//文件:my_lib.dartclass One {}class Two{}class Three{}

使用show导入OneTwo类:

//文件:test.dartimport 'my_lib.dart' show One, Two;void main() {  var one = One();  var two = Two();  //compile error
  var three = Three();
}

也可以使用hide排除Three,和上面是等价的:

//文件:test.dartimport 'my_lib.dart' hide Three;void main() {  var one = One();  var two = Two();
}

延迟加载库

目前只有在web app(dart2js)中才支持延迟加载,Flutter、Dart VM是不支持的,我们这里仅做一下简单介绍。

你需要通过deferred as来声明延迟加载该类库:

import 'package:greetings/hello.dart' deferred as hello;

当你需要使用的时候,通过loadLibrary()加载:

Future greet() async {  await hello.loadLibrary();
  hello.printGreeting();
}

你可以多次调用loadLibrary,它不会被重复加载。

异步支持

这个话题稍微复杂,我们将用另外一篇文章独立讨论这个问题,请留意下一篇内容。

参考资料

  1. 学习Dart的十大理由

  2. A tour of the Dart language

  3. Dart 常量构造器的理解

  4. JavaScript 闭包

关于AgileStudio

我们是一支由资深独立开发者和设计师组成的团队,成员均有扎实的技术实力和多年的产品设计开发经验,提供可信赖的软件定制服务。

自然语言处理 Dart JavaScript 前端开发 Java 编译器 应用服务中间件 开发工具 Maven 容器


]]>
spark过节监控告警系统实现 Mon, 27 Jan 2020 20:58:34 +0800 马上要过年了,大部分公司这个时候都不会再去谋求开新业务,而大数据工匠们,想要过好年,就要保证过年期间自己对自己的应用了如执掌。一般公司都会有轮值人员,至少要有春节应急预案,尤其是对于我们这些搞平台,或者线上应用的,应急预案更是必不可少。今天浪尖主要是分享一下关于在yarn上的spark 任务我们应该做哪些监控,如何监控。

Spark on yarn这种应用形态目前在企业中是最为常见的,对于这种spark的任务,浪尖觉得大家关心的指标大致有:app存活,spark streaming的job堆积情况,job运行状态及进度,stage运行进度,rdd缓存监控,内存监控等。

其实,春节最为重要的就是app存活了,春节期间各大应用应该都会有一部分数据增量,那么实际上就需要我们的程序能有一定的抗流量尖峰的能力,这个也很常见,因为正常的app都会有流量尖峰和低谷,你做一个实时应用程序,必须要去应对流量尖峰,也就是说你程序的处理能力正常要大于流量尖峰的,要是你的数据流量有历史信息,那么就简单了,只需要将spark streaming和flink的处理能力盖过流量最高值即可。当然,会有人说spark streaming 和flink不是有背压系统吗,短暂的流量尖峰可以抗住的呀,当然太短暂的几分钟的流量尖峰,而且你的任务对实时性要求不高,那是可以,否则不行。

1. App存活监控

企业中,很多时候spark的任务都是运行与yarn上的,这个时候可以通过yarn的客户端获取rm上运行 任务的状态。

Configuration conf = new YarnConfiguration();
YarnClient yarnClient = YarnClient.createYarnClient();
yarnClient.init(conf);
yarnClient.start();try{
   List<ApplicationReport> applications = yarnClient.getApplications(EnumSet.of(YarnApplicationState.RUNNING, YarnApplicationState.FINISHED));   System.out.println("ApplicationId ============> "+applications.get(0).getApplicationId());   System.out.println("name ============> "+applications.get(0).getName());   System.out.println("queue ============> "+applications.get(0).getQueue());   System.out.println("queue ============> "+applications.get(0).getUser());
} catch(YarnException e) {
   e.printStackTrace();
} catch(IOException e) {
   e.printStackTrace();
}
       yarnClient.stop();

这种api只适合,spark 和 MapReduce这两类应用,不适合flink。做过flink的应该都很容易理解吧,yarn上运行的flink任务显示,running,但是flink app内部的job却已经挂掉了,这种yarn的flink任务存活不适合,只能用RestClusterClient,具体浪尖在这里就不举例子了,本文主要是讲监控spark应用体系,后续会给出demo测试。

写个yarn的监控

对于这个APP的监控,还有更加细节的监控,比如executor数,内存,CPU等。获取指标的方法:

1.1 ApplicationInfo

通过SparkContext对象的AppStatusStore对象获取ApplicationInfo

val statusStore = sparkContext.statusStore
statusStore.applicationinfo()

获取一个ApplicationInfo对象,然后主要包含以下schema

case class ApplicationInfo private[spark](    id: String,    name: String,    coresGranted: Option[Int],    maxCores: Option[Int],    coresPerExecutor: Option[Int],    memoryPerExecutorMB: Option[Int],    attempts: Seq[ApplicationAttemptInfo])

1.2 AppSummary

通过SparkContext对象的AppStatusStore对象 获取AppSummary

val statusStore = sparkContext.statusStore
statusStore.appSummary()statusStore.appSummary().numCompletedJobs
statusStore.appSummary().numCompletedStages

2.Job监控

主要包括job的运行状态信息,spark streaming的job堆积情况。这个浪尖知识星球里面也分享过主要是自己实现一个StreamingListener,然后通过StreamingContext的实例对象注册到SparkListenerbus即可。

浪尖这里只会举一个就是spark streaming 数据量过大,导致batch不能及时处理而使得batch堆积,实际上就是active batch -1,针对这个给大家做个简单的案例,以供大家参考。

image.png

val waitingBatchUIData = new HashMap[Time, BatchUIData]
ssc.addStreamingListener(new StreamingListener {  override def onStreamingStarted(streamingStarted: StreamingListenerStreamingStarted): Unit = println("started")  override def onReceiverStarted(receiverStarted: StreamingListenerReceiverStarted): Unit = super.onReceiverStarted(receiverStarted)  override def onReceiverError(receiverError: StreamingListenerReceiverError): Unit = super.onReceiverError(receiverError)  override def onReceiverStopped(receiverStopped: StreamingListenerReceiverStopped): Unit = super.onReceiverStopped(receiverStopped)  override def onBatchSubmitted(batchSubmitted: StreamingListenerBatchSubmitted): Unit = {
    synchronized {
      waitingBatchUIData(batchSubmitted.batchInfo.batchTime) =        BatchUIData(batchSubmitted.batchInfo)
    }
  }  override def onBatchStarted(batchStarted: StreamingListenerBatchStarted): Unit =     waitingBatchUIData.remove(batchStarted.batchInfo.batchTime)  
  override def onBatchCompleted(batchCompleted: StreamingListenerBatchCompleted): Unit = super.onBatchCompleted(batchCompleted)  override def onOutputOperationStarted(outputOperationStarted: StreamingListenerOutputOperationStarted): Unit = super.onOutputOperationStarted(outputOperationStarted)  override def onOutputOperationCompleted(outputOperationCompleted: StreamingListenerOutputOperationCompleted): Unit = super.onOutputOperationCompleted(outputOperationCompleted)
})

最终,我们使用waitingBatchUIData的大小,代表待处理的batch大小,比如待处理批次大于10,就告警,这个可以按照任务的重要程度和持续时间来设置一定的告警规则,避免误操作。

3. Stage监控

Stage的运行时间监控,这个重要度比较低。使用的主要API是statusStore.activeStages()得到的是一个Seq[v1.StageData] ,StageData可以包含的信息有:

class StageData private[spark](    val status: StageStatus,    val stageId: Int,    val attemptId: Int,    val numTasks: Int,    val numActiveTasks: Int,    val numCompleteTasks: Int,    val numFailedTasks: Int,    val numKilledTasks: Int,    val numCompletedIndices: Int,    val executorRunTime: Long,    val executorCpuTime: Long,    val submissionTime: Option[Date],    val firstTaskLaunchedTime: Option[Date],    val completionTime: Option[Date],    val failureReason: Option[String],    val inputBytes: Long,    val inputRecords: Long,    val outputBytes: Long,    val outputRecords: Long,    val shuffleReadBytes: Long,    val shuffleReadRecords: Long,    val shuffleWriteBytes: Long,    val shuffleWriteRecords: Long,    val memoryBytesSpilled: Long,    val diskBytesSpilled: Long,    val name: String,    val description: Option[String],    val details: String,    val schedulingPool: String,    val rddIds: Seq[Int],    val accumulatorUpdates: Seq[AccumulableInfo],    val tasks: Option[Map[Long, TaskData]],    val executorSummary: Option[Map[String, ExecutorStageSummary]],    val killedTasksSummary: Map[String, Int])

具体细节大家也可以详细测试哦。

4. RDD监控

这个其实大部分时间我们也是不关心的,主要是可以获取rdd相关的指标信息:

通过SparkContext对象的AppStatusStore

val statusStore = sparkContext.statusStore
statusStore.rddList()

可以获取一个Seq[v1.RDDStorageInfo]对象,可以获取的指标有:

class RDDStorageInfo private[spark](    val id: Int,    val name: String,    val numPartitions: Int,    val numCachedPartitions: Int,    val storageLevel: String,    val memoryUsed: Long,    val diskUsed: Long,    val dataDistribution: Option[Seq[RDDDataDistribution]],    val partitions: Option[Seq[RDDPartitionInfo]])class RDDDataDistribution private[spark](    val address: String,    val memoryUsed: Long,    val memoryRemaining: Long,    val diskUsed: Long,    @JsonDeserialize(contentAs = classOf[JLong])    val onHeapMemoryUsed: Option[Long],
    @JsonDeserialize(contentAs = classOf[JLong])    val offHeapMemoryUsed: Option[Long],
    @JsonDeserialize(contentAs = classOf[JLong])    val onHeapMemoryRemaining: Option[Long],
    @JsonDeserialize(contentAs = classOf[JLong])    val offHeapMemoryRemaining: Option[Long])class RDDPartitionInfo private[spark](    val blockName: String,    val storageLevel: String,    val memoryUsed: Long,    val diskUsed: Long,    val executors: Seq[String])

其中,还有一些api大家自己也可以看看。

5. Rdd内存及缓存监控

主要是监控executor的内存使用情况,然后对一些高内存的任务能及时发现,然后积极排查问题。这个问题监控也比较奇葩,主要是监控RDD的内存和磁盘占用即可。对于缓存的rdd获取,只需要statusStore.rddList()获取的时候给定boolean参数true即可。获取之后依然是一个RDD列表,可以参考4,去进行一些计算展示。

6.Executor监控

关于内存的监控,除了存活监控之外,还有单个executor内存细节。Executor的注册,启动,挂掉都可以通过SparkListener来获取到,而单个executor内部的细节获取也还是通过SparkContext的一个内部变量,叫做SparkStatusTracker。

sc.statusTracker.getExecutorInfos

得到的是一个Array[SparkExecutorInfo],然后通过SparkExecutorInfo就可以获取细节信息:

private class SparkExecutorInfoImpl(    val host: String,    val port: Int,    val cacheSize: Long,    val numRunningTasks: Int,    val usedOnHeapStorageMemory: Long,    val usedOffHeapStorageMemory: Long,    val totalOnHeapStorageMemory: Long,    val totalOffHeapStorageMemory: Long)  extends SparkExecutorInfo

7.总结

浪尖常用的监控就是一app存活监控,二就是定义sparklistener,实现检测sparkstreaming 队列积压了。

总有粉丝问浪尖,如何发现这些细节的,当然是看源码的api,分析源码得到的,框架细节只能如此获得。


]]>
CentOS8 安装 MySQL8.0 Mon, 27 Jan 2020 20:58:34 +0800

环境:Linux centos8 4.18.0-80.el8.x86_64、Mysql8.0.18

1、Mysql官网下载RPM包

Centos8安装Mysql8.0(RPM)

Centos8安装Mysql8.0(RPM)

2、检查是否安装过

rpm -qa | grep -i mysql

3、卸载之前的安装

rpm -e --nodeps 软件名 //强力删除,对相关依赖的文件也进行强力删除

4、安装客户端(mysql-client)

rpm -ivh rpm包
* 安装mysql-community-common-8.0.18* 安装mysql-community-libs-8.0.18* 安装mysql-community-client-8.0.18(前两个是其依赖)

Centos8安装Mysql8.0(RPM)

Centos8安装Mysql8.0(RPM)

Centos8安装Mysql8.0(RPM)

5、安装服务端(mysql-server)

Centos8安装Mysql8.0(RPM)

Centos8安装Mysql8.0(RPM)

6、查看是否安装成功

有以下方式查看:
* ps -ef | grep mysql
* cat /etc/group | grep mysql
* cat /etc/password | grep mysql
* mysqladmin --version...

Centos8安装Mysql8.0(RPM)

Centos8安装Mysql8.0(RPM)

Centos8安装Mysql8.0(RPM)

7、初始化mysql

mysqld --initialize //创建数据文件目录和mysql系统数据库 产生随机root密码

8、启动mysql服务

systemctl start mysqld

Centos8安装Mysql8.0(RPM)

启动失败,因为/var/lib/mysql目录权限不够

Centos8安装Mysql8.0(RPM)

9、/var/lib/mysql目录权限授权

chown -R mysql:mysql /var/lib/mysql/

Centos8安装Mysql8.0(RPM)

10、启动mysql服务

systemctl start mysqld //启动ps -ef | grep mysql //查看mysql服务

Centos8安装Mysql8.0(RPM)

11、查看初始化随机生成的root密码

cat /var/log/mysqld.log | grep password

Centos8安装Mysql8.0(RPM)

12、安全设置

mysql_secure_installation

Centos8安装Mysql8.0(RPM)

Centos8安装Mysql8.0(RPM)

13、mysql登录

mysql -uroot -p //随机密码登录

Centos8安装Mysql8.0(RPM)

14、重置密码(Mysql8.0+有变化)

先把root的旧密码置空
use mysql;
update user set authentication_string='' where user='root';
备注:Mysql5.7+ password字段 已改成 authentication_string字段

MySQL8.0 忘记 root 密码下如何修改密码

重置成新密码
alter user 'root'@'localhost' identified by 'newpassword';
备注:Mysql8.0修改密码方式已有变化(此处是个坑,需要注意)
Mysql8.0之前:
update user set password=password('root') where user='root';

MySQL8.0 忘记 root 密码下如何修改密码

15、退出后使用新密码再登录mysql

mysql -uroot -proot

MySQL8.0 忘记 root 密码下如何修改密码

16、如何停止、重启和查看mysql服务

systemctl stop mysqld //停止服务systemctl restart mysqld //重启服务systemctl status mysqld //查看服务

17、mysql的相关安装目录文件

/usr/bin //相关命令

Centos8安装Mysql8.0(RPM)

/usr/share/mysql //配置文件目录

Centos8安装Mysql8.0(RPM)

/var/lib/mysql //数据库文件存放目录

Centos8安装Mysql8.0(RPM)

/etc/my.cnf //mysql的启动配置文件

Centos8安装Mysql8.0(RPM)

18、后记

my.ini //windows操作系统下的配置文件my.cnf //linux操作系统下的配置文件mysqld //是后台守护进程,即mysql daemonmysql //是客户端命令行


]]>
阿里云新年优惠,新年下单每两千元返50元天猫购物卡 Mon, 27 Jan 2020 20:58:34 +0800 这是爱与真诚的感恩----

       新年采购阿里云每满2000元即可获得50元天猫购物卡。

       比如购买订单总额20000元即可获得500元价值天猫购物卡。

本次活动对象:

       所有通过我们在官网下单的阿里云客户均可参与活动。

活动时间:

       2020年1月6日--2020年2月15日

活动结算时间:

        活动结束后10个工作日内结算

活动联系人:

陈先生   

电话/微信/钉钉:14737363737

其他活动

安心过大年-安全营销全搞定;阿里云保你春节无忧。 https://www.aliyun.com/daily-act/happy-2020?scm

企业级独享实例1折  高稳定、高可靠特性,适用各种企业应用场景。  https://promotion.aliyun.com/ntms/act/enterprise-discount.html


https://promotion.aliyun.com/ntms/act/enterprise-discount.html?scm=20140722.1411.5.1434&aly_as=eAx4LOvl&source=5176.11533457&userCode=d4m00na3&type=copyhttps://promotion.aliyun.com/ntms/act/enterprise-discount.html?scm=20140722.1411.5.1434&aly_as=eAx4LOvl&source=5176.11533457&userCode=d4m00na3&type=copy

                                                                                                                                   广州米姆信息科技有限公司

                                                                                                                                   阿里云华南合作伙伴

                                                                                                                                   2020年1月6日


]]>
【升级】1月7日至9日分析型数据库MySQL版升级通知 Mon, 27 Jan 2020 20:58:34 +0800

【阿里云】【分析型数据库MySQL版】【升级通知】

升级窗口:

北京时间2020年1月7日 10:00 - 18:00

北京时间2020年1月8日 10:00 - 18:00

北京时间2020年1月9日 10:00 - 18:00

升级内容:

升级版本:V2.10.2

升级地域:

 1月7日:华东1金融云、华南1金融云、阿里政务云、新加坡、香港和华北3

 1月8日:美国、华东1和华东2

 1月9日:华南1和华北2

具体升级内容:

修复了子账号在某些场景下update可能会报错的问题。

升级影响:

 1.在升级期间,如果有建表或者删表操作,可能会偶发性的失败,需要重试;

 2.升级您的DB期间,导入有可能会出现短暂的暂停,升级完即刻恢复;

 3.升级您的DB期间,可能会出现短暂的写入中断或者查询闪断,需要应用端重试解决。

给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

]]>
【升级】MQ接入点Region化通知 Mon, 27 Jan 2020 20:58:34 +0800

【阿里云】【MQ】【Region化切换通知】

升级时间:2020年6月30日前

升级内容:为了进一步提升MQ产品的稳定性,保障客户业务的稳定,MQ的接入点需要Region化,即不同的MQ Region 服务,需要使用不同的接入点。

需要您配合,自行根据文档检查应用是否是使用Region化的接入点,如果不是Region化的接入点,则需要合理安排项目迭代,经测试后,切换到Region化的接入点。

Region化的接入点比原接入模式的稳定性更高,我们希望您的应用在2020年6月30日前完成切换,超过期限后,原接入点稳定性会更低。

如果应用本身没有跨Region使用MQ,发送方和订阅方变更可以不同步。如果应用有跨域使用MQ,这个需要根据业务评估切换的时机。接入点Region化文档

升级影响:

1. 如果业务有跨Region使用MQ,使用新的Region化接入点,则不支持这种用法 。

2. 如果已经是Region化的接入点,使用则不受影响。

]]>
【升级】1月6日DDoS高防实例到期超过7天停止业务流量转发通知 Mon, 27 Jan 2020 20:58:34 +0800

【阿里云】【DDOS】【变更通知】

升级窗口:北京时间 2020年1月6日 00:00 - 00:00

升级内容:DDoS高防实例到期超过7天停止业务流量转发

升级影响:2020年1月6日0点起,DDoS高防实例到期超过7天,将停止该高防实例的业务流量转发。如果您正在使用DDoS高防,请留意当前高防实例的到期状态,及时完成续费或设置成自动续费,避免因超过7天未续费,影响正常业务。

]]>
【升级】1月7日和9日DDoS高防(旧版)系统升级通知 Mon, 27 Jan 2020 20:58:34 +0800

【阿里云】【DDos高防(旧版)】【升级通知】

升级窗口:北京时间 2020年1月7日 01:00 - 07:00,1月9日 01:00 - 07:00

升级内容:DDoS高防(旧版)武汉电信机房进行系统升级操作
升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断2-4次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

]]>
【漏洞预警】mongo-express远程命令执行漏洞(CVE-2019-10758) Mon, 27 Jan 2020 20:58:34 +0800

2020年1月3日,阿里云应急响应中心监测到国外研究人员披露CVE-2019-10758漏洞分析利用代码。利用该漏洞可以直接获取服务器权限。


漏洞描述

mongo-express是一个基于Node.js和express的开源的MongoDB Web管理界面。在小于0.54.0的版本中,在经过认证后通过访问特定接口,构造恶意请求,攻击者可以执行任意命令。由于许多mongo-express服务存在未授权访问漏洞,攻击者结合该漏洞可以直接获取服务器权限,阿里云应急响应中心提醒mongo-express用户尽快采取安全措施阻止漏洞攻击。


影响版本

mongo-express < 0.54.0


安全版本

mongo-express >= 0.54.0


安全建议

1. 升级至安全版本。

2. 开启登录验证。


相关链接

https://github.com/masahiro331/CVE-2019-10758/



我们会关注后续进展,请随时关注官方公告。

如有任何问题,可随时通过工单或服务电话95187联系反馈。

阿里云应急响应中心

2020.01.03

]]>
阿里总监谢纯良:我们阿里是如何打造业务中台的? Mon, 27 Jan 2020 20:58:34 +0800 阿里总监谢纯良,在首届全国中台战略大会暨第三届互联网架构峰会上做了精彩分享,题为《阿里巴巴业务中台实践与思考》。

分享嘉宾介绍:谢纯良,阿里云智能事业群,技术方案总监,负责中间件&业务中台,专注于阿里中间件&业务中台技术方案产品化、商业化。



]]>
我写了条商品评论,没想到卖家这样回我!| WWW 阿里优秀论文-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800

1.jpg

评论回复生成任务

在电商平台上,用户针对所购买的商品会留下大量的评论。当用户在评论中提到了一些商品的问题时,商家常常需要通过回复来帮助解决这些问题,否则会对商品的销量带来不利的影响。然而评论的数量越来越多,如果对所有的评论都写回复,需要耗费大量的人力物力。因此开发针对评论的自动回复生成模型就显得很有必要了。

基于序列到序列的文本生成模型[1],在很多方面都取得了成功。然而,针对评论回复生成任务,标准的 Seq2Seq+Attention 模型只能将评论文本的信息考虑进来。而每一个评论都是针对每一个具体的商品而写的,这个商品信息对于生成高质量的回复是很重要的。下图展示了一个淘宝平台上的例子。

3.png

在这个例子中,给定了一条负面的评论,当没有考虑商品信息时,Seq2Seq +Attention 模型倾向于产生通用性的回复,比如“感谢您的支持和反馈”或者"我们会继续努力为您提供更好的服务”。从用户的角度来讲,看到这样的回复的感受是很不好的,这并没有解决他的问题。而在真实情况下,商家通常会根据商品的信息来进行回复,比如在这个例子中,商家提到的店名(奈诗图旗舰店)和材质(聚酯纤维)等等。如果不把商品信息考虑进来,模型就很难生成这样高质量的回复。

在这篇论文中,我们基于序列到序列框架提出了融合商品信息的评论回复生成模型。商品信息通过表格的方式表示,其中包含很多"字段-值”对。下图 (a) 给出了一个例子。

4.png

任务定义

下表给出了本文中使用的主要符号及其含义。

5.png

1.png

我们把回复生成形式化为概率模型,即我们希望在给定X和T的情况下,最大化 Y 的概率:

15.png


商品信息中字段表示方法
2.png

模型网络架构

28.png

上图给出了模型网络架构的示意图。融合商品信息的回复生成模型主要包含四个部分:评论编码器(蓝色)、商品信息编码器(绿色)、回复解码器( 红色)和门限多模态单元(Gated Multimodal Unit,GMU)(黄色)。

评论编码器

3.png

商品信息编码器

4.png

回复解码器
5.png

门限多源注意力机制
6.png
7.png

复制机制
8.png
9.png

结合强化学习方法
在训练的过程中,我们知道正确的输出回复,通过最小化如上损失函数来训练模型。然而在测试阶段,解码器根据上一时刻预测的词来生成下一时刻的词。如果某一步产生错误,随着解码的过程,错误将会累积[4]。由于这一问题,通过最大似然损失来训练模型并不总能产生基于某个评测指标(如 ROUGE,BLEU)的最优的结果,而通常是次优的结果。

11.png

实验

数据集

我们从中国最大的电子商务网站淘宝网(taobao.com)上创建了淘宝数据集。在该数据集中共包含了 100000个(评论,商品信息,回复)三元组。所有的数据都是从服装这个类别下的商品详情页面获得。经统计,在该数据集中,评论的平均长度是 39 个词,而回复的平均长度是 72 个词。商品信息中的记录条数则平均有 15 条。更详细的统计数据可以见下表。

112.png
在我们的实验中,我们使用80%的数据作为训练集,使用10%的数据作为测试集,以及使用10%数据作为验证集。该数据已经进行了开源。

评价指标

为了评估我们的方法的有效性,我们在淘宝数据集上进行实验,并且使用如下几个评价指标来评测生成的文本质量:

  • ROUGE:ROUGE 值是一个被广泛应用在文本摘要领域的自动评价指标。ROUGE 值的计算通过 pyrouge 包[8]获得。在我们的实验中,我们给出 ROUGE- 1,ROUGE-2 和 ROUGE-L 的值。
  • METEOR:METEOR[9]具有几个其他评价指标不具备的特点,比如词干和同义词的匹配,同时,METEOR 也与人工评测比较接近。
  • BLEU:BLEU 是一个广泛应用在机器翻译中的评价指标。BLEU 用来衡量正式文本与生成文本之间的重合度。BLEU 值通过 NLTK 包[10]进行计算, 这里得到的值是 BLEU-1~4 的平均值。
  • 人工评测:我们随机从测试集中采样了 100 个样例,并且邀请了五个不同的志愿者来评测这些生成文本的质量好坏。对于每一个(评论,商品信息,回复)三元组,志愿者需要给出 {0,1,2} 的打分。其中 0 表示回复与评论不相关,或者语句不通顺,含有语法错误;1 表示回复与评论相关,但还是不够好,信息量不够;2 表示评论与回复相关,并且语句通顺自然,回答了评论中的问题。

实验设置

在实验中,我们采用了如下几个基线模型:

12.png

对于基线模型和我们的模型,我们都采用带动量的梯度下降算法(SGD with Momentum)来对模型进行训练。其中动量的大小为 0.9,初始学习率为 0.02。经过 10 轮迭代后,每 4000 步学习率会衰减一次,衰减率为 0.8。我们使用 dropout 技巧来防止过拟合,其中丢弃率为 15%。为了解决梯度爆炸问题,我们进行了梯度裁剪,使得梯度的范数不超过 5。对于词向量,我们首先用 word2vec 进行预训练来进行初始化,然后在训练过程中进行调优。字段向量和位置向量都是随机初始化的。对于所有模型,我们都使用两层的 GRU,其中隐含状态维数为 256。评论和回复共享一个词表,其中词表大小为 15000。超参数详情可以见下表。

114.png

13.png

实验结果

  • 自动评测结果

    116.png


我们首先讨论使用自动评价指标的结果。上表给出了四个基线模型和我们的模型的结果。根据上表结果,我们可以发现“Pointer-Generator”在除了 ROUGE 外的所有指标上获得了比“Seq2Seq + Atte”更好的结果,而“Copynet”在所有评价指标上都超过了“Seq2Seq + Atte”。

这表明了复制机制和指针机制确实很有效。与其他三个基线模型相比,“Copynet + PI”在所有评价指标上都获得了提升。如相比于“Copynet”,ROUGE-L 提升了3.98,METEOR 提升了2.05,BLEU 提升了6.29。这个结果表明,商品信息对于生成高质量的回复确实很有帮助。上表的最后一行给出了我们模型的结果,在所有评价指标上都超过了 4 个基线模型。如相比于“Copynet + PI”,我们的模型在 ROUGE-L 上提升了1.37,METEOR 上提升了0.62,BLEU 上提升了1.55。

■ 人工评测结果

117.png

上表给出了人工评测的结果。在上表中,通过综合 5 个志愿者的结果,我们给出了每个得分所占的百分比。我们也计算了不同志愿者打分之间的方差,结果为 0.25。这表明不同人的打分还比较一致。

从上表中也可以看出,结合商品信息的模型(“Copynet + PI”以及我们的模型)比其他基线模型产生了更多优质的回复(也就是得分为 2)以及更少的通用性回复(得分为 1)。在人工评测中,我们的模型仍然获得了最好的结果。在我们的模型产生的回复中,72.6%获得了 2 分,相比于“Copynet + PI”提升了2.6%。同时,只有12.0%的回复获得了 0 分。除了这些模型产生的回复,我们也把商家所写的回复进行了人工评测,可见于上表的最后一行。商家所写的回复中,有82.4%获得了 2 分,比我们的模型高了9.8%。10.8%的回复获得了 1 分,而只有6.8%的回复获得了 0 分。这表明,我们的模型仍然有巨大的提升空间。

分析

■ 商品信息的影响

118.png

在评论回复的自动生成过程中,我们的模型中使用了商品信息来辅助生成更高质量的回复。在本节中,我们分析商品信息对于模型结果的影响。因此,我们针对使用不同数量(20%,40%,60%,80%,100%)的商品信息进行了实验。这里,20% 意味着我们只使用一个商品中商品信息的 20%。上表给出了我们的模型使用不同商品信息的结果。由此,我们可以发现,随着使用商品信息的增加,所有评价指标都变得越来越高,这表明了商品信息对于评论回复生成任务的重要性。

■ 模型各个组件的影响

119.png

为了分析模型中不同组件对于结果的影响,我们对去除掉其中一部分后的模型进行了实验。上表给出了本次实验的结果,其中第一行为完整模型的结果,其他行分别为:去掉强化学习训练的结果(上表中的 -RL),去掉 GMU 门机制的结果(上表中的 -Gate),去掉复制机制的结果(上表中的 -Copy),去掉字段表示的结果(上表中的 -Field)。
14.png
■ 样例分析

122.png

为了比较不同模型生成的回复之间的不同。我们在上图中给出了两个例子。在上图左边的是一个正面评论的例子,在右边的是一个负面评论的例子。在评论的下方,我们给出了两个基线模型(“Copynet” 和 “Copynet+PI”)以及我们的模型生成的回复。从上图的结果中,我们可以看到,Copynet 对于正面或者是负面的评论都倾向于生成通用性的回复,比如:“您的满意是我们最大的动力”和“我们会继续努力为您提供更优质的服务”。这种通用性回复会给用户一种非常不好的用户体验,尤其是当用户给出的是负面的评论的时候。相反,结合了商品信息的模型(“Copynet+PI”以及我们的模型)生成的回复更多样性,更有针对性。对于不同的评论和商品,能给出不同的回复。比如,在它们的回复中,不仅给出了解释,还提到了品牌名(“伊芙心悦”和“cachecache”)。这种类型的回复对于用户来说更为友好。

小结

在本论文中,我们首先引入了电商平台的评论回复生成任务。给定一条评论,我们希望模型能自动生成回复。评论回复生成任务对于客服来说是非常有帮助的,这能大大减轻人工书写回复的压力,但是该任务之前却没有被仔细研究过。

为了解决该问题,我们首先观测到了商品的信息对于生成高质量的回复是极为重要的。接着,我们基于序列到序列提出了一个可以融合商品信息的深度神经网络。在我们的模型中,我们使用了门限多源注意力机制和复制机制来更好的利用商品信息。除此之外,我们还使用了一种强化学习技巧来帮助模型进行学习优化。

我们基于淘宝网构建了一个 100000 量级的数据集,并且我们开源了该数据集。通过使用多个自动评价指标和人工评测,以及与多个基线模型进行比较实验,证明了我们的模型在该任务上的优势。同时,针对我们的模型进行了大量的分析和研究,包括商品信息对于模型性能的影响分析,模型各组件的作用分析,以及样例分析。

本篇论文收录于顶会 WWW2019

参考文献:

1.BAHDANAUD,CHOK,BENGIOY.Neural machine translation by jointly learning to align and translate[J]. arXiv preprint arXiv:1409.0473, 2014.

2.LEBRETR,GRANGIERD,AULIM.Neural text generation from structured data with application to the biography domain[C]//Proceedings of the 2016 Conference on Empirical Methods in Natural Language Processing. 2016: 1203-1213.
3.AREVALO J, SOLORIO T, MONTES-Y GÓMEZ M, et al. Gated multimodal units for information fusion[J]. arXiv preprint arXiv:1702.01992, 2017.
4.RANZATOM,CHOPRAS,AULIM,etal.Sequence level training with recurrent neural networks[J]. arXiv preprint arXiv:1511.06732, 2015.
5.SEE A, LIU P J, MANNING C D. Get to the point: Summarization with pointer-generator networks[C]//Proceedings of the 55th Annual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers): volume 1. 2017: 1073-1083.
6.GU J, LU Z, LI H, et al. Incorporating copying mechanism in sequence-to-sequence learning[C]//Proceedings of the 54th Annual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers): volume 1. 2016: 1631-1640.
7.Steven J Rennie, Etienne Marcheret, Youssef Mroueh, Jarret Ross, and VaibhavaGoel. 2017. Self-critical sequence training for image captioning. In CVPR.
8.https://pypi.org/project/pyrouge/
9.http://www.cs.cmu.edu/~alavie/METEOR/
10.https://www.nltk.org

]]>
想买奶茶,高德如何让我更快喝到?-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 作者 | 雪糥,星泉

1.jpg

对搜索排序模块做重构

搜索建议(suggest服务)是指:用户在输入框输入query的过程中,为用户自动补全query或POI(Point of Interest,兴趣点,地理信息系统中可以是商铺、小区、公交站等地理位置标注信息),罗列出补全后的所有候选项,并进行智能排序。

2.jpg


我们希望通过suggest服务:智能提示,降低用户的输入成本。它的特点是:响应快、不承担复杂query的检索,可以把它理解为一个简化版的LBS领域信息检索服务。

和通用IR系统一样,suggest也分为doc(LBS中的doc即为POI)的召回和排序两个阶段。其中,排序阶段主要使用query和doc的文本相关性,以及doc本身的特征(weight、click),进行加权算分排序。

但随着业务的不断发展、特征规模越来越大,人工调参逐渐困难,基于规则的排序方式已经很难得到满意的效果。这种情况下,为了解决业务问题,将不得不打上各种补丁,导致代码难以维护。

3.jpg


因此,我们决定对排序模块进行重构,Learning to Rank无疑是一个好的选择。

面临的挑战:样本构造、模型调优

Learning to Rank(LTR)是用机器学习的方法来解决检索系统中的排序问题。业界比较常用的模型是gbrank,loss方案用的最多的是pair wise,这里也保持一致。一般应用LTR解决实际问题,最重要的问题之一就是如何获得样本。

首先,高德地图每天的访问量巨大,这背后隐藏的候选POI更是一个天文数字,想要使用人工标注的方法去获得样本明显不现实。

其次,如果想要使用一些样本自动构造的方法,比如基于用户对POI的点击情况构建样本pair ,也会遇到如下的问题:

  • 容易出现点击过拟合,以前点击什么,以后都给什么结果。
  • 有时,用户点击行为也无法衡量真实满意度。
  • suggest前端只展示排序top10结果,更多的结果没有机会展现给用户,自然没有点击。
  • 部分用户习惯自己输入完整query进行搜索,而不使用搜索建议的补全结果,统计不到这部分用户的需求。

对于这几个问题总结起来就是:无点击数据时,建模很迷茫。但就算有某个POI的点击,却也无法代表用户实际是满意的。

最后,在模型学习中,也面临了特征稀疏性的挑战。统计学习的目标是全局误差的一个最小化。稀疏特征由于影响的样本较少,在全局上影响不大,常常被模型忽略。但是实际中一些中长尾case的解决却往往要依靠这些特征。因此,如何在模型学习过程中进行调优是很重要。

系统建模过程详解

上一节,我们描述了建模的两个难题,一个是样本如何构造,另一个是模型学习如何调优。先看下怎么解决样本构造难题,我们解决方案是:

  • 考量用户在出行场景的行为session,不光看在suggest的某次点击行为,更重要的是,考察用户在出行场景下的行为序列。比如suggest给出搜索建议后,继续搜索的是什么词,出行的地点是去哪里,等等。
  • 不是统计某个query下的点击, 而是把session看作一个整体,用户在session最后的点击行为,会泛化到session中的所有query上。

详细方案

第一步,融合服务端多张日志表,包括搜索建议、搜索、导航等。接着,进行session的切分和清洗。最后,通过把输入session中,末尾query的点击计算到session中所有query上,以此满足实现用户输入session最短的优化目标。

如下图所示:

4.jpg


最终,抽取线上点击日志超过百万条的随机query,每条query召回前N条候选POI。利用上述样本构造方案,最终生成千万级别的有效样本作为gbrank的训练样本。

特征方面,主要考虑了4种建模需求,每种需求都有对应的特征设计方案:

  • 有多个召回链路,包括:不同城市、拼音召回。因此,需要一种特征设计,解决不同召回链路间的可比性。
  • 随着用户的不断输入,目标POI不是静态的,而是动态变化的。需要一种特征能够表示不同query下的动态需求。
  • 低频长尾query,无点击等后验特征,需要补充先验特征。
  • LBS服务,有很强的区域个性化需求。不同区域用户的需求有很大不同。为实现区域个性化,做到千域千面,首先利用geohash算法对地理空间进行分片,每个分片都得到一串唯一的标识符。从而可以在这个标识符(分片)上分别统计特征。

    5.jpg

详细的特征设计,如下表所示:

6.jpg


完成特征设计后,为了更好发挥特征的作用,进行必要的特征工程,包括尺度缩放、特征平滑、去position bias、归一化等。这里不做过多解释。

初版模型,下掉所有规则,在测试集上MRR 有5个点左右的提升,但模型学习也存在一些问题,gbrank特征学习的非常不均匀。树节点分裂时只选择了少数特征,其他特征没有发挥作用。

以上就是前面提到的,建模的第二个难题:模型学习的调优问题。具体来就是如何解决gbrank特征选择不均匀的问题。接下来,我们详细解释下。

先看下,模型的特征重要度。如下图所示:

7.jpg


经过分析,造成特征学习不均衡的原因主要有:
  • 交叉特征query-click的缺失程度较高,60%的样本该特征值为0。该特征的树节点分裂收益较小,特征无法被选择。然而,事实上,在点击充分的情况下,query-click的点击比city-click更接近用户的真实意图。
  • 对于文本相似特征,虽然不会缺失,但是它的正逆序比较低,因此节点分裂收益也比city-click低,同样无法被选择。

综上,由于各种原因,导致树模型学习过程中,特征选择时,不停选择同一个特征(city-click)作为树节点,使得其他特征未起到应有的作用。解决这个问题,方案有两种:

  • 方法一:对稀疏特征的样本、低频query的样本进行过采样,从而增大分裂收益。优点是实现简单,但缺点也很明显:改变了样本的真实分布,并且过采样对所有特征生效,无法灵活的实现调整目标。我们选择了方法二来解决。
  • 方法二:调loss function。按两个样本的特征差值,修改负梯度(残差),从而修改该特征的下一轮分裂收益。例如,对于query-click特征非缺失的样本,学习错误时会产生loss,调loss就是给这个loss增加惩罚项loss_diff。随着loss的增加,下一棵树的分裂收益随之增加,这时query-click特征被选作分裂节点的概率就增加了。

具体的计算公式如下式:

8.jpg


以上公式是交叉熵损失函数的负梯度,loss_diff 相当于对sigmod函数的一个平移。

9.jpg


差值越大,loss_diff越大,惩罚力度越大,相应的下一轮迭代该特征的分裂收益也就越大。

调loss后,重新训练模型,测试集MRR在初版模型的基础又提升了2个点。同时历史排序case的解决比例从40%提升到70%,效果明显。

写在最后

Learning to Rank技术在高德搜索建议应用后,使系统摆脱了策略耦合、依靠补丁的规则排序方式,取得了明显的效果收益。gbrank模型上线后,效果基本覆盖了各频次query的排序需求。

目前,我们已经完成了人群个性化、个体个性化的建模上线,并且正在积极推进深度学习、向量索引、用户行为序列预测在高德搜索建议上的应用。

]]>
1亿人次玩嗨,灵魂画手如何赚红包?-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 作者 | 塞远

1.jpg


从远古时期开始,人类就已经学会了用简单的涂鸦去描绘自己看到的世界,而到了今天,涂鸦或者更准确的说是简笔画,可能是唯一的全人类通用的绘画技能。如果给你一张纸和一支笔,请你画出一双大眼睛,美术生画出来的可能是左边这样的,漫画家画出来的可能是右边这样的:

2.jpg


而我们的灵魂画手画出来的可能是下面这样的......虽然看起来简单,但是一笔一划中都透露着一股神韵......

3.jpg

1.“猜画夺宝”项目背景

淘宝用户参与“猜画夺宝”的方式有两种。一种是日常分享的玩法,用户根据画板上的提示绘画一些特定种类的物体,然后AI会识别出相应的类别并提示用户分享识别到的内容,具体效果如下图所示。小编绘画的抽象派猪年生肖就被AI正确的识别到了。

4.jpg

另一种玩法是在淘宝直播的官方直播间,主持人口头播报12道猜画夺宝题目,用户可以单人也可以组队参与这12道题的作答,每道题都是AI识别用户绘画的物体,如果识别到的内容和题目要求绘画的一致,则作答正确,每答对一题都会有机会抽取现金红包,最后的终极大奖会根据全队的总得分进行抽取。

5.jpg


整个项目中我们见证了许多用户的“灵魂画作”,让我们一睹为快。

6.jpg

2.“猜画夺宝”项目面临的挑战

可能有的读者也试玩过谷歌的“猜画小歌”这一款微信小程序,可以说“猜画小歌”是手机端AI猜画互动游戏的鼻祖,并且谷歌也开源了Quick, Draw!简笔画识别数据集。相较于“猜画小歌”,我们的AI简笔画识别面临更多的挑战:

  • 如果采用类似谷歌的服务器端算法,超过10万人同时参与相同题目的AI猜画有一定风险,因为上传图片,大模型预测,再加预测结果返回,整套流程过于冗长,手机端的互动流畅性难以保证。

    • 如果采用基于手机端的小模型识别算法,虽然速度上能够保证,但是识别准确度方面难免会打一些折扣。

那么我们最终选择了手机端的简笔画识别方案。基于淘宝自研的AliNN深度学习Inference框架,我们的模型可以获得流畅的用户体验,而对于小模型精度方面的损失,我们也研发了一套自己的解决方案,会在下一部分进行详述。

3.“猜画夺宝”项目采用的识别方案

简笔画识别的目标是对任意一幅简笔画物体进行分类,而相较于普通图片中的物体分类,简笔画物体分类的难度更大,因为简笔画物体往往缺少丰富的纹理信息,也存在大量的形状类似的简笔画物体。

在机器学习领域,学术界一直以来都在尝试对简笔画物体进行分类,比较传统的做法是先对简笔画物体进行特征提取(比如用一些传统的SIFT特征或者HOG特征),然后针对这些提取出来的特征,用SVM分类器进行多类物体的分类,当然相较于现在流行的深度神经网络的分类方案,这样的传统算法的准确度比较低。

近些年,随着深度学习的发展,简笔画分类算法有了很大程度的进步,CNN方案和RNN方案是两种比较常用的简笔画分类算法,我下面简单介绍一下这两者的原理。

CNN方案

CNN方案简单地把所有的简笔画图像看成是一幅黑白图,在我们把数据集中所有简笔画的笔画粗细,物体大小归一化到同一个尺度上以后,我们就可以选择适合的CNN网络对这些黑白图进行分类训练了。下图展示了近几年在ImageNet物体分类比赛上取得成功的多个CNN网络,我们可以根据实际的场景,选择相应的CNN网络来做简笔画分类。比如当我们追求准确度的时候,我们可以选择像NASNet这样重量级的网络,而当我们追求性能和精度平衡的时候,我们可以选择像MobileNet这样的可以在移动端运行的CNN网络。

7.jpg


ImageNet上比较成功的CNN网络对比图,准确度vs计算量[1]

RNN方案

这里我们介绍的RNN方案[2] 来自于Tensorflow官网,是一份很好的简笔画识别+Tensorflow应用的教程。那么在这个方案中,简笔画图像不再是简单的黑白笔画信息了,它引入了时序信息,而我们知道RNN是非常适合解决拥有时序信息的问题的。在这个方案中,所有的简笔画物体的笔画由三个信息决定,即笔画经过的点的位置(x,y)坐标和经过这个点的时刻t(从当前笔画开始计算)。如下图中所示,一幅“猫”的简笔画被量化成了一个位置加时间的序列,当然在这幅图中时间信息被进一步简化,如果是笔画开始的点就为1,如果不是笔画开始的点时间信息就为0。

在RNN的预测过程中,这样一幅简笔画序列将经过一系列一维卷积,再经过LSTM层,并将所有 LSTM 步的输出之和馈送到 softmax 层,以便根据我们已知的简笔画类别来决定简笔画的分类。

8.jpg


RNN方案举例[2]


我们的方案

9.jpg


CNN网络简笔画识别流程示例


10.jpg


RNN网络简笔画识别流程示例


综合CNN和RNN的方案,我们在实验中发现,RNN网络要比CNN网络整体的准确度更高,这也显而易见,因为RNN网络的输入信息更丰富,把时序信息也引入了其中,而CNN网络相较于RNN网络平均的预测速度更快,也更容易在手机端进行部署,那么如何能够取长补短,形成我们自己的简笔画识别方案呢?

我们参考了kaggle简笔画识别比赛[3]的前几名方案,把RNN利用的时序信息和CNN的黑白输入图像结合到了一起,不再使用黑白简笔画图像作为输入,而是使用RGB图像进行输入。如下图所示,我们用不同的颜色给不同顺序的笔画进行着色,比如每幅简笔画的第一笔我们用红色进行着色,更进一步,如果我们拥有的时间更精确(比如时间信息是从当前笔画开始的毫秒时间),我们甚至可以把第一笔红色笔画变成从浅红到深红的渐变笔画。用这样的方法,我们就把抽象的时序信息融入到了CNN的输入图像中去,用新的RGB图像作为网络输入。

11.jpg


时序信息融入RGB图像示例[3]


这样,我们新的方案的示意图如下图所示,CNN网络输入是拥有笔画时序信息的RGB简笔画图像,先经过一系列2D卷积层和Max pooling层,再用一系列全连接层把图像抽象成更高维的信息,最后用softmax层把最终的分类结果得到。

12.jpg


我们的时序CNN网络简笔画识别流程示例

4.“猜画夺宝”项目采用的数据集

13.jpg

Google’sQuick, Draw!数据集 [4]


14.jpg


TU-Berlin数据集 [5]

简笔画识别领域的公开数据集并不多,比较著名的有Quick, Draw!数据集[4](上图)和TU-Berlin数据集[5](上图)。之所以简笔画识别数据集比较难构建,是因为一个好的简笔画数据集必须具备以下几个特点[6]:

  • Exhaustive:数据集中的物体种类必须涵盖大部分我们在日常生活中遇到的物体种类。
  • Recognizable:每个物体种类必须从形状就能被识别,而不需要纹理信息。
  • Specific:每个物体种类必须足够明确,不存在太多的视觉上的差异,比如“乐器”就不是一个很好的物体种类,因为存在太多种不同种类的乐器了。

而Quick, Draw!数据集和TU-Berlin数据集就是两个很好的具有上述特点的简笔画识别数据集。

谷歌的Quick, Draw!数据集是现在公开的最大的简笔画数据集,包含345个物体种类,总共约5千万幅简笔画图像,每个类别平均约有15万幅简笔画,而且它们来自于世界各地,由世界各地的用户在20秒内完成单幅简笔画的绘画。所以Quick, Draw!数据集的更倾向于抽象的简笔画流派。

TU-Berlin数据集包含250类物体,每类物体提供了80幅简笔画,而每幅简笔画都是由志愿者花了将近30分钟绘画完成的,所以TU-Berlin数据集更倾向于比较现实流派的简笔画数据集。

综合我们实际的淘宝直播答题场景,我们选择了谷歌的Quick, Draw!数据集作为训练数据集,我们从每一类物体的数据中随机选择了一部分简笔画作为测试图像,其余的数据作为训练图像,训练我们的时序CNN模型。

最终我们的时序CNN模型在保证运行速度达到实时的前提下,在测试集上获得了TOP3准确度超过90%的表现,相比于不加时序信息的CNN模型获得了接近5%的精度提升。

5. “猜画夺宝”项目未来的发展和相关的应用

本次的“猜画夺宝”活动,吸引了超过1亿人次参与互动,说明广大的淘宝消费者对于有趣的手机端智能玩法有很高的认同度。只要互动玩法设计的有趣,参与时的智能科技感强,就很能够获得大众的青睐。

沿着这样的思路,未来我们一方面会尝试对“AI猜画”的算法能力进行不断的升级和优化,让AI能够准确地识别更多种类的物体;另一方面,依托我们自研的移动端智能SDK——PixelAI(见下图),在其他有趣的智能场景,我们也会推出更多的互动玩法。

15.jpg


PixelAISDK能力和功能示意图


PixelAISDK涵盖了人脸、表情、姿态、手势、分割、追踪、SLAM等端上智能算法以及互动渲染能力,还包括了AliBeauty美肤、滤镜、美型美体、上妆四大功能,以及基础图像处理,视频编解码技术,是我们移动端智能互动玩法的基础SDK。目前为止我们根据PixelAI的算法能力推出过本次春节的“猜画夺宝”活动,2018年双11猫晚的“笑脸红包”,以及去年世界杯期间“这就是世界波”栏目的AR头球互动等(参考下图)。

16.jpg


PixelAISDK移动端智能玩法举例:“猜画夺宝”,“笑脸红包”,“AR头球”
未来我们会以PixelAI为基础推出更多有趣的互动玩法,让用户拥有更多的智能体验。

关于作者:塞远(陈志文),淘宝技术部算法小二,个人兴趣是计算机视觉和深度学习,目前主要从事淘宝多媒体算法的开发工作,服务端和移动端算法都有涉猎。

参考资料:

[1] BenchmarkAnalysis of Representative Deep Neural Network Architectures (Simone Bianco,Remi Cadene, Luigi Celona, Paolo Napoletano), In IEEE Access, volume 6, 2018.

[2]https://www.tensorflow.org/tutorials/sequences/recurrent_quickdraw#download_the_data

[3] https://www.kaggle.com/c/quickdraw-doodle-recognition

[4] https://quickdraw.withgoogle.com/data

[5] http://cybertron.cg.tu-berlin.de/eitz/projects/classifysketch/

[6] M. Eitz, J. Hays, and M. Alexa. How dohumans sketch objects? ACM TOG, 31(4):44:1–44:10, July 2012.

]]>
高德首席科学家:视觉是连接真实世界的桥梁-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 作者 | 任小枫

1.jpg

我今天主要给大家介绍视觉及相关技术如何在高德落地,如何帮助连接真实世界。连接真实世界这句话并不只是我个人的想法,而是高德地图的使命,我们的使命是“连接真实世界,让出行更美好”。

首先,简单介绍下高德地图,有超过1亿的日活用户,超过4亿的月活用户,高德地图不光提供导航,也提供出行相关的其他服务,涵盖了信息服务、驾车导航、共享出行、智慧公交、智慧景区、骑行、步行、长途出行等应用场景。

高德地图做的事情是建立人和真实世界的关系,人要跟真实世界建立联系,地图是基础,地图之上还有更多的信息可以获取。

视觉是连接真实世界的桥梁

视觉是连接真实世界的桥梁。为什么?从人的信息获取角度来看,80%的内容是通过视觉获取到的。从人的信息处理来看,人的大脑30%-60%用于视觉感知。从机器的角度,视觉是非常重要的通用感知手段。

人类感知真实世界的方法,还有很多其他方式,例如传感器、LT...但是,作为通用的手段,我一直觉得视觉是第一选择,通用,信息量非常大,可以远距感知,也可以做到实时。

还有一个原因,人类真实世界里(各种元素)80%以上是为了视觉而设计。有的时候,我们对真实世界太过于熟悉,可能不会太在意。但是看一下周围的标志和信息,包括认识的事物,都是根据视觉设计和获取。

因为人类获取信息的主要方式是通过视觉,所以真实世界的设计也是基于视觉。大家可以想象下,如果获取信息的主要方式是通过嗅觉,那这个世界会非常不一样。基于这些,回到我们在做的事情,大家一定不会奇怪,地图信息的获取和建立,绝大部分也是来自于视觉。

视觉技术@高德地图:地图制作

视觉技术在高德地图的应用有很多不同的方式,如下图所示:

2.jpg


左边是地图制作,有常规地图和高精地图,高精地图对应于未来的无人驾驶。右边是跟导航体验相关的,我们在做的一些跟定位相关的工作,也在利用视觉技术希望使导航变得更加便利。因为时间关系,今天只给大家介绍常规地图和导航相关的部分。

地图服务从哪里来,首先要采集资料,目前绝大部分是通过相机和视觉的方式采集信息。真实世界很大,全国有几百万公里道路,再加上其他信息,人工方式目前是处理不过来的,很大程度上需要用自动识别,通过算法识别资料。当然有时候算法没办法做到100%,还需要人工修正,从而制作成地图数据库,来支持地图数据服务。

地图制作任务,常规地图任务通常分为两大类,一类是道路相关,一类是POI挂牌识别。这两类任务都需要较多的视觉技术。例如,在道路标志识别上,算法要做的就是把道路上的标志一个一个全部找出来,同时识别标志的类型和内容。

3.jpg


道路标志有100多种。如果只是处理这些标志,其实并不是那么复杂。现实中,有时候需要用低成本的方式采集数据,这时如何保证图像质量就是需要考虑和解决的问题。

采集信息的时候,有时候图片会有畸变、反光、遮挡等情况,先不说分辨率压缩的问题,成像本身取决于镜头的质量和成本、天气条件、光线等因素,有时候采集回来的图像中差的图很多。这时候就不只是单纯去解决一个理想当中的算法问题,也需要处理很多实际情况。

给大家举几个例子,下面左边的图是实际采集的图像,会有各种各样的问题。大家对相机有些了解的话,知道相机有内参和外参,内参是焦距、中心、畸变。外参是位置、角度,这些都会影响成像效果。

4.jpg

对于识别问题来说,这些相机参数不会造成太大问题,但是如果需要做一些跟几何、位置相关的计算,这时候相机畸变和内外参不准就会造成很大的问题。我们通过把多源数据放在一起做匹配,基本可以解决这个问题。右边是一个实际例子,相机的畸变纠正角度,有一些斜的被纠正过来了,很大的提高了后面的算法处理。

另一个例子,图像质量。有的图质量比较差,但是没办法丢掉,还是有有用的信息。有的原始图像,放大之后非常模糊。如果这时采用图像增强的方法,可以把这张图变得更清楚。改善原始数据的质量,有很多可用的方法。比如提高识别算法精度,提高人工效率,也可以用它做模糊的检测,对比一下增强前后,可以知道哪些是模糊,哪些是不模糊。

刚才举的只是交通标志的例子。还有一个有趣的问题,就是感知电子眼。电子眼很小,而小目标的检测是一个有挑战的问题,在研究领域大家也比较关注。大家可以感受下,拿一张图,如果是太小的东西,放大之后就看不清了,还不如远景。那怎么能比较精确的找到这么小的电子眼呢?

通常方式就是放大区域,因为这个东西太小了,光找这个目标比较难,找到区域放大,引入周边的信息。这些信息可以帮助更好的找到这个小目标,放的再大一点,才能看到其他相关的信息来帮助电子眼的智能检测。
但是放得太大也会有问题,放的太大会引入很多无关的信息。从技术上来说有一些解决方法,现在视觉技术上用的比较多的有一个注意力机制,画一个大框,机器自己会学哪块重要哪块不重要,帮助更好的聚焦到目标本身。当然,尽量会用一些先验信息,比如本身的分布、高度、大小。

5.jpg

光检测还不够,很多时候真实世界在变。很多时候要分辨出哪些变了哪些没变。以前检测出一个电子眼,新的资料又检测出一个电子眼,需要知道这两个是否是同一个。

如何判断?因为这张图表达的不一样,如果仔细看,确实可以看到背景的建筑、架设类型都差不多。需要用算法来判断到底是不是,这就牵涉到目标检测、车道归属、架设类型分析,还要做场景匹配。通过这些,很大程度上可以判断这是一个什么场景,从而判断两张图的元素是不是同一个。

刚才说的是道路,下面是几个跟POI相关的例子。POI的牌子,可以分成好多不同类型,有牌坊式、挂牌式、门脸式等。不仅POI各种各样,非POI其实也各种各样。如果只是检测文字的话,你会发现真实世界里的很多不是POI,有的只是标牌、标语、广告、对联、交通标志等。所以,要区分出POI和非POI。

有很多其他的复杂场景,这里不一一举例了,有些可能平时也不太能想到,比如三维挂牌,它不是一个平的牌子,在街角,可能是一个水果超市,沿着街角弯曲过来。这类牌子很难在一张图里完全检测出来,即使检测出来,一不小心就会分成两块牌子,所以真实世界的复杂性还是会造成更多的问题。

面对这么多复杂性,需要去分析具体场景的情况。很多时候最后的结果往往不是一个算法就能解决所有的问题,需要各种算法的融合。比方说,如果是文字,需要做检测,文字本身也需要做检测和识别。位置的话,需要做一些三维方面的推断。很多时候资料获取到以后也有模糊和遮挡的部分,也要做判断。

每一个判断不是单一办法就可以解决,不是光靠一个模型就能够做到最好的效果,需要的是两个甚至更多的模型,从不同的角度去解决问题,才能够达到更好的效果,这是在数据积累的基础之上。

上面列举的一些问题有一定的复杂性,跟所有的问题一样,越做到后面越难,我们现在还在做,这些算法很大程度上决定了地图制作的效率和触达到用户的地图质量,这些是非常重要的核心问题。

POI也不光是以上介绍的只需要判断是不是POI或者文字识别,很多时候还需要做版面的内容理解。如果一个牌子,需要知道这个牌子上的信息,有时候会有主名称,有时候会有分店,有时候没有,有没有联系方式、营业范围,这些都需要用算法去做。

视觉技术@高德地图:导航

以上介绍的是在地图制作方面有很多的复杂性,需要用视觉算法或者其他算法来处理。接下来分享下在导航方面的。

先说下自己的一个体会。前段时间在西班牙休假,欧洲的环岛特别多,谷歌(地图)导航经常提示我,进了弯道之后从第三个出口出去,我当时特别郁闷,因为要数口子,经常你也不知道那个到底算不算出口,所以走错了好几次。我在国内没开过车,国内的交通更复杂,例如在北京的西直门,有时候可以直接右拐,有时候需要转一个810度的圈。

我们希望对导航的方式做一个比较大的变化,让它变成所见即所得的场景。如果有算法能够直接告诉人们往哪边走,对人来说是更加有用的,能够让开车更加简单,导航变得更加直接。

很多汽车现在都会有摄像头,不管是前端还是后端,很多时候可以获取到视频数据。我们把AI算法计算出的效果叠加在视频上,告诉人们到底该怎么走。

6.jpg


高德在今年4月份发布了AR导航产品,这个产品里有一项是实景增强,它会告诉你应该保持在这条线上继续往前开或者转弯,会有压线的提示,会有箭头告诉你前面右转。

这个产品中,除了引导之外,还有别的功能。例如,也加入了前车的碰撞预警功能,会估计前车的距离和速度,这将帮助大家安全驾驶。其他事物也可以用更加直观的方式展示,例如限速,电子眼,跟斑马线相关的,如果看到前方有人,也会做出提示。

以上的功能看起来可能不那么难,但要实现起来很难。为什么?因为我们希望这是每个人马上就能实用的功能,所以要做到很低的成本。这和自动驾驶系统不一样。从传感器的角度,我们要做的是单个传感器,而且是低成本的相机。从计算的角度来说,自动驾驶系统可能会用一个几百瓦的专用芯片,而对于我们来说,所需要的算力大概只是普通手机的五分之一。

给大家看一个AR导航的例子,这是实际算法的输出,这个例子里面有车辆的检测,车道线的分割,和引导线的计算等。刚才提到了,高性能(低算力)是一个主要挑战,那我们在开发算法的时候就要充分考虑计算效率,包括各种手段,比如模型压缩,小模型训练优化,检测和跟踪的结合,多目标的联合模型,和传统GPS导航的融合,等等,需要几件事情在一个模型里做。

真实世界是非常复杂的,要做到高质量、高效的地图制作,或者做到精准的定位导航,在视觉方面还有很多工作要做。希望通过以上介绍,大家对视觉技术在高德地图中的应用,在出行领域的应用,有了更多的了解,也对高德的使命有了更多了解。

我们在很多时候需要去连接真实世界或者是理解真实世界,才能够让出行更美好。希望能够尽快的把这些做好,让大家实际应用高德APP的时候,能够感受到技术进步带来的体验变化。我今天就讲到这里,谢谢大家。

]]>
线上广告投放出现bug,如何实时发现?-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 作者 | 大倪

1.jpg

一、背景

电商平台的搜索广告数据处理链路通常较长,一般会经历如下过程:

2.jpg

  1. 广告主在后台进行广告投放;
  2. 投放广告品及关键词数据写入数据库;
  3. 数据库中的数据通过全量构建(导入数据仓库再进行离线批处理)或增量构建(借助消息队列和流计算引擎)的方式产出用于构建在线索引的“内容文件”;
  4. BuildService基于“内容文件”,构建出在搜索服务检索时使用的索引。

下图是ICBU的广告系统的买卖家数据处理链路:


3.jpg

右半部分(BP->DB)和offline部分即为广告投放数据的更新过程。

复杂的数据处理链路结合海量(通常是亿级以上)的商品数据,对线上全量商品的投放状态正确性测试提出巨大挑战。从数据库、到离线大规模数据联表处理、到在线索引构建,链路中的任一节点出现异常或数据延迟,都有可能会对广告主以及平台造成“资损”影响,例如:

  1. 广告主在后台操作取消A商品的广告投放,但是因为数据链路处理延迟,搜索引擎中A的状态仍处于“推广中”,导致A能继续在买家搜索广告时得到曝光,相应地当“点击”行为发生时,造成错误扣款。
  2. 广告主设定某个产品只限定对某个地域/国家的客户投放广告,但是因为搜索引擎的过滤逻辑处理不恰当,导致客户的广告品在所有地区都进行广告投放,同样会造成错误点击扣款。

传统的测试手段,或聚焦于广告主后台应用的模块功能测试,或聚焦于搜索引擎的模块功能测试,对于全链路功能的测试缺乏有效和全面的测试手段。而线上的业务监控,则侧重于对业务效果指标的监控,如CTR(click through rate,点击率)、CPC(cost per click,点击成本)、RPM(revenue per impression,千次浏览收益)等。对涉及广告主切身利益和平台总营收的广告错误投放问题,缺乏有效的发现机制。

我们期望对在线搜索广告引擎所有实际曝光的商品,通过反查数据库中曝光时刻前它的最后状态,来校验它在数据库中的投放状态与搜索引擎中的状态的一致性,做到线上广告错误投放问题的实时发现。同时,通过不同的触发检测方式,做到数据变更的各个环节的有效覆盖。

二、阶段成果

我们借助日志流同步服务(TTLog)、海量数据NoSQL存储系统(Lindorm)、实时业务校验平台(BCP)、消息队列(MetaQ)、在线数据实时同步服务(精卫)以及海量日志实时分析系统(Xflush)实现了ICBU搜索广告错误投放问题的线上实时发现,且覆盖线上的全部用户真实曝光流量。同时,通过在数据变更节点增加主动校验的方式,可以做到在特定场景下(该广告品尚未被用户检索)的线上问题先于用户发现。

此外,借助TTLog+实时计算引擎Blink+阿里云日志服务SLS+Xflush的技术体系,实现了线上引擎/算法效果的实时透出。

下面是ICBU广告实时质量大盘:

4.jpg

从八月底开始投入线上使用,目前这套实时系统已经发现了多起线上问题,且几乎都是直接影响资损和广告主的利益。

三、技术实现

图一:

5.jpg

1. 引擎曝光日志数据处理

对于电商搜索广告系统,当一个真实的用户请求触达(如图一中1.1)时,会产生一次实时的广告曝光,相对应地,搜索引擎的日志里会写入一条曝光记录(如图一中2)。我们通过日志流同步服务TTLog对搜索引擎各个服务器节点上的日志数据进行统一的搜集(如图一中3),然后借助数据对账服务平台BCP对接TTLog中的“流式”数据(如图一中4),对数据进行清洗、过滤、采样,然后将待校验的数据推送到消息队列服务MetaQ(如图一中5)。

2. DB数据处理

图二:

6.jpg

如图二所示,通常,业务数据库MySQL针对每个领域对象,只会存储它当前时刻最新的数据。为了获取广告品在引擎中真实曝光的时刻前的最后数据,我们通过精卫监听数据库中的每次数据变更,将变更数据“快照”写入Lindorm(底层是HBase存储,支持海量数据的随机读写)。

3. 数据一致性校验

在广告测试服务igps(我们自己的应用)中,我们通过监听MetaQ的消息变更,拉取MetaQ中待校验的数据(如图一中6),解析获得曝光时每个广告品在搜索引擎中的状态,同时获得其曝光的时刻点。然后基于曝光时刻点,通过查询Lindorm,获得广告品于曝光时刻点前最后在MySQL中的数据状态(如图一中7)。然后igps对该次广告曝光,校验引擎中的数据状态和MySQL中的数据状态的一致性。

如果数据校验不一致,则打印出错误日志。最后,借助海量日志实时分析系统Xflush(如图一中8),我们可以做到对错误数据的实时聚合统计、可视化展示以及监控报警。

4. 数据变更节点的主动校验

因为线上的实时用户搜索流量具有一定的随机性,流量场景的覆盖程度具有很大的不确定性,作为补充,我们在数据变更节点还增加了主动校验。

整个数据链路,数据变更有两个重要节点:

  1. MySQL中的数据变更;
  2. 引擎索引的切换。

对于MySQL中的数据变更:我们通过精卫监听变更,针对单条数据的变更信息,构建出特定的引擎查询请求串,发起查询请求(如图一中1.3)。

对于引擎索引的切换(主要是全量切换):我们通过离线对历史(如过去7天)的线上广告流量进行聚合分析/改写,得到测试用例请求集合。再监听线上引擎索引的切换操作。当引擎索引进行全量切换时,我们主动发起对引擎服务的批量请求(如图一中1.2)。

上述两种主动发起的请求,最后都会复用前面搭建的数据一致性校验系统进行广告投放状态的校验。

7.jpg

上图是对广告投放状态的实时校验错误监控图,从图中我们清晰看到当前时刻,搜索广告链路的数据质量。无论是中美业务DB同步延迟、DB到引擎数据增量处理链路的延迟、或者是发布变更导致的逻辑出错,都会导致错误数据曲线的异常上涨。校验的规则覆盖了推广计划(campaign)、推广组(adgroup)、客户状态(customer)、词的状态(keyword)、品的状态(feed)。校验的节点覆盖了曝光和点击两个不同的环节。

5. 引擎及算法的实时质量

图三:

8.jpg

搜索引擎日志pvlog中蕴含了非常多有价值的信息,利用好这些信息不仅可以做到线上问题的实时发现,还能帮助算法同学感知线上的实时效果提供抓手。如图三所示,通过实时计算引擎Blink我们对TTLog中的pv信息进行解析和切分,然后将拆分的结果输出到阿里云日志服务SLS中,再对接Xflush进行实时的聚合和可视化展示。

9.jpg

如上图所示,上半年我们曾出现过一次线上的资损故障,是搜索应用端构造的搜索广告引擎SP请求串中缺失了一个参数,导致部分头部客户的广告没有在指定地域投放,故障从发生到超过10+客户上报才发现,历经了10几个小时。我们通过对SP请求串的实时key值和重要value值进行实时监控,可以快速发现key值或value值缺失的场景。

此外,不同召回类型、扣费类型、以及扣费价格的分布,不仅可以监控线上异常状态的出现,还可以给算法同学做实验、调参、以及排查线上问题时提供参考。

四、几个核心问题

1. why lindorm?

最初的实现,我们是通过精卫监听业务DB的变更写入另一个新的DB(MySQL),但是性能是一个非常大的瓶颈。我们的数据库分了5+个物理库,1000+张分表,单表的平均数据量达到1000+w行,总数据达到千亿行。

后通过存储的优化和按逻辑进行分表的方式,实现了查询性能从平均1s到70ms的提升。

10.jpg

2. why BCP + MetaQ + igps?

最初我们是想直接使用BCP对数据进行校验:通过igps封装lindorm的查询接口,然后提供hsf接口供在BCP里直接使用。

但是还是因为性能问题:TTLog的一条message平均包含60+条pv,每个pv可能有5个或更多广告,每个广告要查6张表,单条message在BCP校验需要调用约60x5x6=1800次hsf请求。当我们在BCP中对TTLog的数据进行10%的采样时,后端服务igps的性能已经出现瓶颈,hsf线程池被打满,同时7台服务器的cpu平均使用率达到70%以上。

借助MetaQ的引入,可以剔除hsf调用的网络开销,同时将消息的生产和消费解耦,当流量高峰到达时,igps可以保持自己的消费速率不变,更多的消息可以暂存在队列里。通过这一优化,我们不仅扛住了10%的采样,当线上采样率开到100%时,我们的igps的服务器的平均cpu使用率仍只维持在20%上下,而且metaq中没有出现消息堆积。

11.jpg

不过这样一来,bcp的作用从原来的“采样、过滤、校验、报警”,只剩下“采样、过滤”。无法发挥其通过在线编码可以快速适应业务变化的作用。

3. why not all blink?

其实“BCP + MetaQ + igps”的流程可以被“Blink + SLS”取代,那为什么不都统一使用Blink呢。

一方面,目前点击的校验由于其流量相对较小的因素,我们目前是直接在BCP里编写的校验代码,不需要走发布流程,比较快捷。而且BCP拥有如“延迟校验”、“限流控制”等个性化的功能。另一方面,从我们目前使用Blink的体验来看,实时的处理引擎尚有一些不稳定的因素,尤其是会有不稳定的网络抖动(可能是数据源和Blink workder跨机房导致)。

4. SP请求的key值如何拆分?

在做SP请求串的实时key值监控的时候,遇到了一个小难题:SP的请求串中参数key是动态的,并不是每个key都会在每个串中出现,而且不同的请求串key出现的顺序是不一样的。如何切分使其满足Xflush的“列值分组”格式要求。

实现方式是,对每个sp请求串,使用Blink的udtf(自定义表值函数)进行解析,得到每个串的所有key和其对应的value。然后输出时,按照“validKey={key},validValue={value}”的格式对每个sp请求串拆分成多行输出。然后通过Xflush可以按照validKey进行分组,并对行数进行统计。

总结及后续规划

本文介绍了通过大数据的处理技术做到电商搜索广告场景下数据端到端一致性问题的实时发现,并且通过“实时发现”结合“数据变更节点的主动校验”,实现数据全流程的一致性校验。

后续的优化方向主要有两方面:

  1. 结合业务的使用场景,透出更丰富维度的实时数据。
  2. 将该套技术体系“左移”到线下/预发测试阶段,实现“功能、性能、效果”的一键式自动化测试,同时覆盖从搜索应用到引擎的全链路。
]]>
标记(TAG)您的DDoS高防实例资源-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 标签的基本知识
  • 标签是资源Meta信息,标签是一些充当元数据的词和短语,支持用户自定义和系统预制。每个标签都包含一个键(Key)和一个值(Value)。标签是一种资源的属性描述。
  • 标签与资源是 多对多的关系。
  • 标签可让您按各种标准(例如用途、所有者或环境)对 阿里云 资源进行分类。这在您具有相同类型的许多资源时会很有用,您可以根据分配给资源的标签快速识别特定资源。
  • 标签对阿里云资源没有任何语义意义,应严格按字符串进行解析。同时,标签不会自动分配至您的资源。您可以修改标签的key和value,还可以随时删除资源的标签。您可以将标签的值设为空的字符串,但是不能将其设为空值。如果您添加的标签的值与该实例上现有标签的值相同,新的值就会覆盖旧值。如果删除资源,资源的所有标签也会被删除。
  • 可以使用管理控制台 和 API 处理标签。
  • 标签设计详情见最佳实践

标签的限制

  • 最大键(key)长度:128 个 Unicode 字符
  • 最大值(value)长度:128 个 Unicode 字符
  • 同一个资源上的同一个键只能有一个标签。如果您尝试添加现有标签 (相同键),现有标签值会更新为新值。
  • 允许的字符包括 Unicode 字母、空格和数字。
  • 每个资源的最大 自定义 标签数:20
  • 键(key)不支持 aliyun、acs: 开头。 不允许包含http://https:// 。不允许为空字符串。
  • 值(value)不允许包含http://https:// 。允许为空字符串。

操作您的资源

您可以标记新的或现有的防护包实例

  1. API操作

  2. 控制台操作

相关文档

高阶实践:云监控基于tag自动化监控
高阶实践:强制TAG——自动化打标签运维
基于OOS批量修改资源标签(TAG)值(Value)
高阶实践:强制TAG——必须带标签创建ECS 资源
ECS支持跨地域跨资源类型的标签(TAG)操作
如何检查您的资源是否具有您指定的标签?
基于标签批量管理资源
支持标签产品及其文档
标签的最佳实践
通过OOS基于标签批量启动ECS实例实践
如何使用标签控制对ECS 资源的访问?
使用标签检索资源
创建资源标签分组设置
ECS全局标签实践
ECS控制台云资源分组管理---全局标签
标记(TAG)您的MongoDB数据库实例
标记(TAG)您的CDN 资源——域名(domain)
标记(TAG)您的 OSS 资源
标记(TAG)您的 RDS 资源
标记(TAG)您的 SLB 资源
标记(TAG)您的 ECS 资源
标记(TAG)您的redis数据库实例
标记(TAG)您的弹性容器实例(ECI)资源
标记(TAG)您的漏洞扫描(CSS)资源
标记(TAG)您的API网关资源
标记(TAG)您的polardb集群资源

]]>
标记 (TAG) 您的 k8s 集群资源-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 标签的基本知识
  • 标签是资源Meta信息,标签是一些充当元数据的词和短语,支持用户自定义和系统预制。每个标签都包含一个键(Key)和一个值(Value)。标签是一种资源的属性描述。
  • 标签与资源是 多对多的关系。
  • 标签可让您按各种标准(例如用途、所有者或环境)对 阿里云 资源进行分类。这在您具有相同类型的许多资源时会很有用,您可以根据分配给资源的标签快速识别特定资源。
  • 标签对阿里云资源没有任何语义意义,应严格按字符串进行解析。同时,标签不会自动分配至您的资源。您可以修改标签的key和value,还可以随时删除资源的标签。您可以将标签的值设为空的字符串,但是不能将其设为空值。如果您添加的标签的值与该实例上现有标签的值相同,新的值就会覆盖旧值。如果删除资源,资源的所有标签也会被删除。
  • 可以使用管理控制台 和 API 处理标签。
  • 标签设计详情见最佳实践

标签的限制

  • 最大键(key)长度:128 个 Unicode 字符
  • 最大值(value)长度:128 个 Unicode 字符
  • 同一个资源上的同一个键只能有一个标签。如果您尝试添加现有标签 (相同键),现有标签值会更新为新值。
  • 允许的字符包括 Unicode 字母、空格和数字。
  • 每个资源的最大 自定义 标签数:20
  • 键(key)不支持 aliyun、acs: 开头。 不允许包含http://https:// 。不允许为空字符串。
  • 值(value)不允许包含http://https:// 。允许为空字符串。

操作您的资源

您可以标记新的或现有的集群实例

  1. API操作

  2. 控制台操作

相关文档

高阶实践:云监控基于tag自动化监控
高阶实践:强制TAG——自动化打标签运维
基于OOS批量修改资源标签(TAG)值(Value)
高阶实践:强制TAG——必须带标签创建ECS 资源
ECS支持跨地域跨资源类型的标签(TAG)操作
如何检查您的资源是否具有您指定的标签?
基于标签批量管理资源
支持标签产品及其文档
标签的最佳实践
通过OOS基于标签批量启动ECS实例实践
如何使用标签控制对ECS 资源的访问?
使用标签检索资源
创建资源标签分组设置
ECS全局标签实践
ECS控制台云资源分组管理---全局标签
标记(TAG)您的MongoDB数据库实例
标记(TAG)您的CDN 资源——域名(domain)
标记(TAG)您的 OSS 资源
标记(TAG)您的 RDS 资源
标记(TAG)您的 SLB 资源
标记(TAG)您的 ECS 资源
标记(TAG)您的redis数据库实例
标记(TAG)您的弹性容器实例(ECI)资源
标记(TAG)您的漏洞扫描(CSS)资源
标记(TAG)您的API网关资源
标记(TAG)您的polardb集群资源
标记(TAG)您的DDoS高防实例资源

]]>
未来,仅凭几个前端工程师,就能 hold 住一家企业吗?-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 作者 | 克军

​阿里妹导读:微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增加,从一个普通应用演变成一个巨石应用(Frontend Monolith),随之而来的应用不可维护问题。这类问题在企业级 Web 应用中尤为常见。今天,我们就来聊聊拥抱云时代的前端开发架构:微前端。

微前端的价值

阿里云提供的很多商业化产品和服务,本质上是对外提供「能力」,普惠中小企业。目前,能力输出主要是通过 OpenAPI,用以集成到企业自己的业务场景中,这里主要解决的还是企业底层的能力问题——无需雇佣算法工程师,就可以拥有语音、图像识别等能力。安全也是一样,不需要找安全专家,普通的工程师就可以通过控制台高效地处理各种安全事件。

但是,随着云技术不断的下沉,与产业结合的越来越紧密,OpenAPI 唯有把粒度做得越来越细,才能满足各种各样的业务场景,但同时企业侧的学习成本和开发复杂度自然就上去了。控制台做为管(理)控(制)这些能力的工具,目前也只能算是「标品」,必须为了满足不同体量、不同业务特点的需求,灵活地组合和部署,就像是用户自己开发的一样。

综上所述,微前端的价值有 3 点:

解决产品侧的扩展性和组合性。化整为零,自由组合。
解决能力输出的「最后一公里」。
云生态中的「新物种」 — 微应用。

如果微前端只存在工程上的价值,那它是不值得大张旗鼓去做的。

我认为,前端团队需要在这个方面做出业务价值。如果你问我 Ant Design 有什么技术价值?它的价值就是有大量的企业在用,形成某种能力依赖,不需要找设计师或者多么资深的前端工程师,就可以做出看上去很专业的后台界面。

在这条价值链路上,OpenAPI 太底层,控制台不灵活,UI 库太通用。其中的空白点是绑定能力的商业化组件。举个例子,企业的后台管理页上,可以直接 inside 一个「漏洞管理」的微前端应用,和一个 DataV 的微前端应用展示数据,只需要简单配一下即可,不用开发,就能做到“就像自己开发的一样”。反过来也一样,ISV 在阿里云的产品平台上,不仅可以通过小程序的形式,也可以通过微前端应用的形式输入自己的服务。

微前端的问题域

简单地说,搞微前端目的就是要将产品原子化(跟原子化的 OpenAPI 一个道理),再根据客户业务场景组合。每个功能模块能单独迭代,自由集成当然好,但维护成本怎么控制。怎么调试、公共组件版本控制、众多同窗微应用之间怎么“和谐相处”等等。微前端并非只是解决在页面上异步加载一个模块就完事了,更多的是将改造引发的一系列问题需要通过体系化的方案解决,否则就变成反生产力工具。

目前,阿里的微前端方案有 qiankun(乾坤)、Magix、icestack、以及内部很多的微前端解决方案。或多或少都带有一些自身的业务特色,没有明确提出标准,或者明确定义微前端的技术体系到底包含哪些内容。这方面有项目落地的团队真应该再进一步瞄准更高的价值点做,同时广泛交流,这样才能更快得出标准化的东西。我们团队也在实践中,这里我抛出一些开放性问题讨论。

首先必须明确微前端不是框架、不是工具/库,而是一套架构体系,它包括若干库、工具、中心化治理平台以及相关配套设施。

微前端包括 3 部分:

微前端的基础设施。这是目前讨论得最多的,一个微应用如何通过一个组件基座加载进来、脚手架工具、怎么单独构建和部署、怎么联调。
微前端配置中心:标准化的配置文件格式,支持灰度、回滚、红蓝、A/B 等发布策略。
微前端的可观察性工具:对于任何分布式特点的架构,线上/线下治理都很重要。

微前端具体要解决好的 10 个问题:

微应用的注册、异步加载和生命周期管理;
微应用之间、主从之间的消息机制;
微应用之间的安全隔离措施;
微应用的框架无关、版本无关;
微应用之间、主从之间的公共依赖的库、业务逻辑(utils)以及版本怎么管理;
微应用独立调试、和主应用联调的方式,快速定位报错(发射问题);
微应用的发布流程;
微应用打包优化问题;
微应用专有云场景的出包方案;
渐进式升级:用微应用方案平滑重构老项目。

通过问题理解问题是一种思考方式,相信大家能沟通通过微前端三大组成部分和它要解决的 10 个问题,能够有一个大概的理解。下面,看一下我归纳的微前端的架构体系(如图):

123002.jpg

通过上图,很明显的看出前后端分工,以及线上微应用相关配置流程。整体运行环境以及开发流程是非常复杂的,留到大会的时候再详细讲解。

微前端的基本原理

如下图所示,微前端的工程化是从传统前端工程化体系升级上来的。

123003.jpg

比如构建,增加微应用类型的项目构建,有动态的打包策略。传统项目管理工具通常是命令行工具,包括构建、发布、测试,会升级为项目工作台,通过 Web 界面管理项目。一个项目包括哪些微应用,版本,发布策略等在配置中心统一管理。一个大型应用被「碎片化」后,还要能做到「一目了然」。哪个模块报错,加载失败等异常发生第一时间反应在配置中心里。

下面的原型图,就是一个最基本的配置中心的样貌。微前端体系要可控、可观察。

123003.jpg

通过多个微应用的组合,能够在变化如此复杂的需求中,更好的更快的赋能业务。

云时代的前端开发模式

前端开发从 PC 时代到移动时代,从刀耕火种的原始运维到云计算时代,回顾起来,我们会发现——开发模式跟时代背景真是密不可分。前端奋斗 20 年才把页面写好,而现在又变成「切页面」了,只是此「切」非彼「切」。云时代的开发模式注定是「碎片化」的,开发是面向模块的,而页面只是一种组合场景,一种运行时容器。

我想,未来的产品开发主要时间是在「编排」——编排服务、编排逻辑、编排组件、编排访问策略、编排流程。到了云时代,一家企业只要招几个前端工程师就可以了,兼顾开发和运维、资产全部上云,运维任务通过控制台就能完成。开发借助 Serverless 和编排工具就能实现无服务端。在未来,无论是前端工程师还是全栈工程师,都将不复存在,应该叫端到端(F2E -> E2E)工程师了。

]]>
游戏的运行逻辑分析 | Python从入门到精通:入门篇之二十二-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 第二十一节:Python性能优化
首先来看一下游戏的效果图:
image.png
因为还没有学习界面设计,所以咱们还是在DOS命令行里面运行程序,模拟一个游戏的制作。
一、第一部分就是一个“身份的选择”。
1、当我们选择了“唐僧”的时候:
image.png
系统显示我们的身份信息和可以进行的操作。
2、当我们选择了“白骨精”的时候:
image.png
因为“白骨精”是超级大反派,所以是不可以直接当boss的哈,系统为我们匹配的身份依然是“唐僧”。
3、当我们输入1、2之外的内容(数字或者字母):
image.png
系统为我们分配了“唐僧”的身份。接下来就跟上面是一样的了。
所以第一部分“身份的选择”大致逻辑就是如下:
①显示提示信息:
欢迎xxx光临!
请选择你的身份:
1.xxx
2.xxx
请选择:x
②根据用户选择来分配身份(根据不同选择显示不同的提示信息)
1.---
2.---
3.---
目前对于我们这个游戏来说,选择不同身份之后只是提示信息不同,最终分配的身份都是以“唐僧”进行游戏。
二、第二部分就是一个“游戏的进行
当我们完成身份的选择之后,获得了“唐僧”的身份,接下来可以该角色进行游戏。
image.png
显示玩家的基本信息(攻击力 生命值)
身份:“唐僧”
攻击力:2
生命值:2
你也可以设置的复杂一点,包括防御力,敏捷,装备。。但是在这里主要是一个小练习,所以我们就做的简单一点,只包括攻击力和生命值,这些内容都是初始化的内容,一旦选择了身份,值就是固定的。
接下来是可以进行操作的选择,包括练级、打boss、逃跑。
显示玩家可以进行的操作:
1、练级:提升玩家的攻击力和生命值。
2、打boss:玩家对boss进行攻击,玩家要攻击boss,boss也要反击。
在这里要计算boss是否被玩家消灭,玩家是否被boss消灭。
3、逃跑:游戏的退出,显示提示信息,退出游戏。
小提示:在玩家选择了操作之后,比如练级,升级结束依然可以继续选择操作,想想这部分可以怎么写。
游戏的逻辑就是这样啦,相信同学们心里已经有了一个大致的代码样子,接下来就去将它实现吧!
第二十三节:唐僧大战白骨精

视频学习:阿里云大学之Python进阶必看

配套Python进阶文章点击此处获取

]]>
看看前端学习路线,你还有哪些没掌握?-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800

转自:一点通

初级阶段(静态网页制作、JS编程入门)

1.Photoshop入门

图像处理基础知识、图像处理基础知识、分辨率、图像的色彩模式、常用的图像文件格式。Photoshop工作界面的介绍、文件操作、图像的显示效果、图像和画布尺寸的调整、绘制和编辑选区、选区的操作技巧、渐变工具和油漆桶工具。图像合成、图层、蒙版、配色、切图、色彩搭配原则。

2.互联网基本原理

互联网基本原理、服务器、浏览器、HTTP请求的概念。

3.HTML

编程工具介绍、HTML简介。HTML语义化标签、p标签和h系列标签。HTML基本骨架深入、HTML骨架。元信息标记meta、设置页面关键字、字符集、关键字、页面描述。a标签和img标签,相对路径、绝对路径。ul、ol、dl标签、div和span标签介,表单知识。

4.CSS基础和高级技巧

CSS介绍,选择器。文字属性、颜色属性。继承性和层叠性,权重计算。CSS盒模型,width、height、padding、border、margin属性。父子嵌套模型,使用Fireworks精确还原设计图。float浮动属性、浮动的意义、清除浮动的方法。浏览器兼容,CSS hack。background系列属性、css精灵、网页上透明。超级链接的伪类、导航条。position定位、定位小技巧。DIV+CSS布局。

5.静态网站制作项目

搜索引擎优化的概念、页面常用SEO技巧、学会有格调的制作页面。iconfont字体图标。一些常见的CSS高级技巧,比如负margin、压线技术、滑动门、列自撑技术。较复杂布局网站的学习,通栏banner、大背景等时下流行的网站制作方法。

中级阶段(JS进阶、HTML5和CSS3、Ajax和Canvas)

6.JavaScript基础

语句、执行顺序、词法结构、标识符、关键字、变量、常量、alert语句和console控制台。值和变量、数字、文本、布尔值、null和undefined。表达式和运算符、运算符概述。流程控制、赋值语句、条件判断语句、if语句、switch语句、循环控制语句、while语句、do…while语句、for循环。跳转语句:continue、break。函数、参数、返回值、递归、作用域、全局变量、局部变量。

7.JavaScript DOM编程

事件与事件处理概述、事件与事件名称、常用事件、事件处理程序的调用、DOM事件模型、事件流、 事件对象、注册与移除事件。文档对象的常用属性、方法与事件、输出数据。事件的三要素。DOM对象、DOM概述、DOM分层、DOM级别、DOM对象节点属性、遍历文档树、克隆删除替换。动画基础知识、定时器、setInterval和setTimeout、运动效果、 实用的动画、制作运动效果。

8.JQuery页面特效

jQuery对象和DOM对象、jQ选择器、CSS操作、设置和获取HTML、文本和值。事件、加载DOM、事件绑定、合成事件、事件对象的属性。动画、自定义动画方法、动画回调函数、停止动画、其他动画方法。jQuery对表单、表格的操作及更多应用、表单应用、表格应用。jQuery插件,jQueryUI,jQuery ease,jQuery mousewheel等。超多页面特效!结合案例掌握了解jQuery插件的使用。

9.JavaScript进阶

命名空间、对象扩展、数组化、主流框架引入的机制——domReady、无冲突处理。语言模块、字符串的扩展与修复、数组的扩展与修复、数值的扩展与修复、函数的扩展与修复、 日期的扩展与修复、浏览器嗅探与特征侦测 、判定浏览器、事件的支持侦测、样式的支持侦测。类工厂、JavaScript对类的支撑、各种类工厂的实现。浏览器内置的寻找元素的方法、属性模块、如何区分固有属性与自定义属性。

10.HTML5和CSS3

HTML5概述、HTML5新特性、HTML5组织、HTML5构成、HTML5页面的特征、HTML基础、HTML5全局属性、HTML5其他功能、HTML5元素分类。实战HTML5表单、新增的input输入类型。HTML5音频与视频、HTML5多媒体技术概述、在HTML5中播放音频。CSS3编码规范、了解CSS3新增特性。CSS选择器、属性选择器、结构伪类选择器、UI伪类选择器。旋转动画、缩放动画、移动动画、倾斜动画。3D炫酷动画效果。

11.移动web和响应式页面

视口、缩放 、分辨率、物理分辨率、设备像素比、dppx和dpi 、meta视口。百分比布局、流式布局、CSS3新的流式盒模型。触摸和指针事件、触摸事件、手势事件 、其他事件、拖放、滚动层、事件和交互模式、移动端交互综合实战。zepto.js、jQuery Mobile等移动端常见框架。HTML5速成移动端框架。Bootstrap3 、调整响应式导航条断点。移动优先、Bootstrap栅格系统、栅格系统原理、Bootstrap中的JavaScript交互、Bootstrap敏捷开发。

12.JavaScript面向对象

创建对象、属性的查询和设置、 删除属性、检测属性、枚举属性、属性getter和setter、属性的特性、对象的三个属性、序列化对象、对象方法。类和模块、类和原型、类和构造函数、类的扩充、类和类型、子类。原型、实例化和原型、 对象实例化、通过构造器判断对象、继承与原型链、构造函数和原型对象 、构造函数、原型对象 、[[Prototype]]属性 、在构造函数中使用原型对象 、改变原型对象、内建对象的原型对象。

13.服务器知识和PHP入门

后台语言和前台语言的区别。初识PHP、PHP语言的优势、PHP 5的新特性、PHP的发展趋势、PHP的应用领域。PHP环境搭建和开发工具。PHP语言基础、PHP变量、PHP运算符、 PHP的表达式、PHP编码规范、流程控制语句、字符串操作、PHP数组、 PHP与Web页面交互。数据库技术概述、增删改查。

14.Ajax

Ajax概述与Ajax初体验、Ajax技术介绍、XMLHttpRequest对象详解、动态加载和显示数据、XMLHttpRequest对象概述、方法、属性、发送请求、GET和POST请求、运行周期、使用JSON响应、Ajax实用包的封装。JSON的解析、Underscore模板引擎、模板技术、动态组装页面、电话号码归属地查询、验证码等。Ajax实战篇 、Ajax高级表单验证程序 、Ajax动态联动菜单、瀑布流。

15.Canvas和手机游戏

Canvas绘图、基本知识、理解canvas坐标系、获取canvas环境上下文、理解路径、路径操作API 、绘制线条 、绘制矩形 、绘制圆弧 、绘制贝塞尔曲线 、线条属性 、线条颜色 、填充 、绘图状态。图像API、使用canvas绘制图像、坐标变换、绘制文字。游戏原理、制作2D游戏引擎、理解游戏循环、渲染引擎实现、使用引擎构建游戏实例、游戏常用算法。

高级阶段(高端技术和高级框架)

16.Nodejs企业级应用

Node的特点、异步I/O、事件与回调函数、单线程、跨平台、Node的应用场景、I/O密集型、CommonJS规范、Node的模块实现 、路径分析和文件定位、模块编译 、核心模块、JavaScript核心模块的编译过程、网络编程、构建TCP服务。构建HTTP服务、构建WebSocket服务、网络服务与安全。MongoDB、Express、Mongoose、socket.io。小型微博系统、俄罗斯方块对战。Linux使用。

17.Angular.js主流框架

Angular基础知识 、 Angular中的控制器 、Angular中的模板 、Angular的过滤器、依赖注入、MVC模式 、Angular的服务 、与服务端交互 、Angular的指令。使用AngularJS构建一个单一页面应用程序(SPAs:Single Page Applications)。

18.Backbone框架

理解Backbone、模型、集合、视图、事件及其绑定 、RESTful服务、其他相关技术 、使用Require.js组织项目结构。模型(models)、集合(collections)、视图(views)结构学习。绑定键值数据、自定义事件、可枚举函数、声明事件处理函数、RESRful JSON接口。

19.Yeoman脚手架

基本安装、配置、HTML模板、图片压缩、构建工具、包管理器、JSLint测试。

20.Grunt和Gulp

GIT、SVN、Grunt、Gulp、Webpack。通过代码优于配置的策略,Gulp 让简单的任务简单,复杂的任务可管理。利用 Node.js 流的威力,你可以快速构建项目并减少频繁的 IO 操作。通过最少的 API,掌握 Gulp 毫不费力,构建工作尽在掌握:如同一系列流管道。

21.Sass,less和stylus

GIT、SVN、Grunt、Gulp、Webpack。sass中可以定义变量,方便统一修改和维护。用sass进行选择器的嵌套,表示层级关系。用sass中导入其他sass文件,最后编译为一个css文件t。用sass中可用mixin定义一些代码片段,且可传参数,方便日后根据需求调用。

22.ECMAscript2016

简介、let和const命令、变量的解构赋值、字符串的扩展、正则的扩展、数值的扩展、数组的扩展、函数的扩展、对象的扩展、Symbol、Set和Map数据结构、Proxy、Reflect、Iterator和for...of循环、Generator函数、Promise对象、异步操作和Async函数、Class、Decorator、Module、编程风格、读懂规格、二进制数组、SIMD

23.React构建视图组件

React简介、JSX、组件的生命周期、实例化、数据流、事件处理、组件的复合、mixin、DOM操作、动画、性能优化、服务端渲染、周边类库。VUE数据模板、生命周期、过滤器。

24.Vue界面的前端库

遇见Vue.js、数据绑定、指令、计算属性、表单控件绑定、过滤器、Class与Style绑定、过渡、绑定事件、组件、表单校验、分组校验、与服务端通信、RESTful调用。

25.Cordova和Phonegap

使用加速计和位置传感器、文件系统、存储及本地数据库、处理音频、图像和视频、处理通讯录、本地事件、使用XUI、使用jQuery Mobile进行用户界面开发、PhoneGap插件扩展、开发工具及测试。

26.ionic 框架

Ionic和Hybrid应用介绍、配置开发环境 、Ionic导航和核心组件、选项卡、高级列表和表单组件、开发高级应用、使用 Ionic 命令行代理、在页面中使用 ionScroll、过滤器:转换视图中的数据。

27.React Native移动开发

React Native简介、React Native开发基础、常用组件介绍及实践、TextInput组件、九宫格实现、NavigatorIOS组件、Touchable类组件、状态机思维与状态机变量、深入理解UI重新渲染的过程、Navigator组件工作机制、混合开发基础、组件生命周期、数据存储及React Native应用实现步骤。

28.JS微信开发

微信公众平台介绍 、使用云平台快速搭建公众账号 、在新浪SAE上创建App、部署代码 、开发接口的认证 、微信公众平台API详解:基础接口 、回复消息、微信公众平台API详解:高级开发、各接口的调用频次限制、客服接口 、二维码开发 、wechat的JS开发 、搭建开发环境和相关技术介绍 、海量请求的应对方法 、监控服务器的各项指标、恶意请求的应对方法。

好了,希望这里的资料会对你有所帮助。

]]>
IDE 插件新版本发布,总有一个功能帮到你——开发部署提速 8 倍-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 对于开发者而言,提高工作效率大概有 2 种主要方式,第一种方式就是加快自己的工作速度,争取在同一段时间内多码一些代码、多干一些活来实现多产;而聪明的开发者会选择第二种方式,就是通过插件,让一些重复性的、繁琐性的工作被自动化,从而节省出时间来做一些实质性的业务,达到轻松又高效的工作步调。

最近通过发布会直播,了解到一款本地 IDE 插件——Cloud Toolkit,就是通过第二种方式来提高开发者的工作效率。它能够帮助开发者更高效地开发、测试、诊断并部署应用,还支持快速创建 Dubbo 工程等等,自去年 12 月上线以来,就超过几万的开发者在使用这款插件。(观看中间件小姐姐直播演示,请点击)

本文将为大家盘点,Cloud Toolkit 的最新几个版本都为开发者带来了哪些新特性。以下是本文提纲:

  • 核心功能:一键部署应用
  • 支持 Windows 服务器部署
  • 支持 EDAS Kubernetes 集群部署
  • 支持 SSH 代理(跳板机)
  • 应用部署实时日志查看
  • 快速创建开源 RPC 框架 Dubbo 工程

更详细的操作可见:https://help.aliyun.com/document_detail/29968.html(点击“阅读原文”)

核心功能:一键部署应用

通过简单的初始化配置,这款插件就可以实现项目构建、打包、上传、部署的自动化,支持发布到云端(ECS、EDAS 和 Kubernetes 等)和任意服务器(Host)上。它不仅集成阿里巴巴代码规约(自动检测整个Java工程或单个Java文件的代码规范),还内置了 Arthas 程序诊断、Dubbo工具、Terminal Shell 终端和 MySQL 执行器等工具。

支持 Windows 服务器部署

针对采用 Windows 系统的开发者,插件现在已经支持将应用部署到 Windows 服务器,无需在一系列运维工具之间切换,只需在图形界面上选择目标服务器即可快速部署。操作步骤如下:(了解具体操作请点击“阅读原文”

  1. 在 IntelliJ IDEA 中导入您的工程;
  2. 添加服务器;

    • 在顶部菜单栏中选择 Tools > Alibaba Cloud > Alibaba Cloud View > Host
    • 在弹出的 Host 页签中单击 Add Host
    • Add Host 对话框中设置 Host List、Username、Password 和 Tag 等参数,完成后单击 Add

  3. 部署应用;

    • 在 IntelliJ IDEA 界面左侧的 Project 中右键单击您的工程,在快捷菜单中选择 Alibaba Cloud > Deploy to Host
    • Deploy to Host 对话框设置部署参数然后单击 Run

支持 EDAS Kubernetes 集群部署

针对阿里云的 EDAS 用户,Cloud Toolkit 实现了与 EDAS Kubernetes 集群的集成,开发者可以方便地在 IDE 中,直接将应用一键部署到对应的 Kubernetes 集群中去。操作步骤如下:(了解具体操作请点击“阅读原文”

  1. 在 IntelliJ IDEA 上单击 Cloud Toolkit 的图标,在下拉列表中选择 Deploy to EDAS -> EDAS for Kubernetes Application
  2. 在 Deploy to EDAS 对话框配置应用部署参数(如果您还没有在 EDAS 上创建应用,在对话框右上角单击 Create application on EDAS console…,跳转到 EDAS 控制台创建应用);

  1. 单击 Run,IntelliJ IDEA 的 Console 区域会打印部署日志,可以根据日志信息检查部署结果;

支持 SSH 代理(跳板机)

在部署的场景里,部分开发者会遇到如上图的困境:出于安全考虑,本地开发机和远程部署服务器 192.168.0.1 之间,网络上是不连通,只能通过一台代理机(俗称 “跳板机” )来打通网络环境。针对这样的情况,新版本 Cloud Toolkit 已经支持了 SSH 代理,来解决这部分开发者的困境,操作步骤如下:(了解具体操作请点击“阅读原文”

  1. 添加代理机;

    • 点击菜单: Tools - Alibaba Cloud - Alibaba Cloud View - Host
    • 点击 Add Host 按钮,在弹出的弹窗中,依次输入代理机的IP地址、用户名和密码(如下图所示:47.95.120.154 这台机器就是我们添加的机器 P),点击 Add 按钮 完成添加

  2. 添加目标部署机器;

该步骤和上述第一步完全一致,不再赘述。我们添加一台 IP 地址为 192.168.20.247 的机器 T。注意,此时先不要点击 Add 按钮,直接进入第三步。

  1. 将机器 P 设置为机器 T 的代理;

如下图,点击Advanced 标签页,在 SSH Proxy 项中选择刚刚添加的机器 P:47.95.120.154,点击 Add 按钮 完成添加。

应用部署实时日志查看

最新版本 Cloud Toolkit 新增了在对远程服务器应用部署时,查看实时日志的功能,这能够方便开发者在触发应用部署之后,就立即查看远程服务器上部署日志,第一时间看到启动,包括运行时的报错信息,进而跟进排查问题。操作步骤如下:(了解具体操作请点击“阅读原文”

  1. 打开应用部署的配置界面,点击「Advanced」标签页,如下图所示;

  1. Command 中填入查看实时日志的命令即可,并且勾选上 “Automatic open after deploy”,这样配置之后,就会在应用部署过程中,打开实时日志查看的 Terminal 了。查看实时日志的命令一般为:

    tail -f /root/act_tomcat/tomcat/logs/catalina.out -n200

快速创建开源 RPC 框架 Dubbo 工程

在最新版中,提供了快速创建 Dubbo 工程的功能。Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可,Dubbo 基于 Spring 的 Schema 扩展 进行加载。如果不想使用 Spring 配置,可以通过 API 的方式 进行调用。操作步骤如下:(了解具体操作请点击“阅读原文”

  1. 打开 IntelliJ IDEA,进入菜单:File - New - Project...
  2. 选择 JAVA SDK 版本,点击 Next
  3. 如下图,填写基本信息,包括 Dubbo 版本、Spring Boot 版本等;

  1. 确定创建;

如下图所示,就完成了一个完整的 Dubbo 工程的创建了,此工程的结构和 Apache Dubbo 官方样例工程完全一致。

总结

关于插件的功能还有很多,大家可以安装体验一下,相信总有一个特性能击中你的痛点,也期待 Cloud Toolkit 逐渐强大,为更多开发者解决更多的问题。

lADPDgQ9q_wwZq3NAUvNAUc_327_331_jpg_620x10000q90g

钉钉进群:群号 21961177

lALPDgQ9q_wwZr7NASzNASw_300_300_png_620x10000q90g

微信进群:(加产品经理,拉你入群)

]]>
2019年大数据十大新闻盘点-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800

作者:数据观

image.png

随着大数据发展上升为国家战略,全国各地加速布局,开创了大数据产业发展的新局面。在2019年,我国大数据市场保持稳定增长,基础设施建设、人才培养、融合应用、行业规范等成为大数据发展的主基调。那么,在本年度,中国及全球的大数据领域发生了哪些热度比较高的事件?数据观通过详细梳理,盘点了2019年大数据10大新闻。


新闻01 欧盟《通用数据保护条例(GDPR)》实施后,多家企业获罚单

2018年5月25日起,欧盟《通用数据保护条例(GDPR)》正式生效,这一条例被称为“史上最严数据保护法案”。今年1月21日,法国数据监管部门开出首张GDPR罚单,对美国搜索巨头谷歌处以5000万欧元罚款,约合人民币3.66亿元。法国国家信息自由委员会(CNIL)在声明中称,Google在处理个人用户数据时存在缺乏透明度、用户获知信息不充分以及缺乏对个性化广告的有效同意等问题,这也是GDPR实施后数额最大的罚单。

在2019年,被处以重罚的科技企业远不止谷歌一个。7月,针对2018年剑桥分析的隐私泄漏丑闻,经美国联邦贸易委员会(FTC)最终裁决,Facebook被处以罚款50亿美元,这是FTC对一家科技公司所开出的史上最大罚单。


新闻02 工信部等三部门发布绿色数据中心建设指导意见

2月12日,工业和信息化部、国家机关事务管理局、国家能源局联合印发《关于加强绿色数据中心建设的指导意见》,提出以提升数据中心绿色发展水平为目标,以加快技术产品创新和应用为路径,以建立完善绿色标准评价体系等长效机制为保障,大力推动绿色数据中心创建、运维和改造,引导数据中心走高效、清洁、集约、循环的绿色发展道路,实现数据中心持续健康发展。

《意见》明确,要建立健全绿色数据中心标准评价体系和能源资源监管体系,打造一批绿色数据中心先进典型,形成一批具有创新性的绿色技术产品、解决方案,培育一批专业第三方绿色服务机构。


新闻03 大数据、云计算工程技术等成为国家新职业

今年上旬,我国人力资源和社会保障部联合国家市场监管总局、国家统计局向社会发布了13个新职业,其中包括大数据工程技术人员、云计算工程技术人员和数字化管理师三项与大数据行业相关的新职业。

其中,大数据工程技术人员定义为,从事大数据采集、清洗、分析、治理、挖掘等技术研究,并加以利用、管理、维护和服务的工程技术人员。

主要工作任务包括:1.研究、开发大数据采集、清洗、存储及管理、分析及挖掘、展现及应用等技术;2.研究、应用大数据平台体系架构、技术和标准;3.设计、开发、集成、测试大数据软硬件系统;4.大数据采集、大数据清洗、大数据建模与大数据分析;5.管理、维护并保障大数据系统稳定运行;6.监控、管理和保障大数据安全;7.提供大数据的技术咨询和技术服务。

云计算工程技术人员定义为,从事云计算技术研究,云系统构建、部署、运维,云资源管理、应用和服务的工程技术人员。

主要工作任务包括:1.研究、开发虚拟化、云平台、云资源管理和分发等云计算技术,以及大规模数据管理、分布式数据存储等相关技术;2.研究、应用云计算技术、体系架构、协议和标准;3.规划、设计、开发、集成、部署云计算系统;4.管理、维护并保障云计算系统的稳定运行;5.监控、保障云计算系统安全;6.提供云计算系统的技术咨询和技术服务。

数字化管理师定义为利用数字化办公软件平台,进行企业及组织人员架构编辑、组织运营流程维护、工作流协同、大数据决策分析、企业上下游在线化连接,使企业组织在线、沟通在线、协同在线、业务在线、生态在线,实现企业经营管理在线化、数字化的人员。

主要工作任务包括:1.将企业及组织人员架构编辑在数字化管理平台,负责制定企业数字化办公软件推进计划和落地实施方案,进行扁平可视化管理;2.负责数字化办公所有模块的搭建和组织运转必备流程的维护,实现组织高效安全的沟通;3.设定企业及组织工作流协同机制,实现知识经验的沉淀和共享;4.通过业务流程和业务行为的在线化,实现企业的大数据决策分析;5.以企业为中心的上下游和客户都实现在线化连接,用大数据优化整个生态的用户体验,不断提升生产销售效率。


新闻04 196所高校获批“数据科学与大数据技术”专业

教育部今年3月印发了《教育部关于公布2018年度普通高等学校本科专业备案和审批结果的通知》,共196所高校获批“数据科学与大数据技术”专业。加上此前三批高校,我国已有480余所高校成功获批“数据科学与大数据技术”专业。

此外,今年还有25所高校获批“大数据管理与应用”专业。在今年获批的高校中,河南省、山东省、湖北省、陕西省的高校数量领先于其他省份。


新闻05 2019数博会在贵阳成功举办

5月26日,2019中国国际大数据产业博览会(简称“数博会”)在贵州省贵阳市开幕,会议围绕“一会、一展、一发布、大赛及系列活动”展开。习近平总书记再次为数博会发来贺信。

习近平在贺信中指出,当前,以互联网、大数据、人工智能为代表的新一代信息技术蓬勃发展,对各国经济发展、社会进步、人民生活带来重大而深远的影响。各国需要加强合作,深化交流,共同把握好数字化、网络化、智能化发展机遇,处理好大数据发展在法律、安全、政府治理等方面挑战。

本届数博会共举办了162场活动,参会和观展人数超过12.5万人,参会企业(机构)4847家,参展企业(机构)448家,布展面积6万平方米,共展出超过1200余项最新产品、技术和解决方案。

数博会作为全球首个大数据主题博览会,至此已连续在贵阳成功举办五届,2017年升格为国家级博览会,成为充满合作机遇、引领行业发展的国际性盛会,成为共商发展大计、共用最新成果的世界级平台。


新闻06 十余家大数据公司接连被查,祸起网络爬虫、“套路贷”

自今年9月以来,多家大数据公司接连被查,巨变启幕,行业“一夜入冬”。

2019年9月6日,第三方数据风控公司魔蝎数据和新颜科技的相关负责人在同一天被警方带走调查,由此拉开了行业大整顿的序幕。之后,聚信立、天翼征信、公信宝、同盾科技子公司、51信用卡等诸多公司也相继被查。

11月20日再传出消息,江苏淮安警方打击了7家涉嫌侵犯公民个人信息犯罪的公司,涉嫌非法缓存公民个人信息1亿多条,其中,拉卡拉支付旗下的考拉征信涉嫌从上游公司获取接口后,违规将查询接口出卖。

行业最严监管已经来临,业内人士表示:大数据发展和利用从野蛮生长时代进入了正规化管理的关键时代。


新闻07 换脸软件“ZAO”从爆红到被约谈,部分APP侵犯用户隐私受关注

今年9月,一款名为“ZAO”的换脸社交软件因其新颖的形式和强大的功能迅速引爆社交网络。走红之后,ZAO的用户协议引发了广泛质疑,部分用户在网络上公开表达了关于隐私、肖像权等问题的担忧。

随后,针对媒体公开报道和用户曝光的“ZAO”App用户隐私协议不规范,存在数据泄露风险等网络数据安全问题,工业和信息化部网络安全管理局对北京陌陌科技有限公司负责人进行了问询约谈,要求其开展自查整改,依法依规收集使用用户个人信息,规范协议条款,强化网络数据和用户个人信息安全保护。

随着此类换脸软件火爆网络,涉及隐私数据安全的换脸、人脸识别相关应用也越来越受到大家的关注。央视新闻记者调查发现,多款APP还存在着随意收集人脸数据信息的情况。比如一款叫做“人脸打分”的APP和另外一款叫做“证件照随拍”的APP,在没有任何使用协议的情况下,随意采集用户的人脸数据信息;一款名为“转转”的APP上以关键词“人脸数据集”搜索相关信息,迅速弹出了一件名为“人脸相关算法训练数据集”的商品,这个数据集包含5000多张人脸,打包只要10元。此外,在百度一个名为“快眼”的贴吧,也发现有销售者在兜售人脸数据。


新闻08 2019年中国大数据企业50强发布,华为、腾讯、阿里位列前三

在9月举办的2019世界计算机大会上,工信部等部门发布了《2019中国大数据产业发展白皮书》,并公布了2019中国大数据企业50强榜单。榜单显示,随着移动互联网、物联网、云计算产业的深入发展,2019年大数据体量呈现爆发式增长态势。2018年我国大数据产业规模突破6000亿元;随着大数据在各行业的融合应用不断深化,预计2019年中国大数据市场产值将达到8080亿元。

在榜单中,华为、腾讯、阿里、小米、百度、滴滴出行、中科曙光等企业入选50强名单。

该榜单由政府主管部门领导、大数据产业资深专家、名企CIO、高校、研究机构、投资人专家共同组成专家委员会,从大数据产品、解决方案、应用案例、投融资需求及发展潜力等多个维度对大数据企业进行考核评审产生。


新闻09 六个“国家数字经济创新发展试验区”正式启动

今年10月20日,在国家数字经济创新发展试验区启动会在第六届世界互联网大会(乌镇峰会)期间正式召开,会议发布了《国家数字经济创新发展试验区实施方案》,并向浙江省、河北省(雄安新区)、福建省、广东省、重庆市、四川省等6个“国家数字经济创新发展试验区”授牌,正式启动试验区创建工作。

会议要求,各试验区要坚持新发展理念,坚持推动高质量发展,坚持以深化供给侧结构性改革为主线,结合各自优势和结构转型特点,在数字经济要素流通机制、新型生产关系、要素资源配置、产业集聚发展模式等方面开展大胆探索,充分释放新动能。


新闻10 我国多地区出台数据管理办法

今年以来,为规范大数据产业发展,我国多部门、多地区出台了数据管理相关办法。例如,为进一步加强科学数据管理,保障科学数据安全,提高科学数据开放共享水平提供了制度规范,中国科学院在2月印发《中国科学院科学数据管理与开放共享办法(试行)》;5月28日,国家网信办对《数据安全管理办法(征求意见稿)》公开征求意见,征求意见稿提到,国家采取措施,监测、防御、处置来源于中华人民共和国境内外的数据安全风险和威胁,保护数据免受泄露、窃取、篡改、毁损、非法使用等,依法惩治危害数据安全的违法犯罪活动。

此外,今年1月1日,《天津市促进大数据发展应用条例》正式施行;1月17日,《吉林省公共数据和一网通办管理办法(试行)》公布;《贵州省大数据安全保障条例》10月1日起施行;11月1日,《海南省大数据开发应用条例》正式实施;11月15日,《福州市政务数据资源管理办法》《福州市政务数据汇聚共享管理暂行办法》《福州市政务数据资源共享开放考核暂行办法》《福州市公共数据开放管理暂行办法》等四部数据资源相关管理办法正式实施。

]]>
全能的枚举类 | 带你学《Java面向对象编程》之七十五-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 上一篇:迅速了解多例模式竞争者-枚举 | 带你学《Java面向对象编程》之七十四
【本节目标】
通过阅读本节内容,你将了解到enum关键字与Enum抽象类的区别,并能够掌握在枚举类中编写其他内容的方法,学会在日常开发中合理利用枚举类简化代码逻辑。

Enum类

严格意义上来讲,枚举并不属于一种新的结构,其本质相当于是一个类,但是这个类会默认继承Enum类,首先来观察一下Enum类的基本定义:

public abstract class Enum<E extends Enum<E>>
extends Object
implements Comparable<E>, Serializable

现在定义的枚举类的类型就是Enum中所使用的E类型。下面来观察一下Enum类中定义的方法。

No. 方法名称 类型 描述
01 protected Enum(String name,int ordinal) 构造 传入名字和序号
02 public final String name() 普通 获得对象名字
03 public final int ordinal() 普通 获得对象序号

范例:观察Enum类的存在

enum Color {    //枚举类
    RED,GREEN, BLUE ;    //实例化对象
}
public class JavaDemo{
   public static void main(String args[]) {
       for (Color c : Color.values()) {
            System.out.println(c.ordinal() + " - " + c.name()) ;
       }
   }     
}

image.png
图一 执行结果一

在枚举之中每一个对象的序号都是根据枚举对象的定义顺序而决定的。
面试题:请解释enum与Enum的区别?

  • enum:是从JDK1.5之后提供的一个关键字,用于定义枚举类;
  • Enum:是一个抽象类,所以使用enum关键字定义的类就默认继承了此类。

定义枚举结构

一直强调枚举本身就属于多例设计模式,那么既然是多例设计模式,那么在一个类之中可以定义的结构是非常多的,例如:构造方法、普通方法、属性等,那么这些内容在枚举类中依然可以直接定义,但是需要注意的是:枚举类中定义的构造方法不能够采用非私有化定义(public无法使用)。
范例:在枚举类中定义其它的结构

enum Color {    //枚举类
    RED("红色"),GREEN("绿色"), BLUE("蓝色") ;    //枚举对象要写在首行
    private String title ;   //定义属性
    private Color(String title) {
          this.title = title ;
    }
    public String toString() {
          return this.title ;
    }
}
public class JavaDemo{
   public static void main(String args[]) {
      for (Color c : Color.values()) {      
          System.out.println(c.ordinal() + " - " + c.name() + " - " + c) ;
      }
   }     
}

本程序在简化程度上一定要远远高于多例设计模式。除了这种基本的结构之外,在枚举类中也可以实现接口的继承。
范例:让枚举实现接口

interface IMessage{
    public String getMessage() ;
}
enum Color implements IMessage {    //枚举类
    RED("红色"),GREEN("绿色"), BLUE("蓝色") ;    //枚举对象要写在首行
    private String title ;   //定义属性
    private Color(String title) {
          this.title = title ;
    }
    public String toString() {
          return this.title ;
    }
    public String getMessage() {
          return this.title ;
    }
}
public class JavaDemo{
   public static void main(String args[]) {
      IMessage msg = Color.RED ;
      System.out.println(msg.getMessage()) ;     //红色
   }     
}

在枚举类中,最有意思的是它可以直接定义抽象方法,并且要求每一个枚举对象都要独立覆写此抽象方法。
范例:观察枚举中定义抽象方法

enum Color  {    //枚举类
   RED("红色") {
      public String getMessage() {
          return this.toString() ;
      }
   },GREEN("绿色") {
      public String getMessage() {
          return this.toString() ;
      }, BLUE("蓝色") {
      public String getMessage() {
          return this.toString() ;
      };    //枚举对象要写在首行
  private String title ;   //定义属性
  private Color(String title) {
     this.title = title ;
  }
  public String toString() {
     return this.title ;
  }
  public abstract String getMessage() ;
}
public class JavaDemo{
   public static void main(String args[]) {
       System.out.println(Color.RED.getMessage()) ;   //红色
   }     
}

发现枚举的定义是非常灵活的,但是在实际的使用之中,枚举更多情况下还是建议使用它的正确用法,就是定义一个实例对象即可。

枚举的实际应用

下面为了更好的巩固枚举的使用,编写一个程序来观察枚举的应用,例如:现在定义一个Person类,里面一定有性别,性别肯定不希望用户能随意输入,所以使用枚举最合适。
范例:使用枚举

enum Sex {
    MALE("男") ,FEMALE("女") ;
    private String title ;
    private Sex(String title) {
          this.title = title ;
    }
    public String toString() {
          return this.title ;
    }
}
class Person {
    private String name ;
    private int age ;
    private Sex sex ;
    public Person(String name ,int age , Sex sex) {
         this.name = name ;
         this.age = age ;
         this.sex = sex ;
    }
    public String toString() {
         return "姓名:" + this.name + "、年龄:" + this.age + "、性别:" + this.sex ;
    }
}
public class JavaDemo{
    public static void main(String args[]) {
        System.out.println(new Person("张三" ,"20" ,"男")) ;    //姓名:张三、年龄:20、性别:男
    }     
}

这个程序实际上不使用枚举也能正常实现,追加几个判断即可,所以对于枚举的使用,采用自愿即可。

想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
本内容视频来源于阿里云大学

下一篇:出错保障局-异常处理机制 | 带你学《Java面向对象编程》之七十六
更多Java面向对象编程文章查看此处

]]>
出错保障局-异常处理机制 | 带你学《Java面向对象编程》之七十六-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 上一篇:全能的枚举类 | 带你学《Java面向对象编程》之七十五
【本节目标】
通过阅读本节内容,你将了解到异常的基本定义,以及处理异常的基本方法,学会使用try...catch...finally语法实现异常的捕获与处理。

Java语言提供的最强大的支持就在于异常的处理操作上。

认识异常对程序的影响

异常指的是导致程序中断执行的一种指令流。那么下面首先来观察没有异常产生的程序执行结果。
范例:没有异常产生

public class JavaDemo{
   public static void main(String args[]) {
       System.out.println("【1】******程序开始执行******") ; 
       System.out.println("【2】数学计算:" +(10 / 2)) ;   
       System.out.println("【3】******程序执行完毕******") ;    
   }     
}

image.png
图一 执行结果一

在程序执行正常的过程里面会发现,所有的程序会按照既定的结构从头到尾开始执行。
范例:产生异常

public class JavaDemo{
    public static void main(String args[]) {
       System.out.println("【1】*****程序开始执行*****") ; 
       System.out.println("【2】数学计算:" +(10 / 0)) ;   
       System.out.println("【3】*****程序执行完毕*****") ;    
    }     
}

image.png
图二 执行出现错误

在出现错误之后,整个程序将不会按照既定的程序进行执行,而是中断了执行。那么为了保证程序出现了非致命错误之后程序依然可以正常完成,所以就需要有一个完善的异常处理机制,以保证程序的顺利执行。

处理异常

在Java之中如果要进行异常的处理,可以使用:try、catch、finally这几个关键字来完成,其基本的处理结构如下:

try{
       //可能出现异常的语句
}[catch(异常类型 异常对象){
       //异常处理
}catch(异常类型 异常对象){
       //异常处理
}catch(异常类型 异常对象){
       //异常处理
}.....] [finally {
       不管异常是否处理都要执行 ;
}]

在此之中可以使用的组合为:try…catch、try…catch…finally、try…finally。
范例:处理异常

public class JavaDemo{
    public static void main(String args[]) {
        System.out.println("【1】*****程序开始执行*****") ; 
        try {
               System.out.println("【2】数学计算:" +(10 / 0)) ;   
        } catch (ArithmeticException e) {
               System.out.println("【c】处理异常:" + e) ;          //处理异常
        }
        System.out.println("【3】*****程序执行完毕*****") ;    
     }     
}

image.png
图三 执行结果三

此时可以发现现在即便出现了异常,程序也可以正常执行完毕,所以此时的设计属于一个合理设计。但是有一个问题出现了,此时在进行异常处理的时候直接输出的是一个异常类的对象,那么对于此对象如果直接打印(调用toString())所得到的异常信息并不完整,如果要想获得非常完整的异常信息,则可以使用异常类中提供的printStackTrace()方法完成。
范例:获取完整异常信息

public class JavaDemo{
    public static void main(String args[]) {
        System.out.println("【1】*****程序开始执行*****") ; 
        try {
              System.out.println("【2】数学计算:" +(10 / 0)) ;   
        } catch (ArithmeticException e) {
              e.printStackTrace() ;
        }
        System.out.println("【3】*****程序执行完毕*****") ;    
    }     
}

image.png
图四 完整异常信息的输出

对于异常的处理格式也可以在最后追加有一个finally的程序块,表示异常处理后的出口,不管是否出现异常都执行。
范例:使用finally语句

public class JavaDemo{
    public static void main(String args[]) {
        System.out.println("【1】*****程序开始执行*****") ; 
        try {
              System.out.println("【2】数学计算:" +(10 / 0)) ;   
        } catch (ArithmeticException e) {
              e.printStackTrace() ;
        }finally {
                  System.out.println("【F】不管是否出现异常,我都会执行。") ;
        }
        System.out.println("【3】*****程序执行完毕*****") ;    
    }     
}

image.png
图五 执行结果五

此时程序中有异常执行finally,没有异常也执行finally。

想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
本内容视频来源于阿里云大学

下一篇:强悍的异常处理-处理多个异常 | 带你学《Java面向对象编程》之七十七
更多Java面向对象编程文章查看此处

]]>
强悍的异常处理-处理多个异常 | 带你学《Java面向对象编程》之七十七-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 上一篇:出错保障局-异常处理机制 | 带你学《Java面向对象编程》之七十六
【本节目标】
通过阅读本节内容,你将了解到catch块中捕获指定的异常时具有的风险,并学会使用多个catch捕获多个异常的办法来解决这个问题。

处理多个异常

很多时候在程序执行的过程之中可能会产生若干个异常,那么这种情况下也可以使用多个catch进行异常的捕获。假设通过初始化的参数来进行两个数学计算数字的设置。

public class JavaDemo{
    public static void main(String args[]) {
        System.out.println("【1】*****程序开始执行*****") ; 
        try {
              int x = Integer.parseInt(args [0]) ;
              int y = Integer.parseInt(args [1]) ;
              System.out.println("【2】数学计算:" +(x / y)) ;   
        } catch (ArithmeticException e) {
              e.printStackTrace() ;
        }finally {
              System.out.println("【F】不管是否出现异常,我都会执行。") ;
        }
        System.out.println("【3】*****程序执行完毕*****") ;    
   }     
}

对于此时的程序就有可能产生三类异常:

  • 【未处理】程序执行的时候没有输入初始化参数(java JavaDemo):
    java.lang.ArrayIndexOutOfBoundsException;

程序执行结果:

image.png
图一 没有输入初始化参数异常

  • 【未处理】输入时的数据不是数字(java JavaDemo a b):
    java.lang.NumberFormatException;

程序执行结果:

image.png
图二 输入的数据不是数字异常

  • 【已处理】输入的被除数为0(java JavaDemo 10 0):
    Java.lang.ArithmeticException;

程序执行结果:

image.png
图三 已处理的被除数为0异常

现在即便有了异常处理语句,但是如果没有正确的异常捕获,那么程序也会导致中断(finally的代码依然执行),在这样的情况下就必须进行多个异常的捕获。
范例:修改程序代码

public class JavaDemo{
    public static void main(String args[]) {
        System.out.println("【1】*****程序开始执行*****") ; 
        try {
              int x = Integer.parseInt(args [0]) ;
              int y = Integer.parseInt(args [1]) ;
              System.out.println("【2】数学计算:" +(x / y)) ;   
         } catch (ArithmeticException e) {
              e.printStackTrace() ;
         }catch (NumberFormatException e) {
              e.printStackTrace() ;
         }catch (ArrayIndexOutOfBoundsException e) {
              e.printStackTrace() ;
         }finally {
              System.out.println("【F】不管是否出现异常,我都会执行。") ;
         }
         System.out.println("【3】*****程序执行完毕*****") ;    
    }     
}

此时开发者都已经明确的知道了有哪些异常了,那么又何必非要用异常处理呢?直接进行判断就可以了。

想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
本内容视频来源于阿里云大学

更多Java面向对象编程文章查看此处

]]>
没学好数据库的程序员,真的混不到饭吃么?-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 业内有句俗语:

只会写代码的是码农;学好数据库,基本能混口饭吃;在此基础上再学好操作系统和计算机网络,就能当一个不错的程序员。

如果能再把离散数学、数字电路、体系结构、数据结构/算法、编译原理学通透,再加上丰富的实践经验与领域特定知识,就能算是一个优秀的工程师了。

这么说其实是有一些道理的,因为计算说穿了就是两个东西:数据与算法。

目前市面上常见的软件应用,大部分都属于数据密集型应用。通俗的话来讲,就是这些应用干的事儿就是把数据收集起来,需要的时候再拿出来。而这些操作都需要数据库来进行承载。

所以说,数据库离我们很近,也是一项开发者们非常需要掌握的技能。

本期内容重点:

  • 数据库解析
  • 数据库发展史
  • 对开发者的建议

01、啥是数据库?

640-68.jpeg

名词解析:数据库

数据库,简而言之可视为电子化的文件柜 —— 存储电子文件的处所,用户可以对文件中的数据运行新增、截取、更新、删除等操作。所谓“数据库”系以一定方式储存在一起、能与多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。
数据库技术产生于 20 世纪 60 年代末 70 年代初,设置的主要目的是有效地管理和存取大量的数据资源。

数据库其实就是存放数据的仓库。它的存储空间很大,可以存放百万条、千万条、上亿条数据。但是数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。
随着互联网的发展,当今世界是一个充斥着大量数据的世界。数据的来源有很多,比如出行记录、消费记录、浏览的网页、发送的消息等等。除了文本类型的数据,图像、音乐、声音都是数据。

数年来,数据库技术和计算机网络技术的发展相互渗透,相互促进,已成为当今计算机领域发展迅速,应用广泛的两大领域。数据库技术不仅应用于事务处理,并且进一步应用到情报检索、人工智能、专家系统、计算机辅助设计等领域。

数据库的概念实际包括两层意思:

数据库是一个实体,它是能够合理保管数据的“仓库”,用户在该“仓库”中存放要管理的事务数据,“数据”和“库”两个概念结合成为数据库。

数据库是数据管理的新方法和技术,它能更合适的组织数据、更方便的维护数据、更严密的控制数据和更有效的利用数据。

数据库技术研究和管理的对象是数据,所以数据库技术所涉及的具体内容主要包括:

通过对数据的统一组织和管理,按照指定的结构建立相应的数据库和数据仓库;

利用数据库管理系统和数据挖掘系统设计出能够实现对数据库中的数据进行添加、修改、删除、处理、分析、理解、报表和打印等多种功能的数据管理和数据挖掘应用系统;

利用应用管理系统最终实现对数据的处理、分析和理解。

按照层级架构,数据库的架构一般可以划分为三层:

  • 内层:最接近实际存储体,亦即有关数据的实际存储方式;
  • 外层:最接近用户,即有关个别用户观看数据的方式;
  • 概念层:介于两者之间的间接层。

从其应用方式来看,数据库技术主要起着两方面的作用:

信息系统开发作用:利用数据库技术以及互联网技术,结合具体的编程语言,可以开发一个信息系统,从而解决业务数据的输入和管理问题.

数据分析与展示作用:利用RDBMS的数据查询功能对数据库中的数据进行关联组合或逐级汇总分析,并以表格,图形或报表形式将分析结果进行展示,从而解决业务数据的综合利用问题。

02、数据库发展阶段

640-69.jpeg

从原理来看,不难知道数据库技术的核心和基础就是「数据模型」。所以业内回顾数据库的发展阶段时,一般也是根据数据模型的演进作为相关的时间节点。
在数据库的发展历史上,数据库先后经历了层次数据库、网状数据库和关系数据库等各个阶段的发展。在这短短几十年的发展过程中,数据库的发展一般划分为下面这三代:

  • 第一代:网状和层次数据库系统;
  • 第二代:是关系数据库系统;
  • 第三代:以面向对象数据模型为主要特征的数据库系统。

]]> Cassandra 在 360 的实践与改进-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 供宽表数据库选型参考

11月16日在北京,由DataFun和阿里云联合举办的首场Cassandra中文社区线下meetup,奇虎360王锋老师分享了在360的应用,针对HBase和C的选型,C的优缺点及改进方案做了分享,最多时达一万四千节点,值得细细品读。

以下文章来源于DataFunTalk ,作者王锋

导读:2010年,Dropbox 在线云存储在国外被用户熟知,同时国内如360、金山、百度等各个厂商也都陆续推出了自家的网盘类产品;而在 "360云盘" 背后的存储技术支撑之一就是以 Cassandra 为基础的云端存储方案。自此,Cassandra 在360实现技术落地和大规模生产应用,并被持续改进优化,最终形成高峰时期超 10k+ 物理节点的使用规模,成为互联网公司中 Cassandra 生产环境落地规模最大的公司。

本次分享的主要内容是 Cassandra 在360的落地实践过程中遇到的问题,以及一些重要的改进和优化,主要包括:

  • Cassandra 的特点简介
  • Cassandra 在360的选型
  • Cassandra 在360的应用场景
  • Cassandra 在360的技术演进

——Cassandra 的特点简介——

Cassandra 大致有以下的特点:

image.png

Cassandra 完全无中心化设计使得其具备极高的可用性和可平滑的拓展性,并且具有模式灵活,多数据中心,范围查询,列表数据结构,分布式写操作等优势:

❶ 由于其架构在中小规模部署时不需要主节点,相较于完全中心化的分布式存储设计具有更优的成本优势,从3台物理机开始一直拓展到几百台物理机,均可完全不停服情况下平滑拓展,整个过程只需要把拓展节点的进程启动加入集群;

❷ 模式灵活使得 Cassandra 可以在系统运行时随意添加或移除字段,这是一个很惊人的效率提升,特别是在大型部署上;

❸ 多数据中心是指可以调整节点布局来避免某一个数据中心失效,一个备用的数据中心将至少有每条记录的完全复制;

❹ 范围查询是指如果你不喜欢全部的键值查询,则可以设置键的范围来查询,对于每个用户的索引,这是非常方便的;

❺ 分布式写操作是指有可以在任何地方任何时间集中读或写任何数据,并且不会有任何单点失败。

除了以上几点,Cassandra 还有如下的优点:

❶ 海量数据,随时在线,分布式数据库,性能极优

❷ always online,无中心,无单点故障引发抖动

❸ 节点对等,配置一致,线性扩展,易于维护

❹ cql/sdk 能力,易用性好,开发者 & DBA 快速上手

❺ 功能强大,主要有以下几点功能,多 DC 两地三中心,在线更改 schema,丰富数据结构,丰富的索引,监控及工具。

——Cassandra 在360的选型——

image.png

选型之始,我们总结评估了云存储的技术需求特征和当时可承载大规模数据的分布式 K-V 数据库——Cassandra 和 HBase,最终权衡之后,使用 Cassandra 做为主要在线存储。对于 "网盘/云盘" 类产品,其主要流量特征为 "写多读少",要求服务可靠性和数据安全性极高,基本不可容忍服务中断和数据丢失的情况。具体选型分析如下:

❶ Cassandra 相较于 HBase,前者是完全无中心设计,而后者 ( 包括依赖的 HDFS ) 整体来看是强中心化设计,因此 Cassandra 与生俱来不存在关键单点故障影响服务的问题;

❷ Cassandra 使用最终一致性策略,而非 HBase 的强一致性策略,配合读写策略的处理,Cassandra 可以在确保数据安全性、可靠性、一致性的前提下,出现节点宕机而不需要恢复时间,集群读写不产生任何停顿,而此场景下,HBase 需要等待 region 重新分配过程,而这个过程大概会有数秒至数分钟的待分配 region 不可读写;

❸ 从技术细节上,虽然二者均采用了 LSM 的架构,但 Cassandra 直接操作本地磁盘,而 HBase 需要依赖 HDFS 共享存储,加之上述所说的架构设计差异,同等基础设施性能的 Cassandra 写入性能优于 HBase 近乎一个数量级,读性能基本持平。

综上所述,Cassandra 可以更好的适用于云盘在线场景。

——Cassandra 在360的应用场景——

image.png

基于360云盘使用 Cassandra,云盘从15台机器一直到14000+台机器,应用场景主要是个人数据,自己产品中间的图片,内部视频对象等;在2015年,360云盘转型为企业云盘,机器数量就下降了,到2018年,智汇云又继续前行,目前机器差不多是3000左右的规模,以上是360的应用场景。

——Cassandra 在360的技术演进——

image.png

Cassandra 自2010在360开始调研技术落地;2011年使用 Cassandra 0.7.3作为基础版本应用于生产环境;2012年完善数据可靠性和安全性,实现不停机和不单纯依赖读修复的数据快速恢复;2013-2014年以节省成本为目的,实现可擦除编码技术应用于 Cassandra,在确保数据安全和可靠性的前提下实现成本降低60%;2014-2015年面对超大规模集群的超复杂性问题,实现运维自动化,集群具备自主自愈、自主风控等自主运维能力 ( 近 1w5 物理节点,89个集群,两人运维 );2018年,我们发现 Cassandra 社区版本与360版本相当于是不同场景殊途同归 ( 社区为轻 Value,360为重 Value ),并且社区很多好的思路非常值得考虑,因此我们重新调整研发策略,与社区共同成长。

image.png

Cassandra 是一种无中心的系统,对于消息的广播,有一些规模的限制,基本单节点到600台的时候就差不多了,当时云盘的集群规模,单集群是600台,Cassandra 集群规模达到了88个,磁盘使用率达到了90%,主要是为了成本考虑,把磁盘使用率达到了90%。这其中用的是预先划分 range,毕竟当时没有 VNode,使用预先划分首先是使用 RandomPartitioner,使用例如 hash,md5 让数据随机打到环上,这个是使用最多的;还有一种是 OrdePerservingPartitinoer,这是一种保序的方式,把一些 key 保序的存在环上,文件 I/O 使用的 standard 跟 Mmapped,Mmapped 理论上是减少内存拷贝,对性能很好,但是如果数据量涨到80%到90%的时候,Linux 内核页表的开销占用量很大,所以最后放弃了 Mmapped 的方式,使用了 standard 的方式。

image.png

对于 Cassandra 的改进,第一个就是进行可靠性的改进,使用 Local Repair 跟 Backup。影响数据可靠性的因素有:

❶ One/Quorum 存在新增副本不足的问题;

❷ 磁盘/扇区故障:文件损坏、数据丢失 ( 月均故障25-30块 );

❸ 现有数据恢复机制存在不完善之处。

因素 ❶ 是第三副本是否可以成功写入的问题,使用非 ALL 策略写入 Cassandra 时,只要满足写入策略即返回成功,例如 quorum 级别写入3副本数据,当两个节点写入成功即返回写入成功,虽然原始设计为了保障第三副本写入成功使用 hintedhandoff 机制来保证,但程序设计最多能支撑3小时的时间,虽然该项可配但也受限于接入节点的存储容量,因此360针对此问题做了优化,研发 proxycheck 功能将未成功写入打散到全集群,当故障节点恢复时,基于 proxycheck 会修复残缺副本;

因素 ❷ 是磁盘故障,虽然小规模磁盘很少见磁盘损坏,但对于极大规模的存储系统来说,磁盘故障就变得不可忽略了,而 Cassandra 的架构又决定了如果磁盘损坏造成了副本残缺很难发现,只能等待读修复触发或者 repair 工具修复,但对长时间不读取的冷数据很显然存在较大数据风险;

因素 ❸ 是修复机制,无论是因素 ❷ 导致的还是其他问题造成的数据残缺都需要恢复机制尽快恢复数据,但 Cassandra 读修复对冷数据不友好,repair 工具会耗尽整个集群的资源,对于这些挑战,除了读修复,我们实现了一套相当于 RowRepair 的机制。

image.png

首先来说一下文件/磁盘的自动摘除, 存在的问题主要有两点,一点是读写异常,SEEKIOException 影响正常读写,另外一点是各种修复机制,Compact 机制执行失效,针对以上的两点问题,主要采用了基于文件异常访问次数的统计,摘除故障文件数据比重,外部发现基于 SmartCtl 规则反馈,将以上的问题反馈到系统中,就可以精确的知道哪块磁盘有哪些问题。

image.png

修复磁盘故障摘除,此处针对的是全量数据的磁盘故障摘除,使用全盘数据扫描恢复的目的主要有两点,一是用来解决全量文件,因磁盘故障/文件损坏等原因带来的副本不足的情况,二是文件/目录/磁盘摘除,触发后台主动副本修复。全盘数据扫描修复,从 Range 的开始,三个节点都读数据,如果数据存在冲突,就使用另外两个节点去解决数据冲突,最后把数据恢复。每个节点都会附一个 range,range 的主要作用就是从三个节点上把数据取过来进行比对,然后把解决冲突的数据恢复掉,另外一种方式使用 KeyScan+Read-All,使用 KeyScan 拿到的是一些 key,对于大量的插入,像云盘用户是大量的插入比较多,删除的操作很少,这种场景下数据存储使用的是 key-value 的数据格式存储,这种情况下,如果节点上丢掉了哪些数据,可以直接使用 key 来修复这些丢掉的数据。通过这两种方式可以解决文件丢失或者损坏的问题。

image.png

解决了全量数据,接着解决增量数据的检查修复问题。增量数据检查修复主要存在以下三个问题:

❶ 如何保证新写入的数据副本是足够的 ( 拒绝/超时 )

❷ 如何弥补 Hinted handoff 的缺点 ( 时间窗口 )

❸ Quorum 写存在 W

针对以上问题,Hinted handoff 对于 i/o 负载或者 i/o 假死没有考虑到,这种情况下,Hintedhandoff 没有去把出问题的东西记下来,时间窗口存在的问题是如果超时了,丢失的数据可能就记录不下来,所以需要把这两种情况记录下来,以便更好的解决增量数据存在的问题。其原理是:如果提供两种方式,第一种如果 proxycheck 把 value 记录进来以后,数据有问题,可以直接使用另外的副本进行数据修复,还有一种如果不记录的话,可以使用 all 级别读修复来对数据进行恢复。使用 Proxy 节点负责记录副本不全的 row,超时拒绝导致的三个副本可能只写成功了两个,这种情况也需要记录下来,这种情况下,实时的去做数据的恢复或者副本的补全,使用 proxycheck 表来存储辅助的 Keyspace,把所有检测到的副本不足的数据都记录到这张表中,Proxy 节点还用于记录数据的修复,把数据存储,proxycheck 用了两副本,这样做会加大系统的开销,但是数据的可靠性得到保证。

image.png

数据的恢复,涉及到存储,同时,还需要用到数据的备份。当时没有所谓的多 DC 方式,都是自研的备份系统,当时 Cassandra 的集群数量有88个,如果采用 Cassandra+Cassandra 的主备模式,那将又是88个集群,这是对运维和成本的巨大挑战;因此我们选择了在极大规模场景下扩展更好的 HBase 作为备份存储,使用 Cassandra ( 主 ) +HBase ( 备 ) 方案,这样全球88个集群数据备份集中至四大备份中心。大量的数据备份,经常使用的方式就是消息队列,数据的汇聚会增加运维的成本以及数据的落地然后再去做,这样操作的话,延时会比较高。所以在 Cassandra 中做了一个机制,每个节点负责自己的 range 管理,可以记录到自己的缓存表中,从缓冲表取出来备份到数据中心,使用 Thirft 接口,HBase 跟 Cassandra 的接口完全是兼容的,这样设计 HBase 备份中心就相当于一个 Cassandra 的数据中心了,如果数据大量丢失,或者数据出现大量的错误问题,可以直接无缝切换到 HBase 上提供服务,然后再使用 HBase 备份的数据慢慢恢复丢失的数据,用户完全不会感觉到服务异常,提高了用户的体验。

image.png

前面介绍的是数据方面的问题,下面介绍下如何提高磁盘的利用率也就是降低成本。主要是利用虚拟目录来提高磁盘的利用率,磁盘的利用率提高主要问题存在两点:

❶ 节点数量大,SSTable 文件多,磁盘空间导致无法做 major 消重;

❷ SSTable 文件数多,Scan 操作导致 CPU 消耗严重。

对于这两个问题,当时磁盘的利用率达到50%就无法再提高利用率,继而我们采用了分而治之的思想,把一个大 range 使用 Daily—Compact 完成数据 SubRange,切分为几个小 range,每个 range 代表一个目录,由于切分以后,数据量变小,每个 range 都可以做自己的 major,可以把重复的部分都清除掉 ( 但是如果在磁盘利用率90%以上,做一次 major 就很消耗 CPU 性能 ) ,这样做以后,对于 Scan 请求定位 SSTable 打开的文件会更少,效率就会更高,速度也会更快。

image.png

避免写放大的问题。对于如何减少写放大问题,主要存在以下两个问题:

❶ 原有的 Compaction 机制 ( SizeTiered/Leveled ) 较难避免数据重复参与 Compaction 的问题;

❷ 尤其在 SizeTiered 按文件大小分组 Compaction,插入删除频繁的业务难以消重。

针对上述问题,我们采用给 SSTable 增加 level 概念。正常的是给每层的数据从 level 0 -> level 2,到 level 2 后,compaction 就不会参加,也就说最多做两次。360对于这一块做了如下的改进:让每层的 compaction 结合虚拟目录,在 level 0 做 compaction 的时候,分成各种各样的虚拟目录进行 subrange,subrange 里边再去做 compaction,这样的话,就相当于虚拟目录没有重复的数据出现,控制文件参与 compaction 的次数,通过这两种方式,使磁盘的利用率达到了90%左右。

image.png

成本压力。基于成本的考量,使用了 EC 的方式,让3副本变成了1.4个副本,在较少副本数量的同时保证数据的可靠性,同时从数据可用性上考虑的话,数据可用性基本保持以下两点就可以:

❶ 副本方式,也就是连续3节点磁盘故障,数据必丢失;

❷ 条带方式,相邻的14节点故障任意4个数据仍可修复。

对于这个内容,EC 是把原有的数据进行块切分,算出校验块,然后校验块打散到整个集群中,如果丢失了几个块,可以用其他的10个块进行修复,再把分散的块 key 存储到 cfindex 的表中。对于前边的条带方式,主要使用切分 value,value 采用的是 512k 切成等份的4等份,可以得到4个校验块,需要全部打散到不同的数据块上,比如下图中的 k13,k14 不能放到一台机器上,这样才有意义,一旦数据丢失了,还可以方便恢复,如果四个块在一台机器上,坏了两台机器就没法恢复了,key 的数据有两部分,一个是元数据,一个是条带数据。元数据还是保持多副本的形式,但是算出来的条带数据实际上是按环分布,分成单副本的方式去存储,这样其实就可以达到三副本到1.4副本,编码可以在线调整,还可以使用指令集加速,通过指令集对 EC 进行加速,这块比较难的问题是如何把 Key 值分散在整个环上,而且还在不同的机器上,如果使用 Md5 算出来 value 值当作 key 值,还是有可能 Key 值存储在一台机器上,所以还是采用了 OrderPreservingPartitioner 保序的方式去存储。

image.png

接着做了一个 Keyspace 级别的 Patition 策略,以前的 key 存在以下问题:

❶ RandomPartition 可以解决大部分 Key 随机分布的问题;
❷ key 存储有序问题,OrderPreservingPartitioner;
❸ 是条带数据散布的需要,Keyspace 级别的 Partitioner 设定。
前面说 key 存储用到了 OrderPreservingPartitioner,这样在一套系统里需要两套不同的 partition 机制,如果进行数据交互,就需要既要保持 RandomPartition 的结构,还要保持 OrderPreservingPartitioner 的结构。这样的话,数据交换会变的异常的复杂,所以做了一个消息传递,过程中还是使用 LongToken 去存,在使用时,还是需要维护两套,当撤出或者加入环中时,都要进行转化,所以系统会看到两套内容。

image.png

其他的改进点如下:

❶ Hinted handoff :外部工具,解决宕机时间过长,超过 Hinted 时间窗口;

❷ MemTable Flush 选盘策略:避免并发 dump MemTable 带来的 CPU 开销,避免小文件的产生;

❸ Cassandra 集群管控,配置自动加载,磁盘自动下线报修。

]]>
开发者说:如何使用插件降低上传文件部署服务的复杂度-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 lADPDgQ9rZ20n0PNAh3NBLc_1207_541.jpg_620x10000q90g.jpg

“ 这里描述我们实际服务部署的时候频繁发生的两个常用场景。

第一个场景,我们“办公网环境”想要在“准生产环境”下部署,需要做如下工作:
打包、将文件上传到堡垒机上、scp将上传好的包裹传输到“准生产环境”的目标机器、ssh 目标机器、restart重启服务。

第二个场景是,我们可能随时的给“准生产环境”临时上传一些文件,但是仍然需要大量频繁操作。

直到前段时间我看了朋友圈一篇“阿里程序员推荐的15 款常用开发者工具”里面提到 Cloud Toolkit 这个工具,试过后觉得它太好用了,完美降低服务部署的复杂度。

只需要安装相应的插件,配置好所需环境,点击下绿色的箭头,即可帮你打包部署运行到相应环境。

所以迫不及待的写了这篇文章,希望能对你有所帮助
(第一次写文,请多关照(^_^))”

安装Cloud Toolkit插件

lADPDgQ9rZ20n0XNAsTNBHw_1148_708.jpg_620x10000q90g.jpg

安装成功后,会在这两个地方,看到安装的插件

lADPDgQ9rZ20n0fNAvTNBNo_1242_756.jpg_620x10000q90g.jpg

这是一个 Spring Cloud 项目,用的是 jar 包部署。

那么插件的准备工作就已经做好了,笔者将用其中的 auth 项目来演示下一键部署的威力

一键服务部署

添加堡垒机和目标机器

目标是使这样的服务环境可以一键部署

lADPDgQ9rZ20n0vNAVzNAiQ_548_348.jpg_620x10000q90g.jpg

接下来就是在idea里面操作

lADPDgQ9rZ20n087zQGA_384_59.jpg_620x10000q90g.jpg

在选择 Alibaba Cloud Toolkit -> host 单击 add host ,弹出以下界面,我们先来配置堡垒机相关的信息

lALPDgQ9rZ20n1LNAfvNAuQ_740_507.png_620x10000q90g.jpg

记得测试下连接,点击 add ,然后配置目标机器的信息
lALPDgQ9rZ20n1TNAfvNAuQ_740_507.png_620x10000q90g.jpg

记得选择第二个选项卡 ,配置下堡垒机

lALPDgQ9rZ20n1XNAfvNAuQ_740_507.png_620x10000q90g.jpg

然后发现他是通的,就说明这个目标机器配置成功了

不过在部署前,我想看下现在 auth 配置情况
lADPDgQ9rZ20n5DNATnNBR4_1310_313.jpg_620x10000q90g.jpg

单击这个红框框,你会发现居然直接就登陆目标机器啦
lALPDgQ9rZ20n2nNAW_NA4I_898_367.png_620x10000q90g.jpg

现在开始准备配置部署策略啦,只要如图操作就好

lADPDgQ9rZ20n2rMkM0CQg_578_144.jpg_620x10000q90g.jpg
lALPDgQ9rZ20n2vNAoTNAjo_570_644.png_620x10000q90g.jpg

很显然我的 auth 需要部署到 222 机器上,然后他是一个 maven 项目,我希望它打包之后部署到目标机器的 /usr/local/oomp 下。

Spring Cloud 部署需要两个 maven goal

lADPDgQ9rZ20n27NAWbNAio_554_358.jpg_620x10000q90g.jpg
lADPDgQ9rZ20n3DMlM0C9A_756_148.jpg_620x10000q90g.jpg
lADPDgQ9rZ20n3PMlM0C9A_756_148.jpg_620x10000q90g.jpg

我需要过滤掉测试过程,这个具体看实际需要

lALPDgQ9rZ20n3TNAjjNAjo_570_568.png_620x10000q90g.jpg

切换到 Advanced ,这里面可以配置打包上传后,在目标机器将做什么样的操作,例如 我需要重启(你可以先给文件改名)然后看下日志

我勾选了 automatic open after deploy ,点击 apply->run 之后你什么都不用做了,大功告成!!!

欣赏下两个图片

lADPDgQ9rZ20n3bNASfNAxQ_788_295.jpg_620x10000q90g.jpg

lADPDgQ9rZ20n3fNAR3NBQQ_1284_285.jpg_620x10000q90g.jpg

这都是 Cloud Toolkit 做的,以后要是部署这台机器的 auth 服务 ,只需点击绿色箭头。

lADPDgQ9rZ20n49FzOo_234_69.jpg_620x10000q90g.jpg

大功告成,就是图片多了点,其实超简单的!!!

文件上传,命令界面

lALPDgQ9rZ20n5HNAeTNAuQ_740_484.png_620x10000q90g.jpg
lADPDgQ9rZ20n5LNARTNAlY_598_276.jpg_620x10000q90g.jpg

文件上传也是超简单的,可以直接点击 upload ,选择需要上传的路径,指定上传位置即可。

同样 ,它用作命令交互也是超级便利的,单击 terminal

最后 ,感谢阿里巴巴中间件,搞了一个这么好用的插件。

官方链接地址,点击这里

]]>
用Python开发你的第一个小游戏 | Python从入门到精通:入门篇之二十三-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 第二十二节:游戏的运行逻辑分析
一、游戏角色建立
1、显示欢迎信息

print('*'*20,"欢迎来到游戏!",'*'*20)

2、显示身份的选择信息

print("请选择你的角色:")
print("t1.唐僧")
print("t2.白骨精")
#游戏的身份选择
User_Role=input("请输入1~2:")

为了显示出来的内容整齐,在身份“唐僧”和“白骨精”前面加了一个制表符“t”,显示一个格式的缩进。
用input()函数获取用户的输入,并存放在变量User_Role中。
3、根据用户的选择显示不同的提示信息

if User_Role == '1' :
    #用户选择1
    pass
elif User_Role == '2' :
    #用户选择2
    pass
else :
    #用户输入其他
    pass

获取到用户的输入内容之后,要根据不同的输入内容显示不同的提示信息,就要对获取到的内容分情况讨论。在本游戏中,用户可能输入1、2、其他内容,所以就分了三种情况讨论。
接下来分析当用户选择1的时候:

print("你将以->唐僧<-的身份进行游戏")

看一下执行结果:
image.png
为了区分身份和上述的欢迎信息,打印一条分隔符:

print('-'*55)

再设置当用户输入2的时候执行的语句,理论上来说应该是选择白骨精的身份,但是本系统中白骨精是反派boss,所以还是匹配唐僧的身份。
当用户输入其他的内容的时候,提示信息:输入错误,并为用户分配唐僧的游戏身份。
程序如下:

#打印一行分割符
print('-'*55)
if User_Role == '1' :
    #选择1
    print("你已经选择了1,你将以->唐僧<-的身份进行游戏")
elif User_Role == '2' :
    #选择2
    print("你居然选择白骨精,太不要脸了,系统将为你匹配->唐僧<-的身份进行游戏")
else :
    # 输入其他
    print("你的输入错误!系统将为您自动分配身份,你将以->唐僧<-的身份进行游戏")

这就完成了游戏的第一步:角色的建立,接下来就是设置进入游戏之后的页面和功能操作了。
二、游戏的进行
1、显示玩家的基本信息(攻击力 生命值)
创建变量保存玩家的攻击力和生命值:

User_life = 2 #生命值
User_attack = 2 # 攻击力

创建变量保存Boss的信息:

Boss_life = 10 #boss的生命值
Boss_attack = 10 #boss 的攻击力

将玩家的信息在创建角色的时候显示给用户,但是Boss的就不必了,中间打印一行分割符:

#打印一行分割符
print('-'*55)
#显示玩家信息
print(f'你的生命值为 {User_life},你的攻击力为 {User_attack}')

此时执行一下程序,可以看到:
image.png
2、玩家的操作项(练级、打boss、逃跑)
首先打印玩家可进行的操作,并获取玩家的选择。

print("请选择你要进行的操作")
print("t1.练级")
print("t2.打boss")
print("t3.逃跑")
User_option=input("请选择要做的操作,输入1~3:")

执行结果为:
image.png
此时若是这么处理用户的选择的话,就会导致用户只可以做一次选择,只可以进行一次操作,练级也只能是加一次,显然这是不友好的,也是不符合常理的。
我们想让用户可以一直进行操作的选择,那么就要将操作的语句以及对操作的处理放到循环体里面。
这个时候又产生一个问题了,循环几次结束程序呢?
这里答案是,等待用户结束,所以要写成一个死循环,但是可以用break来结束整个循环。
所以将接下来的操作放入一个循环体里面:

while True :

现在就可以来处理用户的选择了,当用户选择1的时候,进行练级,可设置每次练级提升的攻击力和生命值,假设为2,程序如下:

    if User_option == '1' :
        #练级,增加玩家的生命值和攻击力
        User_life += 2
        User_attack += 2
        print(f'恭喜你升级了! 你的生命值为:{User_life},你的攻击力为:{User_attack}')

执行结果为:
image.png
这就完成了想要的循环结果,以及持续的练级。
接下来设置当用户选择打boss的时候的处理方式:
玩家攻击boss,此时要根据玩家的攻击力来判断boss是否还有生命值,如果有则反击,如果没有则玩家胜利,游戏结束。

    elif User_option == '2' :
        #玩家攻击boss,boss掉血,boss掉的生命值为玩家的攻击力
        Boss_life -= User_attack

        # 打印一行分割符
        print('-' * 55)
        print("->唐僧-<攻击了->白骨精<-")
        #判断白骨精是否还有生命值
        if Boss_life <= 0 :
            #玩家攻击力过高,白骨精死亡
            print(f"白骨精受到了你{User_attack}点的攻击力,重伤不治,死亡,->玩家胜利!<-")
            #游戏结束
            break
        else :
            #boss 反击
            User_life -= Boss_attack
            print("->白骨精-<攻击了->唐僧<-")
            #判断玩家是否还有生命值
            if User_life <= 0:
                # 白骨精攻击力过高,唐僧死亡
                print(f"你受到了白骨精{Boss_attack}点的攻击力,重伤死亡->Game Over!<-")
                # 游戏结束
                break

执行结果为:
image.png
如果持续练级,将攻击力提升到大于白骨精的生命值,将会是什么结果呢,再来看下:
image.png
此时就是胜利啦!
接下来处理第三种情况,唐僧退出游戏:
退出游戏那就是结束循环啦,用break即可。

    elif User_option == '3' :
        # 打印一行分割符
        print('-' * 55)
        #逃跑
        print("唐僧一见到白骨精,撒腿就跑!!!游戏结束。。")
        break

执行结果为:
image.png
还要考虑到用户输入错误的情况,再添加一个else语句:

 else :
        # 打印一行分割符
        print('-' * 55)
        print("输入有误!请重新输入!")

执行结果为:
image.png
这就完成了游戏的所有设置啦,同学们可以反复练习几遍,也可以根据自己的想法将游戏设置的更复杂一点。
到这里,入门教程就完全结束啦,同学们有哪些收获呢?
进阶教程即将开启~欢迎同学们的加入!

视频学习:阿里云大学之Python进阶必看

配套Python进阶文章点击此处获取

]]>
【升级】12月31日消息队列MQ升级通知 Mon, 27 Jan 2020 20:58:34 +0800

【阿里云】【消息队列MQ】【升级通知】

升级窗口:北京时间2019年12月31日 01:00 - 09:00
升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

升级影响:

升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

]]>
【升级】1月消息队列MQ升级计划通知 Mon, 27 Jan 2020 20:58:34 +0800

【阿里云】【消息队列MQ】【升级通知】

升级窗口:

北京时间2020年1月2日 01:00 - 09:00

北京时间2020年1月7日 01:00 - 09:00

北京时间2020年1月9日 01:00 - 09:00

北京时间2020年1月14日 01:00 - 09:00

北京时间2020年1月16日 01:00 - 09:00
升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

升级影响:

升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

]]>
【升级】1月至4月边缘节点服务ENS网络方案升级通知 Mon, 27 Jan 2020 20:58:34 +0800

【阿里云】【边缘节点】【升级通知】

升级窗口:

2020年1月-4月,边缘节点分批次进行升级。

升级内容:

全部ENS节点的网络方案升级。

升级前,ENS实例网卡采用bond方式,网卡驱动是ixgbevf;升级后,ENS实例提供eth0和eth1两个网卡,不再采用bond方式,网卡驱动变为virtio_net。

升级影响:

升级割接一般选择在凌晨或其他业务低峰时段进行,单节点升级时间控制在1小时以内。节点升级前48小时,系统将通过云监控、钉钉群等途径进行节点升级通知。

升级操作需要升级宿主机的网卡驱动、并重启宿主机,会导致客户节点内实例若干分钟的网络中断。极端情况宿主机重启失败,会导致该宿主机下的ENS实例数据在其他宿主机重建,本地盘存储数据丢失。如果您对数据可靠性要求高,请在应用层做数据冗余。

给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

]]>
【升级】12月31日至1月7日CDN、DCDN、SCDN系统升级通知 Mon, 27 Jan 2020 20:58:34 +0800

【阿里云】【CDN、DCDN、SCDN】【升级通知】

升级窗口:北京时间 2019年12月31日 00:00:00 - 2020年1月7日 00:00:00

升级内容:取消CDN、DCDN、SCDN这三款产品的CNAME地址返回延迟时间

升级影响:升级期间,CDN、DCDN、SCDN这三款产品在添加域名操作环节的CNAME地址返回延迟时间将从当前的1~2分钟变更为即刻返回,对用户的实际业务无影响,加速域名的DNS解析可切CNAME地址的时间点仍然以域名状态显示“正常运行”为准。升级期间将会逐步从0%灰度到100%。

]]>
【升级】1月7日爬虫风险管理日志超出免费额度容量不再写入通知 Mon, 27 Jan 2020 20:58:34 +0800

【阿里云】【爬虫风险管理】【重要变更通知】

变更时间:北京时间 2020年1月7日 00:00

变更内容:产品日志服务超出免费额度容量(0.5T)不再持续写入,提供付费升级和清空操作

变更影响:尊敬的爬虫风险管理用户,产品的日志服务预计于2019年1月7日对超出免费额度容量(0.5T)时不再写入,您可以付费升级容量额度,或者进行日志容量清空操作,来保证产品日志的继续写入和实时分析。给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187反馈。

]]>
Windows实例搭建FTP站点 Mon, 27 Jan 2020 20:58:34 +0800 Windows实例搭建FTP站点

]]>
容器服务 kubernetes 系统组件介绍-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 作者:joezxh
4.jpeg
镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

1.前言

 容器服务建立 kubernetes 集群后,系统默认建立一批 kubernetes 集群的系统组件与进程,理解他们的部署配置方式和含义,是优化集群,排除 k8s 集群故障的技术基础之一。

2.系统组件介绍:

2.1 Master 组件

2.1.1 kube-apiserver

 基于generic server 上封装的一层官方默认的 apiserver:

  • 提供了集群管理的REST API接口(包括认证授权、数据校验以及集群状态变更);
  • 提供其他模块之间的数据交互和通信的枢纽  (其他模块通过API Server查询或修改数据,只有API Server才直接操作etcd);
  • 资源配额控制的入口,完备的集群安全机制,对相关集群对象增删查改等操作。

部署方式
 kube-apiserver 以 Static pod 静态POD 方式部署,其配置的 yaml 在 master 机器的/etc/kubernetes/manifests/kube-apiserver.yaml,修改该设置,保存后 k8s 集群会自动重启部署 apiserver pod 到Master 机器上。

# ssh 登录 master 机器
cd /etc/kubernetes/manifests
# 查看 yaml 文件
vi /etc/kubernetes/manifests/kube-apiserver.yaml

服务暴露方式
 通过 SLB 负载均衡暴露服务,slb 后端服务器为 apiserver 所在的3台 Master 机器,侦听 pod 提供的6443 https 服务端口。

2.1.2 ETCD:

 用来保存 k8s 集群所有对象的状态信息和网络信息。
部署方式
 Master 机器上启动进程, etcd 在阿里云容器服务中以系统 Service 方式部署。
服务暴露
 2379端口

2.1.3 kube-scheduler:

 kubernetes 调度器,调度 pod 到 ECS 的部署。
部署方式
 静态 POD方式部署,其配置的 yaml 在 master 机器的/etc/kubernetes/manifests/kube- scheduler.yaml,修改该设置,保存后 k8s 集群会自动重启部署。

# ssh 登录 master 机器
cd /etc/kubernetes/manifests
# 查看 yaml 文件
vi /etc/kubernetes/manifests/kube-scheduler.yaml

服务暴露方式
http 10251 端口提供服务

2.1.4 kube-controller-manager:

 k8s 资源对象管理控制器,包括 默认启动的Node Controller, Daemon Controller, Deployment Controller 以及阿里云扩展的相关 Controller 控制器 等;
部署方式
 静态 POD方式部署,其配置的 yaml 在 master 机器的/etc/kubernetes/manifests/kube-controller-manager.yaml,修改该设置,保存后 k8s 集群会自动重启部署。

# ssh 登录 master 机器
cd /etc/kubernetes/manifests
# 查看 yaml 文件
vi /etc/kubernetes/manifests/kube-controller-manager.yaml

服务暴露方式
 http 10252 端口提供服务

2.1.5 cloud-controller-manager:

 云资源管理控制器,实现 Cloud provider,用以云资源的管理。
部署方式
 Daemonset 守护进程方式部署,部署在 Master 机器上,使用 。

# 查看部署文件
kubectl get daemonset cloud-controller-manager -o=yaml -n kube-system
# 查看 pod
kubectl get pods -n kube-system|grep cloud-controller-manager

服务暴露方式
http 10252 端口提供服务

2.2 Node 组件

2.2.1 kubelet:

 kubelet 服务进程,每个 node 上运行该节点,向 Master 注册节点信息。
部署方式
 节点上运行该服务进程
暴露服务
 包括 10250 端口的认证 API、4194 端口的 cAdvisor API、10255 端口的只读 API 以及 10248 端口的健康检查 API

2.2.2 kube-proxy:

 网络通信组件
部署方式
Daemonset 守护进程方式部署,部署在 Master,Node 机器上都有使用 。

# 查看部署文件
kubectl get daemonset kube-proxy-master -o=yaml -n kube-system
# 查看 pod
kubectl get pods -n kube-system|grep kube-proxy-master

2.3 附加组件

名称 部署形式 提供的服务 备注
kube-DNS(Core-DNS) Deployment 端口:53 域名解析服务 建立扩容至多个 POD 副本
nginx ingress controller/default-http-backend Deployment Nginx http 七层协议路由与 http 后台服务 端口:80,443 对于 http 服务访问量高的必须扩容或者独立另外部署
heapster & influxdb Deployment 80-->8082 pod 云监控组件:
influxdb为存储监控数据的时序数据库;
heapster 为容器集群监控和性能分析工具,HPA、Dashborad、Kubectl top都依赖于heapster收集的数据。
坑:注意 influxdb 的内存做限制,防止其内存无限增长。
kube-flannel Daemonset 网络设施进程
logtail Daemonset 日志采集守护进程
flexvolume Daemonset volumen 磁盘管理进程
tiller-deploy Deployment port:44134 helm工具的服务端
metrics-server Deployment 443 功能通 Heapster,采集容器监控与性能数据。
alibaba-log-controller Deployment cloud-controller-manager 扩展 日志管理控制器
alicloud-application-controller Deployment cloud-controller-manager 扩展 应用管理控制器
alicloud-disk-controller Deployment cloud-controller-manager 扩展 磁盘存储控制器
alicloud-monitor-controller Deployment cloud-controller-manager 扩展 云监控
aliyun-acr-credential-helper Deployment cloud-controller-manager 扩展 认证

阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

]]>
2019 年 Java 调查报告:Java 8 仍然最受欢迎-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 尽管 Java 被称为编程语言的“老马”,但它仍在不断发展。

在 Tiobe 排行榜中,它仍然是的第一名,在 PYPL 和 RedMonk 排行榜上则在与 Javascript 和 Python 争夺第二名的位置,按照 GitHub 上库贡献者的数据来看,它是第三大最受欢迎的语言(根据 GitHub 的 10 年数据)。

时光在变,Java 一直没变,过去企业在谈 Java,现在还是在谈 Java,这一点可以在财富 500 强企业的技术栈中得到验证。

为什么 Java 能够一直流行呢?其中的原因有很多,例如企业最看重的是向后兼容性,众所周知,向后兼容性对重大的更新和升级非常敏感,而 20 年前用 Java 5 编写的系统还是能够在 Java 8 下编译和运行。

那么,2019 年 Java 的发展如何呢?Baeldung 调查了 6707 名开发经验丰富的技术人员,并从中获得了一些结论。

使用版本:Java 8 仍然最受欢迎

从 Java 8 之后,Java 的发布周期明显快了很多,现在已经快要到 Java 14 了。你可能会认为大多数人已经切换了更新版本的 Java 了,但实际情况并非如此,根据调查显示,80% 的受访者仍然在使用 Java 8。

image.png

为什么即使有了新版本,Java 8 仍然最受欢迎呢?这其中有很多原因:

首先,Java 8 开创性的语言特性(参见我的闭包和Lambdas 书评)仍然在被编程社区吸收。即使到了2019 年,关于Steam 和Optional 的相关问题仍在网上被热烈讨论,Baeldung 自己也发布了 Java 8 Optional 指南。

其次,Java 9 首先支持的新模块系统使人们感到困惑,而不是使他们感觉更轻松。

第三,在后来的 Java 版本中引入的特性似乎没有足够的说服力让大家下决心切换。

最后,还存在一个大问题,在 2019 年 1 月份之后,Java SE 8 的公共更新需要商业许可。这就是 Oracle JDK 与 OpenJDK 之争的开始,正如我们在之前的文章中所说的:

随着 Oracle JDK 发行和支持的变化,是使用 Oracle JDK,还是 Oracle 的 OpenJDK,或者是其他供应商的 OpenJDK,这中间在权限上存在着相当大的不确定性。此外,在不同的供应商那里是否可以得到免费更新的相关计划,以及 (新的和现有的) 付费的支持模型,这些都要予以考虑。

框架:Spring 占据主导地位

在框架的采用方面,Spring 占据了主导地位。与传统且臃肿的 Java EE 相比,Spring 是现代化的、基于 Java 的企业应用程序的轻量级框架。Spring Boot 的采用率也很高。

构建工具:Maven 断层式第一

在 Java 构建工具的调查中,Maven 以相当大的优势排在了第一位,这表明排在第二位的 Gradle 仍然是 Android 的代名词,之后需要做更多的努力来摆脱这些限制,以便被认为是 Java 构建通用实践的一个可行的选择。

IDE:IntelliJ 大比例占据一位,Eclipse 份额持续下降

在 IDE 的调查中,IntelliJ 以将近 60% 的份额占据了第一的位置,为什么 IntelliJ 如此受欢迎呢?Andrey Cheptsov 曾在一篇博客中这样写道:“在你编写代码时,IntelliJ IDEA 也忙着在构建它的语法树,在类、变量、字段、方法和它们的用法之间创建引用,分析执行流,利用这些信息,它可以提供补全功能,帮助你快速浏览代码,提供错误分析和方便的快速修复。”

而传统的 Eclipse 则有点不妙,其占比从去年的 38% 下降到 32.8%

Web/ 应用服务器:Tomcat 占据第一

早在 2011 年,Forrester 的首席分析师 Mike Gualtieri 副总裁就写过一篇既有预见性又不受时间限制的文章,名为《停止在 WebLogic、WebSphere 和 JBoss 应用服务器上浪费金钱》

他开头写道:

“使用 Apache Tomcat 吧,它是免费的。

我不明白为什么有些公司要在 Oracle Weblogic 或 IBM WebSphere Application Server 这样的 Java 应用服务器上花费数百万美元。我明白为什么有些公司要在 Red Hat JBoss 上花钱,因为他们想要节省在应用服务器上的开销。但是,为什么要花钱呢?Apache Tomcat 将满足大多数 Java web 应用程序的部署需求。”

image.png

该表反映了这种说法:Tomcat 一直保持着轻量级的资源消耗,毫无争议地击败 Jetty 夺得冠军。那些重量级产品的位次反映了它们那个年代已经过时的需求。

其他 JVM 语言:Java 的使用率未发生变化

首先,让人感到有些惊讶和有趣的是,有 62.6% 的开发人员只使用 Java,这与去年不相上下 (62.8%)。考虑到大家对 Kotlin 的大量采用,我的预期是这个数字肯定会下降,但现在看来并没有下降。不过,Kotlin 仍然从去年的 13% 增长到了现在的 16.5%。和 Gradle 一样,Kotlin 完全可以作为一种通用语言来用,尤其是在后端环境中,但在安卓之外它仍然未被接受得到普遍应用。

数据库:关系型数据库比 NoSQL 更受欢迎

MySQL 和 PostgreSQL 是前两名,Oracle 是第三名,MongoDB 和 MS SQL 是第四和第五名。这里有两个值得注意的趋势。与 Percona 的数据库管理系统流行度调查结果一致,关系型数据库管理系统胜过 NoSQL,而开源数据库管理系统则比大型商业数据库管理系统做得更好。就像前文中的 Web 服务器一样,人们寻求的也是更轻量级的等价物,尤其是 PostgreSQL。

总结

总而言之,根据调查结果显示,Java 不会被取代,在未来几年也将在继续保持 Top 3 的位置。不妥,尽管人们仍然坚持使用该语言及其围绕它的生态系统,但他们也在试图远离 Oracle 及其产品,如 IDE(JDeveloper)、服务器 (WebLogic)、JDK 及其旗舰数据库。MySQL 是个特例,因为它基本上不受甲骨文所有权的影响。大多数 Java 用户正在寻找更轻量级、更高效、更便宜、对开发人员和许可更友好的等价物,这些等价物完全比得上 Oracle 的同类产品,甚至更好。

本文转自infoQ

原文链接:https://www.infoq.cn/article/aiKteLpiwAPlyKL5Ef6p

]]>
JVM 垃圾回收揭秘附常见面试题解析-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 JVM 垃圾回收常见面试题

问题答案在文中都有提到

如何判断对象是否死亡(两种方法)。

简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。

如何判断一个常量是废弃常量

如何判断一个类是无用的类

垃圾收集有哪些算法,各自的特点?

HotSpot 为什么要分为新生代和老年代?

常见的垃圾回收器有那些?

介绍一下 CMS,G1 收集器。

Minor Gc 和 Full GC 有什么不同呢?

本文导火索

image.png

当需要排查各种内存溢出问题、当垃圾收集成为系统达到更高并发的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。

1 揭开 JVM 内存分配与回收的神秘面纱

Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java 自动内存管理最核心的功能是 堆 内存中对象的分配与回收。

Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

堆空间的基本结构:

image.png

上图所示的 eden 区、s0("From") 区、s1("To") 区都属于新生代,tentired 区属于老年代。大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s1("To"),并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。经过这次GC后,Eden区和"From"区已经被清空。这个时候,"From"和"To"会交换他们的角色,也就是新的"To"就是上次GC前的“From”,新的"From"就是上次GC前的"To"。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,"To"区被填满之后,会将所有对象移动到年老代中。

image.png

1.1 对象优先在 eden 区分配

目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

大多数情况下,对象在新生代中 eden 区分配。当 eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC.下面我们来进行实际测试以下。

在测试之前我们先来看看 Minor GC 和 Full GC 有什么不同呢?

新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。

老年代 GC(Major GC/Full GC):指发生在老年代的 GC,出现了 Major GC 经常会伴随至少一次的 Minor GC(并非绝对),Major GC 的速度一般会比 Minor GC 的慢 10 倍以上。

测试:

public class GCTest {

    public static void main(String[] args) {
        byte[] allocation1, allocation2;
        allocation1 = new byte[30900*1024];
        //allocation2 = new byte[900*1024];
    }
}

通过以下方式运行:

image.png

添加的参数:-XX:+PrintGCDetails

image.png
运行结果 (红色字体描述有误,应该是对应于 JDK1.7 的永久代):
image.png
从上图我们可以看出 eden 区内存几乎已经被分配完全(即使程序什么也不做,新生代也会使用 2000 多 k 内存)。假如我们再为 allocation2 分配内存会出现什么情况呢?

allocation2 = new byte[900*1024];

image.png
简单解释一下为什么会出现这种情况: 因为给 allocation2 分配内存的时候 eden 区内存几乎已经被分配完了,我们刚刚讲了当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC.GC 期间虚拟机又发现 allocation1 无法存入 Survivor 空间,所以只好通过 分配担保机制 把新生代的对象提前转移到老年代中去,老年代上的空间足够存放 allocation1,所以不会出现 Full GC。执行 Minor GC 后,后面分配的对象如果能够存在 eden 区的话,还是会在 eden 区分配内存。可以执行如下代码验证:

public class GCTest {

    public static void main(String[] args) {
        byte[] allocation1, allocation2,allocation3,allocation4,allocation5;
        allocation1 = new byte[32000*1024];
        allocation2 = new byte[1000*1024];
        allocation3 = new byte[1000*1024];
        allocation4 = new byte[1000*1024];
        allocation5 = new byte[1000*1024];
    }
}

1.2 大对象直接进入老年代

大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。

为什么要这样呢?

为了避免为大对象分配内存时由于分配担保机制带来的复制而降低效率。

1.3 长期存活的对象将进入老年代

既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。为了做到这一点,虚拟机给每个对象一个对象年龄(Age)计数器。

如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1.对象在 Survivor 中每熬过一次 MinorGC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。

1.4 动态对象年龄判定

为了更好的适应不同程序的内存情况,虚拟机不是永远要求对象年龄必须达到了某个值才能进入老年代,如果 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需达到要求的年龄。

2 对象已经死亡?

堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。

image.png

2.1 引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。

这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。 所谓对象之间的相互引用问题,如下面代码所示:除了对象 objA 和 objB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们。

public class ReferenceCountingGc {
    Object instance = null;
    public static void main(String[] args) {
        ReferenceCountingGc objA = new ReferenceCountingGc();
        ReferenceCountingGc objB = new ReferenceCountingGc();
        objA.instance = objB;
        objB.instance = objA;
        objA = null;
        objB = null;

    }
}

2.2 可达性分析算法

这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
image.png

2.3 再谈引用

无论是通过引用计数法判断对象引用数量,还是通过可达性分析法判断对象的引用链是否可达,判定对象的存活都与“引用”有关。

JDK1.2 之前,Java 中引用的定义很传统:如果 reference 类型的数据存储的数值代表的是另一块内存的起始地址,就称这块内存代表一个引用。

JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)

1.强引用(StrongReference)

以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

2.软引用(SoftReference)

如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA 虚拟机就会把这个软引用加入到与之关联的引用队列中。

3.弱引用(WeakReference)

如果一个对象只具有弱引用,那就类似于可有可无的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。

4.虚引用(PhantomReference)

"虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动。

虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

2.4 不可达的对象并非“非死不可”

即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。

被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。

2.5 如何判断一个常量是废弃常量

运行时常量池主要回收的是废弃的常量。那么,我们如何判断一个常量是废弃常量呢?

假如在常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 "abc" 就是废弃常量,如果这时发生内存回收的话而且有必要的话,"abc" 就会被系统清理出常量池。

注意:我们在 可能是把 Java 内存区域讲的最清楚的一篇文章 也讲了 JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。

2.6 如何判断一个类是无用的类

方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢?

判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面 3 个条件才能算是 “无用的类” :

该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。

加载该类的 ClassLoader 已经被回收。

该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。

3 垃圾收集算法

该算法分为“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:

效率问题

空间问题(标记清除后会产生大量不连续的碎片)

image.png

3.2 复制算法

为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
image.png

3.3 标记-整理算法

根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

3.4 分代收集算法

当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

延伸面试问题: HotSpot 为什么要分为新生代和老年代?
根据上面的对分代收集算法的介绍回答。

4 垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现

虽然我们对各个收集器进行比较,但并非要挑选出一个最好的收集器。因为直到现在为止还没有最好的垃圾收集器出现,更加没有万能的垃圾收集器,我们能做的就是根据具体应用场景选择适合自己的垃圾收集器。试想一下:如果有一种四海之内、任何场景下都适用的完美收集器存在,那么我们的 HotSpot 虚拟机就不会实现那么多不同的垃圾收集器了。

4.1 Serial 收集器

Serial(串行)收集器收集器是最基本、历史最悠久的垃圾收集器了。大家看名字就知道这个收集器是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。

新生代采用复制算法,老年代采用标记-整理算法。

image.png

虚拟机的设计者们当然知道 Stop The World 带来的不良用户体验,所以在后续的垃圾收集器设计中停顿时间在不断缩短(仍然还有停顿,寻找最优秀的垃圾收集器的过程仍然在继续)。

但是 Serial 收集器有没有优于其他垃圾收集器的地方呢?当然有,它简单而高效(与其他收集器的单线程相比)。Serial 收集器由于没有线程交互的开销,自然可以获得很高的单线程收集效率。Serial 收集器对于运行在 Client 模式下的虚拟机来说是个不错的选择。

4.2 ParNew 收集器

ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。

新生代采用复制算法,老年代采用标记-整理算法。

image.png

它是许多运行在 Server 模式下的虚拟机的首要选择,除了 Serial 收集器外,只有它能与 CMS 收集器(真正意义上的并发收集器,后面会介绍到)配合工作。
并行和并发概念补充:

并行(Parallel) :指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。

并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行,可能会交替执行),用户程序在继续运行,而垃圾收集器运行在另一个 CPU 上。

4.3 Parallel Scavenge 收集器

Parallel Scavenge 收集器也是使用复制算法的多线程收集器,它看上去几乎和ParNew都一样。 那么它有什么特别之处呢?

-XX:+UseParallelGC 

    使用 Parallel 收集器+ 老年代串行

-XX:+UseParallelOldGC

    使用 Parallel 收集器+ 老年代并行

Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。 Parallel Scavenge 收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,手工优化存在困难的话可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。

新生代采用复制算法,老年代采用标记-整理算法。
image.png

4.4.Serial Old 收集器

Serial 收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:一种用途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使用,另一种用途是作为 CMS 收集器的后备方案。

4.5 Parallel Old 收集器

Parallel Scavenge 收集器的老年代版本。使用多线程和“标记-整理”算法。在注重吞吐量以及 CPU 资源的场合,都可以优先考虑 Parallel Scavenge 收集器和 Parallel Old 收集器。

4.6 CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。

CMS(Concurrent Mark Sweep)收集器是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

从名字中的Mark Sweep这两个词可以看出,CMS 收集器是一种 “标记-清除”算法实现的,它的运作过程相比于前面几种垃圾收集器来说更加复杂一些。整个过程分为四个步骤:

初始标记: 暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快 ;

并发标记: 同时开启 GC 和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。

重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短

并发清除: 开启用户线程,同时 GC 线程开始对为标记的区域做清扫。

image.png

从它的名字就可以看出它是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面三个明显的缺点:

对 CPU 资源敏感;

无法处理浮动垃圾;

它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。

4.7 G1 收集器

G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征.

被视为 JDK1.7 中 HotSpot 虚拟机的一个重要进化特征。它具备一下特点:

并行与并发:G1 能充分利用 CPU、多核环境下的硬件优势,使用多个 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿时间。部分其他收集器原本需要停顿 Java 线程执行的 GC 动作,G1 收集器仍然可以通过并发的方式让 java 程序继续执行。

分代收集:虽然 G1 可以不需要其他收集器配合就能独立管理整个 GC 堆,但是还是保留了分代的概念。

空间整合:与 CMS 的“标记--清理”算法不同,G1 从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。

可预测的停顿:这是 G1 相对于 CMS 的另一个大优势,降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内。

G1 收集器的运作大致分为以下几个步骤:

初始标记

并发标记

最终标记

筛选回收

G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来)。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 GF 收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)。

作者:SnailClimb
链接:https://juejin.im/post/5df8c1de6fb9a0164423dbb3
来源:掘金

]]>
Java 线程基础必备知识-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 一、线程简介

什么是进程?

简言之,进程可视为一个正在运行的程序。它是系统运行程序的基本单位,因此进程是动态的。进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动。进程是操作系统进行资源分配的基本单位。

什么是线程?

线程是操作系统进行调度的基本单位。线程也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。

进程和线程的区别

一个程序至少有一个进程,一个进程至少有一个线程。
线程比进程划分更细,所以执行开销更小,并发性更高。
进程是一个实体,拥有独立的资源;而同一个进程中的多个线程共享进程的资源。

二、线程基本用法

线程(Thread)基本方法清单:

image.png

创建线程

创建线程有三种方式:

继承 Thread 类

实现 Runnable 接口

实现 Callable 接口

继承 Thread 类

通过继承 Thread 类创建线程的步骤:

定义 Thread 类的子类,并覆写该类的 run 方法。run 方法的方法体就代表了线程要完成的任务,因此把 run 方法称为执行体。

创建 Thread 子类的实例,即创建了线程对象。

调用线程对象的 start 方法来启动该线程。

public class ThreadDemo {

    public static void main(String[] args) {
        // 实例化对象
        MyThread tA = new MyThread("Thread 线程-A");
        MyThread tB = new MyThread("Thread 线程-B");
        // 调用线程主体
        tA.start();
        tB.start();
    }

    static class MyThread extends Thread {

        private int ticket = 5;

        MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                ticket--;
            }
        }

    }

}

实现 Runnable 接口

实现 Runnable 接口优于继承 Thread 类,因为:

Java 不支持多重继承,所有的类都只允许继承一个父类,但可以实现多个接口。如果继承了 Thread 类就无法继承其它类,这不利于扩展。

类可能只要求可执行就行,继承整个 Thread 类开销过大。

通过实现 Runnable 接口创建线程的步骤:

定义 Runnable 接口的实现类,并覆写该接口的 run 方法。该 run 方法的方法体同样是该线程的线程执行体。

创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。

调用线程对象的 start 方法来启动该线程。

public class RunnableDemo {

    public static void main(String[] args) {
        // 实例化对象
        Thread tA = new Thread(new MyThread(), "Runnable 线程-A");
        Thread tB = new Thread(new MyThread(), "Runnable 线程-B");
        // 调用线程主体
        tA.start();
        tB.start();
    }

    static class MyThread implements Runnable {

        private int ticket = 5;

        @Override
        public void run() {
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                ticket--;
            }
        }

    }

}

实现 Callable 接口

继承 Thread 类 和 实现 Callable 接口这两种创建线程的方式都没有返回值。所以,线程执行完后,无法得到执行结果。但如果期望得到执行结果该怎么做?

为了解决这个问题,Java 1.5 后,提供了 Callable 接口和 Future 接口,通过它们,可以在线程执行结束后,返回执行结果。

通过实现 Callable 接口创建线程的步骤:

1、创建 Callable 接口的实现类,并实现 call 方法。该 call 方法将作为线程执行体,并且有返回值。

2、创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call 方法的返回值。

3、使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

4、调用 FutureTask 对象的 get 方法来获得线程执行结束后的返回值。

public class CallableDemo {

    public static void main(String[] args) {
        Callable<Long> callable = new MyThread();
        FutureTask<Long> future = new FutureTask<>(callable);
        new Thread(future, "Callable 线程").start();
        try {
            System.out.println("任务耗时:" + (future.get() / 1000000) + "毫秒");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    static class MyThread implements Callable<Long> {

        private int ticket = 10000;

        @Override
        public Long call() {
            long begin = System.nanoTime();
            while (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                ticket--;
            }

            long end = System.nanoTime();
            return (end - begin);
        }

    }

}

FAQ

start 和 run 方法有什么区别

run 方法是线程的执行体。

start 方法会启动线程,然后 JVM 会让这个线程去执行 run 方法。

可以直接调用 Thread 类的 run 方法么

可以。但是如果直接调用 Thread 的 run 方法,它的行为就会和普通的方法一样。

为了在新的线程中执行我们的代码,必须使用 Thread 的 start 方法。

线程休眠

使用 Thread.sleep 方法可以使得当前正在执行的线程进入休眠状态。

使用 Thread.sleep 需要向其传入一个整数值,这个值表示线程将要休眠的毫秒数。

Thread.sleep 方法可能会抛出 InterruptedException,因为异常不能跨线程传播回 main 中,因此必须在本地进行处理。线程中抛出的其它异常也同样需要在本地进行处理。

public class ThreadSleepDemo {

    public static void main(String[] args) {
        new Thread(new MyThread("线程A", 500)).start();
        new Thread(new MyThread("线程B", 1000)).start();
        new Thread(new MyThread("线程C", 1500)).start();
    }

    static class MyThread implements Runnable {

        /** 线程名称 */
        private String name;

        /** 休眠时间 */
        private int time;

        private MyThread(String name, int time) {
            this.name = name;
            this.time = time;
        }

        @Override
        public void run() {
            try {
                // 休眠指定的时间
                Thread.sleep(this.time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.name + "休眠" + this.time + "毫秒。");
        }

    }

}

线程礼让

Thread.yield 方法的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行 。

该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其它线程可以运行。

public class ThreadYieldDemo {

    public static void main(String[] args) {
        MyThread t = new MyThread();
        new Thread(t, "线程A").start();
        new Thread(t, "线程B").start();
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "运行,i = " + i);
                if (i == 2) {
                    System.out.print("线程礼让:");
                    Thread.yield();
                }
            }
        }
    }
}

终止线程

Thread 中的 stop 方法有缺陷,已废弃。

使用 Thread.stop 停止线程会导致它解锁所有已锁定的监视器(由于未经检查的 ThreadDeath 异常会在堆栈中传播,这是自然的结果)。 如果先前由这些监视器保护的任何对象处于不一致状态,则损坏的对象将对其他线程可见,从而可能导致任意行为。Thread.stop 的许多用法应由仅修改某些变量以指示目标线程应停止运行的代码代替。 目标线程应定期检查此变量,如果该变量指示要停止运行,则应按有序方式从其运行方法返回。如果目标线程等待很长时间(例如,在条件变量上),则应使用中断方法来中断等待。

当一个线程运行时,另一个线程可以直接通过 interrupt 方法中断其运行状态。

public class ThreadInterruptDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 实例化Runnable子类对象
        Thread t = new Thread(mt, "线程"); // 实例化Thread对象
        t.start(); // 启动线程
        try {
            Thread.sleep(2000); // 线程休眠2秒
        } catch (InterruptedException e) {
            System.out.println("3、休眠被终止");
        }
        t.interrupt(); // 中断线程执行
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println("1、进入run()方法");
            try {
                Thread.sleep(10000); // 线程休眠10秒
                System.out.println("2、已经完成了休眠");
            } catch (InterruptedException e) {
                System.out.println("3、休眠被终止");
                return; // 返回调用处
            }
            System.out.println("4、run()方法正常结束");
        }
    }
}

如果一个线程的 run 方法执行一个无限循环,并且没有执行 sleep 等会抛出 InterruptedException 的操作,那么调用线程的 interrupt 方法就无法使线程提前结束。

但是调用 interrupt 方法会设置线程的中断标记,此时调用 interrupted 方法会返回 true。因此可以在循环体中使用 interrupted 方法来判断线程是否处于中断状态,从而提前结束线程。

安全地终止线程有两种方法:

定义 volatile 标志位,在 run 方法中使用标志位控制线程终止

使用 interrupt 方法和 Thread.interrupted 方法配合使用来控制线程终止

示例:使用 volatile 标志位控制线程终止

public class ThreadStopDemo2 {

    public static void main(String[] args) throws Exception {
        MyTask task = new MyTask();
        Thread thread = new Thread(task, "MyTask");
        thread.start();
        TimeUnit.MILLISECONDS.sleep(50);
        task.cancel();
    }

    private static class MyTask implements Runnable {

        private volatile boolean flag = true;

        private volatile long count = 0L;

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 线程启动");
            while (flag) {
                System.out.println(count++);
            }
            System.out.println(Thread.currentThread().getName() + " 线程终止");
        }

        /**
         * 通过 volatile 标志位来控制线程终止
         */
        public void cancel() {
            flag = false;
        }

    }

}

示例:使用 interrupt 方法和 Thread.interrupted 方法配合使用来控制线程终止

public class ThreadStopDemo3 {

    public static void main(String[] args) throws Exception {
        MyTask task = new MyTask();
        Thread thread = new Thread(task, "MyTask");
        thread.start();
        TimeUnit.MILLISECONDS.sleep(50);
        thread.interrupt();
    }

    private static class MyTask implements Runnable {

        private volatile long count = 0L;

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 线程启动");
            // 通过 Thread.interrupted 和 interrupt 配合来控制线程终止
            while (!Thread.interrupted()) {
                System.out.println(count++);
            }
            System.out.println(Thread.currentThread().getName() + " 线程终止");
        }
    }
}

守护线程

什么是守护线程?

守护线程(Daemon Thread)是在后台执行并且不会阻止 JVM 终止的线程。当所有非守护线程结束时,程序也就终止,同时会杀死所有守护线程。

与守护线程(Daemon Thread)相反的,叫用户线程(User Thread),也就是非守护线程。

为什么需要守护线程?

守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。典型的应用就是垃圾回收器。
**
如何使用守护线程?**

可以使用 isDaemon 方法判断线程是否为守护线程。

可以使用 setDaemon 方法设置线程为守护线程。

正在运行的用户线程无法设置为守护线程,所以 setDaemon 必须在 thread.start 方法之前设置,否则会抛出 llegalThreadStateException 异常;

一个守护线程创建的子线程依然是守护线程。

不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。

public class ThreadDaemonDemo {

    public static void main(String[] args) {
        Thread t = new Thread(new MyThread(), "线程");
        t.setDaemon(true); // 此线程在后台运行
        System.out.println("线程 t 是否是守护进程:" + t.isDaemon());
        t.start(); // 启动线程
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            while (true) {
                System.out.println(Thread.currentThread().getName() + "在运行。");
            }
        }
    }
}

**
FAQ**

sleep、yield、join 方法有什么区别

yield 方法

yield 方法会 让线程从 Running 状态转入 Runnable 状态。

当调用了 yield 方法后,只有与当前线程相同或更高优先级的Runnable 状态线程才会获得执行的机会。

sleep 方法

sleep 方法会 让线程从 Running 状态转入 Waiting 状态。
sleep 方法需要指定等待的时间,超过等待时间后,JVM 会将线程从 Waiting 状态转入 Runnable 状态。

当调用了 sleep 方法后,无论什么优先级的线程都可以得到执行机会。

sleep 方法不会释放“锁标志”,也就是说如果有 synchronized 同步块,其他线程仍然不能访问共享数据。

join

join 方法会 让线程从 Running 状态转入 Waiting 状态。
当调用了 join 方法后,当前线程必须等待调用 join 方法的线程结束后才能继续执行。

**
为什么 sleep 和 yield 方法是静态的**

Thread 类的 sleep 和 yield 方法将处理 Running 状态的线程。

所以在其他处于非 Running 状态的线程上执行这两个方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。

Java 线程是否按照线程优先级严格执行

即使设置了线程的优先级,也无法保证高优先级的线程一定先执行。

原因在于线程优先级依赖于操作系统的支持,然而,不同的操作系统支持的线程优先级并不相同,不能很好的和 Java 中线程优先级一一对应。

三、线程间通信

当多个线程可以一起工作去解决某个问题时,如果某些部分必须在其它部分之前完成,那么就需要对线程进行协调。

wait/notify/notifyAll

wait - wait 方法使得线程释放其占有的对象锁,让线程从 Running 状态转入 Waiting 状态,并等待 notify / notifyAll 来唤醒 。如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify 或者 notifyAll 来唤醒挂起的线程,造成死锁。
notify - 唤醒一个正在 Waiting 状态的线程,并让它拿到对象锁,具体唤醒哪一个线程由 JVM 控制 。
notifyAll - 唤醒所有正在 Waiting 状态的线程,接下来它们需要竞争对象锁。

注意:

wait、notify、notifyAll 都是 Object 类中的方法,而非 Thread。

wait、notify、notifyAll 只能用在 synchronized 方法或者 synchronized 代码块中使用,否则会在运行时抛出 IllegalMonitorStateException。

为什么 wait、notify、notifyAll 不定义在 Thread 中?为什么 wait、notify、notifyAll 要配合 synchronized 使用?

首先,需要了解几个基本知识点:

每一个 Java 对象都有一个与之对应的 监视器(monitor)
每一个监视器里面都有一个 对象锁 、一个 等待队列、一个 同步队列

了解了以上概念,我们回过头来理解前面两个问题。

为什么这几个方法不定义在 Thread 中?

由于每个对象都拥有对象锁,让当前线程等待某个对象锁,自然应该基于这个对象(Object)来操作,而非使用当前线程(Thread)来操作。因为当前线程可能会等待多个线程的锁,如果基于线程(Thread)来操作,就非常复杂了。

为什么 wait、notify、notifyAll 要配合 synchronized 使用?

如果调用某个对象的 wait 方法,当前线程必须拥有这个对象的对象锁,因此调用 wait 方法必须在 synchronized 方法和 synchronized 代码块中。

生产者、消费者模式是 wait、notify、notifyAll 的一个经典使用案例:

public class ThreadWaitNotifyDemo02 {

    private static final int QUEUE_SIZE = 10;
    private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE);

    public static void main(String[] args) {
        new Producer("生产者A").start();
        new Producer("生产者B").start();
        new Consumer("消费者A").start();
        new Consumer("消费者B").start();
    }

    static class Consumer extends Thread {

        Consumer(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == 0) {
                        try {
                            System.out.println("队列空,等待数据");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.poll(); // 每次移走队首元素
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 从队列取走一个元素,队列当前有:" + queue.size() + "个元素");
                }
            }
        }
    }

    static class Producer extends Thread {

        Producer(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == QUEUE_SIZE) {
                        try {
                            System.out.println("队列满,等待有空余空间");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.offer(1); // 每次插入一个元素
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 向队列取中插入一个元素,队列当前有:" + queue.size() + "个元素");
                }
            }
        }
    }
}

join

在线程操作中,可以使用 join 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。

public class ThreadJoinDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 实例化Runnable子类对象
        Thread t = new Thread(mt, "mythread"); // 实例化Thread对象
        t.start(); // 启动线程
        for (int i = 0; i < 50; i++) {
            if (i > 10) {
                try {
                    t.join(); // 线程强制运行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Main 线程运行 --> " + i);
        }
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println(Thread.currentThread().getName() + " 运行,i = " + i); // 取得当前线程的名字
            }
        }
    }
}

管道

管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。

管道输入/输出流主要包括了如下 4 种具体实现:

PipedOutputStream、PipedInputStream、PipedReader 和 PipedWriter,前两种面向字节,而后两种面向字符。

public class Piped {

    public static void main(String[] args) throws Exception {
        PipedWriter out = new PipedWriter();
        PipedReader in = new PipedReader();
        // 将输出流和输入流进行连接,否则在使用时会抛出IOException
        out.connect(in);
        Thread printThread = new Thread(new Print(in), "PrintThread");
        printThread.start();
        int receive = 0;
        try {
            while ((receive = System.in.read()) != -1) {
                out.write(receive);
            }
        } finally {
            out.close();
        }
    }

    static class Print implements Runnable {

        private PipedReader in;

        Print(PipedReader in) {
            this.in = in;
        }

        public void run() {
            int receive = 0;
            try {
                while ((receive = in.read()) != -1) {
                    System.out.print((char) receive);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

四、线程状态

image.png
java.lang.Thread.State 中定义了 6 种不同的线程状态,在

给定的一个时刻,线程只能处于其中的一个状态。
以下是各状态的说明,以及状态间的联系:

新建(New) - 尚未调用 start 方法的线程处于此状态。此状态意味着:创建的线程尚未启动。

可运行(Runnable) - 已经调用了 start 方法的线程处于此状态。此状态意味着:线程已经在 JVM 中运行。但是在操作系统层面,它可能处于运行状态,也可能等待资源调度(例如处理器资源),资源调度完成就进入运行状态。所以该状态的可运行是指可以被运行,具体有没有运行要看底层操作系统的资源调度。

阻塞(Blocked) - 请求获取 monitor lock 从而进入 synchronized 函数或者代码块,但是其它线程已经占用了该 monitor lock,所以处于阻塞状态。要结束该状态进入 Runnable,从而需要其他线程释放 monitor lock。此状态意味着:线程处于被阻塞状态。

等待(Waiting) - 此状态意味着:线程等待被其他线程显式地唤醒。 阻塞和等待的区别在于,阻塞是被动的,它是在等待获取 monitor lock。而等待是主动的,通过调用 Object.wait 等方法进入。

image.png

定时等待(Timed waiting) - 此状态意味着:无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。

image.png

终止(Terminated) - 线程 run 方法执行结束,或者因异常退出了 run 方法。此状态意味着:线程结束了生命周期。

作者:静默虚空

链接:https://juejin.im/post/5e02b3d6518825127324b032

来源:掘金

]]>
一张图了解Spring Cloud微服务架构-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。Spring Cloud中各个组件在微服务架构中扮演的角色如下图所示,黑线表示注释说明,蓝线由A指向B,表示B从A处获取服务。

image.png

由上图所示微服务架构大致由上图的逻辑结构组成,其包括各种微服务、注册发现、服务网关、熔断器、统一配置、跟踪服务等。下面说说Spring Cloud中的组件分别充当其中的什么角色。

Fegin(接口调用):微服务之间通过Rest接口通讯,Spring Cloud提供Feign框架来支持Rest的调用,Feign使得不同进程的Rest接口调用得以用优雅的方式进行,这种优雅表现得就像同一个进程调用一样。

Netflix eureka(注册发现):微服务模式下,一个大的Web应用通常都被拆分为很多比较小的Web应用(服务),这个时候就需要有一个地方保存这些服务的相关信息,才能让各个小的应用彼此知道对方,这个时候就需要在注册中心进行注册。每个应用启动时向配置的注册中心注册自己的信息(IP地址,端口号, 服务名称等信息),注册中心将他们保存起来,服务间相互调用的时候,通过服务名称就可以到注册中心找到对应的服务信息,从而进行通讯。注册与发现服务为微服务之间的调用带来了方便,解决了硬编码的问题。服务间只通过对方的服务ID,而无需知道其IP和端口即可以获取对方方服务。

Ribbon(负载均衡):Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。为Ribbon,配置服务提供者的地址列表后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机等。当然,我们也可为Ribbon实现自定义的负载均衡算法。在Spring Cloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从EurekaServer获取服务提供者的地址列表,并基于负载均衡算法,请求其中一个服务提供者的实例(为了服务的可靠性,一个微服务可能部署多个实例)。

Hystrix(熔断器):当服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应或超时。在高负载场景下,如果不做任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的崩溃(雪崩效应)。

Hystrix正是为了防止此类问题发生。Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。

跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。

资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。

监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时和被拒绝的请求等。

回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员指定。

Zuul(微服务网关):不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求。例如一个电影购票的手机APP,可能调用多个微服务的接口才能完成一次购票的业务流程,如果让客户端直接与各个微服务通信,会有以下的问题:

客户端会多次请求不同的微服务,增加了客户端的复杂性。

存在跨域请求,在一定场景下处理相对复杂。

认证复杂,每个服务都需要独立认证。

难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将很难实施。

某些微服务可能使用了对防火墙/浏览器不友好的协议,直接访问时会有一定的困难。

以上问题可借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关。使用微服务网关后,微服务网关将封装应用程序的内部结构,客户端只用跟网关交互,而无须直接调用特定微服务的接口。这样,开发就可以得到简化。不仅如此,使用微服务网关还有以下优点:

易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。

易于认证。可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。

减少了客户端与各个微服务之间的交互次数。

Spring Cloud Config( 统一配置服务):对于传统的单体应用,常使用配置文件管理所有配置。例如一个SpringBoot开发的单体应用,可将配置内容放在application.yml文件中。如果需要切换环境,可设置多个Profile,并在启动应用时指定spring.profiles.active={profile}。然而,在微服务架构中,微服务的配置管理一般有以下需求:

集中管理配置。一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此集中管理配置是非常有必要的。

不同环境,不同配置。例如,数据源配置在不同的环境(开发、测试、预发布、生产等)中是不同的。

运行期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小或熔断阈值,并且在调整配置时不停止微服务。

配置修改后可自动更新。如配置内容发生变化,微服务能够自动更新配置。综上所述,对于微服务架构而言,一个通用的配置管理机制是必不可少的,常见做法是使用配置服务器管理配置。Spring Cloud Bus利用Git或SVN等管理配置、采用Kafka或者RabbitMQ等消息总线通知所有应用,从而实现配置的自动更新并且刷新所有微服务实例的配置。

Sleuth+ZipKin(跟踪服务):Sleuth和Zipkin结合使用可以通过图形化的界面查看微服务请求的延迟情况以及各个微服务的依赖情况。需要注意的是Spring Boot 2及以上不在支持Zipkin的自定义,需要到官方网站下载ZipKin相关的jar包。另外需要提一点的是Spring Boot Actuator,提供了很多监控端点如/actuator/info、/actuator/health、/acutator/refresh等,可以查看微服务的信息、健康状况、刷新配置等。

作者:风平浪静如马

链接:https://juejin.im/post/5dff6486518825124f53beb6

来源:掘金

]]>
实际教学简单Java类 | 开发者进阶站-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 点击查看视频课程
Java语言最大的特点在于面向对象的编程设计,并且面向对象的编程设计也在由于Java自身的发展而不断发展,同时,很多最初不支持面向对象的编程也都开始转向了面向对象。

先从简单Java类开始,带你慢慢探索Java进阶之旅,快来开始我们的学习吧!

触摸Java世界的核心:类与对象

所谓的面向过程指的是面对于一个问题的解决方案,更多的情况下是不会做出重用的设计思考的,而面向对象的主要的设计形式为模块化设计,并且可以进行重用配置。在整个的面向对象的设计里面更多情况下考虑的是标准,而在使用的时候根据标准进行拼装。
【本节目标】
本章重点讲解面向对象编程的编程思想,重点需要掌握类与对象的相关概念,包括定义、声明、使用等,类与对象是面向编程的重要概念,不了解类与对象,就不能说会使用Java语言。

点击学习>>

带你“找对象”--Java内存分析

Java之中类属于引用数据类型,引用数据类型最大的困难之处在于要进行内存的管理,同时在进行操作的时候也会发生有内存关系的变化,所以本次针对于之前的程序的内存关系进行一些简单的分析。
【本节目标】
本章深入浅出,结合实例带你了解新建对象的过程中在物理内存上发生的故事,理解通过内存管理实现引用数据类型的流畅使用。通过本章,你将初步了解到堆内存、栈内存相关的知识,学会从内存上分析创建对象的原理,明白通过堆栈地址相互联系实现引用数据类型数据的调用原理。
点击学习>>

对象“变形记”——初识引用与GC

作为Java世界的核心内容,类与对象凭借其引用数据类型的内在特质,实现了引用传递的能力,为整个Java世界添上了浓墨重彩的一笔。
【本节目标】
通过阅读本章,你将通过多组实例从内存上深度了解通过对象声明、方法调用等方法进行引用传递的原理,并深刻理解引用为何会产生内存垃圾以及GC机制的相关内容。
点击学习>>

保守VS开放?看清封装对象属性

高楼万丈,起于平地。本节通过对比正反几个实例剖析了封装对象属性的必要性,介绍了进行封装的基本原则。
【本节目标】
通过阅读本节内容,你将深刻理解封装对象属性的重要性,并学会如何按照Java开发标准正确地实现属性封装与通过封装的方法在类外调用其属性。
点击学习>>

“生而有值”—教你使用构造函数

本节结合多组实例从多个方面介绍了重写构造函数的意义以及构造函数与setter函数的异同,指出了一些编写构造函数相关的注意事项。
【本节目标】
通过阅读本节,你将了解到为对象属性赋值的其他方法,学会通过对构造函数的多样化运用实现对象的快速实例化,使代码显得更加简洁、优雅。
点击学习>>

3分钟速懂匿名对象

当编程过程中仅需要调用一次对象时,为了简便编程过程,减少内存负担,诞生了对象的新形态:匿名对象。
【本节目标】
通过阅读本节,你将了解到使用匿名对象时内存发生的变化,学会直接使用构造函数生成匿名对象便捷地满足一次性调用需求以及各类数据的灵活传递调用。
点击学习>>

揭开this的神秘面纱-属性篇

本节通过几组实例介绍了如何灵活地使用this进行本类属性的调用以及this在类中的特殊含义。
【本节目标】
通过阅读本节内容,能够让你初步了解到作用域的概念,理解this的多种用途,学会使用this进行类内属性的访问。
点击学习>>

揭开this的神秘面纱-方法篇

本节结合几组实例与实际案例介绍了使用this进行本类构造方法与普通方法的调用方法与注意事项。
【本节目标】
通过阅读本节内容,你将了解到借助this实现构造方法的高级重写方式,学会使用this访问类内各类方法,养成编写可重用性高的代码的良好习惯。
点击学习>>

一则案例带你通晓简单Java类

本节通过集中介绍一则案例为你讲解创建一个简单Java类的注意事项。
【本节目标】
通过学习本节,你将了解到创建一个简单Java类时的一些规则,深刻理解“类是对客观事物的抽象”的含义,学会创建一个简单的Java类。
点击学习>>

了解超然物外的static-属性篇

本节通过传统类与使用static的类之间的比较,突出了static对于存储超大量重复性数据的优异表现。
【本节目标】
通过学习本节,你将了解到static定义静态属性的作用,能够分辨使用static的场景,学会通过类名直接调用static属性。
点击学习>>

了解超然物外的static-方法篇

本节简明赅要地指出了静态方法的一些使用场景与限制,并结合实际案例展示了一项static的用途。
【本节目标】
通过学习本节,你将了解到普通方法与static方法的区别,进一步理解使用static修饰方法或属性的时机,学会使用static灵活处理一些实际场景。
点击学习>>

千字掌握“代码块”概念

本节通过多组案例深刻讲解了“{}”在Java世界中扮演的重要角色,简明扼要的介绍了普通代码块、构造块、静态块的异同点。
【本节目标】
通过学习本节,你将初步了解普通代码块、构造块、静态块的异同点以及它们在系统运行中的调用顺序,学会在不同的情况下灵活运用这几类代码块去解决实际的需求。
点击学习>>

六组案例一举拿下Java实体类

本节通过六组案例以Java实体类分别描述各类客观事物,帮助读者进一步掌握Java简单类的编写。
【本节目标】
通过阅读本节内容,你将对如何将客观事物抽象为Java类有更加深刻的理解,并熟练掌握Java类各种属性、方法的编写与相关关键字的运用。
点击学习>>

]]>
循环嵌套打印三角形星星阵 | Python从入门到开发:入门篇之十七-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 先来回顾一下上节课讲过的while语句的循环方式以及它的实际应用。
第十六节:while语句习题课
下面就跟着老师一起来走进循环的“高阶”版本吧~

循环嵌套

按照数学老师的惯例,讲授新知识之前先来看一道例题:
在控制台中打印如下图形:
image.png
打印一个5*5的星星阵。有同学就会想到,写五个输出语句就好啦:

print('* * * * *')
print('* * * * *')
print('* * * * *')
print('* * * * *')
print('* * * * *')

执行结果如下:
image.png
那如果老师要你打印100*51000*58848*5呢。。
此时就可以利用咱们上节课讲过的循环语句来实现:

i=0
while i<5 :
    print('* * * * *')
    i+=1

执行结果也是一样的。而且只需要修改条件表达式i<5中的数字,就可以实现打印100*51000*58848*5。。等等这些,那老师又提要求了,我现在想打印10列,我觉得5列太少了,你的第一想法是修改print语句里面的* 数量就可以实现,那么同样的问题来了,我想打印8848*8848怎么办呢?
用同样的思路:同样对列使用循环,这就形成了一个循环的嵌套。具体怎么嵌套,跟着老师往下看:
首先在里面加一个内层循环,控制列数:

i=0
while i<5 :
    j=0
    while j<5:
        print('* ')
        j+=1
    i+=1

结果会发现执行结果变成了:
image.png
这是为什么呢?在python中,print函数是默认有一个n来控制换行的,也就是说每打印一次*之后就执行了一次换行,那这显然是不行的,我们是想让他打印五次再换行嘛,就需要在后面加一个end=''使得这个换行的功能用空格换掉,再来看一下效果:
image.png
结果发现它全都变成了横向的,这是因为内层的循环再执行一次5列之后还是执行了end=''所以就没有进行换行操作,此时就需要在内层循环结束加一个空的print语句,让其换行即可。

i=0
while i<5 :
    j=0
    while j<5:
        print('* ',end=' ')
        j+=1
    print()
    i+=1

执行结果为:
image.png
这就是咱们想要的结果了。
综上:循环嵌套时,外层循环执行一次,内层循环就执行了一圈(在这里就是5次)。内层循环控制图形的宽度,外层循环控制图形的高度。
此时,我的需求又要变化啦,我不想打印“矩形”了,我想打印一个“三角形”。
image.png
那该怎么来实现呢?
刚刚打印“矩形”的时候老师说了,外层循环控制图形的高度,内层循环控制图形的宽度,现在图形的高度依然是5,所以外层循环是不变的,那么内层循环该怎么变化呢?
如果j的值是固定的,那么就是宽度是固定的,但是这个“三角形”的宽度是变化的,那咱们来分析一下,
当i执行第一次i=0,图形宽度为1,那么j就是小于1;
当i执行第二次i=1,图形宽度为2,j就是小于2;
当i执行第三次i=2,图形宽度为3,j就是小于3。。。
有同学就发现了,j的值是根据i来变化的,j就是小于i+1的值。程序如下:

i=0
while i<5 :
    j=0
    while j<i+1:
        print('* ',end=' ')
        j+=1
    print()
    i+=1

执行结果为:
image.png
这样美丽的“三角形”就绘制出来啦~
大家在理解一下这个逻辑:外层循环执行一次,内层循环就执行一圈,外层i的值在变化,就控制了内层j的值。也就控制了图形的宽度。
练习1
打印99乘法表

1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
...9*9=81

练习2
求100以内所有的质数
下节课来公布答案。

]]>
Oh My God,揭秘淘宝直播流畅买买买的背后!-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 image.png
作者|马俊(东坡)
出品|阿里巴巴新零售淘系技术部

本文知识点提炼:
1、淘宝直播简介
2、四大保障手段
3、整体质量策略

淘宝直播简介

▐ 疯狂的淘宝直播

2019 年的双十一,淘宝直播又疯狂了一把!

双十一当天,淘宝直播成交近 200 亿!超过 50% 的品牌商家通过直播获得增长,直播成为了天猫双十一商家的标配。

image.png

▐ 增长背后的挑战

淘宝直播始于 2015 年,短短四年间打造了一个年成交千亿的行业,走出了一条与众不同的道路。在高速增长的背后是整个系统持续的稳定可靠,但是快速的业务迭代、复杂的系统设计和苛刻的成本控制给系统稳定性带来了不小的挑战。

电商直播是音视频、实时互动和电商交易的技术结晶,相比于传统的电商媒介(图文、视频)它的技术复杂度更高。整个技术框架由音视频流链路和电商/互动逻辑链路构成。音视频流主要涉及推流节点、中心流服务和播放节点;电商/互动逻辑核心是主播中控台、实时消息通道和互动播放。这些系统涉及到不同的技术架构,它们相互复杂地交织在一起,其中任何一个系统的变更都可能影响整个业务链路和逻辑。

image.png

随着 4G 移动网络普及,互联网内容从文字逐步演进到了视频、直播,直播形态的各种产品遍布互联网行业。移动时代直播本身就是一种新的产品形态,当它和电商交织在一起的时候就衍生出更多的可能。业务方往往要从这千万种的可能性中摸索出行之有效的业态和模式,而这种摸索意味着高强度的迭代和尝试。淘宝直播的客户端几乎以每周一个版本的速度前进,服务端更是以日在变化,我们同时维护着 8 个端;除此之外还要承接源源不断的合作诉求和大促职责。不停的迭代和变化确实能够带来产品的进化,但是过于频繁的变更也将系统的稳定性逼到了墙角。

image.png

除开前述的两点之外,直播的内容形式与曾经的图文是截然不同的。更加强调实时的互动、强烈的氛围和流畅的观看,而这些特点本身又对消息通道、网络和 CDN 等软硬件资源提出了更加苛刻的要求。

技术上必然追求最低的成本带给用户最好的体验,包括最小的带宽消耗、最大的机型覆盖、最清晰和流畅的观看体验。这就意味着,必须有一套有效的度量手段来评价我们的成本和产出让 ROI 最大化。

四大保障手段

淘宝直播的质量保障体系主要围绕着如何解决上述三大挑战来进行建设。限于文章的篇幅,会从最核心的四个方面来介绍如何有效地保障淘宝直播以及电商媒体的质量。

▐ 工具建设

俗话说“工欲善其事,必先利其器”。前文说过直播是一个迭代频繁的业务,测试人员在频繁的迭代下不得不面临一次次的测试和反复的业务回归。当业务高度复杂功能繁多的时候,这样的测试和回归简直就是噩梦。

以下图为例,图中展示了部分淘宝直播的互动玩法,通过主播中控台配置具体的互动玩法就能实时地投放到直播间与用户形成互动。但是不同的互动配置和不同的前端展现组合出大量的业务场景,仅靠传统的测试手段或者自动化是无法支撑的。

image.png

有什么更好的办法呢?回到业务逻辑实现,在拥有良好的编程规范的前提下,无论是客户端还是服务端,核心的业务逻辑实现是由各种内部或外部的接口组合而成。通过解构业务逻辑,再将接口组合成测试逻辑。仍旧以互动玩法为例,互动功能可以解构为互动服务、消息通道、主播服务和客户端渲染,将互动服务、消息通道和客户端渲染剥离再按照内置互动配置、调用消息通道接口触发客户端渲染的方式重新构建出新的功能,通过复用业务接口重新构建逻辑的方式可以将业务逻辑上不相关的能力关联起来形成一系列的测试工具。

对整个直播系统进行抽象解构,下图“业务域”内“的主播服务”通过消息通道和服务网关利用互动服务和直播基础服务与客户端进行交互,客户端自身具备通过服务端指令进行动态或者静态渲染的能力。在“测试域”内,可以将业务域解构的各个逻辑重新进行组合。

这种重组可以分为两个方面,客户端侧和服务端侧;在服务端侧不同的接口组合后可以重构出多维信息查询、互动模拟、开放验收等各种业务保障工具;在客户端侧基于动态、静态层和 native 的接口进行二次开发,将服务端信息和客户端本地能力聚合到测试专用的“调试浮层”便于快速能力验证、组件配置和信息透出等。

image.png

通过以上的思路,我们构建了成系列的直播(媒体)专用测试工具、打造了端侧媒体框架,全面提升了测试工作的效率,并以此为基础反复打磨形成一个完整的质量技术架构(见“整体质量策略-技术架构“)。

▐ 链路排查

仅仅通过测试工具提升效率是不够的,在快速迭代中会发生各种线上/线下的问题,问题的快速排查准确定位至关重要。淘宝直播的系统纷繁复杂,涉及到音视频流链路和电商/互动逻辑链路,横跨服务端、CDN、移动端和PC端。通常需要使用不同的工具、平台和手段进行问题排查,而且大多数时候平台之间数据无法关联互通。

image.png

因此需要为复杂的直播体系构建一套全链路的排查系统。工具建设是在接口层面进行业务解构和重新组合,那么链路排查也可以复用这种思想。不同的是要进行链路抽象和简化、业务流程划分、业务数据重组和排查流程构建。更直白的说,就是将不同的业务阶段和不同的技术平台进行抽象和划分;将同一技术平台的数据按照唯一的 ID 进行聚合,再将不同的阶段同一 ID 数据进行聚合;对于聚合在同一 ID 下的数据进行诊断,利用规则匹配、智能算法和人工经验;同时结合线下的测试工具,协助快速调试和复现。

image.png

▐ 数据分析

通过工具建设和链路排查再结合自动化手段,建立了高效的线上线下的质量保障能力;然而链路排查仅解决了具体的问题,对于系统和业务全局层面需要了解更多,例如线上整体质量状况、潜在问题的发现和预防、业务/技术效果评价等等。

在链路排查中,已经将不同业务阶段和技术阶段的数据进行了聚合和分析,在这些数据的基础上进行再加工,包括清洗、计算、聚合和分析,就能够将这些数据更有效地组织起来进行利用。

基于这样的想法,我们设计了一套数据分析的方法将数据划分为“大盘数据”、“纬度数据”、“详情分析”三个层次。

大盘数据主要针对线上的某个横向层面的整体分析和监控,一般划分为“业务”、“技术”、“舆情”、“异常”四个方面,大盘数据的波动意味着某个环节发生了问题。

在大盘的基础上按照不同的业务域和技术域进行拆分,每个域代表一个纬度的变化,每个纬度由多组该域内的指标构成;一般我们按照不同的端来划分技术域,按照不同的业务场景来划分业务域(图中仅为示意)。

当大盘、纬度划分清楚后,每个细节的数据指标都会归属到相应的“大盘”-“纬度”之下,再对这些细节的指标提供对比、趋势分析、多维度聚合等分析工具从而实现从全局到细节的分析和监控,针对特定的指标结合监控系统就能进行有效的告警。

image.png

这一整套数据分析的方法都建立在实时和离线的大数据分析平台之上。首先通过各端的上报工具采集原始数据形成实时数据流和转储的离线数据表;实时数据流通过实时计算平台(阿里云实时计算)对数据流进行清洗和计算;计算完成后将数据转储到搜索引擎,由引擎负责索引、排序和聚合;最后通过引擎接口返回给服务端,服务端可以对引擎提供的数据进行二次加工。

在整个过程中如果实时计算任务出现异常或者丢失,可以通过转储到离线表的数据进行补偿计算再流入到搜索引擎。

image.png

▐ 媒体质量

工具建设(技术框架)、链路排查和数据分析提供了通用的质量保障能力,可以被应用到直播或者多媒体之外的场景。而音视频(直播)有自身的特点,例如画质清晰度的要求、CDN带宽的消耗和移动端的性能限制等,需要媒体专项来保障,因此我们将这些专项定义为媒体质量。

媒体质量总结为三大测试专项和两个建设领域,三大测试专项指的是“特性测试”、“(媒体)SDK测试”、“专项测试”,两大建设领域分别是“音视频实验室建设”和“标准化建设”。

  • 特性测试(画质、特效、卡顿、延时等)

构建一套通用的媒体特性测试框架,对媒体的特性进行检测和评估

  • SDK测试(推流、播放、剪辑三大SDK )

构建统一的媒体demo、统一的SDK测试和报告、

  • 专项测试

覆盖各端的性能指标、渲染能力评估,同时与竞品对比

  • 音视频实验室建设

统一的线下物理实验室、模拟各种光照、音源、采集环境

  • 标准化建设

媒体质量评价的核心,三统一(环境统一、流程统一、标准统一),一体化执行结果可沉淀可分析

image.png

重点介绍下特性测试框架,整个框架由推流端的“预处理模块”、网络端的“可编程网络控制”、播放端的“分析模块”以及“评估模块”四部分块构成。

  • 预处理模块

通过hook的方式实现在推流侧统一采集内容、定制单帧检测点;

  • 可编程网络控制模块

通过程控方式来调节推流端到播放端的网络环境,自动实现网络环境切换统一网络参数;

  • 分析模块

主要是负责抓取播放端解码YUV数据并结合帧检测点和评估算法进行特性分析;

  • 评估模块

提供了不同的特性评价方法可以被分析模块调用。

通过这套框架,模拟完整且标准化的媒体场景,通过调节帧检测点、采集内容、网络参数、编解码参数等实现媒体特性的专项测试。

image.png

整体质量策略

通过这四年从无到有的摸索,淘宝直播和媒体电商业务最核心的质量策略可以抽象为一个核心思想、一套技术架构和一份能力模型。

▐ 核心思想

  • 质量体系必须是平台化的
  • 质量体系不仅仅服务测试
  • 质量体系必须数据说话

image.png

▐ 技术架构

  • 双端技术框架和全栈开发能力
  • 核心技术是大数据分析和媒体技术

image.png

▐ 能力模型

  • 链路排查将逐渐成为系统质量保障标配能力
  • 测试团队应当建设业务专项能力深度(多媒体专项)

image.png

We are hiring

淘系技术质量部电商丰富的场景,广阔的平台等你一起来挑战!
在这里你可以接触到全链路压测、海量的数据处理、人工智能推荐算法等领域;可以涉猎业界最前沿的测试技术、定期而丰富的技术分享;还可以与层层选拔的各路优秀同学共同战斗,共同成长!
欢迎测试开发工程师/专家加入我们,让科技引领面向未来的商业创新和进步。
简历投递至]]> 10亿计算下的合约广告,如何做个性化投放?-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 作者| 荣纯

一、导言

合约保量广告(Guaranteed Delivery)是一种常见的品牌展示广告采买方式,现有的技术解决方案通常是在人群粒度上对问题进行抽象和建模,这种建模方式一方面忽略了相同人群下用户行为的差异,另一方面无法对用户粒度的约束进行精确的控制。

目前学术界关于合约广告流量分配问题的研究,通常会将这个问题抽象为合约侧-供给侧的二部图匹配问题,但目前的分配策略是停留在人群和标签粒度上,这要求人群和标签的划分必须是正交化的;除此之外,在人群层次上进行合约保量分配也还有不少局限性。

首先,由于只在人群层面进行分配,无法通过精准的用户行为预测将用户的个性化行为匹配至正确的广告,会降低广告主的投资回报率,进一步的降低广告平台的未来收入。其次,广告主通常会提出复杂的投放控制要求,比如在用户粒度的频次控制约束,一个典型的做法是,为了能够提高固定预算下的uv触达,广告主往往会限制单个uv的曝光频次。因此,传统的人群标签粒度分配的低效性使得其很难适用于目前的合约广告投放产品。

在本文中,我们尝试建立一个大规模分布式的合约广告投放分配算法,并在其中引入用户个性化的投放指标,在考虑用户交互行为的基础上,在用户粒度进行合约广告的投放分配工作。我们的算法可以处理复杂的约束,例如广告优先级,广告的展示频控以及广告贴位容量限制等。在这个基础上,我们还开发了实时预算平滑策略,通过预算平滑控制进一步优化广告的投放效果(如单位点击成本CPC)。目前我们的系统在阿里妈妈品牌展示广告实际承载了十亿规模的离线计算任务并在线应用,我们也将在最后给出离线和在线的实验结果来验证方案的的准确性。

二、问题定义

1201.png

这是一个经典的online assignment的问题,在数据规模较小且全局信息已知的情况下,使用匈牙利算法、网络流算法和混合整数规划方法都可以获得理想的最优解。但是对于互联网广告投放系统而言,数据规模很大,而且由于性能要求,对于一个请求,不可能也没办法确定全局信息。

1201.png

1201.jpg

1201.png

1201.png

1201.png

1201.png

1201.png

以上就是对问题的定义和描述,众所周知淘系用户日均DAU的规模是亿级别以上,而广告每天也成百上千的规模,在这样一个大规模求解的背景下,难点在于如何对这个问题进行求解并保持提供高并发、低延迟的广告服务。为此我们在离线阶段对这个问题进行了大规模的分布式求解,而在线阶段为了能够适配流量的变化情况,我们开发了独立的pacing模块来进行流量的平滑控制。

三、系统实现

3.1 系统描述

1201.png

上图是我们系统的整体架构图和整体的数据流程,主要包括面向广告主订单签订的CRM管理系统,离线算法和在线引擎部分。广告主作为管理平台和广告主之间的桥梁,主要用于订单信息的制定,包括人群圈选,流量询量,在线合约签单,创意信息绑定等。离线的处理框架将会同步这些合约信息,并与获取的离线日志一起,通过基于PS架构的分配优化模型进行离线分布式算法求解,将计算结果导入到Model中并同步至在线系统。最后当实时请求到来时,Merger作为在线的引擎,将请求RTP服务,pacing服务,和分配模型服务,经过离线的分配算法模型,得到最终投放的广告并返回给前端客户端展示。

3.2 离线优化

离线的部分算法分为2个阶段,第一个阶段我们将原问题转化为其对偶形式,通过并行化求解得到问题的对偶变量。第二阶段,由于现实投放优先级的影响,我们通过离线拓扑排序的方式,给出并行加速的方案,从而使得大规模数据集上的求解效率大幅度提升。

3.2.1 阶段一分布式求解

1201.png

对于我们的问题而言,用户的的量级远远大于合约的数量,所以求解合约侧对偶的的计算代价会远远大于供给侧。我们通过分布式计算的方式进行水平扩展,将供给侧对偶变量的计算分布在ps的worker节点上。而对于供给侧来说,其更新的过程放在server上,woker通过pull的方式获取最新的对偶变量。观察合约侧的对偶变量约束,我们发现其等式求解规模和广告主人群的规模是一致的,这个计算量对于通常的计算而言往往过于巨大,因此我们采用一种近似的方法来计算和更新这个对偶变量。注意到在更新的过程中,这个变量是一个保持单调递增的过程,相邻两轮的迭代满足这样一个不等式:

1201.png

1201.png

上述伪代码是我们更新对偶变量的算法描述,而关于供给侧对偶变量的求解,我们很容易注意到一个结论:

3.2.1-(7).png

而且关于这个方程的目标是单调的,因此可以通过二分求解的方式得到结果,具体过程我们在这里不进一步描述。

3.2.2 阶段二并行优先级加速

1201.png

1201.png

注意到原始的计算过程中 是单独顺序计算的,在数据规模较小的情况下,这个计算量尚可接受,但是当合约数量达到一定规模后,比如上万的规模,计算效率明显比较低下。而之前的性质告诉我们,对于任意2个正交的人群,他们的计算过程并没有优先级的重叠,因此是可以并行计算的。举个简单的例子,对于定向北京和上海的2个用户群来说,即便优先级存在不同,但是由于互相没有重叠,一个对偶的占量并不会影响另一个定向的库存,这样2个任务是完全可以并行计算的。

1201.png

在这个前提下,我们在离线构建二分图的时候,由于保存了每个用户的合约广告挂载情况,从而可以得到每个用户的实际订单的优先顺序,如上图所示,根据原始的订单分配顺序,进一步可以得到所有订单的DAG大图。最后很容易通过拓扑排序的方式得到我们优化后的并行化执行序列。接着拿上图进行举例,原始的更新顺序是[A] → [H], 通过DAG图的构建以及最终拓扑排序的结果,我们最终得到[A, B] → [C, D, E] → [F, G] → [H]这样只有4个需要处理的批次,相比于原先8个批次的执行,效率可以提升近一倍左右,而在实际的实验中,这个优化的效率提升会更加明显。

1201.png

3.3 在线pacing

之前的大多数展示投放,尤其是合约保量这里,许多系统采用了轮盘赌的方法或者是贪心算法来进行在线投放。由于在投放之前,我们模型的对偶变量已经确定,对于未来的投放概率往往是不可改变的,这样会带来一个问题,投放的结果将严重依赖于流量预估的结果,这会导致线上缺量或者超投的情况发生。为了能够及时的适应流量分布的变化,我们在合约投放之后增加一个pacing的调控模块。我们注意到相比于天级别的流量波动,分钟级别的波动往往比较小,因此我们可以在分钟级这个level上,进行实时的调控。

1201.png

1201.png

1201.png

区别于之前合约保量的HWM算法和SHALE算法,我们改进了线上的投放方式,原有的轮盘赌方式本质上在效果层面随机选择广告,对于展示效果而言会有信息损失。如果直接贪心选择效果最好的投放,在线上由于分配占量的问题,会有投放缺量风险,通过将pacing和XSHALE这两种方式结合在一起,引入实时的pacing调控,使得我们的方法可以更好适应线上流量的变化。

四、实验结果

我们在淘宝的数据集上进行离线的验证,这些数据收集于手淘banner和猜你喜欢的合约广告。在离线阶段我们会验证求解对偶变量的正确性和高效性,同时用实际的线上A/B测试来验证我们离线模型和在线pacing服务的正确性。

4.1 离线实验

我们和GD的经典算法HWM算法和SHALE算法进行比较,除了求解时间外,算法指标方面我们从投放完成率,惩罚项,L2-Norm和平均点击成本这四个指标来评测。四个指标的定义分别如下所示:

1201.png

1201.png

离线指标评测如下表和下图所示,在各种情况下与其他方法相比,我们的系统都能将CPC降低近50%。虽然我们在完成率,惩罚项和L2-Norm中有一些收益的损失,但与CPC降低幅度相比,我们可以接受这些损失。时间性能方面,HWM是最快的,因为它仅运行一次迭代。

但是,与SHALE和我们的方法相比,HWM的投放性能相对较差,线上不具备使用的可能。由于我们考虑了多目标的分配, SHALE在完成率方面略胜于我们的方法。但是我们的方法在改进投放指标方面有显著的的提升。如之前所述,广告分配问题的一个瓶颈是求解对偶变量α和β的时间。我们采用了并行化的方案以及基于DAG中拓扑排序的加速调度方式,可以更加快速的计算对偶变量α和β,这种加速比在大数据上的优势会更加的明显。

1201.png
1201.png

进一步分析发现,通过DAG中拓扑排序的加速调度方式,可以大幅度的提升求解性能。我们在真实的7天日常任务上进行测试,用OriginBatch来表示不经DAG加速的迭代轮数,用Reduce Batch来描述加速后的迭代轮数,可以很明显的看到,相比串行的方式,使用DAG的任务可以有效地将计算速度提高14倍左右。

1201.png

我们给出了超参learning rate的调参比较,可以很明显的看到,在learning rate取值较小时,其收敛速度是较慢的,当取值逐渐增大,其收敛速度会显著上升,而当learning rate过大时会在最优解周围出现震荡,极端情况l=2.0时,和最优解的距离相差非常之大,就我们的系统而言,我们通常将learning rate设置在0.5到0.7之间。

1201.png

最后我们测试了超参λ对投放完成率和平均每次点击费用的影响。 可以看到平均CPC随λ的增加而单调下降,也就是说如果想获得更大的平台收益,增大λ似乎是一个不错的选择,但是当λ特别大时,不仅造成了完成率的下降,点击成本也并不会进一步的下降,这样会导致缺量的发生,因此我们一般会控制完成率的损失在一定的范围内,如在1%-3%之间。

1201.png

4.2 在线实验

我们在手淘场景中对我们的方案进行了真实的线上测试,我们用线上的真实点击率来评估我们的投放指标。实际在线A/B实验中,采用预算分桶的测试机制来保障各个实验是在相同的预算下进行的。我们和基于贪心投放的基准算法以及传统SHALE算法进行了对比。而为了能够验证平滑pacing的作用,我们增加了SHALE-CLICK算法,即考虑点击效果,但不考虑pacing,采用轮盘赌方式进行投放。

从图a的曲线上看,使用我们的策略得到的投放曲线最为平滑,基于贪心的基准算法在开始就花费了一半以上的预算。接下来表现不错的是SHALE算法和SHALE-CLICK算法,通过轮盘赌的方式一定程度上进行了平滑的控制,但是由于线上流量分布的变化与离线并不完全一致,导致效果并不是最优的结果。

1201.png

图b给出了不同算法分桶下的实时累计平均点击率,可以看到考虑点击建模的SHALE-Click算法的表现优于贪心基准算法和SHALE算法。贪心算法由于投放过快,导致其流量优选的空间受到了限制,其表现最差。在pacing算法的平滑下,我们的算法表现最佳,在刚开始投放的过程中就显示出比较明显的优势。如图c所示,实验结果显示我们的方法在点击率方面相比baseline提升22.7%,相比SHALE提升21.2%,相比SHALE-CLICK提升10.6%。实验充分表明了我们算法的有效性。

五、总结

通过将用户的和广告的交互指标纳入到分配的目标中去,通过设计离线分布式算法和在线的实时调控算法,可以大幅度的提高合约保量广告的投放效率和合约完成情况。在线的分桶实验进一步表明我们的算法可以在为合约广告平台提供更多收益的同时提升广告主的品牌营销效果。以上工作被 ICDM'19 以长文录用,请参见《Large-Scale Personalized Delivery for Guaranteed Display Advertising with Real-Time Pacing》

编者按:我们是阿里妈妈品牌展示广告团队,一支以 90 后为主的年轻团队,急招算法工程师(base杭州/北京)请联系 brandship@alibaba-inc.com

]]>
阿里巴巴D2 前端论坛最全视频来了!(附PPT下载) | 6大专题持续更新 | 开发者必读(124期)-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800

最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

每日集成开发者社区精品内容,你身边的技术资讯管家。


每日头条

阿里巴巴D2 前端论坛最全视频来了!(附PPT下载) | 6大专题持续更新

D2 前端技术论坛 (Designer & Developer Frontend Technology Forum, 简称 D2),是由阿里经济体前端委员会主办的面向全球前端领域的技术论坛,立志于建设一个促进业内交流、引领前端领域发展的平台。目前 D2 已经成功地举办了13届,为国内外前端领域的开发者和设计者提供了共同探讨行业发展的机会,以技术会友、一起分享技术的乐趣。

12 月 14 日,第十四届 D2 前端技术论坛在杭州圆满举办。来自全国各地的近千名开发者齐聚杭州,聆听 3 大会场、来自 24 位海内外嘉宾的 21 个主题分享。今天,开发者社区整理了6大演讲主题中10位精彩演讲者的视频,供大家交流学习。


最强干货

PHP 依赖镜像出问题后,阿里工程师的一顿“神操作“令人叫绝!

上个月,PHP开发者在网上纷纷反映出现 Composer 镜像无法访问的问题。阿里云内部一位 90 后工程师顾咏连夜开工排查,快速解决问题后,他在问题群里收到了一大波来自用户的红包。顾咏最后谢绝了红包,接受了阿里技术的邀请,来聊一聊这次事件问题背后的技术。

2019 年 AI 领域都发生了什么?

回首即将逝去的 2019 年,在人工智能领域中,都有哪些可圈可点的地方呢?《生成式深度学习》(Generative Deep Learning)(O’ Reilly Media 2019 年出版)一书作者 David Foster 为我们进行了回顾,对人工智能世界在这一年来发生的事情进行了大盘点。

Java开发者,请停止学习框架

假设你面前有两个应聘者,一个对框架特别熟,但是对基础知识一点都不懂;另一个对框架一点都不熟,但是基础知识特别懂。


每天读本书

循环嵌套打印三角形星星阵 | Python从入门到开发:入门篇之十七

本章节讲授如何进行循环的嵌套使用。


精品公开课

50行代码玩转强化学习

强化学习是当前人工智能领域比较热的一个方向,因为其入门门槛相对于其他人工智能方向比较高,因此往往都是听说强化学习很厉害但是没有接触或者用过。本次分享,结合一个优秀的强化学习框架huskarl以及Gym来讲解如何使用50行代码利用TensorFlow2.0高阶API,一步一步的、手把手的教你如何快速实现一个强化学习项目。


每日集成开发者社区精品内容,请持续关注开发者必读

]]>
年度大盘点 | 一文带你回顾阿里云边缘计算的2019-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 在5G万物智联时代,云计算逐步向分布式计算网络架构演进,边缘计算来到爆发前夜。它是连接5G商用潜能的助推器,是引领数据中心变革的技术支撑,为产业数字化转型和城市智慧化建设提供近终端、低延时、更经济的算力基础。

为了构建“离用户更近的计算”,阿里云始终奔跑在边缘计算的进阶之路上。下面让我们一起回顾,2019年阿里云边缘计算的「大事记」吧~

1月 边缘节点服务ENS 2.0发布

ENS 2.0引入MEC资源,将飞天算力调度、容器、函数、安全、大数据与AI等技术能力进一步融合释放,建立了“融合、开放、联动”的边缘计算平台,将计算能力进一步推进至用户10公里生活圈。同时,ENS 2.0也提供了更加弹性灵活、形态多样的分布式算力资源,并支持全面按量付费,算力、存储、带宽按天按月出账计费,帮助使用者进一步降低启动资金以及应对弹性业务需求。
图1.png

3月 边缘节点服务ENS正式上线OpenAPI

边缘节点服务ENS正式支持用户使用阿里云OpenAPI调用和管理边缘实例。基于ENS OpenAPI提供的创建、查询、管控能力,用户可以轻松构建针对边缘节点的自助运维体系。

4月 荣获“5G MEC优秀商用案例奖”

在2019中国联通合作伙伴大会上,阿里云“基于5G边缘计算的新零售应用案例”荣获2019年度MEC优秀商用案例奖。该方案基于5G热点覆盖,将视频通过稳定可靠的链路回传,并借助MEC实现视频业务本地分流和视频AI本地分析。云+网+业务一体化交付模型,让新零售产业从传统的自建IT基础设施及运维工作之中解放出来,转变为灵活按需一键启用边缘计算服务,大幅度减少综合投资成本,简化运维。点击查看详细新闻。

图2.png

6月 阿里云成为中国联通5Gⁿ+边缘云创新业务运营中心首批商用合作伙伴

2019年世界移动通信大会期间,中国联通5Gⁿ+边缘云创新业务运营中心正式成立。阿里云作为首批商用合作伙伴加入,双方将充分发挥各自技术优势,在边缘计算MEC节点、技术验证、产业应用等多方面展开深度合作,共同推进标准建设与5G应用场景创新。

图3.png

7月 国内首个全域边缘节点服务发布

300+边缘节点算力100%覆盖中国大陆31个省区的三大运营商与热门地区的三线城市,全域节点带宽分区定价,满足不同地区差异化需求,更好满足网络质量敏感、时延敏感、广覆盖、流量本地化的各行业客户为体验的追求。自此阿里云边缘计算正式宣告进入“无处不在”的全新篇章。点击查看详细新闻。

图4.png

8月 发布边缘K8s和容器解决方案

边缘节点服务ENS打通容器服务,支持边缘实例接入容器托管集群,赋能开发者DevOps轻松落地,统一进行资源管理与调度、镜像分发、自助运维和云端监控,极大简化了运维工作复杂度。

9月 阿里云定义边缘计算为城市计算

阿里云定义边缘计算是基于场景的城市计算。未来将围绕边缘芯片/设备、边缘计算平台(操作系统)、城市边缘中间件、城市边缘应用及服务这四个边缘技术栈进行布局,进一步与产业上下游的伙伴们深度融合,找到更丰富的、面向多行业的边缘应用场景,为客户打造离用户更‘近’的计算,进而承载更多新型的5G城市计算应用场景落地。点击查看详细新闻。

图5.JPG

9月 牵头编制《信息技术云计算边缘云计算通用技术要求(征求意见稿)》

该标准明确对边缘云计算系统的基础设施、服务能力、统一管控、接口、安全等通用技术要求进行统一的厘清与规范,用于指导“边缘云计算”技术和产品研发、服务交付。这是阿里云继2018年12月牵头制定业界首份《边缘云计算技术与标准化白皮书》后再次助力行业标准化升级。点击查看详细新闻。

图6.JPG

9月 发布边缘存储、边缘智能视频云

阿里云发布边缘存储、边缘视频云全新服务,结合此前的边缘节点服务、边缘容器,从底层资源能力、计算服务、PaaS平台、行业应用贯穿打通,全面升级边缘操作系统及分布式计算分发平台,形成体验式通用服务闭环,边缘计算再次向场景化服务迈出重要一步。点击查看ENS产品详情。

11月 边缘节点服务ENS SLA提升至99.9%

随着边缘节点服务场景持续拓展,ENS客户规模不断扩大,产品对客户的SLA承诺也不断加码。ENS依托稳定性三板斧,即 严格的机房和设备上线标准、全链路服务监控体系、故障快速响应恢复体系,实现99.9%稳定运营超过半年,并在11月正式升级官网SLA至99.9%。

12月 荣获最佳智能边缘计算技术创新平台

2019亚太内容分发大会,阿里云凭借在边缘计算领先的技术布局与创新方案,荣获“最佳智能边缘计算技术创新平台”奖项。点击查看详细新闻。

图8.jpg

12月 首批通过边缘云标准符合性测试

2019中国云计算标准与应用大会上,阿里云获得由中国电子技术标准化研究院颁发的首批《边缘云标准符合性测试证书》。这是业内权威机构首次开展边缘云领域的测评认证,对于产业上下游和技术服务商具有重要指导意义。期间,阿里云积极配合中国电子技术标准化研究院完成了全部用例的编制,并顺利通过测试验收。其用例包含基础设施、基础设施服务能力、平台能力、应用服务能力、统一管控、安全要求等多个方向,获得权威机构的一致认可。点击查看详细新闻。

图9.jpg

以上就是阿里云边缘计算2019年度盘点。

2020年是崭新的一年,阿里云边缘计算将继续秉承“融合、开放、联动”的理念,持续与产业伙伴连接,用技术升级大众娱乐,让科技赋能城市变革,用“离用户更近的计算”加速产业在5G万物智联之路上狂奔。

欢迎加入ENS产品答疑钉钉群:21740823,了解更多资讯。

]]>
成人网站 Pornhub 技术栈首度公开-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 成人网站在推动 Web 发展方面所起到的作用无可辩驳。从突破浏览器的视频能力限制,到利用 WebSocket 推送广告(防止被广告拦截器拦截),你必须不断想出各种聪明的办法,让自己处在 Web 技术创新的最前沿。

最近,我有幸采访了大型成人网站 Pornhub 的一位 Web 开发工程师,了解了相关的开发技术、Web API 的改进,以及作为成人网站开发工程师是一种怎样的体验。

注意:因为成人网站这个行业竞争相当激烈,有一些问题他们不能回答我,这一点我表示理解。

成人网站需要显示大量的图像内容,在开发过程中,你是否使用了大量的图片和视频占位符?开发过程中的内容体验和最终产品差距大吗?

实际上,在开发这个网站时我们并没有使用占位符!归根结底,代码和功能才是最重要的东西,至于界面什么的,到了这个时候我们已经很熟悉了。刚开始时有一点难度,但很快我们就适应了。

在开发过程中,你们是如何模拟直播视频流和第三方广告脚本的?它们都是很重要的资源。

播放器被分为两个组件,基本组件实现了核心功能,用于触发事件。开发是单独进行的,在进行集成时,我们需要用到第三方脚本和广告,这样可以尽早发现问题。对于一些特殊情况,我们会与广告主合作,通过手动的方式来触发一些随机事件。

一般页面上至少会有一个视频、一些 GIF 广告、一些直播预览和其他视频的缩略图。你是如何测定页面性能的?以及如何尽量提升页面的性能?> 我们使用了一些测评系统。

**1.播放器会将视屏播放的性能和用户播放情况发送给我们;
2.我们使用了第三方的 RUM 系统;
3.我们使用了 WebpageTest,这样就可以知道在某个时段发生了什么事情。**

我假设播放器是前端的一个最重要也最复杂的功能。在视频前面插入广告、标记视频的关键部分、改变播放速度,等等,你是如何保持播放器的性能、功能和稳定性的?

我们有一个专门负责开发播放器的团队,他们的首要任务是持续地监控播放器的性能。我们用上了所有可用的工具:浏览器性能工具、WebpageTest、性能指标,等等。每次在发布更新之前,我们都会进行一轮严格的 QA 来保证稳定性和质量。

视频团队有多少专职开发人员?有多少前端开发人员?

我只能说,如果从整个产品的规模来看,我们的团队规模算是中等的。

在从事成人网站开发期间,你看到前端领域经历了哪些发展?有哪些新的 Web API 给你带来很大的帮助?

我看到前端技术在很多方面都有进步。

1.从使用纯 CSS 到使用 LESS 和 Mixin,再到使用灵活的栅格系统和图像标签来适应不同的分辨率和屏幕大小;
2.jQuery 和 jQueryUI 逐渐淡出了我们的视线,我们回到了更加面向对象的纯 JavaScript 编程。一些框架在某些场景下也起到非常有趣的作用;
3.我们很喜欢新的 IntersectionObserver API,用它来加载图像非常高效;
4.我们还使用了画中画 API,让视频漂浮在页面上,不过现在还在争取用户对这个想法的反馈。

展望未来,有没有哪些 Web API 是你希望发生变化、改进的?或者出现新的 Web API?> 我们希望这些 API 能够发生变化或改进:Beacon、WebRTC、Service Worker 和 Fetch。

1.Beacon:在 iOS 上有些问题,对 pageHide 事件支持得不太好;

2.Fetch:没有下载进度,也没有提供拦截请求的方式;

3.WebRTC:在进行直播时,如果分辨率不够大就会有所限制;

4.Service Worker:调用 navigator.serviceWorker.register 不会被 Service Worker 的 Fetch 事件处理器拦截到。

WebVR 在过去几年已经有所改进。目前来看,它的作用有多大?成人网站会投入多大精力来支持 VR 内容?Pornhub 的 WebVR 有涉及触觉技术吗?

我们正在研究如何将 WebXR 应用在沉浸式空间场景中。作为最大的内容分发平台,我们有必要为用户提供让他们能够按照自己的方式来体验网站内容的机会。但我们还在探索,在使用这些新媒体时,内容和平台应该是什么样子。

我们是支持 VR 、计算机视觉和虚拟主播的一个主要平台,我们将继续推动新技术的发展。

每个页面上都有不同类型的媒体和内容,对于桌面版或移动版来说,最需要考虑的东西是什么?

我们主要考虑操作系统和浏览器对功能方面的限制。比如,iOS 和 Android 在访问权限和功能方面就非常不一样。

一些 iOS 设备不允许在全屏时使用自定义播放器,它们会强制使用原生的 QuickTime 播放器。而 Android 则给了我们完全的控制权限,可以在全屏时使用我们的播发器。

另一个例子是 HLS 视频流,IE 和 Edge 对 HLS 视频流质量非常挑剔,所以我们需要控制视频的质量,否则在播放时就断断续续或者出现重影。

目前 Pornhub 可以支持的最低浏览器版本是哪个?现在还支持 IE 吗?

我们支持 IE 很长时间了,但最近不支持 IE 11 之前的版本。另外,我们也停止支持 Flash 播发器。我们现在主要支持 Chrome、Firefox 和 Safari。

**可以分享一下 Pornhub 的技术栈吗?从服务器端到前端,你们使用了哪些库?
基本上,我们使用了这些东西:**

Nginx ;

PHP ;

MySQL ;

Memcached / Redis 。

其他技术还包括 Varnish、ElasticSearch、NodeJS、Go 语言、Vertica。

前端方面,我们主要使用了纯 JavaScript。我们在逐步淘汰 jQuery,并开始使用框架,比如 Vue.js。

在外行看来,成人网站的网页上一般充斥着各种视频缩略图、视频、直播和广告。从开发者的角度来看,是什么东西让一个成人网站变得与众不同?

我们努力让每一个品牌都具备一定程度的独特性,不同的内容、界面体验和功能,还使用了很多不同的算法。

在面试 Pornhub 时,你是怎么想的?你有犹豫过吗?如果有,又是怎么消除这种情绪的?

我没有感到有什么不妥,毕竟这个挑战对我来说充满了吸引力。一想到有数百万人会用到我开发的东西,我就感到很兴奋。这个想法很快就得到了验证,当我开发的功能第一次上线时,我感到很自豪,我还叫我的朋友们也去看看!成人网站永远都不会消亡,它为我们提供了稳定的工作来源。

与开发一般的网站相比,开发成人网站可能会有所不同。当你告诉你的朋友、家人和熟人自己在开发成人网站,你会觉得这是一种耻辱吗?你会犹豫告诉他们这些吗?

我为自己开发的东西感到自豪,我身边的人都知道,也很喜欢它们。这也成了我们的茶余饭后的谈资,非常有意思。

你也在其他地方开发过其他网站,在 Pornhub 的工作氛围有什么不同吗?

这里的氛围非常轻松友好,我不觉得跟在其他地方有什么不同。

作为前端开发人员,你需要与哪些团队密切接触?你们平常常用哪些交流方式?

我们需要与后端开发人员、QA 和产品经理打交道。大部分时间我们会跑到各自的工位上讨论问题,其次是使用聊天工具(Microsoft Teams),然后是电子邮件。

最后,作为一名在成人网站工作的开发工程师,你还有什么想要分享的吗?

我非常高兴能够参与开发这个有如此大规模用户的产品。我们身处技术发展的最前沿,这让一切都变得有趣且颇具挑战性。

后记

这个采访很有启发性。我很惊讶他们在开发时居然没有使用图像。Pornhub 走在 Web 技术的最前沿——WebXR、WebRTC 和 Intersection Observer API。我也很高兴看到他们开始逐步淘汰 jQuery,因为现在的 Web API 很给力。

我很想从他那里挖到更过有关技术和性能的细节,我敢肯定他们的源代码里有很多值得一学的东西。换了是你,你会想问哪些问题?

原文链接

]]>
未编译 Python 代码比 Go 慢 100 倍-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 原文链接
在我看来,编译型代码有两个明显的优势:

每次修改代码都可以得到验证,甚至是在开始运行代码之前。

更快的执行速度。根据具体情况,代码可能被编译成非常底层的运行指令。

我之所以要写这篇文章,是想比较一下编译型代码的执行速度会比解释型快多少。

因为我偏爱编译型编程语言,所以现在有个问题:我手头有很多感兴趣的代码,但它们都是用 Python 写的,我该怎么办?全部重写?部分重写?完全不重写?

先入之见
在这篇文章里,我通过比较 Java 、 Go 和 Python 在处理不同任务时的性能表现来验证我对它们的一些先入之见。首先是 Python,我正在考虑要不要把它替换掉。至于 Java,我已经是 20 多年的粉丝了,一路看着它成熟,不管是性能还是功能都在变得更好。最后是 Go,我两年前才开始用它,但真的很喜欢它。虽然 Go 相比 Java 还缺失了一些特性,比如类继承,但它的语法简洁而紧凑,编译和执行速度都很快,生成的代码也很紧凑,还提供了优雅的 goroutine 来实现并发处理。

以下是我的一些先入之见。

编译型代码的执行速度比解释型代码要快一个数量级。之前,我比较了使用 JIT 和不使用 JIT 编译 Java 代码所获得的性能,它们的比率大概是 30 比 1。

Go 的运行速度比 Java 要快一点。我记得在之前的工作中做过一些测试,发现 Go 在处理某些任务时要比 Java 快 30%,但最近一些文章又说 Java 比 Go 快。

先来测试一把我在之前的一篇文章中通过一些代码比较过 JIT 的性能,后来使用 Python 和 Go 也实现了一遍。这段代码计算 100 的 Fibonacci 数值,每一轮计算 50 次,并打印执行时间(纳秒),共计算 200 轮。代码可以在 GitHub 上找到。

三种语言的输出结果看起来像这样:

复制代码

Java   Go    Python
...
122    123   11683
119    107   11539
123    104   11358
120    115   11926
119    118   11973
120    104   11377
109    103   12960
127    122   15683
112    106   11482
...

平均值是这样:

复制代码

Java   Go    Python
130    105   10050

可以看到,在计算 Fibonacci 数值时,Java 比 Go 要慢一些,大概慢 24%,而 Python 几乎慢了 100 倍,也就是 9458%。

这个结果验证了我最初对 Java 和 Go 的判断,但让我感到吃惊的是 Python 的表现,它慢得不只是一个数量级,是两个!

我在想 Python 为什么会花这么多时间。

我首先想到的是,很多人关注的是 Python 的易用性,并通过牺牲性能来快速获得处理结果。我相信数据科学家们都是这么想的。况且有这么多现成的库可以用,为什么要去找其他的?迟早会有人优化它们的。

第二个原因是很多人没有比较过不同的实现,因为很多初创公司在激烈的竞争中忙于做出产品,根本无暇顾及什么优化不优化。

第三个原因,有一些方式可以让同样的 Python 代码跑得更快。

把 Python 代码编译一下会如何
在做了一些调研之后,我决定使用 PyPy 测试一下相同的 Python 代码。PyPy 是 Python 的另一个实现,它本身就是使用 Python 开发的,包含了一个像 Java 那样的 JIT 编译器。跟 Java 一样,我们需要忽略初始的输出,并跳过 JIT 编译过程,得到的结果如下:

复制代码

Java   Go    Python    PyPy
130    105   10050     1887

PyPy 的平均响应速度比 Python 快 5 倍,但仍然比 Go 慢 20 倍。

更多的测试
以上的测试主要集中在数值的计算上,如果回到最开始所说的 Python 代码,我还需要关注:

Kafka、HTTP 监听器和数据库的 IO;

解析 JSON 消息。

总结
本文通过执行简单的数学运算得出这样的结论:Go 的执行速度比 Java 快一些,比解释运行的 Python 快 2 个数量级。

基于这样的结果,我个人是不会使用 Go 来替换 Java 的。

另一方面,在高负载的关键任务上使用 Python 不是一个好的选择。如果你正面临这种情况,可以考虑使用 Python 编译器作为短期的应急方案。

在决定是否要重写 Python 代码时,还需要考虑到其他因素,比如 IO 和 CPU 方面的问题,但这些超出本文的范围了。

有人提醒我,使用 Go 和 Java 的 64 位整型只能准确计算出 92 的 Fibonacci 数值,再往后会出现溢出(译者:所以代码后来改成了计算 90 的 Fibonacci 数值)。但即使是这样,本文的结论仍然是有效的。

]]>
嘘,这是手淘双11 GMV 暴涨的秘密-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 作者 | 莫凌、桑杨、明依

背景

现状与解决方案

手淘上以列表推荐形式为主的业务场景有不少,以手淘信息流为例,进入猜你喜欢场景的用户,兴趣点常常是不明确的,用户浏览时往往没有明确的商品需求,而是在逛的过程中逐渐去发现想买的商品。而推荐系统在用户逛到买的过程中,往往会下发并呈现不同类型商品让用户从中挑选,推荐系统这个过程中会去捕捉用户的兴趣变化,从而推荐出更符合用户兴趣的商品。然而推荐系统能不能做到用户兴趣变化时立刻给出响应呢?

推荐系统以往的做法都是通过客户端请求后触发云端商品排序,然后将排序好的商品下发给用户,端侧再依次做商品呈现。这样存在下面两个问题:

云端推荐系统对终端用户推荐内容调整机会少,往往都在分页请求时,而简单请求并不能灵活做内容的增删改。
云端推荐系统不能及时获取到用户当前时刻的偏好意图,快速给出反馈。

我们总结发现,目前推荐系统的弊端是,用户偏好的变化与推荐系统对用户感知和对内容的调整时机并不能匹配,会出现推荐的内容并非用户当前时刻想要的,用户浏览和点击意愿都会下降。那么怎样能够让推荐系统及时感知到用户偏好并及时的给出用户想要的内容呢?

我们先透过现象看本质,以上问题的本质在于推荐系统和用户交互过程中的实时性差,以及决策系统可调整性差。实时性差体现在两个方面,推荐系统对终端用户的感知实时性差以及对用户的干预实时性差。而决策系统可调整性差,体现在决策系统对用户内容的调整时机依赖端侧的固定规则请求,可调整的内容局限于当前次下发的内容。如果我们能够解决实时性问题,推荐系统能够实时感知用户偏好,并在任何时机实时调整用户所见内容,推荐的内容可以更符合用户当前的偏好;如果我们能够解决决策系统可调整性差问题,推荐系统可以决定合适的时机去调整用户内容,可以决定用更优的方式去调整具体的内容。那么解决的方案是什么呢?

1201.jpg

我们在手淘信息流中引入机器学习和深度神经网络模型,结合端侧用户特征,在端侧持续感知用户意图,实时决策并实时反馈结果给用户,这样解决了实时性差以及决策系统可调整性差的问题。我们把这个解决方案称之为端智能。

端智能带来的改变

端智能的本质是“端”+“智能”。首先“智能”不是一个新鲜的东西,“智能”不管是在云端或终端,解决的问题是通过机器学习数据的内在机制并推理出最终结论;“端”解决的问题是将”智能“工程化并落地到具体的应用场景,“端”有机的整合端侧数据以及云端下发内容,决定何时触发“智能”做决策,最终决定怎样给用户以反馈。

端智能带来的改变,则是让端上具备了“独立思考”的能力,这让部分决策和计算不再依赖于云端,端侧可以更实时、更有策略的给出结果。说到实时性,5G时代的到来,其低时延特性极大的降低了端和云的交互时间,但这并不影响我们利用端智能实现更低成本的决策和快速响应,反而对于端智能来说,好处是能和云端结合的更紧密。另外由于在端侧能够秒级感知用户意图做出决策,产品和用户贴的更近了,这催生了更多实时性的玩法,产品将不再局限于要到固定的时机如分页请求让云端去给到新的内容反馈,而是思考,当用户表达出来特定的用户意图时,产品应该如何提供与意图相匹配的内容。

端智能与传统差异比较

尽管端智能带来了很多好的改变,但这里依然需要强调一点,并不是说有了端智能就不再需要云智能,怎样做到云&端协同智能才是未来。

端智能的优势在于:

端侧有着丰富的用户特征和触点,有着更多的机会和条件去做决策
实时性高,在端侧处理可节省数据的网络传输时间,节省的时间可用于更快的反馈结果
节省资源,在端侧处理,利用端侧算力和存储空间,可以节省大量的云端计算和存储资源
隐私性好,从产生数据到消费数据都在端侧完成,避免传输到云端引起隐私泄露风险

端智能的不足在于:

设备资源有限,端侧的算力、电力、存储是有限的,不能做大规模高强度的持续计算。
算法规模小,端侧算力小,而且单用户的数据,在算法上并不能做到最优
用户数据有限,端侧数据并不适合长期大量存储,端侧可用数据有限

云智能的优势在于:

大数据,云端可以通过长期大量的来自不同人群的数据进行计算
设备资源充足,云计算的算力、电力、存储都可以根据需求进行配置
算法规模大,可以通过足够的大规模模型,计算出最优解

云智能不足在于:

响应速度慢,受传输带宽影响,不能稳定提供较高的响应速度
用户感知弱,端侧产生的数据同步到云端,数据量限制和传输时间的约束都会削弱云端对用户的感知

从以上云智能和端智能的对比可以看出,端智能适合于依赖端侧用户触点的小规模低时延的计算,而云智能更适合中长期数据大规模计算。同时,端智能往往需要云端提供的长期特征及内容,而云智能也往往需要端上的特征和丰富的触发点,两者优势互补,才能发挥出更好的效果。

端智能基础设施建设

高楼起于平地,打造端智能这幢摩天大楼需要很多基础设施,剥除各种各样边角料和锦上添花的东西后,我们认为构成支撑起端智能体系的骨架组成部分主要有数据、端计算、端计算引擎、端智能决策框架、算法研发平台。其中,端侧数据 、端计算、端计算引擎这三块的作用是实时感知用户,计算出贴合用户的结果;端智能决策框架是触达用户的通道,通过端上实时智能决策衔接用户意图和端计算,最后通过一定的干预手段展现到用户眼前;算法研发平台是开发过程主要接触的平台,能有效提升研发效率。通过一个简单的示意图也许能更好的理解这五大块:

1202.jpg

数据-BehaviX

无论计算是发生在云端还是终端,数据始终是执行所有计算的基本要素之一,端计算的本质也是计算,数据当然也是他的要素之一。在淘宝或者其他阿里系App里我们已经有很多数据沉淀,这些数据包括但不限于商品、商品特征、用户特征等。这些数据同样可以作为端计算的输入来源,但如果只有这些,端计算和云计算相比在数据上似乎没有什么明显优势了,所以我们需要回过头看下端计算作为端智能的重要部分,他的在数据上的核心优势是什么?端计算运行在端上,天然能获取端上的数据,而且是实时获取。我们希望这部分数据是和已有数据是互补的、对端计算是有价值的,端计算的目的之一是千人千面,端上丰富的用户特征,能体现当前用户的实时意图。所以我们在构建了端侧用户特征数据中心BehaviX。

BehaviX作为整个端智能的数据基础,提供给算法特征数据作为模型数据输入源,支持了特征实时同步云端,让云端能够秒级感知到端侧用户特征,提供了算法基于端侧用户特征数据做意图分析的能力。

端智能决策框架-BehaviR

从用户角度来看,用户感知到的不是一堆数据和计算而是能够被感知到的结果,因此,即使计算出来的结果无比贴合用户意图,如果无法及时触达用户也是无用功。触达用户方式多种多样,我们需要基于实际场景放开手大胆探索,合理的产品设计会让用户觉得是在和一个“智能”的App交流,反之,不合理的产品设计会打扰用户、对用户造成困扰。从技术角度来说,我们要设计和做的其实是一条触达通道,通过感知用户触点,我们能根据运营规则配置或者本地模型决策出此时要给用户什么类型的反馈,然后通过下面要讲述的端计算能力计算出贴合用户的结果并展示给用户,以此将端计算和用户连接在一起。

端智能决策框架能简化业务方接入端智能流程,帮助业务方真正做到实时感知、及时干预。

端计算-EdgeRec

端计算简单理解起来可以认为是跑在端上的一段逻辑,这段逻辑可以是一个预置的Native任务,也可以是一个脚本,当然,在最终我们希望他是一个算法模型。算法模型是目前做到千人千面的有效手段之一,其他优势不再累述了,详见下面的友情链接。

回到这里的主角EdgeRec-边缘计算算法,他在在端上实时建模了用户的异构特征序列,为端上决策提供通用的用户状态表达。通过多任务学习,共享通用的用户状态表达,在端上建模多种决策模型。另外,边缘计算算法SDK也提供端上深度学习算法开发的通用解决方案,如:端上深度学习模型库、端上模型拆分部署、端上模型版本控制、端上样本生成等。

端计算引擎-Walle&MNN

端计算引擎是端智能体系中重要的一环,是算法模型的基础环境。无论是iOS还是Android目前都提供了一套环境,但两端差异性比较大,限制也比较多。构建一套端计算引擎的成本是非常高的,但长远来看统一两端引擎、抹平差异是有非常有必要的。Walle和MNN作为当前我们端计算引擎很好地做到了这一点。

Walle是端上整体的Runtime,他为算法的Python脚本、深度模型以及Jarvis的EFC、ESC等特征样本计算库提供运行环境,另外也为BehaviX管理的基础数据提供存储服务。

MNN 是一个轻量级的深度学习端侧推理引擎,核心解决深度神经网络模型在端侧推理运行问题,涵盖深度神经网络模型的优化、转换和推理,其前身为 AliNN。

算法研发平台:Jarvis

算法模型的研发并不是简单地在本地IDE写一份代码那么简单,我们通常需要理论调研、算法开发、模型训练、参数调优、线上验证等等步骤,本地环境是远远不够的,所以算法研发平台的存在能帮助算法同学更高效、更专注地进行研发工作。另外,端智能要出结果,一定是多团队通力合作的结果,多团队合作仅靠口头沟通是远远不够的,我们需要一套合理的流程去简化和规范各项工作,因此,在算法研发平台的基础之上我们仍旧需要一个一站式平台。

Jarvis提供一站式的开发、调试、验证、AB测试、发布、监控平台,与算法同学共建一起打造了端上的特征计算、样本计算等基础库。

整体流程图

我们构建了端智能的五个基础设施,通过端上调度系统,将整个端智能技术体系串联起来,总体来说分为用户触达和用户感知部分。用户触达部分包括端上调度和端上决策,端上调度提供和业务的直接对接,端上决策由端上调度系统在合适的时候拉起本地算法计算;用户感知部分则对用户特征进行标准化端上用户特征,提供端侧计算的数据输入。

1203.jpg

数据效果

从年初信息流端智能立项以来,我们经过最开始的小流量实验,效果逐渐优化,大半年的不断探索试错,信息流端智能于9月中旬在首页猜你喜欢场景全量。双十一当天也取得了不错的业务效果,对商品推荐的准确度提升,信息流GMV和点击量都大幅提升。其实这只是信息流在端智能的开始,相信后面更深入的优化探索,我们将会取到更好的效果。

总结

从我们以往的经验来看,端侧做的更多的是将云端内容以具体的形式呈现给用户。当端侧也具备了感知用户意图并智能做出决策时,端侧的能力就不再局限于“呈现”,端侧也可以”思考“。业务可以利用端侧”思考“能力,将以往在云端解决起来比较困难的问题放到端上去解决,如云端决策实时性问题、大数据量上报云端分析的资源消耗问题;可以结合端侧本身的特性,如传感器、相机、UI呈现等,去思考如何去整合用户特征、数据、端侧算法去大胆尝试找到新的突破口。

]]>
谁说Redis数据必须全部存储到内存?Redis混合存储实例看过来!-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800

Redis 混合存储实例是阿里云自主研发的兼容Redis协议和特性的云数据库产品,混合存储实例突破 Redis 数据必须全部存储到内存的限制,使用磁盘存储全量数据,并将热数据缓存到内存,实现访问性能与存储成本的完美平衡。

作者:张友东,阿里云数据库高级技术专家

架构及特性

image.png

命令兼容

混合存储兼容绝大多数 Redis 命令,与原生 Redis 相比,如下命令不支持或受限制;不支持的主要原因是考虑到性能,如业务中有使用到,请提交工单。

image.png

选型指南 - 场景
image.png

选型指南 - 规格
选择混合存储实例时,需要选择合适的【内存配置 + 磁盘配置】;磁盘决定能存储的数据总量,内存决定能存储的热数据总量,实例生产时会根据存储的规格配置选择合适的CPU资源配置,目前暂不支持自定义CPU核数。

比如【64GB内存 + 256GB磁盘】实例,意思是实例最多能存储 256GB 的数据(以KV存储引擎的物理文件总大小为准),其中 64GB 数据可以缓存在内存。

内存选型建议:Redis 混合存储为保证最大程度的兼容 redis 原生访问协议,要求所有的key必须常驻内存,value 可以根据冷热读来自动决定存储在内存还是磁盘,所以内存空间必须要足以存储所有的key、以及对应的元信息。

image.png

磁盘选型建议:因 Redis 数据存储到 KV 存储引擎,每个key都会额外元数据信息,存储空间占用会有一定的放大,建议在磁盘空间选择上,留有适当余量,按实际存储需求的 1.2 - 1.5倍预估。

案例1:用户A使用Redis Cluster 存储了 100GB 的数据,总的访问QPS不到2W,其中80%的数据都很少访问到。用户A 可以使用 【32GB内存 + 128GB磁盘】 混合存储实例,节省了近 70GB 的内存存储,存储成本下降50%+。

案例2:用户B在IDC自建 Pika/SSDB 实例,解决Redis存储成本高的问题,存储了约400GB的数据,其中活跃访问的在10%左右,集群运维负担很重,想迁移至云数据库;用户B可以使用【64GB内存 + 512GB磁盘】混合存储实例,来保证免运维的同时,服务质量不下降。

性能指标

Redis 混合存储的性能与内存磁盘配比,以及业务的访问高度相关;根据规格配置及业务访问模式的不同,简单 set/get 的性能可在几千到数万之间波动。最好情况所有的访问都内存命中,性能与 Redis 内存版基本一致;最差情况所有的访问都需要从磁盘读取。

测试场景:2000w key,value大小为1KB,25%的热key能存储在内存,get 请求测试数据如下:

image.png

应用场景

  • 视频直播类

视频直播类业务往往存在大量热点数据,大部分的请求都来自于热门的直播间。使用 Redis 混合存储型实例,内存中保留热门直播间的数据,不活跃的直播间数据被自动存储到磁盘上,可以达到对有限内存的最佳利用效果。

  • 电商类

电商类应用有大量的商品数据,新上架的商品会被频繁访问,而较老的商品访问热度不高;使用 Redis 混合存储型实例,可以轻松突破内存容量限制,将大量的商品数据存储到磁盘,在正常业务请求中,活跃的商品数据会逐步缓存在内存中,以最低的成本满足业务需求。

  • 在线教育类

在线教育类的场景,有大量的课程、题库、师生交流信息等数据,通常只有热门课程、最新题库题库会被频繁访问;使用 Redis 混合存储型,将大量的课程信息存储到磁盘,活跃的课程、题库信息会换入到内存并常驻内存,保证高频访问数据的性能,实现性能与存储成本的平衡。

  • 其他场景

其他数据访问有明显冷热特性,对性能要求不高的场景均可使用Redis混合存储来降低存储成本。

常见问题

磁盘还有剩余空间,但内存先满了,导致写入报错 OOM error

  • 内存规格太小,导致内存空间不足以容纳所有key及其元数据信息,建议在控制台升级实例规格即可,增大实例内存。
  • key对应的value比较小,混合存储对于比较小的value(比如小于20byte),不会触发换出换出到磁盘,因为小的value换出到磁盘,在内存里还是会存储一些meta信息,最终导致换出到磁盘并不能腾出内存空间;这个问题混合存储内核在持续优化,尽量适应更多的应用场景。

云数据库 Redis 版

一种稳定可靠、性能卓越、可弹性伸缩的数据库服务。基于飞天分布式系统和全SSD盘高性能存储,支持主备版和集群版两套高可用架构。

]]>
打印九九乘法表 | python从入门到精通:入门篇之十八-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 第十七节:循环嵌套打印三角形星星阵

循环嵌套练习题

18.1 打印九九乘法表

拿到题目先别着急答题,先来分析一下这道题到底要运用哪些“知识点”!
观察一下,这个九九乘法表是不是和上节课的“三角形”阵是一样的框架呢,把每个公式当成一个整体,那就是一个九行的“三角形”阵,图形的高度确定了,那么外层的循环也就确定了。
现在来看内层的循环,也就是图形的宽度怎么写,先将其当作是三角形来打印一次:

i=0
while i<9:
    j=0
    while j<i+1:
        print("* ",end=' ')
        j += 1
    print()
    i += 1

执行结果为:
image.png
显然方向是对的,那么接下来将*换成公式:
image.png
第一行的内容是0开始的,是因为i和j的初始值为0,所以将i+=1j+=1放到循环体的前面来,先将i和j自增:
image.png
图形的宽度比之前多了一个1。在上一步骤里,先将i和j进行了自增,在内层循环里面的时候仍然是j<i+1,所以当外层循环执行一次,内层函数执行了一圈+1,才会导致这种情况发生,此时只需要将j<i+1改成j<i就好啦:
image.png
果然乘法表被大家找出来啦!可以自己多尝试几次,理解一下这个思考的过程。

18.2 找出100以内的所有质数

在之前的课程中咱们讲过如何判断一个数n为质数,首先引入了一个flag=True,然后用小于n的数去整除n,如果出现n%i==0,则证明可以整除,则不是质数。再返回来看这个问题,现在不是要求判断一个数字了,而是判断100以内的所有数字,那怎么能让程序判断完了一个数字之后再自动去执行下一个数字的判断呢,这就要用到循环了。首先搭建一个循环的框架,输出2~100的数字(从2开始是因为0不做判断,1既不是质数也不是合数):

i=2
while i<=100 :
    print(i,' ',end=' ')
    i+=1

执行结果为:
image.png
后面的数字就不展示了。
接下来就要去挨个判断这些数字是不是质数了,当判断是质数的时候再进行print输出。首先还是要引入一个flag,并且值为True,当用小于n的数去整除n,如果出现n%i==0,则不是质数,flag的值改为False,程序如下:

i=2
while i<=100 :
    flag = True
    j=2
    while j<i:
        if i%j==0:
            flag=False
        j+=1
    if flag :
        print(i)
    i+=1

执行结果为:
image.png
可以看到,所有的质数就被打印出来啦!

]]>
第一届阿里云数据可视化峰会圆满落幕-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 12月23日,2019数据可视化年度峰会在阿里巴巴西溪园区白马山庄举行,峰会以“唤醒数据,看见未来”为主题,邀请著名专家学者及一线行业实践者结合自身实践与感悟,与现场业界来宾分享主题演讲。百余位业界KOL齐聚一堂,深入解读数据可视化研发生态,剖析可视化商业化应用的前景和机会,共同探讨了如何创建创新共赢的数据可视化环境。

现场图1.jpg

(现场的气氛可谓是热火朝天啊!还有站在后面的小伙伴,可谓是一票难求)

没有抢到票的小伙伴看看会上都有哪些看点吧!

前瞻数据可视化学术方向与技术构建

中国科技创新2030“新一代人工智能”和“大数据”专项均将可视化和可视分析列为大数据智能急需突破的关键共性技术,可见国家对可视化领域的重视,以及可视化未来蓬勃的机会。目前数据可视化的产品以轻量级小数据可视化工具为主,产品底层技术特性与国外同类产品存在一定差距,所以目前重视大数据行业中可视化生态系统的培育以及基础理论与方法研究还是非常必要的。

同济大学主任曹楠教授

在《针对事件序列数据的可视化及应用》的演讲中,曹楠教授认为对大规模事件序列数据的可视分析与预测能够清晰的揭示用户行为的内在规律与因果关系,在网络安全、电子商务、智慧交通以及精准医疗等诸多领域拥有广泛的应用价值。

lADPDgQ9rYdWvtzNDS_NF3A_6000_3375.jpg

浙江大学博士梅鸿辉

梅鸿辉博士为我们分享了数据可视化的展望与趋势,其中包括中国可视化发展在国际上崛起的历史、可视化学术研发的方向、以及“可视化+”的概念等等。大数据的数据获取、数据清洗、数据模型、数据分析、预测仿真这些环节中都能与可视化融合,形成了可视化+的概念。更提到,数据可视化的研发趋势需要从小数据扩展到大数据,从少数专家扩展到广泛的不特定群体,在实际应用中强调方法的性能、使用的简捷和系统的智能。

阿里云数据可视化团队技术负责人宁朗

宁朗是阿里最早跟随团队将数据可视化真正落地到数据产品,并推动可视化技术能力对外输出的。他在会上中指出大数据可视化不仅仅是数据大屏,强调大数据大屏完成之前的数据收集和清洗非常关键,需要打破数据链融通之间的隔阂。他认为,数据可视化要有行业开发规范、把握创新边界,还要考虑呈现数据对象的感受,而不是盲目的开发。而通过可视化工具,可以响应可视化的快速变动。

宁朗.jpg

洞察可视化生态机会点及DataV规划

随着物联网、云计算、移动互联网等技术的突破,更多的数据得到收集,同时也推动了数据可视化一直不断发展,目前金融、交通运输、生产领域、医疗卫生等行业更是对数据可视化愈发显示刚性需求,正在推进可视化发展的上升拐点。
可视化的终极目标是洞悉蕴含在数据中的现象和规律,从而帮助用户高效而准确的进行决策,所以可视化生态发展的机会点也是业界一直所关注的。

ECharts可视分析负责人李德清

在会上详细地介绍了E charts在可视化中的基建,它基于 Javascript 的数据可视化图表库,可提供直观、生动、可交互、可个性化定制的数据可视化图表。

李得清.jpg

数据城市派的CEO派姐

数据可视化工具可以帮助企业降低数据可视化的成本,使杂乱、大量的数据的可读性得到提高,让企业可以在数据中找到规律,行业上有许多数据可视化案例可以借鉴。目前在实际应用中数据可视化也具有非常广泛的应用场景。作为专业的大数据服务商,派姐对数据可视化的应用场景有深刻的理解,在演讲中以“数据可视化的应用场景”为主题分享了自己的见解。派姐首先回顾了城市规划可视化的发展历程,总结了大数据时代下,城市规划师利用数据可视化的各类场景,包括前期分析、现状分析、辅助设计、辅助决策、辅助服务与管理、数字孪生、感知未来等多种场景。对城市规划圈的数据可视化应用与发展提出了一些希望与建议。

派姐.jpg

阿里云产品专家央久

作为最早进入可视化领域产品之一的DataV,从双11出发,经历过开源、产品化、打包解决方案、再到平台化,数据大屏应用场景也从双 11 电商作战,扩展到智慧城市、智慧交通等诸多领域,所以DataV未来的规划也是备受关注。央久在会上表示DataV未来要做专业化、行业化、智能化的可视化开发工具平台, 能完整地帮助数据可视化从管理到投放,从单点功能向全面化智能发展。

央九1.jpg

大屏显示领域资深技术专家董航程

董航程在《数据可视化LED屏的行业趋势》的演讲中指出目前人性化的设计对工业设计是非常重要的,DataV+优秀的大屏硬件才是未来更好的可视化软硬一体的解决方案。

董航程.jpg

阿里云MVP陈琦

陈琦在现场与大家分享了《数据可视化工作流进化史》。陈琦的团队依托阿里云强大的产品支撑能力,为中央宣传部、国家电网、上海申通地铁等大型国企提供专业的数据可视化定制解决方案。他认为数据可视化的大屏要做数据科学的艺术品,优秀的数据可视化大屏要让观众找到共鸣、找到规律。

陈琦.jpg

此次峰会我们一起探讨了数据可视化的学术动态,了解了数据可视化的基建和技术构建,以及对应用场景的探讨和更多的对未来的期待。相信未来数据可视化会以更细化的形式表达数据,以更全面的维度理解数据,以更美的方式呈现数据,使可视化更加具有冲击力。

]]>
2019年美国政府开展了哪些前沿AI研究项目|全球快讯-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 2019年,美国政府划拨的科研经费为1400亿美元。在众多政府研究项目中,美国国防高级研究计划局(DARPA)和情报高级研究计划局(IARPA)开展的往往最具创新性,例如DARPA此前的研究为互联网、GPS等我们今天熟知的技术奠定了基础。

现在,这两个机构仍然在进行着类似的开创性研究,努力将今天的科技推向一个新的高度。了解这些研究项目有助于我们了解未来科技行业的发展方向。

DARPA

去年,DARPA宣布将投资约20亿美元,带来所谓的“第三波”人工智能浪潮——具有推理能力、能够像人一样交流的系统。现在,这些项目已经在开展中。

今年3月,DARPA开始探索如何让Siri和Alexa等AI系统更好地自学语言。研究员们本质上希望这些系统能够像人类婴儿一样,通过观察周围世界来学习语言,而不是费力地通过庞大的数据集来学习。在这个项目中,研究人员们让AI系统学会将视觉线索(照片、视频和现场演示)与它们听到的声音相关联,最终目标是开发出一种能够真正理解自己所讲内容含义的AI系统。

DARPA还希望AI工具能够评估自己的专业知识,在自己不懂的时候通知操作员。今年2月,该机构启动了“能力感知机器学习”项目,旨在让AI系统对自己的行为进行建模,评估过去的错误,然后将这些信息应用到未来决策中。如果这些AI系统认为自己给出的结果可能不准确,它们会告知用户。

开发AI的最大障碍之一是它们的运行需要巨大的计算能力。为了减少这方面的阻碍,DARPA推出了MicroBRAIN项目,目前正在研究“非常小的飞行昆虫”的大脑,以获取灵感,设计出更节能的AI。

除了改进AI技术本身,DARPA还在利用这项技术解决政府当前面临的一些最紧迫的问题,如教计算机自动检测深度伪造(deepfake)中的错误。官员们也在投资AI,利用这项技术设计更安全的武器系统、车辆和其他网络连接平台。

除了AI,DARPA正在开发的其他前沿技术包括卫星修复机器人、地下自动测绘技术等。

IARPA

AI在国家安全领域有许多应用场景,在2019年,加强监视是IARPA的一个主要目标。

今年4月,这家机构宣布将尝试让AI无缝整合和分析收集自飞机、无人机和其他飞行器的卫星图像和素材。这个名为“基于空间的机器自动识别技术”的项目本质上是希望使用AI来实时监控全球所有的人类活动。这项技术将整合来自多个来源的数据,自动检测和监视地球上的主要建设项目和其他“人为活动”,密切关注各个地方的变化。

IARPA还在利用AI来更好地监控人类的地面活动。今年5月,该机构开始招募团队培训算法,以跟踪人的行踪。具体而言,AI将把安全摄像机拍摄的内容整合在一起,让各机构在拥挤的人群中跟踪个人的行踪。

将这项技术与远程生物特征识别系统(IARPA也从2019年开始探索这项技术)结合在一起,你可以让机器来识别个人身份并跟踪其行踪,你自己不需要动一根手指头。

转自创头条,原文链接:http://www.ctoutiao.com/2573620.html

]]>
break和continue | Python从入门到精通:入门篇之十九-阿里云开发者社区 Mon, 27 Jan 2020 20:58:34 +0800 第十八节:用循环嵌套语句打印九九乘法表

循环终止

19.1 break

break可以用来终止循环,立即退出循环语句(包括else)。
先来看一个示例:创建一个5次的循环。

i=0
while i < 5 :
    print(i)
    i += 1
else :
    print('循环结束')

执行结果为:
image.png
接下来我们为该循环语句加上break,再来看一下效果:

i=0
while i < 5 :
    if i == 3 :
        break
    print(i)
    i += 1
else :
    print('循环结束')

执行结果为:
image.png
只显示了‘0’、‘1’、‘2’,因为当i=3的时候,循环遇到了break被结束了,包括else后面的语句也一起不执行。

19.2 continue

continue用来跳过当次循环。
还是用上面的例子:创建一个5次的循环。

i=0
while i < 5 :
    print(i)
    i += 1
else :
    print('循环结束')

执行结果为:
image.png
接下来我们为程序加上continue语句:

i=0
while i < 5 :
    if i == 3 :
        continue 
    print(i)
    i += 1
else :
    print('循环结束')

执行结果为:
image.png
这个结果和我们想象中有点不一样,continue终止的应该是本次循环,但是后面的4也不出现了。检查下程序哪里出了问题,会发现是continue语句写在了上面,后面的print语句和i自增的语句默认为continue的当次循环语句,所以也不再执行了,我们将循环更新的条件放到上面。

i=0
while i < 5 :
    i += 1
    if i == 3 :
        continue
    print(i)
else :
    print('循环结束')

执行结果为:
image.png
当i=3的时候,循环语句跳过了,直接执行了下次循环。
将continue改成break再看下:

i=0
while i < 5 :
    i += 1
    if i == 3 :
        break 
    print(i)
else :
    print('循环结束')

执行结果为:
image.png
结束了整个循环。
综上所述:break用来终止整个循环,包括else语句,而continue用来终止当次循环,程序会跳过当次循环,进入下一次。注意:如果有多层循环嵌套,两者都是只对离它最近的循环体起作用。

]]>
创业明星|宜创科技创始人宜博:一个不想改变世界的程序员不是一个好的创业者 Mon, 27 Jan 2020 20:58:34 +0800

不想错过16年前的淘宝
7年前的天猫
阿里云发起了“赛道明星班”
挖掘和帮助更多的优秀科技型创企
为企业赋能、为创新加速
做云生态的创企加速器
本期人物是赛道明星班64号学员---宜博

IT技术支撑了全球信息化浪潮,然而软件开发效率却难以像摩尔定律一样快速提升,以至于成为瓶颈。近几年,低代码领域发展迅速,赛道跑出了超10亿美元估值的独角兽OutSystems,巨头企业AWS、Google、Microsoft、Oracle、西门子等也纷纷推出低代码开发平台或通过收购布局低代码。

低代码开发平台,是指那些无需编码或通过少量代码就可以快速生成应用程序的工具,其一方面可以降低企业应用开发人力成本,另一方面可以将原有数月甚至数年的开发时间成倍缩短,从而帮助企业实现降本增效、灵活迭代的价值。

在美国,这一领域已经产生了多个独角兽和准独角兽级别的公司,如Outsystem和Skuid,市场接受程度已经很高。据不完全预估,目前中国的低代码应用开发的渗透率不到5%,而美国的渗透率至少在40%以上,预计到2020年达到75%。中国的低代码平台仍有巨大的发展潜力。

在阿里云创新中心平台上,也孵化出了一批低代码创业公司,宜创科技就是其中一家。初见创始人宜博,是在今年1月份“赛道明星班”北京面试现场,高高的个子,身着程序猿式慵懒T-shirt,话不多,显得格外斯文。接触了近一年后,原来是一个内心有“火”的大男孩。

image.png

心中有火 眼里有光的程序员

宜博,自小便对科技充满了好奇与热情,成长过程中,对电脑编程更是有着格外浓厚的兴趣。1997年,在那个电脑远远没有普及之时,正在念初三的宜博就已经开始学习C语言编程。那个时候,宜博心中最崇拜的人莫过于比尔盖茨,“这个人是个传奇,靠卖软件改变世界,开拓一个新时代”。宜博也希望自己能在新的虚拟世界里做出成绩,实现人生价值。

毕业初期,宜博在计算机行业和编程领域不断拼搏,但逐渐他发现,仅凭写代码是很难真正改变世界的;他开始跳出来审视自己,一边攻读清华经管MBA一边努力在销售、管理等多个商业领域做尝试和探索,逐步实现了从单一的程序员思维到立体的商业思维的转变。

应用产品达千万级日活

宜创科技致力于机器替代人编程,基于自主知识产权的可视化编程语言提升10倍开发效率,hpaPaaS实现企业数字化随需定制。无代码提供的服务由底层可视化编程语言hex,部署云平台hexyun,软件模板市场wudaima,SaaS移动平台企业内外组成。

无代码”开发解决效率问题,”APaaS“则提供定制化的能力。“无代码APaaS”基于“原子级”对编程语言的可视化封装技术,实现所见即所得的开发方式,由机器自动编译出高质量代码,并自动部署到云端,相比于传统的前端或APP开发方式提升了10倍效率。

但“原子级”的产品对终端客户仍然有一定学习成本,且复用能力较弱,因此,宜创科技又开发了对编代码编程要求更低的“分子级”产品。这是一个全流程可视化模板开发平台,具备软件全流程线上开发的能力和付费模板市场。使用这个开发平台的客户只要按照线上标准流程梳理完产品需求和UI/UE设计,平台就自动完成了50%以上的架构搭建和开发工作。这种边开会边开发的模式,减少了传统开发过程中需求设计与开发测试之间信息衰减带来的效率磨损。

除了提升定制化开发的效率,无代码APaaS平台的价值还在于节省成本。在公司以往的服务案例中,原本30个人3个月的工作量,在使用无代码APaaS平台后,只需要由15个人工作1个月的就可以完成,有效降低工期和开发成本。

image.png

客户方面,宜博表示,低代码平台的采购方不仅是SaaS厂商以及终端企业客户,大企业内部业务部门和开发者也是宜创科技的服务对象。无代码APaaS的服务包括线上的开发云平台和线下的私有化APaaS定制化两种模式。2018年,公司为其标杆客户(包括阿里巴巴集团,我爱我家集团,清华大学等)提供包括私有化的APaaS平台及软件开发服务等多种服务,项目金额在几万到几千万不等,部分标杆客户的应用产品达到了千万级别的日活,这也进一步验证了公司产品在代码优化和云端部署方面的质量,稳定性和安全性。

未来,随着产品技术的不断完善与进步,宜创无代码将在互联网门户,金融,O2O,电商,移动IM,办公协同,移动社交,外卖招聘等多个移动及产业互联网等领域有更广泛的应用空间。

宜博告诉我们:宜创无代码的使命是,无代码技术赋能每一个企业数字化;愿景是,让业务专注于业务,让程序员腾出时间做更有意义的事情。宜创无代码一直以来秉承的理念是:a.快速创新:低试错成本;b.快速交付:低项目成本。

赛道明星班,资源的加持

在今年的1月份,宜博通过了“赛道明星班”第二期的面试(学号:64)。通过赛道明星”,宜博结识了各个赛道的精英创始人,与阿里巴巴集团的各条业务线及知名投资机构等产生了深度链接。自今年入班以来,宜创已经成为阿里云IoT、钉钉、阿里云创业中心等多个阿里部门的合作伙伴,目前还正在协同阿里集团技术公益,支持更多公益项目开发。

image.png

2019年6-8月中,宜创科技拿下了1家投资机构的TS,12家机构的NDA,见了9位合伙人级别的投资人,25位投资经理,收获了36氪的全方位媒体曝光。在营收方面,仅目前已上线的AIoT平台,第一个月就实现了200万的收益,营收进一步取得突破。

“加入赛道明星班,让我们直接与朱啸虎、黄明明等一线投资人沟通并建立私人联系,还有首任阿里巴巴铁军校长李立恒等强大顾问团队的加持,项目少走了很多弯路。”宜博在此前接受媒体采访时说。

自赛道明星班启动以来,已成功举办三期,共聚集100位优秀的企业创始人,覆盖AIoT、新零售、新金融、企业服务、移动办公、新科技、机器人、医疗、新能源、智慧教育等多个领域,深入14条行业细分赛道。

到目前为止,学员已与阿里达摩院、阿里云智能、AIoT、钉钉、蚂蚁金服、阿里平头哥、淘宝、天猫等60多条业务线建立链接,产生1000+业务合作,累计130+媒体报道、1500W+阅读量,近200家资本参与赛道明星班项目,达成56笔投资,估值增长263%,已达到718亿。

image.png

据悉,“赛道明星班”第四期已经启动全球招募,扫描下方二维码,获取面试机会!

image.png

原文链接:https://mp.weixin.qq.com/s/CHwaTSpSG6R8Q0yR4ePBMQ

]]>
Zookeeper学习分享 Mon, 27 Jan 2020 20:58:34 +0800 1. Zookeeper简介

Zookeeper是hadoop的分布式协调服务,适用与服务部署管理,是从传统的互联网架构演化而来。传统单体应用系统的耦合度非常高、启动应用时间长、依赖庞大等,zookeeper架构相对传统架构更加简单,更加可靠。

下图为zookeeper架构的角色分布图:

image.png

介绍:

  1. Leader:领导者,负责发起决议,如果有client发送请求到某个server,会由leader进行选举决议,如果过半的server同意则开始执行请求,如果leader出现宕机,follower会选举出新的leader。
  2. Follower:跟随者,接收client请求,只能单独处理读请求,如果存在写请求则将请求上报leader,当leader发起决议时参与决议,且执行。
  3. Observer:观察者,没有选举权的follower,为了提高整体读性能,因为参与选举比较耗时。

2. Zookeeper在测试中的应用

Zookeeper在测试应用中的优势

对于日渐庞大的测试体系,测试人员无法很好的对设备进行管理,自动化测试使用频率越来越高,导致设备“忙不过来”,执行测试任务时以及开发人员调试时需要花费大量的时间搭建测试、开发环境,花费精力去维护环境。但是有了zookeeper架构,一切就变得简单了。

列出以下几点使用优势:

  1. 测试、开发人员更加快速的进行测试及调试节
  2. 稳定性测试过程中能够更加实时的对设备监控
  3. 对于大型的自动化测试平台无法进行负载均衡,可以合理利用此框架进行资源调配
  4. 用户面向对象不再是硬件设备,只要提供硬件型号就能分配到所需设备
  5. 不用花费大量心思去维护设备。只要你请求,它就会合理分配给你,到资源不够分配再进行统一维护

实施思路

下图为基本应用框架:

image.png

根据需要了解设备特性,定制不同类型的代理服务器,统一在zookeeper server创建设备节点,对于用户而言看只要通过对zookeeper server请求对应型号的设备,server端就会把空闲的节点参数分配给用户,且把节点加锁。直到用户使用完毕发送解锁指令后server端把设备设置成空闲状态。

​ 下图为一个简单的运作流程图:

image.png

3. 环境搭建

​ 这里使用ubuntu系统来搭建环境

  1. 安装zookeeper

下载链接:http://archive.apache.org/dist/zookeeper/ 选择对应版本进行下载]解压,这里选择的是3.4.8版本

解压后的目录结构:

image.png

进入bin目录:

image.png

这里面有很多可执行文件,这里使用zkServer.sh启动zookeeper服务

image.png

这里没有配置端口默认端口为2181,以上就算是启动了一个zookeeper服务

  1. 使用client基本命令

开启服务后执行./zkCli.sh -server localhost:2181,进入client模式

4. zookeper的节点和使用

zookeeper节点介绍

zookeeper节点目录结构为树形结构

client可以创建节点以及节点的子节点,且能在节点中添加数据。下图为节点结构图:

image.png

Zookeeper cli使用

那么如何创建节点呢?

连接进入client后使用help命令查看client功能

image.png

基本功能介绍

create [-s] [-e] path data acl

-s :创建一个顺序节点

-e :创建一个临时节点;在于客户端断连时,临时节点会被删除,且临时节点没有子节点

data:节点数据

举例:create -s /test 1234

ls path [watch]

列出Path下对应的Znode

watch:能够监听Znode的的所有变化,可以不选

get path [watch]

获取Path对应的Znode的数据和属性

ls2 path [watch]

查看Path下所有子Znode以及子Znode的属性

set path data [version]

更新节点

delete path [version]

删除节点, 如果要删除的节点有子Znode则无法删除

rmr path

删除节点, 如果有子Znode则递归删除

5. Zookeeper实战

这里在一台PC中建立三个zookeeper sever,当然,在实际搭建集群时应该把集群搭建在不同的服务器中。

文件配置

​ 进入conf目录;能够 查看到zoo.cfg

​ 配置server.1、server.2、server.3

配置文件中端口的作用:clientport中2815为对client端提供服务的端口;2881为集群内及其通讯使用的端口;3881为集群选举leader时使用的端口

dataDir:服务本地数据储存目录

image.png

复制zoo.cfg为zoo2.cfg、zoo3.cfg 修改clientPort配置分别为2182、2183;修改dataDir分别为data2、data3

那么如何把server.x与实际服务器对应起来呢?

在dataDir目录我这边是/tmp/zookeeper/;在data、data2、data3目录中(如果没有此目录需新建)新建myid目录,此文件的作用为标注服务的唯一标识,分别对应config文件中server.x中的x;此步骤为关键步骤

操作:
mkdir /tmp/zookeeper/data;vim myid;
编辑文件内容为1;

​mkdir /tmp/zookeeper/data2;vim myid;

编辑文件内容为2;

mkdir /tmp/zookeeper/data3;vim myid;

编辑文件内容为3;

启动服务

进入bin目录,分别执行./zkServer.sh start ../conf/zoo.cfg;./zkServer.sh start ../conf/zoo2.cfg;./zkServer.sh start ../conf/zoo3.cfg开启三个服务

注意:如果没有关闭防火墙需要关闭防火墙才能开启服务systemctl stop firewalld.service

正确开启服务后我们使用./zkServer.sh status来查看服务状态

./zkServer.sh start ../conf/zoo.cfg

image.png

./zkServer.sh start ../conf/zoo2.cfg

image.png

./zkServer.sh start ../conf/zoo3.cfg

image.png

./zkServer.sh status ../conf/zoo.cfg

image.png

./zkServer.sh status ../conf/zoo2.cfg

image.png

./zkServer.sh status ../conf/zoo3.cfg

image.png

这里选举出server.2为leader,server.1和server.3为flower。

​ client与server.1连接,./zkCli.sh -server localhost:2185

​ 创建Znode, create -s /test 12345;会顺序创建一个id为0的node

image.png

​ get /test0000000000

image.png

查看到test node下的数据,此时,我们退出连接另一个server

./zkCli.sh -server localhost:2182;我们同样可以查看到node信息

image.png

在此服务中进行对test的监听get /test0000000000 watch

在server.1服务中修改test节点,此服务会跳出事件

image.png

6. 总结

通过对zookeeper架构的学习,发现它是一个既好上手又有很大实际用途的工具,我们需要学会如何合理利用好它,对于我们这种多项目多设备并行的测试人员来说,通过它来管理我们的设备,可以非常有效的节省我们的时间,消除了很多此类的烦恼。消除烦恼便能快乐工作。

原文作者:humm1
点击查看原文

]]>
K8Dash - 强大的k8s dashboard Mon, 27 Jan 2020 20:58:34 +0800 K8Dash是管理Kubernetes集群的最简单方法。为什么?

  • 全面的群集管理:命名空间,节点,窗格,副本集,部署,存储,RBAC等
  • 快速且始终如一的即时更新:无需刷新页面即可查看最新信息
  • 一目了然地快速可视化集群运行状况:实时图表可帮助快速跟踪性能不佳的资源
  • 易于CRUD和扩展:加上内联API文档,可以轻松了解每个字段的作用
  • 简单的OpenID集成:无需特殊代理
  • 安装简单:使用提供的yaml资源在不到1分钟的时间内启动K8Dash并运行(不严重)

依赖

  • 运行中的k8s集群
  • 安装metric-server(可以查看历史文章)
  • k8s集群为OpenId配置连接认证

安装

  • 部署
# 很久没更新了高版本需要改一下deployment的版本 apps/v1,端口改为nodeport
kubectl apply -f https://raw.githubusercontent.com/herbrandson/k8dash/master/kubernetes-k8dash.yaml
  • 确保pod和svc状态正常
kubectl get  -n kube-system deploy/k8dash svc/k8dash
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/k8dash   1/1     1            1           2m55s

NAME             TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/k8dash   NodePort   200.0.160.93   <none>        80:30354/TCP   4m17s
  • 生成token
kubectl create serviceaccount k8dash -n kube-system
kubectl create clusterrolebinding k8dash --clusterrole=cluster-admin --serviceaccount=kube-system:k8dash
kubectl get secret k8dash-token-kpt25 -n kube-system -o yaml | grep 'token:' | awk '{print $2}' | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6ImZ6UWpVcGVfUktkc0tfU0FLOFFlRnQ4QTJGR1JwRmZZNzJFWEZCUi1xTlUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Ims4ZGFzaC10b2tlbi1rcHQyNSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrOGRhc2giLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJkNjgxNDBlNi0zMWE2LTRhZDgtYmRlYy1jZGMwMDI0ZTFiY2IiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDprOGRhc2gifQ.sqYyMQPWeHwbaKEp-GahWJiPWSGETGMD-12sHIS08l2dXZEsv1zr8r_mWK56u7LHAnpEKeW8HtVZ-8VMpbYAyQdYBn_rqOpa81E0Gi7JsGTKCKuHJ4UB8fx6zGS4O397Pcn9iKxtQKjEo0JhnIfhDuZUC4yl0Fren60csBpHsGbUs6uSTH1n7BFL1Xk_Slzym9hZVnrrdyWlBXnHPo8xt7GvvbL7hMKJZ23Fk9HqNejjxcEUQMliMi25-rVkh8muO-n6uYoTdupMMwTpk34d8vTgq_XfuM95elCEMc2VWjGXYrRVkViIyomIzRHn_taQ-udRraWS-9_q6khjjWOd2g
  • 使用token访问k8dash


参考:

https://github.com/herbrandson/k8dashdash

欢迎关注公众号: 有点技术

]]>
PHP 依赖镜像出问题后,阿里工程师的一顿“神操作“令人叫绝! Mon, 27 Jan 2020 20:58:34 +0800 作者 | 顾咏

一则消息

前段时间,因为国际网络不稳定问题,国内各大Composer镜像都出现了间歇性无法访问情况,这对国内PHPer的生产工作造成了极大的影响。受此影响,国内各家Composer服务都出现了相同的问题,而阿里工程师的这个解决方案堪称“简单粗暴”,效率高到没朋友!

阿里云的 PHP Composer 最初研发灵感源自阿里内部一位 90 后工程师顾咏。作为负责开发阿里云产品的 PHP SDK的工程师,他在工作中经常遇到同一个问题:尽管已经根据PHP 最新版本发布了新的 SDK,但由于镜像工具没有实时同步版本,导致用户安装不成功。 此外,云效平台企业开发者对镜像工具的使用体验,同样受到这个问题的困扰,为此,阿里技术团队一起设计开发并开源了这套阿里云版镜像工具。

此次国际网络不稳定导致的镜像问题,阿里工程师顾咏第一时间响应了PHPer的诉求,连夜排查问题。 “我们程序员都离不开这个,越早解决越好”,最后终于成功定位问题、完成系统更新,解决了大家的燃眉之急。群里的开发者主动发红包向其致谢,顾咏十分感动,然后拒绝了他:“应该做的,红包不能收。”

1201.jpg

对于PHP 开发者来说,Composer 是必不可少的依赖包管理工具,作为存储 Composer 依赖包的 Packagist,却时常因为网络问题让国内开发者头痛不已,国内开发者安装依赖通常很慢,或者超时导致无法安装,却又没有稳定的镜像服务可以使用。Packagist 鼓励开发者建立镜像,但目前的镜像也有诸多不稳定、不可靠的情况。

阿里云Composer 镜像的推出

今年七月,阿里云提供了 Packagist/Composer 全量镜像服务,其秒级同步的能力、快速稳定的下载服务、页面上的动态数据展示得到了开发者的一致好评。

1202.png

阿里云Composer 镜像的升级

11月16日开始,由于 Composer 镜像出现了间歇性无法访问情况,不少网友通过阿里云钉钉服务群反应阿里云镜像出现不可用的情况,主要 zlib_decode 和 404 错误。在测试其他镜像作对比时发现,其他镜像也存在此类情况。接到反馈后,我们第一时间进行问题排查:

问题定位:阿里工程师立即查看系统状态和日志,未发现异常。初步怀疑是由于 CDN 接入层收国际网络延迟导致不可用。

验证:阿里工程师笔将相同的数据回传至国内 Bucket ,在今经多次、多地域直接访问测试,均成功。

决心升级:以往偶尔遇到这种问题,都被当做正常现象对待,而此次持续时间较长,影响面广,为了彻底解决这类问题,阿里决定升级镜像系统部署方案,直接将最新数据传回国内。

已知现有 Packagist 镜像的问题

1)同步的数据不是 Packagist 的根数据。事实上,官方的根数据不对外公开,开发者平时所访问的数据是镜像,甚至是镜像的镜像。当客户端发起请求后,请求会被官方 DNS 指向其他的镜像站,这些镜像数据与根数据之间已经存在延迟。而由于国际网络或系统设计原因,曾经出现初次官方镜像站与根数据长达数小时不同步 的情况。

2)没有处理代码包 dist。大多数依赖包的源代码存储在在github、gitlab上,因为网络问题,也会导致使用者下载速度慢,甚至下载失败。这也是镜像站需要关注处理的,一般镜像只提供 meta 数据(包数据)。例如官方推荐的 Webysther's mirror code 镜像同步系统就不处理dist。

3)本地文件存储。目前已知的其他镜像系统,是将文件存储在本地,或至少先存储在本地再上传,这样不仅会消耗大量本地磁盘空间,还存在系统最大子目录限制,会使得系统存在致命瓶颈。优化版本使用的软连接方案也会随着包的无限增长需要重构。

4)单进程,性能表现不佳,消耗 CPU、内存资源大。且处理数据耗时长,更新速度慢,系统的设计导致任务不能分发,且同步时间间隔越长,同步的时间越常。

5)没有数据错误统计,官方源数据存在错误,也需要直观的展示,让开发者了解情况。

6)系统同步状态、数据不可视化,镜像是否已更新?什么时候更新?今天更新了多少?下一次什么时候更新?这些数据开发者都不知道。

阿里云镜像的优势

阿里云镜像的架构核心目标是实时、快读、稳定、可移植、可扩展,且具备对数据进行自我修复的能力。那么阿里云镜像和其他镜像有什么区别?阿里云镜像又是如何做到秒级同步的呢?

官方合作

在数据上,阿里云与 Packagist 官方合作,经过和 Packagist 沟通,阿里云在距离官方根数据最近的城市节点部署了服务器,同时阿里云的服务器 IP地址 被加入 Packagist 白名单,允许直接、频繁地访问其根数据(Meta)。获取和解析 Meta 后,系统从代码仓库中下载源代码压缩包,再通过阿里云洛神网络不限带宽的将数据传回国内,这从最大程度上保证了国内用户可以及时、快速地获取最新数据。开发者使用 Composer 安装依赖的数据,都是镜像,甚至是镜像的镜像。例如官方在新加坡的镜像,就数次出现长达数小时的不更新,以此为镜像源的镜像站就无法为开发者提供正常的服务。

实时

阿里云实时同步源数据,对于以下场景的用户具有十分重要的意义:

  1. 迫切需要更新补丁依赖包的使用者。当一个依赖包被发现有bug,得到修复后使用者往往需要第一时间升级更新,镜像同步的越及时、服务越稳定,使用者的补丁修复的也就越早,止损也就更及时。
  2. 检查依赖包发布状态的包开发者来说。对于包的开发者,在发布包后,能尽快的检查发布状态,通过安装命令验证其作品的可用性。

自主研发高性能系统

同步系统由阿里云自主研发,采用 Golang 编写,使用 Redis 做任务队列,心跳协程将更新的数据文件分发到任务队列,30个协程各自分工获取数据传回国内OSS。这意味着所要同步的数据不再是一个单进程按照顺序一个一个传输,而是多个协程,甚至是多台机上的多个协程一起分工,这又将同步时间大幅度缩短。

只分发有效任务

在任务分发的机制上,实现了任务不重复,由于内存会记录已经成功处理过的任务和已分发的任务,所以不会分发旧文件,也不会发布相同的任务,这避免无效、重复工作,更是大幅度的减少了工作量,降低延迟。

重试机制

对于数据获取错误的情况,系统具有重试机制,对于因为网络问题暂时访问错误的源数据、代码包,系统会重试请求。

文件存储

阿里云 Composer 全量镜像,依靠阿里云强大的 OSS 存储源数据和代码压缩包,不占用本地磁盘,在避免最大子目录的问题的同时,还能轻松移植、扩展系统。

错误记录

记录和统计官方错误,阿里云将官方记录当中的一些错误记录下来,在方便内部随时排查问题的同时,也能更准确的了解 Packagist 的情况。

自我修复

处理不成功的任务不会被记录,在间隔时间极短的下一次同步中会得到修复。而执行错误的任务则会使用重试修复。

如果需要人工修复,只需删除响应的 KEY,系统即可重新执行并更新状态。

CDN 支撑

镜像数据对外,接入了阿里云全国 CDN 节点,阿里云强大的网络基础设施保证了开发者如丝般顺滑的使用体验。

状态数据可视化

镜像系统数据状态可视,在阿里云 Composer 全量镜像的官方页面上,动态显示 Packagist 最后更新时间,阿里云同步耗时、下一次刷新 CDN 的时间,系统同步的状态和数据让开发者“心中有数”。

1203.jpg

免费全量镜像站,开发者的福音

阿里做镜像站的历史最早可追溯至2011年,从最开始阿里内部的需求,扩展到为更广大的开发者免费投入资源,提供更快、更稳定的镜像资源。从最初的几台设备,成长为现在覆盖主流语言和主流操作系统的全量镜像站。并且,在这个过程中,一直坚持免费为开发者提供镜像资源,不断追求更快、更稳定的服务。

目前阿里云镜像站不仅提供Centos、Ubuntu、 Fedora、Arch Linux、 Deepin 等10多个发行版的软件安装源和ISO下载服务, 还提供Python, Php 等多款开发语言的包管理镜像服务以及nvidia-cuda, homebrew, kubernetes等 10 多款垂直仓库的镜像服务。每月下载包文件数量已经超过 7 亿次。

国内镜像所做的是缓存所有安装包和元数据到自己的服务器,并通过国内 CDN 进行加速,实现 Composer require/install/update 的操作,并达到最快速度。阿里云的 PHP Composer 全量镜像能够实现与 PHP Packagist 官方实时同步,通过自研的镜像同步系统,实现多协程分工同步、数据自我修复的能力,在保证快速同步的同时,也能快速修复因网络不稳定造成的数据错误。

]]>
高德网络定位算法的演进 Mon, 27 Jan 2020 20:58:34 +0800 1.导读
GPS定位精度高,且早已成为移动设备标配,但GPS也具有一些难以克服的缺陷,包括:

  • 冷启动时间长。GPS启动时,需要进行搜星,锁定卫星信号,然后再进行位置技术,这个过程可能会达到几十秒,即使采用诸如AGPS等技术,仍然有秒级的时间无法定位。
  • 室内或有遮挡的场景。GPS信号弱,无法有效定位。

用户需要持续的有效定位,因此需要另一个技术对GPS进行补充,这就是网络定位技术。

网络定位是将手机设备收到的信号(主要是基站、Wifi、蓝牙)发送到网络服务器,获得位置。之所以要将信号数据发送到网络上,是因为网络定位是利用信号指纹进行定位,需要一个庞大的且持续更新的指纹数据库,这个数据库难以同步到移动设备上。为了进行定位,需要事先建立每个位置的指纹特征,然后在定位时用实时指纹比对每个位置的历史指纹,确定位置。

高德网络定位不仅承担着高德地图用户的定位请求,还面向国内所有主流手机厂商,以及国内30万以上App提供服务,日均处理请求千亿次,峰值QPS百万级。

在过去的几年中,高德网络定位算法经历了从无监督算法向有监督算法的演进,从定位精度、定位能力透出等方面都有了显著的提升。

注:高德网络定位只存在于安卓平台上,在iOS上由于苹果公司未开放任何定位相关的指纹数据(Wifi、基站列表等),定位结果全部来自于iOS自身。

2.基于聚类的无监督算法
经典的指纹定位算法是无监督算法,其核心是计算指纹的相似性,用指纹确定位置。下图是一个例子,AP代表手机扫描到的基站和Wifi设备编号,纵轴代表不同的位置,二者交点的数值代表该位置扫描到该AP的信号强度,为空代表该位置没有扫描到该AP。
fangxing1.png

要对一个新定期请求进行定位(比如AP1:-30,AP2:-50,AP3:-90),一个最简单的方法,是用KNN逐一计算该指纹与历史指纹的相似度(比如用L2距离或者余弦相似度),取相似度最大的历史位置作为用户位置。

这有两个问题,第一是计算量太大(AP是10亿量级,loc是千亿量级),无法满足实时定位的要求,第二是历史指纹在局部可能比较稀疏,对于用户指纹无法精确匹配。

于是需要对历史数据进行预处理,提取出AP和网格的通用指纹,这样在定位时只需要比对一次即可。下图是利用一个AP的历史采集位置进行聚类,获得AP实际位置和覆盖半径的过程,有了每个AP的位置,在定位时将多个AP的位置进行加权平均即可获得最终位置。
fangxing2.png

这种方法需要解决的一个挑战是当有多个候选位置时如何选择,如下图所示,有两个候选位置。
fangxing3.png

fangxing4.png

此时需要设计一个策略进行簇选择,基于每个簇的特征进行打分,找出最有可能的一个簇作为用户位置。

基于加权平均的定位,速度很快,但精度比较差,原因是指纹在空间上的分布并不是连续的,而可能受到建筑、地形、道路的影响,呈现一种不规则的分布,于是在上面定位方式的基础上,发展出一种基于格子排序的算法,可以更精准的定位。

首先将地球划分为25*25的网格,然后统计每个网格内的指纹特征,最后进行格子排序。设候选网格为l,信号向量是S,则定位过程就是计算
fangxing5.png

根据贝叶斯公式,有
fangxing6.png

根据1-1,由于所有候选网格的分母相同,只需要计算分子,即:
fangxing7.png

其中P(l)是某个位置在全量用户位置中出现的概率,可以用定位PV表示,而P(S=S0|l)则需要计算在每个网格内出现某种信号向量的概率,由于向量维数高,概率难以计算,因此对不同维进行独立假设,认为每个信号出现的概率是独立的。有:
fangxing8.png

这样,可以基于历史指纹对每个网格内的每个AP的信号强度进行直方图统计,即可计算出概率,最后对所有格子的概率进行排序,获得概率最高的那一个,如下图:
fangxing9.png

3.基于分层排序的有监督算法
无监督算法的一个问题,是难以迭代,对于badcase无法进行有效优化,一旦调整策略就会影响到其他case,无法获得全局最优。

因此,有监督学习就变得很有必要,高德定位从近两年开始全面转向有监督学习,持续进行特征和模型设计,提升效果,取得了不错的收益,解决了50%以上的大误差问题(5公里以上),在移动Wifi识别上获得了99%以上的识别准确率。

有监督学习需要使用大量的特征,特征的计算需要消耗较多资源,考虑到定位服务要承受10万以上的QPS,模型的复杂性与效果同等重要,因此我们首先将定位服务进行了分层,上面的层级针对大网格,计算粗略的位置,下面的层级针对小网格,逐步细化位置。这样可以极大减少不必要的计算,在性能和效果间取得平衡。
fangxing10.png

对于每一个单独的算法模块,都采用类似下面的神经网络模型对每个候选网格进行打分,再使用LTR损失函数作为目标进行训练,从而获得神经网络的参数。在特征方面,同时考虑以下三类:

  • AP的动态特征,比如信号强度
  • 网格特征,比如PV、UV、AP数、周边候选网格数等
  • AP在网格上的特征,比如信号强度分布、PV、UV等

采用这种方法可以解决绝大部分格子选择不准确的问题,遗留的一个问题是当定位依据特别少的时候,比如只有一个基站和一个Wifi,二者分别位于距离较远的两个网格,此时无论选择哪个都有50%的错误概率。为了解决这个问题,我们引入了用户历史定位点辅助进行各自选择。

在特征部分加入历史定位点序列,输出一个历史位置特征(可以看成是一个预测的新位置),让这个预测位置参与网格打分。当有两个距离较远但打分接近的网格进行对比时,通过预测位置进行加权。这样模型应该可以学出这样的规律:如果网格距离预测位置比较远,打分就降低,如果比较近,分就高。通过这个方法,大误差case的比例可以降低20%。

4.场景化定位
用户在不同场景下对定位的要求是不同的,比如用户在旅途中可能只需要知道大致的位置,不需要很精确,但是在导航时就需要精确的知道自己在哪条道路上,距离出口多远。

因此,除了在整体算法架构上进行优化,高德还在不同特定场景上进行针对性的优化,满足用户不同场景下的定位需求。

室内场景

指纹定位的一个局限,是需要采集带GPS的样本作为真值进行训练,由于GPS只能在室外被采集到,即使用户在室内,其定位结果有很大概率在室外,这会对用户造成不少困扰,特别是在用户准备出行的时候,其定位点的漂移会导致起点偏离真实位置较大。

为了解决这个问题,有两个解决办法,一是采集室内真值,但这种方法需要大量人工采集工作,工作量巨大,目前高德在一些热门商场和交通枢纽进行人工指纹采集(除了基站Wifi还支持蓝牙、传感器定位)。第二个办法是借助大数据,无需人工干预,对Wifi进行建筑/POI关联,用建筑/POI位置去修正定位结果。

Wifi-POI关联有多种方法,一个简单的方法是用POI名字与Wifi名字的相似度判断是否有关联,比如麦当劳的Wifi名字就是McDonald,关联的时候需要考虑中英文、大小写、中英文缩写等。从名称能分析出关联关系的Wifi毕竟是少数。另外一种覆盖能力更强的方法是利用Wifi信号分布规律去挖掘Wifi的真实位置,毕竟绝大部分Wifi都是部署在室内的。

这里我们采用的是CNN的方法,将楼块数据、POI数据、采集真值数据绘制为二维图像,然后进行多层卷积计算,label为Wifi所在的真实楼块区域。下图中蓝色块为楼块,绿色为采集点,颜色越亮代表信号强度越高,红色点代表Wifi真实位置。
fangxing11.png

目前算法能挖掘出30%Wifi对应的真实位置,在最终定位效果上,用户在室内时,能正确定位到室内的样本比例提升了15%

高铁场景

从用户报错情况看,有大量报错是用户乘坐高铁时定位异常。高铁在近两年开通了车载Wifi,这些Wifi都是移动Wifi,因此这些AP是没有一个固定位置的,如果不进行任何处理,算法训练获得的Wifi位置一定是错误的,很大概率会在沿途的某个车站(用户集中,采集量高)。

针对这种场景,需要将移动Wifi全部去除再进行定位。我们开发了针对高铁和普通场景的移动Wifi挖掘算法,利用采集点时空分布等特征判断某个Wifi是否移动,挖掘准确率和召回率均超过99%,可以解决绝大部分高铁定位错误的问题。

地铁场景

地铁场景有点类似高铁,用户扫到的Wifi基本都是移动Wifi(少量车站有固定Wifi),因此只能借助基站进行定位。但基站深埋地下,缺乏采集数据,如何获得基站的真实位置呢?我们采用了两种策略,第一个策略是利用相邻基站信息,当用户在一个请求里或者在短暂时间段内同时扫描到地铁基站(无GPS采集)和非地铁基站(有GPS采集)时,我们可以用后者的位置去推算前者位置,当然这种方式得到的基站位置不太准确。于是我们进行了进一步优化,利用用户轨迹去精准挖掘出每个请求对应的地铁站,从而构建出指纹对应的真值。

基于以上方法,地铁内的定位精度可达到90%以上,实现地铁报站和换乘提醒。

5.未来演进
在未来,定位技术特别是移动设备的定位技术还将快速发展,主要突破可能来自以下方面:

图像定位:谷歌已经发布了基于街景的AR定位,可以解决在城市峡谷区域内的精准定位。这种定位利用了更丰富的数据源,对用户体验的提升也会非常显著。

5G定位:5G相比4G,频率更高,频带更宽,用于测距时精度更高(比如利用相位差进行传输时间计算),行业协会也在孵化5G定位相关的标准,运营商在未来可能会支持基于5G网络的定位,届时在5G覆盖区将会有类似GPS精度的定位效果。

IOT定位:随着物联网的普及,基于NB-IOT的定位技术也会应运而生,它可以使用类似基站定位的方法,或者使用P2P定位的方法为物联网设备进行定位。

]]>
基于ZYNQ的流水线食品外观检测系统设计 Mon, 27 Jan 2020 20:58:34 +0800 0 引言

  随着人们物质生活水平的提高,对食品质量要求也越来越高,而国家经济的快速发展促进食品生产速度随之提升,因此,如何在流水线上保证生产速度的同时检测食品质量已逐渐成为研究问题。

  目前,市面上存在的图像传感器中,大多以模板匹配的方法对特定规格的产品进行检测,如车牌、饼干、门窗等,而对于许多不规则的食品如凤爪、鸭翅、虾仁等却无法对其包括产品尺寸、传送方向等进行有效检测。本设计采用具有双核的ARM-Cortex TM -A9处理系统(PS)和Artix-7可编程逻辑(PL)的ZYNQ作为主控制和算法处理单元,通过图像测量方法提取不规则食品外观共同特征,是进一步检测食品传送方向是否与流水线方向一致,以及食品缺损问题的关键。

  从我国食品发展行业趋势来看,市场上需要能够应用在流水线上的高精度、针对无特定性状食品检测处理的图像传感器,因此其具有十分广阔的市场前景。

  1 系统的总体结构

  整个系统由图像采集模块OV5640、图像处理模块、VGA显示模块、机械执行模块以及光电开关组成。如图1所示。

  图像处理模块是整个系统的控制核心。图像采集单元通过单目摄像头OV5640对食品生产线上的食品成品进行采集,由光电开关检测当前图像采集区域是否有完整食品传入,将触发信号传送给ZYNQ平台对实时采集到的图像数据进行灰度化、阈值分割、图像膨胀、特征提取,以实现运动食品的检测,并对当前检测食品传送方向是否正确以及是否存在缺损作出判断。通过控制分拣开关,将次品与合格品分类集中,方便下一步的产品生产包装工艺。

image.png

image.png
image.png

  视频图像显示单元是在FPGA控制下对处理前后的视频图像进行显示 [1] 。其中图像测量算法是基于背景颜色的图像分割算法,它是本文在灰度阈值分割算法基础上提出的一种适合本系统的算法。

  2 图像采集及显示系统设计

  图像采集系统平台的设计主要包括以下几个部分:单目OV5640摄像头模组,用来实现图像的采集;ZNYQ-7000系列开发板用来实现摄像头采集、数据存储与传递以及完成图像处理算法;VGA显示器用来显示摄像头实时采集的图像以及经ZYNQ开发板处理后的结果。

  2.1 OV5640摄像头模块

  本模块设计中采用美国OmniVision公司的CMOS图像传感器OV5640。OV5640拥有2590×1944的感光阵列,能够以15帧的500万像素的分辨率记录图像,并且可对输出数据格式、图像分辨率、输出帧率以及图像特性等进行配置,满足许多应用需求 [2] 。其摄像头模组通过DVP接口和FPGA连接实现图像的传输,具体参数及主要特性如下:

  (1)具有标准的SCCB接口;

  (2)支持多种视频输出格式:RAW RGB、RGB565/555/444、YCbCr422等;

  (3)支持VGA、QVGA以及1080P分辨率输出;

  (4)支持数字视频端口(DVP)并行输出接口和双车道MIPI输出接口。

  OV5640图像数据采集模块是整个系统的输入模块,它主要负责将摄像头采集到的图像数据传送到FPGA例化的存储器中,使用DVP传输视频时,PCLK为像素时钟,HREF为行同步信号,VSYNC为场同步信号,数据线为8 bit,在FPGA中配置RGB565输出。

  OV5640芯片采集的数据通过FPGA软核VDMA0、VDMA1分别用作视频的输入,将数据写入与PS端相连的DDR中,以及视频的输出。ARM核完成对存储数据的图像处理及分析算法。使能的VDMA在DDR中读取相应的数据,数据经过图像测量算法提取特征后再输出。VDMA获取的数据均为符合AXI4协议的32位数据,经过32位数据转24位RGB 888格式数据后,根据VGA输出协议,在相应的时序控制下,依次转换为DVI数据输出到VGA显示器中 [3] 。

image.png

  2.2 VDMA配置模块

  VDMA是针对视频图像处理的一个特殊的DMA。在ZYNQ-7000的PS中,包含处理器和DDR存储器控制器;而在PL中,实现AXI DMA和AXI数据FIFO。通过AXI-Lite总线,处理器与AXI DMA通信,用于建立、初始化和监控数据传输。VDMA有一个AXI4 MemoryMap接口,用于对存储器进行读写视频数据,AXI4-Lite接口用于读取VDMA状态以及配置VDMA的参数;AXI4-Stream接口用于视频的输入和输出。VDMA系统结构原理如图2所示。

  在该设计中,AXI_MM2S和AXI_S2MM是存储器映射的AXI4总线,提供了对DDR存储器的访问。AXIS_MM2S和AXIS_2MM是AXI4 Stream总线,它可以连续的传输数据,而不需要提供地址信息 [4] 。

  2.3 VGA显示设计

  VGA显示模块分为:上电等待模块、寄存器配置模块、摄像头采集模块、SDRAM控制模块以及系统控制模块。其中FIFO控制模块原理机制如图3所示。

  由于视频图像数据通过ZYNQ系统的高速AXI_HP0口输出,系统使用AXI_VDMA IP核来通过AXI_interconnect连接ZYNQ系统的HP0口,AXI_subset_converter来进行数据格式的转换,转换为24位的视频图像数据,V_axi4s_vid_out IP将视频流装换成RGB888的视频格式信号。最后视频图像通过自定义IP核(rgb2dvi)转换成TMDS信号驱动VGA显示器显示图像。构架好的系统如图4所示。

  2.4 光电开关检测设计

  为减少图像数据处理量,并较准确确定视频图像流中哪一帧中食品进入到单目摄像头图像采集区域内,系统设计中采用对射式光电开关来检测目标,并将触发信号传送到ZYNQ中,在间隔一定时间后处理当前帧图像,确保其采集食品图像的完整性。

  采用对射式的光电开关可分辨不透明的反光物体,有效距离大,不易受干扰,灵敏度高,并且响应时间快,可将触发信号传送给ZYNQ平台的I/O口作为进行图像测量的起始信号。

image.png

  3 系统软件设计

  图像处理部分包括:图像预处理、图像膨胀及基于图像测量的特征提取。软件设计流程如图6。

  3.1 图像预处理设计

  针对不规则食品外观的检测,首先要对图像进行预处理,再进一步用图像测量算法提取图像特征。本文提取的特征是基于灰度图像的,需将采集到的24位真彩色图转换为灰度图

image.png

  由于光源和实际拍摄的情况,采集到的图像可能含有噪声,为排除噪声的影响,先对图像进行去噪的处理。根据项目需要,采用中值滤波的方法。这种非线性的图像平滑法对脉冲干扰级的椒盐噪声抑制效果较好,能保护边缘少受模糊的影响。

  3.2 图像形态学处理

  为了更加明显区分背景与目标,需将灰度图进行二值化处理来区分凤爪与传送带。采用阈值分割的方法,根据阈值将图像中灰度级大于阈值的像素点和小于像素值的像素点分开,从而实现图像分割 [5] 。

image.png

  其中,T为预设的阈值,从0~255中取值,具体大小由工业流水线实际环境决定,根据多次试验可选取150作阈值分割,是目标为黑色,背景为白色。

image.png

  根 据 图像 特 征 提 取算 法 识 别 的要 求 , 需 通过遍历像素值所在区域通过黑白像素值边界的跳跃次数来区分爪趾的数目及食品被放置方向与传送带方向是否一致,因此需要保证目标的连通性要完整,将得到的二值化图像进行一轮腐蚀膨胀,以消除噪声点,使不连续的地方连接起来,为下一步的图像识别及特征提取做基础。

  本系统中以凤爪为例,对二值化后的目标凤爪进行图像膨胀,采用15×15大小的方阵集合做膨胀的结构元素。关键源代码如下(定义flag为记录结构元素区域内各像素值与运算后的结果):

  for(j=0;j

  for(i=0;i

  flag=1; //赋初值

  //采用15×15大小结构元素作掩膜处理

  for(m=j-1;m

  for(n=i-1;n

  if(image_inj==0||image_inm==0)

  {flag=0;break;}

  else{flag=1;}}

  if(flag==0){break;}}

  //根据flag值来输出膨胀后结果

  if(flag==0){image_outj=0;}

  else{image_outj=255;}}}}

  通过图像膨胀后的结果如图7所示:可去除二值化结果中的不连续的噪声点及食品本身瑕疵的影响。

  3.3 图像测量算法设计

  机器视觉定位的最终目的是利用工业相机采集运动食品的图像,再通过算法确定运动目标的特征点与特征边缘,以方便进行机械分拣 [6] 。对于在高速流水线上采集到的视频图像流进行处理,首先,通过光电开关的触发信号在视频流中确定食品目标已全部进入图像采集区域,对该帧图像作图像识别,并在确定帧图像后,识别目标送入方向是否正确及是否存在残缺。

  对于阈值化并经过图像膨胀处理后,目标区域是完整的连通区域,可通过遍历查询提取该不规则图像边缘特征。从凤爪这个研究对象的特点来看具有以下特性:

  (1)从形状上看,凤爪共有4个指头,其中一端较短,其余三个指头较长,中间者为最长。骨干部分占整个长度的一半左右。

  (2)从颜色上看,凤爪色泽绛红,深浅程度基本一致,掌心部分偏暗红。

  以上分析,作为待识别的凤爪,虽然是不规则形状,但无论从颜色还是形状上,都能保持一个较稳定的特性,因此在编写算法程序中,可通过数组记录图像的每行像素值黑白边界跳跃次数,以此提取流水线上加工食品特征,并根据不同食品特征要求通过遍历特征数组及测量算法作判别,按照判别结果启动分拣开关将食品归入不同的箱中。

  4 结论

  本文针对食品流水线上的图像外观辨别问题进行了分析与解决方案的提出,利用图像测量的方法对不规则食品进行特征提取,并采用ARM+FPGA结构的实时图像处理平台以及光电开关的结合,使系统小型化,利于后期进一步开发、成本降低,便于针对特定应用定制等优势、具有较高的工程应用参考价值,流水线食品辨别系统拥有广阔的应用空间。

  参考文献

  [1].杜文略,刘建梁,沈三民等.基于FPGA的运动目标检测系统设计[J].电子技术应用,2014,40(11):36-42.

  [2].张震.基于FPGA的USB3.0高速图像采集系统设计与图像特征提取算法研究[D].西安电子科技大学开发板的摄像头采集与处理系统实验设计与实现[J].计算机教育,2018(4):23-26.

  [3].何宾,张艳辉.Xilinx Zynq-7000嵌入式系统设计与实现[M].北京:电子工业出版社.2016.

  [4].吴长冶.食品(虾仁)分拣系统中的图像处理算法的研究[D].南京理工大学.2012.

  [5].李明.运动食品机器视觉的识别与定位技术的研究[D].哈尔滨商业大学.2014.

  [6].胡健.基于Zynq的智能相机图像处理流水线程序优化与实现[J].通信设计与应用.2017(10):3-4.

  作者简介

  王明全(1973-),男,博士,讲师,主要研究方向:信号与信息处理.

  本文来源于科技期刊《电子产品世界》2019年第6期第41页,欢迎您写论文时引用,并注明出处

]]>
阿里主管通知我试用期延期…… Mon, 27 Jan 2020 20:58:34 +0800

image.png

阿里妹导读:接下来的文章是一篇发布在阿里内网里的文章。花木是一位走出体制的博士,讲述自己Landing的经历。今天,她将这段经历分享给大家,告诉我们:脸先着地又怎样,哪有那么多坦途;最美的,是翻山越岭的一路风景;最幸福的,是酣畅淋漓地打拼后见到了更好的自己。

来阿里之前,我在体制内呆了12年,体制内的人,在一个单位呆十几二十年甚至一辈子,非常正常。然而,在阿里很多人曾冒出离开的念头。我也有好几次想过离开,但最终留下来了。今天就来讲一讲,体制内的我,在阿里这几年的经历和感悟。

试用期延期,从P9降到P8

2014年,我踌躇满志的来到了阿里集团法务部,设立法研团队,准备以法学博士的学历、从事法律政策研究12年的工作经历,在阿里大展身手。然而3个月的时候,我却被延长了试用期,6个月的时候,主管和HRG告诉我,希望我留下来,但要从P9降到P8。

来阿里之前,顺风顺水,对于习惯了当学霸,一路顺风顺水的我来讲,真的是遇到了人生中第一个大坎儿。那一刻的第一想法肯定是离开。离开很容易、找个工作也不难,但阿里之旅就这么结束了吗?如果就这么结束,就是认输,就是承认自己跳不出以前的舒适圈,想到这些,心里又有点不甘。就是因为这一丝的不甘心,让我接受了这个管理决定,留了下来。

忘记9or8的层级,先聚焦手头最重要的事情,深入业务、贴地飞行。当时,为了完成对某项意见的反馈,我们跟BU法务对焦之外,又用1个多月的时间和业务团队对接,深入了解业务需求、整理、反馈,结合自己和团队同学的专业优势,形成了质量很高的反馈意见稿。让合作的内部团队和外部的学者、立法机构看到了阿里的专业和认真。以此为契机,集团法务建立了常态化的标准流程,对我而言,是认识了很多业务的同学、理解了很多业务的细节,尽管刚开始对他们满嘴的专业术语并不理解,经常跟不上他们机关枪式的语速,但我开始真正了解阿里巴巴,嗯,这个地方来对了。

image.png

“真实的面对自己”这句话是我在一次培训的时候听阿里合伙人戴珊讲的,她说出这句话的时候,我内心一震。这几年看下来,所谓真实的面对自己,是既不妄自尊大、也不妄自菲薄,是一个不断自省、跟自己对话的过程。尽管被降级我还是留下来,一方面是相信以自己过往的经历和经验,一定能在阿里有用武之地,只是自己还没找到怎么样把能力在阿里发挥的路径和方法;另一方面,是各种发自内心的不服气,凭什么村里的高考状元、堂堂博士在一个公司都无立锥之地!?然后,就沉下心来,做好一件事情、两件事情,在过程中不断总结好的、反省不好的,后来发现,最终受益的是自己,让自己变得更强大。

现在脑海中经常出现的一句话是:宠辱不惊,看庭前花开花落;去留无意,望天上云卷云舒。真实的面对自己!

转岗做业务,跳进了“坑”里

2015年冬天我转岗到了钉钉,一支创业团队。

跟钉钉的缘分,一开始,我就作为法务在支持,跟业务走的很近后,发现原来自己的知识和经验越来越有发挥的空间,觉得既然这样了就索性跳进去业务看看。

但是到了钉钉的第一件事情,就是一个巨大的“惊喜”——钉钉的一个爆款产品必须在1个月内下线。印象最深就是那天下午,我和一位同学在办公楼外的马路牙子上呆呆的坐了3个小时,不知道能干点儿啥~~~(想起来就凄凉)。之后的一个月我们进入了疯狂的状态,跑了很多家供应商,在碰了十几次壁后,终于在7月26日开始跟一家合作伙伴开发新业务。当时的状态是:早晨最早一班火车去南京跟合作伙伴讨论,晚上回来,继续画图coding,几乎每周2-3次。每天开发出来的一个功能就找最近的同事、用户共创,连夜修改。用了2个月的时间,到9月18日钉钉3.0发布会,主题是“呼吸”,发布了新的替代产品。那一刻,活过来,深深的吸了一口新鲜的空气。2016年,当时觉得是天大的事情,今天只是写在钉钉历史上的一个故事。

image.png

这样的事情,在钉钉并不稀奇,“谁是钉钉、你就是钉钉”这句话是钉钉同学身上的烙印。根据我这三年多在钉钉的体会,一是产品得到了来自用户的认可和期待,变成不管多苦都能持续坚持下去的最大动力,但前提是要贴近用户;二是你坚持,世界就会因你而不同,只要你坚持,运气就会变得很好,关键的时候就会有很多人来帮你;三是我真正了解了产品是怎么做出来的,从以前一个法学学究,变成一个懂产品、懂业务的人,跟以前的同学同事聊天他们都用崇拜的眼光看着我,特有成就感;四是收获了一堆可以把后背交给彼此的战友。

印象最深刻的一件事,2016年得了带状疱疹,全身痛痒所以需要穿宽松的衣服,我开玩笑跟同事说,自己要穿睡衣去上班了……结果第二天我打扮美美的到了项目室,一堆平日里经常穿着公司发的T恤的码农们穿了一堆花花绿绿的睡衣:有戴兔子耳朵的、有印小花的、有毛茸茸的……当时家人是坚决要求我离开杭州回北京的,因为那一屋子的睡衣,我留下来了。

在阿里要“打得开,放得下”

2019年我担任钉钉一个项目的总PM,用一个季度的时间快速的把产品、市场、PR和前线团队协同起来,拿到了不错的结果。但是战役打完,需要把战役积累的经验常态化、数字持续化,发现需要跟原来做这件事情的同事拉通、相互理解,需要引入更多的资源,需要改变原来的生态规则和玩法,需要沟通、说服的人很多,想不通为什么明明已经证明了的事情不能快速复制、明明自己认为正确的事情别人就理解不了,我就变得很焦躁,有一段时间状态很差,看见谁都想吵一架。

这个时候,有一个爱唠叨的老板就变得很重要,我每次跟别人吵完,就会气呼呼的去找大炮(阿里合伙人)吐槽,然后大炮就跟我说**“慢慢来,不着急,每个人都不容易,你要打开、放下,看到别人的长处,学会赞美别人”。
**

半年过去了,尽管我还没做到每次都赞美别人,但明显感觉到周围的人更好沟通、更容易达成共识,我自己的耐性也变得更好,真正的开始理解“视人为人”的内涵。

image.png

保持“好奇心”

回顾过去这五年,很多人遇到降级,就选择了离开。但对我而言,每次遇到挫折,反而是一个更加真实的认识自己的过程,逼着自己去想清楚为什么、什么是自己真正想要的,如果这样的经历可以让我变成更强大的自己、为未来带来更大的空间,那一时的得失、降级降薪这些事情还有那么重要吗?

现在加入阿里很多同学,可能都是行业大拿或极强的专业背景,刚来阿里,一定遇到各种的不适应,但我想说:要相信自己,相信自己是有能力的,否则你也进不来阿里,相信你过去积累的逻辑能力、专业经验会有一天在阿里有用武之地。在此之前,要先沉下心来找一件事情认真的做好,对,在阿里要自己找事情做,坚定的做下去。沉入到同学中间,看下产品经理们怎么样快速画出一个xmind,前端同学看他们怎么做个TMS页面,看下BI的同学怎么分析那些枯燥的数据神奇的发现了一个新的机会……阿里足够大,只要你有好奇心,你学到的越来越多,会慢慢的发现自己更多的潜力,强到你自己都不敢相信。

2019年9月1日,我在阿里5周年,尽管过去了好几个月,尽管我最近记性变得很差,但我清楚的记得那天是个周日,当我看到钉钉上推送来简单的几个字:“亲,恭喜你,五周年快乐”,那一刹那,脑袋里突然一片空白,准确的说,是脑袋里突然冒出一片清澈的天空,宁静而开阔。没有奔流而出的眼泪,没有想象中的百感交集,我轻轻地对自己露出一个浅浅的微笑,阿里人,欢迎你。

注:文中花木为化名。

image.png

]]>
阿里巴巴D2 前端论坛最全视频来了!(附PPT下载) | 6大专题持续更新 Mon, 27 Jan 2020 20:58:34 +0800 摘要:D2 前端技术论坛 (Designer & Developer Frontend Technology Forum, 简称 D2),是由阿里经济体前端委员会主办的面向全球前端领域的技术论坛,立志于建设一个促进业内交流、引领前端领域发展的平台。目前 D2 已经成功地举办了13届,为国内外前端领域的开发者和设计者提供了共同探讨行业发展的机会,以技术会友、一起分享技术的乐趣。

12 月 14 日,第十四届 D2 前端技术论坛在杭州圆满举办。来自全国各地的近千名开发者齐聚杭州,聆听 3 大会场、来自 24 位海内外嘉宾的 21 个主题分享。今天,开发者社区整理了6大演讲主题中10位精彩演讲者的视频,供大家交流学习。

点击pdf下载,收获全套资料

image.png


大会开场致词

圆心 / 阿里经济体前端委员会委员长

点击查看视频>>>



智能化专场
]]> Spark 小文件合并优化实践 Mon, 27 Jan 2020 20:58:34 +0800 作者:梁世威,同盾科技平台工具部研发工程师,从事开源大数据计算/存储和优化方面的工作。


“ 对 spark 任务数据落地(HDFS) 碎片文件过多的问题的优化实践及思考。”

此文是关于公司在 Delta Lake 上线之前对Spark任务写入数据产生碎片文件优化的一些实践。

  • 形成原因
    数据在流转过程中经历 filter/shuffle 等过程后,开发人员难以评估作业写出的数据量。即使使用了 Spark 提供的AE功能,目前也只能控制 shuffle read 阶段的数据量,写出数据的大小实际还会受压缩算法及格式的影响,因此在任务运行时,对分区的数据评估非常困难。
  1. shuffle 分区过多过碎,写入性能会较差且生成的小文件会非常多。
  2. shuffle 分区过少过大,则写入并发度可能会不够,影响任务运行时间。
  • 不利影响
    在产生大量碎片文件后,任务数据读取的速度会变慢(需要寻找读入大量的文件,如果是机械盘更是需要大量的寻址操作),同时会对 hdfs namenode 内存造成很大的压力。

在这种情况下,只能让业务/开发人员主动的合并下数据或者控制分区数量,提高了用户的学习及使用成本,往往效果还非常不理想。
既然在运行过程中对最终落地数据的评估如此困难,是否能将该操作放在数据落地后进行?对此我们进行了一些尝试,希望能自动化的解决/缓解此类问题。

01 一些尝试

大致做了这么一些工作:

  1. 修改 Spark FileFormatWriter 源码,数据落盘时,记录相关的 metrics,主要是一些分区/表的记录数量和文件数量信息。
  2. 在发生落盘操作后,会自动触发碎片文件检测,判断是否需要追加合并数据任务。
  3. 实现一个 MergeTable 语法用于合并表/分区碎片文件,通过系统或者用户直接调用。

第1和第2点主要是平台化的一些工作,包括监测数据落盘,根据采集的 metrics 信息再判断是否需要进行 MergeTable 操作,下文是关于 MergeTable 的一些细节实现。

1.1 使用 extensions 扩展语法

功能:

  1. 能够指定表或者分区进行合并
  2. 合并分区表但不指定分区,则会递归对所有分区进行检测合并
  3. 指定了生成的文件数量,就会跳过规则校验,直接按该数量进行合并

语法:

merge table [表名] [options (fileCount=合并后文件数量)]  --非分区表
merge table [表名] PARTITION(分区信息) [options (fileCount=合并后文件数量)] --分区表

碎片文件校验及合并流程:

image.png

1.2 性能优化

对合并操作的性能优化

  1. 只合并碎片文件
    如果设置的碎片阈值是128M,那么只会将该表/分区内小于该阈值的文件进行合并,同时如果碎片文件数量小于一定阈值,将不会触发合并,这里主要考虑的是合并任务存在一定性能开销,因此允许系统中存在一定量的小文件。
  2. 分区数量及合并方式
    定义了一些规则用于计算输出文件数量及合并方式的选择,获取任务的最大并发度 maxConcurrency 用于计算数据的分块大小,再根据数据碎片文件的总大小选择合并(coalesce/repartition)方式。

a.开启dynamicAllocation
maxConcurrency = spark.dynamicAllocation.maxExecutors * spark.executor.cores

b.未开启 dynamicAllocation
maxConcurrency = spark.executor.instances * spark.executor.cores

以几个场景为例对比优化前后的性能:

场景1:最大并发度100,碎片文件数据100,碎片文件总大小100M,如果使用 coalesce(1),将会只会有1个线程去读/写数据,改为 repartition(1),则会有100个并发读,一个线程顺序写。性能相差100X。

场景2:最大并发度100,碎片文件数量10000,碎片文件总大小100G,如果使用 repartition(200),将会导致100G的数据发生 shuffle,改为 coalesce(200),则能在保持相同并发的情况下避免 200G数据的IO。

场景3:最大并发度200,碎片文件数量10000,碎片文件总大小50G,如果使用 coalesce(100),会保存出100个500M文件,但是会浪费一半的计算性能,改为 coalesce(200),合并耗时会下降为原来的50%。

上述例子的核心都是在充分计算资源的同时避免不必要的IO。

  1. 修复元数据
    因为 merge 操作会修改数据的创建及访问时间,所以在目录替换时需要将元数据信息修改到 merge 前的一个状态,该操作还能避免冷数据扫描的误判。最后还要调用 refresh table 更新表在 spark 中的状态缓存。
  2. commit 前进行校验
    在最终提交前对数据进行校验,判断合并前后数据量是否发生变化(从数据块元数据中直接获取数量,避免发生IO),存在异常则会进行回滚,放弃合并操作。

数据写入后,自动合并效果图:

image.png

02 后记

收益

该同步合并的方式对用户完全透明,已经在我们的线上稳定运行了1年多,成功的将平均文件大小从150M提升到了270M左右,提高了数据读取速度,与此同时 Namenode 的内存压力也得到了极大缓解。

对 MergeTable 操作做了上述的相关优化后,根据不同的数据场景下,能带来数倍至数十倍的性能提升。

缺陷

因为采用的是同步合并的方式,下游任务的启动时间会有一定后延,同时由于没有事务控制,所以在合并过程中数据不可用,这也是我们后来开始引入 Delta Lake 的一个原因。


原文链接:
https://mp.weixin.qq.com/s/195nFBH0kpZEXekHiQAfrA


阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
image.png

对开源大数据和感兴趣的同学可以加小编微信(下图二维码,备注“进群”)进入技术交流微信群。

image.png

]]>
论文推荐|[PR 2019]SegLink++:基于实例感知与组件组合的任意形状密集场景文本检测方法 Mon, 27 Jan 2020 20:58:34 +0800 image.png
    本文简要介绍Pattern Recognition 2019论文“SegLink++: Detecting Dense and Arbitrary-shaped Scene Text by Instance-aware Component Grouping”的主要工作。该论文提出一种对文字实例敏感的自下而上的文字检测方法,解决了自然场景中密集文本和不规则文本的检测问题。该论文提出的Instance-aware Component Grouping(ICG)方法,能够在自下而上的文字检测方法的基础上大大提高密集文本检测的效果。在该论文提出的一个商品密集文本检测数据集DAST1500上,该方法的结果明显优于同时期的其他文字检测方法。
image.png

Fig.1. Comparison of different scene text detectors on one proposed DAST1500 image. (a)SegLink; (b) CTD+TLOC; (c) PixelLink; (d)Proposed ICG.

一、研究背景

    近年来场景文字检测工作主要分为两大类:自上而下的方法和自下而上的方法。自上而下的方法主要借鉴的是通用物体检测的思路,并且根据文字的特点设计相应的检测模型。这类方法通常难以处理不规则文本的检测问题。自下而上的方法,通常先学习文本行的基本组成单元,然后进行单元之间的组合得到文本行检测框。由于其灵活的表征方式,对不规则形状的文本检测有着天然的优势。自下而上的方法按照组成单元的不同又分为两类:组成单元为像素的基于分割的方法,以及组成单元为文字块的基于单元组合的方法。但是,自下而上的方法通常很难区分密集文本。密集文本检测问题是文本检测中一个广泛存在的难点问题。

二、原理简述

image.png

Fig.2. Pipeline of the proposed method.


    现有的自下而上的文字检测方法存在两个问题:一是难以区分密集文本,二是自下而上的方法通常需要一定的后处理进行单元组合,而这个后处理过程一般不能和网络一起进行端到端的训练。为了解决这些问题,该论文首先提出了一种文本块单元之间的吸引关系和排斥关系的表征,然后设计了一种最小生成树算法进行单元组合得到最终的文本检测框。另外,该论文还设计了Instance-aware Loss损失函数,把文本行实例的信息引入到文本块单元的训练过程,实现了后处理过程和网络端到端的训练。
    具体的方法细节如图2所示。对于一张待检测的图片,先用VGG16网络进行特征提取,然后在不同层得到网络输出,其中有文字块单元的分类得分和文字块单元检测框的回归值,包括中心偏移量,宽和高以及旋转角度。另外,网络在不同层还会学习相邻文字块单元之间的吸引和排斥的强弱程度。对应到图2中,蓝色框表示文字块单元,绿色线段表示文字块单元之间的吸引关系,红色线段表示文字块单元之间的排斥关系,为了表示的方便,只画了其中两行文字中的文字块单元以及单元关系。
    在后处理阶段,首先利用阈值得到有效的文字块单元。这些单元以及之间的连接关系可以构成一个图的表征G=(V,E)。其中的节点V代表多尺度的图像金字塔中的文字块单元,边E代表在同一层以及跨层的文字块单元之间的连接关系。其中每个边对应这两个吸引和排斥权重值:和。
    然后设计了一种最小生成树算法进行文字块单元组合,算法伪代码如图3所示。具体的算法流程是,按照吸引关系和排斥关系的强弱值从大到小考察每个关系。如果是吸引关系,则其连接的两个文字单元属于同一个文字单元组;如果是排斥关系,则其连接的两个文字单元之间有一个排斥的约束。遍历所有有效的文字块单元关系,可以得到组合好的文字块单元组,对应图2中,不同的文字块单元组用不同的颜色表示。最后,利用每组文字块单元,可以提取出对应的文字区域的外接检测框。
image.png

Fig.3. Modified MST for text component grouping.


    本文的损失函数分为两部分,。一部分是文字块单元分类和回归的损失函数,另一部分是单元关系的损失函数。这两个函数都有一个实例相关的权重,该权重衡量的是对应标注框与最优检测框的匹配程度,用每个标注框与检测框最大的的倒数表示,整个过程对应图2的绿色虚线框,论文中称之为Instance-aware Loss。这样可以把后处理过程引入到网络的训练过程中,自适应调节每个文字区域中的文字单元和单元关系的损失函数权重,从而进一步提升文字检测效果。

三、主要实验结果及可视化效果

    为了验证方法在不规则密集文本上的检测效果,本文公开了一个商品密集行数据集DAST1500,包括1036张训练图片和500张测试图片,利用ICDAR2015的IoU0.5的方法进行评测。
image.png
image.png
image.png
image.png
image.png

Fig. 4. Some qualitative results on DAST1500, ICDAR15 and CTW1500.


    从Table 1来看,文中的方法在商品密集行数据集DAST1500上取得了优异的性能,大幅领先同时期的其他方法。从方法自身的对比来看,在Baseline基础上引入文字块单元间吸引和排斥的关系表征以及Instance-aware Loss都能有效提升密集文本检测效果。
    对于Table 2,文中的方法在ICDAR15多方向文本检测数据集上取得较好的结果,而且检测效率也不错。另外对于Table 5,在CTW1500曲形文本检测数据集上,本文的方法也取得很好的结果,优于同时期的其他方法。
    图4展示了一些可视化的结果图。可以看到,该方法能处理任意形状的文本,在商品密集文本上也能取得很好的检测效果。

四、总结及讨论

    本文提出了一种Instance-aware Component Grouping(ICG)的自下而上的文字检测方法,实验证明该方法在检测不规则密集文本上的有效性和优越性。ICG中文字块单元之间吸引和排斥关系的表征,以及对文字行实例敏感的Instance-aware Loss,都能够显著改善自下而上的文字检测方法的检测效果。

五、相关资源

论文链接:https://doi.org/10.1016/j.patcog.2019.06.020

下载链接:http://www.vlrlab.net/papers/xu/icg.pdf

DAST1500数据集链接:https://tianchi.aliyun.com/dataset/dataDetail?dataId=12084

原文作者:Jun Tang, Zhibo Yang, Yongpan Wang, Qi Zheng, Yongchao Xu, Xiang Bai

撰稿:汤 俊
编排:高 学
审校:殷 飞
发布:金连文

免责声明:

本文仅代表撰稿者观点,个人理解及总结不一定准确及全面,论文完整思想及论点应以原论文为准。

出处:https://mp.weixin.qq.com/s/LS_8424jn_3ksemKBgg88w
]]>
关键技术 五:LTE-A DC 双连接 | 带你读《5G UDN(超密集网络)技术详解》之十四 Mon, 27 Jan 2020 20:58:34 +0800 第 2 章 LTE 微蜂窝和小小区技术

2.3.5 LTE-A 小小区开关

2.3.6 LTE-A DC 双连接

虽然前述的载波聚合技术,能提供高效的无线频谱资源之间的聚合,但在 LTE 网络侧,由于载波聚合技术架构要求多个分量载波之间,必须由同一个 MAC 实体进行统一的调度和资源管理,这就要求多个分量载波被同一基站所配 置管理,或者虽然跨不同的基站,但它们之间能用理想低时延的回程链路(Ideal Backhual)相连,因此即使是两个独立 MAC 实体,它们之间也可进行实时的 无线资源协同操作。前面的 CoMP 技术中,已阐述了 CoMP 的应用条件要求, 对节点部署成本要求相对较高,因此跨站 CoMP 实际应用并不多。在现实的异 构网络部署中,宏基站之间或者宏基站和微基站之间,可能来自不同的网络设 备供应商(异厂家),或者它们之间通过更低成本的非理想回程链路(Non-Ideal Backhual)承载连接,这就使得载波聚合技术的应用受到很大限制,甚至载波 聚合根本不能正常工作。
为了能够实现非理想回程链路相连接的基站之间的载波聚合(适用的 场景更广泛),以提高系统容量、用户平均吞吐率和峰值速率以及在异构微 蜂窝环境中的移动性能,LTE-A Rel-12 引入了双连接技术(DC,Dual Connectivity),它使得终端和两个异频配置且有独立 MAC 调度的基站,同时 分别建立无线链路(主 RL 和辅 RL),终端同时使用它们的无线资源进行上下 行数据传输。
LTE-A DC 工作模式下,终端的控制面信令无线承载(SRB,Signaling Radio Bearer)建立在主服务小区集合内(通常为宏小区),而用户面数据无线 承载可建立在任一主或辅服务小区集合内。通过 LTE-A DC 技术,不仅 LTE 网络下的无线资源能获得更多的聚合而被利用,终端的平均吞吐率也能像载波 聚合那样获得提升,当终端在不同的辅小小区之间移动切换时,由于终端可以 始终保持与主节点 MeNB 的主无线链路,因此可保证主服务小区集合(MCG, Master Cell Group,可包含一个 Pcell 和若干个 Scell)侧的 SRB 及 DRB 上 数据传输不被中断,从而使用户通信体验得到改善,同时辅服务小区集合侧 (SCG,Secondary Cell Group,可包含一个 PScell 和若干个 Scell)硬切换的 健壮性也增强。此外,MME 通过主 S1-C 连接锚定在主节点(MeNB)上,可 有效减少辅节点(SeNB)侧的小小区切换改变而带来的与核心网的信令交互,从而减少核心网侧的信令负荷。LTE-A DC 也可与各个基站内的 CA/CoMP 等 技术联合在一起应用,因为它们涉及不同的协议层操作。LTE-A 暂时不支持 终端和两个同频配置的基站之间进行双连接操作,因为同频工作的主辅服务小 区间的无线干扰,极大地降低了系统性能的增益(注:5G NR 可以支持终端和 两个同频配置的 gNB 基站之间进行 NR 双连接操作,因为通过波束赋形等增强 手段,同频工作的主辅服务小区间的无线干扰能被抑制)。
LTE-A DC 技术中,网络侧控制面和用户面的架构如图 2-11 所示。
image.png
LTE-A DC 操作下的两个 eNB 分别为主节点 MeNB(Master eNB)和辅 节点 SeNB(Secondary eNB),它们通过普通的 X2 逻辑接口相连接,X2 接 口可由回程链路或者非回程链路回程传输承载。在 LTE-A DC 中,UE 通过 MeNB 与核心网控制面节点 MME 只有唯一一个 S1-MME(S1-C)连接,终 端通过 MeNB 与核心网用户面节点 SGW 可以有多个 S1-U 连接,另外终端还 能同时通过 SeNB 和 SGW 有多个 S1-U 连接(1A 用户面分流架构)。MeNB 还可将 DRB 进行 MCG Split 承载分流操作,通过 X2-U 接口分流一部分 PDCP PDU 到 SeNB 侧,让 SeNB 辅助传输被分流的 PDCP PDU(3C 用户面 分流架构)。注:LTE-A DC 不支持 SCG Split 承载分流操作,即 SeNB 不能 通过 X2-U 接口分流一部分 PDCP PDU 到 MeNB 侧,让 MeNB 辅助传输被分 流的 PDCP PDU(3X 用户面分流架构)。
LTE-A DC 技术中,空口侧下行和上行的控制面 / 用户面架构分别如图 2-12 和图 2-13 所示。
在空口控制面 RRM 测量方面,终端在 MeNB/MCG 和 SeNB/SCG 两侧对 应的无线 RRM 测量,全由 MeNB 负责配置和管理,RRM 测量结果只在 SeNB Addtion/Modification 等流程中传递给 SeNB,辅助 SeNB 生成 SCG 具体的配置。在空口 Measurement Gap 配置使用方面,LTE-A DC 采用 Single Measurement Gap,即只配置给终端一套公共的 Measurement Gap,在 Measurement Gap 对应的时隙内,MeNB 和 SeNB 两侧和终端之间都不能进 行数据调度和传输。
image.png
在终端能力协调方面,由于 MeNB 和 SeNB 在无线资源调度和终端基 带射频能力使用消耗方面是独立进行的,因此与无线资源调度动态紧密相 关 的 终 端 能 力( 如,Maximum number of DL-SCH transport block bits received within a TTI,Maximum number of UL-SCH transport block bits transmitted within a TTI)需在 MeNB 和 SeNB 之间提前进行预分割划 分。MeNB 负责硬分割划分这些终端基带能力,并将能力分割后的结果(SeNB 可使用的部分终端基带能力)发送给 SeNB 使用。MeNB 在终端能力硬分割划 分时,允许分配给 SeNB 的部分能力 +MeNB 自己使用的部分能力之和,稍微 超过终端的实际总能力。为了提高 UE 能力资源的使用率,可以假设:MeNB和 SeNB 会以极低概率同时使用消耗自身侧的最大终端能力部分,否则,一旦 冲突发生,会导致在该时刻两侧的数据传输都失败。
image.png
在空口数据传输安全控制方面,如图 2-14 所示,对于 LTE-A DC 中配 置的 SCG 承载,SeNB 侧使用的根密钥为 S-KeNB,用于继续推导 SCG 承载 的加密密钥 Kup,S-KeNB 是由 MeNB 自己维护的根密钥 KeNB 及小小区计 数器(SCC,Small Cell Counter)联合推导产生,并在生成 S-KeNB 后,在 SeNB Addtion/Modification 流程时发送给 SeNB 使用。每当 MeNB 不变但 SeNB 要变,SCC 都要随之更新和递加 1。SCC 由 MeNB 在空口直接发送给 UE,UE 本地推导产生 SCG 侧根密钥 S-KeNB,和 SeNB 内的 S-KeNB 相匹 配。MeNB 切换变化而导致的根密钥 KeNB 改变,或者 SCC 达到最大值翻转, 或者任何 SCG 承载对应的 PDCP SN 值达到最大值翻转,均可触发 S-KeNB 的更新过程,终端均要触发 SCG Change 流程重启 SCG 用户面。
从上述 LTE-A DC 在 RRM 相关配置、UE 能力协同、安全 S-KeNB 的产 生过程等方面可以看出,SeNB 侧操作很大程度上受到 MeNB 的约束和控制, SeNB 除了在 MAC 调度 PHY 传输上拥有灵活自主权之外,其他 RRC 高层控 制方面基本都受到 MeNB 的强控制,笔者称这种为单 RRC 模型,此时 SeNB 不具备独立的 RRC 决策和配置能力(注:在后面 5G MR-DC 技术中,还有双 RRC 模型,此时 SeNB SN 具备一定独立的 RRC 决策和配置能力,这就能削 弱主辅节点之间的耦合绑定关系)。
image.png
总的来说,LTE-A DC 是一种具有里程碑意义的关键小小区技术,它使终 端从过去的单无线链路工作方式,拓展到了双无线链路工作方式,并且未来可 能进一步拓展到更多无线链路的工作方式。多链路意味着更多的无线资源被聚 合利用和更强的无线链路健壮性。LTE-A DC 的网络侧和空口侧技术架构和主 要的工作方式,也为未来 5G NR 系统相关的多连接技术,如 MR-DC 奠定了 母胎雏形,后面读者将会看到:它不仅适用于同 RAT 系统内的双 / 多连接,还 可适用于多 RAT 系统之间的双 / 多连接操作。
在传统的单无线链路工作方式下,蜂窝网络的部署都只能以小区为中心, 处于服务小区不同物理位置的 UE,通常获得不同的无线覆盖和容量供给性能。 随着 UE 的移动,这种变化常常又会影响到用户的通信业务体验,如数据传输 速率高低变化、无线链路健壮性变差、数据传输中断等。5G UDN 部署为双 / 多无线链路协作创造了客观条件,蜂窝网络的部署可以逼近以用户为中心的终 极目标,即无论移动的 UE 处于网络中服务小区间的何种位置,都可获得多条 无线链路联合的无线覆盖和容量供给。由于多条无线链路之间的性能均衡补充 作用,用户的通信业务体验相对更容易保持前后一致,如数据传输速率保持稳 定、链路健壮性一直很好、无任何数据传输中断等。
在未来 5G UDN 部署场景下,UE 在异构微蜂窝环境下的双 / 多无线链路 协作能力,将会成为一种重要的能力标配,它不仅可以大大提高异构网络的系 统容量和无线资源利用率,还能大大提升用户对各种高性能通信业务的体验期望,如超高的用户峰值速率、超高稳定的平均数据吞吐率、极低的数据传输时 延和超可靠的无线链路健壮性等。有一些 5G 高性能要求蜂窝业务,如高清幻 真视频、移动虚拟现实、大数据同步、无人驾驶控制等,甚至只能在 5G UDN 部署下依赖多连接技术,才能顺利地开展应用,因此 5G 异构微蜂窝网络下的 CoMP 技术和双 / 多连接技术,也必将得到进一步的发展提升。

]]>
Oh My God,揭秘淘宝直播流畅买买买的背后! Mon, 27 Jan 2020 20:58:34 +0800 image.png
作者|马俊(东坡)
出品|阿里巴巴新零售淘系技术部

本文知识点提炼:
1、淘宝直播简介
2、四大保障手段
3、整体质量策略

淘宝直播简介

▐ 疯狂的淘宝直播

2019 年的双十一,淘宝直播又疯狂了一把!

双十一当天,淘宝直播成交近 200 亿!超过 50% 的品牌商家通过直播获得增长,直播成为了天猫双十一商家的标配。

image.png

▐ 增长背后的挑战

淘宝直播始于 2015 年,短短四年间打造了一个年成交千亿的行业,走出了一条与众不同的道路。在高速增长的背后是整个系统持续的稳定可靠,但是快速的业务迭代、复杂的系统设计和苛刻的成本控制给系统稳定性带来了不小的挑战。

电商直播是音视频、实时互动和电商交易的技术结晶,相比于传统的电商媒介(图文、视频)它的技术复杂度更高。整个技术框架由音视频流链路和电商/互动逻辑链路构成。音视频流主要涉及推流节点、中心流服务和播放节点;电商/互动逻辑核心是主播中控台、实时消息通道和互动播放。这些系统涉及到不同的技术架构,它们相互复杂地交织在一起,其中任何一个系统的变更都可能影响整个业务链路和逻辑。

image.png

随着 4G 移动网络普及,互联网内容从文字逐步演进到了视频、直播,直播形态的各种产品遍布互联网行业。移动时代直播本身就是一种新的产品形态,当它和电商交织在一起的时候就衍生出更多的可能。业务方往往要从这千万种的可能性中摸索出行之有效的业态和模式,而这种摸索意味着高强度的迭代和尝试。淘宝直播的客户端几乎以每周一个版本的速度前进,服务端更是以日在变化,我们同时维护着 8 个端;除此之外还要承接源源不断的合作诉求和大促职责。不停的迭代和变化确实能够带来产品的进化,但是过于频繁的变更也将系统的稳定性逼到了墙角。

image.png

除开前述的两点之外,直播的内容形式与曾经的图文是截然不同的。更加强调实时的互动、强烈的氛围和流畅的观看,而这些特点本身又对消息通道、网络和 CDN 等软硬件资源提出了更加苛刻的要求。

技术上必然追求最低的成本带给用户最好的体验,包括最小的带宽消耗、最大的机型覆盖、最清晰和流畅的观看体验。这就意味着,必须有一套有效的度量手段来评价我们的成本和产出让 ROI 最大化。

四大保障手段

淘宝直播的质量保障体系主要围绕着如何解决上述三大挑战来进行建设。限于文章的篇幅,会从最核心的四个方面来介绍如何有效地保障淘宝直播以及电商媒体的质量。

▐ 工具建设

俗话说“工欲善其事,必先利其器”。前文说过直播是一个迭代频繁的业务,测试人员在频繁的迭代下不得不面临一次次的测试和反复的业务回归。当业务高度复杂功能繁多的时候,这样的测试和回归简直就是噩梦。

以下图为例,图中展示了部分淘宝直播的互动玩法,通过主播中控台配置具体的互动玩法就能实时地投放到直播间与用户形成互动。但是不同的互动配置和不同的前端展现组合出大量的业务场景,仅靠传统的测试手段或者自动化是无法支撑的。

image.png

有什么更好的办法呢?回到业务逻辑实现,在拥有良好的编程规范的前提下,无论是客户端还是服务端,核心的业务逻辑实现是由各种内部或外部的接口组合而成。通过解构业务逻辑,再将接口组合成测试逻辑。仍旧以互动玩法为例,互动功能可以解构为互动服务、消息通道、主播服务和客户端渲染,将互动服务、消息通道和客户端渲染剥离再按照内置互动配置、调用消息通道接口触发客户端渲染的方式重新构建出新的功能,通过复用业务接口重新构建逻辑的方式可以将业务逻辑上不相关的能力关联起来形成一系列的测试工具。

对整个直播系统进行抽象解构,下图“业务域”内“的主播服务”通过消息通道和服务网关利用互动服务和直播基础服务与客户端进行交互,客户端自身具备通过服务端指令进行动态或者静态渲染的能力。在“测试域”内,可以将业务域解构的各个逻辑重新进行组合。

这种重组可以分为两个方面,客户端侧和服务端侧;在服务端侧不同的接口组合后可以重构出多维信息查询、互动模拟、开放验收等各种业务保障工具;在客户端侧基于动态、静态层和 native 的接口进行二次开发,将服务端信息和客户端本地能力聚合到测试专用的“调试浮层”便于快速能力验证、组件配置和信息透出等。

image.png

通过以上的思路,我们构建了成系列的直播(媒体)专用测试工具、打造了端侧媒体框架,全面提升了测试工作的效率,并以此为基础反复打磨形成一个完整的质量技术架构(见“整体质量策略-技术架构“)。

▐ 链路排查

仅仅通过测试工具提升效率是不够的,在快速迭代中会发生各种线上/线下的问题,问题的快速排查准确定位至关重要。淘宝直播的系统纷繁复杂,涉及到音视频流链路和电商/互动逻辑链路,横跨服务端、CDN、移动端和PC端。通常需要使用不同的工具、平台和手段进行问题排查,而且大多数时候平台之间数据无法关联互通。

image.png

因此需要为复杂的直播体系构建一套全链路的排查系统。工具建设是在接口层面进行业务解构和重新组合,那么链路排查也可以复用这种思想。不同的是要进行链路抽象和简化、业务流程划分、业务数据重组和排查流程构建。更直白的说,就是将不同的业务阶段和不同的技术平台进行抽象和划分;将同一技术平台的数据按照唯一的 ID 进行聚合,再将不同的阶段同一 ID 数据进行聚合;对于聚合在同一 ID 下的数据进行诊断,利用规则匹配、智能算法和人工经验;同时结合线下的测试工具,协助快速调试和复现。

image.png

▐ 数据分析

通过工具建设和链路排查再结合自动化手段,建立了高效的线上线下的质量保障能力;然而链路排查仅解决了具体的问题,对于系统和业务全局层面需要了解更多,例如线上整体质量状况、潜在问题的发现和预防、业务/技术效果评价等等。

在链路排查中,已经将不同业务阶段和技术阶段的数据进行了聚合和分析,在这些数据的基础上进行再加工,包括清洗、计算、聚合和分析,就能够将这些数据更有效地组织起来进行利用。

基于这样的想法,我们设计了一套数据分析的方法将数据划分为“大盘数据”、“纬度数据”、“详情分析”三个层次。

大盘数据主要针对线上的某个横向层面的整体分析和监控,一般划分为“业务”、“技术”、“舆情”、“异常”四个方面,大盘数据的波动意味着某个环节发生了问题。

在大盘的基础上按照不同的业务域和技术域进行拆分,每个域代表一个纬度的变化,每个纬度由多组该域内的指标构成;一般我们按照不同的端来划分技术域,按照不同的业务场景来划分业务域(图中仅为示意)。

当大盘、纬度划分清楚后,每个细节的数据指标都会归属到相应的“大盘”-“纬度”之下,再对这些细节的指标提供对比、趋势分析、多维度聚合等分析工具从而实现从全局到细节的分析和监控,针对特定的指标结合监控系统就能进行有效的告警。

image.png

这一整套数据分析的方法都建立在实时和离线的大数据分析平台之上。首先通过各端的上报工具采集原始数据形成实时数据流和转储的离线数据表;实时数据流通过实时计算平台(阿里云实时计算)对数据流进行清洗和计算;计算完成后将数据转储到搜索引擎,由引擎负责索引、排序和聚合;最后通过引擎接口返回给服务端,服务端可以对引擎提供的数据进行二次加工。

在整个过程中如果实时计算任务出现异常或者丢失,可以通过转储到离线表的数据进行补偿计算再流入到搜索引擎。

image.png

▐ 媒体质量

工具建设(技术框架)、链路排查和数据分析提供了通用的质量保障能力,可以被应用到直播或者多媒体之外的场景。而音视频(直播)有自身的特点,例如画质清晰度的要求、CDN带宽的消耗和移动端的性能限制等,需要媒体专项来保障,因此我们将这些专项定义为媒体质量。

媒体质量总结为三大测试专项和两个建设领域,三大测试专项指的是“特性测试”、“(媒体)SDK测试”、“专项测试”,两大建设领域分别是“音视频实验室建设”和“标准化建设”。

  • 特性测试(画质、特效、卡顿、延时等)

构建一套通用的媒体特性测试框架,对媒体的特性进行检测和评估

  • SDK测试(推流、播放、剪辑三大SDK )

构建统一的媒体demo、统一的SDK测试和报告、

  • 专项测试

覆盖各端的性能指标、渲染能力评估,同时与竞品对比

  • 音视频实验室建设

统一的线下物理实验室、模拟各种光照、音源、采集环境

  • 标准化建设

媒体质量评价的核心,三统一(环境统一、流程统一、标准统一),一体化执行结果可沉淀可分析

image.png

重点介绍下特性测试框架,整个框架由推流端的“预处理模块”、网络端的“可编程网络控制”、播放端的“分析模块”以及“评估模块”四部分块构成。

  • 预处理模块

通过hook的方式实现在推流侧统一采集内容、定制单帧检测点;

  • 可编程网络控制模块

通过程控方式来调节推流端到播放端的网络环境,自动实现网络环境切换统一网络参数;

  • 分析模块

主要是负责抓取播放端解码YUV数据并结合帧检测点和评估算法进行特性分析;

  • 评估模块

提供了不同的特性评价方法可以被分析模块调用。

通过这套框架,模拟完整且标准化的媒体场景,通过调节帧检测点、采集内容、网络参数、编解码参数等实现媒体特性的专项测试。

image.png

整体质量策略

通过这四年从无到有的摸索,淘宝直播和媒体电商业务最核心的质量策略可以抽象为一个核心思想、一套技术架构和一份能力模型。

▐ 核心思想

  • 质量体系必须是平台化的
  • 质量体系不仅仅服务测试
  • 质量体系必须数据说话

image.png

▐ 技术架构

  • 双端技术框架和全栈开发能力
  • 核心技术是大数据分析和媒体技术

image.png

▐ 能力模型

  • 链路排查将逐渐成为系统质量保障标配能力
  • 测试团队应当建设业务专项能力深度(多媒体专项)

image.png

We are hiring

淘系技术质量部电商丰富的场景,广阔的平台等你一起来挑战!
在这里你可以接触到全链路压测、海量的数据处理、人工智能推荐算法等领域;可以涉猎业界最前沿的测试技术、定期而丰富的技术分享;还可以与层层选拔的各路优秀同学共同战斗,共同成长!
欢迎测试开发工程师/专家加入我们,让科技引领面向未来的商业创新和进步。
简历投递至]]> 10亿计算下的合约广告,如何做个性化投放? Mon, 27 Jan 2020 20:58:34 +0800 作者| 荣纯

一、导言

合约保量广告(Guaranteed Delivery)是一种常见的品牌展示广告采买方式,现有的技术解决方案通常是在人群粒度上对问题进行抽象和建模,这种建模方式一方面忽略了相同人群下用户行为的差异,另一方面无法对用户粒度的约束进行精确的控制。

目前学术界关于合约广告流量分配问题的研究,通常会将这个问题抽象为合约侧-供给侧的二部图匹配问题,但目前的分配策略是停留在人群和标签粒度上,这要求人群和标签的划分必须是正交化的;除此之外,在人群层次上进行合约保量分配也还有不少局限性。

首先,由于只在人群层面进行分配,无法通过精准的用户行为预测将用户的个性化行为匹配至正确的广告,会降低广告主的投资回报率,进一步的降低广告平台的未来收入。其次,广告主通常会提出复杂的投放控制要求,比如在用户粒度的频次控制约束,一个典型的做法是,为了能够提高固定预算下的uv触达,广告主往往会限制单个uv的曝光频次。因此,传统的人群标签粒度分配的低效性使得其很难适用于目前的合约广告投放产品。

在本文中,我们尝试建立一个大规模分布式的合约广告投放分配算法,并在其中引入用户个性化的投放指标,在考虑用户交互行为的基础上,在用户粒度进行合约广告的投放分配工作。我们的算法可以处理复杂的约束,例如广告优先级,广告的展示频控以及广告贴位容量限制等。在这个基础上,我们还开发了实时预算平滑策略,通过预算平滑控制进一步优化广告的投放效果(如单位点击成本CPC)。目前我们的系统在阿里妈妈品牌展示广告实际承载了十亿规模的离线计算任务并在线应用,我们也将在最后给出离线和在线的实验结果来验证方案的的准确性。

二、问题定义

1201.png

这是一个经典的online assignment的问题,在数据规模较小且全局信息已知的情况下,使用匈牙利算法、网络流算法和混合整数规划方法都可以获得理想的最优解。但是对于互联网广告投放系统而言,数据规模很大,而且由于性能要求,对于一个请求,不可能也没办法确定全局信息。

1201.png

1201.jpg

1201.png

1201.png

1201.png

1201.png

1201.png

以上就是对问题的定义和描述,众所周知淘系用户日均DAU的规模是亿级别以上,而广告每天也成百上千的规模,在这样一个大规模求解的背景下,难点在于如何对这个问题进行求解并保持提供高并发、低延迟的广告服务。为此我们在离线阶段对这个问题进行了大规模的分布式求解,而在线阶段为了能够适配流量的变化情况,我们开发了独立的pacing模块来进行流量的平滑控制。

三、系统实现

3.1 系统描述

1201.png

上图是我们系统的整体架构图和整体的数据流程,主要包括面向广告主订单签订的CRM管理系统,离线算法和在线引擎部分。广告主作为管理平台和广告主之间的桥梁,主要用于订单信息的制定,包括人群圈选,流量询量,在线合约签单,创意信息绑定等。离线的处理框架将会同步这些合约信息,并与获取的离线日志一起,通过基于PS架构的分配优化模型进行离线分布式算法求解,将计算结果导入到Model中并同步至在线系统。最后当实时请求到来时,Merger作为在线的引擎,将请求RTP服务,pacing服务,和分配模型服务,经过离线的分配算法模型,得到最终投放的广告并返回给前端客户端展示。

3.2 离线优化

离线的部分算法分为2个阶段,第一个阶段我们将原问题转化为其对偶形式,通过并行化求解得到问题的对偶变量。第二阶段,由于现实投放优先级的影响,我们通过离线拓扑排序的方式,给出并行加速的方案,从而使得大规模数据集上的求解效率大幅度提升。

3.2.1 阶段一分布式求解

1201.png

对于我们的问题而言,用户的的量级远远大于合约的数量,所以求解合约侧对偶的的计算代价会远远大于供给侧。我们通过分布式计算的方式进行水平扩展,将供给侧对偶变量的计算分布在ps的worker节点上。而对于供给侧来说,其更新的过程放在server上,woker通过pull的方式获取最新的对偶变量。观察合约侧的对偶变量约束,我们发现其等式求解规模和广告主人群的规模是一致的,这个计算量对于通常的计算而言往往过于巨大,因此我们采用一种近似的方法来计算和更新这个对偶变量。注意到在更新的过程中,这个变量是一个保持单调递增的过程,相邻两轮的迭代满足这样一个不等式:

1201.png

1201.png

上述伪代码是我们更新对偶变量的算法描述,而关于供给侧对偶变量的求解,我们很容易注意到一个结论:

3.2.1-(7).png

而且关于这个方程的目标是单调的,因此可以通过二分求解的方式得到结果,具体过程我们在这里不进一步描述。

3.2.2 阶段二并行优先级加速

1201.png

1201.png

注意到原始的计算过程中 是单独顺序计算的,在数据规模较小的情况下,这个计算量尚可接受,但是当合约数量达到一定规模后,比如上万的规模,计算效率明显比较低下。而之前的性质告诉我们,对于任意2个正交的人群,他们的计算过程并没有优先级的重叠,因此是可以并行计算的。举个简单的例子,对于定向北京和上海的2个用户群来说,即便优先级存在不同,但是由于互相没有重叠,一个对偶的占量并不会影响另一个定向的库存,这样2个任务是完全可以并行计算的。

1201.png

在这个前提下,我们在离线构建二分图的时候,由于保存了每个用户的合约广告挂载情况,从而可以得到每个用户的实际订单的优先顺序,如上图所示,根据原始的订单分配顺序,进一步可以得到所有订单的DAG大图。最后很容易通过拓扑排序的方式得到我们优化后的并行化执行序列。接着拿上图进行举例,原始的更新顺序是[A] → [H], 通过DAG图的构建以及最终拓扑排序的结果,我们最终得到[A, B] → [C, D, E] → [F, G] → [H]这样只有4个需要处理的批次,相比于原先8个批次的执行,效率可以提升近一倍左右,而在实际的实验中,这个优化的效率提升会更加明显。

1201.png

3.3 在线pacing

之前的大多数展示投放,尤其是合约保量这里,许多系统采用了轮盘赌的方法或者是贪心算法来进行在线投放。由于在投放之前,我们模型的对偶变量已经确定,对于未来的投放概率往往是不可改变的,这样会带来一个问题,投放的结果将严重依赖于流量预估的结果,这会导致线上缺量或者超投的情况发生。为了能够及时的适应流量分布的变化,我们在合约投放之后增加一个pacing的调控模块。我们注意到相比于天级别的流量波动,分钟级别的波动往往比较小,因此我们可以在分钟级这个level上,进行实时的调控。

1201.png

1201.png

1201.png

区别于之前合约保量的HWM算法和SHALE算法,我们改进了线上的投放方式,原有的轮盘赌方式本质上在效果层面随机选择广告,对于展示效果而言会有信息损失。如果直接贪心选择效果最好的投放,在线上由于分配占量的问题,会有投放缺量风险,通过将pacing和XSHALE这两种方式结合在一起,引入实时的pacing调控,使得我们的方法可以更好适应线上流量的变化。

四、实验结果

我们在淘宝的数据集上进行离线的验证,这些数据收集于手淘banner和猜你喜欢的合约广告。在离线阶段我们会验证求解对偶变量的正确性和高效性,同时用实际的线上A/B测试来验证我们离线模型和在线pacing服务的正确性。

4.1 离线实验

我们和GD的经典算法HWM算法和SHALE算法进行比较,除了求解时间外,算法指标方面我们从投放完成率,惩罚项,L2-Norm和平均点击成本这四个指标来评测。四个指标的定义分别如下所示:

1201.png

1201.png

离线指标评测如下表和下图所示,在各种情况下与其他方法相比,我们的系统都能将CPC降低近50%。虽然我们在完成率,惩罚项和L2-Norm中有一些收益的损失,但与CPC降低幅度相比,我们可以接受这些损失。时间性能方面,HWM是最快的,因为它仅运行一次迭代。

但是,与SHALE和我们的方法相比,HWM的投放性能相对较差,线上不具备使用的可能。由于我们考虑了多目标的分配, SHALE在完成率方面略胜于我们的方法。但是我们的方法在改进投放指标方面有显著的的提升。如之前所述,广告分配问题的一个瓶颈是求解对偶变量α和β的时间。我们采用了并行化的方案以及基于DAG中拓扑排序的加速调度方式,可以更加快速的计算对偶变量α和β,这种加速比在大数据上的优势会更加的明显。

1201.png
1201.png

进一步分析发现,通过DAG中拓扑排序的加速调度方式,可以大幅度的提升求解性能。我们在真实的7天日常任务上进行测试,用OriginBatch来表示不经DAG加速的迭代轮数,用Reduce Batch来描述加速后的迭代轮数,可以很明显的看到,相比串行的方式,使用DAG的任务可以有效地将计算速度提高14倍左右。

1201.png

我们给出了超参learning rate的调参比较,可以很明显的看到,在learning rate取值较小时,其收敛速度是较慢的,当取值逐渐增大,其收敛速度会显著上升,而当learning rate过大时会在最优解周围出现震荡,极端情况l=2.0时,和最优解的距离相差非常之大,就我们的系统而言,我们通常将learning rate设置在0.5到0.7之间。

1201.png

最后我们测试了超参λ对投放完成率和平均每次点击费用的影响。 可以看到平均CPC随λ的增加而单调下降,也就是说如果想获得更大的平台收益,增大λ似乎是一个不错的选择,但是当λ特别大时,不仅造成了完成率的下降,点击成本也并不会进一步的下降,这样会导致缺量的发生,因此我们一般会控制完成率的损失在一定的范围内,如在1%-3%之间。

1201.png

4.2 在线实验

我们在手淘场景中对我们的方案进行了真实的线上测试,我们用线上的真实点击率来评估我们的投放指标。实际在线A/B实验中,采用预算分桶的测试机制来保障各个实验是在相同的预算下进行的。我们和基于贪心投放的基准算法以及传统SHALE算法进行了对比。而为了能够验证平滑pacing的作用,我们增加了SHALE-CLICK算法,即考虑点击效果,但不考虑pacing,采用轮盘赌方式进行投放。

从图a的曲线上看,使用我们的策略得到的投放曲线最为平滑,基于贪心的基准算法在开始就花费了一半以上的预算。接下来表现不错的是SHALE算法和SHALE-CLICK算法,通过轮盘赌的方式一定程度上进行了平滑的控制,但是由于线上流量分布的变化与离线并不完全一致,导致效果并不是最优的结果。

1201.png

图b给出了不同算法分桶下的实时累计平均点击率,可以看到考虑点击建模的SHALE-Click算法的表现优于贪心基准算法和SHALE算法。贪心算法由于投放过快,导致其流量优选的空间受到了限制,其表现最差。在pacing算法的平滑下,我们的算法表现最佳,在刚开始投放的过程中就显示出比较明显的优势。如图c所示,实验结果显示我们的方法在点击率方面相比baseline提升22.7%,相比SHALE提升21.2%,相比SHALE-CLICK提升10.6%。实验充分表明了我们算法的有效性。

五、总结

通过将用户的和广告的交互指标纳入到分配的目标中去,通过设计离线分布式算法和在线的实时调控算法,可以大幅度的提高合约保量广告的投放效率和合约完成情况。在线的分桶实验进一步表明我们的算法可以在为合约广告平台提供更多收益的同时提升广告主的品牌营销效果。以上工作被 ICDM'19 以长文录用,请参见《Large-Scale Personalized Delivery for Guaranteed Display Advertising with Real-Time Pacing》

编者按:我们是阿里妈妈品牌展示广告团队,一支以 90 后为主的年轻团队,急招算法工程师(base杭州/北京)请联系 brandship@alibaba-inc.com

]]>
年度大盘点 | 一文带你回顾阿里云边缘计算的2019 Mon, 27 Jan 2020 20:58:34 +0800 在5G万物智联时代,云计算逐步向分布式计算网络架构演进,边缘计算来到爆发前夜。它是连接5G商用潜能的助推器,是引领数据中心变革的技术支撑,为产业数字化转型和城市智慧化建设提供近终端、低延时、更经济的算力基础。

为了构建“离用户更近的计算”,阿里云始终奔跑在边缘计算的进阶之路上。下面让我们一起回顾,2019年阿里云边缘计算的「大事记」吧~

1月 边缘节点服务ENS 2.0发布

ENS 2.0引入MEC资源,将飞天算力调度、容器、函数、安全、大数据与AI等技术能力进一步融合释放,建立了“融合、开放、联动”的边缘计算平台,将计算能力进一步推进至用户10公里生活圈。同时,ENS 2.0也提供了更加弹性灵活、形态多样的分布式算力资源,并支持全面按量付费,算力、存储、带宽按天按月出账计费,帮助使用者进一步降低启动资金以及应对弹性业务需求。
图1.png

3月 边缘节点服务ENS正式上线OpenAPI

边缘节点服务ENS正式支持用户使用阿里云OpenAPI调用和管理边缘实例。基于ENS OpenAPI提供的创建、查询、管控能力,用户可以轻松构建针对边缘节点的自助运维体系。

4月 荣获“5G MEC优秀商用案例奖”

在2019中国联通合作伙伴大会上,阿里云“基于5G边缘计算的新零售应用案例”荣获2019年度MEC优秀商用案例奖。该方案基于5G热点覆盖,将视频通过稳定可靠的链路回传,并借助MEC实现视频业务本地分流和视频AI本地分析。云+网+业务一体化交付模型,让新零售产业从传统的自建IT基础设施及运维工作之中解放出来,转变为灵活按需一键启用边缘计算服务,大幅度减少综合投资成本,简化运维。点击查看详细新闻。

图2.png

6月 阿里云成为中国联通5Gⁿ+边缘云创新业务运营中心首批商用合作伙伴

2019年世界移动通信大会期间,中国联通5Gⁿ+边缘云创新业务运营中心正式成立。阿里云作为首批商用合作伙伴加入,双方将充分发挥各自技术优势,在边缘计算MEC节点、技术验证、产业应用等多方面展开深度合作,共同推进标准建设与5G应用场景创新。

图3.png

7月 国内首个全域边缘节点服务发布

300+边缘节点算力100%覆盖中国大陆31个省区的三大运营商与热门地区的三线城市,全域节点带宽分区定价,满足不同地区差异化需求,更好满足网络质量敏感、时延敏感、广覆盖、流量本地化的各行业客户为体验的追求。自此阿里云边缘计算正式宣告进入“无处不在”的全新篇章。点击查看详细新闻。

图4.png

8月 发布边缘K8s和容器解决方案

边缘节点服务ENS打通容器服务,支持边缘实例接入容器托管集群,赋能开发者DevOps轻松落地,统一进行资源管理与调度、镜像分发、自助运维和云端监控,极大简化了运维工作复杂度。

9月 阿里云定义边缘计算为城市计算

阿里云定义边缘计算是基于场景的城市计算。未来将围绕边缘芯片/设备、边缘计算平台(操作系统)、城市边缘中间件、城市边缘应用及服务这四个边缘技术栈进行布局,进一步与产业上下游的伙伴们深度融合,找到更丰富的、面向多行业的边缘应用场景,为客户打造离用户更‘近’的计算,进而承载更多新型的5G城市计算应用场景落地。点击查看详细新闻。

图5.JPG

9月 牵头编制《信息技术云计算边缘云计算通用技术要求(征求意见稿)》

该标准明确对边缘云计算系统的基础设施、服务能力、统一管控、接口、安全等通用技术要求进行统一的厘清与规范,用于指导“边缘云计算”技术和产品研发、服务交付。这是阿里云继2018年12月牵头制定业界首份《边缘云计算技术与标准化白皮书》后再次助力行业标准化升级。点击查看详细新闻。

图6.JPG

9月 发布边缘存储、边缘智能视频云

阿里云发布边缘存储、边缘视频云全新服务,结合此前的边缘节点服务、边缘容器,从底层资源能力、计算服务、PaaS平台、行业应用贯穿打通,全面升级边缘操作系统及分布式计算分发平台,形成体验式通用服务闭环,边缘计算再次向场景化服务迈出重要一步。点击查看ENS产品详情。

11月 边缘节点服务ENS SLA提升至99.9%

随着边缘节点服务场景持续拓展,ENS客户规模不断扩大,产品对客户的SLA承诺也不断加码。ENS依托稳定性三板斧,即 严格的机房和设备上线标准、全链路服务监控体系、故障快速响应恢复体系,实现99.9%稳定运营超过半年,并在11月正式升级官网SLA至99.9%。

12月 荣获最佳智能边缘计算技术创新平台

2019亚太内容分发大会,阿里云凭借在边缘计算领先的技术布局与创新方案,荣获“最佳智能边缘计算技术创新平台”奖项。点击查看详细新闻。

图8.jpg

12月 首批通过边缘云标准符合性测试

2019中国云计算标准与应用大会上,阿里云获得由中国电子技术标准化研究院颁发的首批《边缘云标准符合性测试证书》。这是业内权威机构首次开展边缘云领域的测评认证,对于产业上下游和技术服务商具有重要指导意义。期间,阿里云积极配合中国电子技术标准化研究院完成了全部用例的编制,并顺利通过测试验收。其用例包含基础设施、基础设施服务能力、平台能力、应用服务能力、统一管控、安全要求等多个方向,获得权威机构的一致认可。点击查看详细新闻。

图9.jpg

以上就是阿里云边缘计算2019年度盘点。

2020年是崭新的一年,阿里云边缘计算将继续秉承“融合、开放、联动”的理念,持续与产业伙伴连接,用技术升级大众娱乐,让科技赋能城市变革,用“离用户更近的计算”加速产业在5G万物智联之路上狂奔。

欢迎加入ENS产品答疑钉钉群:21740823,了解更多资讯。

]]>
【直播活动】全链路云上Elastic Stack | 技术专家讲解Elasticsearch“特有功能” Mon, 27 Jan 2020 20:58:34 +0800

图片2.png

点击链接,了解功能



Elasticsearch 增强版实例介绍

图形/语音向量检索

可视化打标插件:Label

可视化数据查询插件:Query Builder

阿里云Logstash Service

阿里云Beats 即将于2020年1月上线

]]>
国务院出台稳就业指导意见,用地、补贴等多层面鼓励返乡创业 Mon, 27 Jan 2020 20:58:34 +0800 国务院近日印发《关于进一步做好稳就业工作的意见》,要求创造更多就业岗位和稳定现有就业岗位并重,全力做好稳就业工作。

在扶持创业带动就业上,《意见》提出一系列新举措,鼓励和支持更多劳动者创业创新。

进一步放开放贷,加大创业担保贷款政策实施力度,建立信用乡村、信用园区、创业孵化示范载体推荐免担保机制;对“双创”平台进一步放权,实施“双创”支撑平台项目,引导“双创”示范基地、专业化众创空间等优质孵化载体承担相关公共服务事务;鼓励支持返乡创业,在用地、孵化载体建设、人才培训、创业补贴方面给予全方位支持。

根据《意见》,年度新增建设用地计划指标优先保障县以下返乡创业用地,支持建设一批农民工返乡创业园、农村创新创业和返乡创业孵化实训基地,建设一批县级农村电商服务中心、物流配送中心和乡镇运输服务站。同时,实施返乡创业能力提升行动,加强返乡创业重点人群、贫困村创业致富带头人、农村电商人才等培训培育。对返乡农民工首次创业且正常经营1年以上的,有条件的地区可给予一次性创业补贴。

《意见》全文如下:

各省、自治区、直辖市人民政府,国务院各部委、各直属机构:

就业是民生之本、财富之源。当前我国就业形势保持总体平稳,但国内外风险挑战增多,稳就业压力加大。为全力做好稳就业工作,现提出以下意见。

一、总体要求

以习近平新时代中国特色社会主义思想为指导,全面贯彻党的十九大和十九届二中、三中、四中全会精神,坚持把稳就业摆在更加突出位置,强化底线思维,做实就业优先政策,健全有利于更充分更高质量就业的促进机制,坚持创造更多就业岗位和稳定现有就业岗位并重,突出重点、统筹推进、精准施策,全力防范化解规模性失业风险,全力确保就业形势总体稳定。

二、支持企业稳定岗位

(一)加大援企稳岗力度。阶段性降低失业保险费率、工伤保险费率的政策,实施期限延长至2021年4月30日。参保企业面临暂时性生产经营困难且恢复有望、坚持不裁员或少裁员的失业保险稳岗返还政策,以及困难企业开展职工在岗培训的补贴政策,实施期限均延长至2020年12月31日。

(二)加强对企业金融支持。落实普惠金融定向降准政策,释放的资金重点支持民营企业和小微企业融资。鼓励银行完善金融服务民营企业和小微企业的绩效考核激励机制,增加制造业中小微企业中长期贷款和信用贷款。对扩大小微企业融资担保业务规模、降低小微企业融资担保费率等政策性引导较强的地方进行奖补。发挥各级政府中小企业工作领导小组的协调作用,支持中小企业发展,增加就业。发挥各级金融监管机构作用,鼓励银行为重点企业制定专门信贷计划,对遇到暂时困难但符合授信条件的企业,不得盲目抽贷、断贷。

(三)引导企业开拓国内市场。完善省际间信息沟通、收益分享等机制,鼓励中西部和东北地区各类产业园区与东部产业转出地区加强对接,及时掌握有转移意愿的企业清单。推广工业用地长期租赁、先租后让、租让结合和弹性年期供应方式,降低物流和用电用能成本,有条件的地区可加大标准厂房建设力度并提供租金优惠,推动制造业跨区域有序转移。搭建跨部门综合服务平台,加强企业产销融通对接,重点支持相关企业对接国内各大电商平台和各行业、各区域大宗采购项目,支持企业拓展国内市场销售渠道。

(四)规范企业裁员行为。支持企业与职工集体协商,采取协商薪酬、调整工时、轮岗轮休、在岗培训等措施,保留劳动关系。对拟进行经济性裁员的企业,指导其依法依规制定和实施职工安置方案,提前30日向工会或全体职工说明相关情况,依法依规支付经济补偿,偿还拖欠的职工工资,补缴欠缴的社会保险费。

三、开发更多就业岗位

(五)挖掘内需带动就业。实施社区生活服务业发展试点,开展家政服务业提质扩容“领跑者”行动试点工作,深入推进家政培训提升行动和家政服务领域信用建设专项行动。加强旅游公共设施建设,推进区域医疗中心建设,开展支持社会力量发展普惠托育服务专项行动。支持养老服务业发展,通过政府购买服务等方式,支持养老服务机构向重点人群提供服务。鼓励汽车、家电、消费电子产品更新消费,有力有序推进老旧汽车报废更新,鼓励限购城市优化机动车限购管理措施。培育国内服务外包市场,支持行政事业单位、国有企业采购专业服务。

(六)加大投资创造就业。合理扩大有效投资,适当降低部分基础设施等项目资本金比例,加快发行使用地方政府专项债券,确保精准投入补短板重点项目。实施城镇老旧小区改造、棚户区改造、农村危房改造等工程,支持城市停车场设施建设,加快国家物流枢纽网络建设。深入实施新一轮重大技术改造升级工程。

(七)稳定外贸扩大就业。研究适时进一步降低进口关税和制度性成本,扩大出口信用保险覆盖面、合理降低保费,确保审核办理正常退税平均时间在10个工作日以内。发挥行业协会、商会、中介机构等作用,引导企业增强议价能力,鼓励提供公益法律服务。建设国际营销服务体系,加快跨境电子商务综合试验区建设,做大做强外贸综合服务企业。

(八)培育壮大新动能拓展就业空间。加快5G商用发展步伐,深入推进战略性新兴产业集群发展工程,加强人工智能、工业互联网等领域基础设施投资和产业布局。支持科技型企业开展联合技术攻关,完善首台(套)重大技术装备示范应用扶持政策,支持科技型企业到海外投资。加快落实促进平台经济规范健康发展的指导意见,促进新产业新业态新模式快速发展。

四、促进劳动者多渠道就业创业

(九)鼓励企业吸纳就业。降低小微企业创业担保贷款申请条件,当年新招用符合条件人员占现有职工比例下调为20%,职工超过100人的比例下调为10%。对企业吸纳登记失业半年以上人员就业且签订1年以上劳动合同并按规定缴纳社会保险的,有条件的地区可给予一次性吸纳就业补贴,实施期限为2020年1月1日至12月31日。

(十)扶持创业带动就业。持续推进简政放权、放管结合、优化服务改革,进一步优化营商环境,鼓励和支持更多劳动者创业创新。加大创业担保贷款政策实施力度,建立信用乡村、信用园区、创业孵化示范载体推荐免担保机制。实施“双创”支撑平台项目,引导“双创”示范基地、专业化众创空间等优质孵化载体承担相关公共服务事务。鼓励支持返乡创业,年度新增建设用地计划指标优先保障县以下返乡创业用地,支持建设一批农民工返乡创业园、农村创新创业和返乡创业孵化实训基地,建设一批县级农村电商服务中心、物流配送中心和乡镇运输服务站。实施返乡创业能力提升行动,加强返乡创业重点人群、贫困村创业致富带头人、农村电商人才等培训培育。对返乡农民工首次创业且正常经营1年以上的,有条件的地区可给予一次性创业补贴。

(十一)支持灵活就业和新就业形态。支持劳动者通过临时性、非全日制、季节性、弹性工作等灵活多样形式实现就业。研究完善支持灵活就业的政策措施,明确灵活就业、新就业形态人员劳动用工、就业服务、权益保障办法,启动新就业形态人员职业伤害保障试点,抓紧清理取消不合理限制灵活就业的规定。对就业困难人员享受灵活就业社会保险补贴政策期满仍未实现稳定就业的,政策享受期限可延长1年,实施期限为2020年1月1日至12月31日。

(十二)加强托底安置就业。加大对就业困难人员的就业援助力度,鼓励围绕补齐民生短板拓展公益性岗位。对从事公益性岗位政策期满仍未实现稳定就业的,政策享受期限可延长1年,实施期限为2020年1月1日至12月31日。在农村中小型基础设施建设、农村危房改造中实施以工代赈,组织建档立卡贫困人口参与工程项目建设。

(十三)稳定高校毕业生等青年就业。继续组织实施农村教师特岗计划、“三支一扶”计划等基层服务项目。公开招聘一批乡村教师、医生、社会工作者充实基层服务力量。扩大征集应届高校毕业生入伍规模。扩大就业见习规模,适当提高补贴标准,支持企业开发更多见习岗位。

五、大规模开展职业技能培训

(十四)大力推进职业技能提升行动。落实完善职业技能提升行动政策措施,按规定给予职业培训补贴和生活费补贴。针对不同对象开展精准培训,全面开展企业职工技能提升培训或转岗转业培训,组织失业人员参加技能培训或创业培训,实施农民工、高校毕业生、退役军人、建档立卡贫困人口、残疾人等重点群体专项培训计划。支持职业院校(含技工院校)积极承担相应培训任务。

(十五)扩大技能人才培养培训规模。推进落实职业院校奖助学金调整政策,扩大高职院校奖助学金覆盖面、提高补助标准,设立中等职业教育国家奖学金。推进各地技师学院、技工学校纳入职业教育统一招生平台。组织城乡未继续升学的初高中毕业生、20岁以下有意愿的登记失业人员参加劳动预备制培训,按规定给予培训补贴,对其中的农村学员和困难家庭成员给予生活费补贴,实施期限为2020年1月1日至12月31日。

(十六)加强职业培训基础能力建设。启动国家产教融合建设试点,加强公共实训基地和产教融合实训基地建设。支持各类企业和职业院校(含技工院校)合作建设职工培训中心、企业大学和继续教育基地,鼓励设备设施、教学师资、课程教材等培训资源共建共享。实施新职业开发计划,加大职业技能标准和职业培训包开发力度,建立急需紧缺职业目录编制发布制度。

六、做实就业创业服务

(十七)推进就业服务全覆盖。劳动年龄内、有劳动能力、有就业要求、处于失业状态的城乡劳动者可在常住地进行失业登记,申请享受基本公共就业服务。健全就业信息监测系统,开放线上失业登记入口,实现失业人员基本信息、求职意愿和就业服务跨地区共享。加强重大项目、重大工程、专项治理对就业影响跟踪应对,对涉及企业关停并转的,主管部门要及时将企业信息提供给当地人力资源社会保障部门;对可能造成规模性失业的,要同步制定应对措施。

(十八)加强岗位信息归集提供。政府投资项目产生的岗位信息、各方面开发的公益性岗位信息,在本单位网站和同级人力资源社会保障部门网站公开发布。健全岗位信息公共发布平台,市级以上公共就业人才服务机构要在2020年3月底前实现岗位信息在线发布,并向省级、国家级归集,加快实现公共机构岗位信息区域和全国公开发布。

(十九)强化常态化管理服务。实施基层公共就业服务经办能力提升计划,建立登记失业人员定期联系和分级分类服务制度,每月至少进行1次跟踪调查,定期提供职业介绍、职业指导、创业服务,推介就业创业政策和职业培训项目,对其中的就业困难人员提供就业援助。加强重点企业跟踪服务,提供用工指导、政策咨询、劳动关系协调等服务和指导。公共就业人才服务机构、经营性人力资源服务机构和行业协会提供上述服务的,有条件的地区可根据服务人数、成效和成本等,对其给予就业创业服务补助。

七、做好基本生活保障

(二十)更好发挥失业保险作用。对符合领取失业保险金条件的人员,及时发放失业保险金。对领取失业保险金期满仍未就业且距离法定退休年龄不足1年的人员,可继续发放失业保险金直至法定退休年龄。对失业保险金发放出现缺口的地区,采取失业保险调剂金调剂、地方财政补贴等方式予以支持。

(二十一)做好困难人员生活保障。对符合条件的生活困难下岗失业人员,发放临时生活补助。对生活困难的失业人员及家庭,按规定纳入最低生活保障、临时救助等社会救助范围。对实现就业的低保对象,可通过“低保渐退”等措施,增强其就业意愿和就业稳定性。

八、加强组织保障

(二十二)完善工作组织协调机制。县级以上地方政府要切实履行稳就业主体责任,建立政府负责人牵头、相关部门共同参与的工作组织领导机制,明确目标任务、工作责任和督促落实机制,统筹领导和推进本地区稳就业工作和规模性失业风险应对处置,压实促进就业工作责任。

(二十三)完善资金投入保障机制。积极投入就业补助资金,统筹用好失业保险基金、工业企业结构调整专项奖补资金等,用于企业稳定岗位、鼓励就业创业、保障基本生活等稳就业支出。有条件的地方可设立就业风险储备金,用于应对突发性、规模性失业风险。

(二十四)完善就业形势监测机制。持续抓好就业常规统计,提升数据质量和时效性,多维度开展重点区域、重点群体、重点行业、重点企业就业监测。加强移动通信、铁路运输、社保缴纳、招聘求职等大数据比对分析,健全多方参与的就业形势研判机制。

(二十五)完善突发事件处置机制。各地区要第一时间处置因规模性失业引发的群体性突发事件,防止矛盾激化和事态扩大。处置过程中,当地政府可根据需要与可能、统筹不同群体就业需求,依法依规制定临时性应对措施。

(二十六)完善舆论宣传引导机制。大力宣传党中央、国务院稳就业决策部署和支持就业创业政策措施,引导广大劳动者树立正确的劳动观、价值观,选树一批促进就业创业工作典型经验、典型人物,发掘一批在中西部和东北地区、艰苦边远地区、城乡基层就业创业的先进典型,及时开展表彰激励。牢牢把握信息发布和舆论引导主动权,做好舆情监测研判,建立重大舆情沟通协调和应急处置机制,消除误传误解,稳定社会预期。

]]>
第一届阿里云数据可视化峰会圆满落幕 Mon, 27 Jan 2020 20:58:34 +0800 12月23日,2019数据可视化年度峰会在阿里巴巴西溪园区白马山庄举行,峰会以“唤醒数据,看见未来”为主题,邀请著名专家学者及一线行业实践者结合自身实践与感悟,与现场业界来宾分享主题演讲。百余位业界KOL齐聚一堂,深入解读数据可视化研发生态,剖析可视化商业化应用的前景和机会,共同探讨了如何创建创新共赢的数据可视化环境。

现场图1.jpg

(现场的气氛可谓是热火朝天啊!还有站在后面的小伙伴,可谓是一票难求)

没有抢到票的小伙伴看看会上都有哪些看点吧!

前瞻数据可视化学术方向与技术构建

中国科技创新2030“新一代人工智能”和“大数据”专项均将可视化和可视分析列为大数据智能急需突破的关键共性技术,可见国家对可视化领域的重视,以及可视化未来蓬勃的机会。目前数据可视化的产品以轻量级小数据可视化工具为主,产品底层技术特性与国外同类产品存在一定差距,所以目前重视大数据行业中可视化生态系统的培育以及基础理论与方法研究还是非常必要的。

同济大学主任曹楠教授

在《针对事件序列数据的可视化及应用》的演讲中,曹楠教授认为对大规模事件序列数据的可视分析与预测能够清晰的揭示用户行为的内在规律与因果关系,在网络安全、电子商务、智慧交通以及精准医疗等诸多领域拥有广泛的应用价值。

lADPDgQ9rYdWvtzNDS_NF3A_6000_3375.jpg

浙江大学博士梅鸿辉

梅鸿辉博士为我们分享了数据可视化的展望与趋势,其中包括中国可视化发展在国际上崛起的历史、可视化学术研发的方向、以及“可视化+”的概念等等。大数据的数据获取、数据清洗、数据模型、数据分析、预测仿真这些环节中都能与可视化融合,形成了可视化+的概念。更提到,数据可视化的研发趋势需要从小数据扩展到大数据,从少数专家扩展到广泛的不特定群体,在实际应用中强调方法的性能、使用的简捷和系统的智能。

阿里云数据可视化团队技术负责人宁朗

宁朗是阿里最早跟随团队将数据可视化真正落地到数据产品,并推动可视化技术能力对外输出的。他在会上中指出大数据可视化不仅仅是数据大屏,强调大数据大屏完成之前的数据收集和清洗非常关键,需要打破数据链融通之间的隔阂。他认为,数据可视化要有行业开发规范、把握创新边界,还要考虑呈现数据对象的感受,而不是盲目的开发。而通过可视化工具,可以响应可视化的快速变动。

宁朗.jpg

洞察可视化生态机会点及DataV规划

随着物联网、云计算、移动互联网等技术的突破,更多的数据得到收集,同时也推动了数据可视化一直不断发展,目前金融、交通运输、生产领域、医疗卫生等行业更是对数据可视化愈发显示刚性需求,正在推进可视化发展的上升拐点。
可视化的终极目标是洞悉蕴含在数据中的现象和规律,从而帮助用户高效而准确的进行决策,所以可视化生态发展的机会点也是业界一直所关注的。

ECharts可视分析负责人李德清

在会上详细地介绍了E charts在可视化中的基建,它基于 Javascript 的数据可视化图表库,可提供直观、生动、可交互、可个性化定制的数据可视化图表。

李得清.jpg

数据城市派的CEO派姐

数据可视化工具可以帮助企业降低数据可视化的成本,使杂乱、大量的数据的可读性得到提高,让企业可以在数据中找到规律,行业上有许多数据可视化案例可以借鉴。目前在实际应用中数据可视化也具有非常广泛的应用场景。作为专业的大数据服务商,派姐对数据可视化的应用场景有深刻的理解,在演讲中以“数据可视化的应用场景”为主题分享了自己的见解。派姐首先回顾了城市规划可视化的发展历程,总结了大数据时代下,城市规划师利用数据可视化的各类场景,包括前期分析、现状分析、辅助设计、辅助决策、辅助服务与管理、数字孪生、感知未来等多种场景。对城市规划圈的数据可视化应用与发展提出了一些希望与建议。

派姐.jpg

阿里云产品专家央久

作为最早进入可视化领域产品之一的DataV,从双11出发,经历过开源、产品化、打包解决方案、再到平台化,数据大屏应用场景也从双 11 电商作战,扩展到智慧城市、智慧交通等诸多领域,所以DataV未来的规划也是备受关注。央久在会上表示DataV未来要做专业化、行业化、智能化的可视化开发工具平台, 能完整地帮助数据可视化从管理到投放,从单点功能向全面化智能发展。

央九1.jpg

大屏显示领域资深技术专家董航程

董航程在《数据可视化LED屏的行业趋势》的演讲中指出目前人性化的设计对工业设计是非常重要的,DataV+优秀的大屏硬件才是未来更好的可视化软硬一体的解决方案。

董航程.jpg

阿里云MVP陈琦

陈琦在现场与大家分享了《数据可视化工作流进化史》。陈琦的团队依托阿里云强大的产品支撑能力,为中央宣传部、国家电网、上海申通地铁等大型国企提供专业的数据可视化定制解决方案。他认为数据可视化的大屏要做数据科学的艺术品,优秀的数据可视化大屏要让观众找到共鸣、找到规律。

陈琦.jpg

此次峰会我们一起探讨了数据可视化的学术动态,了解了数据可视化的基建和技术构建,以及对应用场景的探讨和更多的对未来的期待。相信未来数据可视化会以更细化的形式表达数据,以更全面的维度理解数据,以更美的方式呈现数据,使可视化更加具有冲击力。

]]>
Serving Client 介绍 Mon, 27 Jan 2020 20:58:34 +0800 本文选自《Knative 云原生应用开发指南》
knative海报.png
更多云原生技术资讯可关注阿里巴巴云原生技术圈

Golang Context

在正式开始介绍 Knative Serving SDK 之前我们先简单的介绍一下 Golang Context 的机理,因为在 Knative Serving 中 client、Informer 的初始化和信息传递完全是基于 Golang Context 实现的。
Golang 是从 1.7 版本开始引入的 Context ,Golang 的 Context 可以很好的简化多个 goroutine 之间以及请求域间的数据传递、取消信号和截至时间等相关操作。Context 主要有两个作用:

  1. 传输必要的数据
  2. 进行协调控制,比如终止 goroutein、设置超时时间等

Context 定义
Context 本身是一个接口

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}

这个接口中定义了四个方法,下面分别介绍如下:

  • Deadline 方法是获取设置的截止时间的意思,到了这个时间点,Context 会自动发起取消请求
  • Done 方法返回一个只读的 chan,如果该方法返回的 chan 可以读取,则意味着 parent Context 已经发起了取消请求, 此时应该应该做清理操作,然后退出 goroutine 并释放资源
  • Err 方法返回取消的错误原因
  • Value 方法获取该 Context 上绑定的值,是一个键值对。所以要通过一个 Key 才可以获取对应的值,这个值是线程安全的

    关于 Context 主要记住一点:可以通过 Value 传递数据,Value 是一个键值对结构。更多详细的介绍可以参见下面这些文章:
  • Concurrency Patterns in Go
  • How to correctly use context.Context in Go 1.7
  • Using context cancellation in Go
  • Go Context

Knative Serving client 源码浅析

在 Context 的这些特性中,Knative Serving 中重度依赖的是 Value 功能。以  Service 的 Informer 初始化为例进行说明,这里可以看到源码

Informer “构造函数”是在 init 函数中自动注册到 injection.Default 中的。当 Informer “构造函数”被调用之后会自动把生成的 Informer 注入到 Context 中 context.WithValue(ctx, Key{}, inf), inf.Informer()
1.png

从上图中可以看到,Informer 初始化的时候需要调用 factory,而 factory 本身是从  Context 中获取的。下面我发再看看 factory 是怎么初始化的。
factory 的初始化
2.png

可以发现 factory 也是把“构造函数”注册到 injection.Default 中,并且会把生成的 SharedInformerFactory 注入到 Context 中。而且 factory 中使用的 client(链接 kube-apiserver 使用的对象)也是从 Context 获取到的。

可以说 Knative Serving SDK 初始化的过程是面向 Context 编程的。关键对象是自动注入到 Context,在使用的时候从 Context 中取出。
顺带提一点,Knative Serving 的日志对象也是在 Context 保存的,当需要打印日志的时候先通过 logger := logging.FromContext(ctx) 从 Context 中拿到 logger,然后就可以使用了。这样做的好处是可以通过管理 logger 对象,比如做 trace 功能。如下所示是基于 logger 打印出来的日志,可以看到对于同一个请求的处理是可以通过 traceID 进行追踪的。下面这段日志都是对 577f8de5-cec9-4c17-84f7-f08d39f40127 这个  trace 的处理。

{"level":"info","ts":"2019-08-28T20:24:39.871+0800","caller":"controller/service.go:67","msg":"Reconcile: default/helloworld-go","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
{"level":"info","ts":"2019-08-28T20:24:39.871+0800","caller":"controller/controller.go:339","msg":"Reconcile succeeded. Time taken: 487.347µs.","knative.dev/traceid":"90653eda-644b-4b1e-8bdb-4a1a7a7ff0d8","knative.dev/key":"eci-test/helloworld-go"}
{"level":"info","ts":"2019-08-28T20:24:39.871+0800","caller":"controller/service.go:106","msg":"service: default/helloworld-go route: default/helloworld-go ","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
{"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:67","msg":"Reconcile: eci-test/helloworld-go","knative.dev/traceid":"22f6c77d-8365-4773-bd78-e011ccb2fa3d","knative.dev/key":"eci-test/helloworld-go"}
{"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:116","msg":"service: default/helloworld-go revisions: 1 ","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
{"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:118","msg":"service: default/helloworld-go revision: default/helloworld-go-cgt65 ","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
{"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/controller.go:339","msg":"Reconcile succeeded. Time taken: 416.527µs.","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
{"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:106","msg

使用 Knative Serving SDK

介绍完 Knative Serving client 的初始化过程,下面我们看一下应该如何在代码中用 Knative Serving SDK 进行编码。
示例参见:https://github.com/knative-sample/serving-controller/blob/b1.0/cmd/app/app.go
这个示例中首先使用配置初始化 *zap.SugaredLogger对象,然后基于 ctx := signals.NewContext() 生成一个 Context。signals.NewContext() 作用是监听 SIGINT 信号,也就是处理 CTRL+c 指令。这里用到了 Context 接口的 Done 函数机制。

构造 Informer
接着使用 ctx, informers := injection.Default.SetupInformers(ctx, cfg) 构造出所有的 informer,然后调用下面这段代码执行注入,把 informer 注入到 Context 中。

// Start all of the informers and wait for them to sync.
    logger.Info("Starting informers.")
    if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
        logger.Fatalw("Failed to start informers", err)
    }

3.png

从 Context 中获取 Informer
实例代码: https://github.com/knative-sample/serving-controller/blob/v0.1/pkg/controller/controller.go
4.png

如上所示,所有的 informer 都是从 Context 中获取的。
最后 Controller 初始化一个 Reconciler 接口,接口的定义如下, 里面只有一个 Reconcile 函数。这个使用方式和 sigs.k8s.io/controller-runtime 使用的逻辑是一样的。如果你之前写过 Operator 之类的功能,对这个操作应该不会陌生。

// Reconciler is the interface that controller implementations are expected
// to implement, so that the shared controller.Impl can drive work through it.
type Reconciler interface {
    Reconcile(ctx context.Context, key string) error
}

在 Reconcile 中调用 Knative API

代码示例: https://github.com/knative-sample/serving-controller/blob/v0.1/pkg/controller/service.go
5.png

现在就可以在 Reconcile 中通过 c.serviceLister.Services(namespace).Get(name) 这种方式直接操作 Seving 资源了。

至此已经把基于 Knative Seving 开发 Serverless 应用的关键脉梳理了一遍。更详细的代码示例请参见:https://github.com/knative-sample/serving-controller/tree/b1.0 ,这里面有完整可以运行的代码。
本文提到的示例代码是基于 Knative 0.10 版本开发的,这个版本的 SDK 需要使用 Golang 1.13 才行。
另外除了文章中提到的示例代码,提供另外一个例子(https://github.com/knative-sample/serving-sdk-demo/tree/b1.0) 这个例子是直接使用 Knative SDK 创建一个 ksvc,这两个例子的侧重点有所不同。可以参考着看。

小结

本文从 Knative Serving client 的初始化过程开始展开,介绍了 Knative informer 的设计以及使用方法。通过本文你可以了解到:

参考资料

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

]]>
定义无处不在的事件 - CloudEvent Mon, 27 Jan 2020 20:58:34 +0800 本文选自《Knative 云原生应用开发指南》
knative海报.png
更多云原生技术资讯可关注阿里巴巴云原生技术圈

背景

Event 事件无处不在,然而每个事件提供者产生的事件各不相同。由于缺乏事件的统一描述,对于事件的开发者来说需要不断的重复学习如何消费不同类型的事件。这也限制了类库、工具和基础设施在跨环境(如 SDK、事件路由或跟踪系统)提供事件数据方面的潜力。从事件数据本身实现的可移植性和生产力上受到了阻碍。

什么是 CloudEvents

CloudEvents 是一种规范,用于以通用格式描述事件数据,以提供跨服务、平台和系统的交互能力。
事件格式指定了如何使用某些编码格式来序列化 CloudEvent。支持这些编码的兼容 CloudEvents 实现必须遵循在相应的事件格式中指定的编码规则。所有实现都必须支持 JSON 格式。

协议规范

命名规范

CloudEvents 属性名称必须由 ASCII 字符集的小写字母(“a”到“z”)或数字(“0”到“9”)组成,并且必须以小写字母开头。属性名称应具有描述性和简洁性,长度不应超过20个字符。

术语定义

本规范定义如下术语:

  • Occurrence: “Occurrence”是指在软件系统运行期间捕获描述信息。
  • Event: "Event" 是表示事件及其上下文的数据记录。
  • Context:  Context 表示上下文,元数据将封装在 Context 属性中。应用程序代码可以使用这些信息来标识事件与系统或其他事件之间的关系。
  • Data: 实际事件中有效信息载荷。
  • Message: 事件通过 Message 从数据源传输到目标地址。
  • Protocol: 消息可以通过各种行业标准协议(如http、amqp、mqtt、smtp)、开源协议(如kafka、nats)或平台/供应商特定协议(aws-kineis、azure-event-grid)进行传递。

上下文属性(Context Attributes)

符合本规范的每个 CloudEvent 必须包括根据需要指定的上下文属性,并且可以包括一个或多个可选的上下文属性。
参考示例:

specversion: 0.2
  type: dev.knative.k8s.event
  source: /apis/serving.knative.dev/v1alpha1/namespaces/default/routes/sls-cloudevent
  id: 269345ff-7d0a-11e9-b1f1-00163f005e02
  time: 2019-05-23T03:23:36Z
  contenttype: application/json
  • type: 事件类型, 通常此属性用于路由、监控、安全策略等。
  • specversion: 表示 CloudEvents 规范的版本。引用 0.2 版本的规范时,事件生产者必须使用 0.2 设置此值。
  • source:表示事件的产生者, 也就是事件源。
  • id: 事件的 id
  • time: 事件的产生时间
  • contenttype: 表示Data 的数据内容格式

扩展属性(Extension Attributes)

CloudEvents 生产者可以在事件中包含其他上下文属性,这些属性可能用于与事件处理相关的辅助操作。

Data

正如术语Data所定义的,CloudEvents 产生具体事件的内容信息封装在数据属性中。例如,KubernetesEventSource所产生的 CloudEvent 的Data信息如下:

data:
  {
    "metadata": {
      "name": "event-display.15a0a2b54007189b",
      "namespace": "default",
      "selfLink": "/api/v1/namespaces/default/events/event-display.15a0a2b54007189b",
      "uid": "9195ff11-7b9b-11e9-b1f1-00163f005e02",
      "resourceVersion": "18070551",
      "creationTimestamp": "2019-05-21T07:39:30Z"
    },
    "involvedObject": {
      "kind": "Route",
      "namespace": "default",
      "name": "event-display",
      "uid": "31c68419-675b-11e9-a087-00163e08f3bc",
      "apiVersion": "serving.knative.dev/v1alpha1",
      "resourceVersion": "9242540"
    },
    "reason": "InternalError",
    "message": "Operation cannot be fulfilled on clusteringresses.networking.internal.knative.dev "route-31c68419-675b-11e9-a087-00163e08f3bc": the object has been modified; please apply your changes to the latest version and try again",
    "source": {
      "component": "route-controller"
    },
    "firstTimestamp": "2019-05-21T07:39:30Z",
    "lastTimestamp": "2019-05-26T07:10:51Z",
    "count": 5636,
    "type": "Warning",
    "eventTime": null,
    "reportingComponent": "",
    "reportingInstance": ""
  }

实现

以 Go SDK 实现 CloudEvent 0.2 规范为例:

事件接收服务

  • 导入cloudevents
import "github.com/cloudevents/sdk-go"

-通过 HTTP 协议接收 CloudEvent 事件

func Receive(event cloudevents.Event) {
    fmt.Printf("cloudevents.Eventn%s", event.String())
}

func main() {
    c, err := cloudevents.NewDefaultClient()
    if err != nil {
        log.Fatalf("failed to create client, %v", err)
    }
    log.Fatal(c.StartReceiver(context.Background(), Receive));
}

事件发送服务

  • 创建一个基于  0.2 协议的 CloudEvent 事件
event := cloudevents.NewEvent()
event.SetID("ABC-123")
event.SetType("com.cloudevents.readme.sent")
event.SetSource("http://localhost:8080/")
event.SetData(data)
  • 通过HTTP协议发送这个CloudEvent
t, err := cloudevents.NewHTTPTransport(
    cloudevents.WithTarget("http://localhost:8080/"),
    cloudevents.WithEncoding(cloudevents.HTTPBinaryV02),
)
if err != nil {
    panic("failed to create transport, " + err.Error())
}

c, err := cloudevents.NewClient(t)
if err != nil {
    panic("unable to create cloudevent client: " + err.Error())
}
if err := c.Send(ctx, event); err != nil {
    panic("failed to send cloudevent: " + err.Error())
}

这样我们就通过 Go SDK 方式实现了 CloudEvent 事件的发送和接收。

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

]]>
关于 Broker/Trigger 事件模型 Mon, 27 Jan 2020 20:58:34 +0800 本文选自《Knative 云原生应用开发指南》
knative海报.png
更多云原生技术资讯可关注阿里巴巴云原生技术圈

Broker 和 Trigger

从 v0.5 开始,Knative Eventing 定义 Broker 和 Trigger 对象,从而能方便的对事件进行过滤(亦如通过 ingress 和 ingress controller 对网络流量的过滤一样)。

  • Broker 提供一个事件集,可以通过属性选择该事件集。它负责接收事件并将其转发给由一个或多个匹配 Trigger 定义的订阅者。
  • Trigger 描述基于事件属性的过滤器。同时可以根据需要创建多个 Trigger。

    如图:

6.png

当前实现方式

Namespace

通过Namespace Reconciler (代码:eventing/pkg/reconciler/v1alpha1/namespace/namespace.go)创建 broker。Namespace Reconciler 会查询所有带knative-eventing-injection: enabled 标签的 namespace。如果存在这样标签的 namespace, 那么Namespace Reconciler将会进行如下处理操作:

  1. 创建 Broker 过滤器的 ServiceAccount:eventing-broker-filter
  2. 通过 RoleBinding 确保 ServiceAccount 的 RBAC 权限
  3. 创建名称为 default 的 Broker
// newBroker creates a placeholder default Broker object for Namespace 'ns'.
func newBroker(ns *corev1.Namespace) *v1alpha1.Broker {
    return &v1alpha1.Broker{
        ObjectMeta: metav1.ObjectMeta{
            Namespace: ns.Name,
            Name:      defaultBroker,
            Labels:    injectedLabels(),
        },
    }
}

Broker (事件代理)

通过Broker Reconciler进行处理 broker,对于每一个 broker, 会进行一下处理操作:

  1. 创建 'trigger'Channel。所有在 Broker 中的 event 事件都会发送到这个Channel, 所有的 Trigger 会订阅这个Channel
  2. 创建'filter'Deployment。这个 Deployment 会运行cmd/broker/filter。其目的是处理与此 Broker 相关的所有 Trigger 的数据平面。说白了其实就做了两件事情,从 Channel 中接收事件,然后转发给事件的订阅者。
  3. 创建'filter' Kubernetes Service。通过该 Service 提供'filter' Deployment的服务访问。
  4. 创建'ingress' Deployment。这个 Deployment 会运行 cmd/broker/ingress。其目的是检查进入 Broker的所有事件
  5. 创建'ingress' Kubernetes Service。通过该 Service提供'Ingress' Deployment的服务访问。
  6. 创建'ingress' Channel。这是一个 Trigger 应答的 Channel。目的是将 Trigger 中返回的事件通过 Ingress Deployment 回写到 Broker。理想情况下,其实不需要这个,可以直接将 Trigger 的响应发送给 k8s Service。但是作为订阅的场景,只允许我们向 Channel 发送响应信息,所以我们需要这个作为中介。
  7. 创建'ingress' Subscription。它通过'ingress' Channel来订阅'ingress' Service

    代码如下:
func (r *reconciler) reconcile(ctx context.Context, b *v1alpha1.Broker) (reconcile.Result, error) {
    // 1. Trigger Channel is created for all events. Triggers will Subscribe to this Channel.
    // 2. Filter Deployment.
    // 3. Ingress Deployment.
    // 4. K8s Services that point at the Deployments.
    // 5. Ingress Channel is created to get events from Triggers back into this Broker via the
    //    Ingress Deployment.
    //   - Ideally this wouldn't exist and we would point the Trigger's reply directly to the K8s
    //     Service. However, Subscriptions only allow us to send replies to Channels, so we need
    //     this as an intermediary.
    // 6. Subscription from the Ingress Channel to the Ingress Service.

    if b.DeletionTimestamp != nil {
        // Everything is cleaned up by the garbage collector.
        return nil
    }

    if b.Spec.ChannelTemplate == nil {
        r.Logger.Error("Broker.Spec.ChannelTemplate is nil",
            zap.String("namespace", b.Namespace), zap.String("name", b.Name))
        return nil
    }

    gvr, _ := meta.UnsafeGuessKindToResource(b.Spec.ChannelTemplate.GetObjectKind().GroupVersionKind())
    channelResourceInterface := r.DynamicClientSet.Resource(gvr).Namespace(b.Namespace)
    if channelResourceInterface == nil {
        return fmt.Errorf("unable to create dynamic client for: %+v", b.Spec.ChannelTemplate)
    }

    track := r.channelableTracker.TrackInNamespace(b)

    triggerChannelName := resources.BrokerChannelName(b.Name, "trigger")
    triggerChannelObjRef := corev1.ObjectReference{
        Kind:       b.Spec.ChannelTemplate.Kind,
        APIVersion: b.Spec.ChannelTemplate.APIVersion,
        Name:       triggerChannelName,
        Namespace:  b.Namespace,
    }
    // Start tracking the trigger channel.
    if err := track(triggerChannelObjRef); err != nil {
        return fmt.Errorf("unable to track changes to the trigger Channel: %v", err)
    }

    logging.FromContext(ctx).Info("Reconciling the trigger channel")
    triggerChan, err := r.reconcileTriggerChannel(ctx, channelResourceInterface, triggerChannelObjRef, b)
    if err != nil {
        logging.FromContext(ctx).Error("Problem reconciling the trigger channel", zap.Error(err))
        b.Status.MarkTriggerChannelFailed("ChannelFailure", "%v", err)
        return err
    }
   ......

    filterDeployment, err := r.reconcileFilterDeployment(ctx, b)
    if err != nil {
        logging.FromContext(ctx).Error("Problem reconciling filter Deployment", zap.Error(err))
        b.Status.MarkFilterFailed("DeploymentFailure", "%v", err)
        return err
    }
    _, err = r.reconcileFilterService(ctx, b)
    if err != nil {
        logging.FromContext(ctx).Error("Problem reconciling filter Service", zap.Error(err))
        b.Status.MarkFilterFailed("ServiceFailure", "%v", err)
        return err
    }
    b.Status.PropagateFilterDeploymentAvailability(filterDeployment)

    ingressDeployment, err := r.reconcileIngressDeployment(ctx, b, triggerChan)
    if err != nil {
        logging.FromContext(ctx).Error("Problem reconciling ingress Deployment", zap.Error(err))
        b.Status.MarkIngressFailed("DeploymentFailure", "%v", err)
        return err
    }

    svc, err := r.reconcileIngressService(ctx, b)
    if err != nil {
        logging.FromContext(ctx).Error("Problem reconciling ingress Service", zap.Error(err))
        b.Status.MarkIngressFailed("ServiceFailure", "%v", err)
        return err
    }
    b.Status.PropagateIngressDeploymentAvailability(ingressDeployment)
    b.Status.SetAddress(&apis.URL{
        Scheme: "http",
        Host:   names.ServiceHostName(svc.Name, svc.Namespace),
    })

    ingressChannelName := resources.BrokerChannelName(b.Name, "ingress")
    ingressChannelObjRef := corev1.ObjectReference{
        Kind:       b.Spec.ChannelTemplate.Kind,
        APIVersion: b.Spec.ChannelTemplate.APIVersion,
        Name:       ingressChannelName,
        Namespace:  b.Namespace,
    }

    // Start tracking the ingress channel.
    if err = track(ingressChannelObjRef); err != nil {
        return fmt.Errorf("unable to track changes to the ingress Channel: %v", err)
    }

    ingressChan, err := r.reconcileIngressChannel(ctx, channelResourceInterface, ingressChannelObjRef, b)
    if err != nil {
        logging.FromContext(ctx).Error("Problem reconciling the ingress channel", zap.Error(err))
        b.Status.MarkIngressChannelFailed("ChannelFailure", "%v", err)
        return err
    }
    b.Status.IngressChannel = &ingressChannelObjRef
    b.Status.PropagateIngressChannelReadiness(&ingressChan.Status)

    ingressSub, err := r.reconcileIngressSubscription(ctx, b, ingressChan, svc)
    if err != nil {
        logging.FromContext(ctx).Error("Problem reconciling the ingress subscription", zap.Error(err))
        b.Status.MarkIngressSubscriptionFailed("SubscriptionFailure", "%v", err)
        return err
    }
    b.Status.PropagateIngressSubscriptionReadiness(&ingressSub.Status)

    return nil
}

Broker 示例:

apiVersion: eventing.knative.dev/v1alpha1
kind: Broker
metadata:
  name: default
spec:
  channelTemplateSpec:
    apiVersion: messaging.knative.dev/v1alpha1
    kind: InMemoryChannel

Trigger (触发器)

通过Trigger Reconciler进行处理 trigger,对于每一个 trigger, 会进行一下处理操作:

  1. 验证 Broker 是否存在
  2. 获取对应 Broker 的 Trigger Channel、 Ingress Channel 以及 Filter Service
  3. 确定订阅者的 URI
  4. 创建一个从 Broker 特定的 Channel 到这个 Trigger 的 kubernetes Service 的订阅。reply 被发送到

    Broker 的 ingress Channel。
  5. 检查是否包含 knative.dev/dependency 的注释。

    代码如下:
func (r *reconciler) reconcile(ctx context.Context, t *v1alpha1.Trigger) error {
    ......
    // 1. Verify the Broker exists.
    // 2. Get the Broker's:
    //   - Trigger Channel
    //   - Ingress Channel
    //   - Filter Service
    // 3. Find the Subscriber's URI.
    // 4. Creates a Subscription from the Broker's Trigger Channel to this Trigger via the Broker's
    //    Filter Service with a specific path, and reply set to the Broker's Ingress Channel.
    // 5. Find whether there is annotation with key "knative.dev/dependency".
    // If not, mark Dependency to be succeeded, else figure out whether the dependency is ready and mark Dependency correspondingly

    if t.DeletionTimestamp != nil {
        // Everything is cleaned up by the garbage collector.
        return nil
    }
    // Tell tracker to reconcile this Trigger whenever the Broker changes.
    brokerObjRef := corev1.ObjectReference{
        Kind:       brokerGVK.Kind,
        APIVersion: brokerGVK.GroupVersion().String(),
        Name:       t.Spec.Broker,
        Namespace:  t.Namespace,
    }

    if err := r.tracker.Track(brokerObjRef, t); err != nil {
        logging.FromContext(ctx).Error("Unable to track changes to Broker", zap.Error(err))
        return err
    }

    b, err := r.brokerLister.Brokers(t.Namespace).Get(t.Spec.Broker)
    if err != nil {
        logging.FromContext(ctx).Error("Unable to get the Broker", zap.Error(err))
        if apierrs.IsNotFound(err) {
            t.Status.MarkBrokerFailed("DoesNotExist", "Broker does not exist")
            _, needDefaultBroker := t.GetAnnotations()[v1alpha1.InjectionAnnotation]
            if t.Spec.Broker == "default" && needDefaultBroker {
                if e := r.labelNamespace(ctx, t); e != nil {
                    logging.FromContext(ctx).Error("Unable to label the namespace", zap.Error(e))
                }
            }
        } else {
            t.Status.MarkBrokerFailed("BrokerGetFailed", "Failed to get broker")
        }
        return err
    }
    t.Status.PropagateBrokerStatus(&b.Status)

    brokerTrigger := b.Status.TriggerChannel
    if brokerTrigger == nil {
        logging.FromContext(ctx).Error("Broker TriggerChannel not populated")
        r.Recorder.Eventf(t, corev1.EventTypeWarning, triggerChannelFailed, "Broker's Trigger channel not found")
        return errors.New("failed to find Broker's Trigger channel")
    }

    brokerIngress := b.Status.IngressChannel
    if brokerIngress == nil {
        logging.FromContext(ctx).Error("Broker IngressChannel not populated")
        r.Recorder.Eventf(t, corev1.EventTypeWarning, ingressChannelFailed, "Broker's Ingress channel not found")
        return errors.New("failed to find Broker's Ingress channel")
    }

    // Get Broker filter service.
    filterSvc, err := r.getBrokerFilterService(ctx, b)
    ......

    subscriberURI, err := r.uriResolver.URIFromDestination(*t.Spec.Subscriber, t)
    if err != nil {
        logging.FromContext(ctx).Error("Unable to get the Subscriber's URI", zap.Error(err))
        return err
    }
    t.Status.SubscriberURI = subscriberURI

    sub, err := r.subscribeToBrokerChannel(ctx, t, brokerTrigger, brokerIngress, filterSvc)
    if err != nil {
        logging.FromContext(ctx).Error("Unable to Subscribe", zap.Error(err))
        t.Status.MarkNotSubscribed("NotSubscribed", "%v", err)
        return err
    }
    t.Status.PropagateSubscriptionStatus(&sub.Status)

    if err := r.checkDependencyAnnotation(ctx, t); err != nil {
        return err
    }

    return nil
}

Trigger 示例:

apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
  name: my-service-trigger
spec:
  broker: default
  filter:
    attributes:
      type: dev.knative.foo.bar
      myextension: my-extension-value
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: my-service

总结

Broker/Trigger 模型出现的意义不仅在于其提供了消息过滤机制,更充分解耦了消息通道的实现,目前除了系统自身支持的基于内存的消息通道 InMemoryChannel 之外,还支持 Kafka、NATS Streaming 等消息服务。
此外结合 CloudEvent 进行事件统一标准传输,无论对于客户端接入事件源,还是消费端提供的消费事件服务,都能极大的提升了应用的跨平台可移植性。

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

]]>
【客户案例】开放搜索如何提升趣店商城20%的销量 Mon, 27 Jan 2020 20:58:34 +0800

本文字数:1508
阅读时间:约3~5分钟

您将了解:
1、搜索业务在快速发展过程中遇到问题
2、为什么会选择开放搜索
3、开放搜索如何满足销量增长需求

以下是来自趣店搜索资深技术专家 樊庆响先生在2019云栖大会现场分享

以下是正文


趣店集团,成立于2014年3月,并与2017年成功在纽交所上市。2018年第四季度集团的注册人数超过7000万,中国领先的金融科技公司。

image.png

(樊庆响,趣店搜索资深技术专家)

业务快速发展带来的搜索能力问题

趣店的快速发展,离不开对核心业务的专注,其业务主要围绕着“现金业务”、“商城业务”两方面推进;在伴随业务量快速上升的过程中,趣店商城在商品品牌、数量、检索数量及难度上,对搜索技术带来了越来越高挑战。
这就让我们对强大的搜索服务有了急迫的需求。

image.png

是选择,也是必然。

对于成长型公司来说,时间成本、人力成本对发展速度具有决定性的影响,而我们能在3年内成功上市,也是因为将主要的时间、人力成本集中在核心业务上,包括用户拉新、升级现金业务。而对于搜索服务,我们希望能在市场上寻找合适的解决方案。

趣店商城有很多资源搭建在阿里云上,比如RDS、Redus、消息队列等;而在2016年阿里云推出开放搜索相关服务的时候,我们就做了很多详细的调研,并且也尝试了除开放搜索以外,如Elasticsearch、以及自研搜索系统。综合对比很多案例,像钉钉、盒马、天猫超市、菜鸟等,最终我们还是觉得开放搜索更加合适,同时阿里云开放搜索又实现了开源。

image.png

选择开放搜索的四大原因

成本低且高效

开放搜索应用,从创建、数据上传、搜索调试、发布上线,初级工程3天时间就可以搞定,意味着在时间、人力成本上是非常低的,而且获得的是搜索功能上更好的能力支持。

image.png

强大的分词、解析能力

我们最主要的应用场景是在趣店商城;像iPhone11的上市,我们前期会通过主营位来推荐,但很多用户还是会通过搜索来寻找商品,那么开放搜索符合用户需求的搜索分词能力,解决了我们这个问题。
得益于达摩院分词以及阿里系电商平台的高关联度,开放搜索有着很好的分词、解析能力,很好的支持用户对商品的搜索匹配度。

高性能、低时延

搜索,往往是面对前端客户的使用需求的,那么高延迟的搜索反应会极大的影响用户的产品体验;刚开始我们在DB上部署相关搜索服务,但随着数据量的增加,伴随而来的是性能问题,这些都随着数据迁移至阿里云开放搜索以后,都得到了解决。

开箱即用、托管、免运维

将搜索服务放在开放搜索上,几乎没有运维成本。当我想调参、扩容、配置类目干预或者搜索干预、以及在双11大促方面的活动配置时,我们就会用到弹性扩容,这个时候,我们会预估整个峰值,来进行相关的弹性扩缩容操作,这些都仅需在控制台直接操作即可,不需要修改任何代码。

以上是开放搜索整体的产品优势,趣店商城将一些数据推送给开放搜索,同时开放搜索输出一些用户想要的精确数据,从而提升商城的成交率、拉动GMV的提升。

image.png

定制个性化的搜索权重策略,是提升成单转化的关键

趣店是一个分期商城,分类的权重配比是由团队对客户做特定分析而定,由于来趣店消费的用户大部分是买手机、3C类的产品,于是我们增加了3C方面的关键词权重。期间我们通过做A/B测试,对开放搜索在代码功能上做嵌入式改造,给一部分用户用A公式,另一部分用户用B公式,经过统计和测算后发现,加入了“销量数据”后,提高了整个搜索的成交量。在阿里云开放搜索上,由于后台提供粗排和精排的方式,我们只需要把精排的公式权重调整,就完成了整个工程。

从搜索性能提升带来的销量提升

在应用效益上,开放搜索解决了我们品牌、品类、关键词、销量、价格等一些简单的搜索问题,给用户带来了很好的搜索体验,同时良好的体验转化为了相关商品更多的精准曝光量,提升订单转化效果。使用开放搜索在技术上最明显的就是接口性能的提升,从100毫秒降低到了20毫秒。

image.png

以上是本次演讲的全部内容,分享了开放搜索一些应用经验,趣店是一个发展很快的团队,而在开放搜索的应用上,不仅在商城上,同时我们也会在新业务进行尝试,如大白汽车、电销搜索等,也都用到了开放搜索的能力。

分享到此结束,谢谢大家。

加入社区

点击 订阅《阿里云搜索与推荐技术交流期刊》

如果你想与更多开发者交流随时交流、了解最前沿的搜索与推荐技术,可以扫码加入社群


TB10DYxkYY1gK0jSZTEXXXDQVXa-894-1075.jpg

]]>
事件注册机制 - Registry Mon, 27 Jan 2020 20:58:34 +0800 本文选自《Knative 云原生应用开发指南》
knative海报.png

更多云原生技术资讯可关注阿里巴巴云原生技术圈

背景

作为事件消费者,之前是无法事先知道哪些事件可以被消费,如果能通过某种方式获得哪些 Broker 提供哪些事件,那么事件消费者就能很方便通过这些 Broker 消费事件。Registry 就是在这样的背景下被提出的,通过 Registry 机制,消费者能针对特定的 Broker 的事件通过 Trigger 进行事件订阅消费。这里需要说明一下,Registry 设计与实现目前是针对 Broker/Trigger 事件处理模型。

诉求

  • 每个Registry 对应一个 Namespace 作为资源隔离的边界
  • Registry 中包括事件类型列表,以提供事件发现机制,通过事件列表,我们可以决定对哪些 Ready 的事件进行消费
  • 由于事件最终需要通过 Trigger 进行订阅,因此事件类型信息中需要包括创建 Trigger 所需要的信息。

实现

定义 EventType CRD 资源

定义 EventType 类型的CRD资源,示例yaml:

apiVersion: eventing.knative.dev/v1alpha1
kind: EventType
metadata:
  name: com.github.pullrequest
  namespace: default
spec:
  type: com.github.pull_request
  source: github.com
  schema: //github.com/schemas/pull_request
  description: "GitHub pull request"
  broker: default
  • name: 设置时需要符合k8s命名规范
  • type:遵循CloudEvent 类型设置。
  • source: 遵循 CloudEvent source设置。
  • schema: 可以是JSON schema, protobuf schema等。可选项。
  • description: 事件描述,可选项。
  • broker: 所提供 EventType 的 Broker。

事件注册与发现

1.创建 EventType
一般我们通过以下两种方式创建 EventType 实现事件的注册。
1.1通过Event 事件源实例自动注册
基于事件源实例,通过事件源控制器创建 EventType 进行注册:

apiVersion: sources.eventing.knative.dev/v1alpha1
kind: GitHubSource
metadata:
  name: github-source-sample
  namespace: default
spec:
  eventTypes:
    - push
    - pull_request
  ownerAndRepository: my-other-user/my-other-repo
  accessToken:
    secretKeyRef:
      name: github-secret
      key: accessToken
  secretToken:
    secretKeyRef:
      name: github-secret
      key: secretToken
  sink:
    apiVersion: eventing.knative.dev/v1alpha1
    kind: Broker
    name: default

通过运行上面的yaml信息,通过GitHubSource 事件源控制器可以创建事件类型dev.knative.source.github.push以及事件类型dev.knative.source.github.pull_request这两个EventType进行注册,其中source为github.com以及名称为default的Broker。具体如下:

apiVersion: eventing.knative.dev/v1alpha1
kind: EventType
metadata:
  generateName: dev.knative.source.github.push-
  namespace: default
  owner: # Owned by github-source-sample
spec:
  type: dev.knative.source.github.push
  source: github.com
  broker: default
---
apiVersion: eventing.knative.dev/v1alpha1
kind: EventType
metadata:
  generateName: dev.knative.source.github.pullrequest-
  namespace: default
  owner: # Owned by github-source-sample
spec:
  type: dev.knative.source.github.pull_request
  source: github.com
  broker: default

这里有两点需要注意:

  • 通过自动生成默认名称,避免名称冲突。对于是否应该在代码(在本例中是GithubSource控制器)创建事件类型时生成,需要进一步讨论。
  • 我们给spec.type加上了dev.knative.source.github.前缀,这个也需要进一步讨论确定是否合理。

1.2手动注册
直接通过创建EventType CR资源实现注册,如:

apiVersion: eventing.knative.dev/v1alpha1
kind: EventType
metadata:
  name: org.bitbucket.repofork
  namespace: default
spec:
  type: org.bitbucket.repo:fork
  source: bitbucket.org
  broker: dev
  description: "BitBucket fork"

通过这种方式可以注册名称为org.bitbucket.repofork, type 为 org.bitbucket.repo:fork, source 为 bitbucket.org 以及属于dev Broker 的 EventType。

2.获取 Registry 的事件注册
事件消费者可以通过如下方式获取 Registry 的事件注册列表:
$ kubectl get eventtypes -n default

NAME                                         TYPE                                    SOURCE          SCHEMA                              BROKER     DESCRIPTION           READY   REASON
org.bitbucket.repofork                       org.bitbucket.repo:fork                 bitbucket.org                                       dev        BitBucket fork        False   BrokerIsNotReady
com.github.pullrequest                       com.github.pull_request                 github.com      //github.com/schemas/pull_request   default    GitHub pull request   True 
dev.knative.source.github.push-34cnb         dev.knative.source.github.push          github.com                                          default                          True 
dev.knative.source.github.pullrequest-86jhv  dev.knative.source.github.pull_request  github.com                                          default                          True

3.Trigger 订阅事件
最后基于事件注册列表信息,事件消费者创建对应的Trigger对Registry中的EventType进行监听消费
Trigger示例:

apiVersion: eventing.knative.dev/v1alpha1
kind: Trigger
metadata:
  name: my-service-trigger
  namespace: default
spec:
  filter:
    sourceAndType:
      type: dev.knative.source.github.push
      source: github.com
  subscriber:
    ref:
     apiVersion: serving.knative.dev/v1alpha1
     kind: Service
     name: my-service

总结

Registry 的设计主要是针对 Broker/Trigger 事件处理模型。创建事件源资源时,会创建EventType注册到Registry。在实现方面,我们可以检查Event Source的Sink类型是否是Broker,如果是,则对其注册EventType。

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

]]>
4G LTE同构宏蜂窝和异构微蜂窝概述 | 带你读《5G UDN(超密集网络)技术详解》之三 Mon, 27 Jan 2020 20:58:34 +0800 第一章 5G 前蜂窝移动历史

1.1 5G前蜂窝移动系统和业务概述

|1.2 4G LTE同构宏蜂窝和异构微蜂窝概述 |

4G 时代最有代表性的蜂窝移动系统就是 LTE,对应的无线接入技术就是 E-UTRA,它由 3GPP 项目组织领导进行了多版本的标准化。从 2008 年的 Rel-8 初始版本开始(准 4G 系统),演进到 2010 年被称为 LTE-A(真正 4G 系统)的 Rel-10 版本,至 2017 年已演进到 Rel-15 版本。由于是长期演进, 所以 E-UTRA 每个新版本都必须保证后向兼容性,即低版本的终端也能正常 接入和使用高版本的 E-UTRA 网络。LTE 网络相比过去的 2G/3G 网络,采取 了无线接入网(RAN,Radio Access Network)“扁平化架构”,如图 1-3 所示。 基站 eNB 是无线接入网 E-UTRAN 内的唯一逻辑节点(注:eNB 物理实现上 也可以通过设备厂家的私有接口分开,但标准逻辑上是同一网元节点),UE 只 有两个 RRC 状态,即空闲态 RRC_IDLE 和连接态 RRC_CONNECTED。“扁 平化架构”使得无线接入侧的所有网络功能,如接入控制、无线资源分配调度、信令和用户数据传输最大限度地靠近空中接口(空口 Uu),以快速适配空口动 态变化的无线资源环境,从而提升系统的效率性能。同时它还能进一步降低基 站 eNB 和终端 UE 之间的数据传输时延。LTE 系统 E-UTRAN 中的逻辑节点 和接口类型精简,避免了很多不同网元节点间的互联互通测试(IOT,InterOperatability Test)等问题,从而增强了蜂窝系统的稳定性和运维性。
eNB 通过直连的逻辑接口 S1,和核心网控制面网元节点 MME 以及用户 面网元节点 SGW 相连接,它们之间可以是多对多的 Flex 连接关系。由于架构 扁平化的特征,处于相同逻辑架构层级的基站 eNB 之间,可以通过直连的逻辑 接口 X2 进行移动性操作和数据协作传输联合操作,这也可以一定程度地增强 LTE 小区边缘的容量和性能,克服终端和 eNB 基站之间通信的“远近效应”, 形成相对平滑一致的无线覆盖和系统容量供给(注:这里相邻的 eNB 基站虽处 于相同的逻辑架构层级,但它们可以是不同类型和功能集合的基站,可扮演着 不同的逻辑角色)。不同于过去的2G/3G网络,LTE采取了“单一核心网PS域”, 不再独立区分传统蜂窝的 CS 域和 PS 域,用统一 PS 域提供了所有 EPS 承载 级别的用户业务承载。各种拥有不同服务质量要求(QoS Profile,Quality of Service Profile)和属性特征(QCI、GBR、AMBR、ARP)的业务数据包(包 括话音数据包),都要通过基站 eNB 配置的数据无线承载(DRB,Data Radio Bearer)来进行统一且差分的处理和上下行数据包在空口的传输。LTE 系统 既能支持一些 QoS 被标准化的常见数据业务类型,用标准化的 QCI 来表达其 QoS 特征,也可以支持那些 QoS 没被标准化的其他数据业务。
image.png
早期,在 LTE 同构宏蜂窝的部署方式下,每个宏基站 eNB 内,可在一个 或者多个 LTE 载波频点上,重叠配置着多个宏服务小区(无线覆盖范围从几百 米到几十千米),从而相邻的多个宏基站 eNB,共同形成较为规整的宏蜂窝状 的无线覆盖。如图 1-4 所示,在某个物理区域内,若干形状大小基本相同的宏 小区有规律地部署在 4 个不同的 LTE 载波频点上,频率垂直方向有重叠覆盖, 位置水平方向的宏小区边缘也有重叠覆盖。在 LTE 同构宏蜂窝中,LTE 无线 覆盖和容量的供给,通常随着物理位置的变化而呈现出单一的拓扑结构,越往 宏小区中心的地方越好。同构宏蜂窝的部署方式,在蜂窝移动网络早期特别强 调无线全覆盖的要求下,较为普遍适用,在未来 5G 蜂窝部署中,移动锚点控 制信令层或基本类业务层,通常也可采取同构宏蜂窝的部署方式。
如图 1-4 所示,“同构”意味着大部分部署的基站 eNB 所提供的无线容量 和处理能力,甚至配置参数(包括天线数目、形态、增益、角度朝向)都基本一致, 运营商不需要在众多的基站设备之间,进行太多的差分对待和精细化管理配置, 因此,相应的设备采购管理、网络规划优化、管理运维等任务,则变得相对 “千篇一律”,相对简单、轻松一些。“宏蜂窝”意味着单个宏基站 eNB,通常 就能无线覆盖较大的物理区域范围,因此整个蜂窝网络中部署的基站总数目和 CAPEX/OPEX 成本就容易控制在特定的预算范围内。在特定的 LTE 宏服务小 区下,对于大部分终端,由于自身的物理活动范围有限,大部分时间内可能只 是处在单个宏基站 eNB 所辖的服务范围内,偶尔才会离开移动到其他相邻的宏 基站服务小区内,因此可避免很多宏基站间的移动性重配置、干扰协调等操作。 因此,同构宏蜂窝通常保证好从核心网→无线接入网→终端这一垂直数据服务 路径即可,对基站之间的水平数据服务路径的要求,可以相对略微地降低。
image.png
总的说来,“同构宏蜂窝”的部署方式,能够简化运营商对蜂窝移动网络的 部署和运维,是一种较为初级、粗糙的方式,但它很不利于蜂窝移动网络资源 的差分化管理和精细化利用,这主要基于下面几点理由和事实。
(1)终端用户的密度和数据流量需求的物理分布通常是不均匀的,有的区 域多,有的区域少,有的区域甚至是“无人区”。由于不同地域和室内外环境之 间的较大差异,用户的移动特性也是不一致的。“同构宏蜂窝”的部署方式,不 利于针对局部区域的具体业务分布需求特性,来进行无线覆盖和系统容量方面 的定制化供给输出。
(2)宏基站 eNB 为了实现远距离无线覆盖,需要较大的射频输出功率,同 理,处于小区远端的终端也需要较大的射频输出功率,才能保证上行数据传输 性能,这是很耗能的。由于单个宏服务小区内用户数众多,它必须长时间处于 激活且持续稳定的工作状态,这对宏基站的能耗和运行稳定性要求很高,比如, 宏基站 eNB 在白天正常工作时段,几乎很难被关闭和参数重配重启。一旦宏基 站 eNB 发生故障重启,常常会带来很大的断网影响。
(3)宏小区覆盖造成位于宏服务小区远端和边缘的终端,需要经历“无线 链路长径”才能和基站进行上下行数据传输,这需要消耗更多的空口无线资源 和发射功率资源,以抵抗路损和无线干扰,但同时必然会对环境造成更大的无 线干扰,因此,“无线链路短径”对于空口数据传输更为高效和有利,也更能节 省基站能量和终端电量。后面读者将会发现:通过小小区和 Relay 中继技术,可 把无线链路长径转化成分段的多条短径,这样可有效地管理空口的无线干扰叠加。
(4)单个大型宏基站 eNB 因为要服务众多的用户(>1 000 人),这对宏基 站的基带处理能力、调度能力 / 算法效率等方面的挑战都很大,比如,能同时 高效服务调度 1 000 个用户和 10 个用户所带来的算法复杂度和基带处理效率 开销是不同的。由于众多用户的物理位置、蜂窝业务特性、无线环境等因素差 别可能很大,因此极易造成宏服务小区的平均谱效和实际无线覆盖、容量被某 些用户拉低,用户间服务的公平度下降,从而无法达到系统的性能目标。
(5)宏基站 eNB 的宏小区,通常只能配置工作在带宽资源较稀缺的低频段(< 3.5 GHz),由于设备发射功率限制(20 MHz 工作带宽典型值为 46 dBm)、空 间物理障碍物导致的不同路损等因素,通常无法在中高频段载波上进行部署工 作。因此宏小区无法利用广阔充裕的中高频段载波资源,此时,微小区或小小 区就自然地适用于中高频段部署。UDN 部署的一个前提就是基站小型化和服务 小小区化。
基于上述“同构宏蜂窝”的多重缺点、弊端,以及运营商们不断强调精细 化其网络运营和更高效、灵活地提供各类蜂窝业务应用的需要,LTE 网络从Rel-10开始,逐渐从“同构宏蜂窝”的部署方式,向各种“异构微蜂窝”(HetNet, Heterogeneous Network)的部署方式转变、演化。简单地定义,“异构微蜂 窝”是指特定物理服务区域内,存在着不同系统容量、不同处理能力和不同配 置参数的各种基站 eNB(从大型宏基站到各类型的微基站甚至家庭基站),外加 其他不同 RAT 技术制式的基站节点(如 RN、WLAN AP、gNB 等),它们之 间混合搭配着异构化部署,不需要像“同构宏蜂窝”那样规律有序,运营商可 根据待服务用户和蜂窝业务的客观具体需求按需、灵活、动态地去构建网络拓 扑,从而实现区域定制化的无线覆盖和容量供给输出。如图 1-5 所示,在某个 物理服务区域内,若干个覆盖形状大小不同的宏微服务小区,无特定规律地部 署在 4 个不同的 LTE 载波频点上,从而蜂窝网络提供的无线通信容量可针对特 定物理位置的具体需求情况而动态变化和定制,可以是同频或异频部署,形成 多样化的拓扑结构,而且可能是“无定形的”。对于“异构微蜂窝”,不仅要保 证好核心网→无线接入网→终端这一垂直数据服务基本路径,还要对基站之间 的水平数据服务路径提出更高的性能要求,因为此时终端很容易且很需要和多个相邻的基站产生联合互操作的关联。
支撑 4G LTE“异构微蜂窝”的相关关键技术,将在后续的章节中详细地 进行介绍分析,5G UDN 本质上也属于一种“异构微蜂窝”的部署方式,此时 除了“异构”和“微蜂窝”的基本特征,还外加了“小区密集化”和“高度协 作化”的重要特征。当 5G UDN 内的小小区部署变得越来越密、越来越小时, 无线接入网节点的上层基带资源和下层无线资源仿似被“云化了”,这就能对 5G 在部署组网成本、工作效率、网络系统性能等方面都带来很大的提升空间, 同时也伴随有很多新的技术挑战。这一技术趋势,其实就是蜂窝移动系统对“云 计算”的基本技术理念,在无线接入网侧的深入应用。
image.png

]]>
Flink Weekly | 每周社区动态更新-12/24 Mon, 27 Jan 2020 20:58:34 +0800 作者:程鹤群(军长)

Apache Flink 中文邮件列表中的 Flink Weekly 周报,计划每周一期,内容涵盖邮件列表中用户问题的解答、社区开发和提议的进展、社区新闻以及其他活动、博客文章等,欢迎持续关注~

本期的主要内容由 Hequn Cheng 整理,包括:发布 Flink 1.10 和 Flink 1.9.2 的更新,关于将 Flink Docker image 发布集成到 Flink 发布过程中的讨论,PyFlink 后期新功能的讨论以及一些博客文章。

Flink开发

  • [releases] Kostas Kloudas 建议在 feature-freeze 期间,关注下1.10新功能的文档。他创建了一个总 umbrella issue(FLINK-15273)来跟踪未完成的文档任务。 [1]
  • [releases] Hequn 展开了一个启动Flink 1.9.2发布的讨论。 本周解决了一个 blocker,还剩一个blocker。 考虑到正在进行的1.10版本以及社区的资源有限,计划在圣诞节后进行1.9.2的投票。[2]
  • [releases] Patrick 建议将 Flink Docker 映像发布集成到Flink发布过程中。 目前的争论点是是否要为发布 Docker 镜像的 Dockerfiles 提供专门的 git repo。[3]
  • [sql] 关于在 Flink SQL 中支持 JSON 函数的讨论似乎已经达成共识。 Jark Wu 建议 Forward Xu 开始 Flip 投票。[4]
  • [runtime] 在试用了新的 FLIP-49 内存配置之后,Stephan 进行了讨论并提供了一些反馈。 他提供了一些关于配置键名称和描述的改进意见。 目前收到了许多其他人的赞同。 [5]
  • [connectors] Flip-27(新的 source 接口)的讨论本周有了一些更新。 本周讨论的重点是“有界和无界”的概念。 [6]
  • [pyflink] Jincheng 展开了一个讨论,意在和社区一起讨论 PyFlink 接下来希望支持的功能。目前有一个人回复,期待 PyFlink 能更好地集成 Jupyter。 [7]

[1] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/Documentation-tasks-for-release-1-10-td36031.html
[2] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Releasing-Flink-1-9-2-td36087.html
[3] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Integrate-Flink-Docker-image-publication-into-Flink-release-process-td36139.html
[4] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Support-JSON-functions-in-Flink-SQL-td32674.html
[5] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Some-feedback-after-trying-out-the-new-FLIP-49-memory-configurations-td36129.html
[6] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-FLIP-27-Refactor-Source-Interface-td24952.html
[7] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-What-parts-of-the-Python-API-should-we-focus-on-next-td36119.html

已知缺陷

  • [FLINK-15262] [1.10.0] kafka connector doesn't read from beginning immediately when 'connector.startup-mode' = 'earliest-offset'. [8]
    即使设置了'connector.startup-mode' = 'earliest-offset'的配置项,Kafka 的 connector 也没有从最开始的点位消费。
  • [FLINK-15300] [1.10.0] Shuffle memory fraction sanity check does not account for its min/max limit. [9]
    如果我们有一个设置 shuffle memory 最小/最大值的配置,但是分数超出最小/最大范围,则完整性检查(TaskExecutorResourceUtils#sanityCheckShuffleMemory)可能会失败。
  • [FLINK-15304] [1.11.0] Remove unexpected Hadoop dependency from Flink's Mesos integration. [10]
    目前 Hadoop 依赖存在于 Flink 的 Mesos 集成中,需要去掉。
  • [FLINK-15313] [1.10.0] Can not insert decimal with precision into sink using TypeInformation. [11]
    如果 Insert 到一张带有 Decimal 类型的表,并且 Decimal 类型包含精度,那么目前 Flink 会抛出异常。
  • [FLINK-15320] [1.10.0] JobManager crashes in the standalone model when cancelling job which subtask' status is scheduled. [12]
    Standalone 集群下,如果 cancel 一个子 task 的状态是 scheduled 的作业,JobManager 会崩溃。

[8] https://issues.apache.org/jira/browse/FLINK-15262
[9] https://issues.apache.org/jira/browse/FLINK-15300
[10] https://issues.apache.org/jira/browse/FLINK-15304
[11] https://issues.apache.org/jira/browse/FLINK-15313
[12] https://issues.apache.org/jira/browse/FLINK-15320

活动/博客文章/其他

  • Philip Wilcox 发布了一个博客,介绍 Bird 公司内他们如何使用 Flink 检测离线踏板车。 该博客主要分享一些如何解决实际业务场景中一系列棘手问题的经验,涉及 Kafka,事件时间,水印和排序。 [13]
  • Preetdeep Kumar 发表了一篇博文,介绍了使用 Apache Flink 处理流数据的用例和最佳实践。[14].

[13] https://www.ververica.com/blog/replayable-process-functions-time-ordering-and-timers
[14] https://dzone.com/articles/streaming-etl-with-apache-flink

2 分钟快速订阅 Flink 中文邮件列表

Apache Flink 中文邮件列表订阅流程:

  1. 发送任意邮件到 user-zh-subscribe@flink.apache.org
  2. 收到官方确认邮件
  3. 回复该邮件 confirm 即可订阅

订阅成功后将收到 Flink 官方的中文邮件列表的消息,您可以向 user-zh@flink.apache.org 发邮件提问也可以帮助别人解答问题,动动手测试一下!

]]>
探讨基于阿里云容器技术架构(二) Mon, 27 Jan 2020 20:58:34 +0800

原文作者:李同刚
原文链接:https://developer.aliyun.com/article/740135?spm=a2c6h.13262185.0.0.2df36171oLnf2Z
更多云原生技术资讯可关注阿里巴巴云原生技术圈

阅读本篇需要具备 Kubernetes 知识和 kubectl 工具基本使用。
上一篇介绍了整体架构图,接下来的文章我们围绕架构图部署应用,本篇我们主要介绍网关的部署方式以、所需要的资源介绍以及可能会遇到的坑,不会对细节进行过多的描述,比如如何打包 docker 镜像等,因为我们不打算写一个从零开始的长而冗余的教程,浪费各位宝贵的阅读时间。部署中遇到问题直接在文章中评论,我会回复。

网关

代码简单主要起到说明作用,所以不做代码讲解了。源码托管在GitHub:https://github.com/Tony-Hangzhou/mvp-samples
网关职责是封装内部服务,对外提供统一API访问,当然也可以加入鉴权和授权的职责。所以网关的网络通讯要求是既要提供给Kubernetes外部访问的入口,又要把请求路由到内部服务(相当于反向代理)。

ZuulApplication.java

@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {

public static void main(String[] args) {    
     SpringApplication.run(ZuulApplication.class, args);    
 }

}

application.yml

spring:
application:

name: api-gateway

zuul:
routes:

user-service:    
   path: /users/**    
   url: http://localhost:8081    
   strip-prefix: false    
 
 order-service:    
   path: /orders/**    
   url: http://localhost:8082    
   strip-prefix: false

management:
endpoints:

web:    
   exposure:    
     include: routes

部署拓扑图

根据前篇介绍的架构图,我们的应用部署图如下,应用部分由网关(Zuul)、Foo、Bar组成。部署所需要的资源阿里云Kubernetes集群、阿里云SLB以及阿里云镜像托管Docker镜像。
17.png

资源准备

阿里云 Kubernetes

申请一个阿里云 Kubernetes 集群,Master 节点默认3个,Node 节点至少一个。

SLB

Kubernetes 暴露 Service 给外部访问有多种方式,我们这里选择 LoadBalancer 方式。阿里云 Kubernetes 支持阿里云 SLB 作为 LoadBalancer,而且支持阿里云内网和外网 SLB 。

  • 外网

service.beta.kubernetes.io/alicloud-loadbalancer-address-type: internet

service.beta.kubernetes.io/alicloud-loadbalancer-id: ***
  • 内网

service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet

service.beta.kubernetes.io/alicloud-loadbalancer-id: ***

阿里云镜像

阿里云镜像为 Docker 镜像提供了托管服务,省掉自己搭建镜像服务器和维护的成本。

避坑指南

  • Pod IP
    原生 Kubernetes Pod IP 是内部IP,外部是无法访问的。阿里云 Kubernetes 通过 CNI 接口,自定义了网络部分的实现,他的 Pod IP 实际上是阿里云内网IP,Kubernetes 外部是可以访问的,但是熟悉 Kubernetes 同学知道这个 IP 随着Pod 被调度是回变化的,所以一定不要使用 Pod IP 直接访问,而是通过 Service 访问。
  • SLB
    Kubernetes 基于申明的方式部署服务,所以我们有一个 gateway-deploy.yaml,然后通过 kubectl 客户端做部署操作。尽可能使用命令 kubectl apply -f gateway-depoy.yaml 操作。避免使用 kubectl delete 和 kubectl create 组合操作,阿里云 SLB 会出现无法绑定到 NodePort 情况,从而造成通过 SLB 无法访问服务。
  • namespace
    避免 namespace 使用中短横线如:service-core。Kubernetes 内部服务使用 DNS 域名访问,如:foo.service-core,会出现通过该域名无法访问情况。去掉短横线或者通过 Service ClusterIP 即可访问,还有一种办法使用 foo.service-core.svc 或者全域名 foo.service-core.svc.cluster.local 也可访问。问题可能是 Kubernetes 短域名解析Bug,具体原因不明,请路过的高手指点一二。

主要操作

1、编写部署文件 gateway-deploy.yaml ,源代码见GitHub:https://github.com/Tony-Hangzhou/mvp-samples
2、打包 Docker 镜像
3、上传镜像到阿里云镜像服务器
4、使用 kubectl apply 命令部署服务

总结

本篇介绍了网关部署以及可能遇到的坑。

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

]]>
日处理数据量超10亿:友信金服基于Flink构建实时用户画像系统的实践 Mon, 27 Jan 2020 20:58:34 +0800 作者 | 杨毅,穆超峰,贺小兵,胡夕

导读:当今生活节奏日益加快,企业面对不断增加的海量信息,其信息筛选和处理效率低下的困扰与日俱增。由于用户营销不够细化,企业 App 中许多不合时宜或不合偏好的消息推送很大程度上影响了用户体验,甚至引发了用户流失。在此背景下,友信金服公司推行全域的数据体系战略,通过打通和整合集团各个业务线数据,利用大数据、人工智能等技术构建统一的数据资产,如 ID-Mapping、用户标签等。友信金服用户画像项目正是以此为背景成立,旨在实现“数据驱动业务与运营”的集团战略。目前该系统支持日处理数据量超 10 亿,接入上百种合规数据源。

一、技术选型

传统基于 Hadoop 生态的离线数据存储计算方案已在业界大规模应用,但受制于离线计算的高时延性,越来越多的数据应用场景已从离线转为实时。这里引用一张表格对目前主流的实时计算框架做个对比。

1.jpg

Apache Storm 的容错机制需要对每条数据进行应答(ACK),因此其吞吐量备受影响,在数据大吞吐量的场景下会有问题,因此不适用此项目的需求。

Apache Spark 总体生态更为完善,且在机器学习的集成和应用性暂时领先,但 Spark 底层还是采用微批(Micro Batching)处理的形式。

Apache Flink 在流式计算上有明显优势:首先其流式计算属于真正意义上的单条处理,即每一条数据都会触发计算。在这一点上明显与 Spark 的微批流式处理方式不同。其次,Flink 的容错机制较为轻量,对吞吐量影响较小,使得 Flink 可以达到很高的吞吐量。最后 Flink 还拥有易用性高,部署简单等优势。相比之下我们最终决定采用基于 Flink 的架构方案。

二、用户画像业务架构

用户画像系统目前为集团线上业务提供用户实时标签数据服务。为此我们的服务需要打通多种数据源,对海量的数字信息进行实时不间断的数据清洗、聚类、分析,从而将它们抽象成标签,并最终为应用方提供高质量的标签服务。在此背景下,我们设计用户画像系统的整体架构如下图所示:

2.jpg

整体架构分为五层:

  1. 接入层:接入原始数据并对其进行处理,如 Kafka、Hive、文件等。
  2. 计算层:选用 Flink 作为实时计算框架,对实时数据进行清洗,关联等操作。
  3. 存储层:对清洗完成的数据进行数据存储,我们对此进行了实时用户画像的模型分层与构建,将不同应用场景的数据分别存储在如 Phoenix,HBase,HDFS,Kafka 等。
  4. 服务层:对外提供统一的数据查询服务,支持从底层明细数据到聚合层数据的多维计算服务。
  5. 应用层:以统一查询服务对各个业务线数据场景进行支撑。目前业务主要包含用户兴趣分、用户质量分、用户的事实信息等数据。

三、用户画像数据处理流程

在整体架构设计方案设计完成之后,我们针对数据也设计了详尽的处理方案。在数据处理阶段,鉴于 Kafka 高吞吐量、高稳定性的特点,我们的用户画像系统统一采用 Kafka 作为分布式发布订阅消息系统。数据清洗阶段利用 Flink 来实现用户唯一性识别、行为数据的清洗等,去除冗余数据。这一过程支持交互计算和多种复杂算法,并支持数据实时 / 离线计算。目前我们数据处理流程迭代了两版,具体方案如下:

1.0 版数据处理流程

数据接入、计算、存储三层处理流程

整体数据来源包含两种:

  1. 历史数据:从外部数据源接入的海量历史业务数据。接入后经过 ETL 处理,进入用户画像底层数据表。
  2. 实时数据:从外部数据源接入的实时业务数据,如用户行为埋点数据,风控数据等。

根据不同业务的指标需求我们直接从集团数据仓库抽取数据并落入 Kafka,或者直接从业务端以 CDC(Capture Data Change)的方式写入 Kafka。在计算层,数据被导入到 Flink 中,通过 DataStream 生成 ID-Mapping、用户标签碎片等数据,然后将生成数据存入 JanusGraph(JanusGraph 是以 HBase 作为后端存储的图数据库介质)与 Kafka,并由 Flink 消费落入 Kafka 的用户标签碎片数据,进行聚合生成最新的用户标签碎片(用户标签碎片是由用户画像系统获取来自多种渠道的碎片化数据块处理后生成的)。

3.jpg

数据服务层处理流程

服务层将存储层存储的用户标签碎片数据,通过 JanusGraph Spark On Yarn 模式,执行 TinkerPop OLAP 计算生成全量用户 Yids 列表文件。Yid 是用户画像系统中定义的集团级用户 ID 标识。结合 Yids 列表文件,在 Flink 中批量读取 HBase 聚合成完整用户画像数据,生成 HDFS 文件,再通过 Flink 批量操作新生成的数据生成用户评分预测标签,将用户评分预测标签落入 Phoenix,之后数据便可通过统一数据服务接口进行获取。下图完整地展示了这一流程。

4.jpg

ID-Mapping 数据结构

为了实现用户标签的整合,用户 ID 之间的强打通,我们将用户 ID 标识看成图的顶点、ID pair 关系看作图的边,比如已经识别浏览器 Cookie 的用户使用手机号登陆了公司网站就形成了对应关系。这样所有用户 ID 标识就构成了一张大图,其中每个小的连通子图 / 连通分支就是一个用户的全部标识 ID 信息。

ID-Mapping 数据由图结构模型构建,图节点包含 UserKey、Device、IdCard、Phone 等类型,分别表示用户的业务 ID、设备 ID、身份证以及电话等信息。节点之间边的生成规则是通过解析数据流中包含的节点信息,以一定的优先级顺序进行节点之间的连接,从而生成节点之间的边。比如,识别了用户手机系统的 Android_ID,之后用户使用邮箱登陆了公司 App,在系统中找到了业务线 UID 就形成了和关系的 ID pair,然后系统根据节点类型进行优先级排序,生成 Android_ID、mail、UID 的关系图。数据图结构模型如下图所示:

5.jpg

Gephi

1.0 版本数据处理流程性能瓶颈

1.0 版本数据处理流程在系统初期较好地满足了我们的日常需求,但随着数据量的增长,该方案遇到了一些性能瓶颈:

  1. 首先,这版的数据处理使用了自研的 Java 程序来实现。随着数据量上涨,自研 JAVA 程序由于数据量暴增导致 JVM 内存大小不可控,同时它的维护成本很高,因此我们决定在新版本中将处理逻辑全部迁移至 Flink 中。
  2. 其次,在生成用户标签过程中,ID-Mapping 出现很多大的连通子图(如下图所示)。这通常是因为用户的行为数据比较随机离散,导致部分节点间连接混乱。这不仅增加了数据的维护难度,也导致部分数据被“污染”。另外这类异常大的子图会严重降低 JanusGraph 与 HBase 的查询性能。

6.jpg

Gephi

  1. 最后,该版方案中数据经 Protocol Buffer(PB)序列化之后存入 HBase,这会导致合并 / 更新用户画像标签碎片的次数过多,使得一个标签需要读取多次 JanusGraph 与 HBase,这无疑会加重 HBase 读取压力。此外,由于数据经过了 PB 序列化,使得其原始存储格式不可读,增加了排查问题的难度。

鉴于这些问题,我们提出了 2.0 版本的解决方案。在 2.0 版本中,我们通过利用 HBase 列式存储、修改图数据结构等优化方案尝试解决以上三个问题。

2.0 版数据处理流程

版本流程优化点

如下图所示,2.0 版本数据处理流程大部分承袭了 1.0 版本。新版本数据处理流程在以下几个方面做了优化:

7.jpg

2.0 版本数据处理流程

  1. 历史数据的离线补录方式由 JAVA 服务变更为使用 Flink 实现。
  2. 优化用户画像图数据结构模型,主要是对边的连接方式进行了修改。之前我们会判断节点的类型并根据预设的优先级顺序将多个节点进行连接,新方案则采用以 UserKey 为中心的连接方式。做此修改后,之前的大的连通子图(图 6)优化为下面的小的连通子图(图 8),同时解决了数据污染问题,保证了数据准确性。另外,1.0 版本中一条数据需要平均读取十多次 HBase 的情况也得到极大缓解。采用新方案之后平均一条数据只需读取三次 HBase,从而降低 HBase 六七倍的读取压力(此处优化是数据计算层优化)。

8.jpg

Gephi

  1. 旧版本是用 Protocol Buffer 作为用户画像数据的存储对象,生成用户画像数据后作为一个列整体存入 HBase。新版本使用 Map 存储用户画像标签数据,Map 的每对 KV 都是单独的标签,KV 在存入 HBase 后也是单独的列。新版本存储模式利用 HBase 做列的扩展与合并,直接生成完整用户画像数据,去掉 Flink 合并 / 更新用户画像标签过程,优化数据加工流程。使用此方案后,存入 HBase 的标签数据具备了即席查询功能。数据具备即席查询是指在 HBase 中可用特定条件直接查看指定标签数据详情的功能,它是数据治理可以实现校验数据质量、数据生命周期、数据安全等功能的基础条件。
  2. 在数据服务层,我们利用 Flink 批量读取 HBase 的 Hive 外部表生成用户质量分等数据,之后将其存入 Phoenix。相比于旧方案中 Spark 全量读 HBase 导致其读压力过大,从而会出现集群节点宕机的问题,新方案能够有效地降低 HBase 的读取压力。经过我们线上验证,新方案对 HBase 的读负载下降了数十倍(此处优化与 2 优化不同,属于服务层优化)。

四、问题

目前,线上部署的用户画像系统中的数据绝大部分是来自于 Kafka 的实时数据。随着数据量越来越多,系统的压力也越来越大,以至于出现了 Flink 背压与 Checkpoint 超时等问题,导致 Flink 提交 Kafka 位移失败,从而影响了数据一致性。这些线上出现的问题让我们开始关注 Flink 的可靠性、稳定性以及性能。针对这些问题,我们进行了详细的分析并结合自身的业务特点,探索并实践出了一些相应的解决方案。

CheckPointing 流程分析与性能优化方案

CheckPointing 流程分析

下图展示了 Flink 中 checkpointing 执行流程图:

9.jpg

Flink 中 checkpointing 执行流程

  1. Coordinator 向所有 Source 节点发出 Barrier。
  2. Task 从输入中收到所有 Barrier 后,将自己的状态写入持久化存储中,并向自己的下游继续传递 Barrier。
  3. 当 Task 完成状态持久化之后将存储后的状态地址通知到 Coordinator。
  4. 当 Coordinator 汇总所有 Task 的状态,并将这些数据的存放路径写入持久化存储中,完成 CheckPointing。

性能优化方案

通过以上流程分析,我们通过三种方式来提高 Checkpointing 性能。这些方案分别是:

  1. 选择合适的 Checkpoint 存储方式
  2. 合理增加算子(Task)并行度
  3. 缩短算子链(Operator Chains)长度

选择合适的 Checkpoint 存储方式

CheckPoint 存储方式有 MemoryStateBackend、FsStateBackend 和 RocksDBStateBackend。由官方文档可知,不同 StateBackend 之间的性能以及安全性是有很大差异的。通常情况下,MemoryStateBackend 适合应用于测试环境,线上环境则最好选择 RocksDBStateBackend。

这有两个原因:首先,RocksDBStateBackend 是外部存储,其他两种 Checkpoint 存储方式都是 JVM 堆存储。受限于 JVM 堆内存的大小,Checkpoint 状态大小以及安全性可能会受到一定的制约;其次,RocksDBStateBackend 支持增量检查点。增量检查点机制(Incremental Checkpoints)仅仅记录对先前完成的检查点的更改,而不是生成完整的状态。与完整检查点相比,增量检查点可以显著缩短 checkpointing 时间,但代价是需要更长的恢复时间。

合理增加算子(Task)并行度

Checkpointing 需要对每个 Task 进行数据状态采集。单个 Task 状态数据越多则 Checkpointing 越慢。所以我们可以通过增加 Task 并行度,减少单个 Task 状态数据的数量来达到缩短 CheckPointing 时间的效果。

缩短算子链(Operator Chains)长度

Flink 算子链(Operator Chains)越长,Task 也会越多,相应的状态数据也就更多,Checkpointing 也会越慢。通过缩短算子链长度,可以减少 Task 数量,从而减少系统中的状态数据总量,间接的达到优化 Checkpointing 的目的。下面展示了 Flink 算子链的合并规则:

  1. 上下游的并行度一致
  2. 下游节点的入度为 1
  3. 上下游节点都在同一个 Slot Group 中
  4. 下游节点的 Chain 策略为 ALWAYS
  5. 上游节点的 Chain 策略为 ALWAYS 或 HEAD
  6. 两个节点间数据分区方式是 Forward
  7. 用户没有禁用 Chain

基于以上这些规则,我们在代码层面上合并了相关度较大的一些 Task,使得平均的操作算子链长度至少缩短了 60%~70%。

Flink 背压产生过程分析及解决方案

背压产生过程分析

在 Flink 运行过程中,每一个操作算子都会消费一个中间 / 过渡状态的流,并对它们进行转换,然后生产一个新的流。这种机制可以类比为:Flink 使用阻塞队列作为有界的缓冲区。跟 Java 里阻塞队列一样,一旦队列达到容量上限,处理速度较慢的消费者会阻塞生产者向队列发送新的消息或事件。下图展示了 Flink 中两个操作算子之间的数据传输以及如何感知到背压的:

10.jpg

首先,Source 中的事件进入 Flink 并被操作算子 1 处理且被序列化到 Buffer 中,然后操作算子 2 从这个 Buffer 中读出该事件。当操作算子 2 处理能力不足的时候,操作算子 1 中的数据便无法放入 Buffer,从而形成背压。背压出现的原因可能有以下两点:

  1. 下游算子处理能力不足;
  2. 数据发生了倾斜。

背压解决方案

实践中我们通过以下方式解决背压问题。首先,缩短算子链会合理的合并算子,节省出资源。其次缩短算子链也会减少 Task(线程)之间的切换、消息的序列化 / 反序列化以及数据在缓冲区的交换次数,进而提高系统的整体吞吐量。最后,根据数据特性将不需要或者暂不需要的数据进行过滤,然后根据业务需求将数据分别处理,比如有些数据源需要实时的处理,有些数据是可以延迟的,最后通过使用 keyBy 关键字,控制 Flink 时间窗口大小,在上游算子处理逻辑中尽量合并更多数据来达到降低下游算子的处理压力。

优化结果

经过以上优化,在每天亿级数据量下,用户画像可以做到实时信息实时处理并无持续背压,Checkpointing 平均时长稳定在 1 秒以内。

五、未来工作的思考和展望

端到端的实时流处理

目前用户画像部分数据都是从 Hive 数据仓库拿到的,数据仓库本身是 T+1 模式,数据延时性较大,所以为了提高数据实时性,端到端的实时流处理很有必要。

端到端是指一端采集原始数据,另一端以报表 / 标签 / 接口的方式对这些对数进行呈现与应用,连接两端的是中间实时流。在后续的工作中,我们计划将现有的非实时数据源全部切换到实时数据源,统一经过 Kafka 和 Flink 处理后再导入到 Phoenix/JanusGraph/HBase。强制所有数据源数据进入 Kafka 的一个好处在于它能够提高整体流程的稳定性与可用性:首先 Kafka 作为下游系统的缓冲,可以避免下游系统的异常影响实时流的计算,起到“削峰填谷”的作用;其次,Flink 自 1.4 版本开始正式支持与 Kafka 的端到端精确一次处理语义,在一致性方面上更有保证。

11.jpg

作者介绍:
杨毅:友信金服计算平台部 JAVA 工程师
穆超峰:友信金服计算平台部数据开发高级工程师
贺小兵:友信金服计算平台部数据开发工程师
胡夕:友信金服计算平台部技术总监

]]>
10万开发者都知道的极速部署方式,你居然不知道!?内含悬赏活动 Mon, 27 Jan 2020 20:58:34 +0800 先来看看程序猿是如何评价极速部署方式

软萌小姐姐介绍 Cloud Toolkit 的一键部署方式

上面小哥哥表扬(tu cao)的就是这一款免费 IDE 插件——Cloud Toolkit,已经有超 12 万开发者下载,是一种公认的极速部署方式,如果你还不了解,点击下方视频,让「软萌小姐姐」来为你介绍。

视频链接:https://www.aliyun.com/daily-act/video?src=https://cloud.video.taobao.com/play/u/2311856963/p/1/e/6/t/1/247487624012.mp4

当您每次修改完代码后,是否正在经历反复地打包?采用 SCP 工具上传?使用 XShell 或 SecureCRT 登陆服务器?替换部署包?重启?

现在开始,请把这些重复繁琐的工作交给 Cloud Toolkit 吧,它能够帮助开发者更高效地开发、测试、诊断并部署应用。Cloud Toolkit 与主流 IDE 及阿里云其他产品无缝集成,帮助您大大简化应用部署到服务器,尤其是阿里云服务器中的操作。通过插件,可以将本地应用一键部署到任意服务器,甚至云端—— ECS、ECS、EDAS、ACK、ACR 和 小程序云 等,而且还可以通过其内嵌的 Arthas 程序诊断、 Terminal Shell 终端和 MySQL 执行器等工具,简化应用开发、测试和诊断的过程。

插件下载地址:https://cn.aliyun.com/product/cloudtoolkit?spm=yunqi1224

  • 部署到 任意服务器 / ECS

Cloud Toolkit 支持标准 SSH 协议,无需在一系列运维工具之间切换,只需在图形界面上选择目标服务器,即可实现应用快速部署。

  • 部署到 容器镜像(ACR)

针对阿里云 ACR 开发者,可以将登陆、拉取、推送、选择仓库等步骤交给插件自动化,实现一键完成所有操作。

  • 部署到 容器服务(ACK)

针对阿里云 Kubernetes 开发者, 可以将本地代码和云端容器进行关联,实现自动化的镜像上传和部署。

  • 部署到 小程序云

针对阿里云 小程序云 开发者,可以使用 Cloud Toolkit 来部署小程序应用,适用于小程序的首次部署、快速迭代、版本回滚等场景。(小程序云应用是面向小程序的应用场景,为开发者提供的一键构建后端应用运行环境、后端服务部署、运维监控等能力的一站式小程序部署服务)

  • 部署到 企业级分布式应用服务 (EDAS)

针对阿里云 EDAS 开发者, 可以将本地代码和云端应用进行关联,实现自动化的部署。

  • 部署到 Serverless 应用引擎(SAE)

针对阿里云 SAE 开发者,可以将应用快速部署到 SAE,适用于快速迭代更新应用的场景。

  • 内置数据库 SQL Console

在 IDE 内,开发者可以浏览阿里云的 RDS 资源;同时,在配置好用户名密码之后,即可通过内置的 SQL Console 连接上 RDS 实例,并快速执行 SQL 语句。

  • 文件上传

Cloud Toolkit 帮助开发者在 IDE 内,一键将本地或者远程 URL 文件上传到服务器指定目录下去,无需在各种 FTP、SCP 工具之间频繁切换;更为重要的是,文件上传完毕后,还支持命令执行,比如:文件解压缩、程序启动等。

  • 快速创建 Dubbo 工程

使用 Cloud Toolkit 可以快速创建 Dubbo 工程,在完成 Apache Dubbo 样例工程的创建和调用验证后,可以将该工程打包(JAR 包)并部署到 EDAS 的不同集群(主要为 ECS 集群和容器服务 Kubernetes 集群)中。

程序猿吐槽大会——悬赏活动

一、我们为什么邀请你来吐槽

我们 Cloud Toolkit 团队秉承一贯的 “用户第一” 原则,希望能在一线倾听我们用户的真实声音、寻找你们的真实需求和建议,而后,我们才能在满足用户需求的基础上,为大家研发出更贴心、更高效的插件,一款真正属于广大开发者的插件。所以,我们满怀诚意,邀请您,请您说出心中对于插件的真实感受,或是吐槽,或是表扬。

二、怎么参与活动

活动时间:12月24日(本周二)——12月30日(下周一)

请点击文末 “阅读原文” 下载插件进行体验(一键部署,不信?你更要来试试),并在文末留言区写下你的吐槽 / 表扬 / 挑战(任选其一),让文章其他阅读者进行 “点赞 ”投票,或者将文章分享给同事 / 朋友进行 “点赞” 拉票。

所有人均可参与投票。

【吐槽】简单描述Cloud Toolkit使用槽点;
【表扬】简单描述Cloud Toolkit的闪光点;
【挑战】请输入你认为能够提升Cloud Toolkit部署效率的方法;

注意:若出现相同评论,只保留第一个人的评论,第二个人的评论无效;任何言论都要遵循真实的原则,获奖后需要提供使用插件的证明截图。

三、我能获得什么

我们 Cloud Toolkit 官方团队将接收到您的所有真心建议、真实需求,然后有针对性地研发新的功能、提升您的体验。您将获得插件 “定制化” 功能,同时,您还能获得以下奖品:

【一等奖】投票数最高的发言者,获得 Cherry机械键盘1个;

【二等奖】投票数第二高的发言者,获得 天猫精灵1个;

【三等奖】投票数第三高的发言者,获得 双肩包1个;

【四、五等奖】投票数第四、五高的发言者,获得 阿里云日历各1个;

【鼓励奖】投票数前十的发言者,均可获得指尖陀螺 / 淘公仔,任选其一(前五除外);

【参与奖】扫描下方二维码进群,艾特客服@郭伟成,即可领奖;

钉钉扫码加群,领奖

微信扫码,产品经理拉你进群,领奖]]>
Sequeue 解析 Mon, 27 Jan 2020 20:58:34 +0800 本文选自《Knative 云原生应用开发指南》
knative海报.png
更多云原生技术资讯可关注阿里巴巴云原生技术圈

Sequence 定义

首先我们看一下 Sequence Spec 定义:

apiVersion: messaging.knative.dev/v1alpha1
kind: Sequence
metadata:
  name: test
spec:
  channelTemplate:
    apiVersion: messaging.knative.dev/v1alpha1
    kind: InMemoryChannel
  steps:
    - ref:
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        name: test
  reply:
    kind: Broker
    apiVersion: eventing.knative.dev/v1alpha1
    name: test

Sequence Spec包括3个部分:

  1. steps: 在 step 中定义了按照顺序执行的服务,每个服务会对应创建 Subscription
  2. channelTemplate:指定了使用具体的那个 Channel
  3. reply:(可选)定义了将最后一个 step 服务结果转发到的目标服务

Sequence 都是适合哪些具体应用场景呢?我们上面也提到了事件处理的 Pipeline。那么在实际场景应用中究竟以什么样的形式体现呢? 现在我们揭晓一下 Sequence 在 Knative Eventing 中提供的如下 4 种使用场景:

  • 直接访问 Service
  • 面向事件处理
  • 级联 Sequence
  • 面向 Broker/Trigger

直接访问 Service 场景

事件源产生的事件直接发送给 Sequence 服务, Sequence 接收到事件之后顺序调用 Service 服务对事件进行处理:
20.png

创建 Knative Service

这里我们创建 3 个 Knative Service 用于事件处理。每个 Service 接收到事件之后会打印当前的事件处理信息。

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: first
spec:
  template:
    spec:
      containers:
        - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
          env:
            - name: STEP
              value: "0"

---
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: second
spec:
  template:
    spec:
      containers:
        - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
          env:
            - name: STEP
              value: "1"
---
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: third
spec:
  template:
    spec:
      containers:
        - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
          env:
            - name: STEP
              value: "2"
---

创建 Sequence

创建顺序调用 first->second->third Service 的 Sequence。

apiVersion: messaging.knative.dev/v1alpha1
kind: Sequence
metadata:
  name: sequence
spec:
  channelTemplate:
    apiVersion: messaging.knative.dev/v1alpha1
    kind: InMemoryChannel
  steps:
    - ref:
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        name: first
    - ref:
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        name: second
    - ref:
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        name: third

创建数据源

创建 CronJobSource 数据源,每隔 1 分钟发送一条事件消息{"message": "Hello world!"}到 Sequence 服务。

apiVersion: sources.eventing.knative.dev/v1alpha1
kind: CronJobSource
metadata:
  name: cronjob-source
spec:
  schedule: "*/1 * * * *"
  data: '{"message": "Hello world!"}'
  sink:
    apiVersion: messaging.knative.dev/v1alpha1
    kind: Sequence
    name: sequence

示例结果

21.png22.png
23.png
24.png

面向事件处理场景

事件源产生的事件直接发送给 Sequence 服务, Sequence 接收到事件之后顺序调用 Service 服务对事件进行处理,处理之后的最终结果会调用event-display Service 显示:
25.png

创建 Knative Service

同上创建 3 个 Knative Service 用于事件处理:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: first
spec:
  template:
    spec:
      containers:
        - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
          env:
            - name: STEP
              value: "0"

---
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: second
spec:
  template:
    spec:
      containers:
        - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
          env:
            - name: STEP
              value: "1"
---
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: third
spec:
  template:
    spec:
      containers:
        - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
          env:
            - name: STEP
              value: "2"
---

创建 Sequence

创建顺序调用 first->second->third Service 的 Sequence,将处理结果通过reply发送给event-display

apiVersion: messaging.knative.dev/v1alpha1
kind: Sequence
metadata:
  name: sequence
spec:
  channelTemplate:
    apiVersion: messaging.knative.dev/v1alpha1
    kind: InMemoryChannel
  steps:
    - ref:
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        name: first
    - ref:
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        name: second
    - ref:
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        name: third
  reply:
    kind: Service
    apiVersion: serving.knative.dev/v1alpha1
    name: event-display

创建结果显示 Service

创建event-display Service, 用于接收最终的结果信息。

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: event-display
spec:
  template:
    spec:
      containers:
        - image: registr